pax_global_header00006660000000000000000000000064151424525720014521gustar00rootroot0000000000000052 comment=e601e5a6bd77a48596b3eaeeb8bb2230ecc0908f uhop-node-re2-e601e5a/000077500000000000000000000000001514245257200145155ustar00rootroot00000000000000uhop-node-re2-e601e5a/.editorconfig000066400000000000000000000003061514245257200171710ustar00rootroot00000000000000root = true [*] charset = utf-8 end_of_line = lf insert_final_newline = true trim_trailing_whitespace = true indent_style = space indent_size = 2 [*.{h,cc,cpp}] indent_style = tab indent_size = 4 uhop-node-re2-e601e5a/.github/000077500000000000000000000000001514245257200160555ustar00rootroot00000000000000uhop-node-re2-e601e5a/.github/FUNDING.yml000066400000000000000000000000431514245257200176670ustar00rootroot00000000000000github: uhop buy_me_a_coffee: uhop uhop-node-re2-e601e5a/.github/actions/000077500000000000000000000000001514245257200175155ustar00rootroot00000000000000uhop-node-re2-e601e5a/.github/actions/linux-alpine-node-20/000077500000000000000000000000001514245257200232645ustar00rootroot00000000000000uhop-node-re2-e601e5a/.github/actions/linux-alpine-node-20/Dockerfile000066400000000000000000000002201514245257200252500ustar00rootroot00000000000000FROM node:20-alpine RUN apk add --no-cache python3 make gcc g++ linux-headers COPY entrypoint.sh /entrypoint.sh ENTRYPOINT ["/entrypoint.sh"] uhop-node-re2-e601e5a/.github/actions/linux-alpine-node-20/action.yml000066400000000000000000000003411514245257200252620ustar00rootroot00000000000000name: 'Create a binary artifact for Node 20 on Alpine Linux' description: 'Create a binary artifact for Node 20 on Alpine Linux using musl' runs: using: 'docker' image: 'Dockerfile' args: - ${{inputs.node-version}} uhop-node-re2-e601e5a/.github/actions/linux-alpine-node-20/entrypoint.sh000077500000000000000000000002301514245257200260310ustar00rootroot00000000000000#!/bin/sh set -e export USERNAME=`whoami` export DEVELOPMENT_SKIP_GETTING_ASSET=true npm i npm run build --if-present npm test npm run save-to-github uhop-node-re2-e601e5a/.github/actions/linux-alpine-node-22/000077500000000000000000000000001514245257200232665ustar00rootroot00000000000000uhop-node-re2-e601e5a/.github/actions/linux-alpine-node-22/Dockerfile000066400000000000000000000002201514245257200252520ustar00rootroot00000000000000FROM node:22-alpine RUN apk add --no-cache python3 make gcc g++ linux-headers COPY entrypoint.sh /entrypoint.sh ENTRYPOINT ["/entrypoint.sh"] uhop-node-re2-e601e5a/.github/actions/linux-alpine-node-22/action.yml000066400000000000000000000003411514245257200252640ustar00rootroot00000000000000name: 'Create a binary artifact for Node 22 on Alpine Linux' description: 'Create a binary artifact for Node 22 on Alpine Linux using musl' runs: using: 'docker' image: 'Dockerfile' args: - ${{inputs.node-version}} uhop-node-re2-e601e5a/.github/actions/linux-alpine-node-22/entrypoint.sh000077500000000000000000000002301514245257200260330ustar00rootroot00000000000000#!/bin/sh set -e export USERNAME=`whoami` export DEVELOPMENT_SKIP_GETTING_ASSET=true npm i npm run build --if-present npm test npm run save-to-github uhop-node-re2-e601e5a/.github/actions/linux-alpine-node-24/000077500000000000000000000000001514245257200232705ustar00rootroot00000000000000uhop-node-re2-e601e5a/.github/actions/linux-alpine-node-24/Dockerfile000066400000000000000000000002201514245257200252540ustar00rootroot00000000000000FROM node:24-alpine RUN apk add --no-cache python3 make gcc g++ linux-headers COPY entrypoint.sh /entrypoint.sh ENTRYPOINT ["/entrypoint.sh"] uhop-node-re2-e601e5a/.github/actions/linux-alpine-node-24/action.yml000066400000000000000000000003411514245257200252660ustar00rootroot00000000000000name: 'Create a binary artifact for Node 24 on Alpine Linux' description: 'Create a binary artifact for Node 24 on Alpine Linux using musl' runs: using: 'docker' image: 'Dockerfile' args: - ${{inputs.node-version}} uhop-node-re2-e601e5a/.github/actions/linux-alpine-node-24/entrypoint.sh000077500000000000000000000002301514245257200260350ustar00rootroot00000000000000#!/bin/sh set -e export USERNAME=`whoami` export DEVELOPMENT_SKIP_GETTING_ASSET=true npm i npm run build --if-present npm test npm run save-to-github uhop-node-re2-e601e5a/.github/actions/linux-alpine-node-25/000077500000000000000000000000001514245257200232715ustar00rootroot00000000000000uhop-node-re2-e601e5a/.github/actions/linux-alpine-node-25/Dockerfile000066400000000000000000000002201514245257200252550ustar00rootroot00000000000000FROM node:25-alpine RUN apk add --no-cache python3 make gcc g++ linux-headers COPY entrypoint.sh /entrypoint.sh ENTRYPOINT ["/entrypoint.sh"] uhop-node-re2-e601e5a/.github/actions/linux-alpine-node-25/action.yml000066400000000000000000000003411514245257200252670ustar00rootroot00000000000000name: 'Create a binary artifact for Node 25 on Alpine Linux' description: 'Create a binary artifact for Node 25 on Alpine Linux using musl' runs: using: 'docker' image: 'Dockerfile' args: - ${{inputs.node-version}} uhop-node-re2-e601e5a/.github/actions/linux-alpine-node-25/entrypoint.sh000077500000000000000000000002301514245257200260360ustar00rootroot00000000000000#!/bin/sh set -e export USERNAME=`whoami` export DEVELOPMENT_SKIP_GETTING_ASSET=true npm i npm run build --if-present npm test npm run save-to-github uhop-node-re2-e601e5a/.github/actions/linux-node-20/000077500000000000000000000000001514245257200220165ustar00rootroot00000000000000uhop-node-re2-e601e5a/.github/actions/linux-node-20/Dockerfile000066400000000000000000000001751514245257200240130ustar00rootroot00000000000000FROM node:20-bullseye RUN apt install python3 make gcc g++ COPY entrypoint.sh /entrypoint.sh ENTRYPOINT ["/entrypoint.sh"] uhop-node-re2-e601e5a/.github/actions/linux-node-20/action.yml000066400000000000000000000005111514245257200240130ustar00rootroot00000000000000name: 'Create a binary artifact for Node 20 on Debian Bullseye Linux' description: 'Create a binary artifact for Node 20 on Debian Bullseye Linux' inputs: node-version: description: 'Node.js version' required: false default: '20' runs: using: 'docker' image: 'Dockerfile' args: - ${{inputs.node-version}} uhop-node-re2-e601e5a/.github/actions/linux-node-20/entrypoint.sh000077500000000000000000000002301514245257200245630ustar00rootroot00000000000000#!/bin/sh set -e export USERNAME=`whoami` export DEVELOPMENT_SKIP_GETTING_ASSET=true npm i npm run build --if-present npm test npm run save-to-github uhop-node-re2-e601e5a/.github/actions/linux-node-22/000077500000000000000000000000001514245257200220205ustar00rootroot00000000000000uhop-node-re2-e601e5a/.github/actions/linux-node-22/Dockerfile000066400000000000000000000001751514245257200240150ustar00rootroot00000000000000FROM node:22-bullseye RUN apt install python3 make gcc g++ COPY entrypoint.sh /entrypoint.sh ENTRYPOINT ["/entrypoint.sh"] uhop-node-re2-e601e5a/.github/actions/linux-node-22/action.yml000066400000000000000000000005111514245257200240150ustar00rootroot00000000000000name: 'Create a binary artifact for Node 22 on Debian Bullseye Linux' description: 'Create a binary artifact for Node 22 on Debian Bullseye Linux' inputs: node-version: description: 'Node.js version' required: false default: '22' runs: using: 'docker' image: 'Dockerfile' args: - ${{inputs.node-version}} uhop-node-re2-e601e5a/.github/actions/linux-node-22/entrypoint.sh000077500000000000000000000002301514245257200245650ustar00rootroot00000000000000#!/bin/sh set -e export USERNAME=`whoami` export DEVELOPMENT_SKIP_GETTING_ASSET=true npm i npm run build --if-present npm test npm run save-to-github uhop-node-re2-e601e5a/.github/actions/linux-node-24/000077500000000000000000000000001514245257200220225ustar00rootroot00000000000000uhop-node-re2-e601e5a/.github/actions/linux-node-24/Dockerfile000066400000000000000000000001751514245257200240170ustar00rootroot00000000000000FROM node:24-bullseye RUN apt install python3 make gcc g++ COPY entrypoint.sh /entrypoint.sh ENTRYPOINT ["/entrypoint.sh"] uhop-node-re2-e601e5a/.github/actions/linux-node-24/action.yml000066400000000000000000000005111514245257200240170ustar00rootroot00000000000000name: 'Create a binary artifact for Node 24 on Debian Bullseye Linux' description: 'Create a binary artifact for Node 24 on Debian Bullseye Linux' inputs: node-version: description: 'Node.js version' required: false default: '24' runs: using: 'docker' image: 'Dockerfile' args: - ${{inputs.node-version}} uhop-node-re2-e601e5a/.github/actions/linux-node-24/entrypoint.sh000077500000000000000000000002301514245257200245670ustar00rootroot00000000000000#!/bin/sh set -e export USERNAME=`whoami` export DEVELOPMENT_SKIP_GETTING_ASSET=true npm i npm run build --if-present npm test npm run save-to-github uhop-node-re2-e601e5a/.github/actions/linux-node-25/000077500000000000000000000000001514245257200220235ustar00rootroot00000000000000uhop-node-re2-e601e5a/.github/actions/linux-node-25/Dockerfile000066400000000000000000000001731514245257200240160ustar00rootroot00000000000000FROM node:25-trixie RUN apt install python3 make gcc g++ COPY entrypoint.sh /entrypoint.sh ENTRYPOINT ["/entrypoint.sh"] uhop-node-re2-e601e5a/.github/actions/linux-node-25/action.yml000066400000000000000000000005051514245257200240230ustar00rootroot00000000000000name: 'Create a binary artifact for Node 25 on Debian Trixie Linux' description: 'Create a binary artifact for Node 25 on Debian Trixie Linux' inputs: node-version: description: 'Node.js version' required: false default: '25' runs: using: 'docker' image: 'Dockerfile' args: - ${{inputs.node-version}} uhop-node-re2-e601e5a/.github/actions/linux-node-25/entrypoint.sh000077500000000000000000000002301514245257200245700ustar00rootroot00000000000000#!/bin/sh set -e export USERNAME=`whoami` export DEVELOPMENT_SKIP_GETTING_ASSET=true npm i npm run build --if-present npm test npm run save-to-github uhop-node-re2-e601e5a/.github/dependabot.yml000066400000000000000000000007661514245257200207160ustar00rootroot00000000000000# To get started with Dependabot version updates, you'll need to specify which # package ecosystems to update and where the package manifests are located. # Please see the documentation for all configuration options: # https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates version: 2 updates: - package-ecosystem: "npm" # See documentation for possible values directory: "/" # Location of package manifests schedule: interval: "weekly" uhop-node-re2-e601e5a/.github/workflows/000077500000000000000000000000001514245257200201125ustar00rootroot00000000000000uhop-node-re2-e601e5a/.github/workflows/build.yml000066400000000000000000000274051514245257200217440ustar00rootroot00000000000000name: Node.js builds on: push: tags: - v?[0-9]+.[0-9]+.[0-9]+.[0-9]+ - v?[0-9]+.[0-9]+.[0-9]+ - v?[0-9]+.[0-9]+ permissions: id-token: write contents: write attestations: write jobs: create-release: name: Create release runs-on: ubuntu-latest steps: - uses: actions/checkout@v5 - env: GH_TOKEN: ${{github.token}} run: | REF=${{github.ref}} TAG=${REF#"refs/tags/"} gh release create -t "Release ${TAG}" -n "" "${{github.ref}}" build: name: Node.js ${{matrix.node-version}} on ${{matrix.os}} needs: create-release runs-on: ${{matrix.os}} strategy: matrix: os: [macos-latest, windows-latest, macos-15-intel, windows-11-arm] node-version: [20, 22, 24, 25] steps: - uses: actions/checkout@v5 with: submodules: true - name: Setup Node.js ${{matrix.node-version}} uses: actions/setup-node@v6 with: node-version: ${{matrix.node-version}} - name: Install the package and run tests env: DEVELOPMENT_SKIP_GETTING_ASSET: true run: | npm i npm run build --if-present npm test - name: Save to GitHub env: GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} run: npm run save-to-github - name: Attest if: env.CREATED_ASSET_NAME != '' uses: actions/attest-build-provenance@v3 with: subject-name: '${{ env.CREATED_ASSET_NAME }}' subject-path: '${{ github.workspace }}/build/Release/re2.node' build-linux-node-20: name: Node.js 20 on Bullseye needs: create-release runs-on: ubuntu-latest continue-on-error: true steps: - uses: actions/checkout@v5 with: submodules: true - name: Install, test, and create artifact uses: ./.github/actions/linux-node-20/ env: GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} - name: Attest if: env.CREATED_ASSET_NAME != '' uses: actions/attest-build-provenance@v3 with: subject-name: '${{ env.CREATED_ASSET_NAME }}' subject-path: '${{ github.workspace }}/build/Release/re2.node' build-linux-node-22: name: Node.js 22 on Bullseye needs: create-release runs-on: ubuntu-latest continue-on-error: true steps: - uses: actions/checkout@v5 with: submodules: true - name: Install, test, and create artifact uses: ./.github/actions/linux-node-22/ env: GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} - name: Attest if: env.CREATED_ASSET_NAME != '' uses: actions/attest-build-provenance@v3 with: subject-name: '${{ env.CREATED_ASSET_NAME }}' subject-path: '${{ github.workspace }}/build/Release/re2.node' build-linux-alpine-node-20: name: Node.js 20 on Alpine needs: create-release runs-on: ubuntu-latest continue-on-error: true steps: - uses: actions/checkout@v5 with: submodules: true - name: Install, test, and create artifact uses: ./.github/actions/linux-alpine-node-20/ env: GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} - name: Attest if: env.CREATED_ASSET_NAME != '' uses: actions/attest-build-provenance@v3 with: subject-name: '${{ env.CREATED_ASSET_NAME }}' subject-path: '${{ github.workspace }}/build/Release/re2.node' build-linux-alpine-node-22: name: Node.js 22 on Alpine needs: create-release runs-on: ubuntu-latest continue-on-error: true steps: - uses: actions/checkout@v5 with: submodules: true - name: Install, test, and create artifact uses: ./.github/actions/linux-alpine-node-22/ env: GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} - name: Attest if: env.CREATED_ASSET_NAME != '' uses: actions/attest-build-provenance@v3 with: subject-name: '${{ env.CREATED_ASSET_NAME }}' subject-path: '${{ github.workspace }}/build/Release/re2.node' build-linux-arm64-node-20: name: Node.js 20 on Bullseye ARM64 needs: create-release runs-on: ubuntu-24.04-arm continue-on-error: true steps: - uses: actions/checkout@v5 with: submodules: true - name: Install, test, and create artifact uses: ./.github/actions/linux-node-20/ env: GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} - name: Attest if: env.CREATED_ASSET_NAME != '' uses: actions/attest-build-provenance@v3 with: subject-name: '${{ env.CREATED_ASSET_NAME }}' subject-path: '${{ github.workspace }}/build/Release/re2.node' build-linux-arm64-node-22: name: Node.js 22 on Bullseye ARM64 needs: create-release runs-on: ubuntu-24.04-arm continue-on-error: true steps: - uses: actions/checkout@v5 with: submodules: true - name: Install, test, and create artifact uses: ./.github/actions/linux-node-22/ env: GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} - name: Attest if: env.CREATED_ASSET_NAME != '' uses: actions/attest-build-provenance@v3 with: subject-name: '${{ env.CREATED_ASSET_NAME }}' subject-path: '${{ github.workspace }}/build/Release/re2.node' build-linux-arm64-alpine-node-20: name: Node.js 20 on Alpine ARM64 needs: create-release runs-on: ubuntu-24.04-arm continue-on-error: true steps: - uses: actions/checkout@v5 with: submodules: true - name: Install, test, and create artifact uses: ./.github/actions/linux-alpine-node-20/ env: GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} - name: Attest if: env.CREATED_ASSET_NAME != '' uses: actions/attest-build-provenance@v3 with: subject-name: '${{ env.CREATED_ASSET_NAME }}' subject-path: '${{ github.workspace }}/build/Release/re2.node' build-linux-arm64-alpine-node-22: name: Node.js 22 on Alpine ARM64 needs: create-release runs-on: ubuntu-24.04-arm continue-on-error: true steps: - uses: actions/checkout@v5 with: submodules: true - name: Install, test, and create artifact uses: ./.github/actions/linux-alpine-node-22/ env: GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} - name: Attest if: env.CREATED_ASSET_NAME != '' uses: actions/attest-build-provenance@v3 with: subject-name: '${{ env.CREATED_ASSET_NAME }}' subject-path: '${{ github.workspace }}/build/Release/re2.node' build-linux-node-24: name: Node.js 24 on Bullseye needs: create-release runs-on: ubuntu-latest continue-on-error: true steps: - uses: actions/checkout@v5 with: submodules: true - name: Install, test, and create artifact uses: ./.github/actions/linux-node-24/ env: GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} - name: Attest if: env.CREATED_ASSET_NAME != '' uses: actions/attest-build-provenance@v3 with: subject-name: '${{ env.CREATED_ASSET_NAME }}' subject-path: '${{ github.workspace }}/build/Release/re2.node' build-linux-alpine-node-24: name: Node.js 24 on Alpine needs: create-release runs-on: ubuntu-latest continue-on-error: true steps: - uses: actions/checkout@v5 with: submodules: true - name: Install, test, and create artifact uses: ./.github/actions/linux-alpine-node-24/ env: GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} - name: Attest if: env.CREATED_ASSET_NAME != '' uses: actions/attest-build-provenance@v3 with: subject-name: '${{ env.CREATED_ASSET_NAME }}' subject-path: '${{ github.workspace }}/build/Release/re2.node' build-linux-arm64-node-24: name: Node.js 24 on Bullseye ARM64 needs: create-release runs-on: ubuntu-24.04-arm continue-on-error: true steps: - uses: actions/checkout@v5 with: submodules: true - name: Install, test, and create artifact uses: ./.github/actions/linux-node-24/ env: GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} - name: Attest if: env.CREATED_ASSET_NAME != '' uses: actions/attest-build-provenance@v3 with: subject-name: '${{ env.CREATED_ASSET_NAME }}' subject-path: '${{ github.workspace }}/build/Release/re2.node' build-linux-arm64-alpine-node-24: name: Node.js 24 on Alpine ARM64 needs: create-release runs-on: ubuntu-24.04-arm continue-on-error: true steps: - uses: actions/checkout@v5 with: submodules: true - name: Install, test, and create artifact uses: ./.github/actions/linux-alpine-node-24/ env: GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} - name: Attest if: env.CREATED_ASSET_NAME != '' uses: actions/attest-build-provenance@v3 with: subject-name: '${{ env.CREATED_ASSET_NAME }}' subject-path: '${{ github.workspace }}/build/Release/re2.node' build-linux-node-25: name: Node.js 25 on Trixie needs: create-release runs-on: ubuntu-latest continue-on-error: true steps: - uses: actions/checkout@v5 with: submodules: true - name: Install, test, and create artifact uses: ./.github/actions/linux-node-25/ env: GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} - name: Attest if: env.CREATED_ASSET_NAME != '' uses: actions/attest-build-provenance@v3 with: subject-name: '${{ env.CREATED_ASSET_NAME }}' subject-path: '${{ github.workspace }}/build/Release/re2.node' build-linux-alpine-node-25: name: Node.js 25 on Alpine needs: create-release runs-on: ubuntu-latest continue-on-error: true steps: - uses: actions/checkout@v5 with: submodules: true - name: Install, test, and create artifact uses: ./.github/actions/linux-alpine-node-25/ env: GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} - name: Attest if: env.CREATED_ASSET_NAME != '' uses: actions/attest-build-provenance@v3 with: subject-name: '${{ env.CREATED_ASSET_NAME }}' subject-path: '${{ github.workspace }}/build/Release/re2.node' build-linux-arm64-node-25: name: Node.js 25 on Trixie ARM64 needs: create-release runs-on: ubuntu-24.04-arm continue-on-error: true steps: - uses: actions/checkout@v5 with: submodules: true - name: Install, test, and create artifact uses: ./.github/actions/linux-node-25/ env: GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} - name: Attest if: env.CREATED_ASSET_NAME != '' uses: actions/attest-build-provenance@v3 with: subject-name: '${{ env.CREATED_ASSET_NAME }}' subject-path: '${{ github.workspace }}/build/Release/re2.node' build-linux-arm64-alpine-node-25: name: Node.js 25 on Alpine ARM64 needs: create-release runs-on: ubuntu-24.04-arm continue-on-error: true steps: - uses: actions/checkout@v5 with: submodules: true - name: Install, test, and create artifact uses: ./.github/actions/linux-alpine-node-25/ env: GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} - name: Attest if: env.CREATED_ASSET_NAME != '' uses: actions/attest-build-provenance@v3 with: subject-name: '${{ env.CREATED_ASSET_NAME }}' subject-path: '${{ github.workspace }}/build/Release/re2.node' uhop-node-re2-e601e5a/.github/workflows/tests.yml000066400000000000000000000013621514245257200220010ustar00rootroot00000000000000name: Node.js CI on: push: branches: ['*'] pull_request: branches: [master] jobs: tests: name: Node.js ${{matrix.node-version}} on ${{matrix.os}} runs-on: ${{matrix.os}} strategy: matrix: os: [ubuntu-latest, macOS-latest, windows-latest] node-version: [20, 22, 24, 25] steps: - uses: actions/checkout@v5 with: submodules: true - name: Setup Node.js ${{matrix.node-version}} uses: actions/setup-node@v6 with: node-version: ${{matrix.node-version}} - name: Install the package and run tests env: DEVELOPMENT_SKIP_GETTING_ASSET: true run: | npm i npm run build --if-present npm test uhop-node-re2-e601e5a/.gitignore000066400000000000000000000001701514245257200165030ustar00rootroot00000000000000node_modules/ build/ report/ coverage/ .AppleDouble /.development /.developmentx /.xdevelopment /scripts/save-local.sh uhop-node-re2-e601e5a/.gitmodules000066400000000000000000000002671514245257200166770ustar00rootroot00000000000000[submodule "vendor/re2"] path = vendor/re2 url = https://github.com/google/re2 [submodule "vendor/abseil-cpp"] path = vendor/abseil-cpp url = https://github.com/abseil/abseil-cpp uhop-node-re2-e601e5a/.prettierrc000066400000000000000000000001761514245257200167050ustar00rootroot00000000000000{ "printWidth": 80, "singleQuote": true, "bracketSpacing": false, "arrowParens": "avoid", "trailingComma": "none" } uhop-node-re2-e601e5a/.vscode/000077500000000000000000000000001514245257200160565ustar00rootroot00000000000000uhop-node-re2-e601e5a/.vscode/c_cpp_properties.json000066400000000000000000000010561514245257200223130ustar00rootroot00000000000000{ "configurations": [ { "name": "Mac", "includePath": [ "${workspaceFolder}/**", "/${env.NVM_INC}/**" ], "defines": [], "macFrameworkPath": [ "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/System/Library/Frameworks" ], "compilerPath": "/usr/bin/clang", "cStandard": "c17", "cppStandard": "c++17", "intelliSenseMode": "macos-clang-arm64" } ], "version": 4 }uhop-node-re2-e601e5a/.vscode/launch.json000066400000000000000000000007711514245257200202300ustar00rootroot00000000000000{ // Use IntelliSense to learn about possible attributes. // Hover to view descriptions of existing attributes. // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 "version": "0.2.0", "configurations": [ { "type": "lldb", "request": "launch", "name": "Debug tests", "preLaunchTask": "npm: build:dev", "program": "${env:NVM_BIN}/node", "args": ["${workspaceFolder}/tests/tests.js"], "cwd": "${workspaceFolder}" } ] } uhop-node-re2-e601e5a/.vscode/settings.json000066400000000000000000000001231514245257200206050ustar00rootroot00000000000000{ "cSpell.words": [ "heya", "PCRE", "replacee", "Submatch" ] } uhop-node-re2-e601e5a/.vscode/tasks.json000066400000000000000000000003251514245257200200760ustar00rootroot00000000000000{ "version": "2.0.0", "tasks": [ { "type": "npm", "script": "build:dev", "group": "build", "problemMatcher": [], "label": "npm: build:dev", "detail": "node-gyp -j max build --debug" } ] } uhop-node-re2-e601e5a/LICENSE000066400000000000000000000035651514245257200155330ustar00rootroot00000000000000This library is available under the terms of the modified BSD license. No external contributions are allowed under licenses which are fundamentally incompatible with the BSD license that this library is distributed under. The text of the BSD license is reproduced below. ------------------------------------------------------------------------------- The "New" BSD License: ********************** Copyright (c) 2005-2025, Eugene Lazutkin All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Eugene Lazutkin nor the names of other contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. uhop-node-re2-e601e5a/README.md000066400000000000000000000540141514245257200160000ustar00rootroot00000000000000# node-re2 [![NPM version][npm-img]][npm-url] [npm-img]: https://img.shields.io/npm/v/re2.svg [npm-url]: https://npmjs.org/package/re2 This project provides Node.js bindings for [RE2](https://github.com/google/re2): a fast, safe alternative to backtracking regular expression engines written by [Russ Cox](http://swtch.com/~rsc/) in C++. To learn more about RE2, start with an overview [Regular Expression Matching in the Wild](http://swtch.com/~rsc/regexp/regexp3.html). More resources can be found at his [Implementing Regular Expressions](http://swtch.com/~rsc/regexp/) page. `RE2`'s regular expression language is almost a superset of what is provided by `RegExp` (see [Syntax](https://github.com/google/re2/wiki/Syntax)), but it lacks two features: backreferences and lookahead assertions. See below for more details. `RE2` always works in the [Unicode mode](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/unicode), which means that all matches that use character codes are interpret as Unicode code points, not as binary values of UTF-16. See `RE2.unicodeWarningLevel` below for more details. `RE2` object emulates standard `RegExp` making it a practical drop-in replacement in most cases. `RE2` is extended to provide `String`-based regular expression methods as well. To help to convert `RegExp` objects to `RE2` its constructor can take `RegExp` directly honoring all properties. It can work with [node.js buffers](http://nodejs.org/api/buffer.html) directly reducing overhead on recoding and copying characters, and making processing/parsing long files fast. This project is implemented in C++ using [nan](https://github.com/nodejs/nan) for Node.js and cannot be used with non-compliant runtimes like web browsers. All documentation can be found in this README and in the [wiki](https://github.com/uhop/node-re2/wiki). ## Why use node-re2? The built-in Node.js regular expression engine can run in exponential time with a special combination: - A vulnerable regular expression - "Evil input" This 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). To tell if your regular expressions are vulnerable, you might try the one of these projects: - [rxxr2](http://www.cs.bham.ac.uk/~hxt/research/rxxr2/) - [safe-regex](https://github.com/substack/safe-regex) However, neither project is perfect. node-re2 can protect your Node.js application from ReDoS. node-re2 makes vulnerable regular expression patterns safe by evaluating them in `RE2` instead of the built-in Node.js regex engine. To run the bundled benchmark, use the following command (make sure that node-re2 is properly built before): ```bash npx nano-bench bench/bad-pattern.mjs ``` ## Standard features `RE2` object can be created just like `RegExp`: * [`new RE2(pattern[, flags])`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp) Supported properties: * [`re2.lastIndex`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/lastIndex) * [`re2.global`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/global) * [`re2.ignoreCase`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/ignoreCase) * [`re2.multiline`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/multiline) * [`re2.dotAll`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/dotAll) — *since 1.17.6.* * [`re2.unicode`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/unicode) * `RE2` engine always works in the Unicode mode. See details below. * [`re2.sticky`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/sticky) — *since 1.7.0.* * [`re2.hasIndices`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/hasIndices) — *since 1.19.0.* * [`re2.source`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/source) * [`re2.flags`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/flags) Supported methods: * [`re2.exec(str)`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/exec) * [`re2.test(str)`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/test) * [`re2.toString()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/toString) Starting with 1.6.0 following well-known symbol-based methods are supported (see [Symbols](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol)): * [`re2[Symbol.match](str)`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/match) * [`re2[Symbol.matchAll](str)`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/matchAll) — *since 1.17.5.* * [`re2[Symbol.search](str)`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/search) * [`re2[Symbol.replace](str, newSubStr|function)`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/replace) * [`re2[Symbol.split](str[, limit])`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/split) It allows to use `RE2` instances on strings directly, just like `RegExp` instances: ```js var re = new RE2("1"); "213".match(re); // [ '1', index: 1, input: '213' ] "213".search(re); // 1 "213".replace(re, "+"); // 2+3 "213".split(re); // [ '2', '3' ] Array.from("2131".matchAll(re)); // returns a generator! // [['1', index: 1, input: '2131'], ['1', index: 3, input: '2131']] ``` Starting with 1.8.0 [named groups](https://tc39.github.io/proposal-regexp-named-groups/) are supported. ## Extensions ### Shortcut construction `RE2` object can be created from a regular expression: ```js var re1 = new RE2(/ab*/ig); // from a RegExp object var re2 = new RE2(re1); // from another RE2 object ``` ### `String` methods Standard `String` defines four more methods that can use regular expressions. `RE2` provides them as methods exchanging positions of a string, and a regular expression: * `re2.match(str)` * See [`str.match(regexp)`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/match) * `re2.replace(str, newSubStr|function)` * See [`str.replace(regexp, newSubStr|function)`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/replace) * `re2.search(str)` * See [`str.search(regexp)`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/search) * `re2.split(str[, limit])` * See [`str.split(regexp[, limit])`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/split) Starting with 1.6.0, these methods added as well-known symbol-based methods to be used transparently with ES6 string/regex machinery. ### `Buffer` support In order to support `Buffer` directly, most methods can accept buffers instead of strings. It speeds up all operations. Following signatures are supported: * `re2.exec(buf)` * `re2.test(buf)` * `re2.match(buf)` * `re2.search(buf)` * `re2.split(buf[, limit])` * `re2.replace(buf, replacer)` Differences with their string-based versions: * All buffers are assumed to be encoded as [UTF-8](http://en.wikipedia.org/wiki/UTF-8) (ASCII is a proper subset of UTF-8). * Instead of strings they return `Buffer` objects, even in composite objects. A buffer can be converted to a string with [`buf.toString()`](http://nodejs.org/api/buffer.html#buffer_buf_tostring_encoding_start_end). * All offsets and lengths are in bytes, rather than characters (each UTF-8 character can occupy from 1 to 4 bytes). This way users can properly slice buffers without costly recalculations from characters to bytes. When `re2.replace()` is used with a replacer function, the replacer can return a buffer, or a string. But all arguments (except for an input object) will be strings, and an offset will be in characters. If you prefer to deal with buffers and byte offsets in a replacer function, set a property `useBuffers` to `true` on the function: ```js function strReplacer(match, offset, input) { // typeof match == "string" return "<= " + offset + " characters|"; } RE2("б").replace("абв", strReplacer); // "а<= 1 characters|в" function bufReplacer(match, offset, input) { // typeof match == "string" return "<= " + offset + " bytes|"; } bufReplacer.useBuffers = true; RE2("б").replace("абв", bufReplacer); // "а<= 2 bytes|в" ``` This feature works for string and buffer inputs. If a buffer was used as an input, its output will be returned as a buffer too, otherwise a string will be returned. ### `RE2.Set` Starting with 1.23.0, use `RE2.Set` when the same string must be tested against many patterns. It builds a single automaton for all of them and frequently beats running a large list of individual regular expressions one by one. Sets support `test()` and `match()` methods. While `test()` can be simulated by combining patterns with `|` and using a regular expression object, `match()` is not because it returns a list of patterns that matched, which is not possible with a regular expression object. Parsing data against multiple choices is a frequent operation in the wild and `RE2.Set` is a fast way to do it. * `new RE2.Set(patterns[, flagsOrOptions][, options])` * `patterns` is any iterable of strings, `Buffer`s, `RegExp`, or `RE2` instances; flags (if provided) apply to the whole set. * `flagsOrOptions` can be a string/`Buffer` with standard flags (`i`, `m`, `s`, `u`, `g`, `y`, `d`). * `options.anchor` can be `'unanchored'` (default), `'start'`, or `'both'`. * `set.test(str)` returns `true` if any pattern matches and `false` otherwise. * `set.match(str)` returns an array of indexes of matching patterns. * This is an array of integer indices of patterns that matched sorted in ascending order. * If no patterns matched, an empty array is returned. * Read-only properties: * `set.size` (number of patterns), `set.flags` (`RegExp` flags as a string), `set.anchor` (anchor mode as a string) * `set.source` (all patterns joined with `|` as a string), `set.sources` (individual pattern sources as an array of strings) It is based on [RE2::Set](https://github.com/google/re2/blob/main/re2/set.h). Example: ```js const routes = new RE2.Set([ '^/users/\\d+$', '^/posts/\\d+$' ], 'i', {anchor: 'start'}); routes.test('/users/7'); // true routes.match('/posts/42'); // [1] routes.sources; // ['^/users/\\d+$', '^/posts/\\d+$'] routes.toString(); // '/^/users/\\d+$|^/posts/\\d+$/iu' ``` To run the bundled benchmark, use the following command (make sure that node-re2 is properly built before): ```bash npx nano-bench bench/set-match.mjs ``` ### Calculate length Two functions to calculate string sizes between [UTF-8](http://en.wikipedia.org/wiki/UTF-8) and [UTF-16](http://en.wikipedia.org/wiki/UTF-16) are exposed on `RE2`: * `RE2.getUtf8Length(str)` — calculates a buffer size in bytes to encode a UTF-16 string as a UTF-8 buffer. * `RE2.getUtf16Length(buf)` — calculates a string size in characters to encode a UTF-8 buffer as a UTF-16 string. JavaScript supports UCS-2 strings with 16-bit characters, while node.js 0.11 supports full UTF-16 as a default string. ### Property: `internalSource` Starting 1.8.0 property `source` emulates the same property of `RegExp`, meaning that it can be used to create an identical `RE2` or `RegExp` instance. Sometimes, for troubleshooting purposes, a user wants to inspect a `RE2` translated source. It is available as a read-only property called `internalSource`. ### Unicode warning level `RE2` engine always works in the Unicode mode. In most cases either there is no difference or the Unicode mode is actually preferred. But sometimes a user wants a tight control over their regular expressions. For those cases, there is a static string property `RE2.unicodeWarningLevel`. Regular expressions in the Unicode mode work as usual. But if a regular expression lacks the Unicode flag, it is always added silently. ```js const x = /./; x.flags; // '' const y = new RE2(x); y.flags; // 'u' ``` In the latter case `RE2` can do following actions depending on `RE2.unicodeWarningLevel`: * `'nothing'` (the default): no warnings or notifications of any kind, a regular expression will be created with `'u'` flag. * `'warnOnce'`: warns exactly once the very first time, a regular expression will be created with `'u'` flag. * Assigning this value resets an internal flag, so `RE2` will warn once again. * `'warn'`: warns every time, a regular expression will be created with `'u'` flag. * `'throw'`: throws a `SyntaxError` every time. * All other warning level values are silently ignored on asignment leaving the previous value unchanged. Warnings and exceptions help to audit an application for stray non-Unicode regular expressions. `RE2.unicodeWarningLevel` is a global property. Be careful manipulating it in a multi-threaded environment as it is shared between threads. ## How to install Installation: ```bash npm install --save re2 ``` While the project is known to work with other package managers, it is not guaranteed nor tested. For example, [yarn](https://yarnpkg.com/) is known to fail in some scenarios (see this [Wiki article](https://github.com/uhop/node-re2/wiki/Problem:-unusual-errors-with-yarn)). ### Precompiled artifacts When installing re2 the [install script](https://github.com/uhop/install-artifact-from-github/blob/master/bin/install-from-cache.js) attempts to download a prebuilt artifact for your system from the Github releases. The download location can be overridden by setting the `RE2_DOWNLOAD_MIRROR` environment variable as seen in the install script. If all attempts to download the prebuilt artifact for your system fails the script attempts to built re2 locally on your machine using [node-gyp](https://github.com/nodejs/node-gyp). ## How to use It is used just like a `RegExp` object. ```js var RE2 = require("re2"); // with default flags var re = new RE2("a(b*)"); var result = re.exec("abbc"); console.log(result[0]); // "abb" console.log(result[1]); // "bb" result = re.exec("aBbC"); console.log(result[0]); // "a" console.log(result[1]); // "" // with explicit flags re = new RE2("a(b*)", "i"); result = re.exec("aBbC"); console.log(result[0]); // "aBb" console.log(result[1]); // "Bb" // from regular expression object var regexp = new RegExp("a(b*)", "i"); re = new RE2(regexp); result = re.exec("aBbC"); console.log(result[0]); // "aBb" console.log(result[1]); // "Bb" // from regular expression literal re = new RE2(/a(b*)/i); result = re.exec("aBbC"); console.log(result[0]); // "aBb" console.log(result[1]); // "Bb" // from another RE2 object var rex = new RE2(re); result = rex.exec("aBbC"); console.log(result[0]); // "aBb" console.log(result[1]); // "Bb" // shortcut result = new RE2("ab*").exec("abba"); // factory result = RE2("ab*").exec("abba"); ``` ## Limitations (things RE2 does not support) `RE2` consciously avoids any regular expression features that require worst-case exponential time to evaluate. These features are essentially those that describe a Context-Free Language (CFL) rather than a Regular Expression, and are extensions to the traditional regular expression language because some people don't know when enough is enough. The most noteworthy missing features are backreferences and lookahead assertions. If your application uses these features, you should continue to use `RegExp`. But since these features are fundamentally vulnerable to [ReDoS](https://www.owasp.org/index.php/Regular_expression_Denial_of_Service_-_ReDoS), you should strongly consider replacing them. `RE2` will throw a `SyntaxError` if you try to declare a regular expression using these features. If you are evaluating an externally-provided regular expression, wrap your `RE2` declarations in a try-catch block. It allows to use `RegExp`, when `RE2` misses a feature: ```js var re = /(a)+(b)*/; try { re = new RE2(re); // use RE2 as a drop-in replacement } catch (e) { // suppress an error, and use // the original RegExp } var result = re.exec(sample); ``` In addition to these missing features, `RE2` also behaves somewhat differently from the built-in regular expression engine in corner cases. ### Backreferences `RE2` doesn't support backreferences, which are numbered references to previously matched groups, like so: `\1`, `\2`, and so on. Example of backrefrences: ```js /(cat|dog)\1/.test("catcat"); // true /(cat|dog)\1/.test("dogdog"); // true /(cat|dog)\1/.test("catdog"); // false /(cat|dog)\1/.test("dogcat"); // false ``` ### Lookahead assertions `RE2` doesn't support lookahead assertions, which are ways to allow a matching dependent on subsequent contents. ```js /abc(?=def)/; // match abc only if it is followed by def /abc(?!def)/; // match abc only if it is not followed by def ``` ### Mismatched behavior `RE2` and the built-in regex engines disagree a bit. Before you switch to `RE2`, verify that your regular expressions continue to work as expected. They should do so in the vast majority of cases. Here is an example of a case where they may not: ```js var RE2 = require("../re2"); var pattern = '(?:(a)|(b)|(c))+'; var built_in = new RegExp(pattern); var re2 = new RE2(pattern); var input = 'abc'; var bi_res = built_in.exec(input); var re2_res = re2.exec(input); console.log('bi_res: ' + bi_res); // prints: bi_res: abc,,,c console.log('re2_res : ' + re2_res); // prints: re2_res : abc,a,b,c ``` ### Unicode `RE2` always works in the Unicode mode. See `RE2.unicodeWarningLevel` above for more details on how to control warnings about this feature. #### Unicode classes `\p{...}` and `\P{...}` `RE2` supports a subset of Unicode classes as defined in [RE2 Syntax](https://github.com/google/re2/wiki/Syntax). Native Google RE2 supports only short names, e.g., `L` for `Letter`, `N` for `Number`, etc. Like `RegExp`, `RE2` supports both short and long names, e.g., `Letter` for `L`, by translating them to short names. Generally, the extended form `\p{name=value}` is not supported. Only form `\p{name}` is supported. The exception is `Script` and `sc` names, e.g., `\p{Script=Latin}` and `\p{sc=Cyrillic}`. The same applies to `\P{...}`. ## Release history - 1.23.3 *Updated Abseil and dev dependencies.* - 1.23.2 *Updated dev dependencies.* - 1.23.1 *Updated Abseil and dev dependencies.* - 1.23.0 *Updated all dependencies, upgraded tooling. New feature: `RE2.Set` (thx, [Wes](https://github.com/wrmedford)).* - 1.22.3 *Technical release: upgraded QEMU emulations to native ARM runners to speed up the build process.* - 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)).* - 1.22.1 *Added support for translation of scripts as Unicode classes.* - 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).* - 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)).* - 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.* - 1.21.3 *Fixed an empty string regression reported by [Rhys Arkins](https://github.com/rarkins), thx! Updated deps.* - 1.21.2 *Fixed another memory regression reported by [matthewvalentine](https://github.com/matthewvalentine), thx! Updated deps. Added more tests and benchmarks.* - 1.21.1 *Fixed a memory regression reported by [matthewvalentine](https://github.com/matthewvalentine), thx! Updated deps.* - 1.21.0 *Fixed the performance problem reported by [matthewvalentine](https://github.com/matthewvalentine) (thx!). The change improves performance for multiple use cases.* - 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.* - 1.20.11 *Updated deps. Added support for Node 22 (thx, [Elton Leong](https://github.com/eltonkl)).* - 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)).* - 1.20.9 *Updated deps. Added more `absail-cpp` files that manifested itself on NixOS. Thx, [Laura Hausmann](https://github.com/zotanmew).* - 1.20.8 *Updated deps: `install-artifact-from-github`. A default HTTPS agent is used for fetching precompiled artifacts avoiding unnecessary long wait times.* - 1.20.7 *Added more `absail-cpp` files that manifested itself on ARM Alpine. Thx, [Laura Hausmann](https://github.com/zotanmew).* - 1.20.6 *Updated deps, notably `node-gyp`.* - 1.20.5 *Updated deps, added Node 21 and retired Node 16 as pre-compilation targets.* - 1.20.4 *Updated deps. Fix: the 2nd argument of the constructor overrides flags. Thx, [gost-serb](https://github.com/gost-serb).* - 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).* - 1.20.2 *Fix: added a missing C++ file, which caused a bug on Alpine Linux. Thx, [rbitanga-manticore](https://github.com/rbitanga-manticore).* - 1.20.1 *Fix: files included in the npm package to build the C++ code.* - 1.20.0 *Updated RE2. New version uses `abseil-cpp` and required the adaptation work. Thx, [Stefano Rivera](https://github.com/stefanor).* The rest can be consulted in the project's wiki [Release history](https://github.com/uhop/node-re2/wiki/Release-history). ## License BSD-3-Clause uhop-node-re2-e601e5a/bench/000077500000000000000000000000001514245257200155745ustar00rootroot00000000000000uhop-node-re2-e601e5a/bench/bad-pattern.mjs000066400000000000000000000007521514245257200205140ustar00rootroot00000000000000import {default as RE2} from '../re2.js'; const BAD_PATTERN = '([a-z]+)+$'; const BAD_INPUT = 'a'.repeat(10) + '!'; const regExp = new RegExp(BAD_PATTERN); const re2 = new RE2(BAD_PATTERN); export default { RegExp: n => { let count = 0; for (let i = 0; i < n; ++i) { if (regExp.test(BAD_INPUT)) ++count; } return count; }, RE2: n => { let count = 0; for (let i = 0; i < n; ++i) { if (re2.test(BAD_INPUT)) ++count; } return count; } }; uhop-node-re2-e601e5a/bench/set-match.mjs000066400000000000000000000026111514245257200201740ustar00rootroot00000000000000import {default as RE2} from '../re2.js'; const PATTERN_COUNT = 200; const patterns = []; for (let i = 0; i < PATTERN_COUNT; ++i) { patterns.push('token' + i + '(?:[a-z]+)?'); } const INPUT_COUNT = 500; const inputs = []; for (let j = 0; j < INPUT_COUNT; ++j) { inputs.push( 'xx' + (j % PATTERN_COUNT) + ' ' + (j & 7) + ' token' + (j % PATTERN_COUNT) + ' tail' ); } const re2Set = new RE2.Set(patterns); const re2List = patterns.map(p => new RE2(p)); const jsList = patterns.map(p => new RegExp(p)); export default { RegExp: n => { let count = 0; for (let i = 0; i < n; ++i) { for (const input of inputs) { const matches = []; for (const pattern of jsList) { if (pattern.test(input)) matches.push(pattern); } count += matches.length; } } return count; }, RE2: n => { let count = 0; for (let i = 0; i < n; ++i) { for (const input of inputs) { const matches = []; for (const pattern of re2List) { if (pattern.test(input)) matches.push(pattern); } count += matches.length; } } return count; }, 'RE2.Set': n => { let count = 0; for (let i = 0; i < n; ++i) { for (const input of inputs) { const matches = re2Set.match(input); count += matches.length; } } return count; } }; uhop-node-re2-e601e5a/binding.gyp000066400000000000000000000167611514245257200166630ustar00rootroot00000000000000{ "targets": [ { "target_name": "re2", "sources": [ "lib/addon.cc", "lib/accessors.cc", "lib/pattern.cc", "lib/util.cc", "lib/new.cc", "lib/exec.cc", "lib/test.cc", "lib/match.cc", "lib/replace.cc", "lib/search.cc", "lib/split.cc", "lib/to_string.cc", "lib/set.cc", "vendor/re2/re2/bitmap256.cc", "vendor/re2/re2/bitstate.cc", "vendor/re2/re2/compile.cc", "vendor/re2/re2/dfa.cc", "vendor/re2/re2/filtered_re2.cc", "vendor/re2/re2/mimics_pcre.cc", "vendor/re2/re2/nfa.cc", "vendor/re2/re2/onepass.cc", "vendor/re2/re2/parse.cc", "vendor/re2/re2/perl_groups.cc", "vendor/re2/re2/prefilter.cc", "vendor/re2/re2/prefilter_tree.cc", "vendor/re2/re2/prog.cc", "vendor/re2/re2/re2.cc", "vendor/re2/re2/regexp.cc", "vendor/re2/re2/set.cc", "vendor/re2/re2/simplify.cc", "vendor/re2/re2/tostring.cc", "vendor/re2/re2/unicode_casefold.cc", "vendor/re2/re2/unicode_groups.cc", "vendor/re2/util/pcre.cc", "vendor/re2/util/rune.cc", "vendor/re2/util/strutil.cc", "vendor/abseil-cpp/absl/base/internal/cycleclock.cc", "vendor/abseil-cpp/absl/base/internal/low_level_alloc.cc", "vendor/abseil-cpp/absl/base/internal/raw_logging.cc", "vendor/abseil-cpp/absl/base/internal/spinlock.cc", "vendor/abseil-cpp/absl/base/internal/spinlock_wait.cc", "vendor/abseil-cpp/absl/base/internal/strerror.cc", "vendor/abseil-cpp/absl/base/internal/sysinfo.cc", "vendor/abseil-cpp/absl/base/internal/thread_identity.cc", "vendor/abseil-cpp/absl/base/internal/throw_delegate.cc", "vendor/abseil-cpp/absl/base/internal/unscaledcycleclock.cc", "vendor/abseil-cpp/absl/container/internal/hashtablez_sampler.cc", "vendor/abseil-cpp/absl/container/internal/hashtablez_sampler_force_weak_definition.cc", "vendor/abseil-cpp/absl/container/internal/raw_hash_set.cc", "vendor/abseil-cpp/absl/debugging/internal/borrowed_fixup_buffer.cc", "vendor/abseil-cpp/absl/debugging/internal/decode_rust_punycode.cc", "vendor/abseil-cpp/absl/debugging/internal/demangle.cc", "vendor/abseil-cpp/absl/debugging/internal/demangle_rust.cc", "vendor/abseil-cpp/absl/debugging/internal/address_is_readable.cc", "vendor/abseil-cpp/absl/debugging/internal/elf_mem_image.cc", "vendor/abseil-cpp/absl/debugging/internal/examine_stack.cc", "vendor/abseil-cpp/absl/debugging/internal/utf8_for_code_point.cc", "vendor/abseil-cpp/absl/debugging/internal/vdso_support.cc", "vendor/abseil-cpp/absl/debugging/stacktrace.cc", "vendor/abseil-cpp/absl/debugging/symbolize.cc", "vendor/abseil-cpp/absl/flags/commandlineflag.cc", "vendor/abseil-cpp/absl/flags/internal/commandlineflag.cc", "vendor/abseil-cpp/absl/flags/internal/flag.cc", "vendor/abseil-cpp/absl/flags/internal/private_handle_accessor.cc", "vendor/abseil-cpp/absl/flags/internal/program_name.cc", "vendor/abseil-cpp/absl/flags/marshalling.cc", "vendor/abseil-cpp/absl/flags/reflection.cc", "vendor/abseil-cpp/absl/flags/usage_config.cc", "vendor/abseil-cpp/absl/hash/internal/city.cc", "vendor/abseil-cpp/absl/hash/internal/hash.cc", "vendor/abseil-cpp/absl/log/internal/globals.cc", "vendor/abseil-cpp/absl/log/internal/log_format.cc", "vendor/abseil-cpp/absl/log/internal/log_message.cc", "vendor/abseil-cpp/absl/log/internal/log_sink_set.cc", "vendor/abseil-cpp/absl/log/internal/nullguard.cc", "vendor/abseil-cpp/absl/log/internal/proto.cc", "vendor/abseil-cpp/absl/log/internal/structured_proto.cc", "vendor/abseil-cpp/absl/log/globals.cc", "vendor/abseil-cpp/absl/log/log_sink.cc", "vendor/abseil-cpp/absl/numeric/int128.cc", "vendor/abseil-cpp/absl/strings/ascii.cc", "vendor/abseil-cpp/absl/strings/charconv.cc", "vendor/abseil-cpp/absl/strings/internal/charconv_bigint.cc", "vendor/abseil-cpp/absl/strings/internal/charconv_parse.cc", "vendor/abseil-cpp/absl/strings/internal/memutil.cc", "vendor/abseil-cpp/absl/strings/internal/str_format/arg.cc", "vendor/abseil-cpp/absl/strings/internal/str_format/bind.cc", "vendor/abseil-cpp/absl/strings/internal/str_format/extension.cc", "vendor/abseil-cpp/absl/strings/internal/str_format/float_conversion.cc", "vendor/abseil-cpp/absl/strings/internal/str_format/output.cc", "vendor/abseil-cpp/absl/strings/internal/str_format/parser.cc", "vendor/abseil-cpp/absl/strings/internal/utf8.cc", "vendor/abseil-cpp/absl/strings/match.cc", "vendor/abseil-cpp/absl/strings/numbers.cc", "vendor/abseil-cpp/absl/strings/str_cat.cc", "vendor/abseil-cpp/absl/strings/str_split.cc", "vendor/abseil-cpp/absl/synchronization/internal/create_thread_identity.cc", "vendor/abseil-cpp/absl/synchronization/internal/graphcycles.cc", "vendor/abseil-cpp/absl/synchronization/internal/futex_waiter.cc", "vendor/abseil-cpp/absl/synchronization/internal/kernel_timeout.cc", "vendor/abseil-cpp/absl/synchronization/internal/per_thread_sem.cc", "vendor/abseil-cpp/absl/synchronization/internal/waiter_base.cc", "vendor/abseil-cpp/absl/synchronization/mutex.cc", "vendor/abseil-cpp/absl/time/clock.cc", "vendor/abseil-cpp/absl/time/duration.cc", "vendor/abseil-cpp/absl/time/internal/cctz/src/time_zone_fixed.cc", "vendor/abseil-cpp/absl/time/internal/cctz/src/time_zone_if.cc", "vendor/abseil-cpp/absl/time/internal/cctz/src/time_zone_impl.cc", "vendor/abseil-cpp/absl/time/internal/cctz/src/time_zone_info.cc", "vendor/abseil-cpp/absl/time/internal/cctz/src/time_zone_libc.cc", "vendor/abseil-cpp/absl/time/internal/cctz/src/time_zone_lookup.cc", "vendor/abseil-cpp/absl/time/internal/cctz/src/time_zone_posix.cc", "vendor/abseil-cpp/absl/time/internal/cctz/src/zone_info_source.cc", "vendor/abseil-cpp/absl/time/time.cc", ], "cflags": [ "-std=c++2a", "-Wall", "-Wextra", "-Wno-sign-compare", "-Wno-unused-parameter", "-Wno-missing-field-initializers", "-Wno-cast-function-type", "-O3", "-g" ], "defines": [ "NDEBUG", "NOMINMAX" ], "include_dirs": [ " #include #include NAN_GETTER(WrappedRE2::GetSource) { if (!WrappedRE2::HasInstance(info.This())) { info.GetReturnValue().Set(Nan::New("(?:)").ToLocalChecked()); return; } auto re2 = Nan::ObjectWrap::Unwrap(info.This()); info.GetReturnValue().Set(Nan::New(re2->source).ToLocalChecked()); } NAN_GETTER(WrappedRE2::GetInternalSource) { if (!WrappedRE2::HasInstance(info.This())) { info.GetReturnValue().Set(Nan::New("(?:)").ToLocalChecked()); return; } auto re2 = Nan::ObjectWrap::Unwrap(info.This()); info.GetReturnValue().Set(Nan::New(re2->regexp.pattern()).ToLocalChecked()); } NAN_GETTER(WrappedRE2::GetFlags) { if (!WrappedRE2::HasInstance(info.This())) { info.GetReturnValue().Set(Nan::New("").ToLocalChecked()); return; } auto re2 = Nan::ObjectWrap::Unwrap(info.This()); std::string flags; if (re2->hasIndices) { flags += "d"; } if (re2->global) { flags += "g"; } if (re2->ignoreCase) { flags += "i"; } if (re2->multiline) { flags += "m"; } if (re2->dotAll) { flags += "s"; } flags += "u"; if (re2->sticky) { flags += "y"; } info.GetReturnValue().Set(Nan::New(flags).ToLocalChecked()); } NAN_GETTER(WrappedRE2::GetGlobal) { if (!WrappedRE2::HasInstance(info.This())) { info.GetReturnValue().SetUndefined(); return; } auto re2 = Nan::ObjectWrap::Unwrap(info.This()); info.GetReturnValue().Set(re2->global); } NAN_GETTER(WrappedRE2::GetIgnoreCase) { if (!WrappedRE2::HasInstance(info.This())) { info.GetReturnValue().SetUndefined(); return; } auto re2 = Nan::ObjectWrap::Unwrap(info.This()); info.GetReturnValue().Set(re2->ignoreCase); } NAN_GETTER(WrappedRE2::GetMultiline) { if (!WrappedRE2::HasInstance(info.This())) { info.GetReturnValue().SetUndefined(); return; } auto re2 = Nan::ObjectWrap::Unwrap(info.This()); info.GetReturnValue().Set(re2->multiline); } NAN_GETTER(WrappedRE2::GetDotAll) { if (!WrappedRE2::HasInstance(info.This())) { info.GetReturnValue().SetUndefined(); return; } auto re2 = Nan::ObjectWrap::Unwrap(info.This()); info.GetReturnValue().Set(re2->dotAll); } NAN_GETTER(WrappedRE2::GetUnicode) { if (!WrappedRE2::HasInstance(info.This())) { info.GetReturnValue().SetUndefined(); return; } info.GetReturnValue().Set(true); } NAN_GETTER(WrappedRE2::GetSticky) { if (!WrappedRE2::HasInstance(info.This())) { info.GetReturnValue().SetUndefined(); return; } auto re2 = Nan::ObjectWrap::Unwrap(info.This()); info.GetReturnValue().Set(re2->sticky); } NAN_GETTER(WrappedRE2::GetHasIndices) { if (!WrappedRE2::HasInstance(info.This())) { info.GetReturnValue().SetUndefined(); return; } auto re2 = Nan::ObjectWrap::Unwrap(info.This()); info.GetReturnValue().Set(re2->hasIndices); } NAN_GETTER(WrappedRE2::GetLastIndex) { if (!WrappedRE2::HasInstance(info.This())) { info.GetReturnValue().SetUndefined(); return; } auto re2 = Nan::ObjectWrap::Unwrap(info.This()); info.GetReturnValue().Set(static_cast(re2->lastIndex)); } NAN_SETTER(WrappedRE2::SetLastIndex) { if (!WrappedRE2::HasInstance(info.This())) { return Nan::ThrowTypeError("Cannot set lastIndex of an invalid RE2 object."); } auto re2 = Nan::ObjectWrap::Unwrap(info.This()); if (value->IsNumber()) { int n = value->NumberValue(Nan::GetCurrentContext()).FromMaybe(0); re2->lastIndex = n <= 0 ? 0 : n; } } WrappedRE2::UnicodeWarningLevels WrappedRE2::unicodeWarningLevel; NAN_GETTER(WrappedRE2::GetUnicodeWarningLevel) { std::string level; switch (unicodeWarningLevel) { case THROW: level = "throw"; break; case WARN: level = "warn"; break; case WARN_ONCE: level = "warnOnce"; break; default: level = "nothing"; break; } info.GetReturnValue().Set(Nan::New(level).ToLocalChecked()); } NAN_SETTER(WrappedRE2::SetUnicodeWarningLevel) { if (value->IsString()) { Nan::Utf8String s(value); if (!strcmp(*s, "throw")) { unicodeWarningLevel = THROW; return; } if (!strcmp(*s, "warn")) { unicodeWarningLevel = WARN; return; } if (!strcmp(*s, "warnOnce")) { unicodeWarningLevel = WARN_ONCE; alreadyWarnedAboutUnicode = false; return; } if (!strcmp(*s, "nothing")) { unicodeWarningLevel = NOTHING; return; } } } uhop-node-re2-e601e5a/lib/addon.cc000066400000000000000000000140041514245257200166560ustar00rootroot00000000000000#include "./wrapped_re2.h" #include "./wrapped_re2_set.h" static NAN_METHOD(GetUtf8Length) { auto t = info[0]->ToString(Nan::GetCurrentContext()); if (t.IsEmpty()) { return; } auto s = t.ToLocalChecked(); info.GetReturnValue().Set(static_cast(s->Utf8Length(v8::Isolate::GetCurrent()))); } static NAN_METHOD(GetUtf16Length) { if (node::Buffer::HasInstance(info[0])) { const auto *s = node::Buffer::Data(info[0]); info.GetReturnValue().Set(static_cast(getUtf16Length(s, s + node::Buffer::Length(info[0])))); return; } info.GetReturnValue().Set(-1); } static void cleanup(void *p) { v8::Isolate *isolate = static_cast(p); auto p_tpl = Nan::GetIsolateData>(isolate); delete p_tpl; } // NAN_MODULE_INIT(WrappedRE2::Init) v8::Local WrappedRE2::Init() { Nan::EscapableHandleScope scope; // prepare constructor template auto tpl = Nan::New(New); tpl->SetClassName(Nan::New("RE2").ToLocalChecked()); auto instanceTemplate = tpl->InstanceTemplate(); instanceTemplate->SetInternalFieldCount(1); // save the template auto isolate = v8::Isolate::GetCurrent(); auto p_tpl = new Nan::Persistent(tpl); Nan::SetIsolateData(isolate, p_tpl); node::AddEnvironmentCleanupHook(isolate, cleanup, isolate); // prototype Nan::SetPrototypeMethod(tpl, "toString", ToString); Nan::SetPrototypeMethod(tpl, "exec", Exec); Nan::SetPrototypeMethod(tpl, "test", Test); Nan::SetPrototypeMethod(tpl, "match", Match); Nan::SetPrototypeMethod(tpl, "replace", Replace); Nan::SetPrototypeMethod(tpl, "search", Search); Nan::SetPrototypeMethod(tpl, "split", Split); Nan::SetPrototypeTemplate(tpl, "source", Nan::New("(?:)").ToLocalChecked()); Nan::SetPrototypeTemplate(tpl, "flags", Nan::New("").ToLocalChecked()); Nan::SetAccessor(instanceTemplate, Nan::New("source").ToLocalChecked(), GetSource); Nan::SetAccessor(instanceTemplate, Nan::New("flags").ToLocalChecked(), GetFlags); Nan::SetAccessor(instanceTemplate, Nan::New("global").ToLocalChecked(), GetGlobal); Nan::SetAccessor(instanceTemplate, Nan::New("ignoreCase").ToLocalChecked(), GetIgnoreCase); Nan::SetAccessor(instanceTemplate, Nan::New("multiline").ToLocalChecked(), GetMultiline); Nan::SetAccessor(instanceTemplate, Nan::New("dotAll").ToLocalChecked(), GetDotAll); Nan::SetAccessor(instanceTemplate, Nan::New("unicode").ToLocalChecked(), GetUnicode); Nan::SetAccessor(instanceTemplate, Nan::New("sticky").ToLocalChecked(), GetSticky); Nan::SetAccessor(instanceTemplate, Nan::New("hasIndices").ToLocalChecked(), GetHasIndices); Nan::SetAccessor(instanceTemplate, Nan::New("lastIndex").ToLocalChecked(), GetLastIndex, SetLastIndex); Nan::SetAccessor(instanceTemplate, Nan::New("internalSource").ToLocalChecked(), GetInternalSource); auto ctr = Nan::GetFunction(tpl).ToLocalChecked(); auto setCtr = WrappedRE2Set::Init(); Nan::Set(ctr, Nan::New("Set").ToLocalChecked(), setCtr); // properties Nan::Export(ctr, "getUtf8Length", GetUtf8Length); Nan::Export(ctr, "getUtf16Length", GetUtf16Length); Nan::SetAccessor(v8::Local(ctr), Nan::New("unicodeWarningLevel").ToLocalChecked(), GetUnicodeWarningLevel, SetUnicodeWarningLevel); return scope.Escape(ctr); } NODE_MODULE_INIT() { Nan::HandleScope scope; Nan::Set(module->ToObject(context).ToLocalChecked(), Nan::New("exports").ToLocalChecked(), WrappedRE2::Init()); } WrappedRE2::~WrappedRE2() { dropCache(); } // private methods void WrappedRE2::dropCache() { if (!lastString.IsEmpty()) { // lastString.ClearWeak(); lastString.Reset(); } if (!lastCache.IsEmpty()) { // lastCache.ClearWeak(); lastCache.Reset(); } lastStringValue.clear(); } const StrVal &WrappedRE2::prepareArgument(const v8::Local &arg, bool ignoreLastIndex) { size_t startFrom = ignoreLastIndex ? 0 : lastIndex; if (!lastString.IsEmpty()) { lastString.ClearWeak(); } if (!lastCache.IsEmpty()) { lastCache.ClearWeak(); } if (lastString == arg && !node::Buffer::HasInstance(arg) && !lastCache.IsEmpty()) { // we have a properly cached string lastStringValue.setIndex(startFrom); return lastStringValue; } dropCache(); if (node::Buffer::HasInstance(arg)) { // no need to cache buffers lastString.Reset(arg); auto argSize = node::Buffer::Length(arg); lastStringValue.reset(arg, argSize, argSize, startFrom, true); return lastStringValue; } // caching the string auto t = arg->ToString(Nan::GetCurrentContext()); if (t.IsEmpty()) { // do not process bad strings lastStringValue.isBad = true; return lastStringValue; } lastString.Reset(arg); auto isolate = v8::Isolate::GetCurrent(); auto s = t.ToLocalChecked(); auto argLength = s->Utf8Length(isolate); auto buffer = node::Buffer::New(isolate, s).ToLocalChecked(); lastCache.Reset(buffer); auto argSize = node::Buffer::Length(buffer); lastStringValue.reset(buffer, argSize, argLength, startFrom); return lastStringValue; }; void WrappedRE2::doneWithLastString() { if (!lastString.IsEmpty()) { static_cast &>(lastString).SetWeak(); } if (!lastCache.IsEmpty()) { static_cast &>(lastCache).SetWeak(); } } // StrVal void StrVal::setIndex(size_t newIndex) { isValidIndex = newIndex <= length; if (!isValidIndex) { index = newIndex; byteIndex = 0; return; } if (newIndex == index) return; if (isBuffer) { byteIndex = index = newIndex; return; } // String if (!newIndex) { byteIndex = index = 0; return; } if (newIndex == length) { byteIndex = size; index = length; return; } byteIndex = index < newIndex ? getUtf16PositionByCounter(data, byteIndex, newIndex - index) : getUtf16PositionByCounter(data, 0, newIndex); index = newIndex; } static char null_buffer[] = {'\0'}; void StrVal::reset(const v8::Local &arg, size_t argSize, size_t argLength, size_t newIndex, bool buffer) { clear(); isBuffer = buffer; size = argSize; length = argLength; data = size ? node::Buffer::Data(arg) : null_buffer; setIndex(newIndex); } uhop-node-re2-e601e5a/lib/exec.cc000066400000000000000000000104651514245257200165240ustar00rootroot00000000000000#include "./wrapped_re2.h" #include NAN_METHOD(WrappedRE2::Exec) { // unpack arguments auto re2 = Nan::ObjectWrap::Unwrap(info.This()); if (!re2) { info.GetReturnValue().SetNull(); return; } PrepareLastString prep(re2, info[0]); StrVal& str = prep; if (str.isBad) return; // throws an exception if (re2->global || re2->sticky) { if (!str.isValidIndex) { re2->lastIndex = 0; info.GetReturnValue().SetNull(); return; } } // actual work std::vector groups(re2->regexp.NumberOfCapturingGroups() + 1); if (!re2->regexp.Match(str, str.byteIndex, str.size, re2->sticky ? re2::RE2::ANCHOR_START : re2::RE2::UNANCHORED, &groups[0], groups.size())) { if (re2->global || re2->sticky) { re2->lastIndex = 0; } info.GetReturnValue().SetNull(); return; } // form a result auto result = Nan::New(), indices = Nan::New(); int indexOffset = re2->global || re2->sticky ? re2->lastIndex : 0; if (str.isBuffer) { for (size_t i = 0, n = groups.size(); i < n; ++i) { const auto &item = groups[i]; const auto data = item.data(); if (data) { Nan::Set(result, i, Nan::CopyBuffer(data, item.size()).ToLocalChecked()); if (re2->hasIndices) { auto pair = Nan::New(); auto offset = data - str.data - str.byteIndex; auto length = item.size(); Nan::Set(pair, 0, Nan::New(indexOffset + static_cast(offset))); Nan::Set(pair, 1, Nan::New(indexOffset + static_cast(offset + length))); Nan::Set(indices, i, pair); } } else { Nan::Set(result, i, Nan::Undefined()); if (re2->hasIndices) { Nan::Set(indices, i, Nan::Undefined()); } } } Nan::Set(result, Nan::New("index").ToLocalChecked(), Nan::New(indexOffset + static_cast(groups[0].data() - str.data - str.byteIndex))); } else { for (size_t i = 0, n = groups.size(); i < n; ++i) { const auto &item = groups[i]; const auto data = item.data(); if (data) { Nan::Set(result, i, Nan::New(data, item.size()).ToLocalChecked()); if (re2->hasIndices) { auto pair = Nan::New(); auto offset = getUtf16Length(str.data + str.byteIndex, data); auto length = getUtf16Length(data, data + item.size()); Nan::Set(pair, 0, Nan::New(indexOffset + static_cast(offset))); Nan::Set(pair, 1, Nan::New(indexOffset + static_cast(offset + length))); Nan::Set(indices, i, pair); } } else { Nan::Set(result, i, Nan::Undefined()); if (re2->hasIndices) { Nan::Set(indices, i, Nan::Undefined()); } } } Nan::Set( result, Nan::New("index").ToLocalChecked(), Nan::New(indexOffset + static_cast(getUtf16Length(str.data + str.byteIndex, groups[0].data())))); } if (re2->global || re2->sticky) { re2->lastIndex += str.isBuffer ? groups[0].data() - str.data + groups[0].size() - str.byteIndex : getUtf16Length(str.data + str.byteIndex, groups[0].data() + groups[0].size()); } Nan::Set(result, Nan::New("input").ToLocalChecked(), info[0]); const auto &groupNames = re2->regexp.CapturingGroupNames(); if (!groupNames.empty()) { auto groups = Nan::New(); Nan::SetPrototype(groups, Nan::Null()); for (auto group : groupNames) { auto value = Nan::Get(result, group.first); if (!value.IsEmpty()) { Nan::Set(groups, Nan::New(group.second).ToLocalChecked(), value.ToLocalChecked()); } } Nan::Set(result, Nan::New("groups").ToLocalChecked(), groups); if (re2->hasIndices) { auto indexGroups = Nan::New(); Nan::SetPrototype(indexGroups, Nan::Null()); for (auto group : groupNames) { auto value = Nan::Get(indices, group.first); if (!value.IsEmpty()) { Nan::Set(indexGroups, Nan::New(group.second).ToLocalChecked(), value.ToLocalChecked()); } } Nan::Set(indices, Nan::New("groups").ToLocalChecked(), indexGroups); } } else { Nan::Set(result, Nan::New("groups").ToLocalChecked(), Nan::Undefined()); if (re2->hasIndices) { Nan::Set(indices, Nan::New("groups").ToLocalChecked(), Nan::Undefined()); } } if (re2->hasIndices) { Nan::Set(result, Nan::New("indices").ToLocalChecked(), indices); } info.GetReturnValue().Set(result); } uhop-node-re2-e601e5a/lib/match.cc000066400000000000000000000115261514245257200166730ustar00rootroot00000000000000#include "./wrapped_re2.h" #include NAN_METHOD(WrappedRE2::Match) { // unpack arguments auto re2 = Nan::ObjectWrap::Unwrap(info.This()); if (!re2) { info.GetReturnValue().SetNull(); return; } PrepareLastString prep(re2, info[0]); StrVal& str = prep; if (str.isBad) return; // throws an exception if (!str.isValidIndex) { re2->lastIndex = 0; info.GetReturnValue().SetNull(); return; } std::vector groups; size_t byteIndex = 0; auto anchor = re2::RE2::UNANCHORED; // actual work if (re2->global) { // global: collect all matches re2::StringPiece match; if (re2->sticky) { anchor = re2::RE2::ANCHOR_START; } while (re2->regexp.Match(str, byteIndex, str.size, anchor, &match, 1)) { groups.push_back(match); byteIndex = match.data() - str.data + match.size(); } if (groups.empty()) { info.GetReturnValue().SetNull(); return; } } else { // non-global: just like exec() if (re2->sticky) { byteIndex = str.byteIndex; anchor = RE2::ANCHOR_START; } groups.resize(re2->regexp.NumberOfCapturingGroups() + 1); if (!re2->regexp.Match(str, byteIndex, str.size, anchor, &groups[0], groups.size())) { if (re2->sticky) re2->lastIndex = 0; info.GetReturnValue().SetNull(); return; } } // form a result auto result = Nan::New(), indices = Nan::New(); if (str.isBuffer) { for (size_t i = 0, n = groups.size(); i < n; ++i) { const auto &item = groups[i]; const auto data = item.data(); if (data) { Nan::Set(result, i, Nan::CopyBuffer(data, item.size()).ToLocalChecked()); if (!re2->global && re2->hasIndices) { auto pair = Nan::New(); auto offset = data - str.data - byteIndex; auto length = item.size(); Nan::Set(pair, 0, Nan::New(static_cast(offset))); Nan::Set(pair, 1, Nan::New(static_cast(offset + length))); Nan::Set(indices, i, pair); } } else { Nan::Set(result, i, Nan::Undefined()); if (!re2->global && re2->hasIndices) Nan::Set(indices, i, Nan::Undefined()); } } if (!re2->global) { Nan::Set(result, Nan::New("index").ToLocalChecked(), Nan::New(static_cast(groups[0].data() - str.data))); Nan::Set(result, Nan::New("input").ToLocalChecked(), info[0]); } } else { for (size_t i = 0, n = groups.size(); i < n; ++i) { const auto &item = groups[i]; const auto data = item.data(); if (data) { Nan::Set(result, i, Nan::New(data, item.size()).ToLocalChecked()); if (!re2->global && re2->hasIndices) { auto pair = Nan::New(); auto offset = getUtf16Length(str.data + byteIndex, data); auto length = getUtf16Length(data, data + item.size()); Nan::Set(pair, 0, Nan::New(static_cast(offset))); Nan::Set(pair, 1, Nan::New(static_cast(offset + length))); Nan::Set(indices, i, pair); } } else { Nan::Set(result, i, Nan::Undefined()); if (!re2->global && re2->hasIndices) { Nan::Set(indices, i, Nan::Undefined()); } } } if (!re2->global) { Nan::Set(result, Nan::New("index").ToLocalChecked(), Nan::New(static_cast(getUtf16Length(str.data, groups[0].data())))); Nan::Set(result, Nan::New("input").ToLocalChecked(), info[0]); } } if (re2->global) { re2->lastIndex = 0; } else if (re2->sticky) { re2->lastIndex += str.isBuffer ? groups[0].data() - str.data + groups[0].size() - byteIndex : getUtf16Length(str.data + byteIndex, groups[0].data() + groups[0].size()); } if (!re2->global) { const auto &groupNames = re2->regexp.CapturingGroupNames(); if (!groupNames.empty()) { auto groups = Nan::New(); Nan::SetPrototype(groups, Nan::Null()); for (auto group : groupNames) { auto value = Nan::Get(result, group.first); if (!value.IsEmpty()) { Nan::Set(groups, Nan::New(group.second).ToLocalChecked(), value.ToLocalChecked()); } } Nan::Set(result, Nan::New("groups").ToLocalChecked(), groups); if (re2->hasIndices) { auto indexGroups = Nan::New(); Nan::SetPrototype(indexGroups, Nan::Null()); for (auto group : groupNames) { auto value = Nan::Get(indices, group.first); if (!value.IsEmpty()) { Nan::Set(indexGroups, Nan::New(group.second).ToLocalChecked(), value.ToLocalChecked()); } } Nan::Set(indices, Nan::New("groups").ToLocalChecked(), indexGroups); } } else { Nan::Set(result, Nan::New("groups").ToLocalChecked(), Nan::Undefined()); if (re2->hasIndices) { Nan::Set(indices, Nan::New("groups").ToLocalChecked(), Nan::Undefined()); } } if (re2->hasIndices) { Nan::Set(result, Nan::New("indices").ToLocalChecked(), indices); } } info.GetReturnValue().Set(result); } uhop-node-re2-e601e5a/lib/new.cc000066400000000000000000000141161514245257200163660ustar00rootroot00000000000000#include "./wrapped_re2.h" #include "./util.h" #include "./pattern.h" #include #include #include #include #include bool WrappedRE2::alreadyWarnedAboutUnicode = false; static 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."; inline bool ensureUniqueNamedGroups(const std::map &groups) { std::unordered_set names; for (auto group : groups) { if (!names.insert(group.second).second) { return false; } } return true; } NAN_METHOD(WrappedRE2::New) { if (!info.IsConstructCall()) { // call a constructor and return the result std::vector> parameters(info.Length()); for (size_t i = 0, n = info.Length(); i < n; ++i) { parameters[i] = info[i]; } auto isolate = v8::Isolate::GetCurrent(); auto p_tpl = Nan::GetIsolateData>(isolate); auto newObject = Nan::NewInstance(Nan::GetFunction(p_tpl->Get(isolate)).ToLocalChecked(), parameters.size(), ¶meters[0]); if (!newObject.IsEmpty()) { info.GetReturnValue().Set(newObject.ToLocalChecked()); } return; } // process arguments std::vector buffer; char *data = NULL; size_t size = 0; std::string source; bool global = false; bool ignoreCase = false; bool multiline = false; bool dotAll = false; bool unicode = false; bool sticky = false; bool hasIndices = false; auto context = Nan::GetCurrentContext(); bool needFlags = true; if (info.Length() > 1) { if (info[1]->IsString()) { auto isolate = v8::Isolate::GetCurrent(); auto t = info[1]->ToString(Nan::GetCurrentContext()); auto s = t.ToLocalChecked(); size = s->Utf8Length(isolate); buffer.resize(size + 1); data = &buffer[0]; s->WriteUtf8(isolate, data, buffer.size()); buffer[size] = '\0'; } else if (node::Buffer::HasInstance(info[1])) { size = node::Buffer::Length(info[1]); data = node::Buffer::Data(info[1]); } for (size_t i = 0; i < size; ++i) { switch (data[i]) { case 'g': global = true; break; case 'i': ignoreCase = true; break; case 'm': multiline = true; break; case 's': dotAll = true; break; case 'u': unicode = true; break; case 'y': sticky = true; break; case 'd': hasIndices = true; break; } } size = 0; needFlags = false; } bool needConversion = true; if (node::Buffer::HasInstance(info[0])) { size = node::Buffer::Length(info[0]); data = node::Buffer::Data(info[0]); source = escapeRegExp(data, size); } else if (info[0]->IsRegExp()) { const auto *re = v8::RegExp::Cast(*info[0]); auto isolate = v8::Isolate::GetCurrent(); auto t = re->GetSource()->ToString(Nan::GetCurrentContext()); auto s = t.ToLocalChecked(); size = s->Utf8Length(isolate); buffer.resize(size + 1); data = &buffer[0]; s->WriteUtf8(isolate, data, buffer.size()); buffer[size] = '\0'; source = escapeRegExp(data, size); if (needFlags) { v8::RegExp::Flags flags = re->GetFlags(); global = bool(flags & v8::RegExp::kGlobal); ignoreCase = bool(flags & v8::RegExp::kIgnoreCase); multiline = bool(flags & v8::RegExp::kMultiline); dotAll = bool(flags & v8::RegExp::kDotAll); unicode = bool(flags & v8::RegExp::kUnicode); sticky = bool(flags & v8::RegExp::kSticky); hasIndices = bool(flags & v8::RegExp::kHasIndices); needFlags = false; } } else if (info[0]->IsObject() && !info[0]->IsString()) { WrappedRE2 *re2 = nullptr; auto object = info[0]->ToObject(context).ToLocalChecked(); if (!object.IsEmpty() && object->InternalFieldCount() > 0) { re2 = Nan::ObjectWrap::Unwrap(object); } if (re2) { const auto &pattern = re2->regexp.pattern(); size = pattern.size(); buffer.resize(size); data = &buffer[0]; memcpy(data, pattern.data(), size); needConversion = false; source = re2->source; if (needFlags) { global = re2->global; ignoreCase = re2->ignoreCase; multiline = re2->multiline; dotAll = re2->dotAll; unicode = true; sticky = re2->sticky; hasIndices = re2->hasIndices; needFlags = false; } } } else if (info[0]->IsString()) { auto isolate = v8::Isolate::GetCurrent(); auto t = info[0]->ToString(Nan::GetCurrentContext()); auto s = t.ToLocalChecked(); size = s->Utf8Length(isolate); buffer.resize(size + 1); data = &buffer[0]; s->WriteUtf8(isolate, data, buffer.size()); buffer[size] = '\0'; source = escapeRegExp(data, size); } if (!data) { return Nan::ThrowTypeError("Expected string, Buffer, RegExp, or RE2 as the 1st argument."); } if (!unicode) { switch (unicodeWarningLevel) { case THROW: return Nan::ThrowSyntaxError(deprecationMessage); case WARN: printDeprecationWarning(deprecationMessage); break; case WARN_ONCE: if (!alreadyWarnedAboutUnicode) { printDeprecationWarning(deprecationMessage); alreadyWarnedAboutUnicode = true; } break; default: break; } } if (needConversion && translateRegExp(data, size, multiline, buffer)) { size = buffer.size() - 1; data = &buffer[0]; } // create and return an object re2::RE2::Options options; options.set_case_sensitive(!ignoreCase); options.set_one_line(!multiline); // to track this state, otherwise it is ignored options.set_dot_nl(dotAll); options.set_log_errors(false); // inappropriate when embedding std::unique_ptr re2(new WrappedRE2(re2::StringPiece(data, size), options, source, global, ignoreCase, multiline, dotAll, sticky, hasIndices)); if (!re2->regexp.ok()) { return Nan::ThrowSyntaxError(re2->regexp.error().c_str()); } if (!ensureUniqueNamedGroups(re2->regexp.CapturingGroupNames())) { return Nan::ThrowSyntaxError("duplicate capture group name"); } re2->Wrap(info.This()); re2.release(); info.GetReturnValue().Set(info.This()); } uhop-node-re2-e601e5a/lib/pattern.cc000066400000000000000000000117541514245257200172570ustar00rootroot00000000000000#include "./pattern.h" #include "./wrapped_re2.h" #include #include #include static char hex[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; inline bool isUpperCaseAlpha(char ch) { return 'A' <= ch && ch <= 'Z'; } inline bool isHexadecimal(char ch) { return ('0' <= ch && ch <= '9') || ('A' <= ch && ch <= 'Z') || ('a' <= ch && ch <= 'z'); } static std::map unicodeClasses = { {"Uppercase_Letter", "Lu"}, {"Lowercase_Letter", "Ll"}, {"Titlecase_Letter", "Lt"}, {"Cased_Letter", "LC"}, {"Modifier_Letter", "Lm"}, {"Other_Letter", "Lo"}, {"Letter", "L"}, {"Nonspacing_Mark", "Mn"}, {"Spacing_Mark", "Mc"}, {"Enclosing_Mark", "Me"}, {"Mark", "M"}, {"Decimal_Number", "Nd"}, {"Letter_Number", "Nl"}, {"Other_Number", "No"}, {"Number", "N"}, {"Connector_Punctuation", "Pc"}, {"Dash_Punctuation", "Pd"}, {"Open_Punctuation", "Ps"}, {"Close_Punctuation", "Pe"}, {"Initial_Punctuation", "Pi"}, {"Final_Punctuation", "Pf"}, {"Other_Punctuation", "Po"}, {"Punctuation", "P"}, {"Math_Symbol", "Sm"}, {"Currency_Symbol", "Sc"}, {"Modifier_Symbol", "Sk"}, {"Other_Symbol", "So"}, {"Symbol", "S"}, {"Space_Separator", "Zs"}, {"Line_Separator", "Zl"}, {"Paragraph_Separator", "Zp"}, {"Separator", "Z"}, {"Control", "Cc"}, {"Format", "Cf"}, {"Surrogate", "Cs"}, {"Private_Use", "Co"}, {"Unassigned", "Cn"}, {"Other", "C"}, }; bool translateRegExp(const char *data, size_t size, bool multiline, std::vector &buffer) { std::string result; bool changed = false; if (!size) { result = "(?:)"; changed = true; } else if (multiline) { result = "(?m)"; changed = true; } for (size_t i = 0; i < size;) { char ch = data[i]; if (ch == '\\') { if (i + 1 < size) { ch = data[i + 1]; switch (ch) { case '\\': result += "\\\\"; i += 2; continue; case 'c': if (i + 2 < size) { ch = data[i + 2]; if (isUpperCaseAlpha(ch)) { result += "\\x"; result += hex[((ch - '@') / 16) & 15]; result += hex[(ch - '@') & 15]; i += 3; changed = true; continue; } } result += "\\c"; i += 2; continue; case 'u': if (i + 2 < size) { ch = data[i + 2]; if (isHexadecimal(ch)) { result += "\\x{"; result += ch; i += 3; for (size_t j = 0; j < 3 && i < size; ++i, ++j) { ch = data[i]; if (!isHexadecimal(ch)) { break; } result += ch; } result += '}'; changed = true; continue; } else if (ch == '{') { result += "\\x"; i += 2; changed = true; continue; } } result += "\\u"; i += 2; continue; case 'p': case 'P': if (i + 2 < size) { if (data[i + 2] == '{') { size_t j = i + 3; while (j < size && data[j] != '}') ++j; if (j < size) { result += "\\"; result += data[i + 1]; std::string name(data + i + 3, j - i - 3); if (unicodeClasses.find(name) != unicodeClasses.end()) { name = unicodeClasses[name]; } else if (name.size() > 7 && !strncmp(name.c_str(), "Script=", 7)) { name = name.substr(7); } else if (name.size() > 3 && !strncmp(name.c_str(), "sc=", 3)) { name = name.substr(3); } if (name.size() == 1) { result += name; } else { result += "{"; result += name; result += "}"; } i = j + 1; changed = true; continue; } } } result += "\\"; result += data[i + 1]; i += 2; continue; default: result += "\\"; size_t sym_size = getUtf8CharSize(ch); result.append(data + i + 1, sym_size); i += sym_size + 1; continue; } } } else if (ch == '/') { result += "\\/"; i += 1; changed = true; continue; } else if (ch == '(' && i + 2 < size && data[i + 1] == '?' && data[i + 2] == '<') { if (i + 3 >= size || (data[i + 3] != '=' && data[i + 3] != '!')) { result += "(?P<"; i += 3; changed = true; continue; } } size_t sym_size = getUtf8CharSize(ch); result.append(data + i, sym_size); i += sym_size; } if (!changed) { return false; } buffer.resize(0); buffer.insert(buffer.end(), result.data(), result.data() + result.size()); buffer.push_back('\0'); return true; } std::string escapeRegExp(const char *data, size_t size) { std::string result; if (!size) { result = "(?:)"; } size_t prevBackSlashes = 0; for (size_t i = 0; i < size;) { char ch = data[i]; if (ch == '\\') { ++prevBackSlashes; } else if (ch == '/' && !(prevBackSlashes & 1)) { result += "\\/"; i += 1; prevBackSlashes = 0; continue; } else { prevBackSlashes = 0; } size_t sym_size = getUtf8CharSize(ch); result.append(data + i, sym_size); i += sym_size; } return result; } uhop-node-re2-e601e5a/lib/pattern.h000066400000000000000000000004651514245257200171160ustar00rootroot00000000000000#pragma once #include #include // Shared helpers for translating JavaScript-style regular expressions // into RE2-compatible patterns. bool translateRegExp(const char *data, size_t size, bool multiline, std::vector &buffer); std::string escapeRegExp(const char *data, size_t size); uhop-node-re2-e601e5a/lib/replace.cc000066400000000000000000000273311514245257200172130ustar00rootroot00000000000000#include "./wrapped_re2.h" #include #include #include #include inline int getMaxSubmatch( const char *data, size_t size, const std::map &namedGroups) { int maxSubmatch = 0, index, index2; const char *nameBegin; const char *nameEnd; for (size_t i = 0; i < size;) { char ch = data[i]; if (ch == '$') { if (i + 1 < size) { ch = data[i + 1]; switch (ch) { case '$': case '&': case '`': case '\'': i += 2; continue; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': index = ch - '0'; if (i + 2 < size) { ch = data[i + 2]; if ('0' <= ch && ch <= '9') { index2 = index * 10 + (ch - '0'); if (maxSubmatch < index2) maxSubmatch = index2; i += 3; continue; } } if (maxSubmatch < index) maxSubmatch = index; i += 2; continue; case '<': nameBegin = data + i + 2; nameEnd = (const char *)memchr(nameBegin, '>', size - i - 2); if (nameEnd) { std::string name(nameBegin, nameEnd - nameBegin); auto group = namedGroups.find(name); if (group != namedGroups.end()) { index = group->second; if (maxSubmatch < index) maxSubmatch = index; } i = nameEnd + 1 - data; } else { i += 2; } continue; } } ++i; continue; } i += getUtf8CharSize(ch); } return maxSubmatch; } inline std::string replace( const char *data, size_t size, const std::vector &groups, const re2::StringPiece &str, const std::map &namedGroups) { std::string result; size_t index, index2; const char *nameBegin; const char *nameEnd; for (size_t i = 0; i < size;) { char ch = data[i]; if (ch == '$') { if (i + 1 < size) { ch = data[i + 1]; switch (ch) { case '$': result += ch; i += 2; continue; case '&': result += (std::string)groups[0]; i += 2; continue; case '`': result += std::string(str.data(), groups[0].data() - str.data()); i += 2; continue; case '\'': result += std::string(groups[0].data() + groups[0].size(), str.data() + str.size() - groups[0].data() - groups[0].size()); i += 2; continue; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': index = ch - '0'; if (i + 2 < size) { ch = data[i + 2]; if ('0' <= ch && ch <= '9') { i += 3; index2 = index * 10 + (ch - '0'); if (index2 && index2 < groups.size()) { result += (std::string)groups[index2]; continue; } else if (index && index < groups.size()) { result += (std::string)groups[index]; result += ch; continue; } result += '$'; result += '0' + index; result += ch; continue; } ch = '0' + index; } i += 2; if (index && index < groups.size()) { result += (std::string)groups[index]; continue; } result += '$'; result += ch; continue; case '<': if (!namedGroups.empty()) { nameBegin = data + i + 2; nameEnd = (const char *)memchr(nameBegin, '>', size - i - 2); if (nameEnd) { std::string name(nameBegin, nameEnd - nameBegin); auto group = namedGroups.find(name); if (group != namedGroups.end()) { index = group->second; result += (std::string)groups[index]; } i = nameEnd + 1 - data; } else { result += "$<"; i += 2; } } else { result += "$<"; i += 2; } continue; } } result += '$'; ++i; continue; } size_t sym_size = getUtf8CharSize(ch); result.append(data + i, sym_size); i += sym_size; } return result; } static Nan::Maybe replace( WrappedRE2 *re2, const StrVal &replacee, const char *replacer, size_t replacer_size) { const re2::StringPiece str = replacee; const char *data = str.data(); size_t size = str.size(); const auto &namedGroups = re2->regexp.NamedCapturingGroups(); std::vector groups(std::min(re2->regexp.NumberOfCapturingGroups(), getMaxSubmatch(replacer, replacer_size, namedGroups)) + 1); const auto &match = groups[0]; size_t byteIndex = 0; std::string result; auto anchor = re2::RE2::UNANCHORED; if (re2->sticky) { if (!re2->global) byteIndex = replacee.byteIndex; anchor = re2::RE2::ANCHOR_START; } if (byteIndex) { result = std::string(data, byteIndex); } bool noMatch = true; while (byteIndex <= size && re2->regexp.Match(str, byteIndex, size, anchor, &groups[0], groups.size())) { noMatch = false; auto offset = match.data() - data; if (!re2->global && re2->sticky) { re2->lastIndex += replacee.isBuffer ? offset + match.size() - byteIndex : getUtf16Length(data + byteIndex, match.data() + match.size()); } if (match.data() == data || offset > static_cast(byteIndex)) { result += std::string(data + byteIndex, offset - byteIndex); } result += replace(replacer, replacer_size, groups, str, namedGroups); if (match.size()) { byteIndex = offset + match.size(); } else if ((size_t)offset < size) { auto sym_size = getUtf8CharSize(data[offset]); result.append(data + offset, sym_size); byteIndex = offset + sym_size; } else { byteIndex = size; break; } if (!re2->global) { break; } } if (byteIndex < size) { result += std::string(data + byteIndex, size - byteIndex); } if (re2->global) { re2->lastIndex = 0; } else if (re2->sticky) { if (noMatch) re2->lastIndex = 0; } return Nan::Just(result); } inline Nan::Maybe replace( const Nan::Callback *replacer, const std::vector &groups, const re2::StringPiece &str, const v8::Local &input, bool useBuffers, const std::map &namedGroups) { std::vector> argv; auto context = Nan::GetCurrentContext(); if (useBuffers) { for (size_t i = 0, n = groups.size(); i < n; ++i) { const auto &item = groups[i]; const auto data = item.data(); if (data) { argv.push_back(Nan::CopyBuffer(data, item.size()).ToLocalChecked()); } else { argv.push_back(Nan::Undefined()); } } argv.push_back(Nan::New(static_cast(groups[0].data() - str.data()))); } else { for (size_t i = 0, n = groups.size(); i < n; ++i) { const auto &item = groups[i]; const auto data = item.data(); if (data) { argv.push_back(Nan::New(data, item.size()).ToLocalChecked()); } else { argv.push_back(Nan::Undefined()); } } argv.push_back(Nan::New(static_cast(getUtf16Length(str.data(), groups[0].data())))); } argv.push_back(input); if (!namedGroups.empty()) { auto groups = Nan::New(); Nan::SetPrototype(groups, Nan::Null()); for (std::pair group : namedGroups) { Nan::Set(groups, Nan::New(group.first).ToLocalChecked(), argv[group.second]); } argv.push_back(groups); } auto maybeResult = Nan::CallAsFunction(replacer->GetFunction(), context->Global(), static_cast(argv.size()), &argv[0]); if (maybeResult.IsEmpty()) { return Nan::Nothing(); } auto result = maybeResult.ToLocalChecked(); if (node::Buffer::HasInstance(result)) { return Nan::Just(std::string(node::Buffer::Data(result), node::Buffer::Length(result))); } auto t = result->ToString(Nan::GetCurrentContext()); if (t.IsEmpty()) { return Nan::Nothing(); } v8::String::Utf8Value s(v8::Isolate::GetCurrent(), t.ToLocalChecked()); return Nan::Just(std::string(*s)); } static Nan::Maybe replace( WrappedRE2 *re2, const StrVal &replacee, const Nan::Callback *replacer, const v8::Local &input, bool useBuffers) { const re2::StringPiece str = replacee; const char *data = str.data(); size_t size = str.size(); std::vector groups(re2->regexp.NumberOfCapturingGroups() + 1); const auto &match = groups[0]; size_t byteIndex = 0; std::string result; auto anchor = re2::RE2::UNANCHORED; if (re2->sticky) { if (!re2->global) byteIndex = replacee.byteIndex; anchor = RE2::ANCHOR_START; } if (byteIndex) { result = std::string(data, byteIndex); } const auto &namedGroups = re2->regexp.NamedCapturingGroups(); bool noMatch = true; while (byteIndex <= size && re2->regexp.Match(str, byteIndex, size, anchor, &groups[0], groups.size())) { noMatch = false; auto offset = match.data() - data; if (!re2->global && re2->sticky) { re2->lastIndex += replacee.isBuffer ? offset + match.size() - byteIndex : getUtf16Length(data + byteIndex, match.data() + match.size()); } if (match.data() == data || offset > static_cast(byteIndex)) { result += std::string(data + byteIndex, offset - byteIndex); } const auto part = replace(replacer, groups, str, input, useBuffers, namedGroups); if (part.IsNothing()) { return part; } result += part.FromJust(); if (match.size()) { byteIndex = offset + match.size(); } else if ((size_t)offset < size) { auto sym_size = getUtf8CharSize(data[offset]); result.append(data + offset, sym_size); byteIndex = offset + sym_size; } else { byteIndex = size; break; } if (!re2->global) { break; } } if (byteIndex < size) { result += std::string(data + byteIndex, size - byteIndex); } if (re2->global) { re2->lastIndex = 0; } else if (re2->sticky) { if (noMatch) { re2->lastIndex = 0; } } return Nan::Just(result); } static bool requiresBuffers(const v8::Local &f) { auto flag(Nan::Get(f, Nan::New("useBuffers").ToLocalChecked()).ToLocalChecked()); if (flag->IsUndefined() || flag->IsNull() || flag->IsFalse()) { return false; } if (flag->IsNumber()) { return flag->NumberValue(Nan::GetCurrentContext()).FromMaybe(0) != 0; } if (flag->IsString()) { return flag->ToString(Nan::GetCurrentContext()).ToLocalChecked()->Length() > 0; } return true; } NAN_METHOD(WrappedRE2::Replace) { auto re2 = Nan::ObjectWrap::Unwrap(info.This()); if (!re2) { info.GetReturnValue().Set(info[0]); return; } PrepareLastString prep(re2, info[0]); StrVal& replacee = prep; if (replacee.isBad) return; // throws an exception if (!replacee.isValidIndex) { info.GetReturnValue().Set(info[0]); return; } std::string result; if (info[1]->IsFunction()) { auto fun = info[1].As(); const std::unique_ptr cb(new Nan::Callback(fun)); const auto replaced = replace(re2, replacee, cb.get(), info[0], requiresBuffers(fun)); if (replaced.IsNothing()) { info.GetReturnValue().Set(info[0]); return; } result = replaced.FromJust(); } else { v8::Local replacer; if (node::Buffer::HasInstance(info[1])) { replacer = info[1].As(); } else { auto t = info[1]->ToString(Nan::GetCurrentContext()); if (t.IsEmpty()) return; // throws an exception replacer = node::Buffer::New(v8::Isolate::GetCurrent(), t.ToLocalChecked()).ToLocalChecked(); } auto data = node::Buffer::Data(replacer); auto size = node::Buffer::Length(replacer); const auto replaced = replace(re2, replacee, data, size); if (replaced.IsNothing()) { info.GetReturnValue().Set(info[0]); return; } result = replaced.FromJust(); } if (replacee.isBuffer) { info.GetReturnValue().Set(Nan::CopyBuffer(result.data(), result.size()).ToLocalChecked()); return; } info.GetReturnValue().Set(Nan::New(result).ToLocalChecked()); } uhop-node-re2-e601e5a/lib/search.cc000066400000000000000000000012461514245257200170420ustar00rootroot00000000000000#include "./wrapped_re2.h" NAN_METHOD(WrappedRE2::Search) { // unpack arguments auto re2 = Nan::ObjectWrap::Unwrap(info.This()); if (!re2) { info.GetReturnValue().Set(-1); return; } PrepareLastString prep(re2, info[0]); StrVal& str = prep; if (str.isBad) return; // throws an exception if (!str.data) return; // actual work re2::StringPiece match; if (re2->regexp.Match(str, 0, str.size, re2->sticky ? re2::RE2::ANCHOR_START : re2::RE2::UNANCHORED, &match, 1)) { info.GetReturnValue().Set(static_cast(str.isBuffer ? match.data() - str.data : getUtf16Length(str.data, match.data()))); return; } info.GetReturnValue().Set(-1); } uhop-node-re2-e601e5a/lib/set.cc000066400000000000000000000432301514245257200163670ustar00rootroot00000000000000#include "./wrapped_re2_set.h" #include "./pattern.h" #include "./util.h" #include "./wrapped_re2.h" #include #include #include #include Nan::Persistent WrappedRE2Set::constructor; struct SetFlags { bool global = false; bool ignoreCase = false; bool multiline = false; bool dotAll = false; bool unicode = false; bool sticky = false; bool hasIndices = false; }; static bool parseFlags(const v8::Local &arg, SetFlags &flags) { const char *data = nullptr; size_t size = 0; std::vector buffer; if (arg->IsString()) { auto isolate = v8::Isolate::GetCurrent(); auto t = arg->ToString(Nan::GetCurrentContext()); if (t.IsEmpty()) { return false; } auto s = t.ToLocalChecked(); size = s->Utf8Length(isolate); buffer.resize(size + 1); s->WriteUtf8(isolate, &buffer[0], buffer.size()); buffer[buffer.size() - 1] = '\0'; data = &buffer[0]; } else if (node::Buffer::HasInstance(arg)) { size = node::Buffer::Length(arg); data = node::Buffer::Data(arg); } else { return false; } for (size_t i = 0; i < size; ++i) { switch (data[i]) { case 'd': flags.hasIndices = true; break; case 'g': flags.global = true; break; case 'i': flags.ignoreCase = true; break; case 'm': flags.multiline = true; break; case 's': flags.dotAll = true; break; case 'u': flags.unicode = true; break; case 'y': flags.sticky = true; break; default: return false; } } return true; } static bool sameEffectiveOptions(const SetFlags &a, const SetFlags &b) { return a.ignoreCase == b.ignoreCase && a.multiline == b.multiline && a.dotAll == b.dotAll && a.unicode == b.unicode; } static std::string flagsToString(const SetFlags &flags) { std::string result; if (flags.hasIndices) { result += 'd'; } if (flags.global) { result += 'g'; } if (flags.ignoreCase) { result += 'i'; } if (flags.multiline) { result += 'm'; } if (flags.dotAll) { result += 's'; } result += 'u'; if (flags.sticky) { result += 'y'; } return result; } static bool collectIterable(const v8::Local &input, std::vector> &items) { auto context = Nan::GetCurrentContext(); auto isolate = v8::Isolate::GetCurrent(); if (input->IsArray()) { auto array = v8::Local::Cast(input); auto length = array->Length(); items.reserve(length); for (uint32_t i = 0; i < length; ++i) { auto maybe = Nan::Get(array, i); if (maybe.IsEmpty()) { return false; } items.push_back(maybe.ToLocalChecked()); } return true; } auto maybeObject = input->ToObject(context); if (maybeObject.IsEmpty()) { return false; } auto object = maybeObject.ToLocalChecked(); auto maybeIteratorFn = object->Get(context, v8::Symbol::GetIterator(isolate)); if (maybeIteratorFn.IsEmpty()) { return false; } auto iteratorFn = maybeIteratorFn.ToLocalChecked(); if (!iteratorFn->IsFunction()) { return false; } auto maybeIterator = iteratorFn.As()->Call(context, object, 0, nullptr); if (maybeIterator.IsEmpty()) { return false; } auto iterator = maybeIterator.ToLocalChecked(); if (!iterator->IsObject()) { return false; } auto nextKey = Nan::New("next").ToLocalChecked(); auto valueKey = Nan::New("value").ToLocalChecked(); auto doneKey = Nan::New("done").ToLocalChecked(); for (;;) { auto maybeNext = Nan::Get(iterator.As(), nextKey); if (maybeNext.IsEmpty()) { return false; } auto next = maybeNext.ToLocalChecked(); if (!next->IsFunction()) { return false; } auto maybeResult = next.As()->Call(context, iterator, 0, nullptr); if (maybeResult.IsEmpty()) { return false; } auto result = maybeResult.ToLocalChecked(); if (!result->IsObject()) { return false; } auto resultObj = result->ToObject(context).ToLocalChecked(); auto maybeDone = Nan::Get(resultObj, doneKey); if (maybeDone.IsEmpty()) { return false; } if (maybeDone.ToLocalChecked()->BooleanValue(isolate)) { break; } auto maybeValue = Nan::Get(resultObj, valueKey); if (maybeValue.IsEmpty()) { return false; } items.push_back(maybeValue.ToLocalChecked()); } return true; } static bool parseAnchor(const v8::Local &arg, re2::RE2::Anchor &anchor) { if (arg.IsEmpty() || arg->IsUndefined() || arg->IsNull()) { anchor = re2::RE2::UNANCHORED; return true; } v8::Local value = arg; if (arg->IsObject() && !arg->IsString()) { auto context = Nan::GetCurrentContext(); auto object = arg->ToObject(context).ToLocalChecked(); auto maybeAnchor = Nan::Get(object, Nan::New("anchor").ToLocalChecked()); if (maybeAnchor.IsEmpty()) { return false; } value = maybeAnchor.ToLocalChecked(); if (value->IsUndefined() || value->IsNull()) { anchor = re2::RE2::UNANCHORED; return true; } } if (!value->IsString()) { return false; } Nan::Utf8String val(value); std::string text(*val, val.length()); if (text == "unanchored") { anchor = re2::RE2::UNANCHORED; return true; } if (text == "start") { anchor = re2::RE2::ANCHOR_START; return true; } if (text == "both") { anchor = re2::RE2::ANCHOR_BOTH; return true; } return false; } static bool fillInput(const v8::Local &arg, StrVal &str, v8::Local &keepAlive) { if (node::Buffer::HasInstance(arg)) { auto size = node::Buffer::Length(arg); str.reset(arg, size, size, 0, true); return true; } auto context = Nan::GetCurrentContext(); auto isolate = v8::Isolate::GetCurrent(); auto t = arg->ToString(context); if (t.IsEmpty()) { return false; } auto s = t.ToLocalChecked(); auto utf8Length = s->Utf8Length(isolate); auto buffer = node::Buffer::New(isolate, s).ToLocalChecked(); keepAlive = buffer; str.reset(buffer, node::Buffer::Length(buffer), utf8Length, 0); return true; } static std::string anchorToString(re2::RE2::Anchor anchor) { switch (anchor) { case re2::RE2::ANCHOR_BOTH: return "both"; case re2::RE2::ANCHOR_START: return "start"; default: return "unanchored"; } } static std::string makeCombinedSource(const std::vector &sources) { if (sources.empty()) { return "(?:)"; } std::string combined; for (size_t i = 0, n = sources.size(); i < n; ++i) { if (i) { combined += '|'; } combined += sources[i]; } return combined; } static 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."; NAN_METHOD(WrappedRE2Set::New) { auto context = Nan::GetCurrentContext(); auto isolate = context->GetIsolate(); if (!info.IsConstructCall()) { std::vector> parameters(info.Length()); for (size_t i = 0, n = info.Length(); i < n; ++i) { parameters[i] = info[i]; } auto maybeNew = Nan::NewInstance(Nan::GetFunction(Nan::New(constructor)).ToLocalChecked(), parameters.size(), ¶meters[0]); if (!maybeNew.IsEmpty()) { info.GetReturnValue().Set(maybeNew.ToLocalChecked()); } return; } if (!info.Length()) { return Nan::ThrowTypeError("Expected an iterable of patterns as the 1st argument."); } SetFlags flags; bool haveFlags = false; bool flagsFromArg = false; v8::Local flagsArg; v8::Local optionsArg; if (info.Length() > 1) { if (info[1]->IsObject() && !info[1]->IsString() && !node::Buffer::HasInstance(info[1])) { optionsArg = info[1]; } else { flagsArg = info[1]; if (info.Length() > 2) { optionsArg = info[2]; } } } if (!flagsArg.IsEmpty()) { if (!parseFlags(flagsArg, flags)) { return Nan::ThrowTypeError("Invalid flags for RE2.Set."); } haveFlags = true; flagsFromArg = true; } re2::RE2::Anchor anchor = re2::RE2::UNANCHORED; if (!optionsArg.IsEmpty()) { if (!parseAnchor(optionsArg, anchor)) { return Nan::ThrowTypeError("Invalid anchor option for RE2.Set."); } } std::vector> patterns; if (!collectIterable(info[0], patterns)) { return Nan::ThrowTypeError("Expected an iterable of patterns as the 1st argument."); } auto mergeFlags = [&](const SetFlags &candidate) { if (flagsFromArg) { return true; } if (!haveFlags) { flags = candidate; haveFlags = true; return true; } return sameEffectiveOptions(flags, candidate); }; for (auto &value : patterns) { SetFlags patternFlags; bool hasFlagsForPattern = false; if (value->IsRegExp()) { const auto *re = v8::RegExp::Cast(*value); v8::RegExp::Flags reFlags = re->GetFlags(); patternFlags.global = bool(reFlags & v8::RegExp::kGlobal); patternFlags.ignoreCase = bool(reFlags & v8::RegExp::kIgnoreCase); patternFlags.multiline = bool(reFlags & v8::RegExp::kMultiline); patternFlags.dotAll = bool(reFlags & v8::RegExp::kDotAll); patternFlags.unicode = bool(reFlags & v8::RegExp::kUnicode); patternFlags.sticky = bool(reFlags & v8::RegExp::kSticky); patternFlags.hasIndices = bool(reFlags & v8::RegExp::kHasIndices); hasFlagsForPattern = true; } else if (value->IsObject()) { auto maybeObj = value->ToObject(context); if (!maybeObj.IsEmpty()) { auto obj = maybeObj.ToLocalChecked(); if (WrappedRE2::HasInstance(obj)) { auto re2 = Nan::ObjectWrap::Unwrap(obj); patternFlags.global = re2->global; patternFlags.ignoreCase = re2->ignoreCase; patternFlags.multiline = re2->multiline; patternFlags.dotAll = re2->dotAll; patternFlags.unicode = true; patternFlags.sticky = re2->sticky; patternFlags.hasIndices = re2->hasIndices; hasFlagsForPattern = true; } } } if (hasFlagsForPattern && !mergeFlags(patternFlags)) { return Nan::ThrowTypeError("All patterns in RE2.Set must use the same flags."); } } if (!flags.unicode) { switch (WrappedRE2::unicodeWarningLevel) { case WrappedRE2::THROW: return Nan::ThrowSyntaxError(setDeprecationMessage); case WrappedRE2::WARN: printDeprecationWarning(setDeprecationMessage); break; case WrappedRE2::WARN_ONCE: if (!WrappedRE2::alreadyWarnedAboutUnicode) { printDeprecationWarning(setDeprecationMessage); WrappedRE2::alreadyWarnedAboutUnicode = true; } break; default: break; } } re2::RE2::Options options; options.set_case_sensitive(!flags.ignoreCase); options.set_one_line(!flags.multiline); options.set_dot_nl(flags.dotAll); options.set_log_errors(false); std::unique_ptr set(new WrappedRE2Set(options, anchor, flagsToString(flags))); std::vector buffer; for (auto &value : patterns) { const char *data = nullptr; size_t size = 0; std::string source; if (node::Buffer::HasInstance(value)) { size = node::Buffer::Length(value); data = node::Buffer::Data(value); source = escapeRegExp(data, size); } else if (value->IsRegExp()) { const auto *re = v8::RegExp::Cast(*value); auto t = re->GetSource()->ToString(context); if (t.IsEmpty()) { return; } auto s = t.ToLocalChecked(); size = s->Utf8Length(isolate); buffer.resize(size + 1); s->WriteUtf8(isolate, &buffer[0], buffer.size()); buffer[size] = '\0'; data = &buffer[0]; source = escapeRegExp(data, size); } else if (value->IsString()) { auto t = value->ToString(context); if (t.IsEmpty()) { return; } auto s = t.ToLocalChecked(); size = s->Utf8Length(isolate); buffer.resize(size + 1); s->WriteUtf8(isolate, &buffer[0], buffer.size()); buffer[size] = '\0'; data = &buffer[0]; source = escapeRegExp(data, size); } else if (value->IsObject()) { auto maybeObj = value->ToObject(context); if (maybeObj.IsEmpty()) { return; } auto obj = maybeObj.ToLocalChecked(); if (!WrappedRE2::HasInstance(obj)) { return Nan::ThrowTypeError("Expected a string, Buffer, RegExp, or RE2 instance in the pattern list."); } auto re2 = Nan::ObjectWrap::Unwrap(obj); source = re2->source; data = source.data(); size = source.size(); } else { return Nan::ThrowTypeError("Expected a string, Buffer, RegExp, or RE2 instance in the pattern list."); } if (translateRegExp(data, size, flags.multiline, buffer)) { data = &buffer[0]; size = buffer.size() - 1; } std::string error; if (set->set.Add(re2::StringPiece(data, size), &error) < 0) { if (error.empty()) { error = "Invalid pattern in RE2.Set."; } return Nan::ThrowSyntaxError(error.c_str()); } set->sources.push_back(source); } if (!set->set.Compile()) { return Nan::ThrowError("RE2.Set could not be compiled."); } set->combinedSource = makeCombinedSource(set->sources); set->Wrap(info.This()); set.release(); info.GetReturnValue().Set(info.This()); } NAN_METHOD(WrappedRE2Set::Test) { auto re2set = Nan::ObjectWrap::Unwrap(info.This()); if (!re2set) { info.GetReturnValue().Set(false); return; } StrVal str; v8::Local keepAlive; if (!fillInput(info[0], str, keepAlive)) { return; } re2::RE2::Set::ErrorInfo errorInfo{re2::RE2::Set::kNoError}; bool matched = re2set->set.Match(str, nullptr, &errorInfo); if (!matched && errorInfo.kind != re2::RE2::Set::kNoError) { const char *message = "RE2.Set matching failed."; switch (errorInfo.kind) { case re2::RE2::Set::kOutOfMemory: message = "RE2.Set matching failed: out of memory."; break; case re2::RE2::Set::kInconsistent: message = "RE2.Set matching failed: inconsistent result."; break; case re2::RE2::Set::kNotCompiled: message = "RE2.Set matching failed: set is not compiled."; break; default: break; } return Nan::ThrowError(message); } info.GetReturnValue().Set(matched); } NAN_METHOD(WrappedRE2Set::Match) { auto re2set = Nan::ObjectWrap::Unwrap(info.This()); if (!re2set) { info.GetReturnValue().Set(Nan::New(0)); return; } StrVal str; v8::Local keepAlive; if (!fillInput(info[0], str, keepAlive)) { return; } std::vector matches; re2::RE2::Set::ErrorInfo errorInfo{re2::RE2::Set::kNoError}; bool matched = re2set->set.Match(str, &matches, &errorInfo); if (!matched && errorInfo.kind != re2::RE2::Set::kNoError) { const char *message = "RE2.Set matching failed."; switch (errorInfo.kind) { case re2::RE2::Set::kOutOfMemory: message = "RE2.Set matching failed: out of memory."; break; case re2::RE2::Set::kInconsistent: message = "RE2.Set matching failed: inconsistent result."; break; case re2::RE2::Set::kNotCompiled: message = "RE2.Set matching failed: set is not compiled."; break; default: break; } return Nan::ThrowError(message); } std::sort(matches.begin(), matches.end()); auto result = Nan::New(matches.size()); for (size_t i = 0, n = matches.size(); i < n; ++i) { Nan::Set(result, i, Nan::New(matches[i])); } info.GetReturnValue().Set(result); } NAN_METHOD(WrappedRE2Set::ToString) { auto re2set = Nan::ObjectWrap::Unwrap(info.This()); if (!re2set) { info.GetReturnValue().SetEmptyString(); return; } std::string result = "/"; result += re2set->combinedSource; result += "/"; result += re2set->flags; info.GetReturnValue().Set(Nan::New(result).ToLocalChecked()); } NAN_GETTER(WrappedRE2Set::GetFlags) { auto re2set = Nan::ObjectWrap::Unwrap(info.This()); if (!re2set) { info.GetReturnValue().Set(Nan::New("u").ToLocalChecked()); return; } info.GetReturnValue().Set(Nan::New(re2set->flags).ToLocalChecked()); } NAN_GETTER(WrappedRE2Set::GetSources) { auto re2set = Nan::ObjectWrap::Unwrap(info.This()); if (!re2set) { info.GetReturnValue().Set(Nan::New(0)); return; } auto result = Nan::New(re2set->sources.size()); for (size_t i = 0, n = re2set->sources.size(); i < n; ++i) { Nan::Set(result, i, Nan::New(re2set->sources[i]).ToLocalChecked()); } info.GetReturnValue().Set(result); } NAN_GETTER(WrappedRE2Set::GetSource) { auto re2set = Nan::ObjectWrap::Unwrap(info.This()); if (!re2set) { info.GetReturnValue().Set(Nan::New("(?:)").ToLocalChecked()); return; } info.GetReturnValue().Set(Nan::New(re2set->combinedSource).ToLocalChecked()); } NAN_GETTER(WrappedRE2Set::GetSize) { auto re2set = Nan::ObjectWrap::Unwrap(info.This()); if (!re2set) { info.GetReturnValue().Set(0); return; } info.GetReturnValue().Set(static_cast(re2set->sources.size())); } NAN_GETTER(WrappedRE2Set::GetAnchor) { auto re2set = Nan::ObjectWrap::Unwrap(info.This()); if (!re2set) { info.GetReturnValue().Set(Nan::New("unanchored").ToLocalChecked()); return; } info.GetReturnValue().Set(Nan::New(anchorToString(re2set->anchor)).ToLocalChecked()); } v8::Local WrappedRE2Set::Init() { Nan::EscapableHandleScope scope; auto tpl = Nan::New(New); tpl->SetClassName(Nan::New("RE2Set").ToLocalChecked()); auto instanceTemplate = tpl->InstanceTemplate(); instanceTemplate->SetInternalFieldCount(1); Nan::SetPrototypeMethod(tpl, "test", Test); Nan::SetPrototypeMethod(tpl, "match", Match); Nan::SetPrototypeMethod(tpl, "toString", ToString); Nan::SetAccessor(instanceTemplate, Nan::New("flags").ToLocalChecked(), GetFlags); Nan::SetAccessor(instanceTemplate, Nan::New("sources").ToLocalChecked(), GetSources); Nan::SetAccessor(instanceTemplate, Nan::New("source").ToLocalChecked(), GetSource); Nan::SetAccessor(instanceTemplate, Nan::New("size").ToLocalChecked(), GetSize); Nan::SetAccessor(instanceTemplate, Nan::New("anchor").ToLocalChecked(), GetAnchor); constructor.Reset(tpl); return scope.Escape(Nan::GetFunction(tpl).ToLocalChecked()); } uhop-node-re2-e601e5a/lib/split.cc000066400000000000000000000042211514245257200167240ustar00rootroot00000000000000#include "./wrapped_re2.h" #include #include #include NAN_METHOD(WrappedRE2::Split) { auto result = Nan::New(); // unpack arguments auto re2 = Nan::ObjectWrap::Unwrap(info.This()); if (!re2) { Nan::Set(result, 0, info[0]); info.GetReturnValue().Set(result); return; } PrepareLastString prep(re2, info[0]); StrVal& str = prep; if (str.isBad) return; // throws an exception size_t limit = std::numeric_limits::max(); if (info.Length() > 1 && info[1]->IsNumber()) { size_t lim = info[1]->NumberValue(Nan::GetCurrentContext()).FromMaybe(0); if (lim > 0) { limit = lim; } } // actual work std::vector groups(re2->regexp.NumberOfCapturingGroups() + 1), pieces; const auto &match = groups[0]; size_t byteIndex = 0; while (byteIndex < str.size && re2->regexp.Match(str, byteIndex, str.size, RE2::UNANCHORED, &groups[0], groups.size())) { if (match.size()) { pieces.push_back(re2::StringPiece(str.data + byteIndex, match.data() - str.data - byteIndex)); byteIndex = match.data() - str.data + match.size(); pieces.insert(pieces.end(), groups.begin() + 1, groups.end()); } else { size_t sym_size = getUtf8CharSize(str.data[byteIndex]); pieces.push_back(re2::StringPiece(str.data + byteIndex, sym_size)); byteIndex += sym_size; } if (pieces.size() >= limit) { break; } } if (pieces.size() < limit && (byteIndex < str.size || (byteIndex == str.size && match.size()))) { pieces.push_back(re2::StringPiece(str.data + byteIndex, str.size - byteIndex)); } if (pieces.empty()) { Nan::Set(result, 0, info[0]); info.GetReturnValue().Set(result); return; } // form a result if (str.isBuffer) { for (size_t i = 0, n = std::min(pieces.size(), limit); i < n; ++i) { const auto &item = pieces[i]; Nan::Set(result, i, Nan::CopyBuffer(item.data(), item.size()).ToLocalChecked()); } } else { for (size_t i = 0, n = std::min(pieces.size(), limit); i < n; ++i) { const auto &item = pieces[i]; Nan::Set(result, i, Nan::New(item.data(), item.size()).ToLocalChecked()); } } info.GetReturnValue().Set(result); } uhop-node-re2-e601e5a/lib/test.cc000066400000000000000000000020011514245257200165420ustar00rootroot00000000000000#include "./wrapped_re2.h" #include NAN_METHOD(WrappedRE2::Test) { // unpack arguments auto re2 = Nan::ObjectWrap::Unwrap(info.This()); if (!re2) { info.GetReturnValue().Set(false); return; } PrepareLastString prep(re2, info[0]); StrVal& str = prep; if (str.isBad) return; // throws an exception if (!re2->global && !re2->sticky) { info.GetReturnValue().Set(re2->regexp.Match(str, 0, str.size, re2::RE2::UNANCHORED, NULL, 0)); return; } if (!str.isValidIndex) { re2->lastIndex = 0; info.GetReturnValue().SetNull(); return; } // actual work re2::StringPiece match; if (re2->regexp.Match(str, str.byteIndex, str.size, re2->sticky ? re2::RE2::ANCHOR_START : re2::RE2::UNANCHORED, &match, 1)) { re2->lastIndex += str.isBuffer ? match.data() - str.data + match.size() - str.byteIndex : getUtf16Length(str.data + str.byteIndex, match.data() + match.size()); info.GetReturnValue().Set(true); return; } re2->lastIndex = 0; info.GetReturnValue().Set(false); } uhop-node-re2-e601e5a/lib/to_string.cc000066400000000000000000000011471514245257200176050ustar00rootroot00000000000000#include "./wrapped_re2.h" #include NAN_METHOD(WrappedRE2::ToString) { // unpack arguments auto re2 = Nan::ObjectWrap::Unwrap(info.This()); if (!re2) { info.GetReturnValue().SetEmptyString(); return; } // actual work std::string buffer("/"); buffer += re2->source; buffer += "/"; if (re2->global) { buffer += "g"; } if (re2->ignoreCase) { buffer += "i"; } if (re2->multiline) { buffer += "m"; } if (re2->dotAll) { buffer += "s"; } buffer += "u"; if (re2->sticky) { buffer += "y"; } info.GetReturnValue().Set(Nan::New(buffer).ToLocalChecked()); } uhop-node-re2-e601e5a/lib/util.cc000066400000000000000000000036451514245257200165570ustar00rootroot00000000000000#include "./util.h" void consoleCall(const v8::Local &methodName, v8::Local text) { auto context = Nan::GetCurrentContext(); auto maybeConsole = bind( Nan::Get(context->Global(), Nan::New("console").ToLocalChecked()), [context](v8::Local console) { return console->ToObject(context); }); if (maybeConsole.IsEmpty()) return; auto console = maybeConsole.ToLocalChecked(); auto maybeMethod = bind( Nan::Get(console, methodName), [context](v8::Local method) { return method->ToObject(context); }); if (maybeMethod.IsEmpty()) return; auto method = maybeMethod.ToLocalChecked(); if (!method->IsFunction()) return; Nan::CallAsFunction(method, console, 1, &text); } void printDeprecationWarning(const char *warning) { std::string prefixedWarning = "DeprecationWarning: "; prefixedWarning += warning; consoleCall(Nan::New("error").ToLocalChecked(), Nan::New(prefixedWarning).ToLocalChecked()); } v8::Local callToString(const v8::Local &object) { auto context = Nan::GetCurrentContext(); auto maybeMethod = bind( Nan::Get(object, Nan::New("toString").ToLocalChecked()), [context](v8::Local method) { return method->ToObject(context); }); if (maybeMethod.IsEmpty()) return Nan::New("No toString() is found").ToLocalChecked(); auto method = maybeMethod.ToLocalChecked(); if (!method->IsFunction()) return Nan::New("No toString() is found").ToLocalChecked(); auto maybeResult = Nan::CallAsFunction(method, object, 0, nullptr); if (maybeResult.IsEmpty()) { return Nan::New("nothing was returned").ToLocalChecked(); } auto result = maybeResult.ToLocalChecked(); if (result->IsObject()) { return callToString(result->ToObject(context).ToLocalChecked()); } Nan::Utf8String val(result->ToString(context).ToLocalChecked()); return Nan::New(std::string(*val, val.length())).ToLocalChecked(); } uhop-node-re2-e601e5a/lib/util.h000066400000000000000000000007011514245257200164070ustar00rootroot00000000000000#pragma once #include "./wrapped_re2.h" template inline v8::MaybeLocal bind(v8::MaybeLocal

