From c7ad6bca2fde7da8141204e5f78f7db453b4e42a Mon Sep 17 00:00:00 2001 From: Specy Date: Tue, 1 Oct 2024 19:11:57 +0200 Subject: [PATCH] feat: add token span info, fixes #1 and #13 --- .github/workflows/ci.yml | 38 +- bindings/typescript/.prettierignore | 4 + bindings/typescript/.prettierrc | 5 + bindings/typescript/README.md | 48 +- bindings/typescript/build.js | 22 +- bindings/typescript/eslint.config.js | 13 + bindings/typescript/package-lock.json | 1958 ++++++++++++++++++++++++- bindings/typescript/package.json | 15 +- bindings/typescript/src/index.ts | 413 +++--- bindings/typescript/src/types.ts | 109 +- bindings/typescript/src/utils.ts | 208 +-- bindings/typescript/tsconfig.json | 10 +- examples/calculator.rs | 2 +- examples/json.rs | 2 +- src/errors.rs | 46 +- src/grammar.rs | 2 +- src/lib.rs | 17 +- src/parser.rs | 91 +- src/span.rs | 78 + src/trace.rs | 10 +- src/tree.rs | 7 +- src/utils.rs | 23 +- tests/grammar.rs | 27 +- tests/parsing.rs | 6 +- tests/span.rs | 90 ++ 25 files changed, 2817 insertions(+), 427 deletions(-) create mode 100644 bindings/typescript/.prettierignore create mode 100644 bindings/typescript/.prettierrc create mode 100644 bindings/typescript/eslint.config.js create mode 100644 src/span.rs create mode 100644 tests/span.rs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1f71ce7..6cf0022 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,8 +13,8 @@ concurrency: cancel-in-progress: ${{ github.event_name == 'pull_request' }} jobs: - conformance: - name: Conformance + conformance-rust: + name: Conformance Rust runs-on: "ubuntu-latest" steps: - name: Checkout @@ -51,7 +51,29 @@ jobs: command: clippy args: -- --deny warnings - test: + conformance-ts: + name: Conformance TypeScript + runs-on: "ubuntu-latest" + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Setup Node.js + uses: actions/setup-node@v3 + with: + node-version: '22' + + - name: Install Dependencies + run: cd bindings/typescript && npm install + + - name: Eslint + run: cd bindings/typescript && npm run lint + + - name: Prettier + run: cd bindings/typescript && npm run format:check + + + test-rust: strategy: matrix: os: @@ -59,7 +81,7 @@ jobs: - macos-latest - ubuntu-latest - name: Test (${{ matrix.os }}) + name: Test Rust (${{ matrix.os }}) runs-on: ${{ matrix.os }} steps: - name: Checkout @@ -83,14 +105,14 @@ jobs: toolchain: stable override: true - - name: Test + - name: Test Rust uses: actions-rs/cargo@v1 with: command: test publish-crates-io: if: startsWith(github.ref, 'refs/tags/v') - needs: [ conformance, test ] + needs: [ conformance-rust, test-rust ] name: Publish to Crates.io runs-on: "ubuntu-latest" @@ -112,7 +134,7 @@ jobs: publish-npm: if: startsWith(github.ref, 'refs/tags/v') - needs: [ conformance, test ] + needs: [ conformance-rust, test-rust ] name: Publish to NPM runs-on: "ubuntu-latest" @@ -130,7 +152,7 @@ jobs: - name: Setup Node.js uses: actions/setup-node@v3 with: - node-version: '20' + node-version: '22' - name: Install Dependencies run: cd bindings/typescript && npm install diff --git a/bindings/typescript/.prettierignore b/bindings/typescript/.prettierignore new file mode 100644 index 0000000..b242667 --- /dev/null +++ b/bindings/typescript/.prettierignore @@ -0,0 +1,4 @@ +# Ignore artifacts: +dist +node_modules +src/pkg diff --git a/bindings/typescript/.prettierrc b/bindings/typescript/.prettierrc new file mode 100644 index 0000000..3c8547b --- /dev/null +++ b/bindings/typescript/.prettierrc @@ -0,0 +1,5 @@ +{ + "tabWidth": 2, + "useTabs": false, + "endOfLine": "lf" +} diff --git a/bindings/typescript/README.md b/bindings/typescript/README.md index e230a95..10fd80d 100644 --- a/bindings/typescript/README.md +++ b/bindings/typescript/README.md @@ -5,6 +5,7 @@ It is focused on providing educational resources for learning about parsing algorithms and compiler construction. The library is designed to be easy to use and understand, making it ideal for students, educators, and developers interested in language processing. ### Table of Contents + 1. [Installation](#installation) 2. [Basic Usage](#basic-usage) 3. [Defining a Grammar](#defining-a-grammar) @@ -16,7 +17,7 @@ It is focused on providing educational resources for learning about parsing algo Before using the `dotlr` library, you need to install it. The following instructions assume you have a project with `npm` already set up. ```bash -npm install dotlr +npm install dotlr ``` ### Importing the Library @@ -24,12 +25,15 @@ npm install dotlr To use the `dotlr` library, import it into your TypeScript files: ```ts -import { Grammar, LR1Parser, LALRParser } from 'dotlr'; +import { Grammar, LR1Parser, LALRParser } from "dotlr"; ``` + this library uses `ts-results` under the hood to handle errors and results. + ```ts -import { Ok, Err } from 'ts-results'; +import { Ok, Err } from "ts-results-es"; ``` + ## Basic Usage The core of the `dotlr` library revolves around defining a grammar and using it to create a parser. The following steps will guide you through this process. @@ -50,12 +54,12 @@ const grammarStr = ` const grammarResult = Grammar.parse(grammarStr); if (grammarResult.ok) { - const grammar = grammarResult.val; - console.log("Grammar successfully parsed!"); - console.log(grammar.getSymbols()); - console.log(grammar.getProductions()); + const grammar = grammarResult.val; + console.log("Grammar successfully parsed!"); + console.log(grammar.getSymbols()); + console.log(grammar.getProductions()); } else { - console.error("Failed to parse grammar:", grammarResult.val); + console.error("Failed to parse grammar:", grammarResult.val); } ``` @@ -71,24 +75,24 @@ The `LR1Parser` class allows you to create an LR(1) parser for the grammar and u const lr1ParserResult = LR1Parser.fromGrammar(grammar); if (lr1ParserResult.ok) { - const lr1Parser = lr1ParserResult.val; - - const input = "aab"; - const parseResult = lr1Parser.parse(input); - - if (parseResult.ok) { - const parseTree = parseResult.val; - console.log("Parse successful!"); - console.log(parseTree); - } else { - console.error("Parse error:", parseResult.val); - } + const lr1Parser = lr1ParserResult.val; + + const input = "aab"; + const parseResult = lr1Parser.parse(input); + + if (parseResult.ok) { + const parseTree = parseResult.val; + console.log("Parse successful!"); + console.log(parseTree); + } else { + console.error("Parse error:", parseResult.val); + } } else { - console.error("Failed to create LR(1) parser:", lr1ParserResult.val); + console.error("Failed to create LR(1) parser:", lr1ParserResult.val); } ``` -- **LR1Parser.fromGrammar()**: Consumes the `Grammar` object and returns an `LR1Parser`, you cannot reuse the *Grammar* object, if you need it, you can clone it by using `grammar.clone()`. +- **LR1Parser.fromGrammar()**: Consumes the `Grammar` object and returns an `LR1Parser`, you cannot reuse the _Grammar_ object, if you need it, you can clone it by using `grammar.clone()`. - **parser.parse()**: method attempts to parse the given input string according to the LR(1) grammar. Returns a parse tree if successful. - **parser.trace()** method can be used to trace the parsing process. It returns a trace and the resulting parse tree at each step, if parsing is successful. - **parser.tokenize()** method can be used to tokenize the input string. It returns a list of tokens. diff --git a/bindings/typescript/build.js b/bindings/typescript/build.js index 6ec81f3..b421af7 100644 --- a/bindings/typescript/build.js +++ b/bindings/typescript/build.js @@ -1,16 +1,14 @@ -import {execSync} from 'child_process'; -import fs from "fs/promises" - +import { execSync } from "child_process"; +import fs from "fs/promises"; async function init() { - console.log("Starting build...") - execSync('tsc', {stdio: 'inherit'}); - await fs.cp("./src/pkg", "./dist/pkg", {recursive: true}); - await fs.unlink("./dist/pkg/package.json"); - await fs.unlink("./dist/pkg/README.md"); - await fs.unlink("./dist/pkg/.gitignore"); - console.log("Build complete") - + console.log("Starting build..."); + execSync("tsc", { stdio: "inherit" }); + await fs.cp("./src/pkg", "./dist/pkg", { recursive: true }); + await fs.unlink("./dist/pkg/package.json"); + await fs.unlink("./dist/pkg/README.md"); + await fs.unlink("./dist/pkg/.gitignore"); + console.log("Build complete"); } -init() +init(); diff --git a/bindings/typescript/eslint.config.js b/bindings/typescript/eslint.config.js new file mode 100644 index 0000000..6e030d8 --- /dev/null +++ b/bindings/typescript/eslint.config.js @@ -0,0 +1,13 @@ +import pluginJs from "@eslint/js"; +import tseslint from "typescript-eslint"; + +export default [ + { + ignores: ["src/pkg/", "dist/", "node_modules/", "build.js"], + }, + { + files: ["src/**/*.{js,mjs,cjs,ts}"], + }, + pluginJs.configs.recommended, + ...tseslint.configs.recommended, +]; diff --git a/bindings/typescript/package-lock.json b/bindings/typescript/package-lock.json index 2d0a20f..fe193e2 100644 --- a/bindings/typescript/package-lock.json +++ b/bindings/typescript/package-lock.json @@ -9,17 +9,1889 @@ "version": "0.3.0", "license": "MIT OR Apache-2.0", "dependencies": { - "ts-results": "^3.3.0" + "ts-results-es": "^4.2.0" }, "devDependencies": { - "typescript": "^5.5.0" + "@eslint/js": "^9.12.0", + "@types/node": "^22.7.5", + "eslint": "^9.12.0", + "prettier": "3.3.3", + "tsx": "^4.19.1", + "typescript": "^5.6.0", + "typescript-eslint": "^8.8.1" } }, - "node_modules/ts-results": { + "node_modules/@esbuild/aix-ppc64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.23.1.tgz", + "integrity": "sha512-6VhYk1diRqrhBAqpJEdjASR/+WVRtfjpqKuNw11cLiaWpAT/Uu+nokB+UJnevzy/P9C/ty6AOe0dwueMrGh/iQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.23.1.tgz", + "integrity": "sha512-uz6/tEy2IFm9RYOyvKl88zdzZfwEfKZmnX9Cj1BHjeSGNuGLuMD1kR8y5bteYmwqKm1tj8m4cb/aKEorr6fHWQ==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.23.1.tgz", + "integrity": "sha512-xw50ipykXcLstLeWH7WRdQuysJqejuAGPd30vd1i5zSyKK3WE+ijzHmLKxdiCMtH1pHz78rOg0BKSYOSB/2Khw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.23.1.tgz", + "integrity": "sha512-nlN9B69St9BwUoB+jkyU090bru8L0NA3yFvAd7k8dNsVH8bi9a8cUAUSEcEEgTp2z3dbEDGJGfP6VUnkQnlReg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.23.1.tgz", + "integrity": "sha512-YsS2e3Wtgnw7Wq53XXBLcV6JhRsEq8hkfg91ESVadIrzr9wO6jJDMZnCQbHm1Guc5t/CdDiFSSfWP58FNuvT3Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.23.1.tgz", + "integrity": "sha512-aClqdgTDVPSEGgoCS8QDG37Gu8yc9lTHNAQlsztQ6ENetKEO//b8y31MMu2ZaPbn4kVsIABzVLXYLhCGekGDqw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.23.1.tgz", + "integrity": "sha512-h1k6yS8/pN/NHlMl5+v4XPfikhJulk4G+tKGFIOwURBSFzE8bixw1ebjluLOjfwtLqY0kewfjLSrO6tN2MgIhA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.23.1.tgz", + "integrity": "sha512-lK1eJeyk1ZX8UklqFd/3A60UuZ/6UVfGT2LuGo3Wp4/z7eRTRYY+0xOu2kpClP+vMTi9wKOfXi2vjUpO1Ro76g==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.23.1.tgz", + "integrity": "sha512-CXXkzgn+dXAPs3WBwE+Kvnrf4WECwBdfjfeYHpMeVxWE0EceB6vhWGShs6wi0IYEqMSIzdOF1XjQ/Mkm5d7ZdQ==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.23.1.tgz", + "integrity": "sha512-/93bf2yxencYDnItMYV/v116zff6UyTjo4EtEQjUBeGiVpMmffDNUyD9UN2zV+V3LRV3/on4xdZ26NKzn6754g==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.23.1.tgz", + "integrity": "sha512-VTN4EuOHwXEkXzX5nTvVY4s7E/Krz7COC8xkftbbKRYAl96vPiUssGkeMELQMOnLOJ8k3BY1+ZY52tttZnHcXQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.23.1.tgz", + "integrity": "sha512-Vx09LzEoBa5zDnieH8LSMRToj7ir/Jeq0Gu6qJ/1GcBq9GkfoEAoXvLiW1U9J1qE/Y/Oyaq33w5p2ZWrNNHNEw==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.23.1.tgz", + "integrity": "sha512-nrFzzMQ7W4WRLNUOU5dlWAqa6yVeI0P78WKGUo7lg2HShq/yx+UYkeNSE0SSfSure0SqgnsxPvmAUu/vu0E+3Q==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.23.1.tgz", + "integrity": "sha512-dKN8fgVqd0vUIjxuJI6P/9SSSe/mB9rvA98CSH2sJnlZ/OCZWO1DJvxj8jvKTfYUdGfcq2dDxoKaC6bHuTlgcw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.23.1.tgz", + "integrity": "sha512-5AV4Pzp80fhHL83JM6LoA6pTQVWgB1HovMBsLQ9OZWLDqVY8MVobBXNSmAJi//Csh6tcY7e7Lny2Hg1tElMjIA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.23.1.tgz", + "integrity": "sha512-9ygs73tuFCe6f6m/Tb+9LtYxWR4c9yg7zjt2cYkjDbDpV/xVn+68cQxMXCjUpYwEkze2RcU/rMnfIXNRFmSoDw==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.23.1.tgz", + "integrity": "sha512-EV6+ovTsEXCPAp58g2dD68LxoP/wK5pRvgy0J/HxPGB009omFPv3Yet0HiaqvrIrgPTBuC6wCH1LTOY91EO5hQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.23.1.tgz", + "integrity": "sha512-aevEkCNu7KlPRpYLjwmdcuNz6bDFiE7Z8XC4CPqExjTvrHugh28QzUXVOZtiYghciKUacNktqxdpymplil1beA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.23.1.tgz", + "integrity": "sha512-3x37szhLexNA4bXhLrCC/LImN/YtWis6WXr1VESlfVtVeoFJBRINPJ3f0a/6LV8zpikqoUg4hyXw0sFBt5Cr+Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.23.1.tgz", + "integrity": "sha512-aY2gMmKmPhxfU+0EdnN+XNtGbjfQgwZj43k8G3fyrDM/UdZww6xrWxmDkuz2eCZchqVeABjV5BpildOrUbBTqA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.23.1.tgz", + "integrity": "sha512-RBRT2gqEl0IKQABT4XTj78tpk9v7ehp+mazn2HbUeZl1YMdaGAQqhapjGTCe7uw7y0frDi4gS0uHzhvpFuI1sA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.23.1.tgz", + "integrity": "sha512-4O+gPR5rEBe2FpKOVyiJ7wNDPA8nGzDuJ6gN4okSA1gEOYZ67N8JPk58tkWtdtPeLz7lBnY6I5L3jdsr3S+A6A==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.23.1.tgz", + "integrity": "sha512-BcaL0Vn6QwCwre3Y717nVHZbAa4UBEigzFm6VdsVdT/MbZ38xoj1X9HPkZhbmaBGUD1W8vxAfffbDe8bA6AKnQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.23.1.tgz", + "integrity": "sha512-BHpFFeslkWrXWyUPnbKm+xYYVYruCinGcftSBaa8zoF9hZO4BcSCFUvHVTtzpIY6YzUnYtuEhZ+C9iEXjxnasg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.11.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.11.1.tgz", + "integrity": "sha512-m4DVN9ZqskZoLU5GlWZadwDnYo3vAEydiUayB9widCl9ffWx2IvPnp6n3on5rJmziJSw9Bv+Z3ChDVdMwXCY8Q==", + "dev": true, + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.18.0.tgz", + "integrity": "sha512-fTxvnS1sRMu3+JjXwJG0j/i4RT9u4qJ+lqS/yCGap4lH4zZGzQ7tu+xZqQmcMZq5OBZDL4QRxQzRjkWcGt8IVw==", + "dev": true, + "dependencies": { + "@eslint/object-schema": "^2.1.4", + "debug": "^4.3.1", + "minimatch": "^3.1.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/core": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.6.0.tgz", + "integrity": "sha512-8I2Q8ykA4J0x0o7cg67FPVnehcqWTBehu/lmY+bolPFHGjh49YzGBMXTvpqVgEbBdvNCSxj6iFgiIyHzf03lzg==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.1.0.tgz", + "integrity": "sha512-4Bfj15dVJdoy3RfZmmo86RK1Fwzn6SstsvK9JS+BaVKqC6QQQQyXekNaC+g+LKNgkQ+2VhGAzm6hO40AhMR3zQ==", + "dev": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/js": { + "version": "9.12.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.12.0.tgz", + "integrity": "sha512-eohesHH8WFRUprDNyEREgqP6beG6htMeUYeCpkEgBCieCMme5r9zFWjzAJp//9S+Kub4rqE+jXe9Cp1a7IYIIA==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.4.tgz", + "integrity": "sha512-BsWiH1yFGjXXS2yvrf5LyuoSIIbPrGUWob917o+BTKuZ7qJdxX8aJLRxs1fS9n6r7vESrq1OUqb68dANcFXuQQ==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.0.tgz", + "integrity": "sha512-vH9PiIMMwvhCx31Af3HiGzsVNULDbyVkHXwlemn/B0TFj/00ho3y55efXrUZTfQipxoHC5u4xq6zblww1zm1Ig==", + "dev": true, + "dependencies": { + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.0.tgz", + "integrity": "sha512-2cbWIHbZVEweE853g8jymffCA+NCMiuqeECeBBLm8dg2oFdjuGJhgN4UAbI+6v0CKbbhvtXA4qV8YR5Ji86nmw==", + "dev": true, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.5", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.5.tgz", + "integrity": "sha512-KSPA4umqSG4LHYRodq31VDwKAvaTF4xmVlzM8Aeh4PlU1JQ3IG0wiA8C25d3RQ9nJyM3mBHyI53K06VVL/oFFg==", + "dev": true, + "dependencies": { + "@humanfs/core": "^0.19.0", + "@humanwhocodes/retry": "^0.3.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz", + "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==", + "dev": true, + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@types/estree": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", + "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", + "dev": true + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true + }, + "node_modules/@types/node": { + "version": "22.7.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.7.5.tgz", + "integrity": "sha512-jML7s2NAzMWc//QSJ1a3prpk78cOPchGvXJsC3C6R6PSMoooztvRVQEz89gmBTBY1SPMaqo5teB4uNHPdetShQ==", + "dev": true, + "dependencies": { + "undici-types": "~6.19.2" + } + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.8.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.8.1.tgz", + "integrity": "sha512-xfvdgA8AP/vxHgtgU310+WBnLB4uJQ9XdyP17RebG26rLtDrQJV3ZYrcopX91GrHmMoH8bdSwMRh2a//TiJ1jQ==", + "dev": true, + "dependencies": { + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "8.8.1", + "@typescript-eslint/type-utils": "8.8.1", + "@typescript-eslint/utils": "8.8.1", + "@typescript-eslint/visitor-keys": "8.8.1", + "graphemer": "^1.4.0", + "ignore": "^5.3.1", + "natural-compare": "^1.4.0", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0", + "eslint": "^8.57.0 || ^9.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "8.8.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.8.1.tgz", + "integrity": "sha512-hQUVn2Lij2NAxVFEdvIGxT9gP1tq2yM83m+by3whWFsWC+1y8pxxxHUFE1UqDu2VsGi2i6RLcv4QvouM84U+ow==", + "dev": true, + "dependencies": { + "@typescript-eslint/scope-manager": "8.8.1", + "@typescript-eslint/types": "8.8.1", + "@typescript-eslint/typescript-estree": "8.8.1", + "@typescript-eslint/visitor-keys": "8.8.1", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.8.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.8.1.tgz", + "integrity": "sha512-X4JdU+66Mazev/J0gfXlcC/dV6JI37h+93W9BRYXrSn0hrE64IoWgVkO9MSJgEzoWkxONgaQpICWg8vAN74wlA==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "8.8.1", + "@typescript-eslint/visitor-keys": "8.8.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "8.8.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.8.1.tgz", + "integrity": "sha512-qSVnpcbLP8CALORf0za+vjLYj1Wp8HSoiI8zYU5tHxRVj30702Z1Yw4cLwfNKhTPWp5+P+k1pjmD5Zd1nhxiZA==", + "dev": true, + "dependencies": { + "@typescript-eslint/typescript-estree": "8.8.1", + "@typescript-eslint/utils": "8.8.1", + "debug": "^4.3.4", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/types": { + "version": "8.8.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.8.1.tgz", + "integrity": "sha512-WCcTP4SDXzMd23N27u66zTKMuEevH4uzU8C9jf0RO4E04yVHgQgW+r+TeVTNnO1KIfrL8ebgVVYYMMO3+jC55Q==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.8.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.8.1.tgz", + "integrity": "sha512-A5d1R9p+X+1js4JogdNilDuuq+EHZdsH9MjTVxXOdVFfTJXunKJR/v+fNNyO4TnoOn5HqobzfRlc70NC6HTcdg==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "8.8.1", + "@typescript-eslint/visitor-keys": "8.8.1", + "debug": "^4.3.4", + "fast-glob": "^3.3.2", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.8.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.8.1.tgz", + "integrity": "sha512-/QkNJDbV0bdL7H7d0/y0qBbV2HTtf0TIyjSDTvvmQEzeVx8jEImEbLuOA4EsvE8gIgqMitns0ifb5uQhMj8d9w==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@typescript-eslint/scope-manager": "8.8.1", + "@typescript-eslint/types": "8.8.1", + "@typescript-eslint/typescript-estree": "8.8.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.8.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.8.1.tgz", + "integrity": "sha512-0/TdC3aeRAsW7MDvYRwEc1Uwm0TIBfzjPFgg60UU2Haj5qsCs9cc3zNgY71edqE3LbWfF/WoZQd3lJoDXFQpag==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "8.8.1", + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/acorn": { + "version": "8.12.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz", + "integrity": "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "node_modules/esbuild": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.23.1.tgz", + "integrity": "sha512-VVNz/9Sa0bs5SELtn3f7qhJCDPCF5oMEl5cO9/SSinpE9hbPVvxbd572HH5AKiP7WD8INO53GgfDDhRjkylHEg==", + "dev": true, + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.23.1", + "@esbuild/android-arm": "0.23.1", + "@esbuild/android-arm64": "0.23.1", + "@esbuild/android-x64": "0.23.1", + "@esbuild/darwin-arm64": "0.23.1", + "@esbuild/darwin-x64": "0.23.1", + "@esbuild/freebsd-arm64": "0.23.1", + "@esbuild/freebsd-x64": "0.23.1", + "@esbuild/linux-arm": "0.23.1", + "@esbuild/linux-arm64": "0.23.1", + "@esbuild/linux-ia32": "0.23.1", + "@esbuild/linux-loong64": "0.23.1", + "@esbuild/linux-mips64el": "0.23.1", + "@esbuild/linux-ppc64": "0.23.1", + "@esbuild/linux-riscv64": "0.23.1", + "@esbuild/linux-s390x": "0.23.1", + "@esbuild/linux-x64": "0.23.1", + "@esbuild/netbsd-x64": "0.23.1", + "@esbuild/openbsd-arm64": "0.23.1", + "@esbuild/openbsd-x64": "0.23.1", + "@esbuild/sunos-x64": "0.23.1", + "@esbuild/win32-arm64": "0.23.1", + "@esbuild/win32-ia32": "0.23.1", + "@esbuild/win32-x64": "0.23.1" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "9.12.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.12.0.tgz", + "integrity": "sha512-UVIOlTEWxwIopRL1wgSQYdnVDcEvs2wyaO6DGo5mXqe3r16IoCNWkR29iHhyaP4cICWjbgbmFUGAhh0GJRuGZw==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.11.0", + "@eslint/config-array": "^0.18.0", + "@eslint/core": "^0.6.0", + "@eslint/eslintrc": "^3.1.0", + "@eslint/js": "9.12.0", + "@eslint/plugin-kit": "^0.2.0", + "@humanfs/node": "^0.16.5", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.3.1", + "@types/estree": "^1.0.6", + "@types/json-schema": "^7.0.15", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.1.0", + "eslint-visitor-keys": "^4.1.0", + "espree": "^10.2.0", + "esquery": "^1.5.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-scope": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.1.0.tgz", + "integrity": "sha512-14dSvlhaVhKKsa9Fx1l8A17s7ah7Ef7wCakJ10LYk6+GYmP9yDti2oq2SEwcyndt6knfcZyhyxwY3i9yL78EQw==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.1.0.tgz", + "integrity": "sha512-Q7lok0mqMUSf5a/AdAZkA5a/gHcO6snwQClVNNvFKCAVlxXucdU8pKydU5ZVZjBx5xr37vGbFFWtLQYreLzrZg==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.2.0.tgz", + "integrity": "sha512-upbkBJbckcCNBDBDXEbuhjbP68n+scUd3k/U2EkyM9nw+I/jPiL4cLF/Al06CF96wRltFda16sxDFrxsI1v0/g==", + "dev": true, + "dependencies": { + "acorn": "^8.12.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.1.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", + "dev": true, + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "node_modules/fast-glob": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true + }, + "node_modules/fastq": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", + "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flatted": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", + "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", + "dev": true + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/get-tsconfig": { + "version": "4.8.1", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.8.1.tgz", + "integrity": "sha512-k9PN+cFBmaLWtVz29SkUoqU5O0slLuHJXt/2P+tMVFT+phsSGXGkp9t3rQIqdz0e+06EHNGs3oM6ZX1s2zHxRg==", + "dev": true, + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { "version": "3.3.0", - "resolved": "https://registry.npmjs.org/ts-results/-/ts-results-3.3.0.tgz", - "integrity": "sha512-FWqxGX2NHp5oCyaMd96o2y2uMQmSu8Dey6kvyuFdRJ2AzfmWo3kWa4UsPlCGlfQ/qu03m09ZZtppMoY8EMHuiA==", - "license": "MIT" + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "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==", + "dev": true + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prettier": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.3.tgz", + "integrity": "sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==", + "dev": true, + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "dev": true, + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/ts-api-utils": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz", + "integrity": "sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==", + "dev": true, + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "typescript": ">=4.2.0" + } + }, + "node_modules/ts-results-es": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/ts-results-es/-/ts-results-es-4.2.0.tgz", + "integrity": "sha512-GfpRk+qvHxa/6gADH8WMN/jXvs5oHYbKtMQc6X9L3VhToy5Lri3iQowyYSytaRcvPDiTT2z3vurzQZXFQFXKRA==" + }, + "node_modules/tsx": { + "version": "4.19.1", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.19.1.tgz", + "integrity": "sha512-0flMz1lh74BR4wOvBjuh9olbnwqCPc35OOlfyzHba0Dc+QNUeWX/Gq2YTbnwcWPO3BMd8fkzRVrHcsR+a7z7rA==", + "dev": true, + "dependencies": { + "esbuild": "~0.23.0", + "get-tsconfig": "^4.7.5" + }, + "bin": { + "tsx": "dist/cli.mjs" + }, + "engines": { + "node": ">=18.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + } + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } }, "node_modules/typescript": { "version": "5.6.2", @@ -34,6 +1906,80 @@ "engines": { "node": ">=14.17" } + }, + "node_modules/typescript-eslint": { + "version": "8.8.1", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.8.1.tgz", + "integrity": "sha512-R0dsXFt6t4SAFjUSKFjMh4pXDtq04SsFKCVGDP3ZOzNP7itF0jBcZYU4fMsZr4y7O7V7Nc751dDeESbe4PbQMQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.8.1", + "@typescript-eslint/parser": "8.8.1", + "@typescript-eslint/utils": "8.8.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/undici-types": { + "version": "6.19.8", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", + "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", + "dev": true + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } } } } diff --git a/bindings/typescript/package.json b/bindings/typescript/package.json index df70c63..e6fc1ba 100644 --- a/bindings/typescript/package.json +++ b/bindings/typescript/package.json @@ -24,12 +24,21 @@ "build-all": "npm run build-wasm:no-test && npm run build-lib", "build-wasm": "cd .. && cargo test && wasm-pack build --out-dir bindings/typescript/src/pkg --out-name dotlr --features wasm --no-default-features", "build-wasm:no-test": "cd .. && wasm-pack build --out-dir bindings/typescript/src/pkg --out-name dotlr --features wasm --no-default-features", - "build-lib": "node build.js" + "build-lib": "node build.js", + "lint": "npx eslint .", + "format": "npx prettier . --write", + "format:check": "npx prettier . --check" }, "devDependencies": { - "typescript": "^5.5.0" + "@eslint/js": "^9.12.0", + "@types/node": "^22.7.5", + "eslint": "^9.12.0", + "prettier": "3.3.3", + "tsx": "^4.19.1", + "typescript": "^5.6.0", + "typescript-eslint": "^8.8.1" }, "dependencies": { - "ts-results": "^3.3.0" + "ts-results-es": "^4.2.0" } } diff --git a/bindings/typescript/src/index.ts b/bindings/typescript/src/index.ts index 9d6f9b5..d03c0e2 100644 --- a/bindings/typescript/src/index.ts +++ b/bindings/typescript/src/index.ts @@ -1,197 +1,252 @@ -import {Grammar as _Grammar, Parser as _Parser,} from './pkg/dotlr' import { - ActionTable, - Automaton, - FirstTable, - FollowTable, - GoToTable, - GrammarError, - ParserError, - ParsingError, - ParsingTables, - Rule, - Token, - Trace, - Tree -} from './types' -import {Err, Ok} from "ts-results"; + Grammar as _Grammar, + Parser as _Parser, + WasmParserError, +} from "./pkg/dotlr"; +import { + ActionTable, + Automaton, + FirstTable, + FollowTable, + GoToTable, + GrammarError, + LALR1ParserOfGrammar, + LR1ParserOfGrammar, + ParserError, + ParsingError, + ParsingTables, + Rule, + Spanned, + Token, + Trace, + Tree, +} from "./types"; +import { Err, Ok } from "ts-results-es"; export class Grammar< - T extends string = string, - NT extends string = string, - R extends string = string + T extends string = string, + NT extends string = string, + R extends string = string, > { - grammar: _Grammar - private cache = { - symbols: null as NT[] | null, - constant_tokens: null as T[] | null, - start_symbol: null as NT | null, - regex_tokens: null as Map | null, - productions: null as Rule>[] | null, - stringify: null as string | null - } - - private constructor(grammar: _Grammar) { - this.grammar = grammar - } - - static parse(grammar: string) { - try { - const res = _Grammar.parse_wasm(grammar) - return Ok(new Grammar(res)) - } catch (e) { - return Err(e as GrammarError) - } - } - - getSymbols() { - return this.cache.symbols ??= this.grammar.symbols_wasm() as NT[] - } - - getConstantTokens() { - return this.cache.constant_tokens ??= this.grammar.constant_tokens_wasm() as T[] - } - - getStartSymbol() { - return this.cache.start_symbol ??= this.grammar.start_symbol_wasm() as NT - } - - getProductions() { - return this.cache.productions ??= this.grammar.rules_wasm() as Rule>[] - } - - getRegexTokens() { - return this.cache.regex_tokens ??= this.grammar.regular_expressions_wasm() as Map - } - - stringify() { - return this.cache.stringify ??= this.grammar.to_string_wasm() as string - } - - clone() { - return new Grammar(this.grammar.clone_wasm()) - } + grammar: _Grammar; + private cache = { + symbols: null as NT[] | null, + constant_tokens: null as T[] | null, + start_symbol: null as NT | null, + regex_tokens: null as Map | null, + productions: null as Rule>[] | null, + stringify: null as string | null, + }; + + private constructor(grammar: _Grammar) { + this.grammar = grammar; + } + + static parse< + T extends string = string, + NT extends string = string, + R extends string = string, + >(grammar: string) { + try { + const res = _Grammar.parse_wasm(grammar); + return Ok(new Grammar(res)); + } catch (e) { + return Err(e as GrammarError); + } + } + + getSymbols() { + return (this.cache.symbols ??= this.grammar.symbols_wasm() as NT[]); + } + + getConstantTokens() { + return (this.cache.constant_tokens ??= + this.grammar.constant_tokens_wasm() as T[]); + } + + getStartSymbol() { + return (this.cache.start_symbol ??= this.grammar.start_symbol_wasm() as NT); + } + + getProductions() { + return (this.cache.productions ??= this.grammar.rules_wasm() as Rule< + Token + >[]); + } + + getRegexTokens() { + return (this.cache.regex_tokens ??= + this.grammar.regular_expressions_wasm() as Map); + } + + stringify() { + return (this.cache.stringify ??= this.grammar.to_string_wasm() as string); + } + + clone() { + return new Grammar(this.grammar.clone_wasm()); + } } - -class Parser< - T extends string = string, - NT extends string = string, - R extends string = string +export class Parser< + T extends string = string, + NT extends string = string, + R extends string = string, > { - - private parser: _Parser - private cache = { - action_table: null as ActionTable> | null, - goto_table: null as GoToTable | null, - parsing_tables: null as ParsingTables> | null, - automaton: null as Automaton> | null, - first_table: null as FirstTable> | null, - follow_table: null as FollowTable> | null, - } - - constructor(parser: _Parser) { - this.parser = parser - } - - parse(input: string) { - try { - return Ok(this.parser.parse_wasm(input) as Tree>) - } catch (e) { - return Err(e as ParsingError) - } - } - - getActionTable() { - return this.cache.action_table ??= this.parser.action_table_wasm() as ActionTable> - } - - getGotoTable() { - return this.cache.goto_table ??= this.parser.goto_table_wasm() as GoToTable - } - - getParseTables() { - return this.cache.parsing_tables ??= this.parser.parsing_tables_wasm() as ParsingTables> - } - - getAutomaton() { - return this.cache.automaton ??= this.parser.automaton_wasm() as Automaton> - } - - getFirstTable() { - return this.cache.first_table ??= this.parser.first_table_wasm() as FirstTable> - } - - getFollowTable() { - return this.cache.follow_table ??= this.parser.follow_table_wasm() as FollowTable> - } - - tokenize(input: string) { - try { - const tokens = this.parser.tokenize_wasm(input) as [Token, string][] - return Ok(tokens.map(([token, slice]) => ({ - token, slice - }))) - } catch (e) { - return Err(e as ParsingError) - } - } - - trace(input: string) { - try { - const [trace, tree] = this.parser.trace_wasm(input) as [Trace>>, Tree>] - return Ok({ - trace, - tree - }) - } catch (e) { - return Err(e as ParsingError) - } - } + private parser: _Parser; + private cache = { + action_table: null as ActionTable> | null, + goto_table: null as GoToTable | null, + parsing_tables: null as ParsingTables> | null, + automaton: null as Automaton> | null, + first_table: null as FirstTable> | null, + follow_table: null as FollowTable> | null, + }; + + constructor(parser: _Parser) { + this.parser = parser; + } + + parse(input: string) { + try { + return Ok(this.parser.parse_wasm(input) as Tree>); + } catch (e) { + return Err(e as ParsingError); + } + } + + getActionTable() { + return (this.cache.action_table ??= + this.parser.action_table_wasm() as ActionTable>); + } + + getGotoTable() { + return (this.cache.goto_table ??= + this.parser.goto_table_wasm() as GoToTable); + } + + getParseTables() { + return (this.cache.parsing_tables ??= + this.parser.parsing_tables_wasm() as ParsingTables>); + } + + getAutomaton() { + return (this.cache.automaton ??= this.parser.automaton_wasm() as Automaton< + Token + >); + } + + getFirstTable() { + return (this.cache.first_table ??= + this.parser.first_table_wasm() as FirstTable>); + } + + getFollowTable() { + return (this.cache.follow_table ??= + this.parser.follow_table_wasm() as FollowTable>); + } + + tokenize(input: string) { + try { + const tokens = this.parser.tokenize_wasm(input) as [ + Spanned>, + string, + ][]; + return Ok( + tokens.map(([token, slice]) => ({ + token, + slice, + })), + ); + } catch (e) { + return Err(e as ParsingError); + } + } + + trace(input: string) { + try { + const [trace, tree] = this.parser.trace_wasm(input) as [ + Trace>>, + Tree>, + ]; + return Ok({ + trace, + tree, + }); + } catch (e) { + return Err(e as ParsingError); + } + } } -class LRParser { - //TODO +// this function tries to recover the serialized parser into the actual parser +function mapParserError(error: WasmParserError, kind: "lalr1" | "lr1") { + const serialized = error.serialize() as ParserError; + if (serialized.type === "Conflict") { + serialized.value.parser = + kind === "lalr1" + ? // @ts-expect-error private constructor + new LALR1Parser(error.into_conflict_parser()) + : // @ts-expect-error private constructor + new LR1Parser(error.into_conflict_parser()); + } + return serialized as ParserError; } export class LR1Parser< - T extends string = string, - NT extends string = string, - R extends string = string + T extends string = string, + NT extends string = string, + R extends string = string, > extends Parser { - private constructor(parser: _Parser) { - super(parser) - } - - /** - * Consumes a grammar and returns a parser, the grammar is consumed and the ownership is transferred to the parser - */ - static fromGrammar(grammar: G) { - try { - return Ok(new LR1Parser(_Parser.new_wasm(grammar.grammar))) - } catch (e) { - return Err(e as ParserError) - } - } + private constructor(parser: _Parser) { + super(parser); + } + + /** + * Consumes a grammar and returns a parser, the grammar is consumed and the ownership is transferred to the parser + */ + static fromGrammar(grammar: G) { + try { + return Ok( + new LR1Parser( + _Parser.new_wasm(grammar.grammar), + ) as LR1ParserOfGrammar, + ); + } catch (e) { + return Err( + mapParserError(e as WasmParserError, "lr1") as ParserError< + LR1ParserOfGrammar + >, + ); + } + } } export class LALR1Parser< - T extends string = string, - NT extends string = string, - R extends string = string + T extends string = string, + NT extends string = string, + R extends string = string, > extends Parser { - private constructor(parser: _Parser) { - super(parser) - } - - /** - * Consumes a grammar and returns a parser, the grammar is consumed and the ownership is transferred to the parser - */ - static fromGrammar(grammar: G) { - try { - return Ok(new LALR1Parser(_Parser.new_wasm(grammar.grammar))) - } catch (e) { - return Err(e as ParserError) - } - } + private constructor(parser: _Parser) { + super(parser); + } + + /** + * Consumes a grammar and returns a parser, the grammar is consumed and the ownership is transferred to the parser + */ + static fromGrammar(grammar: G) { + try { + return Ok( + new LALR1Parser( + _Parser.new_wasm(grammar.grammar), + ) as LALR1ParserOfGrammar, + ); + } catch (e) { + return Err( + mapParserError(e as WasmParserError, "lalr1") as ParserError< + LALR1ParserOfGrammar + >, + ); + } + } } diff --git a/bindings/typescript/src/types.ts b/bindings/typescript/src/types.ts index 4e37a98..2ecf769 100644 --- a/bindings/typescript/src/types.ts +++ b/bindings/typescript/src/types.ts @@ -1,11 +1,12 @@ //TODO not sure how to type Symbol +import { Grammar, LALR1Parser, LR1Parser, Parser } from "./index"; export type Rule = { - symbol: string, - pattern: AtomicPattern[] -} - + symbol: string; + pattern: AtomicPattern[]; +}; //TODO not sure how to type Symbol +//prettier-ignore export type AtomicPattern = { type: 'Symbol', value: string @@ -14,12 +15,13 @@ export type AtomicPattern = { value: T } - +//prettier-ignore export type Tree = { type: 'Terminal' value: { token: T, slice: string + span: Span } } | { type: 'NonTerminal' @@ -29,6 +31,7 @@ export type Tree = { } } +//prettier-ignore export type Token = { type: 'Constant' value: C @@ -39,6 +42,7 @@ export type Token = { type: 'Eof' } +//prettier-ignore export type GrammarError = { type: "UnexpectedToken", value: { @@ -61,77 +65,76 @@ export type GrammarError = { } } -export type ParserError = { +//prettier-ignore +export type ParserError

