├── .gitignore ├── .travis.yml ├── LICENSE ├── Makefile ├── README.md ├── docs ├── README.md ├── interfaces │ ├── _index_d_.isearchparserdictionary.md │ ├── _index_d_.searchparseroffset.md │ ├── _index_d_.searchparseroptions.md │ └── _index_d_.searchparserresult.md └── modules │ └── _index_d_.md ├── index.d.ts ├── index.js ├── lib └── search-query-parser.js ├── package-lock.json ├── package.json ├── test ├── index.html ├── mocha.css ├── mocha.js └── test.js ├── tsconfig.json └── tslint.json /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | # Runtime data 6 | pids 7 | *.pid 8 | *.seed 9 | 10 | # Directory for instrumented libs generated by jscoverage/JSCover 11 | lib-cov 12 | 13 | # Coverage directory used by tools like istanbul 14 | coverage 15 | 16 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 17 | .grunt 18 | 19 | # Compiled binary addons (http://nodejs.org/api/addons.html) 20 | build/Release 21 | 22 | # Dependency directory 23 | # Deployed apps should consider commenting this line out: 24 | # see https://npmjs.org/doc/faq.html#Should-I-check-my-node_modules-folder-into-git 25 | node_modules 26 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "12" 4 | - "13" 5 | - "14" 6 | - "15" 7 | - "16" 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 retraceio 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. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | BINS=./node_modules/.bin 2 | 3 | install: 4 | @npm install . 5 | 6 | test: 7 | @$(BINS)/mocha -R spec 8 | 9 | .PHONY: test 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Search Query Syntax Parser 2 | 3 | > A simple parser for advanced search query syntax. 4 | 5 | [![Build Status](https://travis-ci.org/nepsilon/search-query-parser.svg?branch=master)](https://travis-ci.org/nepsilon/search-query-parser) 6 | 7 | It parses a string like this: 8 | ``` 9 | from:hi@retrace.io,foo@gmail.com to:me subject:vacations date:1/10/2013-15/04/2014 photos 10 | ``` 11 | 12 | And turns it into an object like this: 13 | 14 | ```javascript 15 | { 16 | from: ['hi@retrace.io', 'foo@gmail.com'], 17 | to: 'me', 18 | subject: 'vacations', 19 | date: { 20 | from: '1/10/2013', 21 | to: '15/04/2014' 22 | }, 23 | text: 'photos', 24 | offsets: 25 | [ { keyword: 'from', value: 'hi@retrace.io,foo@gmail.com', offsetStart: 0, offsetEnd: 32 }, 26 | { keyword: 'to', value: 'me', offsetStart: 33, offsetEnd: 38 }, 27 | { keyword: 'subject', value: 'vacations', offsetStart: 39, offsetEnd: 56 }, 28 | { keyword: 'date', value: '1/10/2013-15/04/2014', offsetStart: 57, offsetEnd: 82 }, 29 | { text: 'photos', offsetStart: 83, offsetEnd: 89 } ] 30 | } 31 | ``` 32 | 33 | ## Installation 34 | 35 | ```shell 36 | $ npm install search-query-parser 37 | ``` 38 | 39 | ## Usage 40 | 41 | ```javascript 42 | var searchQuery = require('search-query-parser'); 43 | 44 | var query = 'from:hi@retrace.io,foo@gmail.com to:me subject:vacations date:1/10/2013-15/04/2014 photos'; 45 | var options = {keywords: ['from', 'to', 'subject'], ranges: ['date']} 46 | 47 | var searchQueryObj = searchQuery.parse(query, options); 48 | 49 | // searchQueryObj.from is now ['hi@retrace.io', 'foo@gmail.com'] 50 | // searchQueryObj.to is now 'me' 51 | // searchQueryObj.date is now {from: '1/10/2013', to: '15/04/2014'} 52 | // searchQueryObj.text is now 'photos' 53 | ``` 54 | 55 | You can configure what keywords and ranges the parser should accept with the options argument. 56 | It accepts 5 values: 57 | * `keywords`, that can be separated by commas (,). Accepts an array of strings. 58 | * `ranges`, that can be separated by a hyphen (-). Accepts an array of strings. 59 | * `tokenize`, that controls the behaviour of text search terms. If set to `true`, non-keyword text terms are returned as an array of strings where each term in the array is a whitespace-separated word, or a multi-word term surrounded by single- or double-quotes. 60 | * `alwaysArray`, a boolean that controls the behaviour of the returned query. If set to `true`, all matched keywords would always be arrays instead of strings. If set to `false` they will be strings if matched a single value. Defaults to `false`. 61 | * `offsets`, a boolean that controls the behaviour of the returned query. If set to `true`, the query will contain the offsets object. If set to `false`, the query will not contain the offsets object. Defaults to `true`. 62 | 63 | If no keywords or ranges are specified, or if none are present in the given search query, then `searchQuery.parse` will return a string if `tokenize` is false, or an array of strings under the key `text` if `tokenize` is true. 64 | 65 | ```javascript 66 | var searchQuery = require('search-query-parser'); 67 | 68 | var query = 'a query with "just text"'; 69 | var parsedQuery = searchQuery.parse(query); 70 | // parsedQuery is now 'a query with "just text"' 71 | 72 | var options = {keywords: ['unused']}; 73 | var parsedQueryWithOptions = searchQuery.parse(query, options); 74 | // parsedQueryWithOptions is now 'a query with "just text"' 75 | 76 | var options2 = {tokenize: true}; 77 | var parsedQueryWithTokens = searchQuery.parse(query, options2); 78 | // parsedQueryWithTokens is now: ['a', 'query', 'with', 'just text'] 79 | ``` 80 | 81 | You can also use exclusion syntax, like `-from:sep@foobar.io name:hello,world`. This also works with non-keyword text terms when `tokenize` is set to `true`. 82 | 83 | ```javascript 84 | { 85 | name: ['hello', 'world'], 86 | exclude: { 87 | from: ['sep@foobar.io'] 88 | } 89 | } 90 | ``` 91 | 92 | Sometimes checking against whether a keyword holds string or not can be excessive and prone to errors; it's often easier to simply expect everything is an array even if it means doing 1-iteration loops often. 93 | 94 | ```javascript 95 | var searchQuery = require('search-query-parser'); 96 | 97 | var query = 'test:helloworld fun:yay,happy'; 98 | var options = {keywords: ['test', 'fun']}; 99 | var parsedQueryWithOptions = searchQuery.parse(query, options); 100 | // parsedQueryWithOptions is now: 101 | // { 102 | // test: 'helloworld', 103 | // fun: ['yay', 'happy'] 104 | // } 105 | 106 | var optionsAlwaysArray = {keywords: ['test', 'fun'], alwaysArray: true}; 107 | var parsedQueryWithOptions = searchQuery.parse(query, options); 108 | // parsedQueryWithOptions is now: 109 | // { 110 | // test: ['helloworld'], //No need to check whether test is a string or not! 111 | // fun: ['yay', 'happy'] 112 | // } 113 | ``` 114 | 115 | The offsets object could become pretty huge with long search queries which could be an unnecessary use of space if no functionality depends on it. It can simply be turned off using the option `offsets: false`. 116 | 117 | Anytime, you can go back and stringify the parsed search query. This could be handy if you would like to manipulate the parsed search query object. 118 | 119 | ```javascript 120 | var searchQuery = require('search-query-parser'); 121 | 122 | var query = 'from:hi@retrace.io,foo@gmail.com to:me subject:vacations date:1/10/2013-15/04/2014 photos'; 123 | var options = {keywords: ['from', 'to', 'subject'], ranges: ['date']} 124 | 125 | var searchQueryObj = searchQuery.parse(query, options); 126 | 127 | searchQueryObj.to = 'you'; 128 | var newQuery = searchQuery.stringify(query, options); 129 | 130 | // newQuery is now: photos from:hi@retrace.io,foo@gmail.com to:you subject:vacations date:1/10/2013-15/04/2014 131 | ``` 132 | 133 | ## Typescript 134 | 135 | Typescript types are available for this library in the `docs` directory. 136 | [Browse type documentation here.](docs/README.md) 137 | 138 | Documentation is generated using `node_modules/.bin/typedoc index.d.ts` 139 | 140 | ## Testing 141 | 142 | The 29 tests are written using the BDD testing framework should.js, and run with mocha. 143 | 144 | Run `npm install should` and `npm install -g mocha` to install them both. 145 | 146 | Run tests with `make test`. 147 | 148 | ## License 149 | 150 | The MIT License (MIT) 151 | 152 | Copyright (c) 2014 153 | 154 | Permission is hereby granted, free of charge, to any person obtaining a copy 155 | of this software and associated documentation files (the "Software"), to deal 156 | in the Software without restriction, including without limitation the rights 157 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 158 | copies of the Software, and to permit persons to whom the Software is 159 | furnished to do so, subject to the following conditions: 160 | 161 | The above copyright notice and this permission notice shall be included in all 162 | copies or substantial portions of the Software. 163 | 164 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 165 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 166 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 167 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 168 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 169 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 170 | SOFTWARE. 171 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | 2 | # search-query-parser 3 | 4 | ## Index 5 | 6 | ### External modules 7 | 8 | * ["index.d"](modules/_index_d_.md) 9 | 10 | --- 11 | 12 | -------------------------------------------------------------------------------- /docs/interfaces/_index_d_.isearchparserdictionary.md: -------------------------------------------------------------------------------- 1 | [search-query-parser](../README.md) > ["index.d"](../modules/_index_d_.md) > [ISearchParserDictionary](../interfaces/_index_d_.isearchparserdictionary.md) 2 | 3 | # Interface: ISearchParserDictionary 4 | 5 | ## Hierarchy 6 | 7 | **ISearchParserDictionary** 8 | 9 | ↳ [SearchParserResult](_index_d_.searchparserresult.md) 10 | 11 | ## Indexable 12 | 13 | \[key: `string`\]: `any` 14 | ## Index 15 | 16 | --- 17 | 18 | -------------------------------------------------------------------------------- /docs/interfaces/_index_d_.searchparseroffset.md: -------------------------------------------------------------------------------- 1 | [search-query-parser](../README.md) > ["index.d"](../modules/_index_d_.md) > [SearchParserOffset](../interfaces/_index_d_.searchparseroffset.md) 2 | 3 | # Interface: SearchParserOffset 4 | 5 | ## Hierarchy 6 | 7 | **SearchParserOffset** 8 | 9 | ## Index 10 | 11 | ### Properties 12 | 13 | * [keyword](_index_d_.searchparseroffset.md#keyword) 14 | * [offsetEnd](_index_d_.searchparseroffset.md#offsetend) 15 | * [offsetStart](_index_d_.searchparseroffset.md#offsetstart) 16 | * [value](_index_d_.searchparseroffset.md#value) 17 | 18 | --- 19 | 20 | ## Properties 21 | 22 | 23 | 24 | ### keyword 25 | 26 | **● keyword**: *`string`* 27 | 28 | *Defined in [index.d.ts:20](https://github.com/rtrvrtg/search-query-parser/blob/e4a7ccc/index.d.ts#L20)* 29 | 30 | ___ 31 | 32 | 33 | ### offsetEnd 34 | 35 | **● offsetEnd**: *`number`* 36 | 37 | *Defined in [index.d.ts:23](https://github.com/rtrvrtg/search-query-parser/blob/e4a7ccc/index.d.ts#L23)* 38 | 39 | ___ 40 | 41 | 42 | ### offsetStart 43 | 44 | **● offsetStart**: *`number`* 45 | 46 | *Defined in [index.d.ts:22](https://github.com/rtrvrtg/search-query-parser/blob/e4a7ccc/index.d.ts#L22)* 47 | 48 | ___ 49 | 50 | 51 | ### `` value 52 | 53 | **● value**: *`string`* 54 | 55 | *Defined in [index.d.ts:21](https://github.com/rtrvrtg/search-query-parser/blob/e4a7ccc/index.d.ts#L21)* 56 | 57 | ___ 58 | 59 | -------------------------------------------------------------------------------- /docs/interfaces/_index_d_.searchparseroptions.md: -------------------------------------------------------------------------------- 1 | [search-query-parser](../README.md) > ["index.d"](../modules/_index_d_.md) > [SearchParserOptions](../interfaces/_index_d_.searchparseroptions.md) 2 | 3 | # Interface: SearchParserOptions 4 | 5 | ## Hierarchy 6 | 7 | **SearchParserOptions** 8 | 9 | ## Index 10 | 11 | ### Properties 12 | 13 | * [alwaysArray](_index_d_.searchparseroptions.md#alwaysarray) 14 | * [keywords](_index_d_.searchparseroptions.md#keywords) 15 | * [offsets](_index_d_.searchparseroptions.md#offsets) 16 | * [ranges](_index_d_.searchparseroptions.md#ranges) 17 | * [tokenize](_index_d_.searchparseroptions.md#tokenize) 18 | 19 | --- 20 | 21 | ## Properties 22 | 23 | 24 | 25 | ### `` alwaysArray 26 | 27 | **● alwaysArray**: *`boolean`* 28 | 29 | *Defined in [index.d.ts:12](https://github.com/rtrvrtg/search-query-parser/blob/e4a7ccc/index.d.ts#L12)* 30 | 31 | ___ 32 | 33 | 34 | ### `` keywords 35 | 36 | **● keywords**: *`string`[]* 37 | 38 | *Defined in [index.d.ts:10](https://github.com/rtrvrtg/search-query-parser/blob/e4a7ccc/index.d.ts#L10)* 39 | 40 | ___ 41 | 42 | 43 | ### `` offsets 44 | 45 | **● offsets**: *`boolean`* 46 | 47 | *Defined in [index.d.ts:8](https://github.com/rtrvrtg/search-query-parser/blob/e4a7ccc/index.d.ts#L8)* 48 | 49 | ___ 50 | 51 | 52 | ### `` ranges 53 | 54 | **● ranges**: *`string`[]* 55 | 56 | *Defined in [index.d.ts:11](https://github.com/rtrvrtg/search-query-parser/blob/e4a7ccc/index.d.ts#L11)* 57 | 58 | ___ 59 | 60 | 61 | ### `` tokenize 62 | 63 | **● tokenize**: *`boolean`* 64 | 65 | *Defined in [index.d.ts:9](https://github.com/rtrvrtg/search-query-parser/blob/e4a7ccc/index.d.ts#L9)* 66 | 67 | ___ 68 | 69 | -------------------------------------------------------------------------------- /docs/interfaces/_index_d_.searchparserresult.md: -------------------------------------------------------------------------------- 1 | [search-query-parser](../README.md) > ["index.d"](../modules/_index_d_.md) > [SearchParserResult](../interfaces/_index_d_.searchparserresult.md) 2 | 3 | # Interface: SearchParserResult 4 | 5 | ## Hierarchy 6 | 7 | [ISearchParserDictionary](_index_d_.isearchparserdictionary.md) 8 | 9 | **↳ SearchParserResult** 10 | 11 | ## Indexable 12 | 13 | \[key: `string`\]: `any` 14 | ## Index 15 | 16 | ### Properties 17 | 18 | * [exclude](_index_d_.searchparserresult.md#exclude) 19 | * [offsets](_index_d_.searchparserresult.md#offsets) 20 | * [text](_index_d_.searchparserresult.md#text) 21 | 22 | --- 23 | 24 | ## Properties 25 | 26 | 27 | 28 | ### `` exclude 29 | 30 | **● exclude**: *[ISearchParserDictionary](_index_d_.isearchparserdictionary.md)* 31 | 32 | *Defined in [index.d.ts:29](https://github.com/rtrvrtg/search-query-parser/blob/e4a7ccc/index.d.ts#L29)* 33 | 34 | ___ 35 | 36 | 37 | ### `` offsets 38 | 39 | **● offsets**: *[SearchParserOffset](_index_d_.searchparseroffset.md)[]* 40 | 41 | *Defined in [index.d.ts:28](https://github.com/rtrvrtg/search-query-parser/blob/e4a7ccc/index.d.ts#L28)* 42 | 43 | ___ 44 | 45 | 46 | ### `` text 47 | 48 | **● text**: *`string` \| `string`[]* 49 | 50 | *Defined in [index.d.ts:27](https://github.com/rtrvrtg/search-query-parser/blob/e4a7ccc/index.d.ts#L27)* 51 | 52 | ___ 53 | 54 | -------------------------------------------------------------------------------- /docs/modules/_index_d_.md: -------------------------------------------------------------------------------- 1 | [search-query-parser](../README.md) > ["index.d"](../modules/_index_d_.md) 2 | 3 | # External module: "index.d" 4 | 5 | ## Index 6 | 7 | ### Interfaces 8 | 9 | * [ISearchParserDictionary](../interfaces/_index_d_.isearchparserdictionary.md) 10 | * [SearchParserOffset](../interfaces/_index_d_.searchparseroffset.md) 11 | * [SearchParserOptions](../interfaces/_index_d_.searchparseroptions.md) 12 | * [SearchParserResult](../interfaces/_index_d_.searchparserresult.md) 13 | 14 | ### Functions 15 | 16 | * [parse](_index_d_.md#parse) 17 | 18 | --- 19 | 20 | ## Functions 21 | 22 | 23 | 24 | ### parse 25 | 26 | ▸ **parse**(string: *`string`*, options?: *[SearchParserOptions](../interfaces/_index_d_.searchparseroptions.md)*): `string` \| [SearchParserResult](../interfaces/_index_d_.searchparserresult.md) 27 | 28 | *Defined in [index.d.ts:32](https://github.com/rtrvrtg/search-query-parser/blob/e4a7ccc/index.d.ts#L32)* 29 | 30 | **Parameters:** 31 | 32 | | Name | Type | 33 | | ------ | ------ | 34 | | string | `string` | 35 | | `Optional` options | [SearchParserOptions](../interfaces/_index_d_.searchparseroptions.md) | 36 | 37 | **Returns:** `string` \| [SearchParserResult](../interfaces/_index_d_.searchparserresult.md) 38 | 39 | ___ 40 | 41 | -------------------------------------------------------------------------------- /index.d.ts: -------------------------------------------------------------------------------- 1 | // @file 2 | // Type definitions for search-query-parser. 3 | // Project: https://github.com/nepsilon/search-query-parser 4 | // Definitions by: Geoffrey Roberts 5 | // Definitions: https://github.com/nepsilon/search-query-parser 6 | 7 | export interface SearchParserOptions { 8 | offsets?: boolean; 9 | tokenize?: boolean; 10 | keywords?: string[]; 11 | ranges?: string[]; 12 | alwaysArray?: boolean; 13 | } 14 | 15 | export interface ISearchParserDictionary { 16 | [key: string]: any; 17 | } 18 | 19 | export type SearchParserOffset = (SearchParserKeyWordOffset | SearchParserTextOffset) & { 20 | offsetStart: number; 21 | offsetEnd: number; 22 | } 23 | 24 | export type SearchParserKeyWordOffset = { 25 | keyword: string; 26 | value?: string; 27 | 28 | } 29 | 30 | export type SearchParserTextOffset = { 31 | text: string; 32 | } 33 | 34 | export interface SearchParserResult extends ISearchParserDictionary { 35 | text?: string | string[]; 36 | offsets?: SearchParserOffset[]; 37 | exclude?: ISearchParserDictionary; 38 | } 39 | 40 | export function parse(string: string, options?: SearchParserOptions & { 41 | tokenize: false 42 | }): string; 43 | export function parse(string: string, options?: SearchParserOptions & { 44 | tokenize: true 45 | }): SearchParserResult & { 46 | text?: string[] 47 | }; 48 | export function parse(string: string, options?: SearchParserOptions): string | SearchParserResult; 49 | 50 | export function stringify(searchParserResult: string | SearchParserResult, options?: SearchParserOptions): string; 51 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./lib/search-query-parser'); 2 | -------------------------------------------------------------------------------- /lib/search-query-parser.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * search-query-parser.js 3 | * Copyright(c) 2014-2019 4 | * MIT Licensed 5 | */ 6 | 7 | exports.parse = function (string, options) { 8 | 9 | // Set a default options object when none is provided 10 | if (!options) { 11 | options = {offsets: true}; 12 | } else { 13 | // If options offsets was't passed, set it to true 14 | options.offsets = (typeof options.offsets === 'undefined' ? true : options.offsets) 15 | } 16 | 17 | if (!string) { 18 | string = ''; 19 | } 20 | 21 | // When a simple string, return it 22 | if (-1 === string.indexOf(':') && !options.tokenize) { 23 | return string; 24 | } 25 | // When no keywords or ranges set, treat as a simple string 26 | else if (!options.keywords && !options.ranges && !options.tokenize){ 27 | return string; 28 | } 29 | // Otherwise parse the advanced query syntax 30 | else { 31 | // Our object to store the query object 32 | var query = {text: []}; 33 | // When offsets is true, create their array 34 | if (options.offsets) { 35 | query.offsets = []; 36 | } 37 | var exclusion = {}; 38 | var terms = []; 39 | // Get a list of search terms respecting single and double quotes 40 | var regex = /(\S+:'(?:[^'\\]|\\.)*')|(\S+:"(?:[^"\\]|\\.)*")|(-?"(?:[^"\\]|\\.)*")|(-?'(?:[^'\\]|\\.)*')|\S+|\S+:\S+/g; 41 | var match; 42 | while ((match = regex.exec(string)) !== null) { 43 | var term = match[0]; 44 | var sepIndex = term.indexOf(':'); 45 | if (sepIndex !== -1) { 46 | var split = term.split(':'), 47 | key = term.slice(0, sepIndex), 48 | val = term.slice(sepIndex + 1); 49 | // Strip surrounding quotes 50 | val = val.replace(/^\"|\"$|^\'|\'$/g, ''); 51 | // Strip backslashes respecting escapes 52 | val = (val + '').replace(/\\(.?)/g, function (s, n1) { 53 | switch (n1) { 54 | case '\\': 55 | return '\\'; 56 | case '0': 57 | return '\u0000'; 58 | case '': 59 | return ''; 60 | default: 61 | return n1; 62 | } 63 | }); 64 | terms.push({ 65 | keyword: key, 66 | value: val, 67 | offsetStart: match.index, 68 | offsetEnd: match.index + term.length 69 | }); 70 | } else { 71 | var isExcludedTerm = false; 72 | if (term[0] === '-') { 73 | isExcludedTerm = true; 74 | term = term.slice(1); 75 | } 76 | 77 | // Strip surrounding quotes 78 | term = term.replace(/^\"|\"$|^\'|\'$/g, ''); 79 | // Strip backslashes respecting escapes 80 | term = (term + '').replace(/\\(.?)/g, function (s, n1) { 81 | switch (n1) { 82 | case '\\': 83 | return '\\'; 84 | case '0': 85 | return '\u0000'; 86 | case '': 87 | return ''; 88 | default: 89 | return n1; 90 | } 91 | }); 92 | 93 | if (isExcludedTerm) { 94 | if (exclusion['text']) { 95 | if (exclusion['text'] instanceof Array) { 96 | exclusion['text'].push(term); 97 | } else { 98 | exclusion['text'] = [exclusion['text']]; 99 | exclusion['text'].push(term); 100 | } 101 | } else { 102 | // First time seeing an excluded text term 103 | exclusion['text'] = term; 104 | } 105 | } else { 106 | terms.push({ 107 | text: term, 108 | offsetStart: match.index, 109 | offsetEnd: match.index + term.length 110 | }); 111 | } 112 | } 113 | } 114 | // Reverse to ensure proper order when pop()'ing. 115 | terms.reverse(); 116 | // For each search term 117 | var term; 118 | while (term = terms.pop()) { 119 | // When just a simple term 120 | if (term.text) { 121 | // We add it as pure text 122 | query.text.push(term.text); 123 | // When offsets is true, push a new offset 124 | if (options.offsets) { 125 | query.offsets.push(term); 126 | } 127 | } 128 | // We got an advanced search syntax 129 | else { 130 | var key = term.keyword; 131 | // Check if the key is a registered keyword 132 | options.keywords = options.keywords || []; 133 | var isKeyword = false; 134 | var isExclusion = false; 135 | if (!/^-/.test(key)) { 136 | isKeyword = !(-1 === options.keywords.indexOf(key)); 137 | } else if (key[0] === '-') { 138 | var _key = key.slice(1); 139 | isKeyword = !(-1 === options.keywords.indexOf(_key)) 140 | if (isKeyword) { 141 | key = _key; 142 | isExclusion = true; 143 | } 144 | } 145 | 146 | // Check if the key is a registered range 147 | options.ranges = options.ranges || []; 148 | var isRange = !(-1 === options.ranges.indexOf(key)); 149 | // When the key matches a keyword 150 | if (isKeyword) { 151 | // When offsets is true, push a new offset 152 | if (options.offsets) { 153 | query.offsets.push({ 154 | keyword: key, 155 | value: term.value, 156 | offsetStart: isExclusion ? term.offsetStart + 1 : term.offsetStart, 157 | offsetEnd: term.offsetEnd 158 | }); 159 | } 160 | 161 | var value = term.value; 162 | // When value is a thing 163 | if (value.length) { 164 | // Get an array of values when several are there 165 | var values = value.split(','); 166 | if (isExclusion) { 167 | if (exclusion[key]) { 168 | // ...many times... 169 | if (exclusion[key] instanceof Array) { 170 | // ...and got several values this time... 171 | if (values.length > 1) { 172 | // ... concatenate both arrays. 173 | exclusion[key] = exclusion[key].concat(values); 174 | } 175 | else { 176 | // ... append the current single value. 177 | exclusion[key].push(value); 178 | } 179 | } 180 | // We saw that keyword only once before 181 | else { 182 | // Put both the current value and the new 183 | // value in an array 184 | exclusion[key] = [exclusion[key]]; 185 | exclusion[key].push(value); 186 | } 187 | } 188 | // First time we see that keyword 189 | else { 190 | // ...and got several values this time... 191 | if (values.length > 1) { 192 | // ...add all values seen. 193 | exclusion[key] = values; 194 | } 195 | // Got only a single value this time 196 | else { 197 | // Record its value as a string 198 | if (options.alwaysArray) { 199 | // ...but we always return an array if option alwaysArray is true 200 | exclusion[key] = [value]; 201 | } else { 202 | // Record its value as a string 203 | exclusion[key] = value; 204 | } 205 | } 206 | } 207 | } else { 208 | // If we already have seen that keyword... 209 | if (query[key]) { 210 | // ...many times... 211 | if (query[key] instanceof Array) { 212 | // ...and got several values this time... 213 | if (values.length > 1) { 214 | // ... concatenate both arrays. 215 | query[key] = query[key].concat(values); 216 | } 217 | else { 218 | // ... append the current single value. 219 | query[key].push(value); 220 | } 221 | } 222 | // We saw that keyword only once before 223 | else { 224 | // Put both the current value and the new 225 | // value in an array 226 | query[key] = [query[key]]; 227 | query[key].push(value); 228 | } 229 | } 230 | // First time we see that keyword 231 | else { 232 | // ...and got several values this time... 233 | if (values.length > 1) { 234 | // ...add all values seen. 235 | query[key] = values; 236 | } 237 | // Got only a single value this time 238 | else { 239 | if (options.alwaysArray) { 240 | // ...but we always return an array if option alwaysArray is true 241 | query[key] = [value]; 242 | } else { 243 | // Record its value as a string 244 | query[key] = value; 245 | } 246 | } 247 | } 248 | } 249 | } 250 | } 251 | // The key allows a range 252 | else if (isRange) { 253 | // When offsets is true, push a new offset 254 | if (options.offsets) { 255 | query.offsets.push(term); 256 | } 257 | 258 | var value = term.value; 259 | // Range are separated with a dash 260 | var rangeValues = value.split('-'); 261 | // When both end of the range are specified 262 | // keyword:XXXX-YYYY 263 | query[key] = {}; 264 | if (2 === rangeValues.length) { 265 | query[key].from = rangeValues[0]; 266 | query[key].to = rangeValues[1]; 267 | } 268 | // When pairs of ranges are specified 269 | // keyword:XXXX-YYYY,AAAA-BBBB 270 | else if (!rangeValues.length % 2) { 271 | } 272 | // When only getting a single value, 273 | // or an odd number of values 274 | else { 275 | query[key].from = value; 276 | } 277 | } 278 | else { 279 | // We add it as pure text 280 | var text = term.keyword + ':' + term.value; 281 | query.text.push(text); 282 | 283 | // When offsets is true, push a new offset 284 | if (options.offsets) { 285 | query.offsets.push({ 286 | text: text, 287 | offsetStart: term.offsetStart, 288 | offsetEnd: term.offsetEnd 289 | }); 290 | } 291 | } 292 | } 293 | } 294 | 295 | // Concatenate all text terms if any 296 | if (query.text.length) { 297 | if (!options.tokenize) { 298 | query.text = query.text.join(' ').trim(); 299 | } 300 | } 301 | // Just remove the attribute text when it's empty 302 | else { 303 | delete query.text; 304 | } 305 | 306 | // Return forged query object 307 | query.exclude = exclusion; 308 | return query; 309 | } 310 | 311 | }; 312 | 313 | exports.stringify = function (queryObject, options, prefix) { 314 | 315 | // Set a default options object when none is provided 316 | if (!options) { 317 | options = {offsets: true}; 318 | } 319 | 320 | // If the query object is falsy we can just return an empty string 321 | if (!queryObject) { 322 | return ''; 323 | } 324 | 325 | // If the query object is already a string, we can return it immediately 326 | if (typeof queryObject === 'string') { 327 | return queryObject; 328 | } 329 | 330 | // If the query object is an array, we can return it concatenated with a space 331 | if (Array.isArray(queryObject)) { 332 | return queryObject.join(' '); 333 | } 334 | 335 | // If the query object does not have any keys, we can return an empty string 336 | if (!Object.keys(queryObject).length) { 337 | return ''; 338 | } 339 | 340 | // If the query object contains only text which is a string, we can return it immediately 341 | if (Object.keys(queryObject).length === 3 && !!queryObject.text && !!queryObject.offsets && !!queryObject.exclude && typeof queryObject.text === 'string') { 342 | return queryObject.text; 343 | } 344 | 345 | // We will use a prefix for the exclude syntax later one 346 | if (!prefix) { 347 | prefix = '' 348 | } 349 | 350 | // Helpers 351 | var addQuotes = function (string) { 352 | return string.indexOf(' ') > - 1 ? JSON.stringify(string) : string; 353 | }; 354 | var addPrefix = function (string) { 355 | return prefix + string; 356 | }; 357 | 358 | // Keep track of all single parts in this array 359 | var parts = []; 360 | 361 | // Text 362 | if (queryObject.text) { 363 | var value = []; 364 | if (typeof queryObject.text === 'string') { 365 | value.push(queryObject.text); 366 | } else { 367 | value.push.apply(value, queryObject.text); 368 | } 369 | 370 | if (value.length > 0) { 371 | parts.push(value.map(addQuotes).map(addPrefix).join(' ')); 372 | } 373 | } 374 | 375 | // Keywords 376 | if (options.keywords) { 377 | options.keywords.forEach(function (keyword) { 378 | if (!queryObject[keyword]) { 379 | return 380 | } 381 | 382 | var value = []; 383 | if (typeof queryObject[keyword] === 'string') { 384 | value.push(queryObject[keyword]); 385 | } else { 386 | value.push.apply(value, queryObject[keyword]); 387 | } 388 | 389 | if (value.length > 0) { 390 | parts.push(addPrefix(keyword + ':' + value.map(addQuotes).join(','))); 391 | } 392 | }); 393 | } 394 | 395 | // Ranges 396 | if (options.ranges) { 397 | options.ranges.forEach(function (range) { 398 | if (!queryObject[range]) { 399 | return 400 | } 401 | 402 | var value = queryObject[range].from; 403 | var to = queryObject[range].to; 404 | if (to) { 405 | value = value + '-' + to; 406 | } 407 | 408 | if (value) { 409 | parts.push(addPrefix(range + ':' + value)); 410 | } 411 | }); 412 | } 413 | 414 | // Exclude 415 | if (queryObject.exclude) { 416 | if (Object.keys(queryObject.exclude).length > 0) { 417 | parts.push(exports.stringify(queryObject.exclude, options, '-')); 418 | } 419 | } 420 | 421 | return parts.join(' '); 422 | 423 | }; 424 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "search-query-parser", 3 | "version": "1.6.0", 4 | "lockfileVersion": 2, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "search-query-parser", 9 | "version": "1.6.0", 10 | "license": "MIT", 11 | "devDependencies": { 12 | "mocha": "^10.2.0", 13 | "should": "^13.2.3", 14 | "typescript": "^4.2.2" 15 | } 16 | }, 17 | "node_modules/ansi-colors": { 18 | "version": "4.1.1", 19 | "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", 20 | "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", 21 | "dev": true, 22 | "engines": { 23 | "node": ">=6" 24 | } 25 | }, 26 | "node_modules/ansi-regex": { 27 | "version": "5.0.1", 28 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", 29 | "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", 30 | "dev": true, 31 | "engines": { 32 | "node": ">=8" 33 | } 34 | }, 35 | "node_modules/ansi-styles": { 36 | "version": "4.3.0", 37 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", 38 | "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", 39 | "dev": true, 40 | "dependencies": { 41 | "color-convert": "^2.0.1" 42 | }, 43 | "engines": { 44 | "node": ">=8" 45 | }, 46 | "funding": { 47 | "url": "https://github.com/chalk/ansi-styles?sponsor=1" 48 | } 49 | }, 50 | "node_modules/anymatch": { 51 | "version": "3.1.2", 52 | "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", 53 | "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", 54 | "dev": true, 55 | "dependencies": { 56 | "normalize-path": "^3.0.0", 57 | "picomatch": "^2.0.4" 58 | }, 59 | "engines": { 60 | "node": ">= 8" 61 | } 62 | }, 63 | "node_modules/argparse": { 64 | "version": "2.0.1", 65 | "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", 66 | "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", 67 | "dev": true 68 | }, 69 | "node_modules/balanced-match": { 70 | "version": "1.0.2", 71 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", 72 | "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", 73 | "dev": true 74 | }, 75 | "node_modules/binary-extensions": { 76 | "version": "2.2.0", 77 | "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", 78 | "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", 79 | "dev": true, 80 | "engines": { 81 | "node": ">=8" 82 | } 83 | }, 84 | "node_modules/brace-expansion": { 85 | "version": "1.1.11", 86 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 87 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 88 | "dev": true, 89 | "dependencies": { 90 | "balanced-match": "^1.0.0", 91 | "concat-map": "0.0.1" 92 | } 93 | }, 94 | "node_modules/braces": { 95 | "version": "3.0.2", 96 | "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", 97 | "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", 98 | "dev": true, 99 | "dependencies": { 100 | "fill-range": "^7.0.1" 101 | }, 102 | "engines": { 103 | "node": ">=8" 104 | } 105 | }, 106 | "node_modules/browser-stdout": { 107 | "version": "1.3.1", 108 | "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", 109 | "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", 110 | "dev": true 111 | }, 112 | "node_modules/camelcase": { 113 | "version": "6.3.0", 114 | "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", 115 | "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", 116 | "dev": true, 117 | "engines": { 118 | "node": ">=10" 119 | }, 120 | "funding": { 121 | "url": "https://github.com/sponsors/sindresorhus" 122 | } 123 | }, 124 | "node_modules/chalk": { 125 | "version": "4.1.2", 126 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", 127 | "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", 128 | "dev": true, 129 | "dependencies": { 130 | "ansi-styles": "^4.1.0", 131 | "supports-color": "^7.1.0" 132 | }, 133 | "engines": { 134 | "node": ">=10" 135 | }, 136 | "funding": { 137 | "url": "https://github.com/chalk/chalk?sponsor=1" 138 | } 139 | }, 140 | "node_modules/chalk/node_modules/supports-color": { 141 | "version": "7.2.0", 142 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", 143 | "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", 144 | "dev": true, 145 | "dependencies": { 146 | "has-flag": "^4.0.0" 147 | }, 148 | "engines": { 149 | "node": ">=8" 150 | } 151 | }, 152 | "node_modules/chokidar": { 153 | "version": "3.5.3", 154 | "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", 155 | "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", 156 | "dev": true, 157 | "funding": [ 158 | { 159 | "type": "individual", 160 | "url": "https://paulmillr.com/funding/" 161 | } 162 | ], 163 | "dependencies": { 164 | "anymatch": "~3.1.2", 165 | "braces": "~3.0.2", 166 | "glob-parent": "~5.1.2", 167 | "is-binary-path": "~2.1.0", 168 | "is-glob": "~4.0.1", 169 | "normalize-path": "~3.0.0", 170 | "readdirp": "~3.6.0" 171 | }, 172 | "engines": { 173 | "node": ">= 8.10.0" 174 | }, 175 | "optionalDependencies": { 176 | "fsevents": "~2.3.2" 177 | } 178 | }, 179 | "node_modules/cliui": { 180 | "version": "7.0.4", 181 | "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", 182 | "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", 183 | "dev": true, 184 | "dependencies": { 185 | "string-width": "^4.2.0", 186 | "strip-ansi": "^6.0.0", 187 | "wrap-ansi": "^7.0.0" 188 | } 189 | }, 190 | "node_modules/color-convert": { 191 | "version": "2.0.1", 192 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", 193 | "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", 194 | "dev": true, 195 | "dependencies": { 196 | "color-name": "~1.1.4" 197 | }, 198 | "engines": { 199 | "node": ">=7.0.0" 200 | } 201 | }, 202 | "node_modules/color-name": { 203 | "version": "1.1.4", 204 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", 205 | "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", 206 | "dev": true 207 | }, 208 | "node_modules/concat-map": { 209 | "version": "0.0.1", 210 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 211 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", 212 | "dev": true 213 | }, 214 | "node_modules/debug": { 215 | "version": "4.3.4", 216 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", 217 | "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", 218 | "dev": true, 219 | "dependencies": { 220 | "ms": "2.1.2" 221 | }, 222 | "engines": { 223 | "node": ">=6.0" 224 | }, 225 | "peerDependenciesMeta": { 226 | "supports-color": { 227 | "optional": true 228 | } 229 | } 230 | }, 231 | "node_modules/debug/node_modules/ms": { 232 | "version": "2.1.2", 233 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 234 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", 235 | "dev": true 236 | }, 237 | "node_modules/decamelize": { 238 | "version": "4.0.0", 239 | "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", 240 | "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", 241 | "dev": true, 242 | "engines": { 243 | "node": ">=10" 244 | }, 245 | "funding": { 246 | "url": "https://github.com/sponsors/sindresorhus" 247 | } 248 | }, 249 | "node_modules/diff": { 250 | "version": "5.0.0", 251 | "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", 252 | "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", 253 | "dev": true, 254 | "engines": { 255 | "node": ">=0.3.1" 256 | } 257 | }, 258 | "node_modules/emoji-regex": { 259 | "version": "8.0.0", 260 | "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", 261 | "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", 262 | "dev": true 263 | }, 264 | "node_modules/escalade": { 265 | "version": "3.1.1", 266 | "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", 267 | "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", 268 | "dev": true, 269 | "engines": { 270 | "node": ">=6" 271 | } 272 | }, 273 | "node_modules/escape-string-regexp": { 274 | "version": "4.0.0", 275 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", 276 | "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", 277 | "dev": true, 278 | "engines": { 279 | "node": ">=10" 280 | }, 281 | "funding": { 282 | "url": "https://github.com/sponsors/sindresorhus" 283 | } 284 | }, 285 | "node_modules/fill-range": { 286 | "version": "7.0.1", 287 | "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", 288 | "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", 289 | "dev": true, 290 | "dependencies": { 291 | "to-regex-range": "^5.0.1" 292 | }, 293 | "engines": { 294 | "node": ">=8" 295 | } 296 | }, 297 | "node_modules/find-up": { 298 | "version": "5.0.0", 299 | "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", 300 | "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", 301 | "dev": true, 302 | "dependencies": { 303 | "locate-path": "^6.0.0", 304 | "path-exists": "^4.0.0" 305 | }, 306 | "engines": { 307 | "node": ">=10" 308 | }, 309 | "funding": { 310 | "url": "https://github.com/sponsors/sindresorhus" 311 | } 312 | }, 313 | "node_modules/flat": { 314 | "version": "5.0.2", 315 | "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", 316 | "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", 317 | "dev": true, 318 | "bin": { 319 | "flat": "cli.js" 320 | } 321 | }, 322 | "node_modules/fs.realpath": { 323 | "version": "1.0.0", 324 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 325 | "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", 326 | "dev": true 327 | }, 328 | "node_modules/fsevents": { 329 | "version": "2.3.2", 330 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", 331 | "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", 332 | "dev": true, 333 | "hasInstallScript": true, 334 | "optional": true, 335 | "os": [ 336 | "darwin" 337 | ], 338 | "engines": { 339 | "node": "^8.16.0 || ^10.6.0 || >=11.0.0" 340 | } 341 | }, 342 | "node_modules/get-caller-file": { 343 | "version": "2.0.5", 344 | "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", 345 | "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", 346 | "dev": true, 347 | "engines": { 348 | "node": "6.* || 8.* || >= 10.*" 349 | } 350 | }, 351 | "node_modules/glob": { 352 | "version": "7.2.0", 353 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", 354 | "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", 355 | "dev": true, 356 | "dependencies": { 357 | "fs.realpath": "^1.0.0", 358 | "inflight": "^1.0.4", 359 | "inherits": "2", 360 | "minimatch": "^3.0.4", 361 | "once": "^1.3.0", 362 | "path-is-absolute": "^1.0.0" 363 | }, 364 | "engines": { 365 | "node": "*" 366 | }, 367 | "funding": { 368 | "url": "https://github.com/sponsors/isaacs" 369 | } 370 | }, 371 | "node_modules/glob-parent": { 372 | "version": "5.1.2", 373 | "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", 374 | "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", 375 | "dev": true, 376 | "dependencies": { 377 | "is-glob": "^4.0.1" 378 | }, 379 | "engines": { 380 | "node": ">= 6" 381 | } 382 | }, 383 | "node_modules/has-flag": { 384 | "version": "4.0.0", 385 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", 386 | "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", 387 | "dev": true, 388 | "engines": { 389 | "node": ">=8" 390 | } 391 | }, 392 | "node_modules/he": { 393 | "version": "1.2.0", 394 | "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", 395 | "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", 396 | "dev": true, 397 | "bin": { 398 | "he": "bin/he" 399 | } 400 | }, 401 | "node_modules/inflight": { 402 | "version": "1.0.6", 403 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 404 | "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", 405 | "dev": true, 406 | "dependencies": { 407 | "once": "^1.3.0", 408 | "wrappy": "1" 409 | } 410 | }, 411 | "node_modules/inherits": { 412 | "version": "2.0.4", 413 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 414 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", 415 | "dev": true 416 | }, 417 | "node_modules/is-binary-path": { 418 | "version": "2.1.0", 419 | "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", 420 | "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", 421 | "dev": true, 422 | "dependencies": { 423 | "binary-extensions": "^2.0.0" 424 | }, 425 | "engines": { 426 | "node": ">=8" 427 | } 428 | }, 429 | "node_modules/is-extglob": { 430 | "version": "2.1.1", 431 | "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", 432 | "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", 433 | "dev": true, 434 | "engines": { 435 | "node": ">=0.10.0" 436 | } 437 | }, 438 | "node_modules/is-fullwidth-code-point": { 439 | "version": "3.0.0", 440 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", 441 | "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", 442 | "dev": true, 443 | "engines": { 444 | "node": ">=8" 445 | } 446 | }, 447 | "node_modules/is-glob": { 448 | "version": "4.0.3", 449 | "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", 450 | "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", 451 | "dev": true, 452 | "dependencies": { 453 | "is-extglob": "^2.1.1" 454 | }, 455 | "engines": { 456 | "node": ">=0.10.0" 457 | } 458 | }, 459 | "node_modules/is-number": { 460 | "version": "7.0.0", 461 | "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", 462 | "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", 463 | "dev": true, 464 | "engines": { 465 | "node": ">=0.12.0" 466 | } 467 | }, 468 | "node_modules/is-plain-obj": { 469 | "version": "2.1.0", 470 | "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", 471 | "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", 472 | "dev": true, 473 | "engines": { 474 | "node": ">=8" 475 | } 476 | }, 477 | "node_modules/is-unicode-supported": { 478 | "version": "0.1.0", 479 | "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", 480 | "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", 481 | "dev": true, 482 | "engines": { 483 | "node": ">=10" 484 | }, 485 | "funding": { 486 | "url": "https://github.com/sponsors/sindresorhus" 487 | } 488 | }, 489 | "node_modules/js-yaml": { 490 | "version": "4.1.0", 491 | "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", 492 | "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", 493 | "dev": true, 494 | "dependencies": { 495 | "argparse": "^2.0.1" 496 | }, 497 | "bin": { 498 | "js-yaml": "bin/js-yaml.js" 499 | } 500 | }, 501 | "node_modules/locate-path": { 502 | "version": "6.0.0", 503 | "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", 504 | "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", 505 | "dev": true, 506 | "dependencies": { 507 | "p-locate": "^5.0.0" 508 | }, 509 | "engines": { 510 | "node": ">=10" 511 | }, 512 | "funding": { 513 | "url": "https://github.com/sponsors/sindresorhus" 514 | } 515 | }, 516 | "node_modules/log-symbols": { 517 | "version": "4.1.0", 518 | "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", 519 | "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", 520 | "dev": true, 521 | "dependencies": { 522 | "chalk": "^4.1.0", 523 | "is-unicode-supported": "^0.1.0" 524 | }, 525 | "engines": { 526 | "node": ">=10" 527 | }, 528 | "funding": { 529 | "url": "https://github.com/sponsors/sindresorhus" 530 | } 531 | }, 532 | "node_modules/minimatch": { 533 | "version": "3.1.2", 534 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", 535 | "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", 536 | "dev": true, 537 | "dependencies": { 538 | "brace-expansion": "^1.1.7" 539 | }, 540 | "engines": { 541 | "node": "*" 542 | } 543 | }, 544 | "node_modules/mocha": { 545 | "version": "10.2.0", 546 | "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.2.0.tgz", 547 | "integrity": "sha512-IDY7fl/BecMwFHzoqF2sg/SHHANeBoMMXFlS9r0OXKDssYE1M5O43wUY/9BVPeIvfH2zmEbBfseqN9gBQZzXkg==", 548 | "dev": true, 549 | "dependencies": { 550 | "ansi-colors": "4.1.1", 551 | "browser-stdout": "1.3.1", 552 | "chokidar": "3.5.3", 553 | "debug": "4.3.4", 554 | "diff": "5.0.0", 555 | "escape-string-regexp": "4.0.0", 556 | "find-up": "5.0.0", 557 | "glob": "7.2.0", 558 | "he": "1.2.0", 559 | "js-yaml": "4.1.0", 560 | "log-symbols": "4.1.0", 561 | "minimatch": "5.0.1", 562 | "ms": "2.1.3", 563 | "nanoid": "3.3.3", 564 | "serialize-javascript": "6.0.0", 565 | "strip-json-comments": "3.1.1", 566 | "supports-color": "8.1.1", 567 | "workerpool": "6.2.1", 568 | "yargs": "16.2.0", 569 | "yargs-parser": "20.2.4", 570 | "yargs-unparser": "2.0.0" 571 | }, 572 | "bin": { 573 | "_mocha": "bin/_mocha", 574 | "mocha": "bin/mocha.js" 575 | }, 576 | "engines": { 577 | "node": ">= 14.0.0" 578 | }, 579 | "funding": { 580 | "type": "opencollective", 581 | "url": "https://opencollective.com/mochajs" 582 | } 583 | }, 584 | "node_modules/mocha/node_modules/brace-expansion": { 585 | "version": "2.0.1", 586 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", 587 | "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", 588 | "dev": true, 589 | "dependencies": { 590 | "balanced-match": "^1.0.0" 591 | } 592 | }, 593 | "node_modules/mocha/node_modules/minimatch": { 594 | "version": "5.0.1", 595 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz", 596 | "integrity": "sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==", 597 | "dev": true, 598 | "dependencies": { 599 | "brace-expansion": "^2.0.1" 600 | }, 601 | "engines": { 602 | "node": ">=10" 603 | } 604 | }, 605 | "node_modules/ms": { 606 | "version": "2.1.3", 607 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", 608 | "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", 609 | "dev": true 610 | }, 611 | "node_modules/nanoid": { 612 | "version": "3.3.3", 613 | "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.3.tgz", 614 | "integrity": "sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w==", 615 | "dev": true, 616 | "bin": { 617 | "nanoid": "bin/nanoid.cjs" 618 | }, 619 | "engines": { 620 | "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" 621 | } 622 | }, 623 | "node_modules/normalize-path": { 624 | "version": "3.0.0", 625 | "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", 626 | "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", 627 | "dev": true, 628 | "engines": { 629 | "node": ">=0.10.0" 630 | } 631 | }, 632 | "node_modules/once": { 633 | "version": "1.4.0", 634 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 635 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", 636 | "dev": true, 637 | "dependencies": { 638 | "wrappy": "1" 639 | } 640 | }, 641 | "node_modules/p-limit": { 642 | "version": "3.1.0", 643 | "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", 644 | "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", 645 | "dev": true, 646 | "dependencies": { 647 | "yocto-queue": "^0.1.0" 648 | }, 649 | "engines": { 650 | "node": ">=10" 651 | }, 652 | "funding": { 653 | "url": "https://github.com/sponsors/sindresorhus" 654 | } 655 | }, 656 | "node_modules/p-locate": { 657 | "version": "5.0.0", 658 | "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", 659 | "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", 660 | "dev": true, 661 | "dependencies": { 662 | "p-limit": "^3.0.2" 663 | }, 664 | "engines": { 665 | "node": ">=10" 666 | }, 667 | "funding": { 668 | "url": "https://github.com/sponsors/sindresorhus" 669 | } 670 | }, 671 | "node_modules/path-exists": { 672 | "version": "4.0.0", 673 | "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", 674 | "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", 675 | "dev": true, 676 | "engines": { 677 | "node": ">=8" 678 | } 679 | }, 680 | "node_modules/path-is-absolute": { 681 | "version": "1.0.1", 682 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 683 | "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", 684 | "dev": true, 685 | "engines": { 686 | "node": ">=0.10.0" 687 | } 688 | }, 689 | "node_modules/picomatch": { 690 | "version": "2.3.1", 691 | "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", 692 | "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", 693 | "dev": true, 694 | "engines": { 695 | "node": ">=8.6" 696 | }, 697 | "funding": { 698 | "url": "https://github.com/sponsors/jonschlinkert" 699 | } 700 | }, 701 | "node_modules/randombytes": { 702 | "version": "2.1.0", 703 | "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", 704 | "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", 705 | "dev": true, 706 | "dependencies": { 707 | "safe-buffer": "^5.1.0" 708 | } 709 | }, 710 | "node_modules/readdirp": { 711 | "version": "3.6.0", 712 | "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", 713 | "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", 714 | "dev": true, 715 | "dependencies": { 716 | "picomatch": "^2.2.1" 717 | }, 718 | "engines": { 719 | "node": ">=8.10.0" 720 | } 721 | }, 722 | "node_modules/require-directory": { 723 | "version": "2.1.1", 724 | "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", 725 | "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", 726 | "dev": true, 727 | "engines": { 728 | "node": ">=0.10.0" 729 | } 730 | }, 731 | "node_modules/safe-buffer": { 732 | "version": "5.2.1", 733 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", 734 | "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", 735 | "dev": true, 736 | "funding": [ 737 | { 738 | "type": "github", 739 | "url": "https://github.com/sponsors/feross" 740 | }, 741 | { 742 | "type": "patreon", 743 | "url": "https://www.patreon.com/feross" 744 | }, 745 | { 746 | "type": "consulting", 747 | "url": "https://feross.org/support" 748 | } 749 | ] 750 | }, 751 | "node_modules/serialize-javascript": { 752 | "version": "6.0.0", 753 | "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", 754 | "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", 755 | "dev": true, 756 | "dependencies": { 757 | "randombytes": "^2.1.0" 758 | } 759 | }, 760 | "node_modules/should": { 761 | "version": "13.2.3", 762 | "resolved": "https://registry.npmjs.org/should/-/should-13.2.3.tgz", 763 | "integrity": "sha512-ggLesLtu2xp+ZxI+ysJTmNjh2U0TsC+rQ/pfED9bUZZ4DKefP27D+7YJVVTvKsmjLpIi9jAa7itwDGkDDmt1GQ==", 764 | "dev": true, 765 | "dependencies": { 766 | "should-equal": "^2.0.0", 767 | "should-format": "^3.0.3", 768 | "should-type": "^1.4.0", 769 | "should-type-adaptors": "^1.0.1", 770 | "should-util": "^1.0.0" 771 | } 772 | }, 773 | "node_modules/should-equal": { 774 | "version": "2.0.0", 775 | "resolved": "https://registry.npmjs.org/should-equal/-/should-equal-2.0.0.tgz", 776 | "integrity": "sha512-ZP36TMrK9euEuWQYBig9W55WPC7uo37qzAEmbjHz4gfyuXrEUgF8cUvQVO+w+d3OMfPvSRQJ22lSm8MQJ43LTA==", 777 | "dev": true, 778 | "dependencies": { 779 | "should-type": "^1.4.0" 780 | } 781 | }, 782 | "node_modules/should-format": { 783 | "version": "3.0.3", 784 | "resolved": "https://registry.npmjs.org/should-format/-/should-format-3.0.3.tgz", 785 | "integrity": "sha1-m/yPdPo5IFxT04w01xcwPidxJPE=", 786 | "dev": true, 787 | "dependencies": { 788 | "should-type": "^1.3.0", 789 | "should-type-adaptors": "^1.0.1" 790 | } 791 | }, 792 | "node_modules/should-type": { 793 | "version": "1.4.0", 794 | "resolved": "https://registry.npmjs.org/should-type/-/should-type-1.4.0.tgz", 795 | "integrity": "sha1-B1bYzoRt/QmEOmlHcZ36DUz/XPM=", 796 | "dev": true 797 | }, 798 | "node_modules/should-type-adaptors": { 799 | "version": "1.1.0", 800 | "resolved": "https://registry.npmjs.org/should-type-adaptors/-/should-type-adaptors-1.1.0.tgz", 801 | "integrity": "sha512-JA4hdoLnN+kebEp2Vs8eBe9g7uy0zbRo+RMcU0EsNy+R+k049Ki+N5tT5Jagst2g7EAja+euFuoXFCa8vIklfA==", 802 | "dev": true, 803 | "dependencies": { 804 | "should-type": "^1.3.0", 805 | "should-util": "^1.0.0" 806 | } 807 | }, 808 | "node_modules/should-util": { 809 | "version": "1.0.1", 810 | "resolved": "https://registry.npmjs.org/should-util/-/should-util-1.0.1.tgz", 811 | "integrity": "sha512-oXF8tfxx5cDk8r2kYqlkUJzZpDBqVY/II2WhvU0n9Y3XYvAYRmeaf1PvvIvTgPnv4KJ+ES5M0PyDq5Jp+Ygy2g==", 812 | "dev": true 813 | }, 814 | "node_modules/string-width": { 815 | "version": "4.2.3", 816 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", 817 | "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", 818 | "dev": true, 819 | "dependencies": { 820 | "emoji-regex": "^8.0.0", 821 | "is-fullwidth-code-point": "^3.0.0", 822 | "strip-ansi": "^6.0.1" 823 | }, 824 | "engines": { 825 | "node": ">=8" 826 | } 827 | }, 828 | "node_modules/strip-ansi": { 829 | "version": "6.0.1", 830 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", 831 | "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", 832 | "dev": true, 833 | "dependencies": { 834 | "ansi-regex": "^5.0.1" 835 | }, 836 | "engines": { 837 | "node": ">=8" 838 | } 839 | }, 840 | "node_modules/strip-json-comments": { 841 | "version": "3.1.1", 842 | "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", 843 | "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", 844 | "dev": true, 845 | "engines": { 846 | "node": ">=8" 847 | }, 848 | "funding": { 849 | "url": "https://github.com/sponsors/sindresorhus" 850 | } 851 | }, 852 | "node_modules/supports-color": { 853 | "version": "8.1.1", 854 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", 855 | "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", 856 | "dev": true, 857 | "dependencies": { 858 | "has-flag": "^4.0.0" 859 | }, 860 | "engines": { 861 | "node": ">=10" 862 | }, 863 | "funding": { 864 | "url": "https://github.com/chalk/supports-color?sponsor=1" 865 | } 866 | }, 867 | "node_modules/to-regex-range": { 868 | "version": "5.0.1", 869 | "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", 870 | "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", 871 | "dev": true, 872 | "dependencies": { 873 | "is-number": "^7.0.0" 874 | }, 875 | "engines": { 876 | "node": ">=8.0" 877 | } 878 | }, 879 | "node_modules/typescript": { 880 | "version": "4.5.5", 881 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.5.tgz", 882 | "integrity": "sha512-TCTIul70LyWe6IJWT8QSYeA54WQe8EjQFU4wY52Fasj5UKx88LNYKCgBEHcOMOrFF1rKGbD8v/xcNWVUq9SymA==", 883 | "dev": true, 884 | "bin": { 885 | "tsc": "bin/tsc", 886 | "tsserver": "bin/tsserver" 887 | }, 888 | "engines": { 889 | "node": ">=4.2.0" 890 | } 891 | }, 892 | "node_modules/workerpool": { 893 | "version": "6.2.1", 894 | "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz", 895 | "integrity": "sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==", 896 | "dev": true 897 | }, 898 | "node_modules/wrap-ansi": { 899 | "version": "7.0.0", 900 | "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", 901 | "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", 902 | "dev": true, 903 | "dependencies": { 904 | "ansi-styles": "^4.0.0", 905 | "string-width": "^4.1.0", 906 | "strip-ansi": "^6.0.0" 907 | }, 908 | "engines": { 909 | "node": ">=10" 910 | }, 911 | "funding": { 912 | "url": "https://github.com/chalk/wrap-ansi?sponsor=1" 913 | } 914 | }, 915 | "node_modules/wrappy": { 916 | "version": "1.0.2", 917 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 918 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", 919 | "dev": true 920 | }, 921 | "node_modules/y18n": { 922 | "version": "5.0.8", 923 | "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", 924 | "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", 925 | "dev": true, 926 | "engines": { 927 | "node": ">=10" 928 | } 929 | }, 930 | "node_modules/yargs": { 931 | "version": "16.2.0", 932 | "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", 933 | "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", 934 | "dev": true, 935 | "dependencies": { 936 | "cliui": "^7.0.2", 937 | "escalade": "^3.1.1", 938 | "get-caller-file": "^2.0.5", 939 | "require-directory": "^2.1.1", 940 | "string-width": "^4.2.0", 941 | "y18n": "^5.0.5", 942 | "yargs-parser": "^20.2.2" 943 | }, 944 | "engines": { 945 | "node": ">=10" 946 | } 947 | }, 948 | "node_modules/yargs-parser": { 949 | "version": "20.2.4", 950 | "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", 951 | "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", 952 | "dev": true, 953 | "engines": { 954 | "node": ">=10" 955 | } 956 | }, 957 | "node_modules/yargs-unparser": { 958 | "version": "2.0.0", 959 | "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", 960 | "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", 961 | "dev": true, 962 | "dependencies": { 963 | "camelcase": "^6.0.0", 964 | "decamelize": "^4.0.0", 965 | "flat": "^5.0.2", 966 | "is-plain-obj": "^2.1.0" 967 | }, 968 | "engines": { 969 | "node": ">=10" 970 | } 971 | }, 972 | "node_modules/yocto-queue": { 973 | "version": "0.1.0", 974 | "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", 975 | "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", 976 | "dev": true, 977 | "engines": { 978 | "node": ">=10" 979 | }, 980 | "funding": { 981 | "url": "https://github.com/sponsors/sindresorhus" 982 | } 983 | } 984 | }, 985 | "dependencies": { 986 | "ansi-colors": { 987 | "version": "4.1.1", 988 | "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", 989 | "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", 990 | "dev": true 991 | }, 992 | "ansi-regex": { 993 | "version": "5.0.1", 994 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", 995 | "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", 996 | "dev": true 997 | }, 998 | "ansi-styles": { 999 | "version": "4.3.0", 1000 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", 1001 | "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", 1002 | "dev": true, 1003 | "requires": { 1004 | "color-convert": "^2.0.1" 1005 | } 1006 | }, 1007 | "anymatch": { 1008 | "version": "3.1.2", 1009 | "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", 1010 | "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", 1011 | "dev": true, 1012 | "requires": { 1013 | "normalize-path": "^3.0.0", 1014 | "picomatch": "^2.0.4" 1015 | } 1016 | }, 1017 | "argparse": { 1018 | "version": "2.0.1", 1019 | "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", 1020 | "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", 1021 | "dev": true 1022 | }, 1023 | "balanced-match": { 1024 | "version": "1.0.2", 1025 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", 1026 | "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", 1027 | "dev": true 1028 | }, 1029 | "binary-extensions": { 1030 | "version": "2.2.0", 1031 | "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", 1032 | "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", 1033 | "dev": true 1034 | }, 1035 | "brace-expansion": { 1036 | "version": "1.1.11", 1037 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 1038 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 1039 | "dev": true, 1040 | "requires": { 1041 | "balanced-match": "^1.0.0", 1042 | "concat-map": "0.0.1" 1043 | } 1044 | }, 1045 | "braces": { 1046 | "version": "3.0.2", 1047 | "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", 1048 | "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", 1049 | "dev": true, 1050 | "requires": { 1051 | "fill-range": "^7.0.1" 1052 | } 1053 | }, 1054 | "browser-stdout": { 1055 | "version": "1.3.1", 1056 | "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", 1057 | "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", 1058 | "dev": true 1059 | }, 1060 | "camelcase": { 1061 | "version": "6.3.0", 1062 | "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", 1063 | "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", 1064 | "dev": true 1065 | }, 1066 | "chalk": { 1067 | "version": "4.1.2", 1068 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", 1069 | "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", 1070 | "dev": true, 1071 | "requires": { 1072 | "ansi-styles": "^4.1.0", 1073 | "supports-color": "^7.1.0" 1074 | }, 1075 | "dependencies": { 1076 | "supports-color": { 1077 | "version": "7.2.0", 1078 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", 1079 | "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", 1080 | "dev": true, 1081 | "requires": { 1082 | "has-flag": "^4.0.0" 1083 | } 1084 | } 1085 | } 1086 | }, 1087 | "chokidar": { 1088 | "version": "3.5.3", 1089 | "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", 1090 | "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", 1091 | "dev": true, 1092 | "requires": { 1093 | "anymatch": "~3.1.2", 1094 | "braces": "~3.0.2", 1095 | "fsevents": "~2.3.2", 1096 | "glob-parent": "~5.1.2", 1097 | "is-binary-path": "~2.1.0", 1098 | "is-glob": "~4.0.1", 1099 | "normalize-path": "~3.0.0", 1100 | "readdirp": "~3.6.0" 1101 | } 1102 | }, 1103 | "cliui": { 1104 | "version": "7.0.4", 1105 | "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", 1106 | "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", 1107 | "dev": true, 1108 | "requires": { 1109 | "string-width": "^4.2.0", 1110 | "strip-ansi": "^6.0.0", 1111 | "wrap-ansi": "^7.0.0" 1112 | } 1113 | }, 1114 | "color-convert": { 1115 | "version": "2.0.1", 1116 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", 1117 | "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", 1118 | "dev": true, 1119 | "requires": { 1120 | "color-name": "~1.1.4" 1121 | } 1122 | }, 1123 | "color-name": { 1124 | "version": "1.1.4", 1125 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", 1126 | "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", 1127 | "dev": true 1128 | }, 1129 | "concat-map": { 1130 | "version": "0.0.1", 1131 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 1132 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", 1133 | "dev": true 1134 | }, 1135 | "debug": { 1136 | "version": "4.3.4", 1137 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", 1138 | "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", 1139 | "dev": true, 1140 | "requires": { 1141 | "ms": "2.1.2" 1142 | }, 1143 | "dependencies": { 1144 | "ms": { 1145 | "version": "2.1.2", 1146 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 1147 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", 1148 | "dev": true 1149 | } 1150 | } 1151 | }, 1152 | "decamelize": { 1153 | "version": "4.0.0", 1154 | "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", 1155 | "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", 1156 | "dev": true 1157 | }, 1158 | "diff": { 1159 | "version": "5.0.0", 1160 | "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", 1161 | "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", 1162 | "dev": true 1163 | }, 1164 | "emoji-regex": { 1165 | "version": "8.0.0", 1166 | "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", 1167 | "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", 1168 | "dev": true 1169 | }, 1170 | "escalade": { 1171 | "version": "3.1.1", 1172 | "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", 1173 | "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", 1174 | "dev": true 1175 | }, 1176 | "escape-string-regexp": { 1177 | "version": "4.0.0", 1178 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", 1179 | "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", 1180 | "dev": true 1181 | }, 1182 | "fill-range": { 1183 | "version": "7.0.1", 1184 | "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", 1185 | "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", 1186 | "dev": true, 1187 | "requires": { 1188 | "to-regex-range": "^5.0.1" 1189 | } 1190 | }, 1191 | "find-up": { 1192 | "version": "5.0.0", 1193 | "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", 1194 | "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", 1195 | "dev": true, 1196 | "requires": { 1197 | "locate-path": "^6.0.0", 1198 | "path-exists": "^4.0.0" 1199 | } 1200 | }, 1201 | "flat": { 1202 | "version": "5.0.2", 1203 | "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", 1204 | "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", 1205 | "dev": true 1206 | }, 1207 | "fs.realpath": { 1208 | "version": "1.0.0", 1209 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 1210 | "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", 1211 | "dev": true 1212 | }, 1213 | "fsevents": { 1214 | "version": "2.3.2", 1215 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", 1216 | "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", 1217 | "dev": true, 1218 | "optional": true 1219 | }, 1220 | "get-caller-file": { 1221 | "version": "2.0.5", 1222 | "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", 1223 | "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", 1224 | "dev": true 1225 | }, 1226 | "glob": { 1227 | "version": "7.2.0", 1228 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", 1229 | "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", 1230 | "dev": true, 1231 | "requires": { 1232 | "fs.realpath": "^1.0.0", 1233 | "inflight": "^1.0.4", 1234 | "inherits": "2", 1235 | "minimatch": "^3.0.4", 1236 | "once": "^1.3.0", 1237 | "path-is-absolute": "^1.0.0" 1238 | } 1239 | }, 1240 | "glob-parent": { 1241 | "version": "5.1.2", 1242 | "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", 1243 | "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", 1244 | "dev": true, 1245 | "requires": { 1246 | "is-glob": "^4.0.1" 1247 | } 1248 | }, 1249 | "has-flag": { 1250 | "version": "4.0.0", 1251 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", 1252 | "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", 1253 | "dev": true 1254 | }, 1255 | "he": { 1256 | "version": "1.2.0", 1257 | "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", 1258 | "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", 1259 | "dev": true 1260 | }, 1261 | "inflight": { 1262 | "version": "1.0.6", 1263 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 1264 | "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", 1265 | "dev": true, 1266 | "requires": { 1267 | "once": "^1.3.0", 1268 | "wrappy": "1" 1269 | } 1270 | }, 1271 | "inherits": { 1272 | "version": "2.0.4", 1273 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 1274 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", 1275 | "dev": true 1276 | }, 1277 | "is-binary-path": { 1278 | "version": "2.1.0", 1279 | "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", 1280 | "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", 1281 | "dev": true, 1282 | "requires": { 1283 | "binary-extensions": "^2.0.0" 1284 | } 1285 | }, 1286 | "is-extglob": { 1287 | "version": "2.1.1", 1288 | "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", 1289 | "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", 1290 | "dev": true 1291 | }, 1292 | "is-fullwidth-code-point": { 1293 | "version": "3.0.0", 1294 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", 1295 | "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", 1296 | "dev": true 1297 | }, 1298 | "is-glob": { 1299 | "version": "4.0.3", 1300 | "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", 1301 | "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", 1302 | "dev": true, 1303 | "requires": { 1304 | "is-extglob": "^2.1.1" 1305 | } 1306 | }, 1307 | "is-number": { 1308 | "version": "7.0.0", 1309 | "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", 1310 | "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", 1311 | "dev": true 1312 | }, 1313 | "is-plain-obj": { 1314 | "version": "2.1.0", 1315 | "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", 1316 | "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", 1317 | "dev": true 1318 | }, 1319 | "is-unicode-supported": { 1320 | "version": "0.1.0", 1321 | "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", 1322 | "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", 1323 | "dev": true 1324 | }, 1325 | "js-yaml": { 1326 | "version": "4.1.0", 1327 | "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", 1328 | "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", 1329 | "dev": true, 1330 | "requires": { 1331 | "argparse": "^2.0.1" 1332 | } 1333 | }, 1334 | "locate-path": { 1335 | "version": "6.0.0", 1336 | "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", 1337 | "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", 1338 | "dev": true, 1339 | "requires": { 1340 | "p-locate": "^5.0.0" 1341 | } 1342 | }, 1343 | "log-symbols": { 1344 | "version": "4.1.0", 1345 | "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", 1346 | "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", 1347 | "dev": true, 1348 | "requires": { 1349 | "chalk": "^4.1.0", 1350 | "is-unicode-supported": "^0.1.0" 1351 | } 1352 | }, 1353 | "minimatch": { 1354 | "version": "3.1.2", 1355 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", 1356 | "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", 1357 | "dev": true, 1358 | "requires": { 1359 | "brace-expansion": "^1.1.7" 1360 | } 1361 | }, 1362 | "mocha": { 1363 | "version": "10.2.0", 1364 | "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.2.0.tgz", 1365 | "integrity": "sha512-IDY7fl/BecMwFHzoqF2sg/SHHANeBoMMXFlS9r0OXKDssYE1M5O43wUY/9BVPeIvfH2zmEbBfseqN9gBQZzXkg==", 1366 | "dev": true, 1367 | "requires": { 1368 | "ansi-colors": "4.1.1", 1369 | "browser-stdout": "1.3.1", 1370 | "chokidar": "3.5.3", 1371 | "debug": "4.3.4", 1372 | "diff": "5.0.0", 1373 | "escape-string-regexp": "4.0.0", 1374 | "find-up": "5.0.0", 1375 | "glob": "7.2.0", 1376 | "he": "1.2.0", 1377 | "js-yaml": "4.1.0", 1378 | "log-symbols": "4.1.0", 1379 | "minimatch": "5.0.1", 1380 | "ms": "2.1.3", 1381 | "nanoid": "3.3.3", 1382 | "serialize-javascript": "6.0.0", 1383 | "strip-json-comments": "3.1.1", 1384 | "supports-color": "8.1.1", 1385 | "workerpool": "6.2.1", 1386 | "yargs": "16.2.0", 1387 | "yargs-parser": "20.2.4", 1388 | "yargs-unparser": "2.0.0" 1389 | }, 1390 | "dependencies": { 1391 | "brace-expansion": { 1392 | "version": "2.0.1", 1393 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", 1394 | "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", 1395 | "dev": true, 1396 | "requires": { 1397 | "balanced-match": "^1.0.0" 1398 | } 1399 | }, 1400 | "minimatch": { 1401 | "version": "5.0.1", 1402 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz", 1403 | "integrity": "sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==", 1404 | "dev": true, 1405 | "requires": { 1406 | "brace-expansion": "^2.0.1" 1407 | } 1408 | } 1409 | } 1410 | }, 1411 | "ms": { 1412 | "version": "2.1.3", 1413 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", 1414 | "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", 1415 | "dev": true 1416 | }, 1417 | "nanoid": { 1418 | "version": "3.3.3", 1419 | "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.3.tgz", 1420 | "integrity": "sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w==", 1421 | "dev": true 1422 | }, 1423 | "normalize-path": { 1424 | "version": "3.0.0", 1425 | "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", 1426 | "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", 1427 | "dev": true 1428 | }, 1429 | "once": { 1430 | "version": "1.4.0", 1431 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 1432 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", 1433 | "dev": true, 1434 | "requires": { 1435 | "wrappy": "1" 1436 | } 1437 | }, 1438 | "p-limit": { 1439 | "version": "3.1.0", 1440 | "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", 1441 | "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", 1442 | "dev": true, 1443 | "requires": { 1444 | "yocto-queue": "^0.1.0" 1445 | } 1446 | }, 1447 | "p-locate": { 1448 | "version": "5.0.0", 1449 | "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", 1450 | "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", 1451 | "dev": true, 1452 | "requires": { 1453 | "p-limit": "^3.0.2" 1454 | } 1455 | }, 1456 | "path-exists": { 1457 | "version": "4.0.0", 1458 | "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", 1459 | "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", 1460 | "dev": true 1461 | }, 1462 | "path-is-absolute": { 1463 | "version": "1.0.1", 1464 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 1465 | "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", 1466 | "dev": true 1467 | }, 1468 | "picomatch": { 1469 | "version": "2.3.1", 1470 | "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", 1471 | "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", 1472 | "dev": true 1473 | }, 1474 | "randombytes": { 1475 | "version": "2.1.0", 1476 | "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", 1477 | "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", 1478 | "dev": true, 1479 | "requires": { 1480 | "safe-buffer": "^5.1.0" 1481 | } 1482 | }, 1483 | "readdirp": { 1484 | "version": "3.6.0", 1485 | "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", 1486 | "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", 1487 | "dev": true, 1488 | "requires": { 1489 | "picomatch": "^2.2.1" 1490 | } 1491 | }, 1492 | "require-directory": { 1493 | "version": "2.1.1", 1494 | "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", 1495 | "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", 1496 | "dev": true 1497 | }, 1498 | "safe-buffer": { 1499 | "version": "5.2.1", 1500 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", 1501 | "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", 1502 | "dev": true 1503 | }, 1504 | "serialize-javascript": { 1505 | "version": "6.0.0", 1506 | "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", 1507 | "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", 1508 | "dev": true, 1509 | "requires": { 1510 | "randombytes": "^2.1.0" 1511 | } 1512 | }, 1513 | "should": { 1514 | "version": "13.2.3", 1515 | "resolved": "https://registry.npmjs.org/should/-/should-13.2.3.tgz", 1516 | "integrity": "sha512-ggLesLtu2xp+ZxI+ysJTmNjh2U0TsC+rQ/pfED9bUZZ4DKefP27D+7YJVVTvKsmjLpIi9jAa7itwDGkDDmt1GQ==", 1517 | "dev": true, 1518 | "requires": { 1519 | "should-equal": "^2.0.0", 1520 | "should-format": "^3.0.3", 1521 | "should-type": "^1.4.0", 1522 | "should-type-adaptors": "^1.0.1", 1523 | "should-util": "^1.0.0" 1524 | } 1525 | }, 1526 | "should-equal": { 1527 | "version": "2.0.0", 1528 | "resolved": "https://registry.npmjs.org/should-equal/-/should-equal-2.0.0.tgz", 1529 | "integrity": "sha512-ZP36TMrK9euEuWQYBig9W55WPC7uo37qzAEmbjHz4gfyuXrEUgF8cUvQVO+w+d3OMfPvSRQJ22lSm8MQJ43LTA==", 1530 | "dev": true, 1531 | "requires": { 1532 | "should-type": "^1.4.0" 1533 | } 1534 | }, 1535 | "should-format": { 1536 | "version": "3.0.3", 1537 | "resolved": "https://registry.npmjs.org/should-format/-/should-format-3.0.3.tgz", 1538 | "integrity": "sha1-m/yPdPo5IFxT04w01xcwPidxJPE=", 1539 | "dev": true, 1540 | "requires": { 1541 | "should-type": "^1.3.0", 1542 | "should-type-adaptors": "^1.0.1" 1543 | } 1544 | }, 1545 | "should-type": { 1546 | "version": "1.4.0", 1547 | "resolved": "https://registry.npmjs.org/should-type/-/should-type-1.4.0.tgz", 1548 | "integrity": "sha1-B1bYzoRt/QmEOmlHcZ36DUz/XPM=", 1549 | "dev": true 1550 | }, 1551 | "should-type-adaptors": { 1552 | "version": "1.1.0", 1553 | "resolved": "https://registry.npmjs.org/should-type-adaptors/-/should-type-adaptors-1.1.0.tgz", 1554 | "integrity": "sha512-JA4hdoLnN+kebEp2Vs8eBe9g7uy0zbRo+RMcU0EsNy+R+k049Ki+N5tT5Jagst2g7EAja+euFuoXFCa8vIklfA==", 1555 | "dev": true, 1556 | "requires": { 1557 | "should-type": "^1.3.0", 1558 | "should-util": "^1.0.0" 1559 | } 1560 | }, 1561 | "should-util": { 1562 | "version": "1.0.1", 1563 | "resolved": "https://registry.npmjs.org/should-util/-/should-util-1.0.1.tgz", 1564 | "integrity": "sha512-oXF8tfxx5cDk8r2kYqlkUJzZpDBqVY/II2WhvU0n9Y3XYvAYRmeaf1PvvIvTgPnv4KJ+ES5M0PyDq5Jp+Ygy2g==", 1565 | "dev": true 1566 | }, 1567 | "string-width": { 1568 | "version": "4.2.3", 1569 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", 1570 | "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", 1571 | "dev": true, 1572 | "requires": { 1573 | "emoji-regex": "^8.0.0", 1574 | "is-fullwidth-code-point": "^3.0.0", 1575 | "strip-ansi": "^6.0.1" 1576 | } 1577 | }, 1578 | "strip-ansi": { 1579 | "version": "6.0.1", 1580 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", 1581 | "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", 1582 | "dev": true, 1583 | "requires": { 1584 | "ansi-regex": "^5.0.1" 1585 | } 1586 | }, 1587 | "strip-json-comments": { 1588 | "version": "3.1.1", 1589 | "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", 1590 | "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", 1591 | "dev": true 1592 | }, 1593 | "supports-color": { 1594 | "version": "8.1.1", 1595 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", 1596 | "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", 1597 | "dev": true, 1598 | "requires": { 1599 | "has-flag": "^4.0.0" 1600 | } 1601 | }, 1602 | "to-regex-range": { 1603 | "version": "5.0.1", 1604 | "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", 1605 | "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", 1606 | "dev": true, 1607 | "requires": { 1608 | "is-number": "^7.0.0" 1609 | } 1610 | }, 1611 | "typescript": { 1612 | "version": "4.5.5", 1613 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.5.tgz", 1614 | "integrity": "sha512-TCTIul70LyWe6IJWT8QSYeA54WQe8EjQFU4wY52Fasj5UKx88LNYKCgBEHcOMOrFF1rKGbD8v/xcNWVUq9SymA==", 1615 | "dev": true 1616 | }, 1617 | "workerpool": { 1618 | "version": "6.2.1", 1619 | "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz", 1620 | "integrity": "sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==", 1621 | "dev": true 1622 | }, 1623 | "wrap-ansi": { 1624 | "version": "7.0.0", 1625 | "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", 1626 | "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", 1627 | "dev": true, 1628 | "requires": { 1629 | "ansi-styles": "^4.0.0", 1630 | "string-width": "^4.1.0", 1631 | "strip-ansi": "^6.0.0" 1632 | } 1633 | }, 1634 | "wrappy": { 1635 | "version": "1.0.2", 1636 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 1637 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", 1638 | "dev": true 1639 | }, 1640 | "y18n": { 1641 | "version": "5.0.8", 1642 | "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", 1643 | "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", 1644 | "dev": true 1645 | }, 1646 | "yargs": { 1647 | "version": "16.2.0", 1648 | "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", 1649 | "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", 1650 | "dev": true, 1651 | "requires": { 1652 | "cliui": "^7.0.2", 1653 | "escalade": "^3.1.1", 1654 | "get-caller-file": "^2.0.5", 1655 | "require-directory": "^2.1.1", 1656 | "string-width": "^4.2.0", 1657 | "y18n": "^5.0.5", 1658 | "yargs-parser": "^20.2.2" 1659 | } 1660 | }, 1661 | "yargs-parser": { 1662 | "version": "20.2.4", 1663 | "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", 1664 | "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", 1665 | "dev": true 1666 | }, 1667 | "yargs-unparser": { 1668 | "version": "2.0.0", 1669 | "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", 1670 | "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", 1671 | "dev": true, 1672 | "requires": { 1673 | "camelcase": "^6.0.0", 1674 | "decamelize": "^4.0.0", 1675 | "flat": "^5.0.2", 1676 | "is-plain-obj": "^2.1.0" 1677 | } 1678 | }, 1679 | "yocto-queue": { 1680 | "version": "0.1.0", 1681 | "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", 1682 | "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", 1683 | "dev": true 1684 | } 1685 | } 1686 | } 1687 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "search-query-parser", 3 | "version": "1.6.0", 4 | "description": "Parser for advanced search query syntax", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "mocha -R spec" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git://github.com/nepsilon/search-query-parser" 12 | }, 13 | "keywords": [ 14 | "search syntax parser", 15 | "query syntax parser", 16 | "query", 17 | "parser", 18 | "search query", 19 | "search" 20 | ], 21 | "license": "MIT", 22 | "bugs": { 23 | "url": "https://github.com/nepsilon/search-query-parser/issues" 24 | }, 25 | "homepage": "https://github.com/nepsilon/search-query-parser", 26 | "devDependencies": { 27 | "mocha": "^10.2.0", 28 | "should": "^13.2.3", 29 | "typescript": "^4.2.2" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /test/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Mocha 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /test/mocha.css: -------------------------------------------------------------------------------- 1 | @charset "utf-8"; 2 | 3 | body { 4 | margin:0; 5 | } 6 | 7 | #mocha { 8 | font: 20px/1.5 "Helvetica Neue", Helvetica, Arial, sans-serif; 9 | margin: 60px 50px; 10 | } 11 | 12 | #mocha ul, 13 | #mocha li { 14 | margin: 0; 15 | padding: 0; 16 | } 17 | 18 | #mocha ul { 19 | list-style: none; 20 | } 21 | 22 | #mocha h1, 23 | #mocha h2 { 24 | margin: 0; 25 | } 26 | 27 | #mocha h1 { 28 | margin-top: 15px; 29 | font-size: 1em; 30 | font-weight: 200; 31 | } 32 | 33 | #mocha h1 a { 34 | text-decoration: none; 35 | color: inherit; 36 | } 37 | 38 | #mocha h1 a:hover { 39 | text-decoration: underline; 40 | } 41 | 42 | #mocha .suite .suite h1 { 43 | margin-top: 0; 44 | font-size: .8em; 45 | } 46 | 47 | #mocha .hidden { 48 | display: none; 49 | } 50 | 51 | #mocha h2 { 52 | font-size: 12px; 53 | font-weight: normal; 54 | cursor: pointer; 55 | } 56 | 57 | #mocha .suite { 58 | margin-left: 15px; 59 | } 60 | 61 | #mocha .test { 62 | margin-left: 15px; 63 | overflow: hidden; 64 | } 65 | 66 | #mocha .test.pending:hover h2::after { 67 | content: '(pending)'; 68 | font-family: arial, sans-serif; 69 | } 70 | 71 | #mocha .test.pass.medium .duration { 72 | background: #c09853; 73 | } 74 | 75 | #mocha .test.pass.slow .duration { 76 | background: #b94a48; 77 | } 78 | 79 | #mocha .test.pass::before { 80 | content: '✓'; 81 | font-size: 12px; 82 | display: block; 83 | float: left; 84 | margin-right: 5px; 85 | color: #00d6b2; 86 | } 87 | 88 | #mocha .test.pass .duration { 89 | font-size: 9px; 90 | margin-left: 5px; 91 | padding: 2px 5px; 92 | color: #fff; 93 | -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.2); 94 | -moz-box-shadow: inset 0 1px 1px rgba(0,0,0,.2); 95 | box-shadow: inset 0 1px 1px rgba(0,0,0,.2); 96 | -webkit-border-radius: 5px; 97 | -moz-border-radius: 5px; 98 | -ms-border-radius: 5px; 99 | -o-border-radius: 5px; 100 | border-radius: 5px; 101 | } 102 | 103 | #mocha .test.pass.fast .duration { 104 | display: none; 105 | } 106 | 107 | #mocha .test.pending { 108 | color: #0b97c4; 109 | } 110 | 111 | #mocha .test.pending::before { 112 | content: '◦'; 113 | color: #0b97c4; 114 | } 115 | 116 | #mocha .test.fail { 117 | color: #c00; 118 | } 119 | 120 | #mocha .test.fail pre { 121 | color: black; 122 | } 123 | 124 | #mocha .test.fail::before { 125 | content: '✖'; 126 | font-size: 12px; 127 | display: block; 128 | float: left; 129 | margin-right: 5px; 130 | color: #c00; 131 | } 132 | 133 | #mocha .test pre.error { 134 | color: #c00; 135 | max-height: 300px; 136 | overflow: auto; 137 | } 138 | 139 | /** 140 | * (1): approximate for browsers not supporting calc 141 | * (2): 42 = 2*15 + 2*10 + 2*1 (padding + margin + border) 142 | * ^^ seriously 143 | */ 144 | #mocha .test pre { 145 | display: block; 146 | float: left; 147 | clear: left; 148 | font: 12px/1.5 monaco, monospace; 149 | margin: 5px; 150 | padding: 15px; 151 | border: 1px solid #eee; 152 | max-width: 85%; /*(1)*/ 153 | max-width: calc(100% - 42px); /*(2)*/ 154 | word-wrap: break-word; 155 | border-bottom-color: #ddd; 156 | -webkit-border-radius: 3px; 157 | -webkit-box-shadow: 0 1px 3px #eee; 158 | -moz-border-radius: 3px; 159 | -moz-box-shadow: 0 1px 3px #eee; 160 | border-radius: 3px; 161 | } 162 | 163 | #mocha .test h2 { 164 | position: relative; 165 | } 166 | 167 | #mocha .test a.replay { 168 | position: absolute; 169 | top: 3px; 170 | right: 0; 171 | text-decoration: none; 172 | vertical-align: middle; 173 | display: block; 174 | width: 15px; 175 | height: 15px; 176 | line-height: 15px; 177 | text-align: center; 178 | background: #eee; 179 | font-size: 15px; 180 | -moz-border-radius: 15px; 181 | border-radius: 15px; 182 | -webkit-transition: opacity 200ms; 183 | -moz-transition: opacity 200ms; 184 | transition: opacity 200ms; 185 | opacity: 0.3; 186 | color: #888; 187 | } 188 | 189 | #mocha .test:hover a.replay { 190 | opacity: 1; 191 | } 192 | 193 | #mocha-report.pass .test.fail { 194 | display: none; 195 | } 196 | 197 | #mocha-report.fail .test.pass { 198 | display: none; 199 | } 200 | 201 | #mocha-report.pending .test.pass, 202 | #mocha-report.pending .test.fail { 203 | display: none; 204 | } 205 | #mocha-report.pending .test.pass.pending { 206 | display: block; 207 | } 208 | 209 | #mocha-error { 210 | color: #c00; 211 | font-size: 1.5em; 212 | font-weight: 100; 213 | letter-spacing: 1px; 214 | } 215 | 216 | #mocha-stats { 217 | position: fixed; 218 | top: 15px; 219 | right: 10px; 220 | font-size: 12px; 221 | margin: 0; 222 | color: #888; 223 | z-index: 1; 224 | } 225 | 226 | #mocha-stats .progress { 227 | float: right; 228 | padding-top: 0; 229 | } 230 | 231 | #mocha-stats em { 232 | color: black; 233 | } 234 | 235 | #mocha-stats a { 236 | text-decoration: none; 237 | color: inherit; 238 | } 239 | 240 | #mocha-stats a:hover { 241 | border-bottom: 1px solid #eee; 242 | } 243 | 244 | #mocha-stats li { 245 | display: inline-block; 246 | margin: 0 5px; 247 | list-style: none; 248 | padding-top: 11px; 249 | } 250 | 251 | #mocha-stats canvas { 252 | width: 40px; 253 | height: 40px; 254 | } 255 | 256 | #mocha code .comment { color: #ddd; } 257 | #mocha code .init { color: #2f6fad; } 258 | #mocha code .string { color: #5890ad; } 259 | #mocha code .keyword { color: #8a6343; } 260 | #mocha code .number { color: #2f6fad; } 261 | 262 | @media screen and (max-device-width: 480px) { 263 | #mocha { 264 | margin: 60px 0px; 265 | } 266 | 267 | #mocha #stats { 268 | position: absolute; 269 | } 270 | } 271 | -------------------------------------------------------------------------------- /test/test.js: -------------------------------------------------------------------------------- 1 | var assert = require('assert') 2 | , should = require('should') 3 | , searchquery = require('../'); 4 | 5 | 6 | describe('Search query syntax parser', function () { 7 | 8 | 9 | it('should return a simple string when zero keyword present', function () { 10 | var searchQuery = "fancy pyjama wear"; 11 | var parsedSearchQuery = searchquery.parse(searchQuery); 12 | 13 | parsedSearchQuery.should.be.a.string; 14 | parsedSearchQuery.should.equal(searchQuery); 15 | var parsedAfterStringifySearchQuery = searchquery.parse(searchquery.stringify(parsedSearchQuery)); 16 | parsedAfterStringifySearchQuery.should.be.equal(parsedSearchQuery); 17 | }); 18 | 19 | it('should return a tokenized string when option is set', function () { 20 | var searchQuery = "fancy pyjama wear"; 21 | var options = { tokenize: true }; 22 | var parsedSearchQuery = searchquery.parse(searchQuery, options); 23 | 24 | parsedSearchQuery.should.be.an.Object; 25 | parsedSearchQuery.should.have.property('text', ['fancy', 'pyjama', 'wear']); 26 | 27 | var parsedAfterStringifySearchQuery = searchquery.parse(searchquery.stringify(parsedSearchQuery, options), options); 28 | parsedAfterStringifySearchQuery.offsets = undefined; 29 | parsedSearchQuery.offsets = undefined; 30 | parsedAfterStringifySearchQuery.should.be.eql(parsedSearchQuery); 31 | }); 32 | 33 | it('should return a tokenized string when option is set, respecting double-quotes and escapes', function () { 34 | var searchQuery = 'fancy "py\\"j\\"am\'a w\'ear"'; 35 | var options = { tokenize: true }; 36 | var parsedSearchQuery = searchquery.parse(searchQuery, options); 37 | 38 | parsedSearchQuery.should.be.an.Object; 39 | parsedSearchQuery.should.have.property('text', ['fancy', 'py"j"am\'a w\'ear']); 40 | 41 | var parsedAfterStringifySearchQuery = searchquery.parse(searchquery.stringify(parsedSearchQuery, options), options); 42 | parsedAfterStringifySearchQuery.offsets = undefined; 43 | parsedSearchQuery.offsets = undefined; 44 | parsedAfterStringifySearchQuery.should.be.eql(parsedSearchQuery); 45 | }); 46 | 47 | it('should return a tokenized string when option is set, respecting single-quotes and escapes', function () { 48 | var searchQuery = "fancy 'py\\'j\\'am\"a w\"ear'"; 49 | var options = { tokenize: true }; 50 | var parsedSearchQuery = searchquery.parse(searchQuery, options); 51 | 52 | parsedSearchQuery.should.be.an.Object; 53 | parsedSearchQuery.should.have.property('text', ['fancy', "py'j'am\"a w\"ear"]); 54 | 55 | var parsedAfterStringifySearchQuery = searchquery.parse(searchquery.stringify(parsedSearchQuery, options), options); 56 | parsedAfterStringifySearchQuery.offsets = undefined; 57 | parsedSearchQuery.offsets = undefined; 58 | parsedAfterStringifySearchQuery.should.be.eql(parsedSearchQuery); 59 | }); 60 | 61 | it('should return a tokenized string with negation of unquoted terms', function () { 62 | var searchQuery = "fancy -pyjama -wear"; 63 | var options = { tokenize: true }; 64 | var parsedSearchQuery = searchquery.parse(searchQuery, options); 65 | 66 | parsedSearchQuery.should.be.an.Object; 67 | parsedSearchQuery.should.have.property('text', ['fancy']); 68 | parsedSearchQuery.should.have.property('exclude', {text: ['pyjama', 'wear']}); 69 | 70 | var parsedAfterStringifySearchQuery = searchquery.parse(searchquery.stringify(parsedSearchQuery, options), options); 71 | parsedAfterStringifySearchQuery.offsets = undefined; 72 | parsedSearchQuery.offsets = undefined; 73 | parsedAfterStringifySearchQuery.should.be.eql(parsedSearchQuery); 74 | }); 75 | 76 | it('should return a tokenized string with negation of single-quoted terms', function () { 77 | var searchQuery = "fancy -'pyjama -wear'"; 78 | var options = { tokenize: true }; 79 | var parsedSearchQuery = searchquery.parse(searchQuery, options); 80 | 81 | parsedSearchQuery.should.be.an.Object; 82 | parsedSearchQuery.should.have.property('text', ['fancy']); 83 | parsedSearchQuery.should.have.property('exclude', {text: 'pyjama -wear'}); 84 | 85 | var parsedAfterStringifySearchQuery = searchquery.parse(searchquery.stringify(parsedSearchQuery, options), options); 86 | parsedAfterStringifySearchQuery.offsets = undefined; 87 | parsedSearchQuery.offsets = undefined; 88 | parsedAfterStringifySearchQuery.should.be.eql(parsedSearchQuery); 89 | }); 90 | 91 | it('should return a tokenized string with negation of double-quoted terms', function () { 92 | var searchQuery = 'fancy -"pyjama -wear"'; 93 | var options = { tokenize: true }; 94 | var parsedSearchQuery = searchquery.parse(searchQuery, options); 95 | 96 | parsedSearchQuery.should.be.an.Object; 97 | parsedSearchQuery.should.have.property('text', ['fancy']); 98 | parsedSearchQuery.should.have.property('exclude', {text: 'pyjama -wear'}); 99 | 100 | var parsedAfterStringifySearchQuery = searchquery.parse(searchquery.stringify(parsedSearchQuery, options), options); 101 | parsedAfterStringifySearchQuery.offsets = undefined; 102 | parsedSearchQuery.offsets = undefined; 103 | parsedAfterStringifySearchQuery.should.be.eql(parsedSearchQuery); 104 | }); 105 | 106 | it('should parse a single keyword with no text', function () { 107 | var searchQuery = 'from:jul@foo.com'; 108 | var options = {keywords: ['from']}; 109 | var parsedSearchQuery = searchquery.parse(searchQuery, options); 110 | 111 | parsedSearchQuery.should.be.an.Object; 112 | parsedSearchQuery.should.have.property('from', 'jul@foo.com'); 113 | parsedSearchQuery.should.not.have.property('text'); 114 | parsedSearchQuery.should.have.property('offsets', [{ 115 | keyword: 'from', 116 | value: 'jul@foo.com', 117 | offsetStart: 0, 118 | offsetEnd: 16 119 | }]); 120 | 121 | var parsedAfterStringifySearchQuery = searchquery.parse(searchquery.stringify(parsedSearchQuery, options), options); 122 | parsedAfterStringifySearchQuery.offsets = undefined; 123 | parsedSearchQuery.offsets = undefined; 124 | parsedAfterStringifySearchQuery.should.be.eql(parsedSearchQuery); 125 | }); 126 | 127 | 128 | it('should parse a single keyword with free text after it', function () { 129 | var searchQuery = 'from:jul@foo.com hey buddy!'; 130 | var options = {keywords: ['from']}; 131 | var parsedSearchQuery = searchquery.parse(searchQuery, options); 132 | 133 | parsedSearchQuery.should.be.an.Object; 134 | parsedSearchQuery.should.have.property('from', 'jul@foo.com'); 135 | parsedSearchQuery.should.have.property('text', 'hey buddy!'); 136 | parsedSearchQuery.should.have.property('offsets', [{ 137 | keyword: 'from', 138 | value: 'jul@foo.com', 139 | offsetStart: 0, 140 | offsetEnd: 16 141 | }, { 142 | text: 'hey', 143 | offsetStart: 17, 144 | offsetEnd: 20 145 | }, { 146 | text: 'buddy!', 147 | offsetStart: 21, 148 | offsetEnd: 27 149 | }]); 150 | 151 | var parsedAfterStringifySearchQuery = searchquery.parse(searchquery.stringify(parsedSearchQuery, options), options); 152 | parsedAfterStringifySearchQuery.offsets = undefined; 153 | parsedSearchQuery.offsets = undefined; 154 | parsedAfterStringifySearchQuery.should.be.eql(parsedSearchQuery); 155 | }); 156 | 157 | 158 | it('should ignore keywords that are not specified', function() { 159 | var searchQuery = 'test another other:jul@foo.com'; 160 | var options = { 161 | keywords: ['from'] 162 | }; 163 | var parsedSearchQuery = searchquery.parse(searchQuery, options); 164 | 165 | parsedSearchQuery.should.be.an.Object; 166 | parsedSearchQuery.should.have.not.have.property('other'); 167 | parsedSearchQuery.should.have.property('text', 'test another other:jul@foo.com'); 168 | parsedSearchQuery.should.have.property('offsets', [{ 169 | text: 'test', 170 | offsetStart: 0, 171 | offsetEnd: 4 172 | }, { 173 | text: 'another', 174 | offsetStart: 5, 175 | offsetEnd: 12 176 | }, { 177 | text: 'other:jul@foo.com', 178 | offsetStart: 13, 179 | offsetEnd: 30 180 | }]); 181 | 182 | var parsedAfterStringifySearchQuery = searchquery.parse(searchquery.stringify(parsedSearchQuery, options), options); 183 | parsedAfterStringifySearchQuery.offsets = undefined; 184 | parsedSearchQuery.offsets = undefined; 185 | parsedAfterStringifySearchQuery.should.be.eql(parsedSearchQuery); 186 | }); 187 | 188 | 189 | it('should parse a single keyword with free text before it', function() { 190 | var searchQuery = 'hey you! from:jul@foo.com'; 191 | var options = {keywords: ['from']}; 192 | var parsedSearchQuery = searchquery.parse(searchQuery, options); 193 | 194 | parsedSearchQuery.should.be.an.Object; 195 | parsedSearchQuery.should.have.property('from', 'jul@foo.com'); 196 | parsedSearchQuery.should.have.property('text', 'hey you!'); 197 | parsedSearchQuery.should.have.property('offsets', [{ 198 | text: 'hey', 199 | offsetStart: 0, 200 | offsetEnd: 3 201 | }, { 202 | text: 'you!', 203 | offsetStart: 4, 204 | offsetEnd: 8 205 | }, { 206 | keyword: 'from', 207 | value: 'jul@foo.com', 208 | offsetStart: 9, 209 | offsetEnd: 25 210 | }]); 211 | 212 | var parsedAfterStringifySearchQuery = searchquery.parse(searchquery.stringify(parsedSearchQuery, options), options); 213 | parsedAfterStringifySearchQuery.offsets = undefined; 214 | parsedSearchQuery.offsets = undefined; 215 | parsedAfterStringifySearchQuery.should.be.eql(parsedSearchQuery); 216 | }); 217 | 218 | 219 | it('should parse a single keyword with free text around it', function () { 220 | var searchQuery = 'hey you! from:jul@foo.com pouet'; 221 | var options = {keywords: ['from']}; 222 | var parsedSearchQuery = searchquery.parse(searchQuery, options); 223 | 224 | parsedSearchQuery.should.be.an.Object; 225 | parsedSearchQuery.should.have.property('from', 'jul@foo.com'); 226 | parsedSearchQuery.should.have.property('text', 'hey you! pouet'); 227 | parsedSearchQuery.should.have.property('offsets', [{ 228 | text: 'hey', 229 | offsetStart: 0, 230 | offsetEnd: 3 231 | }, { 232 | text: 'you!', 233 | offsetStart: 4, 234 | offsetEnd: 8 235 | }, { 236 | keyword: 'from', 237 | value: 'jul@foo.com', 238 | offsetStart: 9, 239 | offsetEnd: 25 240 | }, { 241 | text: 'pouet', 242 | offsetStart: 26, 243 | offsetEnd: 31 244 | }]); 245 | 246 | var parsedAfterStringifySearchQuery = searchquery.parse(searchquery.stringify(parsedSearchQuery, options), options); 247 | parsedAfterStringifySearchQuery.offsets = undefined; 248 | parsedSearchQuery.offsets = undefined; 249 | parsedAfterStringifySearchQuery.should.be.eql(parsedSearchQuery); 250 | }); 251 | 252 | 253 | it('should strip any white space at any position', function () { 254 | // We have tabs and regular spaces in the string below 255 | var searchQuery = ' hey you! from:jul@foo.com pouet '; 256 | var options = {keywords: ['from']}; 257 | var parsedSearchQuery = searchquery.parse(searchQuery, options); 258 | 259 | parsedSearchQuery.should.be.an.Object; 260 | parsedSearchQuery.should.have.property('from', 'jul@foo.com'); 261 | parsedSearchQuery.should.have.property('text', 'hey you! pouet'); 262 | parsedSearchQuery.should.have.property('offsets', [{ 263 | text: 'hey', 264 | offsetStart: 3, 265 | offsetEnd: 6 266 | }, { 267 | text: 'you!', 268 | offsetStart: 11, 269 | offsetEnd: 15 270 | }, { 271 | keyword: 'from', 272 | value: 'jul@foo.com', 273 | offsetStart: 16, 274 | offsetEnd: 32 275 | }, { 276 | text: 'pouet', 277 | offsetStart: 35, 278 | offsetEnd: 40 279 | }]); 280 | 281 | var parsedAfterStringifySearchQuery = searchquery.parse(searchquery.stringify(parsedSearchQuery, options), options); 282 | parsedAfterStringifySearchQuery.offsets = undefined; 283 | parsedSearchQuery.offsets = undefined; 284 | parsedAfterStringifySearchQuery.should.be.eql(parsedSearchQuery); 285 | }); 286 | 287 | 288 | it('should parse 2 different keywords with free text', function () { 289 | var searchQuery = 'hey, from:jul@foo.com to:bar@hey.ya so what\'s up gents'; 290 | var options = {keywords: ['from', 'to']}; 291 | var parsedSearchQuery = searchquery.parse(searchQuery, options); 292 | 293 | parsedSearchQuery.should.be.an.Object; 294 | parsedSearchQuery.should.have.property('from', 'jul@foo.com'); 295 | parsedSearchQuery.should.have.property('to', 'bar@hey.ya'); 296 | parsedSearchQuery.should.have.property('text', 'hey, so what\'s up gents'); 297 | parsedSearchQuery.should.have.property('offsets', [{ 298 | text: 'hey,', 299 | offsetStart: 0, 300 | offsetEnd: 4 301 | }, { 302 | keyword: 'from', 303 | value: 'jul@foo.com', 304 | offsetStart: 5, 305 | offsetEnd: 21 306 | }, { 307 | keyword: 'to', 308 | value: 'bar@hey.ya', 309 | offsetStart: 22, 310 | offsetEnd: 35 311 | }, { 312 | text: 'so', 313 | offsetStart: 36, 314 | offsetEnd: 38 315 | }, { 316 | text: 'what\'s', 317 | offsetStart: 39, 318 | offsetEnd: 45 319 | }, { 320 | text: 'up', 321 | offsetStart: 46, 322 | offsetEnd: 48 323 | }, { 324 | text: 'gents', 325 | offsetStart: 49, 326 | offsetEnd: 54 327 | }]); 328 | 329 | var parsedAfterStringifySearchQuery = searchquery.parse(searchquery.stringify(parsedSearchQuery, options), options); 330 | parsedAfterStringifySearchQuery.offsets = undefined; 331 | parsedSearchQuery.offsets = undefined; 332 | parsedAfterStringifySearchQuery.should.be.eql(parsedSearchQuery); 333 | }); 334 | 335 | 336 | it('should concatenate 2 identical keywords value and keep free text', function () { 337 | var searchQuery = 'from:jul@foo.com from:bar@hey.ya vaccationessss'; 338 | var options = {keywords: ['from']}; 339 | var parsedSearchQuery = searchquery.parse(searchQuery, options); 340 | 341 | parsedSearchQuery.should.be.an.Object; 342 | parsedSearchQuery.should.have.property('from'); 343 | parsedSearchQuery.should.have.property('text', 'vaccationessss'); 344 | parsedSearchQuery.from.should.be.an.Array; 345 | parsedSearchQuery.from.length.should.equal(2); 346 | parsedSearchQuery.from.should.containEql('jul@foo.com'); 347 | parsedSearchQuery.from.should.containEql('bar@hey.ya'); 348 | parsedSearchQuery.should.have.property('offsets', [{ 349 | keyword: 'from', 350 | value: 'jul@foo.com', 351 | offsetStart: 0, 352 | offsetEnd: 16 353 | }, { 354 | keyword: 'from', 355 | value: 'bar@hey.ya', 356 | offsetStart: 17, 357 | offsetEnd: 32 358 | }, { 359 | text: 'vaccationessss', 360 | offsetStart: 33, 361 | offsetEnd: 47 362 | }]); 363 | 364 | var parsedAfterStringifySearchQuery = searchquery.parse(searchquery.stringify(parsedSearchQuery, options), options); 365 | parsedAfterStringifySearchQuery.offsets = undefined; 366 | parsedSearchQuery.offsets = undefined; 367 | parsedAfterStringifySearchQuery.should.be.eql(parsedSearchQuery); 368 | }); 369 | 370 | 371 | it('should concatenate a keyword multiple values', function () { 372 | var searchQuery = 'from:jul@foo.com,bar@hey.ya'; 373 | var options = {keywords: ['from']}; 374 | var parsedSearchQuery = searchquery.parse(searchQuery, options); 375 | 376 | parsedSearchQuery.should.be.an.Object; 377 | parsedSearchQuery.should.have.property('from'); 378 | parsedSearchQuery.from.should.be.an.Array; 379 | parsedSearchQuery.from.length.should.equal(2); 380 | parsedSearchQuery.from.should.containEql('jul@foo.com'); 381 | parsedSearchQuery.from.should.containEql('bar@hey.ya'); 382 | parsedSearchQuery.should.have.property('offsets', [{ 383 | keyword: 'from', 384 | value: 'jul@foo.com,bar@hey.ya', 385 | offsetStart: 0, 386 | offsetEnd: 27 387 | }]); 388 | 389 | var parsedAfterStringifySearchQuery = searchquery.parse(searchquery.stringify(parsedSearchQuery, options), options); 390 | parsedAfterStringifySearchQuery.offsets = undefined; 391 | parsedSearchQuery.offsets = undefined; 392 | parsedAfterStringifySearchQuery.should.be.eql(parsedSearchQuery); 393 | }); 394 | 395 | 396 | it('should concatenate values from 2 identical keyword multiple values and keep free text', function () { 397 | var searchQuery = 'from:jul@foo.com,bar@hey.ya from:a@b.c,d@e.f ouch!#'; 398 | var options = {keywords: ['from']}; 399 | var parsedSearchQuery = searchquery.parse(searchQuery, options); 400 | 401 | parsedSearchQuery.should.be.an.Object; 402 | parsedSearchQuery.should.have.property('text', 'ouch!#'); 403 | parsedSearchQuery.should.have.property('from'); 404 | parsedSearchQuery.from.should.be.an.Array; 405 | parsedSearchQuery.from.length.should.equal(4); 406 | parsedSearchQuery.from.should.containEql('jul@foo.com'); 407 | parsedSearchQuery.from.should.containEql('bar@hey.ya'); 408 | parsedSearchQuery.from.should.containEql('a@b.c'); 409 | parsedSearchQuery.from.should.containEql('d@e.f'); 410 | parsedSearchQuery.should.have.property('offsets', [{ 411 | keyword: 'from', 412 | value: 'jul@foo.com,bar@hey.ya', 413 | offsetStart: 0, 414 | offsetEnd: 27 415 | }, { 416 | keyword: 'from', 417 | value: 'a@b.c,d@e.f', 418 | offsetStart: 28, 419 | offsetEnd: 44 420 | }, { 421 | text: 'ouch!#', 422 | offsetStart: 45, 423 | offsetEnd: 51 424 | }]); 425 | 426 | var parsedAfterStringifySearchQuery = searchquery.parse(searchquery.stringify(parsedSearchQuery, options), options); 427 | parsedAfterStringifySearchQuery.offsets = undefined; 428 | parsedSearchQuery.offsets = undefined; 429 | parsedAfterStringifySearchQuery.should.be.eql(parsedSearchQuery); 430 | }); 431 | 432 | it('should always return an array if alwaysArray is set to true', function () { 433 | var searchQuery = 'from:jul@foo.com to:a@b.c -cc:you@foo.com ouch!#'; 434 | 435 | var options = {keywords: ['from', 'to', 'cc'], alwaysArray: true}; 436 | var parsedSearchQuery = searchquery.parse(searchQuery, options); 437 | 438 | parsedSearchQuery.should.be.an.Object; 439 | parsedSearchQuery.should.have.property('text', 'ouch!#'); 440 | parsedSearchQuery.should.have.property('from'); 441 | parsedSearchQuery.should.have.property('to'); 442 | parsedSearchQuery.from.should.be.an.Array; 443 | parsedSearchQuery.to.should.be.an.Array; 444 | parsedSearchQuery.exclude.cc.should.be.an.Array; 445 | parsedSearchQuery.from.length.should.equal(1); 446 | parsedSearchQuery.to.length.should.equal(1); 447 | parsedSearchQuery.exclude.cc.length.should.equal(1); 448 | parsedSearchQuery.from.should.containEql('jul@foo.com'); 449 | parsedSearchQuery.to.should.containEql('a@b.c'); 450 | parsedSearchQuery.exclude.cc.should.containEql('you@foo.com'); 451 | parsedSearchQuery.should.have.property('offsets', [{ 452 | keyword: 'from', 453 | value: 'jul@foo.com', 454 | offsetStart: 0, 455 | offsetEnd: 16 456 | }, { 457 | keyword: 'to', 458 | value: 'a@b.c', 459 | offsetStart: 17, 460 | offsetEnd: 25 461 | }, { 462 | keyword: 'cc', 463 | value: 'you@foo.com', 464 | offsetStart: 27, 465 | offsetEnd: 41 466 | }, { 467 | text: 'ouch!#', 468 | offsetStart: 42, 469 | offsetEnd: 48 470 | }]); 471 | 472 | var parsedAfterStringifySearchQuery = searchquery.parse(searchquery.stringify(parsedSearchQuery, options), options); 473 | parsedAfterStringifySearchQuery.offsets = undefined; 474 | parsedSearchQuery.offsets = undefined; 475 | parsedAfterStringifySearchQuery.should.be.eql(parsedSearchQuery); 476 | }); 477 | 478 | it('should parse range with only 1 end and free text', function () { 479 | var searchQuery = 'date:12/12/2012 ahaha'; 480 | var options = {ranges: ['date']}; 481 | var parsedSearchQuery = searchquery.parse(searchQuery, options); 482 | 483 | parsedSearchQuery.should.be.an.Object; 484 | parsedSearchQuery.should.have.property('text', 'ahaha'); 485 | parsedSearchQuery.should.have.property('date'); 486 | parsedSearchQuery.date.should.be.an.Object; 487 | parsedSearchQuery.date.from.should.containEql('12/12/2012'); 488 | parsedSearchQuery.should.have.property('offsets', [{ 489 | keyword: 'date', 490 | value: '12/12/2012', 491 | offsetStart: 0, 492 | offsetEnd: 15 493 | }, { 494 | text: 'ahaha', 495 | offsetStart: 16, 496 | offsetEnd: 21 497 | }]); 498 | 499 | var parsedAfterStringifySearchQuery = searchquery.parse(searchquery.stringify(parsedSearchQuery, options), options); 500 | parsedAfterStringifySearchQuery.offsets = undefined; 501 | parsedSearchQuery.offsets = undefined; 502 | parsedAfterStringifySearchQuery.should.be.eql(parsedSearchQuery); 503 | }); 504 | 505 | it('should parse range with 2 ends and free text', function () { 506 | var searchQuery = 'date:12/12/2012-01/01/2014 ahaha'; 507 | var options = {ranges: ['date']}; 508 | var parsedSearchQuery = searchquery.parse(searchQuery, options); 509 | 510 | parsedSearchQuery.should.be.an.Object; 511 | parsedSearchQuery.should.have.property('text', 'ahaha'); 512 | parsedSearchQuery.should.have.property('date'); 513 | parsedSearchQuery.date.should.be.an.Object; 514 | parsedSearchQuery.date.from.should.containEql('12/12/2012'); 515 | parsedSearchQuery.date.to.should.containEql('01/01/2014'); 516 | parsedSearchQuery.should.have.property('offsets', [{ 517 | keyword: 'date', 518 | value: '12/12/2012-01/01/2014', 519 | offsetStart: 0, 520 | offsetEnd: 26 521 | }, { 522 | text: 'ahaha', 523 | offsetStart: 27, 524 | offsetEnd: 32 525 | }]); 526 | 527 | var parsedAfterStringifySearchQuery = searchquery.parse(searchquery.stringify(parsedSearchQuery, options), options); 528 | parsedAfterStringifySearchQuery.offsets = undefined; 529 | parsedSearchQuery.offsets = undefined; 530 | parsedAfterStringifySearchQuery.should.be.eql(parsedSearchQuery); 531 | }); 532 | 533 | 534 | it('should be able to parse unicode', function () { 535 | var searchQuery = '✓ about 这个事儿'; 536 | var parsedSearchQuery = searchquery.parse(searchQuery); 537 | 538 | parsedSearchQuery.should.be.a.string; 539 | parsedSearchQuery.should.be.equal('✓ about 这个事儿'); 540 | 541 | var parsedAfterStringifySearchQuery = searchquery.parse(searchquery.stringify(parsedSearchQuery)); 542 | parsedAfterStringifySearchQuery.offsets = undefined; 543 | parsedSearchQuery.offsets = undefined; 544 | parsedAfterStringifySearchQuery.should.be.eql(parsedSearchQuery); 545 | }); 546 | 547 | 548 | it('should be able to parse unicode with keywords and funny spacing', function () { 549 | var searchQuery = ' ✓ about 这个事儿 from:dr@who.co.uk '; 550 | var options = {keywords: ['from']}; 551 | var parsedSearchQuery = searchquery.parse(searchQuery, options); 552 | 553 | parsedSearchQuery.should.be.an.Object; 554 | parsedSearchQuery.should.have.property('text', '✓ about 这个事儿'); 555 | parsedSearchQuery.should.have.property('from', 'dr@who.co.uk'); 556 | 557 | var parsedAfterStringifySearchQuery = searchquery.parse(searchquery.stringify(parsedSearchQuery, options), options); 558 | parsedAfterStringifySearchQuery.offsets = undefined; 559 | parsedSearchQuery.offsets = undefined; 560 | parsedAfterStringifySearchQuery.should.be.eql(parsedSearchQuery); 561 | }); 562 | 563 | 564 | it('should handle absurdly complex and long query', function () { 565 | var searchQuery = ' date:12/12/2012-01/01/2014 ahaha from:jul@foo.com,bar@hey.ya from:a@b.c,d@e.f ouch!# to:me@me.com to:toto@hey.co about that'; 566 | var options = {ranges: ['date'], keywords: ['from', 'to']}; 567 | var parsedSearchQuery = searchquery.parse(searchQuery, options); 568 | 569 | parsedSearchQuery.should.be.an.Object; 570 | parsedSearchQuery.should.have.property('text', 'ahaha ouch!# about that'); 571 | parsedSearchQuery.should.have.property('date'); 572 | parsedSearchQuery.date.should.be.an.Object; 573 | parsedSearchQuery.date.from.should.containEql('12/12/2012'); 574 | parsedSearchQuery.date.to.should.containEql('01/01/2014'); 575 | parsedSearchQuery.should.have.property('from'); 576 | parsedSearchQuery.from.should.be.an.Array; 577 | parsedSearchQuery.from.length.should.equal(4); 578 | parsedSearchQuery.from.should.containEql('jul@foo.com'); 579 | parsedSearchQuery.from.should.containEql('bar@hey.ya'); 580 | parsedSearchQuery.from.should.containEql('a@b.c'); 581 | parsedSearchQuery.from.should.containEql('d@e.f'); 582 | parsedSearchQuery.should.have.property('to'); 583 | parsedSearchQuery.to.should.be.an.Array; 584 | parsedSearchQuery.to.length.should.equal(2); 585 | parsedSearchQuery.to.should.containEql('me@me.com'); 586 | parsedSearchQuery.to.should.containEql('toto@hey.co'); 587 | parsedSearchQuery.should.have.property('offsets', [{ 588 | keyword: 'date', 589 | value: '12/12/2012-01/01/2014', 590 | offsetStart: 3, 591 | offsetEnd: 29 592 | }, { 593 | text: 'ahaha', 594 | offsetStart: 30, 595 | offsetEnd: 35 596 | }, { 597 | keyword: 'from', 598 | value: 'jul@foo.com,bar@hey.ya', 599 | offsetStart: 36, 600 | offsetEnd: 63 601 | }, { 602 | keyword: 'from', 603 | value: 'a@b.c,d@e.f', 604 | offsetStart: 64, 605 | offsetEnd: 80 606 | }, { 607 | text: 'ouch!#', 608 | offsetStart: 81, 609 | offsetEnd: 87 610 | }, { 611 | keyword: 'to', 612 | value: 'me@me.com', 613 | offsetStart: 90, 614 | offsetEnd: 102 615 | }, { 616 | keyword: 'to', 617 | value: 'toto@hey.co', 618 | offsetStart: 103, 619 | offsetEnd: 117 620 | }, { 621 | text: 'about', 622 | offsetStart: 118, 623 | offsetEnd: 123 624 | }, { 625 | text: 'that', 626 | offsetStart: 124, 627 | offsetEnd: 128 628 | }]); 629 | 630 | var parsedAfterStringifySearchQuery = searchquery.parse(searchquery.stringify(parsedSearchQuery, options), options); 631 | parsedAfterStringifySearchQuery.offsets = undefined; 632 | parsedSearchQuery.offsets = undefined; 633 | parsedAfterStringifySearchQuery.should.be.eql(parsedSearchQuery); 634 | }); 635 | 636 | 637 | it('should not split on spaces inside single and double quotes', function () { 638 | var searchQuery = 'name:"Bob Saget" description:\'Banana Sandwiche\''; 639 | var options = {keywords: ['name', 'description']}; 640 | var parsedSearchQuery = searchquery.parse(searchQuery, options); 641 | 642 | parsedSearchQuery.should.be.an.Object; 643 | parsedSearchQuery.should.have.property('name', 'Bob Saget'); 644 | parsedSearchQuery.should.have.property('description', 'Banana Sandwiche'); 645 | parsedSearchQuery.should.have.property('offsets', [{ 646 | keyword: 'name', 647 | value: 'Bob Saget', 648 | offsetStart: 0, 649 | offsetEnd: 16 650 | }, { 651 | keyword: 'description', 652 | value: 'Banana Sandwiche', 653 | offsetStart: 17, 654 | offsetEnd: 47 655 | }]); 656 | 657 | var parsedAfterStringifySearchQuery = searchquery.parse(searchquery.stringify(parsedSearchQuery, options), options); 658 | parsedAfterStringifySearchQuery.offsets = undefined; 659 | parsedSearchQuery.offsets = undefined; 660 | parsedAfterStringifySearchQuery.should.be.eql(parsedSearchQuery); 661 | }); 662 | 663 | 664 | it('should correctly handle escaped single and double quotes', function () { 665 | var searchQuery = 'case1:"This \\"is\\" \'a\' test" case2:\'This "is" \\\'a\\\' test\''; 666 | var options = {keywords: ['case1', 'case2']}; 667 | var parsedSearchQuery = searchquery.parse(searchQuery, options); 668 | 669 | parsedSearchQuery.should.be.an.Object; 670 | parsedSearchQuery.should.have.property('case1', 'This "is" \'a\' test'); 671 | parsedSearchQuery.should.have.property('case2', 'This "is" \'a\' test'); 672 | parsedSearchQuery.should.have.property('offsets', [{ 673 | keyword: 'case1', 674 | value: 'This "is" \'a\' test', 675 | offsetStart: 0, 676 | offsetEnd: 28 677 | }, { 678 | keyword: 'case2', 679 | value: 'This "is" \'a\' test', 680 | offsetStart: 29, 681 | offsetEnd: 57 682 | }]); 683 | 684 | var parsedAfterStringifySearchQuery = searchquery.parse(searchquery.stringify(parsedSearchQuery, options), options); 685 | parsedAfterStringifySearchQuery.offsets = undefined; 686 | parsedSearchQuery.offsets = undefined; 687 | parsedAfterStringifySearchQuery.should.be.eql(parsedSearchQuery); 688 | }); 689 | 690 | 691 | it('should parse a single keyword query in exclusion syntax', function() { 692 | var searchQuery = '-from:jul@foo.com'; 693 | var options = {keywords: ['from']}; 694 | var parsedSearchQuery = searchquery.parse(searchQuery, options); 695 | 696 | parsedSearchQuery.should.be.an.Object; 697 | parsedSearchQuery.exclude.should.be.an.Object; 698 | parsedSearchQuery.exclude.should.have.property('from', 'jul@foo.com'); 699 | parsedSearchQuery.should.not.have.property('text'); 700 | parsedSearchQuery.should.have.property('offsets', [{ 701 | keyword: 'from', 702 | value: 'jul@foo.com', 703 | offsetStart: 1, 704 | offsetEnd: 17 705 | }]); 706 | 707 | var parsedAfterStringifySearchQuery = searchquery.parse(searchquery.stringify(parsedSearchQuery, options), options); 708 | parsedAfterStringifySearchQuery.offsets = undefined; 709 | parsedSearchQuery.offsets = undefined; 710 | parsedAfterStringifySearchQuery.should.be.eql(parsedSearchQuery); 711 | }); 712 | 713 | it('should concatenate a keyword multiple values in exclusion syntax', function() { 714 | var searchQuery = '-from:jul@foo.com,mar@foo.com'; 715 | var options = {keywords: ['from']}; 716 | var parsedSearchQuery = searchquery.parse(searchQuery, options); 717 | 718 | parsedSearchQuery.should.be.an.Object; 719 | parsedSearchQuery.exclude.should.be.an.Object; 720 | parsedSearchQuery.exclude.from.should.containEql('jul@foo.com'); 721 | parsedSearchQuery.exclude.from.should.containEql('mar@foo.com'); 722 | parsedSearchQuery.should.not.have.property('text'); 723 | parsedSearchQuery.should.have.property('offsets', [{ 724 | keyword: 'from', 725 | value: 'jul@foo.com,mar@foo.com', 726 | offsetStart: 1, 727 | offsetEnd: 29 728 | }]); 729 | 730 | var parsedAfterStringifySearchQuery = searchquery.parse(searchquery.stringify(parsedSearchQuery, options), options); 731 | parsedAfterStringifySearchQuery.offsets = undefined; 732 | parsedSearchQuery.offsets = undefined; 733 | parsedAfterStringifySearchQuery.should.be.eql(parsedSearchQuery); 734 | }); 735 | 736 | it('should support keywords which appear multiple times with exclusion syntax', function() { 737 | var searchQuery = '-from:jul@foo.com,mar@foo.com -from:jan@foo.com'; 738 | var options = { 739 | keywords: ['from'] 740 | }; 741 | var parsedSearchQuery = searchquery.parse(searchQuery, options); 742 | 743 | parsedSearchQuery.should.be.an.Object; 744 | parsedSearchQuery.exclude.should.be.an.Object; 745 | parsedSearchQuery.exclude.from.should.containEql('jul@foo.com'); 746 | parsedSearchQuery.exclude.from.should.containEql('mar@foo.com'); 747 | parsedSearchQuery.exclude.from.should.containEql('jan@foo.com'); 748 | parsedSearchQuery.should.not.have.property('text'); 749 | parsedSearchQuery.should.have.property('offsets', [{ 750 | keyword: 'from', 751 | value: 'jul@foo.com,mar@foo.com', 752 | offsetStart: 1, 753 | offsetEnd: 29 754 | }, { 755 | keyword: 'from', 756 | value: 'jan@foo.com', 757 | offsetStart: 31, 758 | offsetEnd: 47 759 | }]); 760 | 761 | var parsedAfterStringifySearchQuery = searchquery.parse(searchquery.stringify(parsedSearchQuery, options), options); 762 | parsedAfterStringifySearchQuery.offsets = undefined; 763 | parsedSearchQuery.offsets = undefined; 764 | parsedAfterStringifySearchQuery.should.be.eql(parsedSearchQuery); 765 | }); 766 | 767 | it('should not return offset when offsets option is set to false', function() { 768 | var searchQuery = '-from:jul@foo.com,mar@foo.com to:bar@hey.ya about date:12/12/2012'; 769 | var options = { 770 | keywords: ['from', 'to'], 771 | ranges: ['date'], 772 | offsets: false 773 | }; 774 | var parsedSearchQuery = searchquery.parse(searchQuery, options); 775 | 776 | parsedSearchQuery.should.be.an.Object; 777 | parsedSearchQuery.exclude.should.be.an.Object; 778 | parsedSearchQuery.exclude.from.should.containEql('jul@foo.com'); 779 | parsedSearchQuery.exclude.from.should.containEql('mar@foo.com'); 780 | parsedSearchQuery.to.should.containEql('bar@hey.ya'); 781 | parsedSearchQuery.should.have.property('text', 'about'); 782 | parsedSearchQuery.should.have.property('date'); 783 | parsedSearchQuery.date.from.should.containEql('12/12/2012'); 784 | parsedSearchQuery.should.not.have.property('offsets'); 785 | 786 | var parsedAfterStringifySearchQuery = searchquery.parse(searchquery.stringify(parsedSearchQuery, options), options); 787 | parsedAfterStringifySearchQuery.offsets = undefined; 788 | parsedSearchQuery.offsets = undefined; 789 | parsedAfterStringifySearchQuery.should.be.eql(parsedSearchQuery); 790 | }); 791 | }); 792 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "target": "es5", 5 | "composite": true, 6 | "sourceMap": true, 7 | "declaration": true, 8 | "declarationMap": true 9 | }, 10 | "include": [ 11 | "./lib/**/*" 12 | ], 13 | "exclude": [], 14 | "typedocOptions": { 15 | "mode": "modules", 16 | "out": "docs", 17 | "include": "./lib", 18 | "exclude": ["./node_modules/**/*"], 19 | "module": "commonjs", 20 | "target": "es5", 21 | "includeDeclarations": true, 22 | "theme": "markdown", 23 | "readme": "none" 24 | } 25 | } -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "dtslint/dt.json" 3 | } --------------------------------------------------------------------------------