param, L lambda) { return param.IsEmpty() ? v8::MaybeLocal() : lambda(param.ToLocalChecked()); } void consoleCall(const v8::Local &methodName, v8::Local text); void printDeprecationWarning(const char *warning); v8::Local callToString(const v8::Local &object); uhop-node-re2-e601e5a/lib/wrapped_re2.h000066400000000000000000000111001514245257200176370ustar00rootroot00000000000000#pragma once #include #include #include struct StrVal { char *data; size_t size, length; size_t index, byteIndex; bool isBuffer, isValidIndex, isBad; StrVal() : data(NULL), size(0), length(0), index(0), byteIndex(0), isBuffer(false), isValidIndex(false), isBad(false) {} operator re2::StringPiece() const { return re2::StringPiece(data, size); } void setIndex(size_t newIndex = 0); void reset(const v8::Local &arg, size_t size, size_t length, size_t newIndex = 0, bool buffer = false); void clear() { isBad = isBuffer = isValidIndex = false; size = length = index = byteIndex = 0; data = nullptr; } }; class WrappedRE2 : public Nan::ObjectWrap { private: WrappedRE2( const re2::StringPiece &pattern, const re2::RE2::Options &options, const std::string &src, const bool &g, const bool &i, const bool &m, const bool &s, const bool &y, const bool &d) : regexp(pattern, options), source(src), global(g), ignoreCase(i), multiline(m), dotAll(s), sticky(y), hasIndices(d), lastIndex(0) {} static NAN_METHOD(New); static NAN_METHOD(ToString); static NAN_GETTER(GetSource); static NAN_GETTER(GetFlags); static NAN_GETTER(GetGlobal); static NAN_GETTER(GetIgnoreCase); static NAN_GETTER(GetMultiline); static NAN_GETTER(GetDotAll); static NAN_GETTER(GetUnicode); static NAN_GETTER(GetSticky); static NAN_GETTER(GetHasIndices); static NAN_GETTER(GetLastIndex); static NAN_SETTER(SetLastIndex); static NAN_GETTER(GetInternalSource); // RegExp methods static NAN_METHOD(Exec); static NAN_METHOD(Test); // String methods static NAN_METHOD(Match); static NAN_METHOD(Replace); static NAN_METHOD(Search); static NAN_METHOD(Split); // strict Unicode warning support static NAN_GETTER(GetUnicodeWarningLevel); static NAN_SETTER(SetUnicodeWarningLevel); public: ~WrappedRE2(); static v8::Local Init(); static inline bool HasInstance(v8::Local object) { auto isolate = v8::Isolate::GetCurrent(); auto p_tpl = Nan::GetIsolateData>(isolate); return p_tpl->Get(isolate)->HasInstance(object); } enum UnicodeWarningLevels { NOTHING, WARN_ONCE, WARN, THROW }; static UnicodeWarningLevels unicodeWarningLevel; static bool alreadyWarnedAboutUnicode; re2::RE2 regexp; std::string source; bool global; bool ignoreCase; bool multiline; bool dotAll; bool sticky; bool hasIndices; size_t lastIndex; friend struct PrepareLastString; private: Nan::Persistent lastString; // weak pointer Nan::Persistent lastCache; // weak pointer StrVal lastStringValue; void dropCache(); const StrVal &prepareArgument(const v8::Local &arg, bool ignoreLastIndex = false); void doneWithLastString(); friend struct PrepareLastString; }; struct PrepareLastString { PrepareLastString(WrappedRE2 *re2, const v8::Local &arg, bool ignoreLastIndex = false) : re2(re2) { re2->prepareArgument(arg, ignoreLastIndex); } ~PrepareLastString() { re2->doneWithLastString(); } operator const StrVal&() const { return re2->lastStringValue; } operator StrVal&() { return re2->lastStringValue; } WrappedRE2 *re2; }; // utilities inline size_t getUtf8Length(const uint16_t *from, const uint16_t *to) { size_t n = 0; while (from != to) { uint16_t ch = *from++; if (ch <= 0x7F) ++n; else if (ch <= 0x7FF) n += 2; else if (0xD800 <= ch && ch <= 0xDFFF) { n += 4; if (from == to) break; ++from; } else if (ch < 0xFFFF) n += 3; else n += 4; } return n; } inline size_t getUtf16Length(const char *from, const char *to) { size_t n = 0; while (from != to) { unsigned ch = *from & 0xFF; if (ch < 0xF0) { if (ch < 0x80) { ++from; } else { if (ch < 0xE0) { from += 2; if (from == to + 1) { ++n; break; } } else { from += 3; if (from > to && from < to + 3) { ++n; break; } } } ++n; } else { from += 4; n += 2; if (from > to && from < to + 4) break; } } return n; } inline size_t getUtf8CharSize(char ch) { return ((0xE5000000 >> ((ch >> 3) & 0x1E)) & 3) + 1; } inline size_t getUtf16PositionByCounter(const char *data, size_t from, size_t n) { for (; n > 0; --n) { size_t s = getUtf8CharSize(data[from]); from += s; if (s == 4 && n >= 2) --n; // this utf8 character will take two utf16 characters // the decrement above is protected to avoid an overflow of an unsigned integer } return from; } uhop-node-re2-e601e5a/lib/wrapped_re2_set.h000066400000000000000000000020001514245257200205110ustar00rootroot00000000000000#pragma once #include #include #include #include #include class WrappedRE2Set : public Nan::ObjectWrap { public: static v8::Local Init(); static inline bool HasInstance(v8::Local object) { auto isolate = v8::Isolate::GetCurrent(); return !constructor.IsEmpty() && constructor.Get(isolate)->HasInstance(object); } private: WrappedRE2Set(const re2::RE2::Options &options, re2::RE2::Anchor anchor, const std::string &flags) : set(options, anchor), flags(flags), anchor(anchor) {} static NAN_METHOD(New); static NAN_METHOD(Test); static NAN_METHOD(Match); static NAN_METHOD(ToString); static NAN_GETTER(GetFlags); static NAN_GETTER(GetSources); static NAN_GETTER(GetSource); static NAN_GETTER(GetSize); static NAN_GETTER(GetAnchor); static Nan::Persistent constructor; re2::RE2::Set set; std::vector sources; std::string combinedSource; std::string flags; re2::RE2::Anchor anchor; }; uhop-node-re2-e601e5a/package-lock.json000066400000000000000000000764521514245257200177470ustar00rootroot00000000000000{ "name": "re2", "version": "1.23.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "re2", "version": "1.23.3", "hasInstallScript": true, "license": "BSD-3-Clause", "dependencies": { "install-artifact-from-github": "^1.4.0", "nan": "^2.25.0", "node-gyp": "^12.2.0" }, "devDependencies": { "@types/node": "^25.2.2", "nano-benchmark": "^1.0.9", "tape-six": "^1.7.0", "tape-six-proc": "^1.2.2", "typescript": "^5.9.3" } }, "node_modules/@isaacs/balanced-match": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/@isaacs/balanced-match/-/balanced-match-4.0.1.tgz", "integrity": "sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==", "license": "MIT", "engines": { "node": "20 || >=22" } }, "node_modules/@isaacs/brace-expansion": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/@isaacs/brace-expansion/-/brace-expansion-5.0.1.tgz", "integrity": "sha512-WMz71T1JS624nWj2n2fnYAuPovhv7EUhk69R6i9dsVyzxt5eM3bjwvgk9L+APE1TRscGysAVMANkB0jh0LQZrQ==", "license": "MIT", "dependencies": { "@isaacs/balanced-match": "^4.0.1" }, "engines": { "node": "20 || >=22" } }, "node_modules/@isaacs/fs-minipass": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz", "integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==", "license": "ISC", "dependencies": { "minipass": "^7.0.4" }, "engines": { "node": ">=18.0.0" } }, "node_modules/@npmcli/agent": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/@npmcli/agent/-/agent-4.0.0.tgz", "integrity": "sha512-kAQTcEN9E8ERLVg5AsGwLNoFb+oEG6engbqAU2P43gD4JEIkNGMHdVQ096FsOAAYpZPB0RSt0zgInKIAS1l5QA==", "license": "ISC", "dependencies": { "agent-base": "^7.1.0", "http-proxy-agent": "^7.0.0", "https-proxy-agent": "^7.0.1", "lru-cache": "^11.2.1", "socks-proxy-agent": "^8.0.3" }, "engines": { "node": "^20.17.0 || >=22.9.0" } }, "node_modules/@npmcli/fs": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-5.0.0.tgz", "integrity": "sha512-7OsC1gNORBEawOa5+j2pXN9vsicaIOH5cPXxoR6fJOmH6/EXpJB2CajXOu1fPRFun2m1lktEFX11+P89hqO/og==", "license": "ISC", "dependencies": { "semver": "^7.3.5" }, "engines": { "node": "^20.17.0 || >=22.9.0" } }, "node_modules/@types/node": { "version": "25.2.2", "resolved": "https://registry.npmjs.org/@types/node/-/node-25.2.2.tgz", "integrity": "sha512-BkmoP5/FhRYek5izySdkOneRyXYN35I860MFAGupTdebyE66uZaR+bXLHq8k4DirE5DwQi3NuhvRU1jqTVwUrQ==", "dev": true, "license": "MIT", "dependencies": { "undici-types": "~7.16.0" } }, "node_modules/abbrev": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-4.0.0.tgz", "integrity": "sha512-a1wflyaL0tHtJSmLSOVybYhy22vRih4eduhhrkcjgrWGnRfrZtovJ2FRjxuTtkkj47O/baf0R86QU5OuYpz8fA==", "license": "ISC", "engines": { "node": "^20.17.0 || >=22.9.0" } }, "node_modules/agent-base": { "version": "7.1.4", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", "license": "MIT", "engines": { "node": ">= 14" } }, "node_modules/cacache": { "version": "20.0.3", "resolved": "https://registry.npmjs.org/cacache/-/cacache-20.0.3.tgz", "integrity": "sha512-3pUp4e8hv07k1QlijZu6Kn7c9+ZpWWk4j3F8N3xPuCExULobqJydKYOTj1FTq58srkJsXvO7LbGAH4C0ZU3WGw==", "license": "ISC", "dependencies": { "@npmcli/fs": "^5.0.0", "fs-minipass": "^3.0.0", "glob": "^13.0.0", "lru-cache": "^11.1.0", "minipass": "^7.0.3", "minipass-collect": "^2.0.1", "minipass-flush": "^1.0.5", "minipass-pipeline": "^1.2.4", "p-map": "^7.0.2", "ssri": "^13.0.0", "unique-filename": "^5.0.0" }, "engines": { "node": "^20.17.0 || >=22.9.0" } }, "node_modules/chownr": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz", "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==", "license": "BlueOak-1.0.0", "engines": { "node": ">=18" } }, "node_modules/commander": { "version": "14.0.3", "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.3.tgz", "integrity": "sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==", "dev": true, "license": "MIT", "engines": { "node": ">=20" } }, "node_modules/console-toolkit": { "version": "1.2.9", "resolved": "https://registry.npmjs.org/console-toolkit/-/console-toolkit-1.2.9.tgz", "integrity": "sha512-8XxH7/Ye6pymPnGOrNBbLSXQ9sCGXpYuE4P/s8/tq9s7vcJMuE6DP7tC8mTQ9QtZ7wg+c6W8k7gpTmr4/GKCwQ==", "dev": true, "license": "BSD-3-Clause", "funding": { "type": "github", "url": "https://github.com/sponsors/uhop" } }, "node_modules/debug": { "version": "4.4.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "license": "MIT", "dependencies": { "ms": "^2.1.3" }, "engines": { "node": ">=6.0" }, "peerDependenciesMeta": { "supports-color": { "optional": true } } }, "node_modules/dollar-shell": { "version": "1.1.8", "resolved": "https://registry.npmjs.org/dollar-shell/-/dollar-shell-1.1.8.tgz", "integrity": "sha512-AFDVLtCXSkbwKSdYp10h2mgNdbkXNYBSKO97GlDsoVgcEjDx8MuSS5OKbT7RAgjUdcTAwo64TP4j1L1MpKO06Q==", "dev": true, "license": "BSD-3-Clause", "funding": { "type": "github", "url": "https://github.com/sponsors/uhop" } }, "node_modules/encoding": { "version": "0.1.13", "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", "license": "MIT", "optional": true, "dependencies": { "iconv-lite": "^0.6.2" } }, "node_modules/env-paths": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", "license": "MIT", "engines": { "node": ">=6" } }, "node_modules/err-code": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz", "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==", "license": "MIT" }, "node_modules/exponential-backoff": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/exponential-backoff/-/exponential-backoff-3.1.1.tgz", "integrity": "sha512-dX7e/LHVJ6W3DE1MHWi9S1EYzDESENfLrYohG2G++ovZrYOkm4Knwa0mc1cn84xJOR4KEU0WSchhLbd0UklbHw==", "license": "Apache-2.0" }, "node_modules/fdir": { "version": "6.4.4", "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.4.tgz", "integrity": "sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==", "license": "MIT", "peerDependencies": { "picomatch": "^3 || ^4" }, "peerDependenciesMeta": { "picomatch": { "optional": true } } }, "node_modules/fs-minipass": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-3.0.3.tgz", "integrity": "sha512-XUBA9XClHbnJWSfBzjkm6RvPsyg3sryZt06BEQoXcF7EK/xpGaQYJgQKDJSUH5SGZ76Y7pFx1QBnXz09rU5Fbw==", "license": "ISC", "dependencies": { "minipass": "^7.0.3" }, "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/glob": { "version": "13.0.0", "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.0.tgz", "integrity": "sha512-tvZgpqk6fz4BaNZ66ZsRaZnbHvP/jG3uKJvAZOwEVUL4RTA5nJeeLYfyN9/VA8NX/V3IBG+hkeuGpKjvELkVhA==", "license": "BlueOak-1.0.0", "dependencies": { "minimatch": "^10.1.1", "minipass": "^7.1.2", "path-scurry": "^2.0.0" }, "engines": { "node": "20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, "node_modules/graceful-fs": { "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", "license": "ISC" }, "node_modules/http-cache-semantics": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz", "integrity": "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==", "license": "BSD-2-Clause" }, "node_modules/http-proxy-agent": { "version": "7.0.2", "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", "license": "MIT", "dependencies": { "agent-base": "^7.1.0", "debug": "^4.3.4" }, "engines": { "node": ">= 14" } }, "node_modules/https-proxy-agent": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", "license": "MIT", "dependencies": { "agent-base": "^7.1.2", "debug": "4" }, "engines": { "node": ">= 14" } }, "node_modules/iconv-lite": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", "license": "MIT", "optional": true, "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" }, "engines": { "node": ">=0.10.0" } }, "node_modules/imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", "license": "MIT", "engines": { "node": ">=0.8.19" } }, "node_modules/install-artifact-from-github": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/install-artifact-from-github/-/install-artifact-from-github-1.4.0.tgz", "integrity": "sha512-+y6WywKZREw5rq7U2jvr2nmZpT7cbWbQQ0N/qfcseYnzHFz2cZz1Et52oY+XttYuYeTkI8Y+R2JNWj68MpQFSg==", "license": "BSD-3-Clause", "bin": { "install-from-cache": "bin/install-from-cache.js", "save-to-github-cache": "bin/save-to-github-cache.js" } }, "node_modules/ip-address": { "version": "10.1.0", "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.1.0.tgz", "integrity": "sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==", "license": "MIT", "engines": { "node": ">= 12" } }, "node_modules/isexe": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz", "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==", "license": "ISC", "engines": { "node": ">=16" } }, "node_modules/lru-cache": { "version": "11.2.4", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.4.tgz", "integrity": "sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg==", "license": "BlueOak-1.0.0", "engines": { "node": "20 || >=22" } }, "node_modules/make-fetch-happen": { "version": "15.0.3", "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-15.0.3.tgz", "integrity": "sha512-iyyEpDty1mwW3dGlYXAJqC/azFn5PPvgKVwXayOGBSmKLxhKZ9fg4qIan2ePpp1vJIwfFiO34LAPZgq9SZW9Aw==", "license": "ISC", "dependencies": { "@npmcli/agent": "^4.0.0", "cacache": "^20.0.1", "http-cache-semantics": "^4.1.1", "minipass": "^7.0.2", "minipass-fetch": "^5.0.0", "minipass-flush": "^1.0.5", "minipass-pipeline": "^1.2.4", "negotiator": "^1.0.0", "proc-log": "^6.0.0", "promise-retry": "^2.0.1", "ssri": "^13.0.0" }, "engines": { "node": "^20.17.0 || >=22.9.0" } }, "node_modules/minimatch": { "version": "10.1.1", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.1.1.tgz", "integrity": "sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ==", "license": "BlueOak-1.0.0", "dependencies": { "@isaacs/brace-expansion": "^5.0.0" }, "engines": { "node": "20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, "node_modules/minipass": { "version": "7.1.2", "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", "license": "ISC", "engines": { "node": ">=16 || 14 >=14.17" } }, "node_modules/minipass-collect": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-2.0.1.tgz", "integrity": "sha512-D7V8PO9oaz7PWGLbCACuI1qEOsq7UKfLotx/C0Aet43fCUB/wfQ7DYeq2oR/svFJGYDHPr38SHATeaj/ZoKHKw==", "license": "ISC", "dependencies": { "minipass": "^7.0.3" }, "engines": { "node": ">=16 || 14 >=14.17" } }, "node_modules/minipass-fetch": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-5.0.0.tgz", "integrity": "sha512-fiCdUALipqgPWrOVTz9fw0XhcazULXOSU6ie40DDbX1F49p1dBrSRBuswndTx1x3vEb/g0FT7vC4c4C2u/mh3A==", "license": "MIT", "dependencies": { "minipass": "^7.0.3", "minipass-sized": "^1.0.3", "minizlib": "^3.0.1" }, "engines": { "node": "^20.17.0 || >=22.9.0" }, "optionalDependencies": { "encoding": "^0.1.13" } }, "node_modules/minipass-flush": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz", "integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==", "license": "ISC", "dependencies": { "minipass": "^3.0.0" }, "engines": { "node": ">= 8" } }, "node_modules/minipass-flush/node_modules/minipass": { "version": "3.3.6", "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", "license": "ISC", "dependencies": { "yallist": "^4.0.0" }, "engines": { "node": ">=8" } }, "node_modules/minipass-flush/node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "license": "ISC" }, "node_modules/minipass-pipeline": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz", "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==", "license": "ISC", "dependencies": { "minipass": "^3.0.0" }, "engines": { "node": ">=8" } }, "node_modules/minipass-pipeline/node_modules/minipass": { "version": "3.3.6", "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", "license": "ISC", "dependencies": { "yallist": "^4.0.0" }, "engines": { "node": ">=8" } }, "node_modules/minipass-pipeline/node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "license": "ISC" }, "node_modules/minipass-sized": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/minipass-sized/-/minipass-sized-1.0.3.tgz", "integrity": "sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==", "license": "ISC", "dependencies": { "minipass": "^3.0.0" }, "engines": { "node": ">=8" } }, "node_modules/minipass-sized/node_modules/minipass": { "version": "3.3.6", "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", "license": "ISC", "dependencies": { "yallist": "^4.0.0" }, "engines": { "node": ">=8" } }, "node_modules/minipass-sized/node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "license": "ISC" }, "node_modules/minizlib": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.1.0.tgz", "integrity": "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==", "license": "MIT", "dependencies": { "minipass": "^7.1.2" }, "engines": { "node": ">= 18" } }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "license": "MIT" }, "node_modules/nan": { "version": "2.25.0", "resolved": "https://registry.npmjs.org/nan/-/nan-2.25.0.tgz", "integrity": "sha512-0M90Ag7Xn5KMLLZ7zliPWP3rT90P6PN+IzVFS0VqmnPktBk3700xUVv8Ikm9EUaUE5SDWdp/BIxdENzVznpm1g==", "license": "MIT" }, "node_modules/nano-benchmark": { "version": "1.0.9", "resolved": "https://registry.npmjs.org/nano-benchmark/-/nano-benchmark-1.0.9.tgz", "integrity": "sha512-5W/G7R4Gn+1uqVi7IC2ZrfphhyTLd+g04X/zzq04P3xO/lZppwlUwdaniXncJPKqd4+tOF05SQQEYBJGjNE+kQ==", "dev": true, "license": "BSD-3-Clause", "dependencies": { "commander": "^14.0.3", "console-toolkit": "^1.2.9" }, "bin": { "nano-bench": "bin/nano-bench.js", "nano-watch": "bin/nano-watch.js" }, "funding": { "type": "github", "url": "https://github.com/sponsors/uhop" } }, "node_modules/negotiator": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", "license": "MIT", "engines": { "node": ">= 0.6" } }, "node_modules/node-gyp": { "version": "12.2.0", "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-12.2.0.tgz", "integrity": "sha512-q23WdzrQv48KozXlr0U1v9dwO/k59NHeSzn6loGcasyf0UnSrtzs8kRxM+mfwJSf0DkX0s43hcqgnSO4/VNthQ==", "license": "MIT", "dependencies": { "env-paths": "^2.2.0", "exponential-backoff": "^3.1.1", "graceful-fs": "^4.2.6", "make-fetch-happen": "^15.0.0", "nopt": "^9.0.0", "proc-log": "^6.0.0", "semver": "^7.3.5", "tar": "^7.5.4", "tinyglobby": "^0.2.12", "which": "^6.0.0" }, "bin": { "node-gyp": "bin/node-gyp.js" }, "engines": { "node": "^20.17.0 || >=22.9.0" } }, "node_modules/nopt": { "version": "9.0.0", "resolved": "https://registry.npmjs.org/nopt/-/nopt-9.0.0.tgz", "integrity": "sha512-Zhq3a+yFKrYwSBluL4H9XP3m3y5uvQkB/09CwDruCiRmR/UJYnn9W4R48ry0uGC70aeTPKLynBtscP9efFFcPw==", "license": "ISC", "dependencies": { "abbrev": "^4.0.0" }, "bin": { "nopt": "bin/nopt.js" }, "engines": { "node": "^20.17.0 || >=22.9.0" } }, "node_modules/p-map": { "version": "7.0.4", "resolved": "https://registry.npmjs.org/p-map/-/p-map-7.0.4.tgz", "integrity": "sha512-tkAQEw8ysMzmkhgw8k+1U/iPhWNhykKnSk4Rd5zLoPJCuJaGRPo6YposrZgaxHKzDHdDWWZvE/Sk7hsL2X/CpQ==", "license": "MIT", "engines": { "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/path-scurry": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.1.tgz", "integrity": "sha512-oWyT4gICAu+kaA7QWk/jvCHWarMKNs6pXOGWKDTr7cw4IGcUbW+PeTfbaQiLGheFRpjo6O9J0PmyMfQPjH71oA==", "license": "BlueOak-1.0.0", "dependencies": { "lru-cache": "^11.0.0", "minipass": "^7.1.2" }, "engines": { "node": "20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, "node_modules/picomatch": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", "license": "MIT", "engines": { "node": ">=12" }, "funding": { "url": "https://github.com/sponsors/jonschlinkert" } }, "node_modules/proc-log": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.1.0.tgz", "integrity": "sha512-iG+GYldRf2BQ0UDUAd6JQ/RwzaQy6mXmsk/IzlYyal4A4SNFw54MeH4/tLkF4I5WoWG9SQwuqWzS99jaFQHBuQ==", "license": "ISC", "engines": { "node": "^20.17.0 || >=22.9.0" } }, "node_modules/promise-retry": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz", "integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==", "license": "MIT", "dependencies": { "err-code": "^2.0.2", "retry": "^0.12.0" }, "engines": { "node": ">=10" } }, "node_modules/retry": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", "license": "MIT", "engines": { "node": ">= 4" } }, "node_modules/safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "license": "MIT", "optional": true }, "node_modules/semver": { "version": "7.7.3", "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", "license": "ISC", "bin": { "semver": "bin/semver.js" }, "engines": { "node": ">=10" } }, "node_modules/smart-buffer": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", "license": "MIT", "engines": { "node": ">= 6.0.0", "npm": ">= 3.0.0" } }, "node_modules/socks": { "version": "2.8.7", "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.7.tgz", "integrity": "sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A==", "license": "MIT", "dependencies": { "ip-address": "^10.0.1", "smart-buffer": "^4.2.0" }, "engines": { "node": ">= 10.0.0", "npm": ">= 3.0.0" } }, "node_modules/socks-proxy-agent": { "version": "8.0.5", "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.5.tgz", "integrity": "sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==", "license": "MIT", "dependencies": { "agent-base": "^7.1.2", "debug": "^4.3.4", "socks": "^2.8.3" }, "engines": { "node": ">= 14" } }, "node_modules/ssri": { "version": "13.0.0", "resolved": "https://registry.npmjs.org/ssri/-/ssri-13.0.0.tgz", "integrity": "sha512-yizwGBpbCn4YomB2lzhZqrHLJoqFGXihNbib3ozhqF/cIp5ue+xSmOQrjNasEE62hFxsCcg/V/z23t4n8jMEng==", "license": "ISC", "dependencies": { "minipass": "^7.0.3" }, "engines": { "node": "^20.17.0 || >=22.9.0" } }, "node_modules/tape-six": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/tape-six/-/tape-six-1.7.0.tgz", "integrity": "sha512-G8+Y1APjC0h11rRF8rCflLHmDj7/e9JGdMlBd6HD6yQL70argw16Ql545fWTn9y367FTg6UmZkLYSZ0Jy4kvnA==", "dev": true, "license": "BSD-3-Clause", "bin": { "tape6": "bin/tape6.js", "tape6-bun": "bin/tape6-bun.js", "tape6-deno": "bin/tape6-deno.js", "tape6-runner": "bin/tape6-runner.js", "tape6-seq": "bin/tape6-seq.js", "tape6-server": "bin/tape6-server.js" }, "funding": { "url": "https://github.com/sponsors/uhop" } }, "node_modules/tape-six-proc": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/tape-six-proc/-/tape-six-proc-1.2.2.tgz", "integrity": "sha512-pFziiBGXQItd9CbX/vAd+/JUS3RIC8T0+df4QK+IKXjNt7Q7X9OZtViXl3AMKJUrk+KLGizk4ziQ8+RQZ+6UOw==", "dev": true, "license": "BSD-3-Clause", "dependencies": { "dollar-shell": "^1.1.8", "tape-six": "^1.7.0" }, "bin": { "tape6-proc": "bin/tape6-proc.js" }, "funding": { "url": "https://github.com/sponsors/uhop" } }, "node_modules/tar": { "version": "7.5.7", "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.7.tgz", "integrity": "sha512-fov56fJiRuThVFXD6o6/Q354S7pnWMJIVlDBYijsTNx6jKSE4pvrDTs6lUnmGvNyfJwFQQwWy3owKz1ucIhveQ==", "license": "BlueOak-1.0.0", "dependencies": { "@isaacs/fs-minipass": "^4.0.0", "chownr": "^3.0.0", "minipass": "^7.1.2", "minizlib": "^3.1.0", "yallist": "^5.0.0" }, "engines": { "node": ">=18" } }, "node_modules/tinyglobby": { "version": "0.2.13", "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.13.tgz", "integrity": "sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw==", "license": "MIT", "dependencies": { "fdir": "^6.4.4", "picomatch": "^4.0.2" }, "engines": { "node": ">=12.0.0" }, "funding": { "url": "https://github.com/sponsors/SuperchupuDev" } }, "node_modules/typescript": { "version": "5.9.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" }, "engines": { "node": ">=14.17" } }, "node_modules/undici-types": { "version": "7.16.0", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", "dev": true, "license": "MIT" }, "node_modules/unique-filename": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-5.0.0.tgz", "integrity": "sha512-2RaJTAvAb4owyjllTfXzFClJ7WsGxlykkPvCr9pA//LD9goVq+m4PPAeBgNodGZ7nSrntT/auWpJ6Y5IFXcfjg==", "license": "ISC", "dependencies": { "unique-slug": "^6.0.0" }, "engines": { "node": "^20.17.0 || >=22.9.0" } }, "node_modules/unique-slug": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-6.0.0.tgz", "integrity": "sha512-4Lup7Ezn8W3d52/xBhZBVdx323ckxa7DEvd9kPQHppTkLoJXw6ltrBCyj5pnrxj0qKDxYMJ56CoxNuFCscdTiw==", "license": "ISC", "dependencies": { "imurmurhash": "^0.1.4" }, "engines": { "node": "^20.17.0 || >=22.9.0" } }, "node_modules/which": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/which/-/which-6.0.0.tgz", "integrity": "sha512-f+gEpIKMR9faW/JgAgPK1D7mekkFoqbmiwvNzuhsHetni20QSgzg9Vhn0g2JSJkkfehQnqdUAx7/e15qS1lPxg==", "license": "ISC", "dependencies": { "isexe": "^3.1.1" }, "bin": { "node-which": "bin/which.js" }, "engines": { "node": "^20.17.0 || >=22.9.0" } }, "node_modules/yallist": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz", "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==", "license": "BlueOak-1.0.0", "engines": { "node": ">=18" } } } } uhop-node-re2-e601e5a/package.json000066400000000000000000000040201514245257200167770ustar00rootroot00000000000000{ "name": "re2", "version": "1.23.3", "description": "Bindings for RE2: fast, safe alternative to backtracking regular expression engines.", "homepage": "https://github.com/uhop/node-re2", "bugs": "https://github.com/uhop/node-re2/issues", "type": "commonjs", "main": "re2.js", "types": "re2.d.ts", "files": [ "binding.gyp", "lib", "re2.d.ts", "scripts/*.js", "vendor" ], "dependencies": { "install-artifact-from-github": "^1.4.0", "nan": "^2.25.0", "node-gyp": "^12.2.0" }, "devDependencies": { "@types/node": "^25.2.2", "nano-benchmark": "^1.0.9", "tape-six": "^1.7.0", "tape-six-proc": "^1.2.2", "typescript": "^5.9.3" }, "scripts": { "test": "tape6-proc --flags FO", "test:seq": "tape6-seq --flags FO", "save-to-github": "save-to-github-cache --artifact build/Release/re2.node", "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", "verify-build": "node scripts/verify-build.js", "build:dev": "node-gyp -j max build --debug", "build": "node-gyp -j max build", "build1": "node-gyp build", "rebuild:dev": "node-gyp -j max rebuild --debug", "rebuild": "node-gyp -j max rebuild", "rebuild1": "node-gyp rebuild", "clean": "node-gyp clean && node-gyp configure", "clean-build": "node-gyp clean", "ts-check": "tsc --noEmit", "lint": "prettier --check *.js *.ts tests/ bench/", "lint:fix": "prettier --write *.js *.ts tests/ bench/" }, "github": "https://github.com/uhop/node-re2", "repository": { "type": "git", "url": "git://github.com/uhop/node-re2.git" }, "keywords": [ "RegExp", "RegEx", "text processing", "PCRE alternative" ], "author": "Eugene Lazutkin (https://lazutkin.com/)", "license": "BSD-3-Clause", "tape6": { "tests": [ "/tests/test-*.mjs", "/tests/test-*.ts" ] } } uhop-node-re2-e601e5a/re2.d.ts000066400000000000000000000043441514245257200160040ustar00rootroot00000000000000declare module 're2' { interface RE2BufferExecArray { index: number; input: Buffer; 0: Buffer; groups?: { [key: string]: Buffer; }; } interface RE2BufferMatchArray { index?: number; input?: Buffer; 0: Buffer; groups?: { [key: string]: Buffer; }; } interface RE2 extends RegExp { exec(str: string): RegExpExecArray | null; exec(str: Buffer): RE2BufferExecArray | null; match(str: string): RegExpMatchArray | null; match(str: Buffer): RE2BufferMatchArray | null; test(str: string | Buffer): boolean; replace( str: K, replaceValue: string | Buffer ): K; replace( str: K, replacer: (substring: string, ...args: any[]) => string | Buffer ): K; search(str: string | Buffer): number; split(str: K, limit?: number): K[]; } interface RE2SetOptions { anchor?: 'unanchored' | 'start' | 'both'; } interface RE2Set { readonly size: number; readonly source: string; readonly sources: string[]; readonly flags: string; readonly anchor: 'unanchored' | 'start' | 'both'; match(str: string | Buffer): number[]; test(str: string | Buffer): boolean; toString(): string; } interface RE2SetConstructor { new ( patterns: Iterable, flagsOrOptions?: string | Buffer | RE2SetOptions, options?: RE2SetOptions ): RE2Set; ( patterns: Iterable, flagsOrOptions?: string | Buffer | RE2SetOptions, options?: RE2SetOptions ): RE2Set; readonly prototype: RE2Set; } interface RE2Constructor extends RegExpConstructor { new (pattern: Buffer | RegExp | RE2 | string): RE2; new (pattern: Buffer | string, flags?: string | Buffer): RE2; (pattern: Buffer | RegExp | RE2 | string): RE2; (pattern: Buffer | string, flags?: string | Buffer): RE2; readonly prototype: RE2; unicodeWarningLevel: 'nothing' | 'warnOnce' | 'warn' | 'throw'; getUtf8Length(value: string): number; getUtf16Length(value: Buffer): number; Set: RE2SetConstructor; } var RE2: RE2Constructor; export = RE2; } uhop-node-re2-e601e5a/re2.js000066400000000000000000000015271514245257200155500ustar00rootroot00000000000000'use strict'; const RE2 = require('./build/Release/re2.node'); // const RE2 = require('./build/Debug/re2.node'); const setAliases = (object, dict) => { for (let [name, alias] of Object.entries(dict)) { Object.defineProperty( object, alias, Object.getOwnPropertyDescriptor(object, name) ); } }; setAliases(RE2.prototype, { match: Symbol.match, search: Symbol.search, replace: Symbol.replace, split: Symbol.split }); RE2.prototype[Symbol.matchAll] = function* (str) { if (!this.global) throw TypeError( 'String.prototype.matchAll() is called with a non-global RE2 argument' ); const re = new RE2(this); re.lastIndex = this.lastIndex; for (;;) { const result = re.exec(str); if (!result) break; if (result[0] === '') ++re.lastIndex; yield result; } }; module.exports = RE2; uhop-node-re2-e601e5a/scripts/000077500000000000000000000000001514245257200162045ustar00rootroot00000000000000uhop-node-re2-e601e5a/scripts/verify-build.js000066400000000000000000000005451514245257200211470ustar00rootroot00000000000000'use strict'; // This is a light-weight script to make sure that the package works. const assert = require('assert').strict; const RE2 = require("../re2"); const sample = "abbcdefabh"; const re1 = new RE2("ab*", "g"); assert(re1.test(sample)); const re2 = RE2("ab*"); assert(re2.test(sample)); const re3 = new RE2("abc"); assert(!re3.test(sample)); uhop-node-re2-e601e5a/tests/000077500000000000000000000000001514245257200156575ustar00rootroot00000000000000uhop-node-re2-e601e5a/tests/manual/000077500000000000000000000000001514245257200171345ustar00rootroot00000000000000uhop-node-re2-e601e5a/tests/manual/matchall-bench.js000066400000000000000000000004131514245257200223320ustar00rootroot00000000000000'use strict'; const RE2 = require('../../re2'); const N = 1_000_000; const s = 'a'.repeat(N), re = new RE2('a', 'g'), matches = s.matchAll(re); let n = 0; for (const _ of matches) ++n; if (n !== s.length) console.log('Wrong result.'); console.log('Done.'); uhop-node-re2-e601e5a/tests/manual/memory-check.js000066400000000000000000000012501514245257200220530ustar00rootroot00000000000000'use strict'; const RE2 = require('../../re2.js'); const L = 20 * 1024 * 1024, N = 100; if (typeof globalThis.gc != 'function') console.log( "Warning: to run it with explicit gc() calls, you should use --expose-gc as a node's argument." ); const gc = typeof globalThis.gc == 'function' ? globalThis.gc : () => {}; const s = 'a'.repeat(L), objects = []; for (let i = 0; i < N; ++i) { const re2 = new RE2('x', 'g'); objects.push(re2); const result = s.replace(re2, ''); if (result.length !== s.length) console.log('Wrong result.'); gc(); } console.log( 'Done. Now it is spinning: check the memory consumption! To stop it, press Ctrl+C.' ); for (;;); uhop-node-re2-e601e5a/tests/manual/memory-monitor.js000066400000000000000000000045131514245257200224720ustar00rootroot00000000000000'use strict'; const RE2 = require('../../re2'); const N = 5_000_000; console.log('Never-ending loop: exit with Ctrl+C.'); const aCharCode = 'a'.charCodeAt(0); const randomAlpha = () => String.fromCharCode(aCharCode + Math.floor(Math.random() * 26)); const humanizeNumber = n => { const negative = n < 0; if (negative) n = -n; const s = n.toFixed(); let group1 = s.length % 3; if (!group1) group1 = 3; let result = s.substring(0, group1); for (let i = group1; i < s.length; i += 3) { result += ',' + s.substring(i, i + 3); } return (negative ? '-' : '') + result; }; const CSI = '\x1B['; const cursorUp = (n = 1) => CSI + (n > 1 ? n.toFixed() : '') + 'A'; const sgr = (cmd = '') => CSI + (Array.isArray(cmd) ? cmd.join(';') : cmd) + 'm'; const RESET = sgr(); const NOTE = sgr(91); let first = true; const maxMemory = { heapTotal: 0, heapUsed: 0, external: 0, arrayBuffers: 0, rss: 0 }, labels = { heapTotal: 'heap total', heapUsed: 'heap used', external: 'external', arrayBuffers: 'array buffers', rss: 'resident set size' }, maxLabelSize = Math.max( ...Array.from(Object.values(labels)).map(label => label.length) ); const report = () => { const memoryUsage = process.memoryUsage(), previousMax = {...maxMemory}; console.log( (first ? '' : '\r' + cursorUp(6)) + ''.padStart(maxLabelSize + 1), 'Current'.padStart(15), 'Max'.padStart(15) ); for (const name in maxMemory) { const prefix = previousMax[name] && previousMax[name] < memoryUsage[name] ? NOTE : RESET; console.log( (labels[name] + ':').padStart(maxLabelSize + 1), prefix + humanizeNumber(memoryUsage[name]).padStart(15) + RESET, humanizeNumber(maxMemory[name]).padStart(15) ); } for (const [name, value] of Object.entries(maxMemory)) { maxMemory[name] = Math.max(value, memoryUsage[name]); } first = false; }; for (;;) { const re2 = new RE2(randomAlpha(), 'g'); let s = ''; for (let i = 0; i < N; ++i) s += randomAlpha(); let n = 0; for (const _ of s.matchAll(re2)) ++n; re2.lastIndex = 0; const r = s.replace(re2, ''); if (r.length + n != s.length) { console.log( 'ERROR!', 's:', s.length, 'r:', r.length, 'n:', n, 're2:', re2.toString() ); break; } report(); } uhop-node-re2-e601e5a/tests/manual/worker.js000066400000000000000000000014451514245257200210070ustar00rootroot00000000000000'use strict'; const {Worker, isMainThread} = require('worker_threads'); const RE2 = require('../../re2'); if (isMainThread) { // This re-loads the current file inside a Worker instance. console.log('Inside Master!'); const worker = new Worker(__filename); worker.on('exit', code => { console.log('Exit code:', code); test('#2'); }); test('#1'); } else { console.log('Inside Worker!'); test(); } function test(msg) { msg && console.log(isMainThread ? 'Main' : 'Worker', msg); const a = new RE2('^\\d+$'); console.log( isMainThread, a.test('123'), a.test('abc'), a.test('123abc'), a instanceof RE2 ); const b = RE2('^\\d+$'); console.log( isMainThread, b.test('123'), b.test('abc'), b.test('123abc'), b instanceof RE2 ); } uhop-node-re2-e601e5a/tests/test-exec.mjs000066400000000000000000000241331514245257200202760ustar00rootroot00000000000000import test from 'tape-six'; import {default as RE2} from '../re2.js'; // tests // These tests are copied from MDN: // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/exec test('exec basic', t => { const re = new RE2('quick\\s(brown).+?(jumps)', 'ig'); t.equal(re.source, 'quick\\s(brown).+?(jumps)'); t.ok(re.ignoreCase); t.ok(re.global); t.ok(!re.multiline); const result = re.exec('The Quick Brown Fox Jumps Over The Lazy Dog'); t.deepEqual(Array.from(result), ['Quick Brown Fox Jumps', 'Brown', 'Jumps']); t.equal(result.index, 4); t.equal(result.input, 'The Quick Brown Fox Jumps Over The Lazy Dog'); t.equal(re.lastIndex, 25); }); test('exec succ', t => { const str = 'abbcdefabh'; const re = new RE2('ab*', 'g'); let result = re.exec(str); t.ok(result); t.equal(result[0], 'abb'); t.equal(result.index, 0); t.equal(re.lastIndex, 3); result = re.exec(str); t.ok(result); t.equal(result[0], 'ab'); t.equal(result.index, 7); t.equal(re.lastIndex, 9); result = re.exec(str); t.notOk(result); }); test('exec simple', t => { const re = new RE2('(hello \\S+)'); const result = re.exec('This is a hello world!'); t.equal(result[1], 'hello world!'); }); test('exec fail', t => { const re = new RE2('(a+)?(b+)?'); let result = re.exec('aaabb'); t.equal(result[1], 'aaa'); t.equal(result[2], 'bb'); result = re.exec('aaacbb'); t.equal(result[1], 'aaa'); t.equal(result[2], undefined); t.equal(result.length, 3); }); test('exec anchored to beginning', t => { const re = RE2('^hello', 'g'); const result = re.exec('hellohello'); t.deepEqual(Array.from(result), ['hello']); t.equal(result.index, 0); t.equal(re.lastIndex, 5); t.equal(re.exec('hellohello'), null); }); test('exec invalid', t => { const re = RE2(''); try { re.exec({ toString() { throw 'corner'; } }); t.fail(); // shouldn't be here } catch (e) { t.equal(e, 'corner'); } }); test('exec anchor 1', t => { const re = new RE2('b|^a', 'g'); var result = re.exec('aabc'); t.ok(result); t.equal(result.index, 0); t.equal(re.lastIndex, 1); result = re.exec('aabc'); t.ok(result); t.equal(result.index, 2); t.equal(re.lastIndex, 3); result = re.exec('aabc'); t.notOk(result); }); test('exec anchor 2', t => { const re = new RE2('(?:^a)', 'g'); let result = re.exec('aabc'); t.ok(result); t.equal(result.index, 0); t.equal(re.lastIndex, 1); result = re.exec('aabc'); t.notOk(result); }); // Unicode tests test('exec unicode', t => { const re = new RE2('охотник\\s(желает).+?(где)', 'ig'); t.equal(re.source, 'охотник\\s(желает).+?(где)'); t.ok(re.ignoreCase); t.ok(re.global); t.ok(!re.multiline); const result = re.exec('Каждый Охотник Желает Знать Где Сидит Фазан'); t.deepEqual(Array.from(result), [ 'Охотник Желает Знать Где', 'Желает', 'Где' ]); t.equal(result.index, 7); t.equal(result.input, 'Каждый Охотник Желает Знать Где Сидит Фазан'); t.equal(re.lastIndex, 31); t.equal( result.input.substr(result.index), 'Охотник Желает Знать Где Сидит Фазан' ); t.equal(result.input.substr(re.lastIndex), ' Сидит Фазан'); }); test('exec unicode subsequent', t => { const str = 'аббвгдеабё'; const re = new RE2('аб*', 'g'); let result = re.exec(str); t.ok(result); t.equal(result[0], 'абб'); t.equal(result.index, 0); t.equal(re.lastIndex, 3); result = re.exec(str); t.ok(result); t.equal(result[0], 'аб'); t.equal(result.index, 7); t.equal(re.lastIndex, 9); result = re.exec(str); t.notOk(result); }); test('exec unicode supplementary', t => { const re = new RE2('\\u{1F603}', 'g'); t.equal(re.source, '\\u{1F603}'); t.notOk(re.ignoreCase); t.ok(re.global); t.notOk(re.multiline); const result = re.exec('\u{1F603}'); // 1F603 is the SMILING FACE WITH OPEN MOUTH emoji t.deepEqual(Array.from(result), ['\u{1F603}']); t.equal(result.index, 0); t.equal(result.input, '\u{1F603}'); t.equal(re.lastIndex, 2); const re2 = new RE2('.', 'g'); t.equal(re2.source, '.'); t.notOk(re2.ignoreCase); t.ok(re2.global); t.notOk(re2.multiline); const result2 = re2.exec('\u{1F603}'); t.deepEqual(Array.from(result2), ['\u{1F603}']); t.equal(result2.index, 0); t.equal(result2.input, '\u{1F603}'); t.equal(re2.lastIndex, 2); const re3 = new RE2('[\u{1F603}-\u{1F605}]', 'g'); t.equal(re3.source, '[\u{1F603}-\u{1F605}]'); t.notOk(re3.ignoreCase); t.ok(re3.global); t.notOk(re3.multiline); const result3 = re3.exec('\u{1F604}'); t.deepEqual(Array.from(result3), ['\u{1F604}']); t.equal(result3.index, 0); t.equal(result3.input, '\u{1F604}'); t.equal(re3.lastIndex, 2); }); // Buffer tests test('exec buffer', t => { const re = new RE2('охотник\\s(желает).+?(где)', 'ig'); const buf = Buffer.from('Каждый Охотник Желает Знать Где Сидит Фазан'); const result = re.exec(buf); t.equal(result.length, 3); t.ok(result[0] instanceof Buffer); t.ok(result[1] instanceof Buffer); t.ok(result[2] instanceof Buffer); t.equal(result[0].toString(), 'Охотник Желает Знать Где'); t.equal(result[1].toString(), 'Желает'); t.equal(result[2].toString(), 'Где'); t.equal(result.index, 13); t.ok(result.input instanceof Buffer); t.equal( result.input.toString(), 'Каждый Охотник Желает Знать Где Сидит Фазан' ); t.equal(re.lastIndex, 58); t.equal( result.input.toString('utf8', result.index), 'Охотник Желает Знать Где Сидит Фазан' ); t.equal(result.input.toString('utf8', re.lastIndex), ' Сидит Фазан'); }); // Sticky tests test('exec sticky', t => { const re = new RE2('\\s+', 'y'); t.equal(re.exec('Hello world, how are you?'), null); re.lastIndex = 5; const result = re.exec('Hello world, how are you?'); t.deepEqual(Array.from(result), [' ']); t.equal(result.index, 5); t.equal(re.lastIndex, 6); const re2 = new RE2('\\s+', 'gy'); t.equal(re2.exec('Hello world, how are you?'), null); re2.lastIndex = 5; const result2 = re2.exec('Hello world, how are you?'); t.deepEqual(Array.from(result2), [' ']); t.equal(result2.index, 5); t.equal(re2.lastIndex, 6); }); test('exec supplemental', t => { const re = new RE2('\\w+', 'g'); const testString = '🤡🤡🤡 Hello clown world!'; let result = re.exec(testString); t.deepEqual(Array.from(result), ['Hello']); result = re.exec(testString); t.deepEqual(Array.from(result), ['clown']); result = re.exec(testString); t.deepEqual(Array.from(result), ['world']); }); // Multiline test test('exec multiline', t => { const re = new RE2('^xy', 'm'), pattern = ` xy1 xy2 (at start of line) xy3`; const result = re.exec(pattern); t.ok(result); t.equal(result[0], 'xy'); t.ok(result.index > 3); t.ok(result.index < pattern.length - 4); t.equal( result[0], pattern.substring(result.index, result.index + result[0].length) ); }); // dotAll tests test('exec dotAll', t => { t.ok(new RE2('a.c').test('abc')); t.ok(new RE2(/a.c/).test('a c')); t.notOk(new RE2(/a.c/).test('a\nc')); t.ok(new RE2('a.c', 's').test('abc')); t.ok(new RE2(/a.c/s).test('a c')); t.ok(new RE2(/a.c/s).test('a\nc')); }); // hasIndices tests test('exec hasIndices', t => { t.notOk(new RE2('1').hasIndices); t.notOk(new RE2(/1/).hasIndices); const re = new RE2('(aa)(?b)?(?ccc)', 'd'); t.ok(re.hasIndices); let result = re.exec('1aabccc2'); t.equal(result.length, 4); t.equal(result.input, '1aabccc2'); t.equal(result.index, 1); t.equal(Object.keys(result.groups).length, 2); t.equal(result.groups.b, 'b'); t.equal(result.groups.c, 'ccc'); t.equal(result[0], 'aabccc'); t.equal(result[1], 'aa'); t.equal(result[2], 'b'); t.equal(result[3], 'ccc'); t.equal(result.indices.length, 4); t.deepEqual(Array.from(result.indices), [ [1, 7], [1, 3], [3, 4], [4, 7] ]); t.equal(Object.keys(result.indices.groups).length, 2); t.deepEqual(result.indices.groups.b, [3, 4]); t.deepEqual(result.indices.groups.c, [4, 7]); result = re.exec('1aaccc2'); t.equal(result.length, 4); t.equal(result.input, '1aaccc2'); t.equal(result.index, 1); t.equal(Object.keys(result.groups).length, 2); t.equal(result.groups.b, undefined); t.equal(result.groups.c, 'ccc'); t.equal(result[0], 'aaccc'); t.equal(result[1], 'aa'); t.equal(result[2], undefined); t.equal(result[3], 'ccc'); t.equal(result.indices.length, 4); t.deepEqual(Array.from(result.indices), [[1, 6], [1, 3], undefined, [3, 6]]); t.equal(Object.keys(result.indices.groups).length, 2); t.deepEqual(result.indices.groups.b, undefined); t.deepEqual(result.indices.groups.c, [3, 6]); try { const re = new RE2(new RegExp('1', 'd')); t.ok(re.hasIndices); } catch (e) { // squelch } }); test('exec hasIndices lastIndex', t => { const re2 = new RE2('a', 'dg'); t.equal(re2.lastIndex, 0); let result = re2.exec('abca'); t.equal(re2.lastIndex, 1); t.equal(result.index, 0); t.deepEqual(Array.from(result.indices), [[0, 1]]); result = re2.exec('abca'); t.equal(re2.lastIndex, 4); t.equal(result.index, 3); t.deepEqual(Array.from(result.indices), [[3, 4]]); result = re2.exec('abca'); t.equal(re2.lastIndex, 0); t.equal(result, null); }); test('exec buffer vs string', t => { const re2 = new RE2('.', 'g'), pattern = 'abcdefg'; re2.lastIndex = 2; const result1 = re2.exec(pattern); re2.lastIndex = 2; const result2 = re2.exec(Buffer.from(pattern)); t.equal(result1[0], 'c'); t.deepEqual(result2[0], Buffer.from('c')); t.equal(result1.index, 2); t.equal(result2.index, 2); }); test('exec found empty string', t => { const re2 = new RE2('^.*?'), match = re2.exec(''); t.equal(match[0], ''); t.equal(match.index, 0); t.equal(match.input, ''); t.equal(match.groups, undefined); }); uhop-node-re2-e601e5a/tests/test-general.mjs000066400000000000000000000142251514245257200207700ustar00rootroot00000000000000import test from 'tape-six'; import {default as RE2} from '../re2.js'; // utilities const compare = (re1, re2, t) => { // compares regular expression objects t.equal(re1.source, re2.source); t.equal(re1.global, re2.global); t.equal(re1.ignoreCase, re2.ignoreCase); t.equal(re1.multiline, re2.multiline); // (t.equal(re1.unicode, re2.unicode)); t.equal(re1.sticky, re2.sticky); }; // tests test('general ctr', t => { t.ok(!!RE2); t.ok(!!RE2.prototype); t.equal(RE2.toString(), 'function RE2() { [native code] }'); }); test('general inst', t => { let re1 = new RE2('\\d+'); t.ok(!!re1); t.ok(re1 instanceof RE2); let re2 = RE2('\\d+'); t.ok(!!re2); t.ok(re2 instanceof RE2); compare(re1, re2, t); re1 = new RE2('\\d+', 'm'); t.ok(!!re1); t.ok(re1 instanceof RE2); re2 = RE2('\\d+', 'm'); t.ok(!!re2); t.ok(re2 instanceof RE2); compare(re1, re2, t); }); test('general inst errors', t => { try { const re = new RE2([]); t.fail(); // shouldn't be here } catch (e) { t.ok(e instanceof TypeError); } try { const re = new RE2({}); t.fail(); // shouldn't be here } catch (e) { t.ok(e instanceof TypeError); } try { const re = new RE2(new Date()); t.fail(); // shouldn't be here } catch (e) { t.ok(e instanceof TypeError); } try { const re = new RE2(null); t.fail(); // shouldn't be here } catch (e) { t.ok(e instanceof TypeError); } try { const re = new RE2(); t.fail(); // shouldn't be here } catch (e) { t.ok(e instanceof TypeError); } try { const re = RE2(); t.fail(); // shouldn't be here } catch (e) { t.ok(e instanceof TypeError); } try { const re = RE2({ toString() { throw 'corner'; } }); t.fail(); // shouldn't be here } catch (e) { t.ok(e instanceof TypeError); } }); test('general in', t => { const re = new RE2('\\d+'); t.ok('exec' in re); t.ok('test' in re); t.ok('match' in re); t.ok('replace' in re); t.ok('search' in re); t.ok('split' in re); t.ok('source' in re); t.ok('flags' in re); t.ok('global' in re); t.ok('ignoreCase' in re); t.ok('multiline' in re); t.ok('dotAll' in re); t.ok('sticky' in re); t.ok('lastIndex' in re); }); test('general present', t => { const re = new RE2('\\d+'); t.equal(typeof re.exec, 'function'); t.equal(typeof re.test, 'function'); t.equal(typeof re.match, 'function'); t.equal(typeof re.replace, 'function'); t.equal(typeof re.search, 'function'); t.equal(typeof re.split, 'function'); t.equal(typeof re.source, 'string'); t.equal(typeof re.flags, 'string'); t.equal(typeof re.global, 'boolean'); t.equal(typeof re.ignoreCase, 'boolean'); t.equal(typeof re.multiline, 'boolean'); t.equal(typeof re.dotAll, 'boolean'); t.equal(typeof re.sticky, 'boolean'); t.equal(typeof re.lastIndex, 'number'); }); test('general lastIndex', t => { const re = new RE2('\\d+'); t.equal(re.lastIndex, 0); re.lastIndex = 5; t.equal(re.lastIndex, 5); re.lastIndex = 0; t.equal(re.lastIndex, 0); }); test('general RegExp', t => { let re1 = new RegExp('\\d+'); let re2 = new RE2('\\d+'); compare(re1, re2, t); re2 = new RE2(re1); compare(re1, re2, t); re1 = new RegExp('a', 'ig'); re2 = new RE2('a', 'ig'); compare(re1, re2, t); re2 = new RE2(re1); compare(re1, re2, t); re1 = /\s/gm; re2 = new RE2('\\s', 'mg'); compare(re1, re2, t); re2 = new RE2(re1); compare(re1, re2, t); re2 = new RE2(/\s/gm); compare(/\s/gm, re2, t); re1 = new RE2('b', 'gm'); re2 = new RE2(re1); compare(re1, re2, t); re1 = new RE2('b', 'sgm'); re2 = new RE2(re1); compare(re1, re2, t); re2 = new RE2(/\s/gms); compare(/\s/gms, re2, t); }); test('general utf8', t => { const s = 'Привет!'; t.equal(s.length, 7); t.equal(RE2.getUtf8Length(s), 13); const b = new Buffer.from(s); t.equal(b.length, 13); t.equal(RE2.getUtf16Length(b), 7); const s2 = '\u{1F603}'; t.equal(s2.length, 2); t.equal(RE2.getUtf8Length(s2), 4); const b2 = new Buffer.from(s2); t.equal(b2.length, 4); t.equal(RE2.getUtf16Length(b2), 2); const s3 = '\uD83D'; t.equal(s3.length, 1); t.equal(RE2.getUtf8Length(s3), 3); const s4 = '🤡'; t.equal(s4.length, 2); t.equal(RE2.getUtf8Length(s4), 4); t.equal(RE2.getUtf16Length(Buffer.from(s4, 'utf8')), s4.length); const b3 = new Buffer.from([0xf0]); t.equal(b3.length, 1); t.equal(RE2.getUtf16Length(b3), 2); try { RE2.getUtf8Length({ toString() { throw 'corner'; } }); t.fail(); // shouldn't be here } catch (e) { t.equal(e, 'corner'); } t.equal( RE2.getUtf16Length({ toString() { throw 'corner'; } }), -1 ); }); test('general flags', t => { let re = new RE2('a', 'u'); t.equal(re.flags, 'u'); re = new RE2('a', 'iu'); t.equal(re.flags, 'iu'); re = new RE2('a', 'mu'); t.equal(re.flags, 'mu'); re = new RE2('a', 'gu'); t.equal(re.flags, 'gu'); re = new RE2('a', 'yu'); t.equal(re.flags, 'uy'); re = new RE2('a', 'yiu'); t.equal(re.flags, 'iuy'); re = new RE2('a', 'yigu'); t.equal(re.flags, 'giuy'); re = new RE2('a', 'miu'); t.equal(re.flags, 'imu'); re = new RE2('a', 'ygu'); t.equal(re.flags, 'guy'); re = new RE2('a', 'myu'); t.equal(re.flags, 'muy'); re = new RE2('a', 'migyu'); t.equal(re.flags, 'gimuy'); re = new RE2('a', 'smigyu'); t.equal(re.flags, 'gimsuy'); }); test('general flags 2nd', t => { let re = new RE2(/a/, 'u'); t.equal(re.flags, 'u'); re = new RE2(/a/gm, 'iu'); t.equal(re.flags, 'iu'); re = new RE2(/a/gi, 'mu'); t.equal(re.flags, 'mu'); re = new RE2(/a/g, 'gu'); t.equal(re.flags, 'gu'); re = new RE2(/a/m, 'yu'); t.equal(re.flags, 'uy'); re = new RE2(/a/, 'yiu'); t.equal(re.flags, 'iuy'); re = new RE2(/a/gim, 'yigu'); t.equal(re.flags, 'giuy'); re = new RE2(/a/gm, 'miu'); t.equal(re.flags, 'imu'); re = new RE2(/a/i, 'ygu'); t.equal(re.flags, 'guy'); re = new RE2(/a/g, 'myu'); t.equal(re.flags, 'muy'); re = new RE2(/a/, 'migyu'); t.equal(re.flags, 'gimuy'); re = new RE2(/a/s, 'smigyu'); t.equal(re.flags, 'gimsuy'); }); uhop-node-re2-e601e5a/tests/test-groups.mjs000066400000000000000000000044661514245257200207000ustar00rootroot00000000000000import test from 'tape-six'; import {default as RE2} from '../re2.js'; // tests test('groups normal', t => { t.equal(RE2('(?\\d)').test('9'), true); t.deepEqual(RE2('(?-)', 'g').match('a-b-c'), ['-', '-']); t.deepEqual(RE2('(?-)').split('a-b-c'), ['a', '-', 'b', '-', 'c']); t.equal(RE2('(?-)', 'g').search('a-b-c'), 1); }); test('groups exec', t => { let result = new RE2('(\\d)').exec('k9'); t.ok(result); t.equal(result[0], '9'); t.equal(result[1], '9'); t.equal(result.index, 1); t.equal(result.input, 'k9'); t.equal(typeof result.groups, 'undefined'); result = new RE2('(?\\d)').exec('k9'); t.ok(result); t.equal(result[0], '9'); t.equal(result[1], '9'); t.equal(result.index, 1); t.equal(result.input, 'k9'); t.deepEqual(result.groups, {a: '9'}); }); test('groups match', t => { let result = new RE2('(\\d)').match('k9'); t.ok(result); t.equal(result[0], '9'); t.equal(result[1], '9'); t.equal(result.index, 1); t.equal(result.input, 'k9'); t.equal(typeof result.groups, 'undefined'); result = new RE2('(?\\d)').match('k9'); t.ok(result); t.equal(result[0], '9'); t.equal(result[1], '9'); t.equal(result.index, 1); t.equal(result.input, 'k9'); t.deepEqual(result.groups, {a: '9'}); }); test('groups match', t => { t.equal(RE2('(?\\w)(?\\d)', 'g').replace('a1b2c', '$2$1'), '1a2bc'); t.equal(RE2('(?\\w)(?\\d)', 'g').replace('a1b2c', '$$'), '1a2bc'); t.equal( RE2('(?\\w)(?\\d)', 'g').replace('a1b2c', replacerByNumbers), '1a2bc' ); t.equal( RE2('(?\\w)(?\\d)', 'g').replace('a1b2c', replacerByNames), '1a2bc' ); function replacerByNumbers(match, group1, group2, index, source, groups) { return group2 + group1; } function replacerByNames(match, group1, group2, index, source, groups) { return groups.d + groups.w; } }); test('groups invalid', t => { try { RE2('(?<>.)'); t.fail(); // shouldn'be here } catch (e) { t.ok(e instanceof SyntaxError); } // TODO: do we need to enforce the correct id? // try { // RE2('(?<1>.)'); // t.fail(); // shouldn'be here // } catch(e) { // eval(t.TEST("e instanceof SyntaxError")); // } try { RE2('(?.)(?.)'); t.fail(); // shouldn'be here } catch (e) { t.ok(e instanceof SyntaxError); } }); uhop-node-re2-e601e5a/tests/test-invalid.mjs000066400000000000000000000014061514245257200207760ustar00rootroot00000000000000import test from 'tape-six'; import {default as RE2} from '../re2.js'; // tests test('invalid', t => { let threw; // Backreferences threw = false; try { new RE2(/(a)\1/); } catch (e) { threw = true; t.ok(e instanceof SyntaxError); t.equal(e.message, 'invalid escape sequence: \\1'); } t.ok(threw); // Lookahead assertions // Positive threw = false; try { new RE2(/a(?=b)/); } catch (e) { threw = true; t.ok(e instanceof SyntaxError); t.equal(e.message, 'invalid perl operator: (?='); } t.ok(threw); // Negative threw = false; try { new RE2(/a(?!b)/); } catch (e) { threw = true; t.ok(e instanceof SyntaxError); t.equal(e.message, 'invalid perl operator: (?!'); } t.ok(threw); }); uhop-node-re2-e601e5a/tests/test-match.mjs000066400000000000000000000075261514245257200204550ustar00rootroot00000000000000import test from 'tape-six'; import {default as RE2} from '../re2.js'; // tests // These tests are copied from MDN: // https://developer-US/docs/Web/JavaScript/Reference/Global_Objects/String/match test('test match', t => { const str = 'For more information, see Chapter 3.4.5.1'; const re = new RE2(/(chapter \d+(\.\d)*)/i); const result = re.match(str); t.equal(result.input, str); t.equal(result.index, 26); t.equal(result.length, 3); t.equal(result[0], 'Chapter 3.4.5.1'); t.equal(result[1], 'Chapter 3.4.5.1'); t.equal(result[2], '.1'); }); test('test_matchGlobal', t => { const re = new RE2(/[A-E]/gi); const result = re.match( 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz' ); t.deepEqual(result, ['A', 'B', 'C', 'D', 'E', 'a', 'b', 'c', 'd', 'e']); }); test('test match fail', t => { const re = new RE2('(a+)?(b+)?'); let result = re.match('aaabb'); t.equal(result[1], 'aaa'); t.equal(result[2], 'bb'); result = re.match('aaacbb'); t.equal(result[1], 'aaa'); t.equal(result[2], undefined); }); test('test match invalid', t => { const re = RE2(''); try { re.match({ toString() { throw 'corner'; } }); t.fail(); // shouldn't be here } catch (e) { t.equal(e, 'corner'); } }); // Unicode tests test('test match unicode', t => { const str = 'Это ГЛАВА 3.4.5.1'; const re = new RE2(/(глава \d+(\.\d)*)/i); const result = re.match(str); t.equal(result.input, str); t.equal(result.index, 4); t.equal(result.length, 3); t.equal(result[0], 'ГЛАВА 3.4.5.1'); t.equal(result[1], 'ГЛАВА 3.4.5.1'); t.equal(result[2], '.1'); }); // Buffer tests test('test match buffer', t => { const buf = Buffer.from('Это ГЛАВА 3.4.5.1'); const re = new RE2(/(глава \d+(\.\d)*)/i); const result = re.match(buf); t.ok(result.input instanceof Buffer); t.equal(result.length, 3); t.ok(result[0] instanceof Buffer); t.ok(result[1] instanceof Buffer); t.ok(result[2] instanceof Buffer); t.equal(result.input, buf); t.equal(result.index, 7); t.equal(result.input.toString('utf8', result.index), 'ГЛАВА 3.4.5.1'); t.equal(result[0].toString(), 'ГЛАВА 3.4.5.1'); t.equal(result[1].toString(), 'ГЛАВА 3.4.5.1'); t.equal(result[2].toString(), '.1'); }); // Sticky tests test('test match sticky', t => { const re = new RE2('\\s+', 'y'); t.equal(re.match('Hello world, how are you?'), null); re.lastIndex = 5; const result = re.match('Hello world, how are you?'); t.deepEqual(Array.from(result), [' ']); t.equal(result.index, 5); t.equal(re.lastIndex, 6); const re2 = new RE2('\\s+', 'gy'); t.equal(re2.match('Hello world, how are you?'), null); re2.lastIndex = 5; t.equal(re2.match('Hello world, how are you?'), null); const re3 = new RE2(/[A-E]/giy); const result3 = re3.match( 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz' ); t.deepEqual(result3, ['A', 'B', 'C', 'D', 'E']); }); // hasIndices tests test('test match has indices', t => { const re = new RE2('(aa)(?b)?(?ccc)', 'd'), str1 = '1aabccc2', str2 = '1aaccc2'; t.deepEqual(str1.match(re), re.exec(str1)); t.deepEqual(str2.match(re), re.exec(str2)); }); test('test match has indices global', t => { const re = new RE2('(?a)', 'dg'), result = 'abca'.match(re); t.deepEqual(result, ['a', 'a']); t.notOk('indices' in result); t.notOk('groups' in result); }); test('test match lastIndex', t => { const re = new RE2(/./g), pattern = 'Я123'; re.lastIndex = 2; const result1 = pattern.match(re); t.deepEqual(result1, ['Я', '1', '2', '3']); t.equal(re.lastIndex, 0); const re2 = RE2(re); re2.lastIndex = 2; const result2 = re2.match(Buffer.from(pattern)); t.deepEqual( result2.map(b => b.toString()), ['Я', '1', '2', '3'] ); t.equal(re2.lastIndex, 0); }); uhop-node-re2-e601e5a/tests/test-matchAll.mjs000066400000000000000000000037411514245257200211010ustar00rootroot00000000000000import test from 'tape-six'; import {default as RE2} from '../re2.js'; // tests // These tests are copied from MDN: // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/matchAll test('test matchAll', t => { const str = 'test1test2'; const re = new RE2(/t(e)(st(\d?))/g); const result = Array.from(str.matchAll(re)); t.equal(result.length, 2); t.equal(result[0].input, str); t.equal(result[0].index, 0); t.equal(result[0].length, 4); t.equal(result[0][0], 'test1'); t.equal(result[0][1], 'e'); t.equal(result[0][2], 'st1'); t.equal(result[0][3], '1'); t.equal(result[1].input, str); t.equal(result[1].index, 5); t.equal(result[1].length, 4); t.equal(result[1][0], 'test2'); t.equal(result[1][1], 'e'); t.equal(result[1][2], 'st2'); t.equal(result[1][3], '2'); }); test('test matchAll iterator', t => { const str = 'table football, foosball'; const re = new RE2('foo[a-z]*', 'g'); const expected = [ {start: 6, finish: 14}, {start: 16, finish: 24} ]; let i = 0; for (const match of str.matchAll(re)) { t.equal(match.index, expected[i].start); t.equal(match.index + match[0].length, expected[i].finish); ++i; } }); test('test matchAll non global', t => { const re = RE2('b'); try { 'abc'.matchAll(re); t.fail(); // shouldn't be here } catch (e) { t.ok(e instanceof TypeError); } }); test('test matchAll lastIndex', t => { const re = RE2('[a-c]', 'g'); re.lastIndex = 1; const expected = ['b', 'c']; let i = 0; for (const match of 'abc'.matchAll(re)) { t.equal(re.lastIndex, 1); t.equal(match[0], expected[i]); ++i; } }); test('test matchAll empty match', t => { const str = 'foo'; // Matches empty strings, but should not cause an infinite loop const re = new RE2('(?:)', 'g'); const result = Array.from(str.matchAll(re)); t.equal(result.length, str.length + 1); for (let i = 0; i < result.length; ++i) { t.equal(result[i][0], ''); } }); uhop-node-re2-e601e5a/tests/test-new.mjs000066400000000000000000000023511514245257200201410ustar00rootroot00000000000000import test from 'tape-six'; import {default as RE2} from '../re2.js'; // tests // these tests modify the global state of RE2 and cannot be run in parallel with other tests in the same process test('test new unicode warnOnce', t => { let errorMessage = ''; const oldConsole = console; console = {error: msg => (errorMessage = msg)}; RE2.unicodeWarningLevel = 'warnOnce'; let a = new RE2('.*'); t.ok(errorMessage); errorMessage = ''; a = new RE2('.?'); t.notOk(errorMessage); RE2.unicodeWarningLevel = 'warnOnce'; a = new RE2('.+'); t.ok(errorMessage); RE2.unicodeWarningLevel = 'nothing'; console = oldConsole; }); test('test new unicode warn', t => { let errorMessage = ''; const oldConsole = console; console = {error: msg => (errorMessage = msg)}; RE2.unicodeWarningLevel = 'warn'; let a = new RE2('.*'); t.ok(errorMessage); errorMessage = ''; a = new RE2('.?'); t.ok(errorMessage); RE2.unicodeWarningLevel = 'nothing'; console = oldConsole; }); test('test new unicode throw', t => { RE2.unicodeWarningLevel = 'throw'; try { let a = new RE2('.'); t.fail(); // shouldn't be here } catch (e) { t.ok(e instanceof SyntaxError); } RE2.unicodeWarningLevel = 'nothing'; }); uhop-node-re2-e601e5a/tests/test-prototype.mjs000066400000000000000000000007231514245257200214160ustar00rootroot00000000000000import test from 'tape-six'; import {default as RE2} from '../re2.js'; // tests test('test prototype', t => { t.equal(RE2.prototype.source, '(?:)'); t.equal(RE2.prototype.flags, ''); t.equal(RE2.prototype.global, undefined); t.equal(RE2.prototype.ignoreCase, undefined); t.equal(RE2.prototype.multiline, undefined); t.equal(RE2.prototype.dotAll, undefined); t.equal(RE2.prototype.sticky, undefined); t.equal(RE2.prototype.lastIndex, undefined); }); uhop-node-re2-e601e5a/tests/test-replace.mjs000066400000000000000000000235671514245257200207770ustar00rootroot00000000000000import test from 'tape-six'; import {default as RE2} from '../re2.js'; // tests // These tests are copied from MDN: // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/replace test('test replace string', t => { let re = new RE2(/apples/gi); let result = re.replace('Apples are round, and apples are juicy.', 'oranges'); t.equal(result, 'oranges are round, and oranges are juicy.'); re = new RE2(/xmas/i); result = re.replace('Twas the night before Xmas...', 'Christmas'); t.equal(result, 'Twas the night before Christmas...'); re = new RE2(/(\w+)\s(\w+)/); result = re.replace('John Smith', '$2, $1'); t.equal(result, 'Smith, John'); }); test('test replace functional replacer', t => { function replacer(match, p1, p2, p3, offset, string) { // p1 is nondigits, p2 digits, and p3 non-alphanumerics return [p1, p2, p3].join(' - '); } const re = new RE2(/([^\d]*)(\d*)([^\w]*)/); const result = re.replace('abc12345#$*%', replacer); t.equal(result, 'abc - 12345 - #$*%'); }); test('test replace functional upper to hyphen lower', t => { function upperToHyphenLower(match) { return '-' + match.toLowerCase(); } const re = new RE2(/[A-Z]/g); const result = re.replace('borderTop', upperToHyphenLower); t.equal(result, 'border-top'); }); test('test replace functional convert', t => { function convert(str, p1, offset, s) { return ((p1 - 32) * 5) / 9 + 'C'; } const re = new RE2(/(\d+(?:\.\d*)?)F\b/g); t.equal(re.replace('32F', convert), '0C'); t.equal(re.replace('41F', convert), '5C'); t.equal(re.replace('50F', convert), '10C'); t.equal(re.replace('59F', convert), '15C'); t.equal(re.replace('68F', convert), '20C'); t.equal(re.replace('77F', convert), '25C'); t.equal(re.replace('86F', convert), '30C'); t.equal(re.replace('95F', convert), '35C'); t.equal(re.replace('104F', convert), '40C'); t.equal(re.replace('113F', convert), '45C'); t.equal(re.replace('212F', convert), '100C'); }); test('test replace functional loop', t => { const logs = []; RE2(/(x_*)|(-)/g).replace('x-x_', function (match, p1, p2) { if (p1) { logs.push('on: ' + p1.length); } if (p2) { logs.push('off: 1'); } }); t.deepEqual(logs, ['on: 1', 'off: 1', 'on: 2']); }); test('test replace invalid', t => { const re = RE2(''); try { re.replace( { toString() { throw 'corner1'; } }, '' ); t.fail(); // shouldn't be here } catch (e) { t.equal(e, 'corner1'); } try { re.replace('', { toString() { throw 'corner2'; } }); t.fail(); // shouldn't be here } catch (e) { t.equal(e, 'corner2'); } let arg2Stringified = false; try { re.replace( { toString() { throw 'corner1'; } }, { toString() { arg2Stringified = true; throw 'corner2'; } } ); t.fail(); // shouldn't be here } catch (e) { t.equal(e, 'corner1'); t.notOk(arg2Stringified); } try { re.replace('', () => { throw 'corner2'; }); t.fail(); // shouldn't be here } catch (e) { t.equal(e, 'corner2'); } try { re.replace('', () => ({ toString() { throw 'corner2'; } })); t.fail(); // shouldn't be here } catch (e) { t.equal(e, 'corner2'); } }); // Unicode tests test('test replace string unicode', t => { let re = new RE2(/яблоки/gi); let result = re.replace('Яблоки красны, яблоки сочны.', 'апельсины'); t.equal(result, 'апельсины красны, апельсины сочны.'); re = new RE2(/иван/i); result = re.replace('Могуч Иван Иванов...', 'Сидор'); t.equal(result, 'Могуч Сидор Иванов...'); re = new RE2(/иван/gi); result = re.replace('Могуч Иван Иванов...', 'Сидор'); t.equal(result, 'Могуч Сидор Сидоров...'); re = new RE2(/([а-яё]+)\s+([а-яё]+)/i); result = re.replace('Пётр Петров', '$2, $1'); t.equal(result, 'Петров, Пётр'); }); test('test replace functional unicode', t => { function replacer(match, offset, string) { t.equal(typeof offset, 'number'); t.equal(typeof string, 'string'); t.ok(offset === 0 || offset === 7); t.equal(string, 'ИВАН и пЁтр'); return match.charAt(0).toUpperCase() + match.substr(1).toLowerCase(); } const re = new RE2(/(?:иван|пётр|сидор)/gi); const result = re.replace('ИВАН и пЁтр', replacer); t.equal(result, 'Иван и Пётр'); }); // Buffer tests test('test replace string buffer', t => { const re = new RE2(/яблоки/gi); let result = re.replace( Buffer.from('Яблоки красны, яблоки сочны.'), 'апельсины' ); t.ok(result instanceof Buffer); t.equal(result.toString(), 'апельсины красны, апельсины сочны.'); result = re.replace( Buffer.from('Яблоки красны, яблоки сочны.'), Buffer.from('апельсины') ); t.ok(result instanceof Buffer); t.equal(result.toString(), 'апельсины красны, апельсины сочны.'); result = re.replace('Яблоки красны, яблоки сочны.', Buffer.from('апельсины')); t.equal(typeof result, 'string'); t.equal(result, 'апельсины красны, апельсины сочны.'); }); test('test replace functional buffer', t => { function replacer(match, offset, string) { t.ok(match instanceof Buffer); t.equal(typeof offset, 'number'); t.equal(typeof string, 'string'); t.ok(offset === 0 || offset === 12); t.equal(string, 'ИВАН и пЁтр'); const s = match.toString(); return s.charAt(0).toUpperCase() + s.substr(1).toLowerCase(); } replacer.useBuffers = true; const re = new RE2(/(?:иван|пётр|сидор)/gi); const result = re.replace('ИВАН и пЁтр', replacer); t.equal(typeof result, 'string'); t.equal(result, 'Иван и Пётр'); }); test('test replace0', t => { const replacer = match => 'MARKER' + match; let re = new RE2(/^/g); let result = re.replace('foo bar', 'MARKER'); t.equal(result, 'MARKERfoo bar'); result = re.replace('foo bar', replacer); t.equal(result, 'MARKERfoo bar'); re = new RE2(/$/g); result = re.replace('foo bar', 'MARKER'); t.equal(result, 'foo barMARKER'); result = re.replace('foo bar', replacer); t.equal(result, 'foo barMARKER'); re = new RE2(/\b/g); result = re.replace('foo bar', 'MARKER'); t.equal(result, 'MARKERfooMARKER MARKERbarMARKER'); result = re.replace('foo bar', replacer); t.equal(result, 'MARKERfooMARKER MARKERbarMARKER'); }); // Sticky tests test('test replace sticky', t => { const re = new RE2(/[A-E]/y); t.equal(re.replace('ABCDEFABCDEF', '!'), '!BCDEFABCDEF'); t.equal(re.replace('ABCDEFABCDEF', '!'), 'A!CDEFABCDEF'); t.equal(re.replace('ABCDEFABCDEF', '!'), 'AB!DEFABCDEF'); t.equal(re.replace('ABCDEFABCDEF', '!'), 'ABC!EFABCDEF'); t.equal(re.replace('ABCDEFABCDEF', '!'), 'ABCD!FABCDEF'); t.equal(re.replace('ABCDEFABCDEF', '!'), 'ABCDEFABCDEF'); t.equal(re.replace('ABCDEFABCDEF', '!'), '!BCDEFABCDEF'); const re2 = new RE2(/[A-E]/gy); t.equal(re2.replace('ABCDEFABCDEF', '!'), '!!!!!FABCDEF'); t.equal(re2.replace('FABCDEFABCDE', '!'), 'FABCDEFABCDE'); re2.lastIndex = 3; t.equal(re2.replace('ABCDEFABCDEF', '!'), '!!!!!FABCDEF'); t.equal(re2.lastIndex, 0); }); // Non-matches test('test replace one non-match', t => { const replacer = (match, capture, offset, string) => { t.equal(typeof offset, 'number'); t.equal(typeof match, 'string'); t.equal(typeof string, 'string'); t.equal(typeof capture, 'undefined'); t.equal(offset, 0); t.equal(string, 'hello '); return ''; }; const re = new RE2(/hello (world)?/); re.replace('hello ', replacer); }); test('test replace two non-matches', t => { const replacer = (match, capture1, capture2, offset, string, groups) => { t.equal(typeof offset, 'number'); t.equal(typeof match, 'string'); t.equal(typeof string, 'string'); t.equal(typeof capture1, 'undefined'); t.equal(typeof capture2, 'undefined'); t.equal(offset, 1); t.equal(match, 'b & y'); t.equal(string, 'ab & yz'); t.equal(typeof groups, 'object'); t.equal(Object.keys(groups).length, 2); t.equal(groups.a, undefined); t.equal(groups.b, undefined); return ''; }; const re = new RE2(/b(?1)? & (?2)?y/); const result = re.replace('ab & yz', replacer); t.equal(result, 'az'); }); test('test replace group simple', t => { const re = new RE2(/(2)/); let result = re.replace('123', '$0'); t.equal(result, '1$03'); result = re.replace('123', '$1'); t.equal(result, '123'); result = re.replace('123', '$2'); t.equal(result, '1$23'); result = re.replace('123', '$00'); t.equal(result, '1$003'); result = re.replace('123', '$01'); t.equal(result, '123'); result = re.replace('123', '$02'); t.equal(result, '1$023'); }); test('test replace group cases', t => { let re = new RE2(/(test)/g); let result = re.replace('123', '$1$20'); t.equal(result, '123'); re = new RE2(/(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)/g); result = re.replace('abcdefghijklmnopqrstuvwxyz123', '$10$20'); t.equal(result, 'jb0wo0123'); re = new RE2(/(.)(.)(.)(.)(.)/g); result = re.replace('abcdefghijklmnopqrstuvwxyz123', '$10$20'); t.equal(result, 'a0b0f0g0k0l0p0q0u0v0z123'); re = new RE2( /(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)/g ); result = re.replace('abcdefghijklmnopqrstuvwxyz123', '$10$20'); t.equal(result, 'jtvwxyz123'); re = new RE2(/abcd/g); result = re.replace('abcd123', '$1$2'); t.equal(result, '$1$2123'); }); test('test replace empty replacement', t => { t.equal('ac', 'abc'.replace(RE2('b'), '')); }); uhop-node-re2-e601e5a/tests/test-search.mjs000066400000000000000000000036201514245257200206150ustar00rootroot00000000000000import test from 'tape-six'; import {default as RE2} from '../re2.js'; // tests test('test search', t => { const str = 'Total is 42 units.'; let re = new RE2(/\d+/i); let result = re.search(str); t.equal(result, 9); re = new RE2('\\b[a-z]+\\b'); result = re.search(str); t.equal(result, 6); re = new RE2('\\b\\w+\\b'); result = re.search(str); t.equal(result, 0); re = new RE2('z', 'gm'); result = re.search(str); t.equal(result, -1); }); test('test search invalid', t => { const re = RE2(''); try { re.search({ toString() { throw 'corner'; } }); t.fail(); // shouldn't be here } catch (e) { t.equal(e, 'corner'); } }); test('test search unicode', t => { const str = 'Всего 42 штуки.'; let re = new RE2(/\d+/i); let result = re.search(str); t.equal(result, 6); re = new RE2('\\s[а-я]+'); result = re.search(str); t.equal(result, 8); re = new RE2('[а-яА-Я]+'); result = re.search(str); t.equal(result, 0); re = new RE2('z', 'gm'); result = re.search(str); t.equal(result, -1); }); test('test search buffer', t => { const buf = Buffer.from('Всего 42 штуки.'); let re = new RE2(/\d+/i); let result = re.search(buf); t.equal(result, 11); re = new RE2('\\s[а-я]+'); result = re.search(buf); t.equal(result, 13); re = new RE2('[а-яА-Я]+'); result = re.search(buf); t.equal(result, 0); re = new RE2('z', 'gm'); result = re.search(buf); t.equal(result, -1); }); test('test search sticky', t => { const str = 'Total is 42 units.'; let re = new RE2(/\d+/y); let result = re.search(str); t.equal(result, -1); re = new RE2('\\b[a-z]+\\b', 'y'); result = re.search(str); t.equal(result, -1); re = new RE2('\\b\\w+\\b', 'y'); result = re.search(str); t.equal(result, 0); re = new RE2('z', 'gmy'); result = re.search(str); t.equal(result, -1); }); uhop-node-re2-e601e5a/tests/test-set.mjs000066400000000000000000000062661514245257200201540ustar00rootroot00000000000000import test from 'tape-six'; import {default as RE2} from '../re2.js'; test('test set basics', t => { const set = new RE2.Set(['foo', 'bar'], 'im'); t.ok(set instanceof Object); t.equal(typeof set.match, 'function'); t.equal(set.size, 2); t.equal(set.flags, 'imu'); t.equal(set.anchor, 'unanchored'); t.ok(Array.isArray(set.sources)); t.equal(set.sources[0], 'foo'); t.equal(set.source, 'foo|bar'); t.equal(set.toString(), '/foo|bar/imu'); }); test('test set matching', t => { const set = new RE2.Set(['foo', 'bar'], 'i'); const result = set.match('xxFOOxxbar'); t.equal(result.length, 2); result.sort((a, b) => a - b); t.deepEqual(result, [0, 1]); t.equal(set.test('nothing here'), false); t.equal(set.match('nothing here').length, 0); }); test('test set anchors', t => { const start = new RE2.Set(['abc'], {anchor: 'start'}); const both = new RE2.Set(['abc'], {anchor: 'both'}); t.equal(start.test('zabc'), false); t.equal(start.test('abc'), true); t.ok(both.test('abc')); t.notOk(both.test('abc1')); }); test('test set iterable', t => { function* gen() { yield 'cat'; yield 'dog'; } const set = new RE2.Set(gen()); t.equal(set.size, 2); const result = set.match('hotdog'); t.equal(result.length, 1); t.equal(result[0], 1); }); test('test set flags override', t => { const set = new RE2.Set([/abc/], 'i'); t.ok(set.test('ABC')); t.equal(set.flags, 'iu'); }); test('test set unicode inputs', t => { const patterns = ['🙂', '猫', '🍣+', '東京', '\\p{Hiragana}+']; const set = new RE2.Set(patterns, 'u'); const input = 'prefix🙂と猫と🍣🍣を食べる東京ひらがな'; const result = set.match(input); t.equal(result.length, 5); t.notEqual(result.indexOf(0), -1); t.notEqual(result.indexOf(1), -1); t.notEqual(result.indexOf(2), -1); t.notEqual(result.indexOf(3), -1); t.notEqual(result.indexOf(4), -1); const buf = Buffer.from(input); const bufResult = set.match(buf); t.equal(bufResult.length, 5); t.ok(set.test(buf)); const miss = new RE2.Set(['🚀', '漢字'], 'u'); t.notOk(miss.test(input)); t.equal(miss.match(input).length, 0); }); test('test set empty and duplicates', t => { const emptySet = new RE2.Set([]); t.equal(emptySet.size, 0); t.equal(emptySet.test('anything'), false); const dup = new RE2.Set(['foo', 'foo', 'bar']); const r = dup.match('foo bar'); // two foo entries plus bar t.equal(r.length, 3); r.sort((a, b) => a - b); t.deepEqual(r, [0, 1, 2]); }); test('test set inconsistent flags', t => { try { const set = new RE2.Set([/abc/i, /abc/m]); t.fail(); } catch (e) { t.ok(e instanceof TypeError); } }); test('test set invalid flags char', t => { try { const set = new RE2.Set(['foo'], 'q'); t.fail(); } catch (e) { t.ok(e instanceof TypeError); } }); test('test set anchor option with flags', t => { const set = new RE2.Set(['^foo', '^bar'], 'i', {anchor: 'both'}); t.equal(set.anchor, 'both'); t.equal(set.match('foo').length, 1); t.equal(set.match('xfoo').length, 0); }); test('test set invalid', t => { try { const set = new RE2.Set([null]); t.fail(); } catch (e) { t.ok(e instanceof TypeError); } }); uhop-node-re2-e601e5a/tests/test-source.mjs000066400000000000000000000032631514245257200206530ustar00rootroot00000000000000import test from 'tape-six'; import {default as RE2} from '../re2.js'; // tests test('test source identity', t => { let re = new RE2('a\\cM\\u34\\u1234\\u10abcdz'); t.equal(re.source, 'a\\cM\\u34\\u1234\\u10abcdz'); re = new RE2('a\\cM\\u34\\u1234\\u{10abcd}z'); t.equal(re.source, 'a\\cM\\u34\\u1234\\u{10abcd}z'); re = new RE2(''); t.equal(re.source, '(?:)'); re = new RE2('foo/bar'); t.equal(re.source, 'foo\\/bar'); re = new RE2('foo\\/bar'); t.equal(re.source, 'foo\\/bar'); re = new RE2('(?bar)', 'u'); t.equal(re.source, '(?bar)'); }); test('test source translation', t => { let re = new RE2('a\\cM\\u34\\u1234\\u10abcdz'); t.equal(re.internalSource, 'a\\x0D\\x{34}\\x{1234}\\x{10ab}cdz'); re = new RE2('a\\cM\\u34\\u1234\\u{10abcd}z'); t.equal(re.internalSource, 'a\\x0D\\x{34}\\x{1234}\\x{10abcd}z'); re = new RE2(''); t.equal(re.internalSource, '(?:)'); re = new RE2('foo/bar'); t.equal(re.internalSource, 'foo\\/bar'); re = new RE2('foo\\/bar'); t.equal(re.internalSource, 'foo\\/bar'); re = new RE2('(?bar)', 'u'); t.equal(re.internalSource, '(?Pbar)'); re = new RE2('foo\\/bar', 'm'); t.equal(re.internalSource, '(?m)foo\\/bar'); }); test('test source backslashes', t => { const compare = (source, expected) => { const s = new RE2(source).source; t.equal(s, expected); }; compare('a/b', 'a\\/b'); compare('a\/b', 'a\\/b'); compare('a\\/b', 'a\\/b'); compare('a\\\/b', 'a\\/b'); compare('a\\\\/b', 'a\\\\\\/b'); compare('a\\\\\/b', 'a\\\\\\/b'); compare('/a/b', '\\/a\\/b'); compare('\\/a/b', '\\/a\\/b'); compare('\\/a\\/b', '\\/a\\/b'); compare('\\/a\\\\/b', '\\/a\\\\\\/b'); }); uhop-node-re2-e601e5a/tests/test-split.mjs000066400000000000000000000126751514245257200205150ustar00rootroot00000000000000import test from 'tape-six'; import {default as RE2} from '../re2.js'; // utilities const verifyBuffer = (bufArray, t) => bufArray.map(x => { t.ok(x instanceof Buffer); return x.toString(); }); // tests // These tests are copied from MDN: // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/split test('test split', t => { let re = new RE2(/\s+/); let result = re.split('Oh brave new world that has such people in it.'); t.deepEqual(result, [ 'Oh', 'brave', 'new', 'world', 'that', 'has', 'such', 'people', 'in', 'it.' ]); re = new RE2(','); result = re.split('Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec'); t.deepEqual(result, [ 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec' ]); re = new RE2(','); result = re.split(',Jan,Feb,Mar,Apr,May,Jun,,Jul,Aug,Sep,Oct,Nov,Dec,'); t.deepEqual(result, [ '', 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', '', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec', '' ]); re = new RE2(/\s*;\s*/); result = re.split( 'Harry Trump ;Fred Barney; Helen Rigby ; Bill Abel ;Chris Hand ' ); t.deepEqual(result, [ 'Harry Trump', 'Fred Barney', 'Helen Rigby', 'Bill Abel', 'Chris Hand ' ]); re = new RE2(/\s+/); result = re.split('Hello World. How are you doing?', 3); t.deepEqual(result, ['Hello', 'World.', 'How']); re = new RE2(/(\d)/); result = re.split('Hello 1 word. Sentence number 2.'); t.deepEqual(result, ['Hello ', '1', ' word. Sentence number ', '2', '.']); t.deepEqual( RE2(/[x-z]*/) .split('asdfghjkl') .reverse() .join(''), 'lkjhgfdsa' ); }); test('test_splitInvalid', t => { const re = RE2(''); try { re.split({ toString() { throw 'corner'; } }); t.fail(); // shouldn't be here } catch (e) { t.equal(e, 'corner'); } }); test('test_cornerCases', t => { const re = new RE2(/1/); const result = re.split('23456'); t.deepEqual(result, ['23456']); }); // Unicode tests test('test split unicode', t => { let re = new RE2(/\s+/); let result = re.split('Она не понимает, что этим убивает меня.'); t.deepEqual(result, [ 'Она', 'не', 'понимает,', 'что', 'этим', 'убивает', 'меня.' ]); re = new RE2(','); result = re.split('Пн,Вт,Ср,Чт,Пт,Сб,Вс'); t.deepEqual(result, ['Пн', 'Вт', 'Ср', 'Чт', 'Пт', 'Сб', 'Вс']); re = new RE2(/\s*;\s*/); result = re.split('Ваня Иванов ;Петро Петренко; Саша Машин ; Маша Сашина'); t.deepEqual(result, [ 'Ваня Иванов', 'Петро Петренко', 'Саша Машин', 'Маша Сашина' ]); re = new RE2(/\s+/); result = re.split('Привет мир. Как дела?', 3); t.deepEqual(result, ['Привет', 'мир.', 'Как']); re = new RE2(/(\d)/); result = re.split('Привет 1 слово. Предложение номер 2.'); t.deepEqual(result, ['Привет ', '1', ' слово. Предложение номер ', '2', '.']); t.deepEqual( RE2(/[э-я]*/) .split('фывапролд') .reverse() .join(''), 'длорпавыф' ); }); // Buffer tests test('test split buffer', t => { let re = new RE2(/\s+/); let result = re.split(Buffer.from('Она не понимает, что этим убивает меня.')); t.deepEqual(verifyBuffer(result, t), [ 'Она', 'не', 'понимает,', 'что', 'этим', 'убивает', 'меня.' ]); re = new RE2(','); result = re.split(Buffer.from('Пн,Вт,Ср,Чт,Пт,Сб,Вс')); t.deepEqual(verifyBuffer(result, t), [ 'Пн', 'Вт', 'Ср', 'Чт', 'Пт', 'Сб', 'Вс' ]); re = new RE2(/\s*;\s*/); result = re.split( Buffer.from('Ваня Иванов ;Петро Петренко; Саша Машин ; Маша Сашина') ); t.deepEqual(verifyBuffer(result, t), [ 'Ваня Иванов', 'Петро Петренко', 'Саша Машин', 'Маша Сашина' ]); re = new RE2(/\s+/); result = re.split(Buffer.from('Привет мир. Как дела?'), 3); t.deepEqual(verifyBuffer(result, t), ['Привет', 'мир.', 'Как']); re = new RE2(/(\d)/); result = re.split(Buffer.from('Привет 1 слово. Предложение номер 2.')); t.deepEqual(verifyBuffer(result, t), [ 'Привет ', '1', ' слово. Предложение номер ', '2', '.' ]); t.deepEqual( RE2(/[э-я]*/) .split(Buffer.from('фывапролд')) .reverse() .join(''), 'длорпавыф' ); }); // Sticky tests test('test split sticky', t => { const re = new RE2(/\s+/y); // sticky is ignored const result = re.split('Oh brave new world that has such people in it.'); t.deepEqual(result, [ 'Oh', 'brave', 'new', 'world', 'that', 'has', 'such', 'people', 'in', 'it.' ]); const result2 = re.split(' Oh brave new world that has such people in it.'); t.deepEqual(result2, [ '', 'Oh', 'brave', 'new', 'world', 'that', 'has', 'such', 'people', 'in', 'it.' ]); }); uhop-node-re2-e601e5a/tests/test-symbols.mjs000066400000000000000000000053261514245257200210450ustar00rootroot00000000000000import test from 'tape-six'; import {default as RE2} from '../re2.js'; // tests test('test match symbol', t => { if (typeof Symbol == 'undefined' || !Symbol.match) return; const str = 'For more information, see Chapter 3.4.5.1'; const re = new RE2(/(chapter \d+(\.\d)*)/i); const result = str.match(re); t.equal(result.input, str); t.equal(result.index, 26); t.equal(result.length, 3); t.equal(result[0], 'Chapter 3.4.5.1'); t.equal(result[1], 'Chapter 3.4.5.1'); t.equal(result[2], '.1'); }); test('test search symbol', t => { if (typeof Symbol == 'undefined' || !Symbol.search) return; const str = 'Total is 42 units.'; let re = new RE2(/\d+/i); let result = str.search(re); t.equal(result, 9); re = new RE2('\\b[a-z]+\\b'); result = str.search(re); t.equal(result, 6); re = new RE2('\\b\\w+\\b'); result = str.search(re); t.equal(result, 0); re = new RE2('z', 'gm'); result = str.search(re); t.equal(result, -1); }); test('test replace symbol', t => { if (typeof Symbol == 'undefined' || !Symbol.replace) return; let re = new RE2(/apples/gi); let result = 'Apples are round, and apples are juicy.'.replace(re, 'oranges'); t.equal(result, 'oranges are round, and oranges are juicy.'); re = new RE2(/xmas/i); result = 'Twas the night before Xmas...'.replace(re, 'Christmas'); t.equal(result, 'Twas the night before Christmas...'); re = new RE2(/(\w+)\s(\w+)/); result = 'John Smith'.replace(re, '$2, $1'); t.equal(result, 'Smith, John'); }); test('test split symbol', t => { if (typeof Symbol == 'undefined' || !Symbol.split) return; let re = new RE2(/\s+/); let result = 'Oh brave new world that has such people in it.'.split(re); t.deepEqual(result, [ 'Oh', 'brave', 'new', 'world', 'that', 'has', 'such', 'people', 'in', 'it.' ]); re = new RE2(','); result = 'Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec'.split(re); t.deepEqual(result, [ 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec' ]); re = new RE2(/\s*;\s*/); result = 'Harry Trump ;Fred Barney; Helen Rigby ; Bill Abel ;Chris Hand '.split(re); t.deepEqual(result, [ 'Harry Trump', 'Fred Barney', 'Helen Rigby', 'Bill Abel', 'Chris Hand ' ]); re = new RE2(/\s+/); result = 'Hello World. How are you doing?'.split(re, 3); t.deepEqual(result, ['Hello', 'World.', 'How']); re = new RE2(/(\d)/); result = 'Hello 1 word. Sentence number 2.'.split(re); t.deepEqual(result, ['Hello ', '1', ' word. Sentence number ', '2', '.']); t.equal( 'asdfghjkl' .split(RE2(/[x-z]*/)) .reverse() .join(''), 'lkjhgfdsa' ); }); uhop-node-re2-e601e5a/tests/test-test.mjs000066400000000000000000000115441514245257200203330ustar00rootroot00000000000000import test from 'tape-six'; import {default as RE2} from '../re2.js'; // tests // These tests are copied from MDN: // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/test test('test test from exec', t => { let re = new RE2('quick\\s(brown).+?(jumps)', 'i'); t.equal(re.test('The Quick Brown Fox Jumps Over The Lazy Dog'), true); t.equal(re.test('tHE qUICK bROWN fOX jUMPS oVER tHE lAZY dOG'), true); t.equal(re.test('the quick brown fox jumps over the lazy dog'), true); t.equal(re.test('THE QUICK BROWN FOX JUMPS OVER THE LAZY DOG'), true); t.equal(re.test('THE KWIK BROWN FOX JUMPS OVER THE LAZY DOG'), false); re = new RE2('ab*', 'g'); t.ok(re.test('abbcdefabh')); t.notOk(re.test('qwerty')); re = new RE2('(hello \\S+)'); t.ok(re.test('This is a hello world!')); t.notOk(re.test('This is a Hello world!')); }); test('test test successive', t => { const str = 'abbcdefabh'; const re = new RE2('ab*', 'g'); let result = re.test(str); t.ok(result); t.equal(re.lastIndex, 3); result = re.test(str); t.ok(result); t.equal(re.lastIndex, 9); result = re.test(str); t.notOk(result); }); test('test test simple', t => { const str = 'abbcdefabh'; const re1 = new RE2('ab*', 'g'); t.ok(re1.test(str)); const re2 = new RE2('ab*'); t.ok(re2.test(str)); const re3 = new RE2('abc'); t.notOk(re3.test(str)); }); test('test test anchored to beginning', t => { const re = RE2('^hello', 'g'); t.ok(re.test('hellohello')); t.notOk(re.test('hellohello')); }); test('test test invalid', t => { const re = RE2(''); try { re.test({ toString() { throw 'corner'; } }); t.fail(); // shouldn't be here } catch (e) { t.equal(e, 'corner'); } }); test('test test anchor 1', t => { const re = new RE2('b|^a', 'g'); let result = re.test('aabc'); t.ok(result); t.equal(re.lastIndex, 1); result = re.test('aabc'); t.ok(result); t.equal(re.lastIndex, 3); result = re.test('aabc'); t.notOk(result); }); test('test test anchor 2', t => { const re = new RE2('(?:^a)', 'g'); let result = re.test('aabc'); t.ok(result); t.equal(re.lastIndex, 1); result = re.test('aabc'); t.notOk(result); }); // Unicode tests test('test test unicode', t => { let re = new RE2('охотник\\s(желает).+?(где)', 'i'); t.ok(re.test('Каждый Охотник Желает Знать Где Сидит Фазан')); t.ok(re.test('кАЖДЫЙ оХОТНИК жЕЛАЕТ зНАТЬ гДЕ сИДИТ фАЗАН')); t.ok(re.test('каждый охотник желает знать где сидит фазан')); t.ok(re.test('КАЖДЫЙ ОХОТНИК ЖЕЛАЕТ ЗНАТЬ ГДЕ СИДИТ ФАЗАН')); t.notOk(re.test('Кажный Стрелок Хочет Найти Иде Прячется Птица')); re = new RE2('аб*', 'g'); t.ok(re.test('аббвгдеабё')); t.notOk(re.test('йцукен')); re = new RE2('(привет \\S+)'); t.ok(re.test('Это просто привет всем.')); t.notOk(re.test('Это просто Привет всем.')); }); test('test test unicode subsequent', t => { const str = 'аббвгдеабё'; const re = new RE2('аб*', 'g'); let result = re.test(str); t.ok(result); t.equal(re.lastIndex, 3); result = re.test(str); t.ok(result); t.equal(re.lastIndex, 9); result = re.test(str); t.notOk(result); }); // Buffer tests test('test test buffer', t => { let re = new RE2('охотник\\s(желает).+?(где)', 'i'); t.ok(re.test(Buffer.from('Каждый Охотник Желает Знать Где Сидит Фазан'))); t.ok(re.test(Buffer.from('кАЖДЫЙ оХОТНИК жЕЛАЕТ зНАТЬ гДЕ сИДИТ фАЗАН'))); t.ok(re.test(Buffer.from('каждый охотник желает знать где сидит фазан'))); t.ok(re.test(Buffer.from('КАЖДЫЙ ОХОТНИК ЖЕЛАЕТ ЗНАТЬ ГДЕ СИДИТ ФАЗАН'))); t.notOk( re.test(Buffer.from('Кажный Стрелок Хочет Найти Иде Прячется Птица')) ); re = new RE2('аб*', 'g'); t.ok(re.test(Buffer.from('аббвгдеабё'))); t.notOk(re.test(Buffer.from('йцукен'))); re = new RE2('(привет \\S+)'); t.ok(re.test(Buffer.from('Это просто привет всем.'))); t.notOk(re.test(Buffer.from('Это просто Привет всем.'))); }); // Sticky tests test('test test sticky', t => { const re = new RE2('\\s+', 'y'); t.notOk(re.test('Hello world, how are you?')); re.lastIndex = 5; t.ok(re.test('Hello world, how are you?')); t.equal(re.lastIndex, 6); const re2 = new RE2('\\s+', 'gy'); t.notOk(re2.test('Hello world, how are you?')); re2.lastIndex = 5; t.ok(re2.test('Hello world, how are you?')); t.equal(re2.lastIndex, 6); }); uhop-node-re2-e601e5a/tests/test-toString.mjs000066400000000000000000000015361514245257200211650ustar00rootroot00000000000000import test from 'tape-six'; import {default as RE2} from '../re2.js'; // tests test('test toString', t => { t.equal(RE2('').toString(), '/(?:)/u'); t.equal(RE2('a').toString(), '/a/u'); t.equal(RE2('b', 'i').toString(), '/b/iu'); t.equal(RE2('c', 'g').toString(), '/c/gu'); t.equal(RE2('d', 'm').toString(), '/d/mu'); t.equal(RE2('\\d+', 'gi') + '', '/\\d+/giu'); t.equal(RE2('\\s*', 'gm') + '', '/\\s*/gmu'); t.equal(RE2('\\S{1,3}', 'ig') + '', '/\\S{1,3}/giu'); t.equal(RE2('\\D{,2}', 'mig') + '', '/\\D{,2}/gimu'); t.equal(RE2('^a{2,}', 'mi') + '', '/^a{2,}/imu'); t.equal(RE2('^a{5}$', 'gim') + '', '/^a{5}$/gimu'); t.equal(RE2('\\u{1F603}/', 'iy') + '', '/\\u{1F603}\\//iuy'); t.equal(RE2('^a{2,}', 'smi') + '', '/^a{2,}/imsu'); t.equal(RE2('c', 'ug').toString(), '/c/gu'); t.equal(RE2('d', 'um').toString(), '/d/mu'); }); uhop-node-re2-e601e5a/tests/test-unicode-classes.mjs000066400000000000000000000012071514245257200224300ustar00rootroot00000000000000import test from 'tape-six'; import {default as RE2} from '../re2.js'; // tests test('test_unicodeClasses', t => { 'use strict'; let re2 = new RE2(/\p{L}/u); t.ok(re2.test('a')); t.notOk(re2.test('1')); re2 = new RE2(/\p{Letter}/u); t.ok(re2.test('a')); t.notOk(re2.test('1')); re2 = new RE2(/\p{Lu}/u); t.ok(re2.test('A')); t.notOk(re2.test('a')); re2 = new RE2(/\p{Uppercase_Letter}/u); t.ok(re2.test('A')); t.notOk(re2.test('a')); re2 = new RE2(/\p{Script=Latin}/u); t.ok(re2.test('a')); t.notOk(re2.test('ф')); re2 = new RE2(/\p{sc=Cyrillic}/u); t.notOk(re2.test('a')); t.ok(re2.test('ф')); }); uhop-node-re2-e601e5a/ts-tests/000077500000000000000000000000001514245257200163035ustar00rootroot00000000000000uhop-node-re2-e601e5a/ts-tests/test-types.ts000066400000000000000000000022741514245257200210010ustar00rootroot00000000000000import RE2 from 're2'; function assertType(_val: T) {} function test_execTypes() { const re = new RE2('quick\\s(brown).+?(?jumps)', 'ig'); const result = re.exec('The Quick Brown Fox Jumps Over The Lazy Dog') if (!(result && result.groups)) { throw 'Unexpected Result' } assertType(result.index) assertType(result.input) assertType(result.groups['verb']) } function test_matchTypes() { const re = new RE2('quick\\s(brown).+?(?jumps)', 'ig'); const result = re.match('The Quick Brown Fox Jumps Over The Lazy Dog') if (!(result && result.index && result.input && result.groups)) { throw 'Unexpected Result' } assertType(result.index) assertType(result.input) assertType(result.groups['verb']) } function test_setTypes() { const set = new RE2.Set(['alpha', Buffer.from('beta')], 'i', {anchor: 'start'}) assertType(set.match('alphabet')) assertType(set.test(Buffer.from('alphabet'))) assertType<'unanchored' | 'start' | 'both'>(set.anchor) assertType(set.sources) assertType(set.flags) } test_execTypes() test_matchTypes() test_setTypes() uhop-node-re2-e601e5a/tsconfig.json000066400000000000000000000011141514245257200172210ustar00rootroot00000000000000{ "compilerOptions": { "noEmit": true, "declaration": true, "esModuleInterop": true, "strict": true, "allowUnusedLabels": false, "allowUnreachableCode": false, "exactOptionalPropertyTypes": true, "noFallthroughCasesInSwitch": true, "noImplicitOverride": true, "noImplicitReturns": true, "noPropertyAccessFromIndexSignature": true, "noUncheckedIndexedAccess": true, "noUnusedLocals": true, "noUnusedParameters": true, "forceConsistentCasingInFileNames": true }, "include": ["**/*.ts"], "exclude": ["vendor/re2/app/**"] } uhop-node-re2-e601e5a/vendor/000077500000000000000000000000001514245257200160125ustar00rootroot00000000000000uhop-node-re2-e601e5a/vendor/abseil-cpp/000077500000000000000000000000001514245257200200315ustar00rootroot00000000000000uhop-node-re2-e601e5a/vendor/re2/000077500000000000000000000000001514245257200165025ustar00rootroot00000000000000