= { type: "EmptyGrammar" } | { type: "UndefinedSymbol", value: { symbol: string - rule: Rule + rule: Rule> } } | { type: "UndefinedRegexToken", value: { regex_token: string - rule: Rule + rule: Rule> } } | { type: "Conflict", value: { - parser: { //TODO all the other types should have a Serialized* version, i'd move this to the lib - grammar: any - first_table: any - follow_table: any - automaton: any - parsing_tables: any - } + parser: P state: number, - token: T, + token: TokenOfParser

} } +//prettier-ignore export type ParsingError = { type: "UnknownToken", value: { token: string + span: Span } } | { type: "UnexpectedToken" value: { token: string + span: Span expected: T[] } } | { type: "UnexpectedEof" value: { + span: Span expected: T[] } } - export type Trace = { - steps: Step[] -} + steps: Step[]; +}; export type Step = { - state_stack: number[] - tree_stack: Tr[] - remaining_tokens: Tr extends Tree ? T[] : never - action_taken: Action -} + state_stack: number[]; + tree_stack: Tr[]; + remaining_tokens: Tr extends Tree ? Spanned[] : never; + action_taken: Action; +}; export type Item = { - rule: Rule, - dot: number, - lookahead: T[] -} + rule: Rule; + dot: number; + lookahead: T[]; +}; export type State = { - id: number - items: Item[] - transitions: Map, number> -} + id: number; + items: Item[]; + transitions: Map, number>; +}; export type Automaton = { - states: State[] -} + states: State[]; +}; +//prettier-ignore export type Action = { type: 'Shift', value: { @@ -148,17 +151,39 @@ export type Action = { rule_index: number } } +export type Span = { + offset: number; + len: number; + column: number; + line: number; +}; +export type Spanned = { + span: Span; + value: T; +}; -export type FirstTable = Map +export type FirstTable = Map; -export type FollowTable = Map +export type FollowTable = Map; -export type GoToTable = Map[] +export type GoToTable = Map[]; -export type ActionTable = Map[] +export type ActionTable = Map[]; -export type ParsingTables = { - action_table: ActionTable - goto_table: GoToTable -} +export type ParsingTables< + NT extends string = string, + T extends Token = Token, +> = { + action_table: ActionTable; + goto_table: GoToTable; +}; + +export type TokenOfParser

= + P extends Parser ? Token : never; + +export type LALR1ParserOfGrammar = + G extends Grammar ? LALR1Parser : never; + +export type LR1ParserOfGrammar = + G extends Grammar ? LR1Parser : never; diff --git a/bindings/typescript/src/utils.ts b/bindings/typescript/src/utils.ts index 6bc180e..d7f223a 100644 --- a/bindings/typescript/src/utils.ts +++ b/bindings/typescript/src/utils.ts @@ -1,126 +1,156 @@ -import type {Action, AtomicPattern, GrammarError, Item, ParserError, ParsingError, Rule, Token, Tree} from "./types"; +import type { + Action, + AtomicPattern, + GrammarError, + Item, + ParserError, + ParsingError, + Rule, + Token, + Tree, +} from "./types"; export function stringifyToken(token: Token, noApostrophes = false) { - if (token.type === 'Eof') return "$" - if (token.type === "Regex") return `%${token.value}` - if (token.type === "Constant") return noApostrophes ? token.value : `'${token.value}'` - return "" + if (token.type === "Eof") return "$"; + if (token.type === "Regex") return `%${token.value}`; + if (token.type === "Constant") + return noApostrophes ? token.value : `'${token.value}'`; + return ""; } export function stringifyAtom(atom: AtomicPattern, noApostrophes = false) { - if (atom.type === 'Symbol') return atom.value - if (atom.type === 'Token') return stringifyToken(atom.value, noApostrophes) - return "" + if (atom.type === "Symbol") return atom.value; + if (atom.type === "Token") return stringifyToken(atom.value, noApostrophes); + return ""; } export function stringifyItem(item: Item, noApostrophes = false) { - const children = item.rule.pattern.map((a) => stringifyAtom(a, noApostrophes)) - //inserts the dot - children.splice(item.dot, 0, '.') - return `${item.rule.symbol} -> ${children.join(' ')}` + const children = item.rule.pattern.map((a) => + stringifyAtom(a, noApostrophes), + ); + //inserts the dot + children.splice(item.dot, 0, "."); + return `${item.rule.symbol} -> ${children.join(" ")}`; } export function stringifyRule(rule: Rule, noApostrophes = false) { - const children = rule.pattern.map(a => stringifyAtom(a, noApostrophes)) - return `${rule.symbol} -> ${children.join(' ')}` + const children = rule.pattern.map((a) => stringifyAtom(a, noApostrophes)); + return `${rule.symbol} -> ${children.join(" ")}`; } export function stringifyLookahead(item: Token[], noApostrophes = false) { - const children = item.map(t => stringifyToken(t, noApostrophes)) - return children.join(" ") + const children = item.map((t) => stringifyToken(t, noApostrophes)); + return children.join(" "); } - export function stringifyAction(action: Action) { - if (action.type === 'Accept') return `a${action.value.rule_index + 1}` - if (action.type === 'Reduce') return `r${action.value.rule_index + 1}` - if (action.type === 'Shift') return `s${action.value.next_state}` - return "" + if (action.type === "Accept") return `a${action.value.rule_index + 1}`; + if (action.type === "Reduce") return `r${action.value.rule_index + 1}`; + if (action.type === "Shift") return `s${action.value.next_state}`; + return ""; } - -export function stringifyActionVerbose(action: Action, rules: Rule[], noApostrophes: boolean = false) { - if (action.type === "Shift") { - return `Shift ${action.value.next_state}` - } else if (action.type === "Accept") { - return `Accept ${action.value.rule_index + 1} (${stringifyRule(rules[action.value.rule_index], noApostrophes)})` - } else if (action.type === "Reduce") { - return `Reduce ${action.value.rule_index + 1} (${stringifyRule(rules[action.value.rule_index], noApostrophes)})` - } - return "" +export function stringifyActionVerbose( + action: Action, + rules: Rule[], + noApostrophes: boolean = false, +) { + if (action.type === "Shift") { + return `Shift ${action.value.next_state}`; + } else if (action.type === "Accept") { + return `Accept ${action.value.rule_index + 1} (${stringifyRule(rules[action.value.rule_index], noApostrophes)})`; + } else if (action.type === "Reduce") { + return `Reduce ${action.value.rule_index + 1} (${stringifyRule(rules[action.value.rule_index], noApostrophes)})`; + } + return ""; } -export function stringifyTreeStack(tree: Tree[], noApostrophes = false): string[] { - return tree.map(i => { - if (i.type === "Terminal") return stringifyToken(i.value.token, noApostrophes) - if (i.type === "NonTerminal") return i.value.symbol - }) +export function stringifyTreeStack( + tree: Tree[], + noApostrophes = false, +): string[] { + return tree.map((i) => { + if (i.type === "Terminal") + return stringifyToken(i.value.token, noApostrophes); + if (i.type === "NonTerminal") return i.value.symbol; + }); } - -export function stringifyTree(tree: Tree, indent: string = '', isLast: boolean = true): string { - const linePrefix = isLast ? '└─ ' : '├─ '; - let result = ''; - - if (tree.type === 'Terminal') { - const {token, slice} = tree.value; - if (token.type !== 'Eof') { - result += `${indent}${linePrefix}${token.value} [${slice}]\n`; - } - } else { - const {symbol, pattern} = tree.value; - result += `${indent}${linePrefix}${symbol}\n`; - - const newIndent = indent + (isLast ? ' ' : '│ '); - pattern.forEach((child, index) => { - result += stringifyTree(child, newIndent, index === pattern.length - 1); - }); +export function stringifyTree( + tree: Tree, + indent: string = "", + isLast: boolean = true, +): string { + const linePrefix = isLast ? "└─ " : "├─ "; + let result = ""; + + if (tree.type === "Terminal") { + const { token, slice } = tree.value; + if (token.type !== "Eof") { + result += `${indent}${linePrefix}${token.value} [${slice}]\n`; } + } else { + const { symbol, pattern } = tree.value; + result += `${indent}${linePrefix}${symbol}\n`; - return result; -} + const newIndent = indent + (isLast ? " " : "│ "); + pattern.forEach((child, index) => { + result += stringifyTree(child, newIndent, index === pattern.length - 1); + }); + } + return result; +} export function stringifyGrammarError(e: GrammarError) { - if (e.type === "UnexpectedToken") { - return `Unexpected token, expected one of:\n${e.value.expected.map(maybeToken).join(', ')}` - } else if (e.type === "UnexpectedEof") { - return `Unexpected end of input, expected one of:\n${e.value.expected.map(maybeToken).join(', ')}` - } else if (e.type === 'InvalidRegex') { - return `Invalid regular expression\n${e.value.regex}` - } - return "Unknown error" + if (e.type === "UnexpectedToken") { + return `Unexpected token at ${stringifySpan(e.value)}, expected one of:\n${e.value.expected.map(maybeToken).join(", ")}`; + } else if (e.type === "UnexpectedEof") { + return `Unexpected end of input, expected one of:\n${e.value.expected.map(maybeToken).join(", ")}`; + } else if (e.type === "InvalidRegex") { + return `Invalid regular expression at ${stringifySpan(e.value)}\n${e.value.regex}`; + } + return "Unknown error"; } -function maybeToken(token: Token|string){ - return typeof token === 'string' ? token : stringifyToken(token) +function maybeToken(token: Token | string) { + return typeof token === "string" ? token : stringifyToken(token); } -export function stringifyParsingError(error: ParsingError){ - if (error.type === "UnexpectedEof") { - return `Unexpected end of input, expected one of:\n${error.value.expected.map(maybeToken).join(", ")}` - } else if (error.type === 'UnknownToken') { - return `Unknown token: ${error.value.token}` - } else if (error.type === "UnexpectedToken") { - return `Unexpected token, expected one of:\n${error.value.expected.map(maybeToken).join(', ')}` - } - return "Unknown error" +function stringifySpan(span: { column: number; line: number }) { + return `${span.line}:${span.column}`; +} + +export function stringifyParsingError(error: ParsingError) { + if (error.type === "UnexpectedEof") { + return `Unexpected end of input at ${stringifySpan(error.value.span)}, expected one of:\n${error.value.expected.map(maybeToken).join(", ")}`; + } else if (error.type === "UnknownToken") { + return `Unknown token at ${stringifySpan(error.value.span)}: ${error.value.token}`; + } else if (error.type === "UnexpectedToken") { + return `Unexpected token at ${stringifySpan(error.value.span)}, expected one of:\n${error.value.expected.map(maybeToken).join(", ")}`; + } + return "Unknown error"; } -export function stringifyParserError(error: ParserError){ - if(error.type === "EmptyGrammar") return "Empty grammar" - if(error.type === "UndefinedSymbol") return `Undefined symbol: ${error.value.symbol}` - if(error.type === "UndefinedRegexToken") return `Undefined regex token: ${error.value.regex_token}` - if(error.type === "Conflict") return `Conflict in state ${error.value.state} on token ${stringifyToken(error.value.token)}` - return "Unknown error" +export function stringifyParserError(error: ParserError) { + if (error.type === "EmptyGrammar") return "Empty grammar"; + if (error.type === "UndefinedSymbol") + return `Undefined symbol: ${error.value.symbol}`; + if (error.type === "UndefinedRegexToken") + return `Undefined regex token: ${error.value.regex_token}`; + if (error.type === "Conflict") + return `Conflict in state ${error.value.state} on token ${stringifyToken(error.value.token)}`; + return "Unknown error"; } -export function stringifyError(error: GrammarError | ParsingError | ParserError){ - const s = stringifyGrammarError(error as GrammarError) - const s2 = stringifyParsingError(error as ParsingError) - const s3 = stringifyParserError(error as ParserError) - if([s, s2, s3].every(s => s === "Unknown error")) return "Unknown error" - if(s !== "Unknown error") return s - if(s2 !== "Unknown error") return s2 - return s3 +export function stringifyError( + error: GrammarError | ParsingError | ParserError, +) { + const s = stringifyGrammarError(error as GrammarError); + const s2 = stringifyParsingError(error as ParsingError); + const s3 = stringifyParserError(error as ParserError); + if ([s, s2, s3].every((s) => s === "Unknown error")) return "Unknown error"; + if (s !== "Unknown error") return s; + if (s2 !== "Unknown error") return s2; + return s3; } diff --git a/bindings/typescript/tsconfig.json b/bindings/typescript/tsconfig.json index 57881a7..40039e7 100644 --- a/bindings/typescript/tsconfig.json +++ b/bindings/typescript/tsconfig.json @@ -3,13 +3,11 @@ "target": "ES2020", "module": "esnext", "moduleResolution": "node", - "lib": [ - "esnext", - "dom" - ], + "lib": ["esnext", "dom"], "declaration": true, "esModuleInterop": true, "outDir": "dist", - "rootDir": "./src", - } + "rootDir": "./src" + }, + "include": ["src"] } diff --git a/examples/calculator.rs b/examples/calculator.rs index b068417..90b0ebc 100644 --- a/examples/calculator.rs +++ b/examples/calculator.rs @@ -84,7 +84,7 @@ fn calculate(parser: &Parser, input: &str) -> ExitCode { fn evaluate(tree: Tree<'_>) -> f64 { match tree { - Tree::Terminal { token, slice } => { + Tree::Terminal { token, slice, .. } => { match token { Token::Regex(regex_token) => { match regex_token.as_str() { diff --git a/examples/json.rs b/examples/json.rs index a499022..9fd1ade 100644 --- a/examples/json.rs +++ b/examples/json.rs @@ -21,7 +21,7 @@ pub enum Value { impl From> for Value { fn from(tree: Tree) -> Value { match tree { - Tree::Terminal { token, slice } => { + Tree::Terminal { token, slice, .. } => { match token { Token::Constant(constant_token) => { match constant_token.as_str() { diff --git a/src/errors.rs b/src/errors.rs index c3a6312..0192bd4 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -84,6 +84,37 @@ pub enum ParserError { Conflict { parser: Box, state: usize, token: Token }, } +#[cfg(feature = "wasm")] +#[cfg_attr(feature = "wasm", wasm_bindgen)] +pub struct WasmParserError { + error: ParserError, +} + +#[cfg(feature = "wasm")] +impl WasmParserError { + pub fn new(error: ParserError) -> Self { + WasmParserError { error } + } +} + +#[cfg(feature = "wasm")] +#[cfg_attr(feature = "wasm", wasm_bindgen)] +impl WasmParserError { + pub fn to_string_wasm(&self) -> String { + format!("{}", self.error) + } + pub fn serialize(&self) -> Result { + serde_wasm_bindgen::to_value(&self.error).map_err(JsValue::from) + } + pub fn into_conflict_parser(self) -> Result { + match self.error { + //&Box + ParserError::Conflict { parser, .. } => Ok(*parser), + _ => Err(JsValue::from("Error is not a conflict")), + } + } +} + /// Parsing error of an input tried to be parsed with a parser. #[cfg_attr(feature = "serde", derive(Serialize))] @@ -93,15 +124,17 @@ pub enum ParserError { pub enum ParsingError { /// An unknown token has been encountered. #[error( - "unknown token {}", + "unknown token {} at {}", format_smolstr!("{}", token).green(), + format_smolstr!("{}:{}", span.line, span.column).cyan() )] - UnknownToken { token: SmolStr }, + UnknownToken { token: SmolStr, span: Span }, /// An unexpected token has been encountered. #[error( - "unexpected token {} (expected {})", + "unexpected token {} at {} (expected {})", format_smolstr!("{}", token).green(), + format_smolstr!("{}:{}", span.line, span.column).cyan(), if expected.len() == 1 { format!("{}", format_smolstr!("{}", expected[0]).green()) } else { @@ -111,11 +144,12 @@ pub enum ParsingError { ) }, )] - UnexpectedToken { token: SmolStr, expected: SmallVec<[Token; 2]> }, + UnexpectedToken { token: SmolStr, expected: SmallVec<[Token; 2]>, span: Span }, /// An unexpected end of input has been encountered. #[error( - "unexpected end of input (expected {})", + "unexpected end of input at {} (expected {})", + format_smolstr!("{}:{}", span.line, span.column).cyan(), if expected.len() == 1 { format!("{}", format_smolstr!("{}", expected[0]).green()) } else { @@ -125,5 +159,5 @@ pub enum ParsingError { ) }, )] - UnexpectedEof { expected: SmallVec<[Token; 2]> }, + UnexpectedEof { expected: SmallVec<[Token; 2]>, span: Span }, } diff --git a/src/grammar.rs b/src/grammar.rs index 2056a49..5e29506 100644 --- a/src/grammar.rs +++ b/src/grammar.rs @@ -222,7 +222,7 @@ pub struct Grammar { symbols: IndexSet, start_symbol: Symbol, constant_tokens: IndexSet, - #[cfg_attr(feature = "serde", serde(serialize_with = "serialize_regex_map"))] + #[cfg_attr(feature = "serde", serde(serialize_with = "utils::serialize_regex_map"))] regular_expressions: IndexMap, rules: Vec, } diff --git a/src/lib.rs b/src/lib.rs index 122b350..3804b33 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,6 +4,7 @@ mod automaton; mod errors; mod grammar; mod parser; +mod span; mod tables; mod trace; mod tree; @@ -30,6 +31,10 @@ pub use { Token, }, parser::Parser, + span::{ + Span, + Spanned, + }, tables::{ Action, FirstTable, @@ -45,20 +50,18 @@ pub use { mod prelude { #[cfg(feature = "serde")] - pub use { - serde_renamed::Serialize, - utils::serialize_regex_map, - }; - + pub use serde_renamed::Serialize; #[cfg(feature = "wasm")] - pub use wasm_bindgen::prelude::*; + pub use { + errors::WasmParserError, + wasm_bindgen::prelude::*, + }; #[cfg(not(target_family = "wasm"))] pub use colored::*; #[cfg(target_family = "wasm")] pub use utils::MockColored; - pub use { super::*, indexmap::{ diff --git a/src/parser.rs b/src/parser.rs index 09c003c..d006f0c 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -44,17 +44,11 @@ impl Parser { #[cfg(feature = "wasm")] #[cfg_attr(feature = "wasm", wasm_bindgen)] impl Parser { - pub fn new_wasm(grammar: Grammar) -> Result { - match Parser::lr(grammar) { - Ok(parser) => Ok(parser), - Err(error) => Err(serde_wasm_bindgen::to_value(&error)?), - } + pub fn new_wasm(grammar: Grammar) -> Result { + Parser::lr(grammar).map_err(WasmParserError::new) } - pub fn new_lalr_wasm(grammar: Grammar) -> Result { - match Parser::lalr(grammar) { - Ok(parser) => Ok(parser), - Err(error) => Err(serde_wasm_bindgen::to_value(&error)?), - } + pub fn new_lalr_wasm(grammar: Grammar) -> Result { + Parser::lalr(grammar).map_err(WasmParserError::new) } } @@ -121,13 +115,21 @@ impl Parser { impl Parser { /// Tokenizes an input into a stream of tokens and their corresponding input slices. - pub fn tokenize<'i>(&self, input: &'i str) -> Result, ParsingError> { - let mut tokens = Vec::new(); + pub fn tokenize<'i>( + &self, + input: &'i str, + ) -> Result, &'i str)>, ParsingError> { + let mut tokens: Vec<(Spanned, &'i str)> = Vec::new(); let mut ordered_constant_tokens = self.grammar.constant_tokens().iter().collect::>(); ordered_constant_tokens.sort_by_key(|token| token.len()); let mut remaining_input = input.trim_start(); + let mut offset = input.len() - remaining_input.len(); + let (initial_new_lines, initial_newline_offset) = utils::count_new_lines(&input[..offset]); + let mut line = initial_new_lines + 1; + let mut last_newline_offset = initial_newline_offset.unwrap_or(0); + let mut column = utils::count_col_position(&input[last_newline_offset..offset]); while !remaining_input.is_empty() { let mut matching_token = None; let mut matching_slice = ""; @@ -139,6 +141,7 @@ impl Parser { break; } } + for (regex_token, regex) in self.grammar.regular_expressions() { if let Some(match_info) = regex.find(remaining_input) { if match_info.len() > matching_slice.len() { @@ -149,28 +152,61 @@ impl Parser { } if matching_token.is_none() { + let span = Span { offset, len: 1, line, column }; return Err(ParsingError::UnknownToken { token: format_smolstr!("{}", remaining_input.chars().next().unwrap()), + span, }); } - tokens.push((matching_token.unwrap(), matching_slice)); - remaining_input = remaining_input[matching_slice.len()..].trim(); + let token = Spanned::new(matching_token.unwrap(), Span { + offset, + len: matching_slice.len(), + line, + column, + }); + + let (slice_lines, slice_newline_offset) = utils::count_new_lines(matching_slice); + line += slice_lines; + + if let Some(slice_newline_offset) = slice_newline_offset { + last_newline_offset = offset + slice_newline_offset + } + + tokens.push((token, matching_slice)); + remaining_input = remaining_input[matching_slice.len()..].trim_start(); + + // add back to the offset the whitespace that was trimmed + let old_offset = offset; + offset = input.len() - remaining_input.len(); + let whitespace = &input[old_offset..offset]; + let (whitespace_lines, whitespace_newline_offset) = utils::count_new_lines(whitespace); + line += whitespace_lines; + + if let Some(whitespace_newline_offset) = whitespace_newline_offset { + last_newline_offset = old_offset + whitespace_newline_offset; + } + // skip the newline character + column = utils::count_col_position(&input[last_newline_offset..offset]); } - tokens.push((Token::Eof, "\0")); + let eof = Spanned::new(Token::Eof, Span { offset, len: 0, line, column }); + tokens.push((eof, "\0")); Ok(tokens) } /// Parses a tokenized input. - pub fn parse<'i>(&self, tokens: Vec<(Token, &'i str)>) -> Result, ParsingError> { + pub fn parse<'i>( + &self, + tokens: Vec<(Spanned, &'i str)>, + ) -> Result, ParsingError> { self.parse_and_trace_internal(tokens, false).map(|(_, tree)| tree) } /// Traces the parsing of a tokenized input. pub fn trace<'i>( &self, - tokens: Vec<(Token, &'i str)>, + tokens: Vec<(Spanned, &'i str)>, ) -> Result<(Trace<'i>, Tree<'i>), ParsingError> { self.parse_and_trace_internal(tokens, true) } @@ -264,7 +300,7 @@ impl Parser { /// Internal parsing logic. fn parse_and_trace_internal<'i>( &self, - mut tokens: Vec<(Token, &'i str)>, + mut tokens: Vec<(Spanned, &'i str)>, traced: bool, ) -> Result<(Trace<'i>, Tree<'i>), ParsingError> { let mut state_stack = vec![0]; @@ -279,7 +315,8 @@ impl Parser { let (mut current_token, mut current_slice) = remaining_tokens.pop().unwrap(); loop { let current_state = *state_stack.last().unwrap(); - let action_to_take = match self.action_table()[current_state].get(¤t_token) { + let action_to_take = match self.action_table()[current_state].get(current_token.value()) + { Some(actions) => { assert_eq!(actions.len(), 1); *actions.iter().next().unwrap() @@ -290,10 +327,14 @@ impl Parser { expected.push(token.clone()); } - return Err(if current_token == Token::Eof { - ParsingError::UnexpectedEof { expected } + return Err(if *current_token == Token::Eof { + ParsingError::UnexpectedEof { expected, span: current_token.span().clone() } } else { - ParsingError::UnexpectedToken { token: current_slice.into(), expected } + ParsingError::UnexpectedToken { + token: current_slice.into(), + expected, + span: current_token.span().clone(), + } }); }, }; @@ -320,11 +361,9 @@ impl Parser { return Ok((trace, parse_tree)); }, Action::Shift { next_state } => { + let (token, span) = current_token.clone().into_components(); state_stack.push(next_state); - tree_stack.push(Tree::Terminal { - token: current_token.clone(), - slice: current_slice, - }); + tree_stack.push(Tree::Terminal { token, span, slice: current_slice }); (current_token, current_slice) = remaining_tokens.pop().unwrap(); }, Action::Reduce { rule_index } => { diff --git a/src/span.rs b/src/span.rs new file mode 100644 index 0000000..87fe028 --- /dev/null +++ b/src/span.rs @@ -0,0 +1,78 @@ +use crate::prelude::*; + +/// Position information of a token in the input string. +#[cfg_attr(feature = "serde", derive(Serialize))] +#[cfg_attr(feature = "serde", serde(crate = "serde_renamed"))] +#[derive(Clone, Debug, PartialEq)] +pub struct Span { + /// Byte offset of the span in the input string. + pub offset: usize, + /// Length of the span. + pub len: usize, + /// Line number of the span in the input string. + pub line: usize, + /// Column number of the span in the input string. + pub column: usize, +} + +/// Wrapper over any type with span information. +#[cfg(not(feature = "serde"))] +#[derive(Clone, Debug)] +pub struct Spanned { + /// The value of the span. + pub value: T, + /// The span information. + span: Span, +} +/// Wrapper over any type with span information. +#[cfg(feature = "serde")] +#[cfg_attr(feature = "serde", derive(Serialize))] +#[cfg_attr(feature = "serde", serde(crate = "serde_renamed"))] +#[derive(Clone, Debug)] +pub struct Spanned { + /// Span of the value. + value: T, + /// Span information. + span: Span, +} + + +impl< + #[cfg(not(feature = "serde"))] T: Debug + Clone, + #[cfg(feature = "serde")] T: Serialize + Debug + Clone, +> Spanned +{ + /// Creates a new Spanned value. + pub fn new(value: T, span: Span) -> Self { + Self { value, span } + } + /// Gets the span information. + pub fn span(&self) -> &Span { + &self.span + } + + /// Converts the Spanned value into a tuple of the value and the span. + pub fn into_components(self) -> (T, Span) { + (self.value, self.span) + } + /// Gets the value of the span. + pub fn value(&self) -> &T { + &self.value + } + /// Converts the Spanned value into the value. + pub fn into_value(self) -> T { + self.value + } +} + +impl< + #[cfg(not(feature = "serde"))] T: Debug + Clone, + #[cfg(feature = "serde")] T: Serialize + Debug + Clone, +> Deref for Spanned +{ + type Target = T; + + fn deref(&self) -> &Self::Target { + &self.value + } +} diff --git a/src/trace.rs b/src/trace.rs index d7da69a..4225a20 100644 --- a/src/trace.rs +++ b/src/trace.rs @@ -8,7 +8,7 @@ use crate::prelude::*; pub struct Step<'i> { pub(crate) state_stack: Vec, pub(crate) tree_stack: Vec>, - pub(crate) remaining_tokens: Vec, + pub(crate) remaining_tokens: Vec>, pub(crate) action_taken: Action, } @@ -24,7 +24,7 @@ impl<'i> Step<'i> { } /// Gets the remaining tokens during the step. - pub fn remaining_tokens(&self) -> &[Token] { + pub fn remaining_tokens(&self) -> &[Spanned] { &self.remaining_tokens } @@ -43,7 +43,7 @@ pub struct Trace<'i> { steps: Vec>, } -impl<'i> Trace<'i> { +impl Trace<'_> { /// Creates a new trace. pub fn new() -> Self { Self { steps: vec![] } @@ -64,7 +64,7 @@ impl<'i> Trace<'i> { } } -impl<'i> Trace<'i> { +impl Trace<'_> { /// Dumps the trace to stdout. pub fn dump(&self, grammar: &Grammar) { let mut pretty_trace_table = Table::new(); @@ -91,7 +91,7 @@ impl<'i> Trace<'i> { } }) .join(" "); - let remaining_input = step.remaining_tokens.iter().rev().join(" "); + let remaining_input = step.remaining_tokens.iter().rev().map(|t| t.value()).join(" "); let action_taken = match step.action_taken { Action::Shift { next_state } => { format!("Shift {}", next_state) diff --git a/src/tree.rs b/src/tree.rs index c2bee2c..f18fa44 100644 --- a/src/tree.rs +++ b/src/tree.rs @@ -13,6 +13,8 @@ pub enum Tree<'i> { Terminal { /// Matching token. token: Token, + /// Matching span. + span: Span, /// Matching slice. slice: &'i str, }, @@ -24,15 +26,14 @@ pub enum Tree<'i> { pattern: Vec>, }, } - -impl<'i> Tree<'i> { +impl Tree<'_> { /// Dumps the parse tree to stdout. pub fn dump(&self) { println!("{}", self); } } -impl<'i> Display for Tree<'i> { +impl Display for Tree<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn display_name_of(tree: &Tree) -> String { match tree { diff --git a/src/utils.rs b/src/utils.rs index f370c43..7413653 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -24,7 +24,7 @@ impl> MockColored for T { } } -/// Serialize a map of regex objects to a map of regex strings. +/// Serializes a map of regex objects to a map of regex strings. #[cfg(feature = "serde")] pub fn serialize_regex_map( map: &IndexMap, @@ -39,3 +39,24 @@ where } map_serializer.end() } + + +/// Counts the number of new lines in a slice and returns the offset after the last new line. +pub fn count_new_lines(slice: &str) -> (usize, Option) { + let mut offset_after_newline = None; + let mut count = 0; + for (offset, byte) in slice.bytes().enumerate() { + if byte == b'\n' { + offset_after_newline = Some(offset + 1); + count += 1; + } + } + (count, offset_after_newline) +} + +/// Counts column position of a char. +/// +/// The resulting column position is the 1 indexed utf-8 charater in the slice. +pub fn count_col_position(slice: &str) -> usize { + slice.chars().count() + 1 +} diff --git a/tests/grammar.rs b/tests/grammar.rs index a96fbe9..b0a065b 100644 --- a/tests/grammar.rs +++ b/tests/grammar.rs @@ -30,17 +30,32 @@ fn failing_to_parse_syntactically_incorrect_grammars() { #[test] fn raising_correct_error_when_parsing_unexpected_token_grammar() { let error = Grammar::parse(common::grammars::UNEXPECTED_TOKEN).unwrap_err(); - assert_eq!( - error.to_string(), - "unexpected token -> at line 1 column 6 \ - (expected one of symbol, constant token, regular expression token)" - ); + match error { + dotlr::GrammarError::UnexpectedToken { line, column, token, expected } => { + assert_eq!(line, 1); + assert_eq!(column, 6); + assert_eq!(token.as_str(), "->"); + assert_eq!(expected.iter().map(|token| token.as_str()).collect::>(), [ + "symbol", + "constant token", + "regular expression token" + ]); + }, + _ => unreachable!(), + } } #[test] fn raising_correct_error_when_parsing_invalid_regex_grammar() { let error = Grammar::parse(common::grammars::INVALID_REGEX).unwrap_err(); - assert_eq!(error.to_string(), "invalid regex /[1-9][0-9+/ at line 3 column 8"); + match error { + dotlr::GrammarError::InvalidRegex { line, column, regex } => { + assert_eq!(line, 3); + assert_eq!(column, 8); + assert_eq!(regex.as_str(), "/[1-9][0-9+/"); + }, + _ => unreachable!(), + } } diff --git a/tests/parsing.rs b/tests/parsing.rs index 0244a31..a46b566 100644 --- a/tests/parsing.rs +++ b/tests/parsing.rs @@ -13,7 +13,7 @@ fn raising_correct_error_when_encountering_unknown_token_during_parsing_calculat let parser = Parser::lr(grammar).unwrap(); let error = parser.tokenize("a").unwrap_err(); - assert_eq!(error.to_string(), "unknown token a"); + assert_eq!(error.to_string(), "unknown token a at 1:1"); } #[test] @@ -23,7 +23,7 @@ fn raising_correct_error_when_encountering_unexpected_token_during_parsing_calcu let tokens = parser.tokenize("1 + /").unwrap(); let error = parser.parse(tokens).unwrap_err(); - assert_eq!(error.to_string(), "unexpected token / (expected one of '(', %f)"); + assert_eq!(error.to_string(), "unexpected token / at 1:5 (expected one of '(', %f)"); } #[test] @@ -35,7 +35,7 @@ fn raising_correct_error_when_encountering_unexpected_eof_during_parsing_calcula let error = parser.parse(tokens).unwrap_err(); assert_eq!( error.to_string(), - "unexpected end of input (expected one of '^', '+', '-', '*', '/', ')')", + "unexpected end of input at 1:7 (expected one of '^', '+', '-', '*', '/', ')')", ); } diff --git a/tests/span.rs b/tests/span.rs new file mode 100644 index 0000000..1982466 --- /dev/null +++ b/tests/span.rs @@ -0,0 +1,90 @@ +mod common; + +use dotlr::{ + Grammar, + Parser, + Span, + Spanned, + Token, +}; + +/// Formats the expected and got tokens and spans into a more readable format. +#[allow(unused)] +fn fmt_expected<'i>(tokens: &[(Spanned, &'i str)], spans: &[Span]) -> String { + if tokens.len() != spans.len() { + panic!( + "Mismatch in the number of tokens and spans. Expected {} got {}", + spans.len(), + tokens.len() + ); + } + format!( + "[Expected -> Got] [Offset expected -> Offset Got] {{length}} \n{}", + tokens + .iter() + .zip(spans) + .map(|((expected_token, slice), got)| { + let span = expected_token.span(); + format!( + "{}:{} -> {}:{} ({} -> {}) [{}] {{{} -> {}}}", + span.line, + span.column, + got.line, + got.column, + span.offset, + got.offset, + slice, + span.len, + got.len + ) + }) + .collect::>() + .join("\n") + ) +} + +/// Checks if the spans of the tokens are equal to the expected spans. +#[allow(unused)] +fn check_spans<'i>(tokens: Vec<(Spanned, &'i str)>, spans: &[Span]) { + if tokens.len() != spans.len() { + panic!( + "Mismatch in the number of tokens and spans. Expected {} got {}", + spans.len(), + tokens.len() + ); + } + for (token, expected_span) in tokens.iter().zip(spans) { + let span = token.0.span(); + if *span != *expected_span { + panic!("{}", fmt_expected(&tokens, spans)); + } + } +} + + +#[test] +fn correctly_calculate_spans_multi_line() { + let grammar = Grammar::parse(common::grammars::CALCULATOR).unwrap(); + let parser = Parser::lalr(grammar).unwrap(); + // do not remove the spaces in the string + let str = " 11 + 221+3 ++20 + + +44 +5"; + let tokens = parser.tokenize(str).unwrap(); + + check_spans(tokens, &[ + Span { line: 1, column: 3, offset: 2, len: 2 }, + Span { line: 1, column: 6, offset: 5, len: 1 }, + Span { line: 1, column: 9, offset: 8, len: 3 }, + Span { line: 1, column: 12, offset: 11, len: 1 }, + Span { line: 1, column: 13, offset: 12, len: 1 }, + Span { line: 2, column: 1, offset: 14, len: 1 }, + Span { line: 2, column: 2, offset: 15, len: 2 }, + Span { line: 4, column: 5, offset: 23, len: 1 }, + Span { line: 4, column: 6, offset: 24, len: 2 }, + Span { line: 4, column: 9, offset: 27, len: 1 }, + Span { line: 4, column: 10, offset: 28, len: 1 }, + Span { line: 4, column: 11, offset: 29, len: 0 }, + ]); +}