├── .github └── workflows │ └── blank.yml ├── .gitignore ├── .npmignore ├── .prettierrc ├── LICENSE ├── README.md ├── cli.ts ├── docs ├── autocomplete.gif └── readme_0.gif ├── index.ts ├── lib ├── autocomplete.ts ├── cache.ts ├── create.ts ├── directiveParser.ts ├── make_fake_expression.ts └── utils.ts ├── package-lock.json ├── package.json ├── sql.ts ├── test ├── cache.ts ├── directives.ts └── utils.ts ├── tsconfig.json └── typings.d.ts /.github/workflows/blank.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: [push] 4 | 5 | jobs: 6 | build: 7 | runs-on: ubuntu-latest 8 | strategy: 9 | matrix: 10 | node: ["12", "14", "16"] 11 | name: Node ${{ matrix.node }} tests 12 | steps: 13 | - uses: actions/checkout@v1 14 | - name: Tests 15 | uses: actions/setup-node@v1 16 | with: 17 | node-version: ${{ matrix.node }} 18 | - run: npm install 19 | - run: npm test 20 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # common 2 | .cache/ 3 | .vscode/ 4 | .gitignore.d/ 5 | node_modules/ 6 | dist/ 7 | *.map 8 | 9 | # specific 10 | *.js 11 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | # common 2 | .cache/ 3 | .vscode/ 4 | .github/ 5 | .gitignore.d/ 6 | 7 | # specific 8 | .prettierrc 9 | node_modules 10 | test 11 | tsconfig.json 12 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "tabWidth": 2, 3 | "useTabs": false, 4 | "printWidth": 120, 5 | "arrowParens": "avoid", 6 | "trailingComma": "all", 7 | "endOfLine": "lf", 8 | "bracketSpacing": true 9 | } 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 xialvjun 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ts-sql-plugin 2 | TypeScript Language Service Plugin for SQL with a tagged template strings SQL builder. Inspired by [andywer/squid](https://github.com/andywer/squid) 3 | 4 | #### check sql error demo: 5 | ![readme_0](./docs/readme_0.gif) 6 | 7 | #### autocomplete demo: 8 | ![autocomplete](./docs/autocomplete.gif) 9 | 10 | # Usage 11 | 12 | ## run as a language service plugin 13 | 14 | Install the plugin, run: 15 | 16 | ```sh 17 | npm install ts-sql-plugin -D 18 | ``` 19 | 20 | Then, configure the `plugins` section in your *tsconfig.json*: 21 | 22 | ```json 23 | { 24 | "compilerOptions": { 25 | "module": "commonjs", 26 | "target": "es5", 27 | "plugins": [ 28 | { 29 | "name": "ts-sql-plugin", 30 | "command": "psql -c", // optionnal 31 | "tags": { // optionnal 32 | "sql": "sql", 33 | "raw": "raw", 34 | "cond": "cond", 35 | "and": "and", 36 | "or": "or", 37 | "ins": "ins", 38 | "upd": "upd", 39 | "mock": "mock" 40 | }, 41 | "mock": "0", 42 | "cost_pattern": "/\\(cost=\\d+\\.?\\d*\\.\\.(\\d+\\.?\\d*)/", 43 | "error_cost": null, // 100, 44 | "warn_cost": null, // 50, 45 | "info_cost": null, // -1, 46 | "schema_command": "pg" // pg | mysql | custom - read the source 47 | } 48 | ] 49 | } 50 | } 51 | ``` 52 | 53 | **Note**: If you're using Visual Studio Code, you'll have to use the first approach above, with a 54 | path to the module, or run the "TypeScript: Select TypeScript Version" command and choose "Use 55 | Workspace Version", or click the version number between "TypeScript" and 😃 in the lower-right 56 | corner. Otherwise, VS Code will not be able to find your plugin. See https://github.com/microsoft/TypeScript/wiki/Writing-a-Language-Service-Plugin#testing-locally 57 | 58 | ## run as a command line application 59 | 60 | Install the plugin, run: 61 | 62 | ```sh 63 | npm install ts-sql-plugin -g 64 | ``` 65 | 66 | Then run: 67 | 68 | ```sh 69 | ts-sql-plugin -p ./my_ts_project -c 'psql -U postgres -c' 70 | ``` 71 | 72 | # Then in your code: 73 | 74 | In the code below, some of the table names or column names are intentionally wrong. `ts-sql-plugin` will show you the errors. 75 | 76 | ```ts 77 | import sql from 'ts-sql-plugin/sql'; 78 | 79 | sql`select * from wrong_table_name where wrong_column_name=${name}`; 80 | // { text: "select * from wrong_table_name where wrong_column_name=??", values: [name] } 81 | // you should transform the text to pg specific or mysql specific query text 82 | 83 | // sql.and 84 | sql`select * from person where ${sql.and({ wrong_column_name: value, name: name })}`; 85 | 86 | // sql.or 87 | sql`select * from person where ${sql.or([{ 'name like': 'abc%', age: 23 }, { 'age >': 23 }])}`; 88 | sql`select * from person where (name like ${'abc%'} and age=${23}) or age > ${23}`; 89 | 90 | // sql.ins 91 | sql`insert into person ${sql.ins({ id: uuid(), name: name, ageeee: wrong_column_name_value })}`; 92 | sql`insert into person ${sql.ins([{ id: uuid(), name, age: 23 }, {id, name:'ppp', age:30}])}`; 93 | 94 | // sql.upd 95 | sql`update person set ${sql.upd({ wrong_name_column: name, age: 23 })} where id=${id}`; 96 | 97 | // like, >, < etc 98 | sql`select * from person where ${sql.and({ 'name like': '%'+name_like+'%', 'ageee >': age_bigger_than })}`; 99 | 100 | // sql.raw with ?: operator 101 | sql`select * from person order by age ${reverse ? sql.raw`desc` : sql.raw`asc`}`; 102 | 103 | // sql.cond 104 | sql`select * from person where name=${name} ${sql.cond(!!age_bigger_than)` and ageeee > ${age_bigger_than}`}`; 105 | 106 | // ! where in will produce error because of node-postgres doesn't support it. use where column=any() 107 | sql`select * from person where id in (${[uuid(), uuid()]})`; 108 | sql`select * from person where id = any(${[uuid(), uuid()]})`; 109 | sql`select * from ${sql.mock<'person' | 'book'>(sql.raw([table_name]))}` 110 | sql`select * from person where age = ${sql.mock<'12'>(some_value_can_not_use_the_default_mock__0)}` 111 | 112 | // you can use sql queries inside each other 113 | const s1 = sql`select * from subscriptions.attribute where entity_id = any(${[7045]})`; 114 | const s2 = sql`select * from (${s1}) attr where attribute_id = any(${[7049, 7050]})`; 115 | 116 | // ignore cost for query stuff implemented 117 | const s1 = sql` 118 | -- ts-sql-plugin:ignore-cost 119 | select * from subscriptions.attribute 120 | `; 121 | 122 | // you can emit sql to explicit file 123 | // this will be emitted to emit-sql/AllAttribute.sql file 124 | // you may change `emit-sql` folder to another via `--emit_dir` option of cli 125 | // also `--watch` option of cli can be used to emit it in realtime 126 | const s1 = sql` 127 | /* ts-sql-plugin:emit("AllAttribute") */ 128 | /* @name AllAttribute */ 129 | select * from subscriptions.attribute 130 | `; 131 | ``` 132 | 133 | **And there is a complete example using [ts-sql-plugin](https://github.com/xialvjun/ts-sql-plugin) and [skm_ts](https://github.com/xialvjun/skm_ts) in folder test_ts_sql_plugin.** 134 | 135 | ## Generate types from emitted sql 136 | 137 | You can use [pgtyped](https://github.com/adelsz/pgtyped) tool 138 | for generating types from your emitted sql via directive: 139 | ``` 140 | /* ts-sql-plugin:emit("SomeInterface") */ 141 | /* @name SomeInterface */ 142 | ``` 143 | 144 | For generate types in realtime, use `--watch` option of cli. 145 | 146 | ## VSCode syntax highlight extension 147 | 148 | https://marketplace.visualstudio.com/items?itemName=darky.vscode-ts-sql-plugin 149 | -------------------------------------------------------------------------------- /cli.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | import crypto from "crypto"; 4 | import fs from "fs"; 5 | import path from "path"; 6 | import spawn from "await-spawn"; 7 | import flattenDeep from "lodash.flattendeep"; 8 | 9 | import ts from "typescript"; 10 | import { program } from "commander"; 11 | import shq from "shell-quote"; 12 | 13 | import { 14 | default_mock, 15 | default_cost_pattern, 16 | default_tags, 17 | default_command, 18 | merge_defaults, 19 | get_all_ts_files, 20 | report, 21 | TsSqlPluginConfig, 22 | trim_middle_comments, 23 | } from "./lib/utils"; 24 | import { make_fake_expression } from "./lib/make_fake_expression"; 25 | import { directiveRegex, parseDirectives } from "./lib/directiveParser"; 26 | import { addToSqlCache, existsInSqlCache } from './lib/cache'; 27 | 28 | program 29 | .option("-w, --watch", `watch mode of cli`) 30 | .option("-p, --project ", "ts project path where tsconfig.json is in", "./") 31 | .option("-e, --exclude ", "regexp to exclude files", "node_modules") 32 | .option("--emit_dir ", "where sqls will be emitted") 33 | // ! --mock, --error_cost, --warn_cost, --info_cost, --cost_pattern, --tags, --command are the same as plugin options 34 | // ! do not use commander default option value, to use the options in tsconfig.json 35 | .option( 36 | "-m, --mock ", 37 | `string to mock the values in the fake sqls, you can override it with: sql.mock<'null'>(any_value) (default: "${default_mock}")`, 38 | ) 39 | .option("--error_cost ", `throw error if explain cost exceeds treshold`) 40 | .option("--warn_cost ", `log warning if explain cost exceeds treshold`) 41 | .option("--info_cost ", `log info if explain cost exceeds treshold`) 42 | .option( 43 | "--cost_pattern ", 44 | `regexp to extract cost from command stdout, make sure the number is the first capturing group (default: "/${default_cost_pattern.source}/")`, 45 | // (str, pv) => new RegExp(str || pv), 46 | // default_cost_pattern.source as any as RegExp 47 | ) 48 | .option( 49 | "-t, --tags ", 50 | `tags you used in you ts file (default: "${Object.entries(default_tags) 51 | .map(it => it.join("=")) 52 | .join(",")}")`, 53 | (str, pv) => { 54 | return Object.fromEntries(str.split(",").map(s => s.split("="))); 55 | // return [(pv as any) as string, str] 56 | // .map((it) => Object.fromEntries(it.split(",").map((s) => s.split("=")))) 57 | // .reduce((acc, cv) => ({ ...acc, ...cv }), {}) as typeof default_tags; 58 | }, 59 | // (Object.entries(default_tags) 60 | // .map((it) => it.join("=")) 61 | // .join(",") as any) as typeof default_tags 62 | ) 63 | .option( 64 | "-c, --command ", 65 | `command to be run to explain the fake sql (default: "${default_command}")` /* default_command */, 66 | ) 67 | .description( 68 | `Explain all your sqls in your code to test them. \n\nEg: ts-sql-plugin -p ./my_ts_projet/tsconfig.json -c 'psql -c'`, 69 | ) 70 | .action(async () => { 71 | let cli_config: any = program.opts(); 72 | if (typeof cli_config.command !== "string") { 73 | cli_config.command = undefined; 74 | } 75 | cli_config.exclude = new RegExp(cli_config.exclude); 76 | 77 | const ts_config_path = ts.findConfigFile(cli_config.project, ts.sys.fileExists, "tsconfig.json"); 78 | if (!ts_config_path) { 79 | throw new Error("Could not find a valid 'tsconfig.json'."); 80 | } 81 | const { config: ts_config } = ts.parseConfigFileTextToJson( 82 | ts_config_path, 83 | await fs.promises.readFile(ts_config_path, { encoding: "utf8" }), 84 | ); 85 | let plugin_config: TsSqlPluginConfig = (ts_config.compilerOptions.plugins as any[])?.find( 86 | it => it.name === "ts-sql-plugin", 87 | ); 88 | plugin_config = merge_defaults(plugin_config, cli_config); 89 | const cost_pattern = new RegExp(plugin_config.cost_pattern!); 90 | 91 | const initProgram = ts.createProgram(get_all_ts_files(path.dirname(ts_config_path)), ts_config.compilerOptions); 92 | let fake_expression = make_fake_expression(initProgram.getTypeChecker(), plugin_config.tags); 93 | 94 | if (cli_config.watch) { 95 | const watchHost = ts.createWatchCompilerHost(ts_config_path, undefined, ts.sys); 96 | const watchProgram = ts.createWatchProgram(watchHost); 97 | const onChangeFile = (fileName: string) => { 98 | const file = watchProgram.getProgram().getSourceFile(fileName); 99 | if (file) { 100 | fake_expression = make_fake_expression( 101 | watchProgram.getProgram().getProgram().getTypeChecker(), 102 | plugin_config.tags, 103 | ); 104 | delint(file); 105 | } 106 | }; 107 | const watchFile = (sourceFile: ts.SourceFile) => { 108 | watchHost.watchFile(sourceFile.fileName, (fileName, event) => { 109 | if (event !== ts.FileWatcherEventKind.Deleted) { 110 | onChangeFile(fileName); 111 | } 112 | }); 113 | }; 114 | const source_files = watchProgram 115 | .getProgram() 116 | .getSourceFiles() 117 | .filter(it => !cli_config.exclude.test(it.fileName)); 118 | source_files.forEach(watchFile); 119 | source_files.forEach(it => onChangeFile(it.fileName)); 120 | return; 121 | } 122 | 123 | let has_error = false; 124 | let report_errors: [sourceFile: ts.SourceFile, node: ts.Node, message: string, level?: 1 | 2][] = []; 125 | console.log("-- Start init sql check and emit..."); 126 | for (const file of initProgram.getSourceFiles().filter(f => !cli_config.exclude.test(f.fileName))) { 127 | await delint(file); 128 | } 129 | if (has_error) { 130 | console.error("\n\n-- Your code can not pass all sql test!!!\n"); 131 | report_errors.forEach(args => report(...args)); 132 | process.exit(1); 133 | } 134 | console.log("\n\n-- Init sql check and emit finished.\n"); 135 | 136 | function recursiveAllChildrenNodes(node: ts.Node): ts.Node[] { 137 | return (node.getChildren().map(n => [n, ...recursiveAllChildrenNodes(n)]) as unknown) as ts.Node[]; 138 | } 139 | 140 | async function delint(sourceFile: ts.SourceFile) { 141 | const treeNodes = recursiveAllChildrenNodes(sourceFile); 142 | const sqlTagNodes = flattenDeep(treeNodes).filter( 143 | (n): n is ts.TaggedTemplateExpression => 144 | n.kind === ts.SyntaxKind.TaggedTemplateExpression && 145 | (n as ts.TaggedTemplateExpression).tag.getText() === plugin_config.tags.sql, 146 | ); 147 | 148 | await Promise.all(sqlTagNodes.map(delintNode)); 149 | 150 | async function delintNode(node: ts.TaggedTemplateExpression) { 151 | let query_configs = fake_expression(node); 152 | for (const qc of query_configs) { 153 | let s: string = trim_middle_comments(qc.text).replace(/\?\?/gm, plugin_config.mock); 154 | 155 | const directives = parseDirectives(s); 156 | if (cli_config.emit_dir) { 157 | const emit_directive = directives.find(d => d.directive === "emit"); 158 | if (emit_directive) { 159 | const fileName = (emit_directive.arg as string) ?? crypto.createHash("sha1").update(s).digest("hex"); 160 | const filePath = `${cli_config.emit_dir}/${fileName}.sql`; 161 | try { 162 | fs.writeFileSync(filePath, s + ";"); 163 | } catch (err) { 164 | console.log(`-- Emit Error occured, when emitting file "${filePath}"`); 165 | } 166 | } 167 | } 168 | 169 | console.log(`\n\n-- EXPLAIN\n${s};`); 170 | const [_command, ..._command_args] = (shq 171 | .parse(plugin_config.command) 172 | .concat("EXPLAIN " + s) as any) as string[]; 173 | 174 | if (existsInSqlCache(_command, _command_args)) { 175 | continue; 176 | } 177 | 178 | const p = await spawn(_command, _command_args).catch((e: Error & { stderr: string }) => e); 179 | const stdout = p.toString(); 180 | if (p instanceof Error) { 181 | has_error = true; 182 | report(sourceFile, node, p.stderr); 183 | report_errors.push([sourceFile, node, p.stderr, 2]); 184 | break; 185 | } 186 | 187 | if ( 188 | (plugin_config.error_cost || plugin_config.warn_cost || plugin_config.info_cost) && 189 | !directives.some(d => d.directive === "ignore-cost") 190 | ) { 191 | const match = stdout.match(cost_pattern); 192 | if (match) { 193 | const [_, cost_str] = match; 194 | const cost = Number(cost_str); 195 | if (cost > plugin_config.error_cost!) { 196 | has_error = true; 197 | report(sourceFile, node, `Error: explain cost is too high: ${cost}\n${s}`, 2); 198 | report_errors.push([sourceFile, node, `Error: explain cost is too high: ${cost}\n${s}`, 2]); 199 | break; 200 | } else if (cost > plugin_config.warn_cost!) { 201 | report(sourceFile, node, `Warn: explain cost is at warning: ${cost}\n${s}`, 2); 202 | } else if (cost > plugin_config.info_cost!) { 203 | report(sourceFile, node, `Info: explain cost is ok: ${cost}\n${s}`, 1); 204 | } 205 | } else { 206 | has_error = true; 207 | report( 208 | sourceFile, 209 | node, 210 | `Error: can not extract cost with cost_pattern: ${cost_pattern.source}\n${stdout}\n${s}`, 211 | 2, 212 | ); 213 | report_errors.push([ 214 | sourceFile, 215 | node, 216 | `Error: can not extract cost with cost_pattern: ${cost_pattern.source}\n${stdout}\n${s}`, 217 | 2, 218 | ]); 219 | break; 220 | } 221 | } 222 | 223 | addToSqlCache(_command, _command_args); 224 | } 225 | } 226 | } 227 | }) 228 | .parseAsync(process.argv); 229 | -------------------------------------------------------------------------------- /docs/autocomplete.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xialvjun/ts-sql-plugin/34c9fed4d7e0b042f52148a0d906b3d2ec277e40/docs/autocomplete.gif -------------------------------------------------------------------------------- /docs/readme_0.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xialvjun/ts-sql-plugin/34c9fed4d7e0b042f52148a0d906b3d2ec277e40/docs/readme_0.gif -------------------------------------------------------------------------------- /index.ts: -------------------------------------------------------------------------------- 1 | import tss from "typescript/lib/tsserverlibrary"; 2 | import { makeCreate } from './lib/create'; 3 | 4 | export = (mod: { typescript: typeof tss }) => { 5 | const create = makeCreate(mod); 6 | return { create }; 7 | }; 8 | -------------------------------------------------------------------------------- /lib/autocomplete.ts: -------------------------------------------------------------------------------- 1 | import { TemplateContext, TemplateLanguageService } from "typescript-template-language-service-decorator"; 2 | import tss from "typescript/lib/tsserverlibrary"; 3 | import { Index } from "lunr"; 4 | 5 | import { SchemaInfo } from "./utils"; 6 | 7 | export class SqlTemplateAutocomplete implements TemplateLanguageService { 8 | constructor(private db_info_search_index: Index, private db_info: SchemaInfo) {} 9 | 10 | getCompletionEntryDetails({}, {}, name: string): tss.CompletionEntryDetails { 11 | const documentation = this.db_info_search_index.search(name).map(({ ref }) => { 12 | const db_info_item = this.db_info[ref as any]; 13 | return { 14 | kind: tss.ScriptElementKind.keyword, 15 | text: ` 16 | schema: ${db_info_item.schema}, 17 | table: ${db_info_item.table}, 18 | column: ${db_info_item.column} 19 | `.replace(/ /gm, " "), 20 | }; 21 | }); 22 | return { 23 | name, 24 | displayParts: [], 25 | kind: tss.ScriptElementKind.keyword, 26 | kindModifiers: "ts-sql-plugin", 27 | documentation, 28 | }; 29 | } 30 | 31 | getCompletionsAtPosition(context: TemplateContext, position: tss.LineAndCharacter): tss.CompletionInfo { 32 | const entries = new Map(); 33 | const query = context.text 34 | .split(/\n/g) 35 | [position.line].slice(0, position.character) 36 | .match(/[\s\.]+([\w]*)$/)?.[1]; 37 | if (query) { 38 | this.db_info_search_index.search(`*${query}*`).forEach(({ ref }) => { 39 | const db_info_item = this.db_info[ref as any]; 40 | if (db_info_item.schema.match(query)) { 41 | entries.set(`schema|${db_info_item.schema}`, { 42 | name: db_info_item.schema, 43 | kind: tss.ScriptElementKind.keyword, 44 | sortText: `3 - ${db_info_item.schema}`, 45 | }); 46 | } 47 | if (db_info_item.table.match(query)) { 48 | entries.set(`table|${db_info_item.table}`, { 49 | name: db_info_item.table, 50 | kind: tss.ScriptElementKind.string, 51 | sortText: `2 - ${db_info_item.table}`, 52 | }); 53 | } 54 | if (db_info_item.column.match(query)) { 55 | entries.set(`column|${db_info_item.column}`, { 56 | name: db_info_item.column, 57 | kind: tss.ScriptElementKind.constElement, 58 | sortText: `1 - ${db_info_item.column}`, 59 | }); 60 | } 61 | }); 62 | } 63 | return { 64 | isGlobalCompletion: false, 65 | isMemberCompletion: false, 66 | isNewIdentifierLocation: false, 67 | entries: Array.from(entries.values()), 68 | }; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /lib/cache.ts: -------------------------------------------------------------------------------- 1 | import crypto from 'crypto'; 2 | 3 | const sqlCache = new Set(); 4 | 5 | export const addToSqlCache = (command: string, command_args: string[]) => { 6 | return sqlCache.add( 7 | crypto.createHash('sha1').update(`${command}${command_args.join()}`).digest('hex') 8 | ); 9 | }; 10 | 11 | export const existsInSqlCache = (command: string, command_args: string[]) => { 12 | return sqlCache.has( 13 | crypto.createHash('sha1').update(`${command}${command_args.join()}`).digest('hex') 14 | ); 15 | }; 16 | -------------------------------------------------------------------------------- /lib/create.ts: -------------------------------------------------------------------------------- 1 | import child_process from "child_process"; 2 | 3 | import tss from "typescript/lib/tsserverlibrary"; 4 | import { decorateWithTemplateLanguageService } from "typescript-template-language-service-decorator"; 5 | import shq from "shell-quote"; 6 | import lunr from "lunr"; 7 | 8 | import { find_all_nodes, merge_defaults } from "./utils"; 9 | import { make_fake_expression } from "./make_fake_expression"; 10 | import { SqlTemplateAutocomplete } from "./autocomplete"; 11 | import { parseDirectives } from "./directiveParser"; 12 | import { addToSqlCache, existsInSqlCache } from './cache'; 13 | 14 | export function makeCreate(mod: { typescript: typeof tss }) { 15 | return function create(info: tss.server.PluginCreateInfo): tss.LanguageService { 16 | const logger = (msg: string) => info.project.projectService.logger.info(`[ts-sql-plugin] ${msg}`); 17 | 18 | const config = merge_defaults(info.config); 19 | const cost_pattern = new RegExp(config.cost_pattern!); 20 | 21 | const proxy = new Proxy(info.languageService, { 22 | get(target, p, receiver) { 23 | if (p === "getSemanticDiagnostics") { 24 | return function getSemanticDiagnostics(fileName: string): tss.Diagnostic[] { 25 | let origin_diagnostics = target.getSemanticDiagnostics(fileName); 26 | 27 | const program = info.languageService.getProgram()!; 28 | const fake_expression = make_fake_expression(program.getTypeChecker(), config.tags); 29 | 30 | const source_file = program.getSourceFile(fileName)!; 31 | const nodes: tss.TaggedTemplateExpression[] = find_all_nodes( 32 | source_file, 33 | n => 34 | n.kind === tss.SyntaxKind.TaggedTemplateExpression && 35 | (n as tss.TaggedTemplateExpression).tag.getText() === config.tags.sql, 36 | ) as any; 37 | 38 | const explain_rss = nodes.map(n => { 39 | const make_diagnostic = (code: any, category: any, messageText: any) => 40 | ({ 41 | file: source_file, 42 | start: n.getStart(), 43 | length: n.getEnd() - n.getStart(), 44 | source: "ts-sql-plugin", 45 | code, 46 | category, 47 | messageText, 48 | } as tss.Diagnostic); 49 | // one sql`select * from person ${xxx ? sql.raw`aaa` : sql.raw`bbb`}` may generate two sqls, need to be explained one by one 50 | let query_configs = fake_expression(n); 51 | for (const qc of query_configs) { 52 | let s: string = qc.text.replace(/\?\?/gm, config.mock); 53 | let resp = make_diagnostic(1, tss.DiagnosticCategory.Suggestion, s); 54 | 55 | // ! Never pass unsanitized user input to child_process.execSync. 56 | // let stdout = ""; 57 | // try { 58 | // stdout = child_process.execSync(`${config.command} 'EXPLAIN ${s}'`, { encoding: 'utf8' }); 59 | // } catch (error) { 60 | // return make_diagnostic(1, tss.DiagnosticCategory.Error, error.stderr + "\n" + s); 61 | // } 62 | 63 | const [_command, ..._command_args] = (shq 64 | .parse(config.command) 65 | .concat("EXPLAIN " + s) as any) as string[]; 66 | 67 | if (existsInSqlCache(_command, _command_args)) { 68 | return resp; 69 | } 70 | 71 | const p = child_process.spawnSync(_command, _command_args, { encoding: "utf8" }); 72 | if (p.status) { 73 | return make_diagnostic(1, tss.DiagnosticCategory.Error, p.stderr + "\n" + s); 74 | } 75 | 76 | const directives = parseDirectives(s); 77 | 78 | if ( 79 | [config.error_cost, config.warn_cost, config.info_cost].some(it => it != void 0) && 80 | !directives.some(x => x.directive === "ignore-cost") 81 | ) { 82 | const match = p.stdout.match(cost_pattern); 83 | if (match) { 84 | const [_, cost_str] = match; 85 | const cost = Number(cost_str); 86 | if (cost > config.error_cost!) { 87 | return make_diagnostic( 88 | 1, 89 | tss.DiagnosticCategory.Error, 90 | `explain cost is too high: ${cost}\n${s}`, 91 | ); 92 | } 93 | if (cost > config.warn_cost!) { 94 | resp = make_diagnostic( 95 | 1, 96 | tss.DiagnosticCategory.Warning, 97 | `explain cost is at warning: ${cost}\n${s}`, 98 | ); 99 | } 100 | if (cost > config.info_cost!) { 101 | resp = make_diagnostic(1, tss.DiagnosticCategory.Suggestion, `explain cost is ok: ${cost}\n${s}`); 102 | } 103 | } else { 104 | return make_diagnostic( 105 | 1, 106 | tss.DiagnosticCategory.Error, 107 | `can not extract cost with cost_pattern: ${cost_pattern.source}\n${p.stdout}\n${s}`, 108 | ); 109 | } 110 | } 111 | 112 | addToSqlCache(_command, _command_args); 113 | return resp; 114 | } 115 | }); 116 | return [...origin_diagnostics, ...(explain_rss.filter(v => !!v) as any[])]; 117 | }; 118 | } 119 | return (target as any)[p]; 120 | }, 121 | }); 122 | 123 | if (!config.schema_command) { 124 | return proxy; 125 | } 126 | 127 | const [_schema_command, ..._schema_command_args] = (shq.parse(config.schema_command) as any) as string[]; 128 | const schema_info_p = child_process.spawnSync(_schema_command, _schema_command_args, { encoding: "utf8" }); 129 | if (schema_info_p.status) { 130 | throw Error(schema_info_p.stderr); 131 | } 132 | const schema_info = schema_info_p.stdout 133 | .split("\n") 134 | .map(it => it.match(/\w+/g)) 135 | .filter(it => it?.length === 3) 136 | .map((raw, id) => { 137 | const [schema, table, column] = raw!; 138 | return { schema, table, column, id }; 139 | }); 140 | 141 | return decorateWithTemplateLanguageService( 142 | mod.typescript, 143 | proxy, 144 | info.project, 145 | new SqlTemplateAutocomplete( 146 | lunr(function () { 147 | this.ref("id"); 148 | this.field("schema"); 149 | this.field("table"); 150 | this.field("column"); 151 | schema_info.forEach(schema_info_item => this.add(schema_info_item)); 152 | }), 153 | schema_info, 154 | // schema_info.reduce((acc, schema_info_item) => { 155 | // acc[schema_info_item.id] = schema_info_item; 156 | // return acc; 157 | // }, {} as any), 158 | ), 159 | { tags: [config.tags.sql, config.tags.raw], enableForStringWithSubstitutions: true }, 160 | ); 161 | }; 162 | } 163 | -------------------------------------------------------------------------------- /lib/directiveParser.ts: -------------------------------------------------------------------------------- 1 | export interface IDirective { 2 | directive: string; 3 | arg: any; 4 | } 5 | 6 | export const directiveRegex = new RegExp( 7 | /^\s*(--|\/\*)\s*ts-sql-plugin:(?[\w\d\-]+)(?:\((?.*)\))?\s*(\*\/)?\s*$/, 8 | ); 9 | 10 | const takeUntil = (source: T[], predicate: (val: T) => boolean) => { 11 | const takeUntil = source.findIndex(predicate); 12 | return source.slice(0, takeUntil); 13 | }; 14 | 15 | export const parseDirectives = (query: string): IDirective[] => { 16 | return takeUntil(query.split("\n"), line => !line.match(/^\s*(?:(--|\/\*).*)?$/)).reduce((accum, line) => { 17 | const match = line.trimLeft().match(directiveRegex); 18 | if (match) { 19 | const { directive, arg } = match.groups!; 20 | return [...accum, { directive, arg: arg && JSON.parse(arg) }]; 21 | } else { 22 | return accum; 23 | } 24 | }, [] as IDirective[]); 25 | }; 26 | -------------------------------------------------------------------------------- /lib/make_fake_expression.ts: -------------------------------------------------------------------------------- 1 | import ts from 'typescript'; // used as value, passed in by tsserver at runtime 2 | // import tss from 'typescript/lib/tsserverlibrary'; // used as type only 3 | import { is_array, deep_flatten } from '@xialvjun/js-utils'; 4 | 5 | import sql from '../sql'; 6 | import { Tags } from './utils'; 7 | 8 | export const make_fake_expression = ( 9 | type_checker: ts.TypeChecker, 10 | tags: Tags, 11 | ) => { 12 | const fns = { 13 | [tags.and]: sql.and, 14 | [tags.or]: sql.or, 15 | [tags.ins]: sql.ins, 16 | [tags.upd]: sql.upd, 17 | [tags.mock]: sql.mock, 18 | }; 19 | const tag_regex = new RegExp( 20 | '^' + tags.sql + '$|' + tags.raw + '$|' + tags.cond + '\\(', 21 | ); 22 | return fake_expression; 23 | 24 | function fake_expression_from_tagged_template(n: ts.TaggedTemplateExpression) { 25 | const fn = sql.raw; 26 | if (n.template.kind === ts.SyntaxKind.NoSubstitutionTemplateLiteral) { 27 | return [fn(([n.template.text] as unknown) as TemplateStringsArray)]; 28 | } 29 | if (n.template.kind === ts.SyntaxKind.TemplateExpression) { 30 | const texts = ([ 31 | n.template.head.text, 32 | ...n.template.templateSpans.map(span => span.literal.text), 33 | ] as unknown) as TemplateStringsArray; 34 | let values: any[][] = n.template.templateSpans 35 | .map(span => fake_expression(span.expression)) 36 | .map(v => (is_array(v) ? deep_flatten(v) : [v])); 37 | 38 | // * 要想编译期校验 sql, 则 sql 模板字符串内的所有有 sql.symbol 的对象都需要直接在模板字符串内定义(其实 and,ins,upd 可以不用, 只要给它们分配泛型类型就足够, 但是 raw 必须如此, 39 | // * 而且就算匹配类型, 也得寻找类型原始出处, 也容易出错, 所以干脆统一要求在模板字符串内定义)... 40 | // * 然后要做分支 raw, 则需要每个分支单独 explain 校验(不然肯定出错, 例如 asc desc 同时出现)... 41 | // * 做分支检测最好是出现分支时, 把 texts,values 复制一份, 分支各自进行下去, 进行到最终点的时候, 自行检测, 不需要统一检测所有分支 42 | // var arr = [[1],[21,22,23], [31,32], [4]]; 43 | // // debugger; 44 | // var rs = arr.reduce((acc, cv) => { 45 | // return cv.map(v => { 46 | // return acc.map(ac => { 47 | // return ac.concat(v); 48 | // }) 49 | // }).reduce((acc, cv) => acc.concat(cv), []); 50 | // }, [[]]); 51 | // console.table(rs); 52 | // // rs should be [[1,21,31,4],[1,22,31,4],[1,23,31,4],[1,21,32,4],[1,22,32,4],[1,23,32,4]]; 53 | 54 | let all_values = values.reduce( 55 | (acc, cv) => { 56 | return cv 57 | .map(v => acc.map(ac => ac.concat(v))) 58 | .reduce((acc, cv) => acc.concat(cv), []); 59 | }, 60 | [[]], 61 | ); 62 | return all_values.map(_values => sql.raw(texts, ..._values)); 63 | } 64 | } 65 | 66 | function fake_expression_from_tagged_value_declaration(valueDeclaration: ts.Declaration) { 67 | const childCount = valueDeclaration.getChildCount(); 68 | const template = valueDeclaration.getChildAt(childCount - 1); 69 | if (ts.isTaggedTemplateExpression(template) && tag_regex.test(template.tag.getText())) { 70 | return fake_expression_from_tagged_template(template); 71 | } 72 | } 73 | 74 | function fake_expression_from_function_value_declaration(valueDeclaration: ts.Declaration) { 75 | const fnBody = ( 76 | (valueDeclaration).body || 77 | (valueDeclaration).initializer && (valueDeclaration).initializer.body); 78 | const returnStatement = (fnBody && fnBody.statements || (>[])).find(s => ts.isReturnStatement(s)); 79 | if (returnStatement) { 80 | const template = returnStatement.getChildren().find(c => ts.isTaggedTemplateExpression(c)) as ts.TaggedTemplateExpression; 81 | if (template && tag_regex.test(template.tag.getText())) { 82 | return fake_expression_from_tagged_template(template); 83 | } 84 | } 85 | } 86 | 87 | // ! fake raw``,and(),ins(),upd(),?: and other expression. sql`` is just a special kind of raw``. 88 | function fake_expression(n: ts.Expression): any { 89 | if (ts.isIdentifier(n)) { 90 | const symbol = type_checker.getSymbolAtLocation(n); 91 | if (symbol && symbol.valueDeclaration) { 92 | const fromDirectVariable = fake_expression_from_tagged_value_declaration(symbol.valueDeclaration); 93 | if (fromDirectVariable) { 94 | return fromDirectVariable; 95 | } 96 | } 97 | if (symbol && symbol.declarations && symbol.declarations.length && (ts).isAliasSymbolDeclaration(symbol.declarations[0])) { 98 | const aliasedSymbol = type_checker.getAliasedSymbol(symbol); 99 | if (aliasedSymbol.valueDeclaration) { 100 | return fake_expression_from_tagged_value_declaration(aliasedSymbol.valueDeclaration); 101 | } 102 | } 103 | } 104 | if (ts.isCallExpression(n)) { 105 | const fn = fns[(n.expression.getLastToken() || n.expression).getText()]; 106 | if (!!fn) { 107 | if (fn == sql.mock) { 108 | if (n.typeArguments?.length !== 1) { 109 | return sql.raw`You must assign typeArgument to sql.mock`; 110 | } 111 | const t = type_checker.getTypeFromTypeNode(n.typeArguments[0]); 112 | const ts: ts.StringLiteralType[] = []; 113 | let has_error_type = false; 114 | const get_types = (t: ts.Type) => t.isStringLiteral() ? ts.push(t) : t.isUnion() ? t.types.forEach(get_types) : (has_error_type=true); 115 | get_types(t); 116 | if (has_error_type) { 117 | return sql.raw`The typeArgument of sql.mock must be String type`; 118 | } 119 | return ts.map(t => sql.raw([t.value] as any)); 120 | } 121 | const t = type_checker.getTypeAtLocation(n.arguments[0]); 122 | let fake: any = null; 123 | if (fn == sql.and || fn == sql.upd || fn == sql.ins) { 124 | const ut = t.getNumberIndexType() as ts.UnionType; 125 | if (fn == sql.ins && !!ut) { 126 | if (!!ut.types) { 127 | fake = ut.types.map(t => object_type_to_fake(t)); 128 | } else { 129 | fake = object_type_to_fake(ut); 130 | } 131 | } else { 132 | fake = object_type_to_fake(t); 133 | } 134 | } 135 | if (fn == sql.or) { 136 | const ut = t.getNumberIndexType() as ts.UnionType; 137 | if (!!ut.types) { 138 | fake = ut.types.map(t => object_type_to_fake(t)); 139 | } else { 140 | fake = [object_type_to_fake(ut)]; 141 | } 142 | } 143 | return (fn as any)(fake); 144 | } 145 | const symbol = type_checker.getSymbolAtLocation(n.expression); 146 | if (symbol && symbol.valueDeclaration) { 147 | const resp = fake_expression_from_function_value_declaration(symbol.valueDeclaration); 148 | if (resp) { 149 | return resp; 150 | } 151 | } 152 | if (symbol && symbol.declarations && symbol.declarations.length && (ts).isAliasSymbolDeclaration(symbol.declarations[0])) { 153 | const aliasedSymbol = type_checker.getAliasedSymbol(symbol); 154 | if (aliasedSymbol.valueDeclaration) { 155 | const resp = fake_expression_from_function_value_declaration(aliasedSymbol.valueDeclaration); 156 | if (resp) { 157 | return resp; 158 | } 159 | } 160 | } 161 | } 162 | if (ts.isTaggedTemplateExpression(n)) { 163 | // 因为又 sql.cond(boolean)`` 所以不能直接 n.tag.getText() === tags.xxx 164 | if (tag_regex.test(n.tag.getText())) { 165 | return fake_expression_from_tagged_template(n); 166 | } 167 | } 168 | if (ts.isConditionalExpression(n)) { 169 | return [fake_expression(n.whenTrue), fake_expression(n.whenFalse)]; 170 | } 171 | return null; 172 | } 173 | }; 174 | 175 | // function isTypeReference(type: ts.Type): type is ts.TypeReference { 176 | // return !!( 177 | // type.getFlags() & ts.TypeFlags.Object && 178 | // (type as ts.ObjectType).objectFlags & ts.ObjectFlags.Reference 179 | // ); 180 | // } 181 | 182 | // function isArrayType(type: ts.Type): boolean { 183 | // return isTypeReference(type) && ( 184 | // type.target.symbol.name === "Array" || 185 | // type.target.symbol.name === "ReadonlyArray" 186 | // ); 187 | // } 188 | 189 | const object_type_to_fake = (t: ts.Type) => { 190 | return t 191 | .getProperties() 192 | .reduce((acc, cv) => Object.assign(acc, { [cv.getName()]: null }), {}); 193 | }; 194 | -------------------------------------------------------------------------------- /lib/utils.ts: -------------------------------------------------------------------------------- 1 | import fs from "fs"; 2 | import path from "path"; 3 | 4 | import ts from "typescript"; 5 | import { quote } from "shell-quote"; 6 | import { merge } from "@xialvjun/js-utils"; 7 | 8 | export const find_all_nodes = (sourceFile: ts.SourceFile, cond: (n: ts.Node) => boolean) => { 9 | const result: ts.Node[] = []; 10 | function find(node: ts.Node) { 11 | if (cond(node)) { 12 | result.push(node); 13 | return; 14 | } else { 15 | ts.forEachChild(node, find); 16 | } 17 | } 18 | find(sourceFile); 19 | return result; 20 | }; 21 | 22 | export const get_all_ts_files = (dirpath: string) => { 23 | let ts_files: string[] = []; 24 | const paths = fs.readdirSync(dirpath).map(it => path.join(dirpath, it)); 25 | const path_stats = paths.map(it => [it, fs.statSync(it)] as const); 26 | const exts = [".ts", ".tsx"]; 27 | const ts_folders = path_stats.filter( 28 | ([p, s]) => 29 | (s.isDirectory() && path.basename(p) !== "node_modules") || (s.isFile() && exts.indexOf(path.extname(p)) > -1), 30 | ); 31 | ts_folders.forEach(([p, s]) => { 32 | if (s.isFile()) { 33 | ts_files.push(p); 34 | } 35 | }); 36 | ts_folders.forEach(([p, s]) => { 37 | if (s.isDirectory()) { 38 | ts_files = ts_files.concat(get_all_ts_files(p)); 39 | } 40 | }); 41 | return ts_files; 42 | }; 43 | 44 | // ! level 不用 3, console 也不用 error, 不然做 输出管道 就很麻烦 45 | export function report(sourceFile: ts.SourceFile, node: ts.Node, message: string, level: 1 | 2 = 1) { 46 | const { line, character } = sourceFile.getLineAndCharacterOfPosition(node.getStart()); 47 | let str = `${sourceFile.fileName} (${line + 1},${character + 1}):\n${message}` 48 | .split("\n") 49 | .filter(v => v.trim()) 50 | .map(it => "-- " + it) 51 | .join("\n"); 52 | console[(["", "info", "warn", "error"] as const)[level]](str); 53 | } 54 | 55 | export interface Tags { 56 | // template string 57 | sql: string; 58 | raw: string; 59 | cond: string; 60 | // call expression 61 | and: string; 62 | or: string; 63 | ins: string; 64 | upd: string; 65 | mock: string; 66 | } 67 | 68 | export interface TsSqlPluginConfig { 69 | mock: string; 70 | error_cost?: number; 71 | warn_cost?: number; 72 | info_cost?: number; 73 | cost_pattern: string | null; 74 | tags: Tags; 75 | command: string; 76 | schema_command: string | null; 77 | } 78 | 79 | export const default_mock = "0"; 80 | export const default_cost_pattern = /\(cost=\d+\.?\d*\.\.(\d+\.?\d*)/; 81 | export const default_tags: Tags = { 82 | sql: "sql", 83 | raw: "raw", 84 | cond: "cond", 85 | and: "and", 86 | or: "or", 87 | ins: "ins", 88 | upd: "upd", 89 | mock: "mock", 90 | }; 91 | export const default_command = `psql -c`; 92 | 93 | export const merge_defaults = (...configs: TsSqlPluginConfig[]) => { 94 | const config: TsSqlPluginConfig = merge( 95 | { 96 | mock: default_mock, 97 | cost_pattern: default_cost_pattern.source, 98 | command: default_command, 99 | schema_command: "pg", 100 | tags: { ...default_tags }, 101 | }, 102 | ...configs, 103 | ); 104 | config.cost_pattern = config.cost_pattern || default_cost_pattern.source; 105 | if (config.schema_command === "pg") { 106 | config.schema_command = `${config.command} ${quote([ 107 | `copy (select table_schema, table_name, column_name from information_schema.columns WHERE table_schema=CURRENT_SCHEMA()) to stdout delimiter ','`, 108 | ])}`; 109 | } 110 | if (config.schema_command === "mysql") { 111 | config.schema_command = `${config.command} ${quote([ 112 | `SELECT TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME from information_schema.COLUMNS WHERE TABLE_SCHEMA=DATABASE()`, 113 | ])}`; 114 | } 115 | return config; 116 | }; 117 | 118 | export type SchemaInfo = { 119 | schema: string; 120 | table: string; 121 | column: string; 122 | id: number; 123 | }[]; 124 | 125 | export const trim_middle_comments = (q: string) => { 126 | let isHeadComment = true; 127 | return q 128 | .trim() 129 | .split("\n") 130 | .filter(l => 131 | l.trim().match(/^\-\-/) || l.trim().match(/^\/\*.*\*\/$/) 132 | ? isHeadComment 133 | ? true 134 | : false 135 | : ((isHeadComment = false), true), 136 | ) 137 | .join("\n"); 138 | }; 139 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ts-sql-plugin", 3 | "version": "0.10.1", 4 | "lockfileVersion": 2, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "ts-sql-plugin", 9 | "version": "0.10.1", 10 | "license": "MIT", 11 | "dependencies": { 12 | "@xialvjun/js-utils": "^0.3.7", 13 | "await-spawn": "4.0.2", 14 | "commander": "^5.0.0", 15 | "lodash.flattendeep": "4.4.0", 16 | "lunr": "^2.3.8", 17 | "shell-quote": "^1.7.2", 18 | "tslib": "^2.0.0", 19 | "typescript-template-language-service-decorator": "^2.2.0" 20 | }, 21 | "bin": { 22 | "ts-sql-plugin": "cli.js" 23 | }, 24 | "devDependencies": { 25 | "@types/chai": "^4.2.11", 26 | "@types/lodash.flattendeep": "4.4.6", 27 | "@types/lunr": "^2.3.3", 28 | "@types/mocha": "^7.0.2", 29 | "@types/node": "^12.12.41", 30 | "@types/shell-quote": "^1.7.0", 31 | "chai": "^4.2.0", 32 | "mocha": "^10.2.0", 33 | "ts-node": "^8.10.2", 34 | "typescript": "^4.0.2" 35 | }, 36 | "peerDependencies": { 37 | "typescript": "^4.0.2" 38 | } 39 | }, 40 | "node_modules/@types/chai": { 41 | "version": "4.2.11", 42 | "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.2.11.tgz", 43 | "integrity": "sha512-t7uW6eFafjO+qJ3BIV2gGUyZs27egcNRkUdalkud+Qa3+kg//f129iuOFivHDXQ+vnU3fDXuwgv0cqMCbcE8sw==", 44 | "dev": true 45 | }, 46 | "node_modules/@types/lodash": { 47 | "version": "4.14.168", 48 | "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.168.tgz", 49 | "integrity": "sha512-oVfRvqHV/V6D1yifJbVRU3TMp8OT6o6BG+U9MkwuJ3U8/CsDHvalRpsxBqivn71ztOFZBTfJMvETbqHiaNSj7Q==", 50 | "dev": true 51 | }, 52 | "node_modules/@types/lodash.flattendeep": { 53 | "version": "4.4.6", 54 | "resolved": "https://registry.npmjs.org/@types/lodash.flattendeep/-/lodash.flattendeep-4.4.6.tgz", 55 | "integrity": "sha512-uLm2MaRVlqJSGsMK0RZpP5T3KqReq+9WbYDHCUhBhp98v56hMG/Yht52bsoTSui9xz2mUvQ9NfG3LrNGDL92Ng==", 56 | "dev": true, 57 | "dependencies": { 58 | "@types/lodash": "*" 59 | } 60 | }, 61 | "node_modules/@types/lunr": { 62 | "version": "2.3.3", 63 | "resolved": "https://registry.npmjs.org/@types/lunr/-/lunr-2.3.3.tgz", 64 | "integrity": "sha512-09sXZZVsB3Ib41U0fC+O1O+4UOZT1bl/e+/QubPxpqDWHNEchvx/DEb1KJMOwq6K3MTNzZFoNSzVdR++o1DVnw==", 65 | "dev": true 66 | }, 67 | "node_modules/@types/mocha": { 68 | "version": "7.0.2", 69 | "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-7.0.2.tgz", 70 | "integrity": "sha512-ZvO2tAcjmMi8V/5Z3JsyofMe3hasRcaw88cto5etSVMwVQfeivGAlEYmaQgceUSVYFofVjT+ioHsATjdWcFt1w==", 71 | "dev": true 72 | }, 73 | "node_modules/@types/node": { 74 | "version": "12.12.50", 75 | "resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.50.tgz", 76 | "integrity": "sha512-5ImO01Fb8YsEOYpV+aeyGYztcYcjGsBvN4D7G5r1ef2cuQOpymjWNQi5V0rKHE6PC2ru3HkoUr/Br2/8GUA84w==", 77 | "dev": true 78 | }, 79 | "node_modules/@types/shell-quote": { 80 | "version": "1.7.0", 81 | "resolved": "https://registry.npmjs.org/@types/shell-quote/-/shell-quote-1.7.0.tgz", 82 | "integrity": "sha512-0CJaSSayMigMKu/Egx1eVcFjgGYkP6T4dyPRs462BFrMB/OK2FAJFV/24+6R4cGIXw6ZQpZK8XEd2UCVKfkZRw==", 83 | "dev": true 84 | }, 85 | "node_modules/@xialvjun/js-utils": { 86 | "version": "0.3.7", 87 | "resolved": "https://registry.npmjs.org/@xialvjun/js-utils/-/js-utils-0.3.7.tgz", 88 | "integrity": "sha512-IUkqFT5x8m0PJOlv08Vm802EsrZDtEoZVKYk8q4NDfeUMhCU7TRhHKrETygEFenCefVPyVq11SKGYO4oDPCuUA==" 89 | }, 90 | "node_modules/ansi-colors": { 91 | "version": "4.1.1", 92 | "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", 93 | "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", 94 | "dev": true, 95 | "engines": { 96 | "node": ">=6" 97 | } 98 | }, 99 | "node_modules/ansi-regex": { 100 | "version": "5.0.1", 101 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", 102 | "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", 103 | "dev": true, 104 | "engines": { 105 | "node": ">=8" 106 | } 107 | }, 108 | "node_modules/ansi-styles": { 109 | "version": "4.3.0", 110 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", 111 | "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", 112 | "dev": true, 113 | "dependencies": { 114 | "color-convert": "^2.0.1" 115 | }, 116 | "engines": { 117 | "node": ">=8" 118 | }, 119 | "funding": { 120 | "url": "https://github.com/chalk/ansi-styles?sponsor=1" 121 | } 122 | }, 123 | "node_modules/anymatch": { 124 | "version": "3.1.3", 125 | "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", 126 | "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", 127 | "dev": true, 128 | "dependencies": { 129 | "normalize-path": "^3.0.0", 130 | "picomatch": "^2.0.4" 131 | }, 132 | "engines": { 133 | "node": ">= 8" 134 | } 135 | }, 136 | "node_modules/arg": { 137 | "version": "4.1.3", 138 | "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", 139 | "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", 140 | "dev": true 141 | }, 142 | "node_modules/argparse": { 143 | "version": "2.0.1", 144 | "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", 145 | "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", 146 | "dev": true 147 | }, 148 | "node_modules/assertion-error": { 149 | "version": "1.1.0", 150 | "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", 151 | "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", 152 | "dev": true, 153 | "engines": { 154 | "node": "*" 155 | } 156 | }, 157 | "node_modules/await-spawn": { 158 | "version": "4.0.2", 159 | "resolved": "https://registry.npmjs.org/await-spawn/-/await-spawn-4.0.2.tgz", 160 | "integrity": "sha512-GdADmeLJiMvGKJD3xWBcX40DMn07JNH1sqJYgYJZH7NTGJ3B1qDjKBKzxhhyR1hjIcnUGFUmE/+4D1HcHAJBAA==", 161 | "license": "MIT", 162 | "dependencies": { 163 | "bl": "^4.0.3" 164 | }, 165 | "engines": { 166 | "node": ">=10" 167 | } 168 | }, 169 | "node_modules/balanced-match": { 170 | "version": "1.0.2", 171 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", 172 | "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", 173 | "dev": true 174 | }, 175 | "node_modules/base64-js": { 176 | "version": "1.5.1", 177 | "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", 178 | "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", 179 | "funding": [ 180 | { 181 | "type": "github", 182 | "url": "https://github.com/sponsors/feross" 183 | }, 184 | { 185 | "type": "patreon", 186 | "url": "https://www.patreon.com/feross" 187 | }, 188 | { 189 | "type": "consulting", 190 | "url": "https://feross.org/support" 191 | } 192 | ] 193 | }, 194 | "node_modules/binary-extensions": { 195 | "version": "2.2.0", 196 | "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", 197 | "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", 198 | "dev": true, 199 | "engines": { 200 | "node": ">=8" 201 | } 202 | }, 203 | "node_modules/bl": { 204 | "version": "4.1.0", 205 | "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", 206 | "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", 207 | "dependencies": { 208 | "buffer": "^5.5.0", 209 | "inherits": "^2.0.4", 210 | "readable-stream": "^3.4.0" 211 | } 212 | }, 213 | "node_modules/brace-expansion": { 214 | "version": "2.0.1", 215 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", 216 | "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", 217 | "dev": true, 218 | "dependencies": { 219 | "balanced-match": "^1.0.0" 220 | } 221 | }, 222 | "node_modules/braces": { 223 | "version": "3.0.3", 224 | "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", 225 | "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", 226 | "dev": true, 227 | "dependencies": { 228 | "fill-range": "^7.1.1" 229 | }, 230 | "engines": { 231 | "node": ">=8" 232 | } 233 | }, 234 | "node_modules/browser-stdout": { 235 | "version": "1.3.1", 236 | "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", 237 | "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", 238 | "dev": true 239 | }, 240 | "node_modules/buffer": { 241 | "version": "5.7.1", 242 | "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", 243 | "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", 244 | "funding": [ 245 | { 246 | "type": "github", 247 | "url": "https://github.com/sponsors/feross" 248 | }, 249 | { 250 | "type": "patreon", 251 | "url": "https://www.patreon.com/feross" 252 | }, 253 | { 254 | "type": "consulting", 255 | "url": "https://feross.org/support" 256 | } 257 | ], 258 | "dependencies": { 259 | "base64-js": "^1.3.1", 260 | "ieee754": "^1.1.13" 261 | } 262 | }, 263 | "node_modules/buffer-from": { 264 | "version": "1.1.1", 265 | "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", 266 | "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", 267 | "dev": true 268 | }, 269 | "node_modules/camelcase": { 270 | "version": "6.3.0", 271 | "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", 272 | "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", 273 | "dev": true, 274 | "engines": { 275 | "node": ">=10" 276 | }, 277 | "funding": { 278 | "url": "https://github.com/sponsors/sindresorhus" 279 | } 280 | }, 281 | "node_modules/chai": { 282 | "version": "4.2.0", 283 | "resolved": "https://registry.npmjs.org/chai/-/chai-4.2.0.tgz", 284 | "integrity": "sha512-XQU3bhBukrOsQCuwZndwGcCVQHyZi53fQ6Ys1Fym7E4olpIqqZZhhoFJoaKVvV17lWQoXYwgWN2nF5crA8J2jw==", 285 | "dev": true, 286 | "dependencies": { 287 | "assertion-error": "^1.1.0", 288 | "check-error": "^1.0.2", 289 | "deep-eql": "^3.0.1", 290 | "get-func-name": "^2.0.0", 291 | "pathval": "^1.1.0", 292 | "type-detect": "^4.0.5" 293 | }, 294 | "engines": { 295 | "node": ">=4" 296 | } 297 | }, 298 | "node_modules/chalk": { 299 | "version": "4.1.2", 300 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", 301 | "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", 302 | "dev": true, 303 | "dependencies": { 304 | "ansi-styles": "^4.1.0", 305 | "supports-color": "^7.1.0" 306 | }, 307 | "engines": { 308 | "node": ">=10" 309 | }, 310 | "funding": { 311 | "url": "https://github.com/chalk/chalk?sponsor=1" 312 | } 313 | }, 314 | "node_modules/chalk/node_modules/supports-color": { 315 | "version": "7.2.0", 316 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", 317 | "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", 318 | "dev": true, 319 | "dependencies": { 320 | "has-flag": "^4.0.0" 321 | }, 322 | "engines": { 323 | "node": ">=8" 324 | } 325 | }, 326 | "node_modules/check-error": { 327 | "version": "1.0.2", 328 | "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", 329 | "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=", 330 | "dev": true, 331 | "engines": { 332 | "node": "*" 333 | } 334 | }, 335 | "node_modules/chokidar": { 336 | "version": "3.5.3", 337 | "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", 338 | "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", 339 | "dev": true, 340 | "funding": [ 341 | { 342 | "type": "individual", 343 | "url": "https://paulmillr.com/funding/" 344 | } 345 | ], 346 | "dependencies": { 347 | "anymatch": "~3.1.2", 348 | "braces": "~3.0.2", 349 | "glob-parent": "~5.1.2", 350 | "is-binary-path": "~2.1.0", 351 | "is-glob": "~4.0.1", 352 | "normalize-path": "~3.0.0", 353 | "readdirp": "~3.6.0" 354 | }, 355 | "engines": { 356 | "node": ">= 8.10.0" 357 | }, 358 | "optionalDependencies": { 359 | "fsevents": "~2.3.2" 360 | } 361 | }, 362 | "node_modules/cliui": { 363 | "version": "7.0.4", 364 | "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", 365 | "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", 366 | "dev": true, 367 | "dependencies": { 368 | "string-width": "^4.2.0", 369 | "strip-ansi": "^6.0.0", 370 | "wrap-ansi": "^7.0.0" 371 | } 372 | }, 373 | "node_modules/color-convert": { 374 | "version": "2.0.1", 375 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", 376 | "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", 377 | "dev": true, 378 | "dependencies": { 379 | "color-name": "~1.1.4" 380 | }, 381 | "engines": { 382 | "node": ">=7.0.0" 383 | } 384 | }, 385 | "node_modules/color-name": { 386 | "version": "1.1.4", 387 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", 388 | "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", 389 | "dev": true 390 | }, 391 | "node_modules/commander": { 392 | "version": "5.1.0", 393 | "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz", 394 | "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==", 395 | "engines": { 396 | "node": ">= 6" 397 | } 398 | }, 399 | "node_modules/concat-map": { 400 | "version": "0.0.1", 401 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 402 | "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", 403 | "dev": true 404 | }, 405 | "node_modules/debug": { 406 | "version": "4.3.4", 407 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", 408 | "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", 409 | "dev": true, 410 | "dependencies": { 411 | "ms": "2.1.2" 412 | }, 413 | "engines": { 414 | "node": ">=6.0" 415 | }, 416 | "peerDependenciesMeta": { 417 | "supports-color": { 418 | "optional": true 419 | } 420 | } 421 | }, 422 | "node_modules/debug/node_modules/ms": { 423 | "version": "2.1.2", 424 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 425 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", 426 | "dev": true 427 | }, 428 | "node_modules/decamelize": { 429 | "version": "4.0.0", 430 | "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", 431 | "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", 432 | "dev": true, 433 | "engines": { 434 | "node": ">=10" 435 | }, 436 | "funding": { 437 | "url": "https://github.com/sponsors/sindresorhus" 438 | } 439 | }, 440 | "node_modules/deep-eql": { 441 | "version": "3.0.1", 442 | "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz", 443 | "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==", 444 | "dev": true, 445 | "dependencies": { 446 | "type-detect": "^4.0.0" 447 | }, 448 | "engines": { 449 | "node": ">=0.12" 450 | } 451 | }, 452 | "node_modules/diff": { 453 | "version": "5.0.0", 454 | "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", 455 | "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", 456 | "dev": true, 457 | "engines": { 458 | "node": ">=0.3.1" 459 | } 460 | }, 461 | "node_modules/emoji-regex": { 462 | "version": "8.0.0", 463 | "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", 464 | "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", 465 | "dev": true 466 | }, 467 | "node_modules/escalade": { 468 | "version": "3.1.1", 469 | "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", 470 | "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", 471 | "dev": true, 472 | "engines": { 473 | "node": ">=6" 474 | } 475 | }, 476 | "node_modules/escape-string-regexp": { 477 | "version": "4.0.0", 478 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", 479 | "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", 480 | "dev": true, 481 | "engines": { 482 | "node": ">=10" 483 | }, 484 | "funding": { 485 | "url": "https://github.com/sponsors/sindresorhus" 486 | } 487 | }, 488 | "node_modules/fill-range": { 489 | "version": "7.1.1", 490 | "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", 491 | "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", 492 | "dev": true, 493 | "dependencies": { 494 | "to-regex-range": "^5.0.1" 495 | }, 496 | "engines": { 497 | "node": ">=8" 498 | } 499 | }, 500 | "node_modules/find-up": { 501 | "version": "5.0.0", 502 | "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", 503 | "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", 504 | "dev": true, 505 | "dependencies": { 506 | "locate-path": "^6.0.0", 507 | "path-exists": "^4.0.0" 508 | }, 509 | "engines": { 510 | "node": ">=10" 511 | }, 512 | "funding": { 513 | "url": "https://github.com/sponsors/sindresorhus" 514 | } 515 | }, 516 | "node_modules/flat": { 517 | "version": "5.0.2", 518 | "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", 519 | "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", 520 | "dev": true, 521 | "bin": { 522 | "flat": "cli.js" 523 | } 524 | }, 525 | "node_modules/fs.realpath": { 526 | "version": "1.0.0", 527 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 528 | "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", 529 | "dev": true 530 | }, 531 | "node_modules/fsevents": { 532 | "version": "2.3.2", 533 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", 534 | "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", 535 | "dev": true, 536 | "hasInstallScript": true, 537 | "optional": true, 538 | "os": [ 539 | "darwin" 540 | ], 541 | "engines": { 542 | "node": "^8.16.0 || ^10.6.0 || >=11.0.0" 543 | } 544 | }, 545 | "node_modules/get-caller-file": { 546 | "version": "2.0.5", 547 | "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", 548 | "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", 549 | "dev": true, 550 | "engines": { 551 | "node": "6.* || 8.* || >= 10.*" 552 | } 553 | }, 554 | "node_modules/get-func-name": { 555 | "version": "2.0.0", 556 | "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", 557 | "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=", 558 | "dev": true, 559 | "engines": { 560 | "node": "*" 561 | } 562 | }, 563 | "node_modules/glob": { 564 | "version": "7.2.0", 565 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", 566 | "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", 567 | "dev": true, 568 | "dependencies": { 569 | "fs.realpath": "^1.0.0", 570 | "inflight": "^1.0.4", 571 | "inherits": "2", 572 | "minimatch": "^3.0.4", 573 | "once": "^1.3.0", 574 | "path-is-absolute": "^1.0.0" 575 | }, 576 | "engines": { 577 | "node": "*" 578 | }, 579 | "funding": { 580 | "url": "https://github.com/sponsors/isaacs" 581 | } 582 | }, 583 | "node_modules/glob-parent": { 584 | "version": "5.1.2", 585 | "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", 586 | "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", 587 | "dev": true, 588 | "dependencies": { 589 | "is-glob": "^4.0.1" 590 | }, 591 | "engines": { 592 | "node": ">= 6" 593 | } 594 | }, 595 | "node_modules/glob/node_modules/brace-expansion": { 596 | "version": "1.1.11", 597 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 598 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 599 | "dev": true, 600 | "dependencies": { 601 | "balanced-match": "^1.0.0", 602 | "concat-map": "0.0.1" 603 | } 604 | }, 605 | "node_modules/glob/node_modules/minimatch": { 606 | "version": "3.1.2", 607 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", 608 | "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", 609 | "dev": true, 610 | "dependencies": { 611 | "brace-expansion": "^1.1.7" 612 | }, 613 | "engines": { 614 | "node": "*" 615 | } 616 | }, 617 | "node_modules/has-flag": { 618 | "version": "4.0.0", 619 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", 620 | "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", 621 | "dev": true, 622 | "engines": { 623 | "node": ">=8" 624 | } 625 | }, 626 | "node_modules/he": { 627 | "version": "1.2.0", 628 | "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", 629 | "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", 630 | "dev": true, 631 | "bin": { 632 | "he": "bin/he" 633 | } 634 | }, 635 | "node_modules/ieee754": { 636 | "version": "1.2.1", 637 | "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", 638 | "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", 639 | "funding": [ 640 | { 641 | "type": "github", 642 | "url": "https://github.com/sponsors/feross" 643 | }, 644 | { 645 | "type": "patreon", 646 | "url": "https://www.patreon.com/feross" 647 | }, 648 | { 649 | "type": "consulting", 650 | "url": "https://feross.org/support" 651 | } 652 | ] 653 | }, 654 | "node_modules/inflight": { 655 | "version": "1.0.6", 656 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 657 | "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", 658 | "dev": true, 659 | "dependencies": { 660 | "once": "^1.3.0", 661 | "wrappy": "1" 662 | } 663 | }, 664 | "node_modules/inherits": { 665 | "version": "2.0.4", 666 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 667 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" 668 | }, 669 | "node_modules/is-binary-path": { 670 | "version": "2.1.0", 671 | "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", 672 | "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", 673 | "dev": true, 674 | "dependencies": { 675 | "binary-extensions": "^2.0.0" 676 | }, 677 | "engines": { 678 | "node": ">=8" 679 | } 680 | }, 681 | "node_modules/is-extglob": { 682 | "version": "2.1.1", 683 | "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", 684 | "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", 685 | "dev": true, 686 | "engines": { 687 | "node": ">=0.10.0" 688 | } 689 | }, 690 | "node_modules/is-fullwidth-code-point": { 691 | "version": "3.0.0", 692 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", 693 | "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", 694 | "dev": true, 695 | "engines": { 696 | "node": ">=8" 697 | } 698 | }, 699 | "node_modules/is-glob": { 700 | "version": "4.0.3", 701 | "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", 702 | "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", 703 | "dev": true, 704 | "dependencies": { 705 | "is-extglob": "^2.1.1" 706 | }, 707 | "engines": { 708 | "node": ">=0.10.0" 709 | } 710 | }, 711 | "node_modules/is-number": { 712 | "version": "7.0.0", 713 | "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", 714 | "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", 715 | "dev": true, 716 | "engines": { 717 | "node": ">=0.12.0" 718 | } 719 | }, 720 | "node_modules/is-plain-obj": { 721 | "version": "2.1.0", 722 | "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", 723 | "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", 724 | "dev": true, 725 | "engines": { 726 | "node": ">=8" 727 | } 728 | }, 729 | "node_modules/is-unicode-supported": { 730 | "version": "0.1.0", 731 | "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", 732 | "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", 733 | "dev": true, 734 | "engines": { 735 | "node": ">=10" 736 | }, 737 | "funding": { 738 | "url": "https://github.com/sponsors/sindresorhus" 739 | } 740 | }, 741 | "node_modules/js-yaml": { 742 | "version": "4.1.0", 743 | "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", 744 | "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", 745 | "dev": true, 746 | "dependencies": { 747 | "argparse": "^2.0.1" 748 | }, 749 | "bin": { 750 | "js-yaml": "bin/js-yaml.js" 751 | } 752 | }, 753 | "node_modules/locate-path": { 754 | "version": "6.0.0", 755 | "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", 756 | "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", 757 | "dev": true, 758 | "dependencies": { 759 | "p-locate": "^5.0.0" 760 | }, 761 | "engines": { 762 | "node": ">=10" 763 | }, 764 | "funding": { 765 | "url": "https://github.com/sponsors/sindresorhus" 766 | } 767 | }, 768 | "node_modules/lodash.flattendeep": { 769 | "version": "4.4.0", 770 | "resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz", 771 | "integrity": "sha1-+wMJF/hqMTTlvJvsDWngAT3f7bI=" 772 | }, 773 | "node_modules/log-symbols": { 774 | "version": "4.1.0", 775 | "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", 776 | "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", 777 | "dev": true, 778 | "dependencies": { 779 | "chalk": "^4.1.0", 780 | "is-unicode-supported": "^0.1.0" 781 | }, 782 | "engines": { 783 | "node": ">=10" 784 | }, 785 | "funding": { 786 | "url": "https://github.com/sponsors/sindresorhus" 787 | } 788 | }, 789 | "node_modules/lunr": { 790 | "version": "2.3.8", 791 | "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.8.tgz", 792 | "integrity": "sha512-oxMeX/Y35PNFuZoHp+jUj5OSEmLCaIH4KTFJh7a93cHBoFmpw2IoPs22VIz7vyO2YUnx2Tn9dzIwO2P/4quIRg==" 793 | }, 794 | "node_modules/make-error": { 795 | "version": "1.3.6", 796 | "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", 797 | "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", 798 | "dev": true 799 | }, 800 | "node_modules/minimatch": { 801 | "version": "5.0.1", 802 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz", 803 | "integrity": "sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==", 804 | "dev": true, 805 | "dependencies": { 806 | "brace-expansion": "^2.0.1" 807 | }, 808 | "engines": { 809 | "node": ">=10" 810 | } 811 | }, 812 | "node_modules/mocha": { 813 | "version": "10.2.0", 814 | "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.2.0.tgz", 815 | "integrity": "sha512-IDY7fl/BecMwFHzoqF2sg/SHHANeBoMMXFlS9r0OXKDssYE1M5O43wUY/9BVPeIvfH2zmEbBfseqN9gBQZzXkg==", 816 | "dev": true, 817 | "dependencies": { 818 | "ansi-colors": "4.1.1", 819 | "browser-stdout": "1.3.1", 820 | "chokidar": "3.5.3", 821 | "debug": "4.3.4", 822 | "diff": "5.0.0", 823 | "escape-string-regexp": "4.0.0", 824 | "find-up": "5.0.0", 825 | "glob": "7.2.0", 826 | "he": "1.2.0", 827 | "js-yaml": "4.1.0", 828 | "log-symbols": "4.1.0", 829 | "minimatch": "5.0.1", 830 | "ms": "2.1.3", 831 | "nanoid": "3.3.3", 832 | "serialize-javascript": "6.0.0", 833 | "strip-json-comments": "3.1.1", 834 | "supports-color": "8.1.1", 835 | "workerpool": "6.2.1", 836 | "yargs": "16.2.0", 837 | "yargs-parser": "20.2.4", 838 | "yargs-unparser": "2.0.0" 839 | }, 840 | "bin": { 841 | "_mocha": "bin/_mocha", 842 | "mocha": "bin/mocha.js" 843 | }, 844 | "engines": { 845 | "node": ">= 14.0.0" 846 | }, 847 | "funding": { 848 | "type": "opencollective", 849 | "url": "https://opencollective.com/mochajs" 850 | } 851 | }, 852 | "node_modules/ms": { 853 | "version": "2.1.3", 854 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", 855 | "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", 856 | "dev": true 857 | }, 858 | "node_modules/nanoid": { 859 | "version": "3.3.3", 860 | "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.3.tgz", 861 | "integrity": "sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w==", 862 | "dev": true, 863 | "bin": { 864 | "nanoid": "bin/nanoid.cjs" 865 | }, 866 | "engines": { 867 | "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" 868 | } 869 | }, 870 | "node_modules/normalize-path": { 871 | "version": "3.0.0", 872 | "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", 873 | "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", 874 | "dev": true, 875 | "engines": { 876 | "node": ">=0.10.0" 877 | } 878 | }, 879 | "node_modules/once": { 880 | "version": "1.4.0", 881 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 882 | "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", 883 | "dev": true, 884 | "dependencies": { 885 | "wrappy": "1" 886 | } 887 | }, 888 | "node_modules/p-limit": { 889 | "version": "3.1.0", 890 | "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", 891 | "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", 892 | "dev": true, 893 | "dependencies": { 894 | "yocto-queue": "^0.1.0" 895 | }, 896 | "engines": { 897 | "node": ">=10" 898 | }, 899 | "funding": { 900 | "url": "https://github.com/sponsors/sindresorhus" 901 | } 902 | }, 903 | "node_modules/p-locate": { 904 | "version": "5.0.0", 905 | "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", 906 | "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", 907 | "dev": true, 908 | "dependencies": { 909 | "p-limit": "^3.0.2" 910 | }, 911 | "engines": { 912 | "node": ">=10" 913 | }, 914 | "funding": { 915 | "url": "https://github.com/sponsors/sindresorhus" 916 | } 917 | }, 918 | "node_modules/path-exists": { 919 | "version": "4.0.0", 920 | "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", 921 | "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", 922 | "dev": true, 923 | "engines": { 924 | "node": ">=8" 925 | } 926 | }, 927 | "node_modules/path-is-absolute": { 928 | "version": "1.0.1", 929 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 930 | "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", 931 | "dev": true, 932 | "engines": { 933 | "node": ">=0.10.0" 934 | } 935 | }, 936 | "node_modules/pathval": { 937 | "version": "1.1.1", 938 | "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", 939 | "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", 940 | "dev": true, 941 | "engines": { 942 | "node": "*" 943 | } 944 | }, 945 | "node_modules/picomatch": { 946 | "version": "2.3.1", 947 | "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", 948 | "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", 949 | "dev": true, 950 | "engines": { 951 | "node": ">=8.6" 952 | }, 953 | "funding": { 954 | "url": "https://github.com/sponsors/jonschlinkert" 955 | } 956 | }, 957 | "node_modules/randombytes": { 958 | "version": "2.1.0", 959 | "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", 960 | "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", 961 | "dev": true, 962 | "dependencies": { 963 | "safe-buffer": "^5.1.0" 964 | } 965 | }, 966 | "node_modules/readable-stream": { 967 | "version": "3.6.0", 968 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", 969 | "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", 970 | "dependencies": { 971 | "inherits": "^2.0.3", 972 | "string_decoder": "^1.1.1", 973 | "util-deprecate": "^1.0.1" 974 | }, 975 | "engines": { 976 | "node": ">= 6" 977 | } 978 | }, 979 | "node_modules/readdirp": { 980 | "version": "3.6.0", 981 | "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", 982 | "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", 983 | "dev": true, 984 | "dependencies": { 985 | "picomatch": "^2.2.1" 986 | }, 987 | "engines": { 988 | "node": ">=8.10.0" 989 | } 990 | }, 991 | "node_modules/require-directory": { 992 | "version": "2.1.1", 993 | "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", 994 | "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", 995 | "dev": true, 996 | "engines": { 997 | "node": ">=0.10.0" 998 | } 999 | }, 1000 | "node_modules/safe-buffer": { 1001 | "version": "5.2.1", 1002 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", 1003 | "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", 1004 | "funding": [ 1005 | { 1006 | "type": "github", 1007 | "url": "https://github.com/sponsors/feross" 1008 | }, 1009 | { 1010 | "type": "patreon", 1011 | "url": "https://www.patreon.com/feross" 1012 | }, 1013 | { 1014 | "type": "consulting", 1015 | "url": "https://feross.org/support" 1016 | } 1017 | ] 1018 | }, 1019 | "node_modules/serialize-javascript": { 1020 | "version": "6.0.0", 1021 | "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", 1022 | "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", 1023 | "dev": true, 1024 | "dependencies": { 1025 | "randombytes": "^2.1.0" 1026 | } 1027 | }, 1028 | "node_modules/shell-quote": { 1029 | "version": "1.7.3", 1030 | "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.7.3.tgz", 1031 | "integrity": "sha512-Vpfqwm4EnqGdlsBFNmHhxhElJYrdfcxPThu+ryKS5J8L/fhAwLazFZtq+S+TWZ9ANj2piSQLGj6NQg+lKPmxrw==" 1032 | }, 1033 | "node_modules/source-map": { 1034 | "version": "0.6.1", 1035 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", 1036 | "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", 1037 | "dev": true, 1038 | "engines": { 1039 | "node": ">=0.10.0" 1040 | } 1041 | }, 1042 | "node_modules/source-map-support": { 1043 | "version": "0.5.19", 1044 | "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz", 1045 | "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==", 1046 | "dev": true, 1047 | "dependencies": { 1048 | "buffer-from": "^1.0.0", 1049 | "source-map": "^0.6.0" 1050 | } 1051 | }, 1052 | "node_modules/string_decoder": { 1053 | "version": "1.3.0", 1054 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", 1055 | "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", 1056 | "dependencies": { 1057 | "safe-buffer": "~5.2.0" 1058 | } 1059 | }, 1060 | "node_modules/string-width": { 1061 | "version": "4.2.3", 1062 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", 1063 | "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", 1064 | "dev": true, 1065 | "dependencies": { 1066 | "emoji-regex": "^8.0.0", 1067 | "is-fullwidth-code-point": "^3.0.0", 1068 | "strip-ansi": "^6.0.1" 1069 | }, 1070 | "engines": { 1071 | "node": ">=8" 1072 | } 1073 | }, 1074 | "node_modules/strip-ansi": { 1075 | "version": "6.0.1", 1076 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", 1077 | "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", 1078 | "dev": true, 1079 | "dependencies": { 1080 | "ansi-regex": "^5.0.1" 1081 | }, 1082 | "engines": { 1083 | "node": ">=8" 1084 | } 1085 | }, 1086 | "node_modules/strip-json-comments": { 1087 | "version": "3.1.1", 1088 | "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", 1089 | "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", 1090 | "dev": true, 1091 | "engines": { 1092 | "node": ">=8" 1093 | }, 1094 | "funding": { 1095 | "url": "https://github.com/sponsors/sindresorhus" 1096 | } 1097 | }, 1098 | "node_modules/supports-color": { 1099 | "version": "8.1.1", 1100 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", 1101 | "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", 1102 | "dev": true, 1103 | "dependencies": { 1104 | "has-flag": "^4.0.0" 1105 | }, 1106 | "engines": { 1107 | "node": ">=10" 1108 | }, 1109 | "funding": { 1110 | "url": "https://github.com/chalk/supports-color?sponsor=1" 1111 | } 1112 | }, 1113 | "node_modules/to-regex-range": { 1114 | "version": "5.0.1", 1115 | "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", 1116 | "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", 1117 | "dev": true, 1118 | "dependencies": { 1119 | "is-number": "^7.0.0" 1120 | }, 1121 | "engines": { 1122 | "node": ">=8.0" 1123 | } 1124 | }, 1125 | "node_modules/ts-node": { 1126 | "version": "8.10.2", 1127 | "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-8.10.2.tgz", 1128 | "integrity": "sha512-ISJJGgkIpDdBhWVu3jufsWpK3Rzo7bdiIXJjQc0ynKxVOVcg2oIrf2H2cejminGrptVc6q6/uynAHNCuWGbpVA==", 1129 | "dev": true, 1130 | "dependencies": { 1131 | "arg": "^4.1.0", 1132 | "diff": "^4.0.1", 1133 | "make-error": "^1.1.1", 1134 | "source-map-support": "^0.5.17", 1135 | "yn": "3.1.1" 1136 | }, 1137 | "bin": { 1138 | "ts-node": "dist/bin.js", 1139 | "ts-node-script": "dist/bin-script.js", 1140 | "ts-node-transpile-only": "dist/bin-transpile.js", 1141 | "ts-script": "dist/bin-script-deprecated.js" 1142 | }, 1143 | "engines": { 1144 | "node": ">=6.0.0" 1145 | } 1146 | }, 1147 | "node_modules/ts-node/node_modules/diff": { 1148 | "version": "4.0.2", 1149 | "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", 1150 | "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", 1151 | "dev": true, 1152 | "engines": { 1153 | "node": ">=0.3.1" 1154 | } 1155 | }, 1156 | "node_modules/tslib": { 1157 | "version": "2.0.0", 1158 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.0.tgz", 1159 | "integrity": "sha512-lTqkx847PI7xEDYJntxZH89L2/aXInsyF2luSafe/+0fHOMjlBNXdH6th7f70qxLDhul7KZK0zC8V5ZIyHl0/g==" 1160 | }, 1161 | "node_modules/type-detect": { 1162 | "version": "4.0.8", 1163 | "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", 1164 | "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", 1165 | "dev": true, 1166 | "engines": { 1167 | "node": ">=4" 1168 | } 1169 | }, 1170 | "node_modules/typescript": { 1171 | "version": "4.0.2", 1172 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.0.2.tgz", 1173 | "integrity": "sha512-e4ERvRV2wb+rRZ/IQeb3jm2VxBsirQLpQhdxplZ2MEzGvDkkMmPglecnNDfSUBivMjP93vRbngYYDQqQ/78bcQ==", 1174 | "dev": true, 1175 | "bin": { 1176 | "tsc": "bin/tsc", 1177 | "tsserver": "bin/tsserver" 1178 | }, 1179 | "engines": { 1180 | "node": ">=4.2.0" 1181 | } 1182 | }, 1183 | "node_modules/typescript-template-language-service-decorator": { 1184 | "version": "2.2.0", 1185 | "resolved": "https://registry.npmjs.org/typescript-template-language-service-decorator/-/typescript-template-language-service-decorator-2.2.0.tgz", 1186 | "integrity": "sha512-xiolqt1i7e22rpqMaprPgSFVgU64u3b9n6EJlAaUYE61jumipKAdI1+O5khPlWslpTUj80YzjUKjJ2jxT0D74w==" 1187 | }, 1188 | "node_modules/util-deprecate": { 1189 | "version": "1.0.2", 1190 | "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", 1191 | "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" 1192 | }, 1193 | "node_modules/workerpool": { 1194 | "version": "6.2.1", 1195 | "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz", 1196 | "integrity": "sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==", 1197 | "dev": true 1198 | }, 1199 | "node_modules/wrap-ansi": { 1200 | "version": "7.0.0", 1201 | "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", 1202 | "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", 1203 | "dev": true, 1204 | "dependencies": { 1205 | "ansi-styles": "^4.0.0", 1206 | "string-width": "^4.1.0", 1207 | "strip-ansi": "^6.0.0" 1208 | }, 1209 | "engines": { 1210 | "node": ">=10" 1211 | }, 1212 | "funding": { 1213 | "url": "https://github.com/chalk/wrap-ansi?sponsor=1" 1214 | } 1215 | }, 1216 | "node_modules/wrappy": { 1217 | "version": "1.0.2", 1218 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 1219 | "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", 1220 | "dev": true 1221 | }, 1222 | "node_modules/y18n": { 1223 | "version": "5.0.8", 1224 | "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", 1225 | "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", 1226 | "dev": true, 1227 | "engines": { 1228 | "node": ">=10" 1229 | } 1230 | }, 1231 | "node_modules/yargs": { 1232 | "version": "16.2.0", 1233 | "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", 1234 | "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", 1235 | "dev": true, 1236 | "dependencies": { 1237 | "cliui": "^7.0.2", 1238 | "escalade": "^3.1.1", 1239 | "get-caller-file": "^2.0.5", 1240 | "require-directory": "^2.1.1", 1241 | "string-width": "^4.2.0", 1242 | "y18n": "^5.0.5", 1243 | "yargs-parser": "^20.2.2" 1244 | }, 1245 | "engines": { 1246 | "node": ">=10" 1247 | } 1248 | }, 1249 | "node_modules/yargs-parser": { 1250 | "version": "20.2.4", 1251 | "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", 1252 | "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", 1253 | "dev": true, 1254 | "engines": { 1255 | "node": ">=10" 1256 | } 1257 | }, 1258 | "node_modules/yargs-unparser": { 1259 | "version": "2.0.0", 1260 | "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", 1261 | "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", 1262 | "dev": true, 1263 | "dependencies": { 1264 | "camelcase": "^6.0.0", 1265 | "decamelize": "^4.0.0", 1266 | "flat": "^5.0.2", 1267 | "is-plain-obj": "^2.1.0" 1268 | }, 1269 | "engines": { 1270 | "node": ">=10" 1271 | } 1272 | }, 1273 | "node_modules/yn": { 1274 | "version": "3.1.1", 1275 | "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", 1276 | "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", 1277 | "dev": true, 1278 | "engines": { 1279 | "node": ">=6" 1280 | } 1281 | }, 1282 | "node_modules/yocto-queue": { 1283 | "version": "0.1.0", 1284 | "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", 1285 | "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", 1286 | "dev": true, 1287 | "engines": { 1288 | "node": ">=10" 1289 | }, 1290 | "funding": { 1291 | "url": "https://github.com/sponsors/sindresorhus" 1292 | } 1293 | } 1294 | }, 1295 | "dependencies": { 1296 | "@types/chai": { 1297 | "version": "4.2.11", 1298 | "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.2.11.tgz", 1299 | "integrity": "sha512-t7uW6eFafjO+qJ3BIV2gGUyZs27egcNRkUdalkud+Qa3+kg//f129iuOFivHDXQ+vnU3fDXuwgv0cqMCbcE8sw==", 1300 | "dev": true 1301 | }, 1302 | "@types/lodash": { 1303 | "version": "4.14.168", 1304 | "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.168.tgz", 1305 | "integrity": "sha512-oVfRvqHV/V6D1yifJbVRU3TMp8OT6o6BG+U9MkwuJ3U8/CsDHvalRpsxBqivn71ztOFZBTfJMvETbqHiaNSj7Q==", 1306 | "dev": true 1307 | }, 1308 | "@types/lodash.flattendeep": { 1309 | "version": "4.4.6", 1310 | "resolved": "https://registry.npmjs.org/@types/lodash.flattendeep/-/lodash.flattendeep-4.4.6.tgz", 1311 | "integrity": "sha512-uLm2MaRVlqJSGsMK0RZpP5T3KqReq+9WbYDHCUhBhp98v56hMG/Yht52bsoTSui9xz2mUvQ9NfG3LrNGDL92Ng==", 1312 | "dev": true, 1313 | "requires": { 1314 | "@types/lodash": "*" 1315 | } 1316 | }, 1317 | "@types/lunr": { 1318 | "version": "2.3.3", 1319 | "resolved": "https://registry.npmjs.org/@types/lunr/-/lunr-2.3.3.tgz", 1320 | "integrity": "sha512-09sXZZVsB3Ib41U0fC+O1O+4UOZT1bl/e+/QubPxpqDWHNEchvx/DEb1KJMOwq6K3MTNzZFoNSzVdR++o1DVnw==", 1321 | "dev": true 1322 | }, 1323 | "@types/mocha": { 1324 | "version": "7.0.2", 1325 | "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-7.0.2.tgz", 1326 | "integrity": "sha512-ZvO2tAcjmMi8V/5Z3JsyofMe3hasRcaw88cto5etSVMwVQfeivGAlEYmaQgceUSVYFofVjT+ioHsATjdWcFt1w==", 1327 | "dev": true 1328 | }, 1329 | "@types/node": { 1330 | "version": "12.12.50", 1331 | "resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.50.tgz", 1332 | "integrity": "sha512-5ImO01Fb8YsEOYpV+aeyGYztcYcjGsBvN4D7G5r1ef2cuQOpymjWNQi5V0rKHE6PC2ru3HkoUr/Br2/8GUA84w==", 1333 | "dev": true 1334 | }, 1335 | "@types/shell-quote": { 1336 | "version": "1.7.0", 1337 | "resolved": "https://registry.npmjs.org/@types/shell-quote/-/shell-quote-1.7.0.tgz", 1338 | "integrity": "sha512-0CJaSSayMigMKu/Egx1eVcFjgGYkP6T4dyPRs462BFrMB/OK2FAJFV/24+6R4cGIXw6ZQpZK8XEd2UCVKfkZRw==", 1339 | "dev": true 1340 | }, 1341 | "@xialvjun/js-utils": { 1342 | "version": "0.3.7", 1343 | "resolved": "https://registry.npmjs.org/@xialvjun/js-utils/-/js-utils-0.3.7.tgz", 1344 | "integrity": "sha512-IUkqFT5x8m0PJOlv08Vm802EsrZDtEoZVKYk8q4NDfeUMhCU7TRhHKrETygEFenCefVPyVq11SKGYO4oDPCuUA==" 1345 | }, 1346 | "ansi-colors": { 1347 | "version": "4.1.1", 1348 | "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", 1349 | "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", 1350 | "dev": true 1351 | }, 1352 | "ansi-regex": { 1353 | "version": "5.0.1", 1354 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", 1355 | "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", 1356 | "dev": true 1357 | }, 1358 | "ansi-styles": { 1359 | "version": "4.3.0", 1360 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", 1361 | "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", 1362 | "dev": true, 1363 | "requires": { 1364 | "color-convert": "^2.0.1" 1365 | } 1366 | }, 1367 | "anymatch": { 1368 | "version": "3.1.3", 1369 | "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", 1370 | "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", 1371 | "dev": true, 1372 | "requires": { 1373 | "normalize-path": "^3.0.0", 1374 | "picomatch": "^2.0.4" 1375 | } 1376 | }, 1377 | "arg": { 1378 | "version": "4.1.3", 1379 | "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", 1380 | "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", 1381 | "dev": true 1382 | }, 1383 | "argparse": { 1384 | "version": "2.0.1", 1385 | "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", 1386 | "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", 1387 | "dev": true 1388 | }, 1389 | "assertion-error": { 1390 | "version": "1.1.0", 1391 | "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", 1392 | "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", 1393 | "dev": true 1394 | }, 1395 | "await-spawn": { 1396 | "version": "4.0.2", 1397 | "resolved": "https://registry.npmjs.org/await-spawn/-/await-spawn-4.0.2.tgz", 1398 | "integrity": "sha512-GdADmeLJiMvGKJD3xWBcX40DMn07JNH1sqJYgYJZH7NTGJ3B1qDjKBKzxhhyR1hjIcnUGFUmE/+4D1HcHAJBAA==", 1399 | "requires": { 1400 | "bl": "^4.0.3" 1401 | } 1402 | }, 1403 | "balanced-match": { 1404 | "version": "1.0.2", 1405 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", 1406 | "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", 1407 | "dev": true 1408 | }, 1409 | "base64-js": { 1410 | "version": "1.5.1", 1411 | "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", 1412 | "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" 1413 | }, 1414 | "binary-extensions": { 1415 | "version": "2.2.0", 1416 | "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", 1417 | "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", 1418 | "dev": true 1419 | }, 1420 | "bl": { 1421 | "version": "4.1.0", 1422 | "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", 1423 | "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", 1424 | "requires": { 1425 | "buffer": "^5.5.0", 1426 | "inherits": "^2.0.4", 1427 | "readable-stream": "^3.4.0" 1428 | } 1429 | }, 1430 | "brace-expansion": { 1431 | "version": "2.0.1", 1432 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", 1433 | "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", 1434 | "dev": true, 1435 | "requires": { 1436 | "balanced-match": "^1.0.0" 1437 | } 1438 | }, 1439 | "braces": { 1440 | "version": "3.0.3", 1441 | "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", 1442 | "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", 1443 | "dev": true, 1444 | "requires": { 1445 | "fill-range": "^7.1.1" 1446 | } 1447 | }, 1448 | "browser-stdout": { 1449 | "version": "1.3.1", 1450 | "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", 1451 | "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", 1452 | "dev": true 1453 | }, 1454 | "buffer": { 1455 | "version": "5.7.1", 1456 | "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", 1457 | "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", 1458 | "requires": { 1459 | "base64-js": "^1.3.1", 1460 | "ieee754": "^1.1.13" 1461 | } 1462 | }, 1463 | "buffer-from": { 1464 | "version": "1.1.1", 1465 | "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", 1466 | "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", 1467 | "dev": true 1468 | }, 1469 | "camelcase": { 1470 | "version": "6.3.0", 1471 | "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", 1472 | "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", 1473 | "dev": true 1474 | }, 1475 | "chai": { 1476 | "version": "4.2.0", 1477 | "resolved": "https://registry.npmjs.org/chai/-/chai-4.2.0.tgz", 1478 | "integrity": "sha512-XQU3bhBukrOsQCuwZndwGcCVQHyZi53fQ6Ys1Fym7E4olpIqqZZhhoFJoaKVvV17lWQoXYwgWN2nF5crA8J2jw==", 1479 | "dev": true, 1480 | "requires": { 1481 | "assertion-error": "^1.1.0", 1482 | "check-error": "^1.0.2", 1483 | "deep-eql": "^3.0.1", 1484 | "get-func-name": "^2.0.0", 1485 | "pathval": "^1.1.0", 1486 | "type-detect": "^4.0.5" 1487 | } 1488 | }, 1489 | "chalk": { 1490 | "version": "4.1.2", 1491 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", 1492 | "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", 1493 | "dev": true, 1494 | "requires": { 1495 | "ansi-styles": "^4.1.0", 1496 | "supports-color": "^7.1.0" 1497 | }, 1498 | "dependencies": { 1499 | "supports-color": { 1500 | "version": "7.2.0", 1501 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", 1502 | "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", 1503 | "dev": true, 1504 | "requires": { 1505 | "has-flag": "^4.0.0" 1506 | } 1507 | } 1508 | } 1509 | }, 1510 | "check-error": { 1511 | "version": "1.0.2", 1512 | "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", 1513 | "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=", 1514 | "dev": true 1515 | }, 1516 | "chokidar": { 1517 | "version": "3.5.3", 1518 | "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", 1519 | "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", 1520 | "dev": true, 1521 | "requires": { 1522 | "anymatch": "~3.1.2", 1523 | "braces": "~3.0.2", 1524 | "fsevents": "~2.3.2", 1525 | "glob-parent": "~5.1.2", 1526 | "is-binary-path": "~2.1.0", 1527 | "is-glob": "~4.0.1", 1528 | "normalize-path": "~3.0.0", 1529 | "readdirp": "~3.6.0" 1530 | } 1531 | }, 1532 | "cliui": { 1533 | "version": "7.0.4", 1534 | "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", 1535 | "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", 1536 | "dev": true, 1537 | "requires": { 1538 | "string-width": "^4.2.0", 1539 | "strip-ansi": "^6.0.0", 1540 | "wrap-ansi": "^7.0.0" 1541 | } 1542 | }, 1543 | "color-convert": { 1544 | "version": "2.0.1", 1545 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", 1546 | "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", 1547 | "dev": true, 1548 | "requires": { 1549 | "color-name": "~1.1.4" 1550 | } 1551 | }, 1552 | "color-name": { 1553 | "version": "1.1.4", 1554 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", 1555 | "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", 1556 | "dev": true 1557 | }, 1558 | "commander": { 1559 | "version": "5.1.0", 1560 | "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz", 1561 | "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==" 1562 | }, 1563 | "concat-map": { 1564 | "version": "0.0.1", 1565 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 1566 | "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", 1567 | "dev": true 1568 | }, 1569 | "debug": { 1570 | "version": "4.3.4", 1571 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", 1572 | "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", 1573 | "dev": true, 1574 | "requires": { 1575 | "ms": "2.1.2" 1576 | }, 1577 | "dependencies": { 1578 | "ms": { 1579 | "version": "2.1.2", 1580 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 1581 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", 1582 | "dev": true 1583 | } 1584 | } 1585 | }, 1586 | "decamelize": { 1587 | "version": "4.0.0", 1588 | "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", 1589 | "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", 1590 | "dev": true 1591 | }, 1592 | "deep-eql": { 1593 | "version": "3.0.1", 1594 | "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz", 1595 | "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==", 1596 | "dev": true, 1597 | "requires": { 1598 | "type-detect": "^4.0.0" 1599 | } 1600 | }, 1601 | "diff": { 1602 | "version": "5.0.0", 1603 | "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", 1604 | "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", 1605 | "dev": true 1606 | }, 1607 | "emoji-regex": { 1608 | "version": "8.0.0", 1609 | "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", 1610 | "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", 1611 | "dev": true 1612 | }, 1613 | "escalade": { 1614 | "version": "3.1.1", 1615 | "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", 1616 | "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", 1617 | "dev": true 1618 | }, 1619 | "escape-string-regexp": { 1620 | "version": "4.0.0", 1621 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", 1622 | "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", 1623 | "dev": true 1624 | }, 1625 | "fill-range": { 1626 | "version": "7.1.1", 1627 | "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", 1628 | "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", 1629 | "dev": true, 1630 | "requires": { 1631 | "to-regex-range": "^5.0.1" 1632 | } 1633 | }, 1634 | "find-up": { 1635 | "version": "5.0.0", 1636 | "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", 1637 | "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", 1638 | "dev": true, 1639 | "requires": { 1640 | "locate-path": "^6.0.0", 1641 | "path-exists": "^4.0.0" 1642 | } 1643 | }, 1644 | "flat": { 1645 | "version": "5.0.2", 1646 | "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", 1647 | "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", 1648 | "dev": true 1649 | }, 1650 | "fs.realpath": { 1651 | "version": "1.0.0", 1652 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 1653 | "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", 1654 | "dev": true 1655 | }, 1656 | "fsevents": { 1657 | "version": "2.3.2", 1658 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", 1659 | "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", 1660 | "dev": true, 1661 | "optional": true 1662 | }, 1663 | "get-caller-file": { 1664 | "version": "2.0.5", 1665 | "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", 1666 | "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", 1667 | "dev": true 1668 | }, 1669 | "get-func-name": { 1670 | "version": "2.0.0", 1671 | "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", 1672 | "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=", 1673 | "dev": true 1674 | }, 1675 | "glob": { 1676 | "version": "7.2.0", 1677 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", 1678 | "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", 1679 | "dev": true, 1680 | "requires": { 1681 | "fs.realpath": "^1.0.0", 1682 | "inflight": "^1.0.4", 1683 | "inherits": "2", 1684 | "minimatch": "^3.0.4", 1685 | "once": "^1.3.0", 1686 | "path-is-absolute": "^1.0.0" 1687 | }, 1688 | "dependencies": { 1689 | "brace-expansion": { 1690 | "version": "1.1.11", 1691 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 1692 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 1693 | "dev": true, 1694 | "requires": { 1695 | "balanced-match": "^1.0.0", 1696 | "concat-map": "0.0.1" 1697 | } 1698 | }, 1699 | "minimatch": { 1700 | "version": "3.1.2", 1701 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", 1702 | "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", 1703 | "dev": true, 1704 | "requires": { 1705 | "brace-expansion": "^1.1.7" 1706 | } 1707 | } 1708 | } 1709 | }, 1710 | "glob-parent": { 1711 | "version": "5.1.2", 1712 | "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", 1713 | "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", 1714 | "dev": true, 1715 | "requires": { 1716 | "is-glob": "^4.0.1" 1717 | } 1718 | }, 1719 | "has-flag": { 1720 | "version": "4.0.0", 1721 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", 1722 | "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", 1723 | "dev": true 1724 | }, 1725 | "he": { 1726 | "version": "1.2.0", 1727 | "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", 1728 | "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", 1729 | "dev": true 1730 | }, 1731 | "ieee754": { 1732 | "version": "1.2.1", 1733 | "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", 1734 | "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==" 1735 | }, 1736 | "inflight": { 1737 | "version": "1.0.6", 1738 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 1739 | "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", 1740 | "dev": true, 1741 | "requires": { 1742 | "once": "^1.3.0", 1743 | "wrappy": "1" 1744 | } 1745 | }, 1746 | "inherits": { 1747 | "version": "2.0.4", 1748 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 1749 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" 1750 | }, 1751 | "is-binary-path": { 1752 | "version": "2.1.0", 1753 | "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", 1754 | "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", 1755 | "dev": true, 1756 | "requires": { 1757 | "binary-extensions": "^2.0.0" 1758 | } 1759 | }, 1760 | "is-extglob": { 1761 | "version": "2.1.1", 1762 | "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", 1763 | "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", 1764 | "dev": true 1765 | }, 1766 | "is-fullwidth-code-point": { 1767 | "version": "3.0.0", 1768 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", 1769 | "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", 1770 | "dev": true 1771 | }, 1772 | "is-glob": { 1773 | "version": "4.0.3", 1774 | "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", 1775 | "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", 1776 | "dev": true, 1777 | "requires": { 1778 | "is-extglob": "^2.1.1" 1779 | } 1780 | }, 1781 | "is-number": { 1782 | "version": "7.0.0", 1783 | "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", 1784 | "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", 1785 | "dev": true 1786 | }, 1787 | "is-plain-obj": { 1788 | "version": "2.1.0", 1789 | "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", 1790 | "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", 1791 | "dev": true 1792 | }, 1793 | "is-unicode-supported": { 1794 | "version": "0.1.0", 1795 | "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", 1796 | "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", 1797 | "dev": true 1798 | }, 1799 | "js-yaml": { 1800 | "version": "4.1.0", 1801 | "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", 1802 | "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", 1803 | "dev": true, 1804 | "requires": { 1805 | "argparse": "^2.0.1" 1806 | } 1807 | }, 1808 | "locate-path": { 1809 | "version": "6.0.0", 1810 | "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", 1811 | "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", 1812 | "dev": true, 1813 | "requires": { 1814 | "p-locate": "^5.0.0" 1815 | } 1816 | }, 1817 | "lodash.flattendeep": { 1818 | "version": "4.4.0", 1819 | "resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz", 1820 | "integrity": "sha1-+wMJF/hqMTTlvJvsDWngAT3f7bI=" 1821 | }, 1822 | "log-symbols": { 1823 | "version": "4.1.0", 1824 | "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", 1825 | "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", 1826 | "dev": true, 1827 | "requires": { 1828 | "chalk": "^4.1.0", 1829 | "is-unicode-supported": "^0.1.0" 1830 | } 1831 | }, 1832 | "lunr": { 1833 | "version": "2.3.8", 1834 | "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.8.tgz", 1835 | "integrity": "sha512-oxMeX/Y35PNFuZoHp+jUj5OSEmLCaIH4KTFJh7a93cHBoFmpw2IoPs22VIz7vyO2YUnx2Tn9dzIwO2P/4quIRg==" 1836 | }, 1837 | "make-error": { 1838 | "version": "1.3.6", 1839 | "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", 1840 | "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", 1841 | "dev": true 1842 | }, 1843 | "minimatch": { 1844 | "version": "5.0.1", 1845 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz", 1846 | "integrity": "sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==", 1847 | "dev": true, 1848 | "requires": { 1849 | "brace-expansion": "^2.0.1" 1850 | } 1851 | }, 1852 | "mocha": { 1853 | "version": "10.2.0", 1854 | "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.2.0.tgz", 1855 | "integrity": "sha512-IDY7fl/BecMwFHzoqF2sg/SHHANeBoMMXFlS9r0OXKDssYE1M5O43wUY/9BVPeIvfH2zmEbBfseqN9gBQZzXkg==", 1856 | "dev": true, 1857 | "requires": { 1858 | "ansi-colors": "4.1.1", 1859 | "browser-stdout": "1.3.1", 1860 | "chokidar": "3.5.3", 1861 | "debug": "4.3.4", 1862 | "diff": "5.0.0", 1863 | "escape-string-regexp": "4.0.0", 1864 | "find-up": "5.0.0", 1865 | "glob": "7.2.0", 1866 | "he": "1.2.0", 1867 | "js-yaml": "4.1.0", 1868 | "log-symbols": "4.1.0", 1869 | "minimatch": "5.0.1", 1870 | "ms": "2.1.3", 1871 | "nanoid": "3.3.3", 1872 | "serialize-javascript": "6.0.0", 1873 | "strip-json-comments": "3.1.1", 1874 | "supports-color": "8.1.1", 1875 | "workerpool": "6.2.1", 1876 | "yargs": "16.2.0", 1877 | "yargs-parser": "20.2.4", 1878 | "yargs-unparser": "2.0.0" 1879 | } 1880 | }, 1881 | "ms": { 1882 | "version": "2.1.3", 1883 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", 1884 | "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", 1885 | "dev": true 1886 | }, 1887 | "nanoid": { 1888 | "version": "3.3.3", 1889 | "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.3.tgz", 1890 | "integrity": "sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w==", 1891 | "dev": true 1892 | }, 1893 | "normalize-path": { 1894 | "version": "3.0.0", 1895 | "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", 1896 | "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", 1897 | "dev": true 1898 | }, 1899 | "once": { 1900 | "version": "1.4.0", 1901 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 1902 | "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", 1903 | "dev": true, 1904 | "requires": { 1905 | "wrappy": "1" 1906 | } 1907 | }, 1908 | "p-limit": { 1909 | "version": "3.1.0", 1910 | "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", 1911 | "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", 1912 | "dev": true, 1913 | "requires": { 1914 | "yocto-queue": "^0.1.0" 1915 | } 1916 | }, 1917 | "p-locate": { 1918 | "version": "5.0.0", 1919 | "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", 1920 | "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", 1921 | "dev": true, 1922 | "requires": { 1923 | "p-limit": "^3.0.2" 1924 | } 1925 | }, 1926 | "path-exists": { 1927 | "version": "4.0.0", 1928 | "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", 1929 | "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", 1930 | "dev": true 1931 | }, 1932 | "path-is-absolute": { 1933 | "version": "1.0.1", 1934 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 1935 | "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", 1936 | "dev": true 1937 | }, 1938 | "pathval": { 1939 | "version": "1.1.1", 1940 | "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", 1941 | "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", 1942 | "dev": true 1943 | }, 1944 | "picomatch": { 1945 | "version": "2.3.1", 1946 | "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", 1947 | "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", 1948 | "dev": true 1949 | }, 1950 | "randombytes": { 1951 | "version": "2.1.0", 1952 | "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", 1953 | "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", 1954 | "dev": true, 1955 | "requires": { 1956 | "safe-buffer": "^5.1.0" 1957 | } 1958 | }, 1959 | "readable-stream": { 1960 | "version": "3.6.0", 1961 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", 1962 | "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", 1963 | "requires": { 1964 | "inherits": "^2.0.3", 1965 | "string_decoder": "^1.1.1", 1966 | "util-deprecate": "^1.0.1" 1967 | } 1968 | }, 1969 | "readdirp": { 1970 | "version": "3.6.0", 1971 | "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", 1972 | "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", 1973 | "dev": true, 1974 | "requires": { 1975 | "picomatch": "^2.2.1" 1976 | } 1977 | }, 1978 | "require-directory": { 1979 | "version": "2.1.1", 1980 | "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", 1981 | "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", 1982 | "dev": true 1983 | }, 1984 | "safe-buffer": { 1985 | "version": "5.2.1", 1986 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", 1987 | "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" 1988 | }, 1989 | "serialize-javascript": { 1990 | "version": "6.0.0", 1991 | "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", 1992 | "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", 1993 | "dev": true, 1994 | "requires": { 1995 | "randombytes": "^2.1.0" 1996 | } 1997 | }, 1998 | "shell-quote": { 1999 | "version": "1.7.3", 2000 | "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.7.3.tgz", 2001 | "integrity": "sha512-Vpfqwm4EnqGdlsBFNmHhxhElJYrdfcxPThu+ryKS5J8L/fhAwLazFZtq+S+TWZ9ANj2piSQLGj6NQg+lKPmxrw==" 2002 | }, 2003 | "source-map": { 2004 | "version": "0.6.1", 2005 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", 2006 | "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", 2007 | "dev": true 2008 | }, 2009 | "source-map-support": { 2010 | "version": "0.5.19", 2011 | "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz", 2012 | "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==", 2013 | "dev": true, 2014 | "requires": { 2015 | "buffer-from": "^1.0.0", 2016 | "source-map": "^0.6.0" 2017 | } 2018 | }, 2019 | "string_decoder": { 2020 | "version": "1.3.0", 2021 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", 2022 | "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", 2023 | "requires": { 2024 | "safe-buffer": "~5.2.0" 2025 | } 2026 | }, 2027 | "string-width": { 2028 | "version": "4.2.3", 2029 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", 2030 | "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", 2031 | "dev": true, 2032 | "requires": { 2033 | "emoji-regex": "^8.0.0", 2034 | "is-fullwidth-code-point": "^3.0.0", 2035 | "strip-ansi": "^6.0.1" 2036 | } 2037 | }, 2038 | "strip-ansi": { 2039 | "version": "6.0.1", 2040 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", 2041 | "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", 2042 | "dev": true, 2043 | "requires": { 2044 | "ansi-regex": "^5.0.1" 2045 | } 2046 | }, 2047 | "strip-json-comments": { 2048 | "version": "3.1.1", 2049 | "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", 2050 | "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", 2051 | "dev": true 2052 | }, 2053 | "supports-color": { 2054 | "version": "8.1.1", 2055 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", 2056 | "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", 2057 | "dev": true, 2058 | "requires": { 2059 | "has-flag": "^4.0.0" 2060 | } 2061 | }, 2062 | "to-regex-range": { 2063 | "version": "5.0.1", 2064 | "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", 2065 | "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", 2066 | "dev": true, 2067 | "requires": { 2068 | "is-number": "^7.0.0" 2069 | } 2070 | }, 2071 | "ts-node": { 2072 | "version": "8.10.2", 2073 | "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-8.10.2.tgz", 2074 | "integrity": "sha512-ISJJGgkIpDdBhWVu3jufsWpK3Rzo7bdiIXJjQc0ynKxVOVcg2oIrf2H2cejminGrptVc6q6/uynAHNCuWGbpVA==", 2075 | "dev": true, 2076 | "requires": { 2077 | "arg": "^4.1.0", 2078 | "diff": "^4.0.1", 2079 | "make-error": "^1.1.1", 2080 | "source-map-support": "^0.5.17", 2081 | "yn": "3.1.1" 2082 | }, 2083 | "dependencies": { 2084 | "diff": { 2085 | "version": "4.0.2", 2086 | "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", 2087 | "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", 2088 | "dev": true 2089 | } 2090 | } 2091 | }, 2092 | "tslib": { 2093 | "version": "2.0.0", 2094 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.0.tgz", 2095 | "integrity": "sha512-lTqkx847PI7xEDYJntxZH89L2/aXInsyF2luSafe/+0fHOMjlBNXdH6th7f70qxLDhul7KZK0zC8V5ZIyHl0/g==" 2096 | }, 2097 | "type-detect": { 2098 | "version": "4.0.8", 2099 | "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", 2100 | "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", 2101 | "dev": true 2102 | }, 2103 | "typescript": { 2104 | "version": "4.0.2", 2105 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.0.2.tgz", 2106 | "integrity": "sha512-e4ERvRV2wb+rRZ/IQeb3jm2VxBsirQLpQhdxplZ2MEzGvDkkMmPglecnNDfSUBivMjP93vRbngYYDQqQ/78bcQ==", 2107 | "dev": true 2108 | }, 2109 | "typescript-template-language-service-decorator": { 2110 | "version": "2.2.0", 2111 | "resolved": "https://registry.npmjs.org/typescript-template-language-service-decorator/-/typescript-template-language-service-decorator-2.2.0.tgz", 2112 | "integrity": "sha512-xiolqt1i7e22rpqMaprPgSFVgU64u3b9n6EJlAaUYE61jumipKAdI1+O5khPlWslpTUj80YzjUKjJ2jxT0D74w==" 2113 | }, 2114 | "util-deprecate": { 2115 | "version": "1.0.2", 2116 | "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", 2117 | "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" 2118 | }, 2119 | "workerpool": { 2120 | "version": "6.2.1", 2121 | "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz", 2122 | "integrity": "sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==", 2123 | "dev": true 2124 | }, 2125 | "wrap-ansi": { 2126 | "version": "7.0.0", 2127 | "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", 2128 | "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", 2129 | "dev": true, 2130 | "requires": { 2131 | "ansi-styles": "^4.0.0", 2132 | "string-width": "^4.1.0", 2133 | "strip-ansi": "^6.0.0" 2134 | } 2135 | }, 2136 | "wrappy": { 2137 | "version": "1.0.2", 2138 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 2139 | "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", 2140 | "dev": true 2141 | }, 2142 | "y18n": { 2143 | "version": "5.0.8", 2144 | "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", 2145 | "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", 2146 | "dev": true 2147 | }, 2148 | "yargs": { 2149 | "version": "16.2.0", 2150 | "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", 2151 | "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", 2152 | "dev": true, 2153 | "requires": { 2154 | "cliui": "^7.0.2", 2155 | "escalade": "^3.1.1", 2156 | "get-caller-file": "^2.0.5", 2157 | "require-directory": "^2.1.1", 2158 | "string-width": "^4.2.0", 2159 | "y18n": "^5.0.5", 2160 | "yargs-parser": "^20.2.2" 2161 | } 2162 | }, 2163 | "yargs-parser": { 2164 | "version": "20.2.4", 2165 | "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", 2166 | "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", 2167 | "dev": true 2168 | }, 2169 | "yargs-unparser": { 2170 | "version": "2.0.0", 2171 | "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", 2172 | "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", 2173 | "dev": true, 2174 | "requires": { 2175 | "camelcase": "^6.0.0", 2176 | "decamelize": "^4.0.0", 2177 | "flat": "^5.0.2", 2178 | "is-plain-obj": "^2.1.0" 2179 | } 2180 | }, 2181 | "yn": { 2182 | "version": "3.1.1", 2183 | "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", 2184 | "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", 2185 | "dev": true 2186 | }, 2187 | "yocto-queue": { 2188 | "version": "0.1.0", 2189 | "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", 2190 | "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", 2191 | "dev": true 2192 | } 2193 | } 2194 | } 2195 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ts-sql-plugin", 3 | "version": "0.10.1", 4 | "description": "TypeScript Language Service Plugin for SQL with a tagged template strings SQL builder.", 5 | "keywords": [ 6 | "typescript", 7 | "language service", 8 | "sql" 9 | ], 10 | "main": "index.js", 11 | "bin": "cli.js", 12 | "scripts": { 13 | "build": "tsc -p .", 14 | "watch": "tsc --watch -p .", 15 | "test": "mocha -r ts-node/register test/**/*.ts" 16 | }, 17 | "author": "xialvjun@live.com", 18 | "license": "MIT", 19 | "repository": { 20 | "type": "git", 21 | "url": "https://github.com/xialvjun/ts-sql-plugin.git" 22 | }, 23 | "devDependencies": { 24 | "@types/chai": "^4.2.11", 25 | "@types/lodash.flattendeep": "4.4.6", 26 | "@types/lunr": "^2.3.3", 27 | "@types/mocha": "^7.0.2", 28 | "@types/node": "^12.12.41", 29 | "@types/shell-quote": "^1.7.0", 30 | "chai": "^4.2.0", 31 | "mocha": "^10.2.0", 32 | "ts-node": "^8.10.2", 33 | "typescript": "^4.0.2" 34 | }, 35 | "peerDependencies": { 36 | "typescript": "^4.0.2" 37 | }, 38 | "dependencies": { 39 | "@xialvjun/js-utils": "^0.3.7", 40 | "await-spawn": "4.0.2", 41 | "commander": "^5.0.0", 42 | "lodash.flattendeep": "4.4.0", 43 | "lunr": "^2.3.8", 44 | "shell-quote": "^1.7.2", 45 | "tslib": "^2.0.0", 46 | "typescript-template-language-service-decorator": "^2.2.0" 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /sql.ts: -------------------------------------------------------------------------------- 1 | const parseValue = (v: any) => { 2 | return !!v && v[symbol] 3 | ? { text: v.text, values: v.values} 4 | : { text: '??', values: [v] }; 5 | } 6 | 7 | const raw = (texts: TemplateStringsArray | string[], ...vs: any[]) => { 8 | let text = texts[0] || ''; 9 | let values: any[] = []; 10 | let parseResult = null; 11 | vs.forEach((v, idx) => { 12 | parseResult = parseValue(v); 13 | text += parseResult.text; 14 | values.push(...parseResult.values) 15 | text += texts[idx + 1] || ""; 16 | }); 17 | return { [symbol]: true, text, values }; 18 | }; 19 | 20 | export const sql = (texts: TemplateStringsArray, ...vs: any[]) => raw(texts, ...vs); 21 | 22 | export default sql; 23 | 24 | const symbol = (sql.symbol = Symbol("sql")); 25 | 26 | sql.raw = raw; 27 | 28 | sql.cond = (condition: boolean) => (condition ? raw : (..._: any[]) => raw``); 29 | 30 | // const to_and = {m: undefined, n: undefined}; 31 | // no first and 32 | // sql`select * from a where ${sql.and(to_and)}` 33 | // with first and 34 | // sql`select * from a where (1=1 ${sql.and(to_and)}) or (${sql.and(another_to_and)})` 35 | // sql`select * from a where 1=1 and ${sql.and(to_and)}` 36 | // 东西加多了是硬伤, 加少了可以有 sql.raw, 所以尽量少加 37 | sql.and = (obj: object) => { 38 | let kvs = Object.entries(obj) 39 | .filter(([k, v]) => v !== undefined) 40 | .sort(([ka, va], [kb, vb]) => (ka < kb ? -1 : ka > kb ? 1 : 0)); 41 | let values: any[] = []; 42 | if (kvs.length === 0) { 43 | return { [symbol]: true, text: "", values }; 44 | } 45 | let text = kvs 46 | .map(([k, v]) => { 47 | values.push(v); 48 | return validate_identifier(k) + " ??"; 49 | }) 50 | .join(" AND "); 51 | return { [symbol]: true, text, values }; 52 | }; 53 | 54 | sql.or = (objs: T) => { 55 | return objs 56 | .map(obj => sql.and(obj)) 57 | .reduce( 58 | (acc, cv, idx) => { 59 | acc.text += `${idx === 0 ? "" : " OR"} (${cv.text})`; 60 | acc.values = acc.values.concat(cv.values); 61 | return acc; 62 | }, 63 | { [symbol]: true, text: "", values: [] }, 64 | ); 65 | }; 66 | 67 | sql.ins = (obj_or_objs: object | object[]) => { 68 | let objs: any[] = [].concat(obj_or_objs as any); 69 | let keys = Object.keys(Object.assign({}, ...objs)).sort(); 70 | let values: any[] = []; 71 | let parseResult = null; 72 | let text = `(${keys.map(k => validate_identifier(k).split(" ")[0]).join(", ")}) VALUES ${objs 73 | .map( 74 | obj => 75 | `(${keys 76 | .map(k => { 77 | parseResult = parseValue(obj[k]) 78 | values.push(...parseResult.values); 79 | return parseResult.text; 80 | }) 81 | .join(", ")})`, 82 | ) 83 | .join(", ")}`; 84 | return { [symbol]: true, text, values }; 85 | }; 86 | 87 | sql.upd = (obj: object) => { 88 | let kvs = Object.entries(obj) 89 | .filter(([k, v]) => v !== undefined) 90 | .sort(([ka, va], [kb, vb]) => (ka < kb ? -1 : ka > kb ? 1 : 0)); 91 | let values: any[] = []; 92 | let parseResult = null 93 | let text = kvs 94 | .map(([k, v]) => { 95 | parseResult = parseValue(v); 96 | values.push(...parseResult.values); 97 | return validate_identifier(k) + `${parseResult.text}`; 98 | }) 99 | .join(", "); 100 | return { [symbol]: true, text, values }; 101 | }; 102 | 103 | sql.mock = (value: any) => value; 104 | 105 | function validate_identifier(identifier: string) { 106 | // we can believe a functionnal sql (ignore it's good or bad) has to include more than one space, so forbid it 107 | const match_space = identifier.match(/\s/g); 108 | if (!match_space) { 109 | return identifier + ' ='; 110 | } 111 | if (match_space.length === 1) { 112 | return identifier; 113 | } 114 | throw Error("ts-sql-plugin sql param object key can not have more than one space"); 115 | } 116 | 117 | // ? 有想过把所有数据都放在类型系统上, 这样 sql.raw`` 得到的结果就可以作为变量到处传递了, 不需要限制死在 sql`` 内部使用, 与运行时等同...但问题是 TemplateStringsArray 把字符串模板的 const 字符串信息丢失了, 这里只能 typescript 上游去解决, 这样在类型上根本无法得到 raw 里面的字符串, 至于从变量传递作用域上, 那结果就是完全不确定的 118 | // interface AAA { 119 | // __texts: TSA; 120 | // __values: VS; 121 | // } 122 | 123 | // function abc(texts: TSA, ...vs: VS): AAA { 124 | // return {__texts: texts, __values: vs} 125 | // } 126 | 127 | // var a = abc`select * from ${123} and good ${new Date()} ${window}`; 128 | // // var a: AAA<['select * from ', ' and good ', ' ', ''], [number, Date, Window]> 129 | 130 | // enum ExpressionKind { 131 | // RAW, 132 | // SQL, 133 | // AND, 134 | // INS, 135 | // UPD, 136 | // }; 137 | 138 | // interface Expression { 139 | // __kind__: ExpressionKind; 140 | // text: string; 141 | // values: any[]; 142 | // } 143 | 144 | // interface RawExpression extends Expression { 145 | // __kind__: ExpressionKind.RAW; 146 | // } 147 | 148 | // interface SqlExpression extends Expression { 149 | // __kind__: ExpressionKind.SQL; 150 | // } 151 | 152 | // interface AndExpression extends Expression { 153 | // __kind__: ExpressionKind.AND; 154 | // } 155 | 156 | // interface InsExpression extends Expression { 157 | // __kind__: ExpressionKind.INS; 158 | // } 159 | 160 | // interface UpdExpression extends Expression { 161 | // __kind__: ExpressionKind.UPD; 162 | // } 163 | 164 | // // raw: raw 165 | // // and: and 166 | // // ins: ins 167 | // // upd: upd 168 | -------------------------------------------------------------------------------- /test/cache.ts: -------------------------------------------------------------------------------- 1 | import { expect } from "chai"; 2 | import { addToSqlCache, existsInSqlCache } from "../lib/cache"; 3 | 4 | describe("Cache", () => { 5 | it("cache", () => { 6 | const set = addToSqlCache("command", ["a", "b"]); 7 | expect(set.size).equal(1); 8 | 9 | const exists = existsInSqlCache("command", ["a", "b"]) 10 | expect(exists).equal(true); 11 | 12 | const exists2 = existsInSqlCache("command", ["a", "b", "c"]) 13 | expect(exists2).equal(false); 14 | }); 15 | }); 16 | -------------------------------------------------------------------------------- /test/directives.ts: -------------------------------------------------------------------------------- 1 | import { expect } from "chai"; 2 | import { parseDirectives } from "../lib/directiveParser"; 3 | 4 | describe("parseDirectives ", () => { 5 | it("should return [] when theres no directives specified", function () { 6 | expect(parseDirectives("select * from *")).to.have.lengthOf(0); 7 | expect(parseDirectives("-- helloworld")).to.have.lengthOf(0); 8 | expect(parseDirectives("-- ts-sql-plugin: \n select")).to.have.lengthOf( 9 | 0 10 | ); 11 | }); 12 | 13 | it("should parse single line directives", function () { 14 | const result = parseDirectives(` 15 | -- ts-sql-plugin:ignore-cost 16 | select * from * 17 | `); 18 | expect(result).to.have.lengthOf(1, "it should find one directive"); 19 | expect(result[0].directive).to.eq("ignore-cost"); 20 | expect(result[0].arg).to.be.undefined; 21 | }); 22 | 23 | it("should parse single line with json argument", function () { 24 | const result = parseDirectives(` 25 | -- ts-sql-plugin:emit("../emitdir/filename") 26 | select * from * 27 | `); 28 | expect(result).to.have.lengthOf(1, "it should find one directive"); 29 | expect(result[0].directive).to.eq("emit"); 30 | expect(result[0].arg).to.eq("../emitdir/filename"); 31 | }); 32 | 33 | it("should parse multiple lines with directive", function () { 34 | const result = parseDirectives(` 35 | -- ts-sql-plugin:emit("../emitdir/filename") 36 | -- ts-sql-plugin:ignore-cost 37 | select * from * 38 | `); 39 | expect(result).to.have.lengthOf(2, "it should find two directives"); 40 | expect(result[0]).to.deep.eq({ 41 | directive: "emit", 42 | arg: "../emitdir/filename", 43 | }); 44 | expect(result[1]).to.deep.eq({ directive: "ignore-cost", arg: undefined }); 45 | }); 46 | 47 | it("should extract only first directives", function () { 48 | const result = parseDirectives(` 49 | -- ts-sql-plugin:emit("../emitdir/filename") 50 | -- ts-sql-plugin:ignore-cost 51 | select * from ( 52 | -- ts-sql-plugin:ignore-cost 53 | select * from * 54 | ) x 55 | `); 56 | expect(result).to.have.lengthOf(2, "it should find two directives"); 57 | expect(result[0]).to.deep.eq({ 58 | directive: "emit", 59 | arg: "../emitdir/filename", 60 | }); 61 | expect(result[1]).to.deep.eq({ directive: "ignore-cost", arg: undefined }); 62 | }); 63 | 64 | it("test /**/ comments", function () { 65 | const result = parseDirectives(` 66 | /* ts-sql-plugin:emit("../emitdir/filename") */ 67 | /* ts-sql-plugin:ignore-cost */ 68 | select * from ( 69 | /* ts-sql-plugin:ignore-cost */ 70 | select * from * 71 | ) x 72 | `); 73 | expect(result).to.have.lengthOf(2, "it should find two directives"); 74 | expect(result[0]).to.deep.eq({ 75 | directive: "emit", 76 | arg: "../emitdir/filename", 77 | }); 78 | expect(result[1]).to.deep.eq({ directive: "ignore-cost", arg: undefined }); 79 | }); 80 | 81 | }); 82 | -------------------------------------------------------------------------------- /test/utils.ts: -------------------------------------------------------------------------------- 1 | import { expect } from "chai"; 2 | import { trim_middle_comments } from "../lib/utils"; 3 | 4 | describe("Utils", () => { 5 | describe("trim_middle_comments", () => { 6 | it("SQL without comments as is", () => { 7 | const sql = ` 8 | select * from users 9 | where id = 1`; 10 | expect(trim_middle_comments(sql)).equal(sql.trim()); 11 | }); 12 | 13 | it("comments on head should be preserved", () => { 14 | const sql = ` 15 | 16 | -- stay here 17 | /* ts-sql-plugin:ignore-cost */ 18 | select * from users 19 | where id = 1`; 20 | expect(trim_middle_comments(sql)).equal(sql.trim()); 21 | }); 22 | 23 | it("drop comments from middle side", () => { 24 | const sql = ` 25 | -- stay here 26 | /* ts-sql-plugin:ignore-cost */ 27 | select * from ( 28 | -- remove from here 29 | /* @name Users */ 30 | /* ts-sql-plugin:ignore-cost */ 31 | select * from users 32 | ) x 33 | where id = 1`; 34 | expect(trim_middle_comments(sql)).equal( 35 | ` 36 | -- stay here 37 | /* ts-sql-plugin:ignore-cost */ 38 | select * from ( 39 | select * from users 40 | ) x 41 | where id = 1`.trim(), 42 | ); 43 | }); 44 | }); 45 | }); 46 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | /* Visit https://aka.ms/tsconfig.json to read more about this file */ 4 | 5 | /* Basic Options */ 6 | // "incremental": true, /* Enable incremental compilation */ 7 | "target": "ES2019" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */, 8 | "module": "CommonJS" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */, 9 | "lib": ["ES2020"] /* Specify library files to be included in the compilation. */, 10 | // "allowJs": true, /* Allow javascript files to be compiled. */ 11 | // "checkJs": true, /* Report errors in .js files. */ 12 | // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ 13 | // "declaration": true, /* Generates corresponding '.d.ts' file. */ 14 | // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ 15 | "sourceMap": true /* Generates corresponding '.map' file. */, 16 | // "outFile": "./", /* Concatenate and emit output to single file. */ 17 | // "outDir": "./", /* Redirect output structure to the directory. */ 18 | // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ 19 | // "composite": true, /* Enable project compilation */ 20 | // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ 21 | // "removeComments": true, /* Do not emit comments to output. */ 22 | // "noEmit": true, /* Do not emit outputs. */ 23 | "importHelpers": true /* Import emit helpers from 'tslib'. */, 24 | // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ 25 | // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ 26 | 27 | /* Strict Type-Checking Options */ 28 | "strict": true /* Enable all strict type-checking options. */, 29 | // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ 30 | // "strictNullChecks": true, /* Enable strict null checks. */ 31 | // "strictFunctionTypes": true, /* Enable strict checking of function types. */ 32 | // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ 33 | // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ 34 | // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ 35 | // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ 36 | 37 | /* Additional Checks */ 38 | // "noUnusedLocals": true, /* Report errors on unused locals. */ 39 | // "noUnusedParameters": true, /* Report errors on unused parameters. */ 40 | // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ 41 | // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ 42 | 43 | /* Module Resolution Options */ 44 | "moduleResolution": "node" /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */, 45 | // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ 46 | // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ 47 | // "rootDirs": ["./"], /* List of root folders whose combined content represents the structure of the project at runtime. */ 48 | // "typeRoots": [], /* List of folders to include type definitions from. */ 49 | // "types": [], /* Type declaration files to be included in compilation. */ 50 | // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ 51 | "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */, 52 | // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ 53 | // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ 54 | 55 | /* Source Map Options */ 56 | // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ 57 | // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ 58 | // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ 59 | // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ 60 | 61 | /* Experimental Options */ 62 | // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ 63 | // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ 64 | 65 | /* Advanced Options */ 66 | "skipLibCheck": true /* Skip type checking of declaration files. */, 67 | "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */ 68 | }, 69 | // "files": ["."], 70 | // "include": ["./**/*"], 71 | "exclude": [".cache/", ".vscode/", "node_modules/", "dist/", "./gitignore.d/"] 72 | } 73 | -------------------------------------------------------------------------------- /typings.d.ts: -------------------------------------------------------------------------------- 1 | declare module "await-spawn" { 2 | export default function (cmd: string, args: string[], opts?: object): Promise; 3 | } 4 | --------------------------------------------------------------------------------