├── .gitignore ├── .editorconfig ├── .vscode └── settings.json ├── .prettierrc.json ├── pnpm-workspace.yaml ├── vitest.config.ts ├── .github └── workflows │ └── main.yml ├── package.json ├── LICENSE ├── CHANGELOG.md ├── tsconfig.json ├── README.md ├── tests └── index.test.ts ├── src └── index.ts └── pnpm-lock.yaml /.gitignore: -------------------------------------------------------------------------------- 1 | /.idea 2 | /node_modules 3 | /coverage 4 | /dist -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | [*.{md,markdown}] 2 | trim_trailing_whitespace = false -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "markdown.extension.toc.levels": "1..3" 3 | } -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "trailingComma": "es5", 3 | "tabWidth": 2, 4 | "semi": false, 5 | "singleQuote": true 6 | } -------------------------------------------------------------------------------- /pnpm-workspace.yaml: -------------------------------------------------------------------------------- 1 | overrides: 2 | tough-cookie@<4.1.3: '>=4.1.3' 3 | esbuild@<=0.24.2: '>=0.25.0' 4 | form-data@<2.5.4: '>=2.5.4' 5 | -------------------------------------------------------------------------------- /vitest.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vitest/config' 2 | 3 | export default defineConfig({ 4 | test: { 5 | coverage: { 6 | reporter: ['lcov'], 7 | }, 8 | }, 9 | }) -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | branches: 4 | - develop 5 | - master 6 | 7 | name: Test Coveralls 8 | 9 | jobs: 10 | 11 | build: 12 | name: Build 13 | runs-on: ubuntu-latest 14 | steps: 15 | - uses: pnpm/action-setup@v2 16 | with: 17 | version: 8.6.1 18 | 19 | - uses: actions/checkout@v1 20 | 21 | - name: Use Node.js 18.x 22 | uses: actions/setup-node@v1 23 | with: 24 | node-version: 18.x 25 | 26 | - name: pnpm install, make test-coverage 27 | run: | 28 | pnpm install 29 | pnpm run test 30 | 31 | - name: Coveralls 32 | uses: coverallsapp/github-action@master 33 | with: 34 | github-token: ${{ secrets.GITHUB_TOKEN }} 35 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "markov-strings", 3 | "version": "3.0.2", 4 | "description": "A Markov string generator", 5 | "main": "dist/index.js", 6 | "files": [ 7 | "dist" 8 | ], 9 | "scripts": { 10 | "dev": "tsc --watch", 11 | "build": "tsc", 12 | "test": "vitest run --coverage" 13 | }, 14 | "engines": { 15 | "node": ">=8.0.0" 16 | }, 17 | "repository": { 18 | "type": "git", 19 | "url": "https://github.com/scambier/markov-strings" 20 | }, 21 | "keywords": [ 22 | "markov", 23 | "string", 24 | "chain", 25 | "procedural", 26 | "generation", 27 | "text" 28 | ], 29 | "author": "scambier", 30 | "license": "MIT", 31 | "devDependencies": { 32 | "@types/lodash": "^4.17.21", 33 | "@types/lodash-es": "^4.17.12", 34 | "@types/node": "^22.19.2", 35 | "@vitest/coverage-v8": "2.1.8", 36 | "coveralls-next": "^6.0.1", 37 | "prettier": "^3.7.4", 38 | "typescript": "^5.9.3", 39 | "vitest": "^2.1.9" 40 | }, 41 | "dependencies": { 42 | "lodash-es": "^4.17.21" 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Simon Cambier 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## 3.0.0 - BREAKING CHANGES 4 | 5 | - Refactoring to facilitate iterative construction of the corpus (multiple `.addData()` instead of a one-time `buildCorpus()`), and export/import of corpus internal data. 6 | 7 | ## 2.1.0 8 | 9 | - Add an optionnal `prng` parameter at generation to use a specific Pseudo Random Number Generator 10 | 11 | ## 2.0.4 12 | 13 | - Dependencies update 14 | 15 | ## 2.0.0 16 | 17 | - **Refactoring with breaking changes** 18 | - The constructor and generator take two different options objects 19 | - Most of generator options are gone, except `filter` and `maxTries` 20 | - Tests have been rewritten with jest, in TypeScript 21 | 22 | ## 1.5.0 23 | 24 | - Code rewritten in TypeScript. You can now `import MarkovGenerator from 'markov-strings'` 25 | 26 | ## 1.4.0 27 | 28 | - New `filter()` method, thanks @flpvsk 29 | 30 | ## 1.3.4 - 1.3.5 31 | 32 | - Dependencies update 33 | 34 | ## 1.3.3 35 | 36 | - Updated README. Version bump for npm 37 | 38 | ## 1.3.2 39 | 40 | - Fixed an infinite loop bug 41 | - Performance improvement 42 | 43 | ## 1.3.1 44 | 45 | - Updated README example 46 | - Removed a useless line 47 | 48 | ## 1.3.0 49 | 50 | - New feature: the generator now accepts arrays of objects, and tells the user which objects were used to build a sentence 51 | - Fixed all unit tests 52 | - Added a changelog 53 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": true, 3 | "include": [ 4 | "src/**/*" 5 | ], 6 | "compilerOptions": { 7 | /* Basic Options */ 8 | "target": "es6", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017','ES2018' or 'ESNEXT'. */ 9 | "module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */ 10 | // "lib": [], /* Specify library files to be included in the compilation. */ 11 | // "allowJs": true, /* Allow javascript files to be compiled. */ 12 | // "checkJs": true, /* Report errors in .js files. */ 13 | // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ 14 | "declaration": true, /* Generates corresponding '.d.ts' file. */ 15 | "declarationDir": "./dist", 16 | // "sourceMap": true, /* Generates corresponding '.map' file. */ 17 | // "outFile": "./", /* Concatenate and emit output to single file. */ 18 | "outDir": "./dist", /* Redirect output structure to the directory. */ 19 | // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ 20 | // "removeComments": true, /* Do not emit comments to output. */ 21 | // "noEmit": true, /* Do not emit outputs. */ 22 | // "importHelpers": true, /* Import emit helpers from 'tslib'. */ 23 | // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ 24 | // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ 25 | 26 | /* Strict Type-Checking Options */ 27 | "strict": true, /* Enable all strict type-checking options. */ 28 | // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ 29 | // "strictNullChecks": true, /* Enable strict null checks. */ 30 | // "strictFunctionTypes": true, /* Enable strict checking of function types. */ 31 | // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ 32 | // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ 33 | // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ 34 | 35 | /* Additional Checks */ 36 | // "noUnusedLocals": true, /* Report errors on unused locals. */ 37 | // "noUnusedParameters": true, /* Report errors on unused parameters. */ 38 | // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ 39 | // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ 40 | 41 | /* Module Resolution Options */ 42 | // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ 43 | // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ 44 | // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ 45 | // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ 46 | // "typeRoots": [], /* List of folders to include type definitions from. */ 47 | // "types": [], /* Type declaration files to be included in compilation. */ 48 | // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ 49 | "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ 50 | // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ 51 | 52 | /* Source Map Options */ 53 | // "sourceRoot": "./", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ 54 | // "mapRoot": "./", /* Specify the location where debugger should locate map files instead of generated locations. */ 55 | // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ 56 | // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ 57 | 58 | /* Experimental Options */ 59 | // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ 60 | // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ 61 | } 62 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.org/scambier/markov-strings.svg?branch=master)](https://travis-ci.org/scambier/markov-strings) 2 | [![Coverage Status](https://coveralls.io/repos/github/scambier/markov-strings/badge.svg?branch=master)](https://coveralls.io/github/scambier/markov-strings?branch=master) 3 | [![npm version](https://badge.fury.io/js/markov-strings.svg)](https://badge.fury.io/js/markov-strings) 4 | 5 | 6 | --- 7 | ! This is the readme for markov-strings **3.x.x.** - The docs for the older **2.x.x** are [here](https://github.com/scambier/markov-strings/tree/v2) ! 8 | 9 | --- 10 | 11 | # Markov-strings 12 | 13 | A simplistic Markov chain text generator. 14 | Give it an array of strings, and it will output a randomly generated string. 15 | 16 | A rust port of this library is available [here](https://github.com/scambier/markov-strings-rust). 17 | 18 | This module was created for the Mastodon bot [@BelgicaNews](https://botsin.space/@BelgicaNews). 19 | 20 | - [Markov-strings](#markov-strings) 21 | - [Prerequisites](#prerequisites) 22 | - [Installing](#installing) 23 | - [Usage](#usage) 24 | - [API](#api) 25 | - [`new Markov([options])`](#new-markovoptions) 26 | - [`.addData(data)`](#adddatadata) 27 | - [`.generate([options])`](#generateoptions) 28 | - [`.export()` and `.import(data)`](#export-and-importdata) 29 | - [Unit tests](#unit-tests) 30 | - [Changelog](#changelog) 31 | - [Running the tests](#running-the-tests) 32 | 33 | ## Prerequisites 34 | 35 | Built and tested with NodeJS 18 36 | 37 | ## Installing 38 | 39 | `npm install --save markov-strings` 40 | 41 | ## Usage 42 | 43 | ```js 44 | import Markov from 'markov-strings' 45 | // Not recommended: you can use `require()` if needed, instead of `import` 46 | // const Markov = require('markov-strings').default 47 | 48 | // Build the Markov generator 49 | const markov = new Markov({ stateSize: 2 }) 50 | 51 | // Add data for the generator 52 | const data = [/* insert a few hundreds/thousands sentences here */] 53 | markov.addData(data) 54 | 55 | const options = { 56 | maxTries: 20, // Give up if I don't have a sentence after 20 tries (default is 10) 57 | 58 | // If you want to get seeded results, you can provide an external PRNG. 59 | prng: Math.random, // Default value if left empty 60 | 61 | // You'll often need to manually filter raw results to get something that fits your needs. 62 | filter: (result) => { 63 | return result.string.split(' ').length >= 5 && // At least 5 words 64 | result.string.endsWith('.') // End sentences with a dot. 65 | } 66 | } 67 | 68 | // Generate a sentence 69 | const result = markov.generate(options) 70 | console.log(result) 71 | /* 72 | { 73 | string: 'lorem ipsum dolor sit amet etc.', 74 | score: 42, 75 | tries: 5, 76 | refs: [ an array of objects ] 77 | } 78 | */ 79 | ``` 80 | 81 | Markov-strings is built in TypeScript, and exports several types to help you. Take a look at [the source](https://github.com/scambier/markov-strings/blob/master/src/index.ts) to see how it works. 82 | 83 | ## API 84 | 85 | ### `new Markov([options])` 86 | 87 | Create a generator instance. 88 | 89 | #### options 90 | 91 | ```js 92 | { 93 | stateSize: number 94 | } 95 | ``` 96 | 97 | The `stateSize` is the number of words for each "link" of the generated sentence. `1` will output gibberish sentences without much sense. `2` is a sensible default for most cases. `3` and more can create good sentences if you have a corpus that allows it. 98 | 99 | ### `.addData(data)` 100 | 101 | To function correctly, the Markov generator needs its internal data to be correctly structured. `.addData(data)` allows you add raw data, that is automatically formatted to fit the internal structure. 102 | 103 | You can call `.addData(data)` as often as you need, **with new data each time (!)**. Multiple calls of `.addData()` with the same data is not recommended, because it will skew the random generation of results. 104 | 105 | #### data 106 | 107 | ```js 108 | string[] | Array<{ string: string }> 109 | ``` 110 | 111 | `data` is an array of strings (sentences), or an array of objects. If you wish to use objects, each one must have a `string` attribute. The bigger the array, the better and more varied the results. 112 | 113 | Examples: 114 | 115 | ```js 116 | [ 'lorem ipsum', 'dolor sit amet' ] 117 | ``` 118 | 119 | or 120 | 121 | ```js 122 | [ 123 | { string: 'lorem ipsum', attr: 'value' }, 124 | { string: 'dolor sit amet', attr: 'other value' } 125 | ] 126 | ``` 127 | 128 | The additionnal data passed with objects will be returned in the `refs` array of the generated sentence. 129 | 130 | ### `.generate([options])` 131 | 132 | Returns an object of type `MarkovResult`: 133 | 134 | ```ts 135 | { 136 | string: string, // The resulting sentence 137 | score: number, // A relative "score" based on the number of possible permutations. Higher is "better", but the actual value depends on your corpus 138 | refs: Array<{ string: string }>, // The array of references used to build the sentence 139 | tries: number // The number of tries it took to output this result 140 | } 141 | ``` 142 | 143 | The `refs` array will contain all objects that have been used to build the sentence. May be useful to fetch meta data or make stats. 144 | 145 | #### options 146 | 147 | ```ts 148 | { 149 | maxTries: number // The max number of tentatives before giving up (default is 10) 150 | prng: Math.random, // An external Pseudo Random Number Generator if you want to get seeded results 151 | filter: (result: MarkovResult) => boolean // A callback to filter results (see example above) 152 | } 153 | ``` 154 | 155 | ### `.export()` and `.import(data)` 156 | 157 | You can export and import the markov built corpus. The exported data is a serializable object, and must be deserialized before being re-imported. 158 | 159 | [Example use-case](https://github.com/scambier/markov-strings/issues/9) 160 | 161 | ## Running the tests 162 | 163 | `npm test` 164 | -------------------------------------------------------------------------------- /tests/index.test.ts: -------------------------------------------------------------------------------- 1 | import { map, some } from 'lodash-es' 2 | import Markov, { MarkovResult } from '../src' 3 | import { describe, expect, it, beforeEach, vi } from 'vitest' 4 | 5 | const data = [ 6 | 'Lorem ipsum dolor sit amet', 7 | 'Lorem ipsum duplicate start words', 8 | 'Consectetur adipiscing elit', 9 | 'Quisque tempor, erat vel lacinia imperdiet', 10 | 'Justo nisi fringilla dui', 11 | 'Egestas bibendum eros nisi ut lacus', 12 | "fringilla dui avait annoncé une rupture avec le erat vel: il n'en est rien…", 13 | 'Fusce tincidunt tempor, erat vel lacinia vel ex pharetra pretium lacinia imperdiet' 14 | ] 15 | 16 | describe('Markov class', () => { 17 | describe('Constructor', () => { 18 | 19 | it('should have a default stateSize', () => { 20 | const markov = new Markov() 21 | expect(markov.options.stateSize).toBe(2) 22 | }) 23 | 24 | it('should save a different stateSize', () => { 25 | const markov = new Markov({ stateSize: 3 }) 26 | expect(markov.options.stateSize).toBe(3) 27 | }) 28 | }) 29 | 30 | describe('Adding data', () => { 31 | it('should build synchronously', () => { 32 | const markov = new Markov() 33 | expect(markov.corpus).toEqual({}) 34 | markov.addData(data) 35 | expect(markov.corpus).not.toEqual({}) 36 | }) 37 | 38 | 39 | it('should throw an error if the data structure is invalid', () => { 40 | const markov = new Markov() 41 | expect(() => { 42 | // @ts-ignore 43 | markov.addData([{}]) 44 | }).toThrowError() 45 | }) 46 | 47 | it('should accept objects', () => { 48 | const markov = new Markov() 49 | markov.addData(data.map(o => ({ string: o }))) 50 | expect(markov.corpus).not.toEqual({}) 51 | }) 52 | 53 | }) 54 | 55 | describe('After adding data', () => { 56 | let markov: Markov 57 | beforeEach(() => { 58 | markov = new Markov() 59 | markov.addData(data) 60 | }) 61 | 62 | describe('The startWords array', () => { 63 | it('should contain the right values', () => { 64 | const start = markov.startWords 65 | expect(some(start, { words: 'Lorem ipsum' })).toBeTruthy() 66 | expect(some(start, { words: 'Consectetur adipiscing' })).toBeTruthy() 67 | expect(some(start, { words: 'Quisque tempor,' })).toBeTruthy() 68 | expect(some(start, { words: 'Justo nisi' })).toBeTruthy() 69 | expect(some(start, { words: 'Egestas bibendum' })).toBeTruthy() 70 | expect(some(start, { words: 'fringilla dui' })).toBeTruthy() 71 | expect(some(start, { words: 'Fusce tincidunt' })).toBeTruthy() 72 | }) 73 | 74 | it('should have the right length', () => { 75 | expect(markov.startWords).toHaveLength(7) 76 | }) 77 | }) 78 | 79 | describe('The endWords array', () => { 80 | it('should have the right length', () => { 81 | expect(markov.endWords).toHaveLength(7) 82 | }) 83 | 84 | it('should contain the right values', () => { 85 | const end = markov.endWords 86 | expect(some(end, { words: 'sit amet' })).toBeTruthy() 87 | expect(some(end, { words: 'start words' })).toBeTruthy() 88 | expect(some(end, { words: 'adipiscing elit' })).toBeTruthy() 89 | expect(some(end, { words: 'fringilla dui' })).toBeTruthy() 90 | expect(some(end, { words: 'ut lacus' })).toBeTruthy() 91 | expect(some(end, { words: 'est rien…' })).toBeTruthy() 92 | }) 93 | }) 94 | 95 | describe('The corpus itself', () => { 96 | it('should have the right values for the right keys', () => { 97 | const corpus = markov.corpus 98 | expect(some(corpus['Lorem ipsum'], { words: 'dolor sit' })).toBeTruthy() 99 | expect( 100 | some(corpus['Lorem ipsum'], { words: 'duplicate start' }) 101 | ).toBeTruthy() 102 | expect( 103 | some(corpus['tempor, erat'], { words: 'vel lacinia' }) 104 | ).toBeTruthy() 105 | }) 106 | }) 107 | 108 | describe('Export data', () => { 109 | it('should clone the original corpus values', () => { 110 | const exported = markov.export() 111 | 112 | expect(exported.corpus).toEqual(markov.corpus) 113 | expect(exported.corpus).not.toBe(markov.corpus) 114 | 115 | expect(exported.startWords).not.toBe(markov.startWords) 116 | expect(exported.startWords).toEqual(markov.startWords) 117 | 118 | expect(exported.endWords).not.toBe(markov.endWords) 119 | expect(exported.endWords).toEqual(markov.endWords) 120 | 121 | expect(exported.options).toEqual(markov.options) 122 | expect(exported.options).not.toBe(markov.options) 123 | }) 124 | }) 125 | 126 | describe('Import data', () => { 127 | it('should overwrite original values', () => { 128 | const exported = markov.export() 129 | const newMarkov = new Markov() 130 | 131 | // Make sure that the corpus is empty 132 | expect(newMarkov.corpus).toEqual({}) 133 | 134 | newMarkov.import(exported) 135 | 136 | expect(newMarkov.corpus).toEqual(exported.corpus) 137 | expect(newMarkov.corpus).not.toBe(exported.corpus) 138 | 139 | expect(newMarkov.startWords).toEqual(exported.startWords) 140 | expect(newMarkov.startWords).not.toBe(exported.startWords) 141 | 142 | expect(newMarkov.endWords).toEqual(exported.endWords) 143 | expect(newMarkov.endWords).not.toBe(exported.endWords) 144 | 145 | expect(newMarkov.options).toEqual(exported.options) 146 | expect(newMarkov.options).not.toBe(exported.options) 147 | }) 148 | }) 149 | }) 150 | 151 | describe('The sentence generator', () => { 152 | let markov: Markov 153 | beforeEach(() => { 154 | markov = new Markov() 155 | markov.addData(data) 156 | }) 157 | 158 | it('should throw an error if the corpus is not built', () => { 159 | markov = new Markov() 160 | expect(() => { 161 | markov.generate() 162 | }).toThrowError('Corpus is empty. There is either no data, or the data is not sufficient to create markov chains.') 163 | }) 164 | 165 | it('should return a result if under the tries limit', () => { 166 | expect.assertions(10) 167 | 168 | for (let i = 0; i < 10; i++) { 169 | const sentence = markov.generate({ maxTries: 20 }) 170 | expect(sentence.tries).toBeLessThanOrEqual(20) 171 | } 172 | }) 173 | 174 | it('should call the `filter` callback', () => { 175 | const filter = vi.fn(x => true) 176 | markov.generate({ filter }) 177 | expect(filter).toHaveBeenCalled() 178 | }) 179 | 180 | it('should throw an error after 10 tries, by default', () => { 181 | expect(() => { 182 | markov.generate({ 183 | filter(result: MarkovResult): boolean { 184 | return false 185 | } 186 | }) 187 | }).toThrowError('10') 188 | }) 189 | 190 | it('should end with a value from endWords', async () => { 191 | expect.assertions(10) 192 | 193 | for (let i = 0; i < 10; i++) { 194 | const result = markov.generate() 195 | const arr = result.string.split(' ') 196 | const end = arr.slice(arr.length - 2, arr.length) 197 | expect(map(markov.endWords, 'words')).toContain(end.join(' ')) 198 | } 199 | }) 200 | 201 | it(`should pass the result object to 'filter(result)'`, async () => { 202 | expect.assertions(6) 203 | 204 | const options = { 205 | minWords: 5, 206 | maxTries: 10, 207 | filter: (result: MarkovResult): boolean => { 208 | expect(Object.keys(result)).toHaveLength(4) 209 | expect(result).toHaveProperty('string') 210 | expect(result).toHaveProperty('score') 211 | expect(result).toHaveProperty('refs') 212 | expect(Array.isArray(result.refs)).toBeTruthy() 213 | expect(result).toHaveProperty('tries') 214 | return true 215 | } 216 | } 217 | markov.generate(options) 218 | }) 219 | 220 | }) 221 | }) 222 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import { 2 | cloneDeep, 3 | flatten, 4 | includes, 5 | isEmpty, 6 | isString, 7 | slice, 8 | some, 9 | uniqBy, 10 | assignIn 11 | } from 'lodash-es' 12 | 13 | export type MarkovInputData = { string: string }[] 14 | 15 | export type MarkovGenerateOptions = { 16 | maxTries?: number 17 | prng?: () => number 18 | filter?: (result: MarkovResult) => boolean 19 | } 20 | 21 | /** 22 | * Data to build the Markov instance 23 | */ 24 | export type MarkovConstructorOptions = { 25 | stateSize?: number 26 | } 27 | 28 | /** 29 | * While `stateSize` is optional as a constructor parameter, 30 | * it must exist as a member 31 | */ 32 | export type MarkovDataMembers = { 33 | stateSize: number 34 | } 35 | 36 | export type MarkovResult = { 37 | string: string 38 | score: number 39 | refs: MarkovInputData 40 | tries: number 41 | } 42 | 43 | export type MarkovFragment = { 44 | words: string 45 | refs: MarkovInputData 46 | } 47 | 48 | export type Corpus = { [key: string]: MarkovFragment[] } 49 | 50 | export type MarkovImportExport = { 51 | corpus: Corpus 52 | startWords: MarkovFragment[] 53 | endWords: MarkovFragment[] 54 | options: MarkovDataMembers 55 | } 56 | 57 | function sampleWithPRNG( 58 | array: T[], 59 | prng: () => number = Math.random 60 | ): T | undefined { 61 | const length = array == null ? 0 : array.length 62 | return length ? array[Math.floor(prng() * length)] : undefined 63 | } 64 | 65 | export default class Markov { 66 | public data: MarkovInputData 67 | public options: MarkovDataMembers 68 | 69 | public startWords: MarkovFragment[] = [] 70 | public endWords: MarkovFragment[] = [] 71 | public corpus: Corpus = {} 72 | 73 | private defaultOptions: MarkovDataMembers = { 74 | stateSize: 2, 75 | } 76 | 77 | /** 78 | * Creates an instance of Markov generator. 79 | * 80 | * @param {MarkovConstructorOptions} [options={}] 81 | * @memberof Markov 82 | */ 83 | constructor(options: MarkovConstructorOptions = {}) { 84 | this.data = [] 85 | 86 | // Save options 87 | this.options = this.defaultOptions 88 | assignIn(this.options, options) 89 | } 90 | 91 | /** 92 | * Imports a corpus. This overwrites existing data. 93 | * 94 | * @param data 95 | */ 96 | public import(data: MarkovImportExport): void { 97 | this.options = cloneDeep(data.options) 98 | this.corpus = cloneDeep(data.corpus) 99 | this.startWords = cloneDeep(data.startWords) 100 | this.endWords = cloneDeep(data.endWords) 101 | } 102 | 103 | /** 104 | * Exports structed data used to generate sentence. 105 | */ 106 | public export(): MarkovImportExport { 107 | return cloneDeep({ 108 | options: this.options, 109 | corpus: this.corpus, 110 | startWords: this.startWords, 111 | endWords: this.endWords, 112 | }) 113 | } 114 | 115 | public addData(rawData: MarkovInputData | string[]) { 116 | // Format data if necessary 117 | let input: MarkovInputData = [] 118 | if (isString(rawData[0])) { 119 | input = (rawData as string[]).map((s) => ({ string: s })) 120 | } else if (rawData[0].hasOwnProperty('string')) { 121 | input = rawData as MarkovInputData 122 | } else { 123 | throw new Error('Objects in your corpus must have a "string" property') 124 | } 125 | 126 | this.buildCorpus(input) 127 | 128 | this.data = this.data.concat(input) 129 | } 130 | 131 | /** 132 | * Builds the corpus. You must call this before generating sentences. 133 | * 134 | * @memberof Markov 135 | */ 136 | private buildCorpus(data: MarkovInputData): void { 137 | const options = this.options 138 | 139 | // Loop through all sentences 140 | data.forEach((item) => { 141 | const line = item.string 142 | const words = line.split(' ') 143 | const stateSize = options.stateSize // Default value of 2 is set in the constructor 144 | 145 | //#region Start words 146 | // "Start words" is the list of words that can start a generated chain. 147 | 148 | const start = slice(words, 0, stateSize).join(' ') 149 | const oldStartObj = this.startWords.find((o) => o.words === start) 150 | 151 | // If we already have identical startWords 152 | if (oldStartObj) { 153 | // If the current item is not present in the references, add it 154 | if (!includes(oldStartObj.refs, item)) { 155 | oldStartObj.refs.push(item) 156 | } 157 | } else { 158 | // Add the startWords (and reference) to the list 159 | this.startWords.push({ words: start, refs: [item] }) 160 | } 161 | 162 | //#endregion Start words 163 | 164 | //#region End words 165 | // "End words" is the list of words that can end a generated chain. 166 | 167 | const end = slice(words, words.length - stateSize, words.length).join(' ') 168 | const oldEndObj = this.endWords.find((o) => o.words === end) 169 | if (oldEndObj) { 170 | if (!includes(oldEndObj.refs, item)) { 171 | oldEndObj.refs.push(item) 172 | } 173 | } else { 174 | this.endWords.push({ words: end, refs: [item] }) 175 | } 176 | 177 | //#endregion End words 178 | 179 | //#region Corpus generation 180 | 181 | // We loop through all words in the sentence to build "blocks" of `stateSize` 182 | // e.g. for a stateSize of 2, "lorem ipsum dolor sit amet" will have the following blocks: 183 | // "lorem ipsum", "ipsum dolor", "dolor sit", and "sit amet" 184 | for (let i = 0; i < words.length - 1; i++) { 185 | const curr = slice(words, i, i + stateSize).join(' ') 186 | const next = slice(words, i + stateSize, i + stateSize * 2).join(' ') 187 | if (!next || next.split(' ').length !== options.stateSize) { 188 | continue 189 | } 190 | 191 | // Check if the corpus already has a corresponding "curr" block 192 | if (this.corpus.hasOwnProperty(curr)) { 193 | const oldObj = this.corpus[curr].find((o) => o.words === next) 194 | if (oldObj) { 195 | // If the corpus already has the chain "curr -> next", 196 | // just add the current reference for this block 197 | oldObj.refs.push(item) 198 | } else { 199 | // Add the new "next" block in the list of possible paths for "curr" 200 | this.corpus[curr].push({ words: next, refs: [item] }) 201 | } 202 | } else { 203 | // Add the "curr" block and link it with the "next" one 204 | this.corpus[curr] = [{ words: next, refs: [item] }] 205 | } 206 | } 207 | 208 | //#endregion Corpus generation 209 | }) 210 | } 211 | 212 | /** 213 | * Generates a result, that contains a string and its references 214 | * 215 | * @param {MarkovGenerateOptions} [options={}] 216 | * @returns {MarkovResult} 217 | * @memberof Markov 218 | */ 219 | public generate(options: MarkovGenerateOptions = {}): MarkovResult { 220 | if (isEmpty(this.corpus)) { 221 | throw new Error( 222 | 'Corpus is empty. There is either no data, or the data is not sufficient to create markov chains.' 223 | ) 224 | } 225 | 226 | const corpus = cloneDeep(this.corpus) 227 | const maxTries = options.maxTries ? options.maxTries : 10 228 | const prng = options.prng ? options.prng : Math.random 229 | 230 | let tries: number 231 | 232 | // We loop through fragments to create a complete sentence 233 | for (tries = 1; tries <= maxTries; tries++) { 234 | let ended = false 235 | 236 | // Create an array of MarkovCorpusItems 237 | // The first item is a random startWords element 238 | const arr = [sampleWithPRNG(this.startWords, prng)!] 239 | 240 | let score = 0 241 | 242 | // loop to build a complete sentence 243 | for (let innerTries = 0; innerTries < maxTries; innerTries++) { 244 | const block = arr[arr.length - 1] // last value in array 245 | const state = sampleWithPRNG(corpus[block.words], prng) // Find a following item in the corpus 246 | 247 | // If a state cannot be found, the sentence can't be completed 248 | if (!state) { 249 | break 250 | } 251 | 252 | // add new state to list 253 | arr.push(state) 254 | 255 | // increment score 256 | score += corpus[block.words].length - 1 // increment score 257 | 258 | // is sentence finished? 259 | if (some(this.endWords, { words: state.words })) { 260 | ended = true 261 | break 262 | } 263 | } 264 | 265 | const sentence = arr 266 | .map((o) => o.words) 267 | .join(' ') 268 | .trim() 269 | 270 | const result = { 271 | string: sentence, 272 | score, 273 | refs: uniqBy(flatten(arr.map((o) => o.refs)), 'string'), 274 | tries, 275 | } 276 | 277 | // sentence is not ended or incorrect 278 | if ( 279 | !ended || 280 | (typeof options.filter === 'function' && !options.filter(result)) 281 | ) { 282 | continue 283 | } 284 | 285 | return result 286 | } 287 | throw new Error( 288 | `Failed to build a sentence after ${tries - 1 289 | } tries. Possible solutions: try a less restrictive filter(), give more raw data to the corpus builder, or increase the number of maximum tries.` 290 | ) 291 | } 292 | } 293 | -------------------------------------------------------------------------------- /pnpm-lock.yaml: -------------------------------------------------------------------------------- 1 | lockfileVersion: '9.0' 2 | 3 | settings: 4 | autoInstallPeers: true 5 | excludeLinksFromLockfile: false 6 | 7 | overrides: 8 | tough-cookie@<4.1.3: '>=4.1.3' 9 | esbuild@<=0.24.2: '>=0.25.0' 10 | form-data@<2.5.4: '>=2.5.4' 11 | 12 | importers: 13 | 14 | .: 15 | dependencies: 16 | lodash-es: 17 | specifier: ^4.17.21 18 | version: 4.17.21 19 | devDependencies: 20 | '@types/lodash': 21 | specifier: ^4.17.21 22 | version: 4.17.21 23 | '@types/lodash-es': 24 | specifier: ^4.17.12 25 | version: 4.17.12 26 | '@types/node': 27 | specifier: ^22.19.2 28 | version: 22.19.2 29 | '@vitest/coverage-v8': 30 | specifier: 2.1.8 31 | version: 2.1.8(vitest@2.1.9(@types/node@22.19.2)) 32 | coveralls-next: 33 | specifier: ^6.0.1 34 | version: 6.0.1 35 | prettier: 36 | specifier: ^3.7.4 37 | version: 3.7.4 38 | typescript: 39 | specifier: ^5.9.3 40 | version: 5.9.3 41 | vitest: 42 | specifier: ^2.1.9 43 | version: 2.1.9(@types/node@22.19.2) 44 | 45 | packages: 46 | 47 | '@ampproject/remapping@2.3.0': 48 | resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} 49 | engines: {node: '>=6.0.0'} 50 | 51 | '@babel/helper-string-parser@7.27.1': 52 | resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} 53 | engines: {node: '>=6.9.0'} 54 | 55 | '@babel/helper-validator-identifier@7.28.5': 56 | resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==} 57 | engines: {node: '>=6.9.0'} 58 | 59 | '@babel/parser@7.28.5': 60 | resolution: {integrity: sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==} 61 | engines: {node: '>=6.0.0'} 62 | hasBin: true 63 | 64 | '@babel/types@7.28.5': 65 | resolution: {integrity: sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==} 66 | engines: {node: '>=6.9.0'} 67 | 68 | '@bcoe/v8-coverage@0.2.3': 69 | resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} 70 | 71 | '@esbuild/aix-ppc64@0.25.12': 72 | resolution: {integrity: sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==} 73 | engines: {node: '>=18'} 74 | cpu: [ppc64] 75 | os: [aix] 76 | 77 | '@esbuild/android-arm64@0.25.12': 78 | resolution: {integrity: sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==} 79 | engines: {node: '>=18'} 80 | cpu: [arm64] 81 | os: [android] 82 | 83 | '@esbuild/android-arm@0.25.12': 84 | resolution: {integrity: sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==} 85 | engines: {node: '>=18'} 86 | cpu: [arm] 87 | os: [android] 88 | 89 | '@esbuild/android-x64@0.25.12': 90 | resolution: {integrity: sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==} 91 | engines: {node: '>=18'} 92 | cpu: [x64] 93 | os: [android] 94 | 95 | '@esbuild/darwin-arm64@0.25.12': 96 | resolution: {integrity: sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==} 97 | engines: {node: '>=18'} 98 | cpu: [arm64] 99 | os: [darwin] 100 | 101 | '@esbuild/darwin-x64@0.25.12': 102 | resolution: {integrity: sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==} 103 | engines: {node: '>=18'} 104 | cpu: [x64] 105 | os: [darwin] 106 | 107 | '@esbuild/freebsd-arm64@0.25.12': 108 | resolution: {integrity: sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==} 109 | engines: {node: '>=18'} 110 | cpu: [arm64] 111 | os: [freebsd] 112 | 113 | '@esbuild/freebsd-x64@0.25.12': 114 | resolution: {integrity: sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==} 115 | engines: {node: '>=18'} 116 | cpu: [x64] 117 | os: [freebsd] 118 | 119 | '@esbuild/linux-arm64@0.25.12': 120 | resolution: {integrity: sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==} 121 | engines: {node: '>=18'} 122 | cpu: [arm64] 123 | os: [linux] 124 | 125 | '@esbuild/linux-arm@0.25.12': 126 | resolution: {integrity: sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==} 127 | engines: {node: '>=18'} 128 | cpu: [arm] 129 | os: [linux] 130 | 131 | '@esbuild/linux-ia32@0.25.12': 132 | resolution: {integrity: sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==} 133 | engines: {node: '>=18'} 134 | cpu: [ia32] 135 | os: [linux] 136 | 137 | '@esbuild/linux-loong64@0.25.12': 138 | resolution: {integrity: sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==} 139 | engines: {node: '>=18'} 140 | cpu: [loong64] 141 | os: [linux] 142 | 143 | '@esbuild/linux-mips64el@0.25.12': 144 | resolution: {integrity: sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==} 145 | engines: {node: '>=18'} 146 | cpu: [mips64el] 147 | os: [linux] 148 | 149 | '@esbuild/linux-ppc64@0.25.12': 150 | resolution: {integrity: sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==} 151 | engines: {node: '>=18'} 152 | cpu: [ppc64] 153 | os: [linux] 154 | 155 | '@esbuild/linux-riscv64@0.25.12': 156 | resolution: {integrity: sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==} 157 | engines: {node: '>=18'} 158 | cpu: [riscv64] 159 | os: [linux] 160 | 161 | '@esbuild/linux-s390x@0.25.12': 162 | resolution: {integrity: sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==} 163 | engines: {node: '>=18'} 164 | cpu: [s390x] 165 | os: [linux] 166 | 167 | '@esbuild/linux-x64@0.25.12': 168 | resolution: {integrity: sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==} 169 | engines: {node: '>=18'} 170 | cpu: [x64] 171 | os: [linux] 172 | 173 | '@esbuild/netbsd-arm64@0.25.12': 174 | resolution: {integrity: sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==} 175 | engines: {node: '>=18'} 176 | cpu: [arm64] 177 | os: [netbsd] 178 | 179 | '@esbuild/netbsd-x64@0.25.12': 180 | resolution: {integrity: sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==} 181 | engines: {node: '>=18'} 182 | cpu: [x64] 183 | os: [netbsd] 184 | 185 | '@esbuild/openbsd-arm64@0.25.12': 186 | resolution: {integrity: sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==} 187 | engines: {node: '>=18'} 188 | cpu: [arm64] 189 | os: [openbsd] 190 | 191 | '@esbuild/openbsd-x64@0.25.12': 192 | resolution: {integrity: sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==} 193 | engines: {node: '>=18'} 194 | cpu: [x64] 195 | os: [openbsd] 196 | 197 | '@esbuild/openharmony-arm64@0.25.12': 198 | resolution: {integrity: sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==} 199 | engines: {node: '>=18'} 200 | cpu: [arm64] 201 | os: [openharmony] 202 | 203 | '@esbuild/sunos-x64@0.25.12': 204 | resolution: {integrity: sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==} 205 | engines: {node: '>=18'} 206 | cpu: [x64] 207 | os: [sunos] 208 | 209 | '@esbuild/win32-arm64@0.25.12': 210 | resolution: {integrity: sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==} 211 | engines: {node: '>=18'} 212 | cpu: [arm64] 213 | os: [win32] 214 | 215 | '@esbuild/win32-ia32@0.25.12': 216 | resolution: {integrity: sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==} 217 | engines: {node: '>=18'} 218 | cpu: [ia32] 219 | os: [win32] 220 | 221 | '@esbuild/win32-x64@0.25.12': 222 | resolution: {integrity: sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==} 223 | engines: {node: '>=18'} 224 | cpu: [x64] 225 | os: [win32] 226 | 227 | '@isaacs/cliui@8.0.2': 228 | resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} 229 | engines: {node: '>=12'} 230 | 231 | '@istanbuljs/schema@0.1.3': 232 | resolution: {integrity: sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==} 233 | engines: {node: '>=8'} 234 | 235 | '@jridgewell/gen-mapping@0.3.13': 236 | resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} 237 | 238 | '@jridgewell/resolve-uri@3.1.2': 239 | resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} 240 | engines: {node: '>=6.0.0'} 241 | 242 | '@jridgewell/sourcemap-codec@1.5.5': 243 | resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} 244 | 245 | '@jridgewell/trace-mapping@0.3.31': 246 | resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} 247 | 248 | '@pkgjs/parseargs@0.11.0': 249 | resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} 250 | engines: {node: '>=14'} 251 | 252 | '@rollup/rollup-android-arm-eabi@4.53.3': 253 | resolution: {integrity: sha512-mRSi+4cBjrRLoaal2PnqH82Wqyb+d3HsPUN/W+WslCXsZsyHa9ZeQQX/pQsZaVIWDkPcpV6jJ+3KLbTbgnwv8w==} 254 | cpu: [arm] 255 | os: [android] 256 | 257 | '@rollup/rollup-android-arm64@4.53.3': 258 | resolution: {integrity: sha512-CbDGaMpdE9sh7sCmTrTUyllhrg65t6SwhjlMJsLr+J8YjFuPmCEjbBSx4Z/e4SmDyH3aB5hGaJUP2ltV/vcs4w==} 259 | cpu: [arm64] 260 | os: [android] 261 | 262 | '@rollup/rollup-darwin-arm64@4.53.3': 263 | resolution: {integrity: sha512-Nr7SlQeqIBpOV6BHHGZgYBuSdanCXuw09hon14MGOLGmXAFYjx1wNvquVPmpZnl0tLjg25dEdr4IQ6GgyToCUA==} 264 | cpu: [arm64] 265 | os: [darwin] 266 | 267 | '@rollup/rollup-darwin-x64@4.53.3': 268 | resolution: {integrity: sha512-DZ8N4CSNfl965CmPktJ8oBnfYr3F8dTTNBQkRlffnUarJ2ohudQD17sZBa097J8xhQ26AwhHJ5mvUyQW8ddTsQ==} 269 | cpu: [x64] 270 | os: [darwin] 271 | 272 | '@rollup/rollup-freebsd-arm64@4.53.3': 273 | resolution: {integrity: sha512-yMTrCrK92aGyi7GuDNtGn2sNW+Gdb4vErx4t3Gv/Tr+1zRb8ax4z8GWVRfr3Jw8zJWvpGHNpss3vVlbF58DZ4w==} 274 | cpu: [arm64] 275 | os: [freebsd] 276 | 277 | '@rollup/rollup-freebsd-x64@4.53.3': 278 | resolution: {integrity: sha512-lMfF8X7QhdQzseM6XaX0vbno2m3hlyZFhwcndRMw8fbAGUGL3WFMBdK0hbUBIUYcEcMhVLr1SIamDeuLBnXS+Q==} 279 | cpu: [x64] 280 | os: [freebsd] 281 | 282 | '@rollup/rollup-linux-arm-gnueabihf@4.53.3': 283 | resolution: {integrity: sha512-k9oD15soC/Ln6d2Wv/JOFPzZXIAIFLp6B+i14KhxAfnq76ajt0EhYc5YPeX6W1xJkAdItcVT+JhKl1QZh44/qw==} 284 | cpu: [arm] 285 | os: [linux] 286 | 287 | '@rollup/rollup-linux-arm-musleabihf@4.53.3': 288 | resolution: {integrity: sha512-vTNlKq+N6CK/8UktsrFuc+/7NlEYVxgaEgRXVUVK258Z5ymho29skzW1sutgYjqNnquGwVUObAaxae8rZ6YMhg==} 289 | cpu: [arm] 290 | os: [linux] 291 | 292 | '@rollup/rollup-linux-arm64-gnu@4.53.3': 293 | resolution: {integrity: sha512-RGrFLWgMhSxRs/EWJMIFM1O5Mzuz3Xy3/mnxJp/5cVhZ2XoCAxJnmNsEyeMJtpK+wu0FJFWz+QF4mjCA7AUQ3w==} 294 | cpu: [arm64] 295 | os: [linux] 296 | 297 | '@rollup/rollup-linux-arm64-musl@4.53.3': 298 | resolution: {integrity: sha512-kASyvfBEWYPEwe0Qv4nfu6pNkITLTb32p4yTgzFCocHnJLAHs+9LjUu9ONIhvfT/5lv4YS5muBHyuV84epBo/A==} 299 | cpu: [arm64] 300 | os: [linux] 301 | 302 | '@rollup/rollup-linux-loong64-gnu@4.53.3': 303 | resolution: {integrity: sha512-JiuKcp2teLJwQ7vkJ95EwESWkNRFJD7TQgYmCnrPtlu50b4XvT5MOmurWNrCj3IFdyjBQ5p9vnrX4JM6I8OE7g==} 304 | cpu: [loong64] 305 | os: [linux] 306 | 307 | '@rollup/rollup-linux-ppc64-gnu@4.53.3': 308 | resolution: {integrity: sha512-EoGSa8nd6d3T7zLuqdojxC20oBfNT8nexBbB/rkxgKj5T5vhpAQKKnD+h3UkoMuTyXkP5jTjK/ccNRmQrPNDuw==} 309 | cpu: [ppc64] 310 | os: [linux] 311 | 312 | '@rollup/rollup-linux-riscv64-gnu@4.53.3': 313 | resolution: {integrity: sha512-4s+Wped2IHXHPnAEbIB0YWBv7SDohqxobiiPA1FIWZpX+w9o2i4LezzH/NkFUl8LRci/8udci6cLq+jJQlh+0g==} 314 | cpu: [riscv64] 315 | os: [linux] 316 | 317 | '@rollup/rollup-linux-riscv64-musl@4.53.3': 318 | resolution: {integrity: sha512-68k2g7+0vs2u9CxDt5ktXTngsxOQkSEV/xBbwlqYcUrAVh6P9EgMZvFsnHy4SEiUl46Xf0IObWVbMvPrr2gw8A==} 319 | cpu: [riscv64] 320 | os: [linux] 321 | 322 | '@rollup/rollup-linux-s390x-gnu@4.53.3': 323 | resolution: {integrity: sha512-VYsFMpULAz87ZW6BVYw3I6sWesGpsP9OPcyKe8ofdg9LHxSbRMd7zrVrr5xi/3kMZtpWL/wC+UIJWJYVX5uTKg==} 324 | cpu: [s390x] 325 | os: [linux] 326 | 327 | '@rollup/rollup-linux-x64-gnu@4.53.3': 328 | resolution: {integrity: sha512-3EhFi1FU6YL8HTUJZ51imGJWEX//ajQPfqWLI3BQq4TlvHy4X0MOr5q3D2Zof/ka0d5FNdPwZXm3Yyib/UEd+w==} 329 | cpu: [x64] 330 | os: [linux] 331 | 332 | '@rollup/rollup-linux-x64-musl@4.53.3': 333 | resolution: {integrity: sha512-eoROhjcc6HbZCJr+tvVT8X4fW3/5g/WkGvvmwz/88sDtSJzO7r/blvoBDgISDiCjDRZmHpwud7h+6Q9JxFwq1Q==} 334 | cpu: [x64] 335 | os: [linux] 336 | 337 | '@rollup/rollup-openharmony-arm64@4.53.3': 338 | resolution: {integrity: sha512-OueLAWgrNSPGAdUdIjSWXw+u/02BRTcnfw9PN41D2vq/JSEPnJnVuBgw18VkN8wcd4fjUs+jFHVM4t9+kBSNLw==} 339 | cpu: [arm64] 340 | os: [openharmony] 341 | 342 | '@rollup/rollup-win32-arm64-msvc@4.53.3': 343 | resolution: {integrity: sha512-GOFuKpsxR/whszbF/bzydebLiXIHSgsEUp6M0JI8dWvi+fFa1TD6YQa4aSZHtpmh2/uAlj/Dy+nmby3TJ3pkTw==} 344 | cpu: [arm64] 345 | os: [win32] 346 | 347 | '@rollup/rollup-win32-ia32-msvc@4.53.3': 348 | resolution: {integrity: sha512-iah+THLcBJdpfZ1TstDFbKNznlzoxa8fmnFYK4V67HvmuNYkVdAywJSoteUszvBQ9/HqN2+9AZghbajMsFT+oA==} 349 | cpu: [ia32] 350 | os: [win32] 351 | 352 | '@rollup/rollup-win32-x64-gnu@4.53.3': 353 | resolution: {integrity: sha512-J9QDiOIZlZLdcot5NXEepDkstocktoVjkaKUtqzgzpt2yWjGlbYiKyp05rWwk4nypbYUNoFAztEgixoLaSETkg==} 354 | cpu: [x64] 355 | os: [win32] 356 | 357 | '@rollup/rollup-win32-x64-msvc@4.53.3': 358 | resolution: {integrity: sha512-UhTd8u31dXadv0MopwGgNOBpUVROFKWVQgAg5N1ESyCz8AuBcMqm4AuTjrwgQKGDfoFuz02EuMRHQIw/frmYKQ==} 359 | cpu: [x64] 360 | os: [win32] 361 | 362 | '@types/estree@1.0.8': 363 | resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} 364 | 365 | '@types/lodash-es@4.17.12': 366 | resolution: {integrity: sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==} 367 | 368 | '@types/lodash@4.17.21': 369 | resolution: {integrity: sha512-FOvQ0YPD5NOfPgMzJihoT+Za5pdkDJWcbpuj1DjaKZIr/gxodQjY/uWEFlTNqW2ugXHUiL8lRQgw63dzKHZdeQ==} 370 | 371 | '@types/node@22.19.2': 372 | resolution: {integrity: sha512-LPM2G3Syo1GLzXLGJAKdqoU35XvrWzGJ21/7sgZTUpbkBaOasTj8tjwn6w+hCkqaa1TfJ/w67rJSwYItlJ2mYw==} 373 | 374 | '@vitest/coverage-v8@2.1.8': 375 | resolution: {integrity: sha512-2Y7BPlKH18mAZYAW1tYByudlCYrQyl5RGvnnDYJKW5tCiO5qg3KSAy3XAxcxKz900a0ZXxWtKrMuZLe3lKBpJw==} 376 | peerDependencies: 377 | '@vitest/browser': 2.1.8 378 | vitest: 2.1.8 379 | peerDependenciesMeta: 380 | '@vitest/browser': 381 | optional: true 382 | 383 | '@vitest/expect@2.1.9': 384 | resolution: {integrity: sha512-UJCIkTBenHeKT1TTlKMJWy1laZewsRIzYighyYiJKZreqtdxSos/S1t+ktRMQWu2CKqaarrkeszJx1cgC5tGZw==} 385 | 386 | '@vitest/mocker@2.1.9': 387 | resolution: {integrity: sha512-tVL6uJgoUdi6icpxmdrn5YNo3g3Dxv+IHJBr0GXHaEdTcw3F+cPKnsXFhli6nO+f/6SDKPHEK1UN+k+TQv0Ehg==} 388 | peerDependencies: 389 | msw: ^2.4.9 390 | vite: ^5.0.0 391 | peerDependenciesMeta: 392 | msw: 393 | optional: true 394 | vite: 395 | optional: true 396 | 397 | '@vitest/pretty-format@2.1.9': 398 | resolution: {integrity: sha512-KhRIdGV2U9HOUzxfiHmY8IFHTdqtOhIzCpd8WRdJiE7D/HUcZVD0EgQCVjm+Q9gkUXWgBvMmTtZgIG48wq7sOQ==} 399 | 400 | '@vitest/runner@2.1.9': 401 | resolution: {integrity: sha512-ZXSSqTFIrzduD63btIfEyOmNcBmQvgOVsPNPe0jYtESiXkhd8u2erDLnMxmGrDCwHCCHE7hxwRDCT3pt0esT4g==} 402 | 403 | '@vitest/snapshot@2.1.9': 404 | resolution: {integrity: sha512-oBO82rEjsxLNJincVhLhaxxZdEtV0EFHMK5Kmx5sJ6H9L183dHECjiefOAdnqpIgT5eZwT04PoggUnW88vOBNQ==} 405 | 406 | '@vitest/spy@2.1.9': 407 | resolution: {integrity: sha512-E1B35FwzXXTs9FHNK6bDszs7mtydNi5MIfUWpceJ8Xbfb1gBMscAnwLbEu+B44ed6W3XjL9/ehLPHR1fkf1KLQ==} 408 | 409 | '@vitest/utils@2.1.9': 410 | resolution: {integrity: sha512-v0psaMSkNJ3A2NMrUEHFRzJtDPFn+/VWZ5WxImB21T9fjucJRmS7xCS3ppEnARb9y11OAzaD+P2Ps+b+BGX5iQ==} 411 | 412 | ansi-regex@5.0.1: 413 | resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} 414 | engines: {node: '>=8'} 415 | 416 | ansi-regex@6.2.2: 417 | resolution: {integrity: sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==} 418 | engines: {node: '>=12'} 419 | 420 | ansi-styles@4.3.0: 421 | resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} 422 | engines: {node: '>=8'} 423 | 424 | ansi-styles@6.2.3: 425 | resolution: {integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==} 426 | engines: {node: '>=12'} 427 | 428 | argparse@2.0.1: 429 | resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} 430 | 431 | assertion-error@2.0.1: 432 | resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} 433 | engines: {node: '>=12'} 434 | 435 | balanced-match@1.0.2: 436 | resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} 437 | 438 | brace-expansion@2.0.2: 439 | resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==} 440 | 441 | cac@6.7.14: 442 | resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} 443 | engines: {node: '>=8'} 444 | 445 | chai@5.3.3: 446 | resolution: {integrity: sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==} 447 | engines: {node: '>=18'} 448 | 449 | check-error@2.1.1: 450 | resolution: {integrity: sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==} 451 | engines: {node: '>= 16'} 452 | 453 | color-convert@2.0.1: 454 | resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} 455 | engines: {node: '>=7.0.0'} 456 | 457 | color-name@1.1.4: 458 | resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} 459 | 460 | coveralls-next@6.0.1: 461 | resolution: {integrity: sha512-G0xhUJE9M3OCKf8Xn/cGWrIh4xJI1w7MgJx30HpUfJf6yxyFS1SVGgXtFKmPGb5qEEh4H6oTUk0JQRVjq0ZgDQ==} 462 | engines: {node: '>=20'} 463 | hasBin: true 464 | 465 | cross-spawn@7.0.6: 466 | resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} 467 | engines: {node: '>= 8'} 468 | 469 | debug@4.4.3: 470 | resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} 471 | engines: {node: '>=6.0'} 472 | peerDependencies: 473 | supports-color: '*' 474 | peerDependenciesMeta: 475 | supports-color: 476 | optional: true 477 | 478 | deep-eql@5.0.2: 479 | resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==} 480 | engines: {node: '>=6'} 481 | 482 | eastasianwidth@0.2.0: 483 | resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} 484 | 485 | emoji-regex@8.0.0: 486 | resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} 487 | 488 | emoji-regex@9.2.2: 489 | resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} 490 | 491 | es-module-lexer@1.7.0: 492 | resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==} 493 | 494 | esbuild@0.25.12: 495 | resolution: {integrity: sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==} 496 | engines: {node: '>=18'} 497 | hasBin: true 498 | 499 | estree-walker@3.0.3: 500 | resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} 501 | 502 | expect-type@1.3.0: 503 | resolution: {integrity: sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==} 504 | engines: {node: '>=12.0.0'} 505 | 506 | foreground-child@3.3.1: 507 | resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} 508 | engines: {node: '>=14'} 509 | 510 | fsevents@2.3.3: 511 | resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} 512 | engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} 513 | os: [darwin] 514 | 515 | glob@10.5.0: 516 | resolution: {integrity: sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==} 517 | hasBin: true 518 | 519 | has-flag@4.0.0: 520 | resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} 521 | engines: {node: '>=8'} 522 | 523 | html-escaper@2.0.2: 524 | resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} 525 | 526 | is-fullwidth-code-point@3.0.0: 527 | resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} 528 | engines: {node: '>=8'} 529 | 530 | isexe@2.0.0: 531 | resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} 532 | 533 | istanbul-lib-coverage@3.2.2: 534 | resolution: {integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==} 535 | engines: {node: '>=8'} 536 | 537 | istanbul-lib-report@3.0.1: 538 | resolution: {integrity: sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==} 539 | engines: {node: '>=10'} 540 | 541 | istanbul-lib-source-maps@5.0.6: 542 | resolution: {integrity: sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A==} 543 | engines: {node: '>=10'} 544 | 545 | istanbul-reports@3.2.0: 546 | resolution: {integrity: sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==} 547 | engines: {node: '>=8'} 548 | 549 | jackspeak@3.4.3: 550 | resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} 551 | 552 | js-yaml@4.1.1: 553 | resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==} 554 | hasBin: true 555 | 556 | lcov-parse@1.0.0: 557 | resolution: {integrity: sha512-aprLII/vPzuQvYZnDRU78Fns9I2Ag3gi4Ipga/hxnVMCZC8DnR2nI7XBqrPoywGfxqIx/DgarGvDJZAD3YBTgQ==} 558 | hasBin: true 559 | 560 | lodash-es@4.17.21: 561 | resolution: {integrity: sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==} 562 | 563 | loupe@3.2.1: 564 | resolution: {integrity: sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==} 565 | 566 | lru-cache@10.4.3: 567 | resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} 568 | 569 | magic-string@0.30.21: 570 | resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} 571 | 572 | magicast@0.3.5: 573 | resolution: {integrity: sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ==} 574 | 575 | make-dir@4.0.0: 576 | resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==} 577 | engines: {node: '>=10'} 578 | 579 | minimatch@9.0.5: 580 | resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} 581 | engines: {node: '>=16 || 14 >=14.17'} 582 | 583 | minipass@7.1.2: 584 | resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} 585 | engines: {node: '>=16 || 14 >=14.17'} 586 | 587 | ms@2.1.3: 588 | resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} 589 | 590 | nanoid@3.3.11: 591 | resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} 592 | engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} 593 | hasBin: true 594 | 595 | package-json-from-dist@1.0.1: 596 | resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} 597 | 598 | path-key@3.1.1: 599 | resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} 600 | engines: {node: '>=8'} 601 | 602 | path-scurry@1.11.1: 603 | resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} 604 | engines: {node: '>=16 || 14 >=14.18'} 605 | 606 | pathe@1.1.2: 607 | resolution: {integrity: sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==} 608 | 609 | pathval@2.0.1: 610 | resolution: {integrity: sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==} 611 | engines: {node: '>= 14.16'} 612 | 613 | picocolors@1.1.1: 614 | resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} 615 | 616 | postcss@8.5.6: 617 | resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} 618 | engines: {node: ^10 || ^12 || >=14} 619 | 620 | prettier@3.7.4: 621 | resolution: {integrity: sha512-v6UNi1+3hSlVvv8fSaoUbggEM5VErKmmpGA7Pl3HF8V6uKY7rvClBOJlH6yNwQtfTueNkGVpOv/mtWL9L4bgRA==} 622 | engines: {node: '>=14'} 623 | hasBin: true 624 | 625 | rollup@4.53.3: 626 | resolution: {integrity: sha512-w8GmOxZfBmKknvdXU1sdM9NHcoQejwF/4mNgj2JuEEdRaHwwF12K7e9eXn1nLZ07ad+du76mkVsyeb2rKGllsA==} 627 | engines: {node: '>=18.0.0', npm: '>=8.0.0'} 628 | hasBin: true 629 | 630 | semver@7.7.3: 631 | resolution: {integrity: sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==} 632 | engines: {node: '>=10'} 633 | hasBin: true 634 | 635 | shebang-command@2.0.0: 636 | resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} 637 | engines: {node: '>=8'} 638 | 639 | shebang-regex@3.0.0: 640 | resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} 641 | engines: {node: '>=8'} 642 | 643 | siginfo@2.0.0: 644 | resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} 645 | 646 | signal-exit@4.1.0: 647 | resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} 648 | engines: {node: '>=14'} 649 | 650 | source-map-js@1.2.1: 651 | resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} 652 | engines: {node: '>=0.10.0'} 653 | 654 | stackback@0.0.2: 655 | resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} 656 | 657 | std-env@3.10.0: 658 | resolution: {integrity: sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==} 659 | 660 | string-width@4.2.3: 661 | resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} 662 | engines: {node: '>=8'} 663 | 664 | string-width@5.1.2: 665 | resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} 666 | engines: {node: '>=12'} 667 | 668 | strip-ansi@6.0.1: 669 | resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} 670 | engines: {node: '>=8'} 671 | 672 | strip-ansi@7.1.2: 673 | resolution: {integrity: sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==} 674 | engines: {node: '>=12'} 675 | 676 | supports-color@7.2.0: 677 | resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} 678 | engines: {node: '>=8'} 679 | 680 | test-exclude@7.0.1: 681 | resolution: {integrity: sha512-pFYqmTw68LXVjeWJMST4+borgQP2AyMNbg1BpZh9LbyhUeNkeaPF9gzfPGUAnSMV3qPYdWUwDIjjCLiSDOl7vg==} 682 | engines: {node: '>=18'} 683 | 684 | tinybench@2.9.0: 685 | resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} 686 | 687 | tinyexec@0.3.2: 688 | resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==} 689 | 690 | tinypool@1.1.1: 691 | resolution: {integrity: sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==} 692 | engines: {node: ^18.0.0 || >=20.0.0} 693 | 694 | tinyrainbow@1.2.0: 695 | resolution: {integrity: sha512-weEDEq7Z5eTHPDh4xjX789+fHfF+P8boiFB+0vbWzpbnbsEr/GRaohi/uMKxg8RZMXnl1ItAi/IUHWMsjDV7kQ==} 696 | engines: {node: '>=14.0.0'} 697 | 698 | tinyspy@3.0.2: 699 | resolution: {integrity: sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==} 700 | engines: {node: '>=14.0.0'} 701 | 702 | typescript@5.9.3: 703 | resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} 704 | engines: {node: '>=14.17'} 705 | hasBin: true 706 | 707 | undici-types@6.21.0: 708 | resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} 709 | 710 | vite-node@2.1.9: 711 | resolution: {integrity: sha512-AM9aQ/IPrW/6ENLQg3AGY4K1N2TGZdR5e4gu/MmmR2xR3Ll1+dib+nook92g4TV3PXVyeyxdWwtaCAiUL0hMxA==} 712 | engines: {node: ^18.0.0 || >=20.0.0} 713 | hasBin: true 714 | 715 | vite@5.4.21: 716 | resolution: {integrity: sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==} 717 | engines: {node: ^18.0.0 || >=20.0.0} 718 | hasBin: true 719 | peerDependencies: 720 | '@types/node': ^18.0.0 || >=20.0.0 721 | less: '*' 722 | lightningcss: ^1.21.0 723 | sass: '*' 724 | sass-embedded: '*' 725 | stylus: '*' 726 | sugarss: '*' 727 | terser: ^5.4.0 728 | peerDependenciesMeta: 729 | '@types/node': 730 | optional: true 731 | less: 732 | optional: true 733 | lightningcss: 734 | optional: true 735 | sass: 736 | optional: true 737 | sass-embedded: 738 | optional: true 739 | stylus: 740 | optional: true 741 | sugarss: 742 | optional: true 743 | terser: 744 | optional: true 745 | 746 | vitest@2.1.9: 747 | resolution: {integrity: sha512-MSmPM9REYqDGBI8439mA4mWhV5sKmDlBKWIYbA3lRb2PTHACE0mgKwA8yQ2xq9vxDTuk4iPrECBAEW2aoFXY0Q==} 748 | engines: {node: ^18.0.0 || >=20.0.0} 749 | hasBin: true 750 | peerDependencies: 751 | '@edge-runtime/vm': '*' 752 | '@types/node': ^18.0.0 || >=20.0.0 753 | '@vitest/browser': 2.1.9 754 | '@vitest/ui': 2.1.9 755 | happy-dom: '*' 756 | jsdom: '*' 757 | peerDependenciesMeta: 758 | '@edge-runtime/vm': 759 | optional: true 760 | '@types/node': 761 | optional: true 762 | '@vitest/browser': 763 | optional: true 764 | '@vitest/ui': 765 | optional: true 766 | happy-dom: 767 | optional: true 768 | jsdom: 769 | optional: true 770 | 771 | which@2.0.2: 772 | resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} 773 | engines: {node: '>= 8'} 774 | hasBin: true 775 | 776 | why-is-node-running@2.3.0: 777 | resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} 778 | engines: {node: '>=8'} 779 | hasBin: true 780 | 781 | wrap-ansi@7.0.0: 782 | resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} 783 | engines: {node: '>=10'} 784 | 785 | wrap-ansi@8.1.0: 786 | resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} 787 | engines: {node: '>=12'} 788 | 789 | snapshots: 790 | 791 | '@ampproject/remapping@2.3.0': 792 | dependencies: 793 | '@jridgewell/gen-mapping': 0.3.13 794 | '@jridgewell/trace-mapping': 0.3.31 795 | 796 | '@babel/helper-string-parser@7.27.1': {} 797 | 798 | '@babel/helper-validator-identifier@7.28.5': {} 799 | 800 | '@babel/parser@7.28.5': 801 | dependencies: 802 | '@babel/types': 7.28.5 803 | 804 | '@babel/types@7.28.5': 805 | dependencies: 806 | '@babel/helper-string-parser': 7.27.1 807 | '@babel/helper-validator-identifier': 7.28.5 808 | 809 | '@bcoe/v8-coverage@0.2.3': {} 810 | 811 | '@esbuild/aix-ppc64@0.25.12': 812 | optional: true 813 | 814 | '@esbuild/android-arm64@0.25.12': 815 | optional: true 816 | 817 | '@esbuild/android-arm@0.25.12': 818 | optional: true 819 | 820 | '@esbuild/android-x64@0.25.12': 821 | optional: true 822 | 823 | '@esbuild/darwin-arm64@0.25.12': 824 | optional: true 825 | 826 | '@esbuild/darwin-x64@0.25.12': 827 | optional: true 828 | 829 | '@esbuild/freebsd-arm64@0.25.12': 830 | optional: true 831 | 832 | '@esbuild/freebsd-x64@0.25.12': 833 | optional: true 834 | 835 | '@esbuild/linux-arm64@0.25.12': 836 | optional: true 837 | 838 | '@esbuild/linux-arm@0.25.12': 839 | optional: true 840 | 841 | '@esbuild/linux-ia32@0.25.12': 842 | optional: true 843 | 844 | '@esbuild/linux-loong64@0.25.12': 845 | optional: true 846 | 847 | '@esbuild/linux-mips64el@0.25.12': 848 | optional: true 849 | 850 | '@esbuild/linux-ppc64@0.25.12': 851 | optional: true 852 | 853 | '@esbuild/linux-riscv64@0.25.12': 854 | optional: true 855 | 856 | '@esbuild/linux-s390x@0.25.12': 857 | optional: true 858 | 859 | '@esbuild/linux-x64@0.25.12': 860 | optional: true 861 | 862 | '@esbuild/netbsd-arm64@0.25.12': 863 | optional: true 864 | 865 | '@esbuild/netbsd-x64@0.25.12': 866 | optional: true 867 | 868 | '@esbuild/openbsd-arm64@0.25.12': 869 | optional: true 870 | 871 | '@esbuild/openbsd-x64@0.25.12': 872 | optional: true 873 | 874 | '@esbuild/openharmony-arm64@0.25.12': 875 | optional: true 876 | 877 | '@esbuild/sunos-x64@0.25.12': 878 | optional: true 879 | 880 | '@esbuild/win32-arm64@0.25.12': 881 | optional: true 882 | 883 | '@esbuild/win32-ia32@0.25.12': 884 | optional: true 885 | 886 | '@esbuild/win32-x64@0.25.12': 887 | optional: true 888 | 889 | '@isaacs/cliui@8.0.2': 890 | dependencies: 891 | string-width: 5.1.2 892 | string-width-cjs: string-width@4.2.3 893 | strip-ansi: 7.1.2 894 | strip-ansi-cjs: strip-ansi@6.0.1 895 | wrap-ansi: 8.1.0 896 | wrap-ansi-cjs: wrap-ansi@7.0.0 897 | 898 | '@istanbuljs/schema@0.1.3': {} 899 | 900 | '@jridgewell/gen-mapping@0.3.13': 901 | dependencies: 902 | '@jridgewell/sourcemap-codec': 1.5.5 903 | '@jridgewell/trace-mapping': 0.3.31 904 | 905 | '@jridgewell/resolve-uri@3.1.2': {} 906 | 907 | '@jridgewell/sourcemap-codec@1.5.5': {} 908 | 909 | '@jridgewell/trace-mapping@0.3.31': 910 | dependencies: 911 | '@jridgewell/resolve-uri': 3.1.2 912 | '@jridgewell/sourcemap-codec': 1.5.5 913 | 914 | '@pkgjs/parseargs@0.11.0': 915 | optional: true 916 | 917 | '@rollup/rollup-android-arm-eabi@4.53.3': 918 | optional: true 919 | 920 | '@rollup/rollup-android-arm64@4.53.3': 921 | optional: true 922 | 923 | '@rollup/rollup-darwin-arm64@4.53.3': 924 | optional: true 925 | 926 | '@rollup/rollup-darwin-x64@4.53.3': 927 | optional: true 928 | 929 | '@rollup/rollup-freebsd-arm64@4.53.3': 930 | optional: true 931 | 932 | '@rollup/rollup-freebsd-x64@4.53.3': 933 | optional: true 934 | 935 | '@rollup/rollup-linux-arm-gnueabihf@4.53.3': 936 | optional: true 937 | 938 | '@rollup/rollup-linux-arm-musleabihf@4.53.3': 939 | optional: true 940 | 941 | '@rollup/rollup-linux-arm64-gnu@4.53.3': 942 | optional: true 943 | 944 | '@rollup/rollup-linux-arm64-musl@4.53.3': 945 | optional: true 946 | 947 | '@rollup/rollup-linux-loong64-gnu@4.53.3': 948 | optional: true 949 | 950 | '@rollup/rollup-linux-ppc64-gnu@4.53.3': 951 | optional: true 952 | 953 | '@rollup/rollup-linux-riscv64-gnu@4.53.3': 954 | optional: true 955 | 956 | '@rollup/rollup-linux-riscv64-musl@4.53.3': 957 | optional: true 958 | 959 | '@rollup/rollup-linux-s390x-gnu@4.53.3': 960 | optional: true 961 | 962 | '@rollup/rollup-linux-x64-gnu@4.53.3': 963 | optional: true 964 | 965 | '@rollup/rollup-linux-x64-musl@4.53.3': 966 | optional: true 967 | 968 | '@rollup/rollup-openharmony-arm64@4.53.3': 969 | optional: true 970 | 971 | '@rollup/rollup-win32-arm64-msvc@4.53.3': 972 | optional: true 973 | 974 | '@rollup/rollup-win32-ia32-msvc@4.53.3': 975 | optional: true 976 | 977 | '@rollup/rollup-win32-x64-gnu@4.53.3': 978 | optional: true 979 | 980 | '@rollup/rollup-win32-x64-msvc@4.53.3': 981 | optional: true 982 | 983 | '@types/estree@1.0.8': {} 984 | 985 | '@types/lodash-es@4.17.12': 986 | dependencies: 987 | '@types/lodash': 4.17.21 988 | 989 | '@types/lodash@4.17.21': {} 990 | 991 | '@types/node@22.19.2': 992 | dependencies: 993 | undici-types: 6.21.0 994 | 995 | '@vitest/coverage-v8@2.1.8(vitest@2.1.9(@types/node@22.19.2))': 996 | dependencies: 997 | '@ampproject/remapping': 2.3.0 998 | '@bcoe/v8-coverage': 0.2.3 999 | debug: 4.4.3 1000 | istanbul-lib-coverage: 3.2.2 1001 | istanbul-lib-report: 3.0.1 1002 | istanbul-lib-source-maps: 5.0.6 1003 | istanbul-reports: 3.2.0 1004 | magic-string: 0.30.21 1005 | magicast: 0.3.5 1006 | std-env: 3.10.0 1007 | test-exclude: 7.0.1 1008 | tinyrainbow: 1.2.0 1009 | vitest: 2.1.9(@types/node@22.19.2) 1010 | transitivePeerDependencies: 1011 | - supports-color 1012 | 1013 | '@vitest/expect@2.1.9': 1014 | dependencies: 1015 | '@vitest/spy': 2.1.9 1016 | '@vitest/utils': 2.1.9 1017 | chai: 5.3.3 1018 | tinyrainbow: 1.2.0 1019 | 1020 | '@vitest/mocker@2.1.9(vite@5.4.21(@types/node@22.19.2))': 1021 | dependencies: 1022 | '@vitest/spy': 2.1.9 1023 | estree-walker: 3.0.3 1024 | magic-string: 0.30.21 1025 | optionalDependencies: 1026 | vite: 5.4.21(@types/node@22.19.2) 1027 | 1028 | '@vitest/pretty-format@2.1.9': 1029 | dependencies: 1030 | tinyrainbow: 1.2.0 1031 | 1032 | '@vitest/runner@2.1.9': 1033 | dependencies: 1034 | '@vitest/utils': 2.1.9 1035 | pathe: 1.1.2 1036 | 1037 | '@vitest/snapshot@2.1.9': 1038 | dependencies: 1039 | '@vitest/pretty-format': 2.1.9 1040 | magic-string: 0.30.21 1041 | pathe: 1.1.2 1042 | 1043 | '@vitest/spy@2.1.9': 1044 | dependencies: 1045 | tinyspy: 3.0.2 1046 | 1047 | '@vitest/utils@2.1.9': 1048 | dependencies: 1049 | '@vitest/pretty-format': 2.1.9 1050 | loupe: 3.2.1 1051 | tinyrainbow: 1.2.0 1052 | 1053 | ansi-regex@5.0.1: {} 1054 | 1055 | ansi-regex@6.2.2: {} 1056 | 1057 | ansi-styles@4.3.0: 1058 | dependencies: 1059 | color-convert: 2.0.1 1060 | 1061 | ansi-styles@6.2.3: {} 1062 | 1063 | argparse@2.0.1: {} 1064 | 1065 | assertion-error@2.0.1: {} 1066 | 1067 | balanced-match@1.0.2: {} 1068 | 1069 | brace-expansion@2.0.2: 1070 | dependencies: 1071 | balanced-match: 1.0.2 1072 | 1073 | cac@6.7.14: {} 1074 | 1075 | chai@5.3.3: 1076 | dependencies: 1077 | assertion-error: 2.0.1 1078 | check-error: 2.1.1 1079 | deep-eql: 5.0.2 1080 | loupe: 3.2.1 1081 | pathval: 2.0.1 1082 | 1083 | check-error@2.1.1: {} 1084 | 1085 | color-convert@2.0.1: 1086 | dependencies: 1087 | color-name: 1.1.4 1088 | 1089 | color-name@1.1.4: {} 1090 | 1091 | coveralls-next@6.0.1: 1092 | dependencies: 1093 | js-yaml: 4.1.1 1094 | lcov-parse: 1.0.0 1095 | 1096 | cross-spawn@7.0.6: 1097 | dependencies: 1098 | path-key: 3.1.1 1099 | shebang-command: 2.0.0 1100 | which: 2.0.2 1101 | 1102 | debug@4.4.3: 1103 | dependencies: 1104 | ms: 2.1.3 1105 | 1106 | deep-eql@5.0.2: {} 1107 | 1108 | eastasianwidth@0.2.0: {} 1109 | 1110 | emoji-regex@8.0.0: {} 1111 | 1112 | emoji-regex@9.2.2: {} 1113 | 1114 | es-module-lexer@1.7.0: {} 1115 | 1116 | esbuild@0.25.12: 1117 | optionalDependencies: 1118 | '@esbuild/aix-ppc64': 0.25.12 1119 | '@esbuild/android-arm': 0.25.12 1120 | '@esbuild/android-arm64': 0.25.12 1121 | '@esbuild/android-x64': 0.25.12 1122 | '@esbuild/darwin-arm64': 0.25.12 1123 | '@esbuild/darwin-x64': 0.25.12 1124 | '@esbuild/freebsd-arm64': 0.25.12 1125 | '@esbuild/freebsd-x64': 0.25.12 1126 | '@esbuild/linux-arm': 0.25.12 1127 | '@esbuild/linux-arm64': 0.25.12 1128 | '@esbuild/linux-ia32': 0.25.12 1129 | '@esbuild/linux-loong64': 0.25.12 1130 | '@esbuild/linux-mips64el': 0.25.12 1131 | '@esbuild/linux-ppc64': 0.25.12 1132 | '@esbuild/linux-riscv64': 0.25.12 1133 | '@esbuild/linux-s390x': 0.25.12 1134 | '@esbuild/linux-x64': 0.25.12 1135 | '@esbuild/netbsd-arm64': 0.25.12 1136 | '@esbuild/netbsd-x64': 0.25.12 1137 | '@esbuild/openbsd-arm64': 0.25.12 1138 | '@esbuild/openbsd-x64': 0.25.12 1139 | '@esbuild/openharmony-arm64': 0.25.12 1140 | '@esbuild/sunos-x64': 0.25.12 1141 | '@esbuild/win32-arm64': 0.25.12 1142 | '@esbuild/win32-ia32': 0.25.12 1143 | '@esbuild/win32-x64': 0.25.12 1144 | 1145 | estree-walker@3.0.3: 1146 | dependencies: 1147 | '@types/estree': 1.0.8 1148 | 1149 | expect-type@1.3.0: {} 1150 | 1151 | foreground-child@3.3.1: 1152 | dependencies: 1153 | cross-spawn: 7.0.6 1154 | signal-exit: 4.1.0 1155 | 1156 | fsevents@2.3.3: 1157 | optional: true 1158 | 1159 | glob@10.5.0: 1160 | dependencies: 1161 | foreground-child: 3.3.1 1162 | jackspeak: 3.4.3 1163 | minimatch: 9.0.5 1164 | minipass: 7.1.2 1165 | package-json-from-dist: 1.0.1 1166 | path-scurry: 1.11.1 1167 | 1168 | has-flag@4.0.0: {} 1169 | 1170 | html-escaper@2.0.2: {} 1171 | 1172 | is-fullwidth-code-point@3.0.0: {} 1173 | 1174 | isexe@2.0.0: {} 1175 | 1176 | istanbul-lib-coverage@3.2.2: {} 1177 | 1178 | istanbul-lib-report@3.0.1: 1179 | dependencies: 1180 | istanbul-lib-coverage: 3.2.2 1181 | make-dir: 4.0.0 1182 | supports-color: 7.2.0 1183 | 1184 | istanbul-lib-source-maps@5.0.6: 1185 | dependencies: 1186 | '@jridgewell/trace-mapping': 0.3.31 1187 | debug: 4.4.3 1188 | istanbul-lib-coverage: 3.2.2 1189 | transitivePeerDependencies: 1190 | - supports-color 1191 | 1192 | istanbul-reports@3.2.0: 1193 | dependencies: 1194 | html-escaper: 2.0.2 1195 | istanbul-lib-report: 3.0.1 1196 | 1197 | jackspeak@3.4.3: 1198 | dependencies: 1199 | '@isaacs/cliui': 8.0.2 1200 | optionalDependencies: 1201 | '@pkgjs/parseargs': 0.11.0 1202 | 1203 | js-yaml@4.1.1: 1204 | dependencies: 1205 | argparse: 2.0.1 1206 | 1207 | lcov-parse@1.0.0: {} 1208 | 1209 | lodash-es@4.17.21: {} 1210 | 1211 | loupe@3.2.1: {} 1212 | 1213 | lru-cache@10.4.3: {} 1214 | 1215 | magic-string@0.30.21: 1216 | dependencies: 1217 | '@jridgewell/sourcemap-codec': 1.5.5 1218 | 1219 | magicast@0.3.5: 1220 | dependencies: 1221 | '@babel/parser': 7.28.5 1222 | '@babel/types': 7.28.5 1223 | source-map-js: 1.2.1 1224 | 1225 | make-dir@4.0.0: 1226 | dependencies: 1227 | semver: 7.7.3 1228 | 1229 | minimatch@9.0.5: 1230 | dependencies: 1231 | brace-expansion: 2.0.2 1232 | 1233 | minipass@7.1.2: {} 1234 | 1235 | ms@2.1.3: {} 1236 | 1237 | nanoid@3.3.11: {} 1238 | 1239 | package-json-from-dist@1.0.1: {} 1240 | 1241 | path-key@3.1.1: {} 1242 | 1243 | path-scurry@1.11.1: 1244 | dependencies: 1245 | lru-cache: 10.4.3 1246 | minipass: 7.1.2 1247 | 1248 | pathe@1.1.2: {} 1249 | 1250 | pathval@2.0.1: {} 1251 | 1252 | picocolors@1.1.1: {} 1253 | 1254 | postcss@8.5.6: 1255 | dependencies: 1256 | nanoid: 3.3.11 1257 | picocolors: 1.1.1 1258 | source-map-js: 1.2.1 1259 | 1260 | prettier@3.7.4: {} 1261 | 1262 | rollup@4.53.3: 1263 | dependencies: 1264 | '@types/estree': 1.0.8 1265 | optionalDependencies: 1266 | '@rollup/rollup-android-arm-eabi': 4.53.3 1267 | '@rollup/rollup-android-arm64': 4.53.3 1268 | '@rollup/rollup-darwin-arm64': 4.53.3 1269 | '@rollup/rollup-darwin-x64': 4.53.3 1270 | '@rollup/rollup-freebsd-arm64': 4.53.3 1271 | '@rollup/rollup-freebsd-x64': 4.53.3 1272 | '@rollup/rollup-linux-arm-gnueabihf': 4.53.3 1273 | '@rollup/rollup-linux-arm-musleabihf': 4.53.3 1274 | '@rollup/rollup-linux-arm64-gnu': 4.53.3 1275 | '@rollup/rollup-linux-arm64-musl': 4.53.3 1276 | '@rollup/rollup-linux-loong64-gnu': 4.53.3 1277 | '@rollup/rollup-linux-ppc64-gnu': 4.53.3 1278 | '@rollup/rollup-linux-riscv64-gnu': 4.53.3 1279 | '@rollup/rollup-linux-riscv64-musl': 4.53.3 1280 | '@rollup/rollup-linux-s390x-gnu': 4.53.3 1281 | '@rollup/rollup-linux-x64-gnu': 4.53.3 1282 | '@rollup/rollup-linux-x64-musl': 4.53.3 1283 | '@rollup/rollup-openharmony-arm64': 4.53.3 1284 | '@rollup/rollup-win32-arm64-msvc': 4.53.3 1285 | '@rollup/rollup-win32-ia32-msvc': 4.53.3 1286 | '@rollup/rollup-win32-x64-gnu': 4.53.3 1287 | '@rollup/rollup-win32-x64-msvc': 4.53.3 1288 | fsevents: 2.3.3 1289 | 1290 | semver@7.7.3: {} 1291 | 1292 | shebang-command@2.0.0: 1293 | dependencies: 1294 | shebang-regex: 3.0.0 1295 | 1296 | shebang-regex@3.0.0: {} 1297 | 1298 | siginfo@2.0.0: {} 1299 | 1300 | signal-exit@4.1.0: {} 1301 | 1302 | source-map-js@1.2.1: {} 1303 | 1304 | stackback@0.0.2: {} 1305 | 1306 | std-env@3.10.0: {} 1307 | 1308 | string-width@4.2.3: 1309 | dependencies: 1310 | emoji-regex: 8.0.0 1311 | is-fullwidth-code-point: 3.0.0 1312 | strip-ansi: 6.0.1 1313 | 1314 | string-width@5.1.2: 1315 | dependencies: 1316 | eastasianwidth: 0.2.0 1317 | emoji-regex: 9.2.2 1318 | strip-ansi: 7.1.2 1319 | 1320 | strip-ansi@6.0.1: 1321 | dependencies: 1322 | ansi-regex: 5.0.1 1323 | 1324 | strip-ansi@7.1.2: 1325 | dependencies: 1326 | ansi-regex: 6.2.2 1327 | 1328 | supports-color@7.2.0: 1329 | dependencies: 1330 | has-flag: 4.0.0 1331 | 1332 | test-exclude@7.0.1: 1333 | dependencies: 1334 | '@istanbuljs/schema': 0.1.3 1335 | glob: 10.5.0 1336 | minimatch: 9.0.5 1337 | 1338 | tinybench@2.9.0: {} 1339 | 1340 | tinyexec@0.3.2: {} 1341 | 1342 | tinypool@1.1.1: {} 1343 | 1344 | tinyrainbow@1.2.0: {} 1345 | 1346 | tinyspy@3.0.2: {} 1347 | 1348 | typescript@5.9.3: {} 1349 | 1350 | undici-types@6.21.0: {} 1351 | 1352 | vite-node@2.1.9(@types/node@22.19.2): 1353 | dependencies: 1354 | cac: 6.7.14 1355 | debug: 4.4.3 1356 | es-module-lexer: 1.7.0 1357 | pathe: 1.1.2 1358 | vite: 5.4.21(@types/node@22.19.2) 1359 | transitivePeerDependencies: 1360 | - '@types/node' 1361 | - less 1362 | - lightningcss 1363 | - sass 1364 | - sass-embedded 1365 | - stylus 1366 | - sugarss 1367 | - supports-color 1368 | - terser 1369 | 1370 | vite@5.4.21(@types/node@22.19.2): 1371 | dependencies: 1372 | esbuild: 0.25.12 1373 | postcss: 8.5.6 1374 | rollup: 4.53.3 1375 | optionalDependencies: 1376 | '@types/node': 22.19.2 1377 | fsevents: 2.3.3 1378 | 1379 | vitest@2.1.9(@types/node@22.19.2): 1380 | dependencies: 1381 | '@vitest/expect': 2.1.9 1382 | '@vitest/mocker': 2.1.9(vite@5.4.21(@types/node@22.19.2)) 1383 | '@vitest/pretty-format': 2.1.9 1384 | '@vitest/runner': 2.1.9 1385 | '@vitest/snapshot': 2.1.9 1386 | '@vitest/spy': 2.1.9 1387 | '@vitest/utils': 2.1.9 1388 | chai: 5.3.3 1389 | debug: 4.4.3 1390 | expect-type: 1.3.0 1391 | magic-string: 0.30.21 1392 | pathe: 1.1.2 1393 | std-env: 3.10.0 1394 | tinybench: 2.9.0 1395 | tinyexec: 0.3.2 1396 | tinypool: 1.1.1 1397 | tinyrainbow: 1.2.0 1398 | vite: 5.4.21(@types/node@22.19.2) 1399 | vite-node: 2.1.9(@types/node@22.19.2) 1400 | why-is-node-running: 2.3.0 1401 | optionalDependencies: 1402 | '@types/node': 22.19.2 1403 | transitivePeerDependencies: 1404 | - less 1405 | - lightningcss 1406 | - msw 1407 | - sass 1408 | - sass-embedded 1409 | - stylus 1410 | - sugarss 1411 | - supports-color 1412 | - terser 1413 | 1414 | which@2.0.2: 1415 | dependencies: 1416 | isexe: 2.0.0 1417 | 1418 | why-is-node-running@2.3.0: 1419 | dependencies: 1420 | siginfo: 2.0.0 1421 | stackback: 0.0.2 1422 | 1423 | wrap-ansi@7.0.0: 1424 | dependencies: 1425 | ansi-styles: 4.3.0 1426 | string-width: 4.2.3 1427 | strip-ansi: 6.0.1 1428 | 1429 | wrap-ansi@8.1.0: 1430 | dependencies: 1431 | ansi-styles: 6.2.3 1432 | string-width: 5.1.2 1433 | strip-ansi: 7.1.2 1434 | --------------------------------------------------------------------------------