diff --git a/CHANGELOG.md b/CHANGELOG.md index cbc39fd..c580562 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,11 +1,33 @@ # Omnisearch Changelog -## 1.2.0, 1.2.1 +## 1.3.x + +### New + +* Chinese support by @aidenlx in https://github.com/scambier/obsidian-omnisearch/pull/37 + * You need to install https://github.com/aidenlx/cm-chs-patch to enable this feature +* Settings page https://github.com/scambier/obsidian-omnisearch/issues/41 +* Do not show indexing Notice by default by @chrisgrieser in https://github.com/scambier/obsidian-omnisearch/pull/46 +* Include notes that don't exist https://github.com/scambier/obsidian-omnisearch/issues/14 + +### Improved + +* Better accessibility https://github.com/scambier/obsidian-omnisearch/issues/50 +* Note aliases are now scored as high as the filename in search results https://github.com/scambier/obsidian-omnisearch/issues/34 +* By default, reindexing is now done when the app is out of focus, and not after each save https://github.com/scambier/obsidian-omnisearch/issues/57 + * On mobile, indexing is only done at startup + +### Fixed + +* Showing an error when a note can't be created https://github.com/scambier/obsidian-omnisearch/issues/52 + + +## 1.2.x ### New * #42 Files that are present in Obsidian's "Excluded Files" list are downranked by a factor of 3 (_desktop only_) -## 1.2.1 +## 1.1.1 ### Fixes * Fixed a crash when no results were returned @@ -28,4 +50,4 @@ This works as a "post-search" filter and does not allow for partial words search ## 1.0.0 * First non-beta release -* Includes Vault search and In-File search \ No newline at end of file +* Includes Vault search and In-File search diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 0fc8d0b..461dd16 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -13,6 +13,12 @@ Please read this document before beginning work on a Pull Request. - Omnisearch is still in its infancy: some important features are missing, and there will be architectural changes. - As such, I may refuse your PR simply because it will have to be refactored in a short-ish term +## "Good First Issue" + +Are you a beginner, looking for a small open source contribution? Look at the "[good first issues](https://github.com/scambier/obsidian-omnisearch/labels/good%20first%20issue)". Those issues have a limited scope, don't require intricate knowledge of the code, and are easy enough to locate, fix, and test. + +If you wish to work on one of these issues, leave a comment and I'll assign it to you and give you some pointers. + ## Code guidelines - Respect the existing style @@ -38,4 +44,5 @@ Always respect those UI & UX points: ## Style guidelines -(todo) +- .ts files must be formatted with "Prettier ESLint" +- .svelte files must be formatted with "Svelte for VS Code" diff --git a/README.md b/README.md index 0c23417..796936a 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,11 @@ # Omnisearch for Obsidian +[![Sponsor me](https://img.shields.io/badge/%E2%9D%A4%20Like%20this%20plugin%3F-Sponsor%20me!-ff69b4)](https://github.com/sponsors/scambier) + ![GitHub release (latest by date including pre-releases)](https://img.shields.io/github/v/release/scambier/obsidian-omnisearch) ![GitHub release (latest by date including pre-releases)](https://img.shields.io/github/v/release/scambier/obsidian-omnisearch?include_prereleases&label=BRAT%20beta) [![Active Development](https://img.shields.io/badge/Maintenance%20Level-Actively%20Developed-brightgreen.svg)](https://gist.github.com/cheerfulstoic/d107229326a01ff0f333a1d3476e068d) -**Omnisearch** is a search engine that "_just works_". Type what you're looking for, and it will instantly show you the most relevant results. +**Omnisearch** is a search engine that "_just works_". It always instantly shows you the most relevant results, thanks to its smart weighting algorithm. Under the hood, it uses the excellent [MiniSearch](https://github.com/lucaong/minisearch) library. @@ -11,10 +13,10 @@ Under the hood, it uses the excellent [MiniSearch](https://github.com/lucaong/mi ## Features -- Keyboard-centric, you never have to use your mouse - Automatic document scoring using the [BM25 algorithm](https://github.com/lucaong/minisearch/issues/129#issuecomment-1046257399) - The relevance of a document against a query depends on the number of times the query terms appear in the document, its filename, and its headings -- Instant search results, with highlighting +- Keyboard first: you never have to use your mouse +- Instant & highlighted search results - Resistance to typos - In-file search to quickly skim multiple results in a single note - Search filters: expressions in quotes and exclusions @@ -22,8 +24,8 @@ Under the hood, it uses the excellent [MiniSearch](https://github.com/lucaong/mi ## Installation -- Omnisearch is available on [the official Community Plugins repository](https://obsidian.md/plugins?search=omnisearch#). -- You can also install it through [BRAT](https://github.com/TfTHacker/obsidian42-brat) for pre-releases. Be advised that those versions can be buggy. +- Omnisearch is available on [the official Community Plugins repository](https://obsidian.md/plugins?search=Omnisearch). +- Beta releases can be installed through [BRAT](https://github.com/TfTHacker/obsidian42-brat). **Be advised that those versions can be buggy.** You can check the [CHANGELOG](./CHANGELOG.md) for more information on the different versions. @@ -33,13 +35,13 @@ Omnisearch can be used within 2 different contexts: ### Vault Search -Omnisearch's core feature, accessible with the Command Palette "_Omnisearch: Vault search_". This modal searches through your vault and returns the most relevant notes first. The notes that contain the query terms in their filename or headings are weighted higher than the others. +Omnisearch's core feature, accessible with the Command Palette "**_Omnisearch: Vault search_**". This modal searches through your vault and returns the most relevant notes. That's all you need to _find_ a note. -If you need to list all the matches of a single note, you can do so by using `alt+enter` to open the In-File Search. +If you want to list all the search matches of a single note, you can do so by using `alt+enter` to open the In-File Search. ### In-File Search -Also accessible through the command palette "_Omnisearch: In-file search_". This modal searches through the active note's content and lists the results. +Also accessible through the Command Palette "**_Omnisearch: In-file search_**". This modal searches through the active note's content and lists the matching results. Just press enter to automatically scroll to the right place. ## Customization diff --git a/manifest-beta.json b/manifest-beta.json index e7fd777..75e776d 100644 --- a/manifest-beta.json +++ b/manifest-beta.json @@ -1,7 +1,7 @@ { "id": "omnisearch", "name": "Omnisearch", - "version": "1.2.1", + "version": "1.3.5-beta3", "minAppVersion": "0.14.2", "description": "A search engine that just works", "author": "Simon Cambier", diff --git a/manifest.json b/manifest.json index e7fd777..10e672b 100644 --- a/manifest.json +++ b/manifest.json @@ -1,7 +1,7 @@ { "id": "omnisearch", "name": "Omnisearch", - "version": "1.2.1", + "version": "1.3.4", "minAppVersion": "0.14.2", "description": "A search engine that just works", "author": "Simon Cambier", diff --git a/package.json b/package.json index 44a7df1..54b2351 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "scambier.obsidian-search", - "version": "1.2.1", + "version": "1.3.5-beta3", "description": "A search engine for Obsidian", "main": "dist/main.js", "scripts": { @@ -15,14 +15,14 @@ "author": "Simon Cambier", "license": "GPL-3", "devDependencies": { - "@babel/preset-env": "^7.16.11", + "@babel/preset-env": "^7.17.10", "@babel/preset-typescript": "^7.16.7", "@testing-library/jest-dom": "^5.16.4", "@tsconfig/svelte": "^3.0.0", - "@types/jest": "^27.4.1", - "@types/node": "^16.11.27", - "@typescript-eslint/eslint-plugin": "^5.20.0", - "@typescript-eslint/parser": "^5.20.0", + "@types/jest": "^27.5.0", + "@types/node": "^16.11.34", + "@typescript-eslint/eslint-plugin": "^5.23.0", + "@typescript-eslint/parser": "^5.23.0", "babel-jest": "^27.5.1", "builtin-modules": "^3.2.0", "esbuild": "0.13.12", @@ -38,11 +38,11 @@ "obsidian": "latest", "prettier": "^2.6.2", "prettier-eslint": "^13.0.0", - "svelte": "^3.47.0", + "svelte": "^3.48.0", "svelte-jester": "^2.3.2", "svelte-preprocess": "^4.10.6", "tslib": "2.3.1", - "typescript": "^4.6.3" + "typescript": "^4.6.4" }, "dependencies": { "minisearch": "^5.0.0-beta1" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d9db0d6..e1e0110 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1,14 +1,14 @@ lockfileVersion: 5.3 specifiers: - '@babel/preset-env': ^7.16.11 + '@babel/preset-env': ^7.17.10 '@babel/preset-typescript': ^7.16.7 '@testing-library/jest-dom': ^5.16.4 '@tsconfig/svelte': ^3.0.0 - '@types/jest': ^27.4.1 - '@types/node': ^16.11.27 - '@typescript-eslint/eslint-plugin': ^5.20.0 - '@typescript-eslint/parser': ^5.20.0 + '@types/jest': ^27.5.0 + '@types/node': ^16.11.34 + '@typescript-eslint/eslint-plugin': ^5.23.0 + '@typescript-eslint/parser': ^5.23.0 babel-jest: ^27.5.1 builtin-modules: ^3.2.0 esbuild: 0.13.12 @@ -25,52 +25,53 @@ specifiers: obsidian: latest prettier: ^2.6.2 prettier-eslint: ^13.0.0 - svelte: ^3.47.0 + svelte: ^3.48.0 svelte-jester: ^2.3.2 svelte-preprocess: ^4.10.6 tslib: 2.3.1 - typescript: ^4.6.3 + typescript: ^4.6.4 dependencies: minisearch: 5.0.0-beta1 devDependencies: - '@babel/preset-env': 7.16.11 + '@babel/preset-env': 7.17.10 '@babel/preset-typescript': 7.16.7 '@testing-library/jest-dom': 5.16.4 '@tsconfig/svelte': 3.0.0 - '@types/jest': 27.4.1 - '@types/node': 16.11.27 - '@typescript-eslint/eslint-plugin': 5.20.0_4fbab8ed861393b891eae3bdfcc6594e - '@typescript-eslint/parser': 5.20.0_eslint@7.12.1+typescript@4.6.3 + '@types/jest': 27.5.0 + '@types/node': 16.11.34 + '@typescript-eslint/eslint-plugin': 5.23.0_8d5dd86e19d360799b5f51045840aced + '@typescript-eslint/parser': 5.23.0_eslint@7.12.1+typescript@4.6.4 babel-jest: 27.5.1 builtin-modules: 3.2.0 esbuild: 0.13.12 esbuild-plugin-copy: 1.3.0_esbuild@0.13.12 - esbuild-svelte: 0.7.0_esbuild@0.13.12+svelte@3.47.0 + esbuild-svelte: 0.7.0_esbuild@0.13.12+svelte@3.48.0 eslint: 7.12.1 eslint-config-standard: 16.0.3_0fed5a0754053d34bf68c4e5b9539157 eslint-plugin-import: 2.22.1_eslint@7.12.1 eslint-plugin-node: 11.1.0_eslint@7.12.1 eslint-plugin-promise: 5.0.0_eslint@7.12.1 - eslint-plugin-svelte3: 3.4.1_eslint@7.12.1+svelte@3.47.0 + eslint-plugin-svelte3: 3.4.1_eslint@7.12.1+svelte@3.48.0 jest: 27.5.1 - obsidian: 0.14.6 + obsidian: 0.14.8 prettier: 2.6.2 prettier-eslint: 13.0.0 - svelte: 3.47.0 - svelte-jester: 2.3.2_jest@27.5.1+svelte@3.47.0 - svelte-preprocess: 4.10.6_svelte@3.47.0+typescript@4.6.3 + svelte: 3.48.0 + svelte-jester: 2.3.2_jest@27.5.1+svelte@3.48.0 + svelte-preprocess: 4.10.6_svelte@3.48.0+typescript@4.6.4 tslib: 2.3.1 - typescript: 4.6.3 + typescript: 4.6.4 packages: - /@ampproject/remapping/2.1.2: - resolution: {integrity: sha512-hoyByceqwKirw7w3Z7gnIIZC3Wx3J484Y3L/cMpXFbr7d9ZQj2mODrirNzcJa+SM3UlpWXYvKV4RlRpFXlWgXg==} + /@ampproject/remapping/2.2.0: + resolution: {integrity: sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==} engines: {node: '>=6.0.0'} dependencies: - '@jridgewell/trace-mapping': 0.3.9 + '@jridgewell/gen-mapping': 0.1.1 + '@jridgewell/trace-mapping': 0.3.11 dev: true /@babel/code-frame/7.16.7: @@ -80,25 +81,25 @@ packages: '@babel/highlight': 7.17.9 dev: true - /@babel/compat-data/7.17.7: - resolution: {integrity: sha512-p8pdE6j0a29TNGebNm7NzYZWB3xVZJBZ7XGs42uAKzQo8VQ3F0By/cQCtUEABwIqw5zo6WA4NbmxsfzADzMKnQ==} + /@babel/compat-data/7.17.10: + resolution: {integrity: sha512-GZt/TCsG70Ms19gfZO1tM4CVnXsPgEPBCpJu+Qz3L0LUDsY5nZqFZglIoPC1kIYOtNBZlrnFT+klg12vFGZXrw==} engines: {node: '>=6.9.0'} dev: true - /@babel/core/7.17.9: - resolution: {integrity: sha512-5ug+SfZCpDAkVp9SFIZAzlW18rlzsOcJGaetCjkySnrXXDUw9AR8cDUm1iByTmdWM6yxX6/zycaV76w3YTF2gw==} + /@babel/core/7.17.10: + resolution: {integrity: sha512-liKoppandF3ZcBnIYFjfSDHZLKdLHGJRkoWtG8zQyGJBQfIYobpnVGI5+pLBNtS6psFLDzyq8+h5HiVljW9PNA==} engines: {node: '>=6.9.0'} dependencies: - '@ampproject/remapping': 2.1.2 + '@ampproject/remapping': 2.2.0 '@babel/code-frame': 7.16.7 - '@babel/generator': 7.17.9 - '@babel/helper-compilation-targets': 7.17.7_@babel+core@7.17.9 + '@babel/generator': 7.17.10 + '@babel/helper-compilation-targets': 7.17.10_@babel+core@7.17.10 '@babel/helper-module-transforms': 7.17.7 '@babel/helpers': 7.17.9 - '@babel/parser': 7.17.9 + '@babel/parser': 7.17.10 '@babel/template': 7.16.7 - '@babel/traverse': 7.17.9 - '@babel/types': 7.17.0 + '@babel/traverse': 7.17.10 + '@babel/types': 7.17.10 convert-source-map: 1.8.0 debug: 4.3.4 gensync: 1.0.0-beta.2 @@ -108,20 +109,20 @@ packages: - supports-color dev: true - /@babel/generator/7.17.9: - resolution: {integrity: sha512-rAdDousTwxbIxbz5I7GEQ3lUip+xVCXooZNbsydCWs3xA7ZsYOv+CFRdzGxRX78BmQHu9B1Eso59AOZQOJDEdQ==} + /@babel/generator/7.17.10: + resolution: {integrity: sha512-46MJZZo9y3o4kmhBVc7zW7i8dtR1oIK/sdO5NcfcZRhTGYi+KKJRtHNgsU6c4VUcJmUNV/LQdebD/9Dlv4K+Tg==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.17.0 + '@babel/types': 7.17.10 + '@jridgewell/gen-mapping': 0.1.1 jsesc: 2.5.2 - source-map: 0.5.7 dev: true /@babel/helper-annotate-as-pure/7.16.7: resolution: {integrity: sha512-s6t2w/IPQVTAET1HitoowRGXooX8mCgtuP5195wD/QJPV6wYjpujCGF7JuMODVX2ZAJOf1GT6DT9MHEZvLOFSw==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.17.0 + '@babel/types': 7.17.10 dev: true /@babel/helper-builder-binary-assignment-operator-visitor/7.16.7: @@ -129,31 +130,31 @@ packages: engines: {node: '>=6.9.0'} dependencies: '@babel/helper-explode-assignable-expression': 7.16.7 - '@babel/types': 7.17.0 + '@babel/types': 7.17.10 dev: true - /@babel/helper-compilation-targets/7.17.7: - resolution: {integrity: sha512-UFzlz2jjd8kroj0hmCFV5zr+tQPi1dpC2cRsDV/3IEW8bJfCPrPpmcSN6ZS8RqIq4LXcmpipCQFPddyFA5Yc7w==} + /@babel/helper-compilation-targets/7.17.10: + resolution: {integrity: sha512-gh3RxjWbauw/dFiU/7whjd0qN9K6nPJMqe6+Er7rOavFh0CQUSwhAE3IcTho2rywPJFxej6TUUHDkWcYI6gGqQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 dependencies: - '@babel/compat-data': 7.17.7 + '@babel/compat-data': 7.17.10 '@babel/helper-validator-option': 7.16.7 - browserslist: 4.20.2 + browserslist: 4.20.3 semver: 6.3.0 dev: true - /@babel/helper-compilation-targets/7.17.7_@babel+core@7.17.9: - resolution: {integrity: sha512-UFzlz2jjd8kroj0hmCFV5zr+tQPi1dpC2cRsDV/3IEW8bJfCPrPpmcSN6ZS8RqIq4LXcmpipCQFPddyFA5Yc7w==} + /@babel/helper-compilation-targets/7.17.10_@babel+core@7.17.10: + resolution: {integrity: sha512-gh3RxjWbauw/dFiU/7whjd0qN9K6nPJMqe6+Er7rOavFh0CQUSwhAE3IcTho2rywPJFxej6TUUHDkWcYI6gGqQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 dependencies: - '@babel/compat-data': 7.17.7 - '@babel/core': 7.17.9 + '@babel/compat-data': 7.17.10 + '@babel/core': 7.17.10 '@babel/helper-validator-option': 7.16.7 - browserslist: 4.20.2 + browserslist: 4.20.3 semver: 6.3.0 dev: true @@ -189,10 +190,10 @@ packages: peerDependencies: '@babel/core': ^7.4.0-0 dependencies: - '@babel/helper-compilation-targets': 7.17.7 + '@babel/helper-compilation-targets': 7.17.10 '@babel/helper-module-imports': 7.16.7 '@babel/helper-plugin-utils': 7.16.7 - '@babel/traverse': 7.17.9 + '@babel/traverse': 7.17.10 debug: 4.3.4 lodash.debounce: 4.0.8 resolve: 1.22.0 @@ -205,14 +206,14 @@ packages: resolution: {integrity: sha512-SLLb0AAn6PkUeAfKJCCOl9e1R53pQlGAfc4y4XuMRZfqeMYLE0dM1LMhqbGAlGQY0lfw5/ohoYWAe9V1yibRag==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.17.0 + '@babel/types': 7.17.10 dev: true /@babel/helper-explode-assignable-expression/7.16.7: resolution: {integrity: sha512-KyUenhWMC8VrxzkGP0Jizjo4/Zx+1nNZhgocs+gLzyZyB8SHidhoq9KK/8Ato4anhwsivfkBLftky7gvzbZMtQ==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.17.0 + '@babel/types': 7.17.10 dev: true /@babel/helper-function-name/7.17.9: @@ -220,28 +221,28 @@ packages: engines: {node: '>=6.9.0'} dependencies: '@babel/template': 7.16.7 - '@babel/types': 7.17.0 + '@babel/types': 7.17.10 dev: true /@babel/helper-hoist-variables/7.16.7: resolution: {integrity: sha512-m04d/0Op34H5v7pbZw6pSKP7weA6lsMvfiIAMeIvkY/R4xQtBSMFEigu9QTZ2qB/9l22vsxtM8a+Q8CzD255fg==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.17.0 + '@babel/types': 7.17.10 dev: true /@babel/helper-member-expression-to-functions/7.17.7: resolution: {integrity: sha512-thxXgnQ8qQ11W2wVUObIqDL4p148VMxkt5T/qpN5k2fboRyzFGFmKsTGViquyM5QHKUy48OZoca8kw4ajaDPyw==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.17.0 + '@babel/types': 7.17.10 dev: true /@babel/helper-module-imports/7.16.7: resolution: {integrity: sha512-LVtS6TqjJHFc+nYeITRo6VLXve70xmq7wPhWTqDJusJEgGmkAACWwMiTNrvfoQo6hEhFwAIixNkvB0jPXDL8Wg==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.17.0 + '@babel/types': 7.17.10 dev: true /@babel/helper-module-transforms/7.17.7: @@ -254,8 +255,8 @@ packages: '@babel/helper-split-export-declaration': 7.16.7 '@babel/helper-validator-identifier': 7.16.7 '@babel/template': 7.16.7 - '@babel/traverse': 7.17.9 - '@babel/types': 7.17.0 + '@babel/traverse': 7.17.10 + '@babel/types': 7.17.10 transitivePeerDependencies: - supports-color dev: true @@ -264,7 +265,7 @@ packages: resolution: {integrity: sha512-EtgBhg7rd/JcnpZFXpBy0ze1YRfdm7BnBX4uKMBd3ixa3RGAE002JZB66FJyNH7g0F38U05pXmA5P8cBh7z+1w==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.17.0 + '@babel/types': 7.17.10 dev: true /@babel/helper-plugin-utils/7.16.7: @@ -278,7 +279,7 @@ packages: dependencies: '@babel/helper-annotate-as-pure': 7.16.7 '@babel/helper-wrap-function': 7.16.8 - '@babel/types': 7.17.0 + '@babel/types': 7.17.10 transitivePeerDependencies: - supports-color dev: true @@ -290,8 +291,8 @@ packages: '@babel/helper-environment-visitor': 7.16.7 '@babel/helper-member-expression-to-functions': 7.17.7 '@babel/helper-optimise-call-expression': 7.16.7 - '@babel/traverse': 7.17.9 - '@babel/types': 7.17.0 + '@babel/traverse': 7.17.10 + '@babel/types': 7.17.10 transitivePeerDependencies: - supports-color dev: true @@ -300,21 +301,21 @@ packages: resolution: {integrity: sha512-txyMCGroZ96i+Pxr3Je3lzEJjqwaRC9buMUgtomcrLe5Nd0+fk1h0LLA+ixUF5OW7AhHuQ7Es1WcQJZmZsz2XA==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.17.0 + '@babel/types': 7.17.10 dev: true /@babel/helper-skip-transparent-expression-wrappers/7.16.0: resolution: {integrity: sha512-+il1gTy0oHwUsBQZyJvukbB4vPMdcYBrFHa0Uc4AizLxbq6BOYC51Rv4tWocX9BLBDLZ4kc6qUFpQ6HRgL+3zw==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.17.0 + '@babel/types': 7.17.10 dev: true /@babel/helper-split-export-declaration/7.16.7: resolution: {integrity: sha512-xbWoy/PFoxSWazIToT9Sif+jJTlrMcndIsaOKvTA6u7QEo7ilkRZpjew18/W3c7nm8fXdUDXh02VXTbZ0pGDNw==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.17.0 + '@babel/types': 7.17.10 dev: true /@babel/helper-validator-identifier/7.16.7: @@ -333,8 +334,8 @@ packages: dependencies: '@babel/helper-function-name': 7.17.9 '@babel/template': 7.16.7 - '@babel/traverse': 7.17.9 - '@babel/types': 7.17.0 + '@babel/traverse': 7.17.10 + '@babel/types': 7.17.10 transitivePeerDependencies: - supports-color dev: true @@ -344,8 +345,8 @@ packages: engines: {node: '>=6.9.0'} dependencies: '@babel/template': 7.16.7 - '@babel/traverse': 7.17.9 - '@babel/types': 7.17.0 + '@babel/traverse': 7.17.10 + '@babel/types': 7.17.10 transitivePeerDependencies: - supports-color dev: true @@ -359,8 +360,8 @@ packages: js-tokens: 4.0.0 dev: true - /@babel/parser/7.17.9: - resolution: {integrity: sha512-vqUSBLP8dQHFPdPi9bc5GK9vRkYHJ49fsZdtoJ8EQ8ibpwk5rPKfvNIwChB0KVXcIjcepEBBd2VHC5r9Gy8ueg==} + /@babel/parser/7.17.10: + resolution: {integrity: sha512-n2Q6i+fnJqzOaq2VkdXxy2TCPCWQZHiCo0XqmrCvDWcZQKRyZzYi4Z0yxlBuN0w+r2ZHmre+Q087DSrw3pbJDQ==} engines: {node: '>=6.0.0'} hasBin: true dev: true @@ -489,8 +490,8 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/compat-data': 7.17.7 - '@babel/helper-compilation-targets': 7.17.7 + '@babel/compat-data': 7.17.10 + '@babel/helper-compilation-targets': 7.17.10 '@babel/helper-plugin-utils': 7.16.7 '@babel/plugin-syntax-object-rest-spread': 7.8.3 '@babel/plugin-transform-parameters': 7.16.7 @@ -561,12 +562,12 @@ packages: '@babel/helper-plugin-utils': 7.16.7 dev: true - /@babel/plugin-syntax-async-generators/7.8.4_@babel+core@7.17.9: + /@babel/plugin-syntax-async-generators/7.8.4_@babel+core@7.17.10: resolution: {integrity: sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.17.9 + '@babel/core': 7.17.10 '@babel/helper-plugin-utils': 7.16.7 dev: true @@ -578,12 +579,12 @@ packages: '@babel/helper-plugin-utils': 7.16.7 dev: true - /@babel/plugin-syntax-bigint/7.8.3_@babel+core@7.17.9: + /@babel/plugin-syntax-bigint/7.8.3_@babel+core@7.17.10: resolution: {integrity: sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.17.9 + '@babel/core': 7.17.10 '@babel/helper-plugin-utils': 7.16.7 dev: true @@ -595,12 +596,12 @@ packages: '@babel/helper-plugin-utils': 7.16.7 dev: true - /@babel/plugin-syntax-class-properties/7.12.13_@babel+core@7.17.9: + /@babel/plugin-syntax-class-properties/7.12.13_@babel+core@7.17.10: resolution: {integrity: sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.17.9 + '@babel/core': 7.17.10 '@babel/helper-plugin-utils': 7.16.7 dev: true @@ -637,12 +638,12 @@ packages: '@babel/helper-plugin-utils': 7.16.7 dev: true - /@babel/plugin-syntax-import-meta/7.10.4_@babel+core@7.17.9: + /@babel/plugin-syntax-import-meta/7.10.4_@babel+core@7.17.10: resolution: {integrity: sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.17.9 + '@babel/core': 7.17.10 '@babel/helper-plugin-utils': 7.16.7 dev: true @@ -654,12 +655,12 @@ packages: '@babel/helper-plugin-utils': 7.16.7 dev: true - /@babel/plugin-syntax-json-strings/7.8.3_@babel+core@7.17.9: + /@babel/plugin-syntax-json-strings/7.8.3_@babel+core@7.17.10: resolution: {integrity: sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.17.9 + '@babel/core': 7.17.10 '@babel/helper-plugin-utils': 7.16.7 dev: true @@ -671,12 +672,12 @@ packages: '@babel/helper-plugin-utils': 7.16.7 dev: true - /@babel/plugin-syntax-logical-assignment-operators/7.10.4_@babel+core@7.17.9: + /@babel/plugin-syntax-logical-assignment-operators/7.10.4_@babel+core@7.17.10: resolution: {integrity: sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.17.9 + '@babel/core': 7.17.10 '@babel/helper-plugin-utils': 7.16.7 dev: true @@ -688,12 +689,12 @@ packages: '@babel/helper-plugin-utils': 7.16.7 dev: true - /@babel/plugin-syntax-nullish-coalescing-operator/7.8.3_@babel+core@7.17.9: + /@babel/plugin-syntax-nullish-coalescing-operator/7.8.3_@babel+core@7.17.10: resolution: {integrity: sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.17.9 + '@babel/core': 7.17.10 '@babel/helper-plugin-utils': 7.16.7 dev: true @@ -705,12 +706,12 @@ packages: '@babel/helper-plugin-utils': 7.16.7 dev: true - /@babel/plugin-syntax-numeric-separator/7.10.4_@babel+core@7.17.9: + /@babel/plugin-syntax-numeric-separator/7.10.4_@babel+core@7.17.10: resolution: {integrity: sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.17.9 + '@babel/core': 7.17.10 '@babel/helper-plugin-utils': 7.16.7 dev: true @@ -722,12 +723,12 @@ packages: '@babel/helper-plugin-utils': 7.16.7 dev: true - /@babel/plugin-syntax-object-rest-spread/7.8.3_@babel+core@7.17.9: + /@babel/plugin-syntax-object-rest-spread/7.8.3_@babel+core@7.17.10: resolution: {integrity: sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.17.9 + '@babel/core': 7.17.10 '@babel/helper-plugin-utils': 7.16.7 dev: true @@ -739,12 +740,12 @@ packages: '@babel/helper-plugin-utils': 7.16.7 dev: true - /@babel/plugin-syntax-optional-catch-binding/7.8.3_@babel+core@7.17.9: + /@babel/plugin-syntax-optional-catch-binding/7.8.3_@babel+core@7.17.10: resolution: {integrity: sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.17.9 + '@babel/core': 7.17.10 '@babel/helper-plugin-utils': 7.16.7 dev: true @@ -756,12 +757,12 @@ packages: '@babel/helper-plugin-utils': 7.16.7 dev: true - /@babel/plugin-syntax-optional-chaining/7.8.3_@babel+core@7.17.9: + /@babel/plugin-syntax-optional-chaining/7.8.3_@babel+core@7.17.10: resolution: {integrity: sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.17.9 + '@babel/core': 7.17.10 '@babel/helper-plugin-utils': 7.16.7 dev: true @@ -783,18 +784,18 @@ packages: '@babel/helper-plugin-utils': 7.16.7 dev: true - /@babel/plugin-syntax-top-level-await/7.14.5_@babel+core@7.17.9: + /@babel/plugin-syntax-top-level-await/7.14.5_@babel+core@7.17.10: resolution: {integrity: sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.17.9 + '@babel/core': 7.17.10 '@babel/helper-plugin-utils': 7.16.7 dev: true - /@babel/plugin-syntax-typescript/7.16.7: - resolution: {integrity: sha512-YhUIJHHGkqPgEcMYkPCKTyGUdoGKWtopIycQyjJH8OjvRgOYsXsaKehLVPScKJWAULPxMa4N1vCe6szREFlZ7A==} + /@babel/plugin-syntax-typescript/7.17.10: + resolution: {integrity: sha512-xJefea1DWXW09pW4Tm9bjwVlPDyYA2it3fWlmEjpYz6alPvTUjL0EOzNzI/FEOyI3r4/J7uVH5UqKgl1TQ5hqQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -802,13 +803,13 @@ packages: '@babel/helper-plugin-utils': 7.16.7 dev: true - /@babel/plugin-syntax-typescript/7.16.7_@babel+core@7.17.9: - resolution: {integrity: sha512-YhUIJHHGkqPgEcMYkPCKTyGUdoGKWtopIycQyjJH8OjvRgOYsXsaKehLVPScKJWAULPxMa4N1vCe6szREFlZ7A==} + /@babel/plugin-syntax-typescript/7.17.10_@babel+core@7.17.10: + resolution: {integrity: sha512-xJefea1DWXW09pW4Tm9bjwVlPDyYA2it3fWlmEjpYz6alPvTUjL0EOzNzI/FEOyI3r4/J7uVH5UqKgl1TQ5hqQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.17.9 + '@babel/core': 7.17.10 '@babel/helper-plugin-utils': 7.16.7 dev: true @@ -932,7 +933,7 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/helper-compilation-targets': 7.17.7 + '@babel/helper-compilation-targets': 7.17.10 '@babel/helper-function-name': 7.17.9 '@babel/helper-plugin-utils': 7.16.7 dev: true @@ -1009,8 +1010,8 @@ packages: - supports-color dev: true - /@babel/plugin-transform-named-capturing-groups-regex/7.16.8: - resolution: {integrity: sha512-j3Jw+n5PvpmhRR+mrgIh04puSANCk/T/UA3m3P1MjJkhlK906+ApHhDIqBQDdOgL/r1UYpz4GNclTXxyZrYGSw==} + /@babel/plugin-transform-named-capturing-groups-regex/7.17.10: + resolution: {integrity: sha512-v54O6yLaJySCs6mGzaVOUw9T967GnH38T6CQSAtnzdNPwu84l2qAjssKzo/WSO8Yi7NF+7ekm5cVbF/5qiIgNA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 @@ -1129,7 +1130,7 @@ packages: dependencies: '@babel/helper-create-class-features-plugin': 7.17.9 '@babel/helper-plugin-utils': 7.16.7 - '@babel/plugin-syntax-typescript': 7.16.7 + '@babel/plugin-syntax-typescript': 7.17.10 transitivePeerDependencies: - supports-color dev: true @@ -1153,14 +1154,14 @@ packages: '@babel/helper-plugin-utils': 7.16.7 dev: true - /@babel/preset-env/7.16.11: - resolution: {integrity: sha512-qcmWG8R7ZW6WBRPZK//y+E3Cli151B20W1Rv7ln27vuPaXU/8TKms6jFdiJtF7UDTxcrb7mZd88tAeK9LjdT8g==} + /@babel/preset-env/7.17.10: + resolution: {integrity: sha512-YNgyBHZQpeoBSRBg0xixsZzfT58Ze1iZrajvv0lJc70qDDGuGfonEnMGfWeSY0mQ3JTuCWFbMkzFRVafOyJx4g==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/compat-data': 7.17.7 - '@babel/helper-compilation-targets': 7.17.7 + '@babel/compat-data': 7.17.10 + '@babel/helper-compilation-targets': 7.17.10 '@babel/helper-plugin-utils': 7.16.7 '@babel/helper-validator-option': 7.16.7 '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.16.7 @@ -1212,7 +1213,7 @@ packages: '@babel/plugin-transform-modules-commonjs': 7.17.9 '@babel/plugin-transform-modules-systemjs': 7.17.8 '@babel/plugin-transform-modules-umd': 7.16.7 - '@babel/plugin-transform-named-capturing-groups-regex': 7.16.8 + '@babel/plugin-transform-named-capturing-groups-regex': 7.17.10 '@babel/plugin-transform-new-target': 7.16.7 '@babel/plugin-transform-object-super': 7.16.7 '@babel/plugin-transform-parameters': 7.16.7 @@ -1227,11 +1228,11 @@ packages: '@babel/plugin-transform-unicode-escapes': 7.16.7 '@babel/plugin-transform-unicode-regex': 7.16.7 '@babel/preset-modules': 0.1.5 - '@babel/types': 7.17.0 + '@babel/types': 7.17.10 babel-plugin-polyfill-corejs2: 0.3.1 babel-plugin-polyfill-corejs3: 0.5.2 babel-plugin-polyfill-regenerator: 0.3.1 - core-js-compat: 3.22.2 + core-js-compat: 3.22.5 semver: 6.3.0 transitivePeerDependencies: - supports-color @@ -1245,7 +1246,7 @@ packages: '@babel/helper-plugin-utils': 7.16.7 '@babel/plugin-proposal-unicode-property-regex': 7.16.7 '@babel/plugin-transform-dotall-regex': 7.16.7 - '@babel/types': 7.17.0 + '@babel/types': 7.17.10 esutils: 2.0.3 dev: true @@ -1274,30 +1275,30 @@ packages: engines: {node: '>=6.9.0'} dependencies: '@babel/code-frame': 7.16.7 - '@babel/parser': 7.17.9 - '@babel/types': 7.17.0 + '@babel/parser': 7.17.10 + '@babel/types': 7.17.10 dev: true - /@babel/traverse/7.17.9: - resolution: {integrity: sha512-PQO8sDIJ8SIwipTPiR71kJQCKQYB5NGImbOviK8K+kg5xkNSYXLBupuX9QhatFowrsvo9Hj8WgArg3W7ijNAQw==} + /@babel/traverse/7.17.10: + resolution: {integrity: sha512-VmbrTHQteIdUUQNTb+zE12SHS/xQVIShmBPhlNP12hD5poF2pbITW1Z4172d03HegaQWhLffdkRJYtAzp0AGcw==} engines: {node: '>=6.9.0'} dependencies: '@babel/code-frame': 7.16.7 - '@babel/generator': 7.17.9 + '@babel/generator': 7.17.10 '@babel/helper-environment-visitor': 7.16.7 '@babel/helper-function-name': 7.17.9 '@babel/helper-hoist-variables': 7.16.7 '@babel/helper-split-export-declaration': 7.16.7 - '@babel/parser': 7.17.9 - '@babel/types': 7.17.0 + '@babel/parser': 7.17.10 + '@babel/types': 7.17.10 debug: 4.3.4 globals: 11.12.0 transitivePeerDependencies: - supports-color dev: true - /@babel/types/7.17.0: - resolution: {integrity: sha512-TmKSNO4D5rzhL5bjWFcVHHLETzfQ/AmbKpKPOSjlP0WoHZ6L911fgoOKY4Alp/emzG4cHJdyN49zpgkbXFEHHw==} + /@babel/types/7.17.10: + resolution: {integrity: sha512-9O26jG0mBYfGkUYCYZRnBwbVLd1UZOICEr2Em6InB6jVfsAv1GKgwXHmrSg+WFWDmeKTA6vyTZiN8tCSM5Oo3A==} engines: {node: '>=6.9.0'} dependencies: '@babel/helper-validator-identifier': 7.16.7 @@ -1373,7 +1374,7 @@ packages: engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} dependencies: '@jest/types': 27.5.1 - '@types/node': 16.11.27 + '@types/node': 16.11.34 chalk: 4.1.2 jest-message-util: 27.5.1 jest-util: 27.5.1 @@ -1394,7 +1395,7 @@ packages: '@jest/test-result': 27.5.1 '@jest/transform': 27.5.1 '@jest/types': 27.5.1 - '@types/node': 16.11.27 + '@types/node': 16.11.34 ansi-escapes: 4.3.2 chalk: 4.1.2 emittery: 0.8.1 @@ -1431,7 +1432,7 @@ packages: dependencies: '@jest/fake-timers': 27.5.1 '@jest/types': 27.5.1 - '@types/node': 16.11.27 + '@types/node': 16.11.34 jest-mock: 27.5.1 dev: true @@ -1441,7 +1442,7 @@ packages: dependencies: '@jest/types': 27.5.1 '@sinonjs/fake-timers': 8.1.0 - '@types/node': 16.11.27 + '@types/node': 16.11.34 jest-message-util: 27.5.1 jest-mock: 27.5.1 jest-util: 27.5.1 @@ -1470,7 +1471,7 @@ packages: '@jest/test-result': 27.5.1 '@jest/transform': 27.5.1 '@jest/types': 27.5.1 - '@types/node': 16.11.27 + '@types/node': 16.11.34 chalk: 4.1.2 collect-v8-coverage: 1.0.1 exit: 0.1.2 @@ -1529,7 +1530,7 @@ packages: resolution: {integrity: sha512-ipON6WtYgl/1329g5AIJVbUuEh0wZVbdpGwC99Jw4LwuoBNS95MVphU6zOeD9pDkon+LLbFL7lOQRapbB8SCHw==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} dependencies: - '@babel/core': 7.17.9 + '@babel/core': 7.17.10 '@jest/types': 27.5.1 babel-plugin-istanbul: 6.1.1 chalk: 4.1.2 @@ -1554,25 +1555,38 @@ packages: dependencies: '@types/istanbul-lib-coverage': 2.0.4 '@types/istanbul-reports': 3.0.1 - '@types/node': 16.11.27 + '@types/node': 16.11.34 '@types/yargs': 16.0.4 chalk: 4.1.2 dev: true - /@jridgewell/resolve-uri/3.0.6: - resolution: {integrity: sha512-R7xHtBSNm+9SyvpJkdQl+qrM3Hm2fea3Ef197M3mUug+v+yR+Rhfbs7PBtcBUVnIWJ4JcAdjvij+c8hXS9p5aw==} + /@jridgewell/gen-mapping/0.1.1: + resolution: {integrity: sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==} + engines: {node: '>=6.0.0'} + dependencies: + '@jridgewell/set-array': 1.1.1 + '@jridgewell/sourcemap-codec': 1.4.13 + dev: true + + /@jridgewell/resolve-uri/3.0.7: + resolution: {integrity: sha512-8cXDaBBHOr2pQ7j77Y6Vp5VDT2sIqWyWQ56TjEq4ih/a4iST3dItRe8Q9fp0rrIl9DoKhWQtUQz/YpOxLkXbNA==} engines: {node: '>=6.0.0'} dev: true - /@jridgewell/sourcemap-codec/1.4.11: - resolution: {integrity: sha512-Fg32GrJo61m+VqYSdRSjRXMjQ06j8YIYfcTqndLYVAaHmroZHLJZCydsWBOTDqXS2v+mjxohBWEMfg97GXmYQg==} + /@jridgewell/set-array/1.1.1: + resolution: {integrity: sha512-Ct5MqZkLGEXTVmQYbGtx9SVqD2fqwvdubdps5D3djjAkgkKwT918VNOz65pEHFaYTeWcukmJmH5SwsA9Tn2ObQ==} + engines: {node: '>=6.0.0'} dev: true - /@jridgewell/trace-mapping/0.3.9: - resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} + /@jridgewell/sourcemap-codec/1.4.13: + resolution: {integrity: sha512-GryiOJmNcWbovBxTfZSF71V/mXbgcV3MewDe3kIMCLyIh5e7SKAeUZs+rMnJ8jkMolZ/4/VsdBmMrw3l+VdZ3w==} + dev: true + + /@jridgewell/trace-mapping/0.3.11: + resolution: {integrity: sha512-RllI476aSMsxzeI9TtlSMoNTgHDxEmnl6GkkHwhr0vdL8W+0WuesyI8Vd3rBOfrwtPXbPxdT9ADJdiOKgzxPQA==} dependencies: - '@jridgewell/resolve-uri': 3.0.6 - '@jridgewell/sourcemap-codec': 1.4.11 + '@jridgewell/resolve-uri': 3.0.7 + '@jridgewell/sourcemap-codec': 1.4.13 dev: true /@nodelib/fs.scandir/2.1.5: @@ -1618,7 +1632,7 @@ packages: chalk: 3.0.0 css: 3.0.0 css.escape: 1.5.1 - dom-accessibility-api: 0.5.13 + dom-accessibility-api: 0.5.14 lodash: 4.17.21 redent: 3.0.0 dev: true @@ -1635,30 +1649,30 @@ packages: /@types/babel__core/7.1.19: resolution: {integrity: sha512-WEOTgRsbYkvA/KCsDwVEGkd7WAr1e3g31VHQ8zy5gul/V1qKullU/BU5I68X5v7V3GnB9eotmom4v5a5gjxorw==} dependencies: - '@babel/parser': 7.17.9 - '@babel/types': 7.17.0 + '@babel/parser': 7.17.10 + '@babel/types': 7.17.10 '@types/babel__generator': 7.6.4 '@types/babel__template': 7.4.1 - '@types/babel__traverse': 7.17.0 + '@types/babel__traverse': 7.17.1 dev: true /@types/babel__generator/7.6.4: resolution: {integrity: sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg==} dependencies: - '@babel/types': 7.17.0 + '@babel/types': 7.17.10 dev: true /@types/babel__template/7.4.1: resolution: {integrity: sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==} dependencies: - '@babel/parser': 7.17.9 - '@babel/types': 7.17.0 + '@babel/parser': 7.17.10 + '@babel/types': 7.17.10 dev: true - /@types/babel__traverse/7.17.0: - resolution: {integrity: sha512-r8aveDbd+rzGP+ykSdF3oPuTVRWRfbBiHl0rVDM2yNEmSMXfkObQLV46b4RnCv3Lra51OlfnZhkkFaDl2MIRaA==} + /@types/babel__traverse/7.17.1: + resolution: {integrity: sha512-kVzjari1s2YVi77D3w1yuvohV2idweYXMCDzqBiVNN63TcDWrIlTVOYpqVrvbbyOE/IyzBoTKF0fdnLPEORFxA==} dependencies: - '@babel/types': 7.17.0 + '@babel/types': 7.17.10 dev: true /@types/codemirror/0.0.108: @@ -1678,7 +1692,7 @@ packages: /@types/graceful-fs/4.1.5: resolution: {integrity: sha512-anKkLmZZ+xm4p8JWBf4hElkM4XR+EZeA2M9BAkkTldmcyDY4mbdIJnRghDJH3Ov5ooY7/UAoENtmdMSkaAd7Cw==} dependencies: - '@types/node': 16.11.27 + '@types/node': 16.11.34 dev: true /@types/istanbul-lib-coverage/2.0.4: @@ -1697,8 +1711,8 @@ packages: '@types/istanbul-lib-report': 3.0.0 dev: true - /@types/jest/27.4.1: - resolution: {integrity: sha512-23iPJADSmicDVrWk+HT58LMJtzLAnB2AgIzplQuq/bSrGaxCrlvRFjGbXmamnnk/mAmCdLStiGqggu28ocUyiw==} + /@types/jest/27.5.0: + resolution: {integrity: sha512-9RBFx7r4k+msyj/arpfaa0WOOEcaAZNmN+j80KFbFCoSqCJGHTz7YMAMGQW9Xmqm5w6l5c25vbSjMwlikJi5+g==} dependencies: jest-matcher-utils: 27.5.1 pretty-format: 27.5.1 @@ -1712,8 +1726,8 @@ packages: resolution: {integrity: sha1-7ihweulOEdK4J7y+UnC86n8+ce4=} dev: true - /@types/node/16.11.27: - resolution: {integrity: sha512-C1pD3kgLoZ56Uuy5lhfOxie4aZlA3UMGLX9rXteq4WitEZH6Rl80mwactt9QG0w0gLFlN/kLBTFnGXtDVWvWQw==} + /@types/node/16.11.34: + resolution: {integrity: sha512-UrWGDyLAlQ2Z8bNOGWTsqbP9ZcBeTYBVuTRNxXTztBy5KhWUFI3BaeDWoCP/CzV/EVGgO1NTYzv9ZytBI9GAEw==} dev: true /@types/prettier/2.6.0: @@ -1727,7 +1741,7 @@ packages: /@types/sass/1.43.1: resolution: {integrity: sha512-BPdoIt1lfJ6B7rw35ncdwBZrAssjcwzI5LByIrYs+tpXlj/CAkuVdRsgZDdP4lq5EjyWzwxZCqAoFyHKFwp32g==} dependencies: - '@types/node': 16.11.27 + '@types/node': 16.11.34 dev: true /@types/stack-utils/2.0.1: @@ -1743,7 +1757,7 @@ packages: /@types/testing-library__jest-dom/5.14.3: resolution: {integrity: sha512-oKZe+Mf4ioWlMuzVBaXQ9WDnEm1+umLx0InILg+yvZVBBDmzV5KfZyLrCvadtWcx8+916jLmHafcmqqffl+iIw==} dependencies: - '@types/jest': 27.4.1 + '@types/jest': 27.5.0 dev: true /@types/yargs-parser/21.0.0: @@ -1756,8 +1770,8 @@ packages: '@types/yargs-parser': 21.0.0 dev: true - /@typescript-eslint/eslint-plugin/5.20.0_4fbab8ed861393b891eae3bdfcc6594e: - resolution: {integrity: sha512-fapGzoxilCn3sBtC6NtXZX6+P/Hef7VDbyfGqTTpzYydwhlkevB+0vE0EnmHPVTVSy68GUncyJ/2PcrFBeCo5Q==} + /@typescript-eslint/eslint-plugin/5.23.0_8d5dd86e19d360799b5f51045840aced: + resolution: {integrity: sha512-hEcSmG4XodSLiAp1uxv/OQSGsDY6QN3TcRU32gANp+19wGE1QQZLRS8/GV58VRUoXhnkuJ3ZxNQ3T6Z6zM59DA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: '@typescript-eslint/parser': ^5.0.0 @@ -1767,18 +1781,18 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/parser': 5.20.0_eslint@7.12.1+typescript@4.6.3 - '@typescript-eslint/scope-manager': 5.20.0 - '@typescript-eslint/type-utils': 5.20.0_eslint@7.12.1+typescript@4.6.3 - '@typescript-eslint/utils': 5.20.0_eslint@7.12.1+typescript@4.6.3 + '@typescript-eslint/parser': 5.23.0_eslint@7.12.1+typescript@4.6.4 + '@typescript-eslint/scope-manager': 5.23.0 + '@typescript-eslint/type-utils': 5.23.0_eslint@7.12.1+typescript@4.6.4 + '@typescript-eslint/utils': 5.23.0_eslint@7.12.1+typescript@4.6.4 debug: 4.3.4 eslint: 7.12.1 functional-red-black-tree: 1.0.1 ignore: 5.2.0 regexpp: 3.2.0 semver: 7.3.7 - tsutils: 3.21.0_typescript@4.6.3 - typescript: 4.6.3 + tsutils: 3.21.0_typescript@4.6.4 + typescript: 4.6.4 transitivePeerDependencies: - supports-color dev: true @@ -1821,8 +1835,8 @@ packages: - supports-color dev: true - /@typescript-eslint/parser/5.20.0_eslint@7.12.1+typescript@4.6.3: - resolution: {integrity: sha512-UWKibrCZQCYvobmu3/N8TWbEeo/EPQbS41Ux1F9XqPzGuV7pfg6n50ZrFo6hryynD8qOTTfLHtHjjdQtxJ0h/w==} + /@typescript-eslint/parser/5.23.0_eslint@7.12.1+typescript@4.6.4: + resolution: {integrity: sha512-V06cYUkqcGqpFjb8ttVgzNF53tgbB/KoQT/iB++DOIExKmzI9vBJKjZKt/6FuV9c+zrDsvJKbJ2DOCYwX91cbw==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 @@ -1831,26 +1845,26 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/scope-manager': 5.20.0 - '@typescript-eslint/types': 5.20.0 - '@typescript-eslint/typescript-estree': 5.20.0_typescript@4.6.3 + '@typescript-eslint/scope-manager': 5.23.0 + '@typescript-eslint/types': 5.23.0 + '@typescript-eslint/typescript-estree': 5.23.0_typescript@4.6.4 debug: 4.3.4 eslint: 7.12.1 - typescript: 4.6.3 + typescript: 4.6.4 transitivePeerDependencies: - supports-color dev: true - /@typescript-eslint/scope-manager/5.20.0: - resolution: {integrity: sha512-h9KtuPZ4D/JuX7rpp1iKg3zOH0WNEa+ZIXwpW/KWmEFDxlA/HSfCMhiyF1HS/drTICjIbpA6OqkAhrP/zkCStg==} + /@typescript-eslint/scope-manager/5.23.0: + resolution: {integrity: sha512-EhjaFELQHCRb5wTwlGsNMvzK9b8Oco4aYNleeDlNuL6qXWDF47ch4EhVNPh8Rdhf9tmqbN4sWDk/8g+Z/J8JVw==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dependencies: - '@typescript-eslint/types': 5.20.0 - '@typescript-eslint/visitor-keys': 5.20.0 + '@typescript-eslint/types': 5.23.0 + '@typescript-eslint/visitor-keys': 5.23.0 dev: true - /@typescript-eslint/type-utils/5.20.0_eslint@7.12.1+typescript@4.6.3: - resolution: {integrity: sha512-WxNrCwYB3N/m8ceyoGCgbLmuZwupvzN0rE8NBuwnl7APgjv24ZJIjkNzoFBXPRCGzLNkoU/WfanW0exvp/+3Iw==} + /@typescript-eslint/type-utils/5.23.0_eslint@7.12.1+typescript@4.6.4: + resolution: {integrity: sha512-iuI05JsJl/SUnOTXA9f4oI+/4qS/Zcgk+s2ir+lRmXI+80D8GaGwoUqs4p+X+4AxDolPpEpVUdlEH4ADxFy4gw==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: '*' @@ -1859,11 +1873,11 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/utils': 5.20.0_eslint@7.12.1+typescript@4.6.3 + '@typescript-eslint/utils': 5.23.0_eslint@7.12.1+typescript@4.6.4 debug: 4.3.4 eslint: 7.12.1 - tsutils: 3.21.0_typescript@4.6.3 - typescript: 4.6.3 + tsutils: 3.21.0_typescript@4.6.4 + typescript: 4.6.4 transitivePeerDependencies: - supports-color dev: true @@ -1873,8 +1887,8 @@ packages: engines: {node: ^8.10.0 || ^10.13.0 || >=11.10.1} dev: true - /@typescript-eslint/types/5.20.0: - resolution: {integrity: sha512-+d8wprF9GyvPwtoB4CxBAR/s0rpP25XKgnOvMf/gMXYDvlUC3rPFHupdTQ/ow9vn7UDe5rX02ovGYQbv/IUCbg==} + /@typescript-eslint/types/5.23.0: + resolution: {integrity: sha512-NfBsV/h4dir/8mJwdZz7JFibaKC3E/QdeMEDJhiAE3/eMkoniZ7MjbEMCGXw6MZnZDMN3G9S0mH/6WUIj91dmw==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dev: true @@ -1900,8 +1914,8 @@ packages: - supports-color dev: true - /@typescript-eslint/typescript-estree/5.20.0_typescript@4.6.3: - resolution: {integrity: sha512-36xLjP/+bXusLMrT9fMMYy1KJAGgHhlER2TqpUVDYUQg4w0q/NW/sg4UGAgVwAqb8V4zYg43KMUpM8vV2lve6w==} + /@typescript-eslint/typescript-estree/5.23.0_typescript@4.6.4: + resolution: {integrity: sha512-xE9e0lrHhI647SlGMl+m+3E3CKPF1wzvvOEWnuE3CCjjT7UiRnDGJxmAcVKJIlFgK6DY9RB98eLr1OPigPEOGg==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: typescript: '*' @@ -1909,28 +1923,28 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/types': 5.20.0 - '@typescript-eslint/visitor-keys': 5.20.0 + '@typescript-eslint/types': 5.23.0 + '@typescript-eslint/visitor-keys': 5.23.0 debug: 4.3.4 globby: 11.1.0 is-glob: 4.0.3 semver: 7.3.7 - tsutils: 3.21.0_typescript@4.6.3 - typescript: 4.6.3 + tsutils: 3.21.0_typescript@4.6.4 + typescript: 4.6.4 transitivePeerDependencies: - supports-color dev: true - /@typescript-eslint/utils/5.20.0_eslint@7.12.1+typescript@4.6.3: - resolution: {integrity: sha512-lHONGJL1LIO12Ujyx8L8xKbwWSkoUKFSO+0wDAqGXiudWB2EO7WEUT+YZLtVbmOmSllAjLb9tpoIPwpRe5Tn6w==} + /@typescript-eslint/utils/5.23.0_eslint@7.12.1+typescript@4.6.4: + resolution: {integrity: sha512-dbgaKN21drqpkbbedGMNPCtRPZo1IOUr5EI9Jrrh99r5UW5Q0dz46RKXeSBoPV+56R6dFKpbrdhgUNSJsDDRZA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 dependencies: '@types/json-schema': 7.0.11 - '@typescript-eslint/scope-manager': 5.20.0 - '@typescript-eslint/types': 5.20.0 - '@typescript-eslint/typescript-estree': 5.20.0_typescript@4.6.3 + '@typescript-eslint/scope-manager': 5.23.0 + '@typescript-eslint/types': 5.23.0 + '@typescript-eslint/typescript-estree': 5.23.0_typescript@4.6.4 eslint: 7.12.1 eslint-scope: 5.1.1 eslint-utils: 3.0.0_eslint@7.12.1 @@ -1946,11 +1960,11 @@ packages: eslint-visitor-keys: 1.3.0 dev: true - /@typescript-eslint/visitor-keys/5.20.0: - resolution: {integrity: sha512-1flRpNF+0CAQkMNlTJ6L/Z5jiODG/e5+7mk6XwtPOUS3UrTz3UOiAg9jG2VtKsWI6rZQfy4C6a232QNRZTRGlg==} + /@typescript-eslint/visitor-keys/5.23.0: + resolution: {integrity: sha512-Vd4mFNchU62sJB8pX19ZSPog05B0Y0CE2UxAZPT5k4iqhRYjPnqyY3woMxCd0++t9OTqkgjST+1ydLBi7e2Fvg==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dependencies: - '@typescript-eslint/types': 5.20.0 + '@typescript-eslint/types': 5.23.0 eslint-visitor-keys: 3.3.0 dev: true @@ -1984,8 +1998,8 @@ packages: hasBin: true dev: true - /acorn/8.7.0: - resolution: {integrity: sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==} + /acorn/8.7.1: + resolution: {integrity: sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A==} engines: {node: '>=0.4.0'} hasBin: true dev: true @@ -2083,13 +2097,13 @@ packages: engines: {node: '>=6.0'} dev: true - /array-includes/3.1.4: - resolution: {integrity: sha512-ZTNSQkmWumEbiHO2GF4GmWxYVTiQyJy2XOTa15sdQSrvKn7l+180egQMqlrMOUMCyLMD7pmyQe4mMDUT6Behrw==} + /array-includes/3.1.5: + resolution: {integrity: sha512-iSDYZMMyTPkiFasVqfuAQnWAYcvO/SeBSCGKePoEthjp4LEMTe4uLc7b025o4jAZpHhihh8xPo99TNWUWWkGDQ==} engines: {node: '>= 0.4'} dependencies: call-bind: 1.0.2 define-properties: 1.1.4 - es-abstract: 1.19.5 + es-abstract: 1.20.0 get-intrinsic: 1.1.1 is-string: 1.0.7 dev: true @@ -2105,7 +2119,7 @@ packages: dependencies: call-bind: 1.0.2 define-properties: 1.1.4 - es-abstract: 1.19.5 + es-abstract: 1.20.0 es-shim-unscopables: 1.0.0 dev: true @@ -2142,18 +2156,18 @@ packages: - supports-color dev: true - /babel-jest/27.5.1_@babel+core@7.17.9: + /babel-jest/27.5.1_@babel+core@7.17.10: resolution: {integrity: sha512-cdQ5dXjGRd0IBRATiQ4mZGlGlRE8kJpjPOixdNRdT+m3UcNqmYWN6rK6nvtXYfY3D76cb8s/O1Ss8ea24PIwcg==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} peerDependencies: '@babel/core': ^7.8.0 dependencies: - '@babel/core': 7.17.9 + '@babel/core': 7.17.10 '@jest/transform': 27.5.1 '@jest/types': 27.5.1 '@types/babel__core': 7.1.19 babel-plugin-istanbul: 6.1.1 - babel-preset-jest: 27.5.1_@babel+core@7.17.9 + babel-preset-jest: 27.5.1_@babel+core@7.17.10 chalk: 4.1.2 graceful-fs: 4.2.10 slash: 3.0.0 @@ -2185,9 +2199,9 @@ packages: engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} dependencies: '@babel/template': 7.16.7 - '@babel/types': 7.17.0 + '@babel/types': 7.17.10 '@types/babel__core': 7.1.19 - '@types/babel__traverse': 7.17.0 + '@types/babel__traverse': 7.17.1 dev: true /babel-plugin-polyfill-corejs2/0.3.1: @@ -2195,7 +2209,7 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/compat-data': 7.17.7 + '@babel/compat-data': 7.17.10 '@babel/helper-define-polyfill-provider': 0.3.1 semver: 6.3.0 transitivePeerDependencies: @@ -2208,7 +2222,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/helper-define-polyfill-provider': 0.3.1 - core-js-compat: 3.22.2 + core-js-compat: 3.22.5 transitivePeerDependencies: - supports-color dev: true @@ -2242,24 +2256,24 @@ packages: '@babel/plugin-syntax-top-level-await': 7.14.5 dev: true - /babel-preset-current-node-syntax/1.0.1_@babel+core@7.17.9: + /babel-preset-current-node-syntax/1.0.1_@babel+core@7.17.10: resolution: {integrity: sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==} peerDependencies: '@babel/core': ^7.0.0 dependencies: - '@babel/core': 7.17.9 - '@babel/plugin-syntax-async-generators': 7.8.4_@babel+core@7.17.9 - '@babel/plugin-syntax-bigint': 7.8.3_@babel+core@7.17.9 - '@babel/plugin-syntax-class-properties': 7.12.13_@babel+core@7.17.9 - '@babel/plugin-syntax-import-meta': 7.10.4_@babel+core@7.17.9 - '@babel/plugin-syntax-json-strings': 7.8.3_@babel+core@7.17.9 - '@babel/plugin-syntax-logical-assignment-operators': 7.10.4_@babel+core@7.17.9 - '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3_@babel+core@7.17.9 - '@babel/plugin-syntax-numeric-separator': 7.10.4_@babel+core@7.17.9 - '@babel/plugin-syntax-object-rest-spread': 7.8.3_@babel+core@7.17.9 - '@babel/plugin-syntax-optional-catch-binding': 7.8.3_@babel+core@7.17.9 - '@babel/plugin-syntax-optional-chaining': 7.8.3_@babel+core@7.17.9 - '@babel/plugin-syntax-top-level-await': 7.14.5_@babel+core@7.17.9 + '@babel/core': 7.17.10 + '@babel/plugin-syntax-async-generators': 7.8.4_@babel+core@7.17.10 + '@babel/plugin-syntax-bigint': 7.8.3_@babel+core@7.17.10 + '@babel/plugin-syntax-class-properties': 7.12.13_@babel+core@7.17.10 + '@babel/plugin-syntax-import-meta': 7.10.4_@babel+core@7.17.10 + '@babel/plugin-syntax-json-strings': 7.8.3_@babel+core@7.17.10 + '@babel/plugin-syntax-logical-assignment-operators': 7.10.4_@babel+core@7.17.10 + '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3_@babel+core@7.17.10 + '@babel/plugin-syntax-numeric-separator': 7.10.4_@babel+core@7.17.10 + '@babel/plugin-syntax-object-rest-spread': 7.8.3_@babel+core@7.17.10 + '@babel/plugin-syntax-optional-catch-binding': 7.8.3_@babel+core@7.17.10 + '@babel/plugin-syntax-optional-chaining': 7.8.3_@babel+core@7.17.10 + '@babel/plugin-syntax-top-level-await': 7.14.5_@babel+core@7.17.10 dev: true /babel-preset-jest/27.5.1: @@ -2272,15 +2286,15 @@ packages: babel-preset-current-node-syntax: 1.0.1 dev: true - /babel-preset-jest/27.5.1_@babel+core@7.17.9: + /babel-preset-jest/27.5.1_@babel+core@7.17.10: resolution: {integrity: sha512-Nptf2FzlPCWYuJg41HBqXVT8ym6bXOevuCTbhxlUpjwtysGaIWFvDEjp4y+G7fl13FgOdjs7P/DmErqH7da0Ag==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} peerDependencies: '@babel/core': ^7.0.0 dependencies: - '@babel/core': 7.17.9 + '@babel/core': 7.17.10 babel-plugin-jest-hoist: 27.5.1 - babel-preset-current-node-syntax: 1.0.1_@babel+core@7.17.9 + babel-preset-current-node-syntax: 1.0.1_@babel+core@7.17.10 dev: true /balanced-match/1.0.2: @@ -2305,15 +2319,15 @@ packages: resolution: {integrity: sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==} dev: true - /browserslist/4.20.2: - resolution: {integrity: sha512-CQOBCqp/9pDvDbx3xfMi+86pr4KXIf2FDkTTdeuYw8OxS9t898LA1Khq57gtufFILXpfgsSx5woNgsBgvGjpsA==} + /browserslist/4.20.3: + resolution: {integrity: sha512-NBhymBQl1zM0Y5dQT/O+xiLP9/rzOIQdKM/eMJBAq7yBgaB6krIYLGejrwVYnSHZdqjscB1SPuAjHwxjvN6Wdg==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true dependencies: - caniuse-lite: 1.0.30001332 - electron-to-chromium: 1.4.118 + caniuse-lite: 1.0.30001339 + electron-to-chromium: 1.4.137 escalade: 3.1.1 - node-releases: 2.0.3 + node-releases: 2.0.4 picocolors: 1.0.0 dev: true @@ -2358,8 +2372,8 @@ packages: engines: {node: '>=10'} dev: true - /caniuse-lite/1.0.30001332: - resolution: {integrity: sha512-10T30NYOEQtN6C11YGg411yebhvpnC6Z102+B95eAsN0oB6KUs01ivE8u+G6FMIRtIrVlYXhL+LUwQ3/hXwDWw==} + /caniuse-lite/1.0.30001339: + resolution: {integrity: sha512-Es8PiVqCe+uXdms0Gu5xP5PF2bxLR7OBp3wUzUnuO7OHzhOfCyg3hdiGWVPVxhiuniOzng+hTc1u3fEQ0TlkSQ==} dev: true /chalk/1.1.3: @@ -2476,10 +2490,10 @@ packages: safe-buffer: 5.1.2 dev: true - /core-js-compat/3.22.2: - resolution: {integrity: sha512-Fns9lU06ZJ07pdfmPMu7OnkIKGPKDzXKIiuGlSvHHapwqMUF2QnnsWwtueFZtSyZEilP0o6iUeHQwpn7LxtLUw==} + /core-js-compat/3.22.5: + resolution: {integrity: sha512-rEF75n3QtInrYICvJjrAgV03HwKiYvtKHdPtaba1KucG+cNZ4NJnH9isqt979e67KZlhpbCOTwnsvnIr+CVeOg==} dependencies: - browserslist: 4.20.2 + browserslist: 4.20.3 semver: 7.0.0 dev: true @@ -2628,8 +2642,8 @@ packages: esutils: 2.0.3 dev: true - /dom-accessibility-api/0.5.13: - resolution: {integrity: sha512-R305kwb5CcMDIpSHUnLyIAp7SrSPBx6F0VfQFB3M75xVMHhXJJIdePYgbPPh1o57vCHNu5QztokWUPsLjWzFqw==} + /dom-accessibility-api/0.5.14: + resolution: {integrity: sha512-NMt+m9zFMPZe0JcY9gN224Qvk6qLIdqex29clBvc/y75ZBX9YA9wNK3frsYvu2DI1xcCIwxwnX+TlsJ2DSOADg==} dev: true /domexception/2.0.1: @@ -2639,8 +2653,8 @@ packages: webidl-conversions: 5.0.0 dev: true - /electron-to-chromium/1.4.118: - resolution: {integrity: sha512-maZIKjnYDvF7Fs35nvVcyr44UcKNwybr93Oba2n3HkKDFAtk0svERkLN/HyczJDS3Fo4wU9th9fUQd09ZLtj1w==} + /electron-to-chromium/1.4.137: + resolution: {integrity: sha512-0Rcpald12O11BUogJagX3HsCN3FE83DSqWjgXoHo5a72KUKMSfI39XBgJpgNNxS9fuGzytaFjE06kZkiVFy2qA==} dev: true /emittery/0.8.1: @@ -2669,16 +2683,18 @@ packages: is-arrayish: 0.2.1 dev: true - /es-abstract/1.19.5: - resolution: {integrity: sha512-Aa2G2+Rd3b6kxEUKTF4TaW67czBLyAv3z7VOhYRU50YBx+bbsYZ9xQP4lMNazePuFlybXI0V4MruPos7qUo5fA==} + /es-abstract/1.20.0: + resolution: {integrity: sha512-URbD8tgRthKD3YcC39vbvSDrX23upXnPcnGAjQfgxXF5ID75YcENawc9ZX/9iTP9ptUyfCLIxTTuMYoRfiOVKA==} engines: {node: '>= 0.4'} dependencies: call-bind: 1.0.2 es-to-primitive: 1.2.1 function-bind: 1.1.1 + function.prototype.name: 1.1.5 get-intrinsic: 1.1.1 get-symbol-description: 1.0.0 has: 1.0.3 + has-property-descriptors: 1.0.0 has-symbols: 1.0.3 internal-slot: 1.0.3 is-callable: 1.2.4 @@ -2690,9 +2706,10 @@ packages: object-inspect: 1.12.0 object-keys: 1.1.1 object.assign: 4.1.2 - string.prototype.trimend: 1.0.4 - string.prototype.trimstart: 1.0.4 - unbox-primitive: 1.0.1 + regexp.prototype.flags: 1.4.3 + string.prototype.trimend: 1.0.5 + string.prototype.trimstart: 1.0.5 + unbox-primitive: 1.0.2 dev: true /es-shim-unscopables/1.0.0: @@ -2837,7 +2854,7 @@ packages: dev: true optional: true - /esbuild-svelte/0.7.0_esbuild@0.13.12+svelte@3.47.0: + /esbuild-svelte/0.7.0_esbuild@0.13.12+svelte@3.48.0: resolution: {integrity: sha512-hfiauhEXtGocUf7oVcxTrLhhF57ajBbvNCCClsS3KntEeITddKU+1WFhmsCt9SO0dQJlCFzJtpPu2dI7dRkXBw==} engines: {node: '>=12'} peerDependencies: @@ -2845,7 +2862,7 @@ packages: svelte: '>=3.43.0' dependencies: esbuild: 0.13.12 - svelte: 3.47.0 + svelte: 3.48.0 dev: true /esbuild-windows-32/0.13.12: @@ -2970,7 +2987,7 @@ packages: peerDependencies: eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 dependencies: - array-includes: 3.1.4 + array-includes: 3.1.5 array.prototype.flat: 1.3.0 contains-path: 0.1.0 debug: 2.6.9 @@ -3010,7 +3027,7 @@ packages: eslint: 7.12.1 dev: true - /eslint-plugin-svelte3/3.4.1_eslint@7.12.1+svelte@3.47.0: + /eslint-plugin-svelte3/3.4.1_eslint@7.12.1+svelte@3.48.0: resolution: {integrity: sha512-7p59WG8qV8L6wLdl4d/c3mdjkgVglQCdv5XOTk/iNPBKXuuV+Q0eFP5Wa6iJd/G2M1qR3BkLPEzaANOqKAZczw==} engines: {node: '>=10'} peerDependencies: @@ -3018,7 +3035,7 @@ packages: svelte: ^3.2.0 dependencies: eslint: 7.12.1 - svelte: 3.47.0 + svelte: 3.48.0 dev: true /eslint-scope/5.1.1: @@ -3301,10 +3318,24 @@ packages: resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==} dev: true + /function.prototype.name/1.1.5: + resolution: {integrity: sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + define-properties: 1.1.4 + es-abstract: 1.20.0 + functions-have-names: 1.2.3 + dev: true + /functional-red-black-tree/1.0.1: resolution: {integrity: sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=} dev: true + /functions-have-names/1.2.3: + resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} + dev: true + /gensync/1.0.0-beta.2: resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} engines: {node: '>=6.9.0'} @@ -3682,8 +3713,8 @@ packages: resolution: {integrity: sha512-6Lthe1hqXHBNsqvgDzGO6l03XNeu3CrG4RqQ1KM9+l5+jNGpEJfIELx1NS3SEHmJQA8np/u+E4EPRKRiu6m19A==} engines: {node: '>=8'} dependencies: - '@babel/core': 7.17.9 - '@babel/parser': 7.17.9 + '@babel/core': 7.17.10 + '@babel/parser': 7.17.10 '@istanbuljs/schema': 0.1.3 istanbul-lib-coverage: 3.2.0 semver: 6.3.0 @@ -3735,7 +3766,7 @@ packages: '@jest/environment': 27.5.1 '@jest/test-result': 27.5.1 '@jest/types': 27.5.1 - '@types/node': 16.11.27 + '@types/node': 16.11.34 chalk: 4.1.2 co: 4.6.0 dedent: 0.7.0 @@ -3794,10 +3825,10 @@ packages: ts-node: optional: true dependencies: - '@babel/core': 7.17.9 + '@babel/core': 7.17.10 '@jest/test-sequencer': 27.5.1 '@jest/types': 27.5.1 - babel-jest: 27.5.1_@babel+core@7.17.9 + babel-jest: 27.5.1_@babel+core@7.17.10 chalk: 4.1.2 ci-info: 3.3.0 deepmerge: 4.2.2 @@ -3860,7 +3891,7 @@ packages: '@jest/environment': 27.5.1 '@jest/fake-timers': 27.5.1 '@jest/types': 27.5.1 - '@types/node': 16.11.27 + '@types/node': 16.11.34 jest-mock: 27.5.1 jest-util: 27.5.1 jsdom: 16.7.0 @@ -3878,7 +3909,7 @@ packages: '@jest/environment': 27.5.1 '@jest/fake-timers': 27.5.1 '@jest/types': 27.5.1 - '@types/node': 16.11.27 + '@types/node': 16.11.34 jest-mock: 27.5.1 jest-util: 27.5.1 dev: true @@ -3894,7 +3925,7 @@ packages: dependencies: '@jest/types': 27.5.1 '@types/graceful-fs': 4.1.5 - '@types/node': 16.11.27 + '@types/node': 16.11.34 anymatch: 3.1.2 fb-watchman: 2.0.1 graceful-fs: 4.2.10 @@ -3916,7 +3947,7 @@ packages: '@jest/source-map': 27.5.1 '@jest/test-result': 27.5.1 '@jest/types': 27.5.1 - '@types/node': 16.11.27 + '@types/node': 16.11.34 chalk: 4.1.2 co: 4.6.0 expect: 27.5.1 @@ -3971,7 +4002,7 @@ packages: engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} dependencies: '@jest/types': 27.5.1 - '@types/node': 16.11.27 + '@types/node': 16.11.34 dev: true /jest-pnp-resolver/1.2.2_jest-resolve@27.5.1: @@ -4027,7 +4058,7 @@ packages: '@jest/test-result': 27.5.1 '@jest/transform': 27.5.1 '@jest/types': 27.5.1 - '@types/node': 16.11.27 + '@types/node': 16.11.34 chalk: 4.1.2 emittery: 0.8.1 graceful-fs: 4.2.10 @@ -4084,7 +4115,7 @@ packages: resolution: {integrity: sha512-jZCyo6iIxO1aqUxpuBlwTDMkzOAJS4a3eYz3YzgxxVQFwLeSA7Jfq5cbqCY+JLvTDrWirgusI/0KwxKMgrdf7w==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} dependencies: - '@types/node': 16.11.27 + '@types/node': 16.11.34 graceful-fs: 4.2.10 dev: true @@ -4092,16 +4123,16 @@ packages: resolution: {integrity: sha512-yYykXI5a0I31xX67mgeLw1DZ0bJB+gpq5IpSuCAoyDi0+BhgU/RIrL+RTzDmkNTchvDFWKP8lp+w/42Z3us5sA==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} dependencies: - '@babel/core': 7.17.9 - '@babel/generator': 7.17.9 - '@babel/plugin-syntax-typescript': 7.16.7_@babel+core@7.17.9 - '@babel/traverse': 7.17.9 - '@babel/types': 7.17.0 + '@babel/core': 7.17.10 + '@babel/generator': 7.17.10 + '@babel/plugin-syntax-typescript': 7.17.10_@babel+core@7.17.10 + '@babel/traverse': 7.17.10 + '@babel/types': 7.17.10 '@jest/transform': 27.5.1 '@jest/types': 27.5.1 - '@types/babel__traverse': 7.17.0 + '@types/babel__traverse': 7.17.1 '@types/prettier': 2.6.0 - babel-preset-current-node-syntax: 1.0.1_@babel+core@7.17.9 + babel-preset-current-node-syntax: 1.0.1_@babel+core@7.17.10 chalk: 4.1.2 expect: 27.5.1 graceful-fs: 4.2.10 @@ -4123,7 +4154,7 @@ packages: engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} dependencies: '@jest/types': 27.5.1 - '@types/node': 16.11.27 + '@types/node': 16.11.34 chalk: 4.1.2 ci-info: 3.3.0 graceful-fs: 4.2.10 @@ -4148,7 +4179,7 @@ packages: dependencies: '@jest/test-result': 27.5.1 '@jest/types': 27.5.1 - '@types/node': 16.11.27 + '@types/node': 16.11.34 ansi-escapes: 4.3.2 chalk: 4.1.2 jest-util: 27.5.1 @@ -4159,7 +4190,7 @@ packages: resolution: {integrity: sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==} engines: {node: '>= 10.13.0'} dependencies: - '@types/node': 16.11.27 + '@types/node': 16.11.34 merge-stream: 2.0.0 supports-color: 8.1.1 dev: true @@ -4207,7 +4238,7 @@ packages: optional: true dependencies: abab: 2.0.6 - acorn: 8.7.0 + acorn: 8.7.1 acorn-globals: 6.0.0 cssom: 0.4.4 cssstyle: 2.3.0 @@ -4472,8 +4503,8 @@ packages: resolution: {integrity: sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs=} dev: true - /node-releases/2.0.3: - resolution: {integrity: sha512-maHFz6OLqYxz+VQyCAtA3PTX4UP/53pa05fyDNc9CwjvJ0yEh6+xBwKsgCxMNhS8taUKBFYxfuiaD9U/55iFaw==} + /node-releases/2.0.4: + resolution: {integrity: sha512-gbMzqQtTtDz/00jQzZ21PQzdI9PyLYqUSvD0p3naOhX4odFji0ZxYdnVwPTxmSwkmxhcFImpozceidSG+AgoPQ==} dev: true /normalize-package-data/2.5.0: @@ -4526,11 +4557,11 @@ packages: dependencies: call-bind: 1.0.2 define-properties: 1.1.4 - es-abstract: 1.19.5 + es-abstract: 1.20.0 dev: true - /obsidian/0.14.6: - resolution: {integrity: sha512-oXPJ8Zt10WhN19bk5l4mZuXRZbbdT1QoMgxGGJ0bB7UcJa0bozDzugS5L/QiV9gDoujpUPxDWNVahEel6r0Fpw==} + /obsidian/0.14.8: + resolution: {integrity: sha512-CQz+B2HSbhGVEBwZBL3rPl29ruOBmEhCbBmW7PIILnnRh6fFFvYy3kZLHVTUidzvRGZnEW/mQ7n9LXeJCp2a/Q==} dependencies: '@codemirror/state': 0.19.9 '@codemirror/view': 0.19.48 @@ -4831,6 +4862,15 @@ packages: '@babel/runtime': 7.17.9 dev: true + /regexp.prototype.flags/1.4.3: + resolution: {integrity: sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + define-properties: 1.1.4 + functions-have-names: 1.2.3 + dev: true + /regexpp/3.2.0: resolution: {integrity: sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==} engines: {node: '>=8'} @@ -5045,11 +5085,6 @@ packages: source-map: 0.6.1 dev: true - /source-map/0.5.7: - resolution: {integrity: sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=} - engines: {node: '>=0.10.0'} - dev: true - /source-map/0.6.1: resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} engines: {node: '>=0.10.0'} @@ -5123,18 +5158,20 @@ packages: strip-ansi: 6.0.1 dev: true - /string.prototype.trimend/1.0.4: - resolution: {integrity: sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==} + /string.prototype.trimend/1.0.5: + resolution: {integrity: sha512-I7RGvmjV4pJ7O3kdf+LXFpVfdNOxtCW/2C8f6jNiW4+PQchwxkCDzlk1/7p+Wl4bqFIZeF47qAHXLuHHWKAxog==} dependencies: call-bind: 1.0.2 define-properties: 1.1.4 + es-abstract: 1.20.0 dev: true - /string.prototype.trimstart/1.0.4: - resolution: {integrity: sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==} + /string.prototype.trimstart/1.0.5: + resolution: {integrity: sha512-THx16TJCGlsN0o6dl2o6ncWUsdgnLRSA23rRE5pyGBw/mLr3Ej/R2LaqCtgP8VNMGZsvMWnf9ooZPyY2bHvUFg==} dependencies: call-bind: 1.0.2 define-properties: 1.1.4 + es-abstract: 1.20.0 dev: true /strip-ansi/3.0.1: @@ -5228,7 +5265,7 @@ packages: engines: {node: '>= 0.4'} dev: true - /svelte-jester/2.3.2_jest@27.5.1+svelte@3.47.0: + /svelte-jester/2.3.2_jest@27.5.1+svelte@3.48.0: resolution: {integrity: sha512-JtxSz4FWAaCRBXbPsh4LcDs4Ua7zdXgLC0TZvT1R56hRV0dymmNP+abw67DTPF7sQPyNxWsOKd0Sl7Q8SnP8kg==} engines: {node: '>=14'} peerDependencies: @@ -5236,10 +5273,10 @@ packages: svelte: '>= 3' dependencies: jest: 27.5.1 - svelte: 3.47.0 + svelte: 3.48.0 dev: true - /svelte-preprocess/4.10.6_svelte@3.47.0+typescript@4.6.3: + /svelte-preprocess/4.10.6_svelte@3.48.0+typescript@4.6.4: resolution: {integrity: sha512-I2SV1w/AveMvgIQlUF/ZOO3PYVnhxfcpNyGt8pxpUVhPfyfL/CZBkkw/KPfuFix5FJ9TnnNYMhACK3DtSaYVVQ==} engines: {node: '>= 9.11.2'} requiresBuild: true @@ -5286,12 +5323,12 @@ packages: magic-string: 0.25.9 sorcery: 0.10.0 strip-indent: 3.0.0 - svelte: 3.47.0 - typescript: 4.6.3 + svelte: 3.48.0 + typescript: 4.6.4 dev: true - /svelte/3.47.0: - resolution: {integrity: sha512-4JaJp3HEoTCGARRWZQIZDUanhYv0iyoHikklVHVLH9xFE9db22g4TDv7CPeNA8HD1JgjXI1vlhR1JZvvhaTu2Q==} + /svelte/3.48.0: + resolution: {integrity: sha512-fN2YRm/bGumvjUpu6yI3BpvZnpIm9I6A7HR4oUNYd7ggYyIwSA/BX7DJ+UXXffLp6XNcUijyLvttbPVCYa/3xQ==} engines: {node: '>= 8'} dev: true @@ -5393,14 +5430,14 @@ packages: typescript: 3.9.10 dev: true - /tsutils/3.21.0_typescript@4.6.3: + /tsutils/3.21.0_typescript@4.6.4: resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==} engines: {node: '>= 6'} peerDependencies: typescript: '>=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta' dependencies: tslib: 1.14.1 - typescript: 4.6.3 + typescript: 4.6.4 dev: true /type-check/0.3.2: @@ -5444,16 +5481,16 @@ packages: hasBin: true dev: true - /typescript/4.6.3: - resolution: {integrity: sha512-yNIatDa5iaofVozS/uQJEl3JRWLKKGJKh6Yaiv0GLGSuhpFJe7P3SbHZ8/yjAHRQwKRoA6YZqlfjXWmVzoVSMw==} + /typescript/4.6.4: + resolution: {integrity: sha512-9ia/jWHIEbo49HfjrLGfKbZSuWo9iTMwXO+Ca3pRsSpbsMbc7/IU8NKdCZVRRBafVPGnoJeFL76ZOAA84I9fEg==} engines: {node: '>=4.2.0'} hasBin: true dev: true - /unbox-primitive/1.0.1: - resolution: {integrity: sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw==} + /unbox-primitive/1.0.2: + resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==} dependencies: - function-bind: 1.1.1 + call-bind: 1.0.2 has-bigints: 1.0.2 has-symbols: 1.0.3 which-boxed-primitive: 1.0.2 diff --git a/src/__tests__/query-tests.ts b/src/__tests__/query-tests.ts index fc2fdec..28f0271 100644 --- a/src/__tests__/query-tests.ts +++ b/src/__tests__/query-tests.ts @@ -40,6 +40,14 @@ describe('The Query class', () => { ).toBeTruthy() }) + it('should not exclude words when there is no space before', () => { + // Act + const query = new Query('foo bar-baz') + + // Assert + expect(query.exclusions).toHaveLength(0) + }) + describe('.getExactTerms()', () => { it('should an array of strings containg "exact" values', () => { // Act diff --git a/src/__tests__/utils-tests.ts b/src/__tests__/utils-tests.ts new file mode 100644 index 0000000..986b458 --- /dev/null +++ b/src/__tests__/utils-tests.ts @@ -0,0 +1,49 @@ +import type { CachedMetadata } from 'obsidian' +import { getAliasesFromMetadata } from '../utils' + +describe('Utils', () => { + describe('getAliasesFromMetadata', () => { + it('should return an empty array if no metadata is provided', () => { + // Act + const actual = getAliasesFromMetadata(null) + // Assert + expect(actual).toEqual([]) + }) + it('should return an empty array if no aliases are provided', () => { + // Act + const actual = getAliasesFromMetadata({}) + // Assert + expect(actual).toEqual([]) + }) + it('should return the aliases array as-is', () => { + // Arrange + const metadata = { + frontmatter: { aliases: ['foo', 'bar'] }, + } as CachedMetadata + // Act + const actual = getAliasesFromMetadata(metadata) + // Assert + expect(actual).toEqual(['foo', 'bar']) + }) + it('should convert the aliases string into an array', () => { + // Arrange + const metadata = { + frontmatter: { aliases: 'foo, bar' }, + } as CachedMetadata + // Act + const actual = getAliasesFromMetadata(metadata) + // Assert + expect(actual).toEqual(['foo', 'bar']) + }) + it('should return an empty array if the aliases field is an empty string', () => { + // Arrange + const metadata = { + frontmatter: { aliases: '' }, + } as CachedMetadata + // Act + const actual = getAliasesFromMetadata(metadata) + // Assert + expect(actual).toEqual([]) + }) + }) +}) diff --git a/src/components/GlyphAddNote.svelte b/src/components/GlyphAddNote.svelte new file mode 100644 index 0000000..22832ad --- /dev/null +++ b/src/components/GlyphAddNote.svelte @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/src/components/InputSearch.svelte b/src/components/InputSearch.svelte index 7c13437..88c7902 100644 --- a/src/components/InputSearch.svelte +++ b/src/components/InputSearch.svelte @@ -1,5 +1,6 @@
+ {#if glyph} + + {/if}
diff --git a/src/components/ResultItemVault.svelte b/src/components/ResultItemVault.svelte index 5430c13..90bbc10 100644 --- a/src/components/ResultItemVault.svelte +++ b/src/components/ResultItemVault.svelte @@ -1,4 +1,6 @@ - + - {@html note.basename.replace(reg, highlighter)} + {@html title.replace(reg, highlighter)} - - {matches.length} {matches.length > 1 ? "matches" : "match"} - + {#if matches.length > 0} + + {matches.length} {matches.length > 1 ? "matches" : "match"} + + {/if}
{@html cleanedContent.replace(reg, highlighter)}
diff --git a/src/globals.ts b/src/globals.ts index f1c65e1..772a6be 100644 --- a/src/globals.ts +++ b/src/globals.ts @@ -23,10 +23,17 @@ export type SearchNote = { export type IndexedNote = { path: string basename: string + mtime: number + content: string + aliases: string + tags: string[], headings1: string headings2: string headings3: string + + doesNotExist?: boolean + parent?: string } export type SearchMatch = { @@ -46,5 +53,13 @@ export type ResultNote = { matches: SearchMatch[] } +let inComposition = false +export function toggleInputComposition(toggle: boolean): void { + inComposition = toggle +} +export function isInputComposition(): boolean { + return inComposition +} + export const SPACE_OR_PUNCTUATION = /[|\n\r -#%-*,-/:;?@[-\]_{}\u00A0\u00A1\u00A7\u00AB\u00B6\u00B7\u00BB\u00BF\u037E\u0387\u055A-\u055F\u0589\u058A\u05BE\u05C0\u05C3\u05C6\u05F3\u05F4\u0609\u060A\u060C\u060D\u061B\u061E\u061F\u066A-\u066D\u06D4\u0700-\u070D\u07F7-\u07F9\u0830-\u083E\u085E\u0964\u0965\u0970\u09FD\u0A76\u0AF0\u0C77\u0C84\u0DF4\u0E4F\u0E5A\u0E5B\u0F04-\u0F12\u0F14\u0F3A-\u0F3D\u0F85\u0FD0-\u0FD4\u0FD9\u0FDA\u104A-\u104F\u10FB\u1360-\u1368\u1400\u166E\u1680\u169B\u169C\u16EB-\u16ED\u1735\u1736\u17D4-\u17D6\u17D8-\u17DA\u1800-\u180A\u1944\u1945\u1A1E\u1A1F\u1AA0-\u1AA6\u1AA8-\u1AAD\u1B5A-\u1B60\u1BFC-\u1BFF\u1C3B-\u1C3F\u1C7E\u1C7F\u1CC0-\u1CC7\u1CD3\u2000-\u200A\u2010-\u2029\u202F-\u2043\u2045-\u2051\u2053-\u205F\u207D\u207E\u208D\u208E\u2308-\u230B\u2329\u232A\u2768-\u2775\u27C5\u27C6\u27E6-\u27EF\u2983-\u2998\u29D8-\u29DB\u29FC\u29FD\u2CF9-\u2CFC\u2CFE\u2CFF\u2D70\u2E00-\u2E2E\u2E30-\u2E4F\u3000-\u3003\u3008-\u3011\u3014-\u301F\u3030\u303D\u30A0\u30FB\uA4FE\uA4FF\uA60D-\uA60F\uA673\uA67E\uA6F2-\uA6F7\uA874-\uA877\uA8CE\uA8CF\uA8F8-\uA8FA\uA8FC\uA92E\uA92F\uA95F\uA9C1-\uA9CD\uA9DE\uA9DF\uAA5C-\uAA5F\uAADE\uAADF\uAAF0\uAAF1\uABEB\uFD3E\uFD3F\uFE10-\uFE19\uFE30-\uFE52\uFE54-\uFE61\uFE63\uFE68\uFE6A\uFE6B\uFF01-\uFF03\uFF05-\uFF0A\uFF0C-\uFF0F\uFF1A\uFF1B\uFF1F\uFF20\uFF3B-\uFF3D\uFF3F\uFF5B\uFF5D\uFF5F-\uFF65]+/u diff --git a/src/main.ts b/src/main.ts index f5ea6a4..a0d8be8 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,14 +1,22 @@ import { Plugin, TFile } from 'obsidian' import { + addNoteToReindex, addToIndex, initGlobalSearchIndex, removeFromIndex, - removeFromIndexByPath, } from './search' import { OmnisearchInFileModal, OmnisearchVaultModal } from './modals' import { loadSettings, SettingsTab } from './settings' import { OmnisearchSuggest } from './suggestions' +// let mainWindow: { on: any; off: any } | null = null +// try { +// mainWindow = require('electron').remote.getCurrentWindow() +// } +// catch (e) { +// console.log("Can't load electron, mobile platform") +// } + export default class OmnisearchPlugin extends Plugin { async onload(): Promise { await loadSettings(this) @@ -41,19 +49,18 @@ export default class OmnisearchPlugin extends Plugin { ) this.registerEvent( this.app.vault.on('delete', file => { - removeFromIndex(file) + removeFromIndex(file.path) }), ) this.registerEvent( this.app.vault.on('modify', async file => { - removeFromIndex(file) - await addToIndex(file) + addNoteToReindex(file) }), ) this.registerEvent( this.app.vault.on('rename', async (file, oldPath) => { if (file instanceof TFile && file.path.endsWith('.md')) { - removeFromIndexByPath(oldPath) + removeFromIndex(oldPath) await addToIndex(file) } }), @@ -62,4 +69,6 @@ export default class OmnisearchPlugin extends Plugin { await initGlobalSearchIndex() }) } + + onunload(): void {} } diff --git a/src/modals.ts b/src/modals.ts index 01417b8..77cc04f 100644 --- a/src/modals.ts +++ b/src/modals.ts @@ -1,7 +1,8 @@ import { App, Modal, TFile } from 'obsidian' import ModalVault from './components/ModalVault.svelte' import ModalInFile from './components/ModalInFile.svelte' -import { eventBus } from './globals' +import { eventBus, isInputComposition } from './globals' +import { settings } from './settings' abstract class OmnisearchModal extends Modal { constructor(app: App) { @@ -17,6 +18,9 @@ abstract class OmnisearchModal extends Modal { this.modalEl.tabIndex = -1 // Setup events that can be listened through the event bus + + // #region Up/Down navigation + this.scope.register([], 'ArrowDown', e => { e.preventDefault() eventBus.emit('arrow-down') @@ -25,6 +29,39 @@ abstract class OmnisearchModal extends Modal { e.preventDefault() eventBus.emit('arrow-up') }) + + // Ctrl+j/k + for (const key of [ + { k: 'j', dir: 'down' }, + { k: 'k', dir: 'up' }, + ] as const) { + for (const modifier of ['Ctrl', 'Meta'] as const) { + this.scope.register([modifier], key.k, e => { + if (settings.CtrlJK && this.app.vault.getConfig('vimMode')) { + e.preventDefault() + eventBus.emit('arrow-' + key.dir) + } + }) + } + } + + // Ctrl+n/p + for (const key of [ + { k: 'n', dir: 'down' }, + { k: 'p', dir: 'up' }, + ] as const) { + for (const modifier of ['Ctrl', 'Meta'] as const) { + this.scope.register([modifier], key.k, e => { + if (settings.CtrlNP && this.app.vault.getConfig('vimMode')) { + e.preventDefault() + eventBus.emit('arrow-' + key.dir) + } + }) + } + } + + // #endregion Up/Down navigation + this.scope.register(['Ctrl'], 'Enter', e => { e.preventDefault() eventBus.emit('ctrl-enter') // Open in new pane @@ -33,17 +70,23 @@ abstract class OmnisearchModal extends Modal { e.preventDefault() eventBus.emit('ctrl-enter') // Open in new pane (but on Mac) }) + this.scope.register(['Alt'], 'Enter', e => { e.preventDefault() eventBus.emit('alt-enter') // Open the InFile modal }) + this.scope.register(['Shift'], 'Enter', e => { e.preventDefault() eventBus.emit('shift-enter') // Create a new note }) + this.scope.register([], 'Enter', e => { - e.preventDefault() - eventBus.emit('enter') // Open in current pane + if (!isInputComposition()) { + // Check if the user is still typing + e.preventDefault() + eventBus.emit('enter') // Open in current pane + } }) } } diff --git a/src/notes.ts b/src/notes.ts index 2020b4f..8445058 100644 --- a/src/notes.ts +++ b/src/notes.ts @@ -1,6 +1,58 @@ -import { MarkdownView } from 'obsidian' -import type { ResultNote } from './globals' +import { + MarkdownView, + TFile, + WorkspaceLeaf, + type CachedMetadata, +} from 'obsidian' +import type { IndexedNote, ResultNote } from './globals' import { stringsToRegex } from './utils' +import { settings } from './settings' + +/** + * This is an in-memory cache of the notes, with all their computed fields + * used by the search engine. + * This cache allows us to quickly de-index notes when they are deleted or updated. + */ +export let notesCache: Record = {} + +const notesCacheFilePath = `${app.vault.configDir}/plugins/omnisearch/notesCache.json` + +export function resetNotesCache(): void { + notesCache = {} +} + +export async function loadNotesCache(): Promise { + if ( + settings.storeIndexInFile && + (await app.vault.adapter.exists(notesCacheFilePath)) + ) { + try { + const json = await app.vault.adapter.read(notesCacheFilePath) + notesCache = JSON.parse(json) + console.log('Notes cache loaded from the file') + } + catch (e) { + console.trace('Could not load Notes cache from the file') + console.error(e) + } + } + + if (!notesCache) { + notesCache = {} + } +} +export function getNoteFromCache(key: string): IndexedNote | undefined { + return notesCache[key] +} +export function getNonExistingNotesFromCache(): IndexedNote[] { + return Object.values(notesCache).filter(note => note.doesNotExist) +} +export function addNoteToCache(filename: string, note: IndexedNote): void { + notesCache[filename] = note +} +export function removeNoteFromCache(key: string): void { + delete notesCache[key] +} export async function openNote( item: ResultNote, @@ -9,7 +61,26 @@ export async function openNote( const reg = stringsToRegex(item.foundWords) reg.exec(item.content) const offset = reg.lastIndex - await app.workspace.openLinkText(item.path, '', newPane) + + // Check if the note is already open + // const pane = MarkdownView.getPane(item.path) + + // Check if the note is already open, + // to avoid opening it twice if the first one is pinned + let existing = false + app.workspace.iterateAllLeaves(leaf => { + if (leaf.view instanceof MarkdownView) { + if (leaf.getViewState().state?.file === item.path) { + app.workspace.setActiveLeaf(leaf, false, true) + existing = true + } + } + }) + + if (!existing) { + // Open a new note + await app.workspace.openLinkText(item.path, '', newPane) + } const view = app.workspace.getActiveViewOfType(MarkdownView) if (!view) { @@ -38,5 +109,46 @@ export async function createNote(name: string): Promise { } catch (e) { console.error(e) + throw e } } + +/** + * For a given file, returns a list of links leading to notes that don't exist + * @param file + * @param metadata + * @returns + */ +export function getNonExistingNotes( + file: TFile, + metadata: CachedMetadata, +): string[] { + return (metadata.links ?? []) + .map(l => { + const path = removeAnchors(l.link) + return app.metadataCache.getFirstLinkpathDest(path, file.path) + ? '' + : l.link + }) + .filter(l => !!l) +} + +/** + * Removes anchors and headings + * @param name + * @returns + */ +export function removeAnchors(name: string): string { + return name.split(/[\^#]+/)[0] +} + +export async function saveNotesCacheToFile(): Promise { + const json = JSON.stringify(notesCache) + await app.vault.adapter.write(notesCacheFilePath, json) + console.log('Notes cache saved to the file') +} + +export function isCacheOutdated(file: TFile): boolean { + const indexedNote = getNoteFromCache(file.path) + return !indexedNote || indexedNote.mtime !== file.stat.mtime +} diff --git a/src/query.ts b/src/query.ts index 1726cbb..1bb8d2a 100644 --- a/src/query.ts +++ b/src/query.ts @@ -1,4 +1,6 @@ -import { stripSurroundingQuotes } from './utils' +import { settings } from './settings' +import { removeDiacritics, stripSurroundingQuotes } from './utils' +import { parseQuery } from './vendor/parse-query' type QueryToken = { /** @@ -20,6 +22,7 @@ export class Query { public exclusions: QueryToken[] = [] constructor(text = '') { + if (settings.ignoreDiacritics) text = removeDiacritics(text) const tokens = parseQuery(text.toLowerCase(), { tokenize: true }) this.exclusions = tokens.exclude.text .map(this.formatToken) @@ -47,334 +50,3 @@ export class Query { } } } - -/*! - * search-query-parser.js - * Original: https://github.com/nepsilon/search-query-parser - * Modified by Simon Cambier - * Copyright(c) 2014-2019 - * MIT Licensed - */ - -interface SearchParserOptions { - offsets?: boolean - tokenize: true - keywords?: string[] - ranges?: string[] - alwaysArray?: boolean -} - -interface ISearchParserDictionary { - [key: string]: any -} - -type SearchParserKeyWordOffset = { - keyword: string - value?: string -} - -type SearchParserTextOffset = { - text: string -} - -type SearchParserOffset = ( - | SearchParserKeyWordOffset - | SearchParserTextOffset -) & { - offsetStart: number - offsetEnd: number -} - -interface SearchParserResult extends ISearchParserDictionary { - text: string[] - offsets: SearchParserOffset[] - exclude: { text: string[] } -} - -function parseQuery( - string: string, - options: SearchParserOptions, -): SearchParserResult { - // Set a default options object when none is provided - if (!options) { - options = { offsets: true, tokenize: true } - } - else { - // If options offsets was't passed, set it to true - options.offsets = - typeof options.offsets === 'undefined' ? true : options.offsets - } - - if (!string) { - string = '' - } - - // Our object to store the query object - const query: SearchParserResult = { - text: [], - offsets: [], - exclude: { text: [] }, - } - // When offsets is true, create their array - if (options.offsets) { - query.offsets = [] - } - const exclusion: ISearchParserDictionary & { text: string[] } = { text: [] } - const terms = [] - // Get a list of search terms respecting single and double quotes - const regex = - /(\S+:'(?:[^'\\]|\\.)*')|(\S+:"(?:[^"\\]|\\.)*")|(-?"(?:[^"\\]|\\.)*")|(-?'(?:[^'\\]|\\.)*')|\S+|\S+:\S+/g - let match - while ((match = regex.exec(string)) !== null) { - let term = match[0] - const sepIndex = term.indexOf(':') - - // Terms that contain a `:` - if (sepIndex !== -1) { - const key = term.slice(0, sepIndex) - let val = term.slice(sepIndex + 1) - - // Strip backslashes respecting escapes - val = (val + '').replace(/\\(.?)/g, function (s, n1) { - switch (n1) { - case '\\': - return '\\' - case '0': - return '\u0000' - case '': - return '' - default: - return n1 - } - }) - terms.push({ - keyword: key, - value: val, - offsetStart: match.index, - offsetEnd: match.index + term.length, - }) - } - - // Other terms - else { - let isExcludedTerm = false - if (term[0] === '-') { - isExcludedTerm = true - term = term.slice(1) - } - - // Strip backslashes respecting escapes - term = (term + '').replace(/\\(.?)/g, function (s, n1) { - switch (n1) { - case '\\': - return '\\' - case '0': - return '\u0000' - case '': - return '' - default: - return n1 - } - }) - - if (isExcludedTerm) { - exclusion.text.push(term) - } - else { - terms.push({ - text: term, - offsetStart: match.index, - offsetEnd: match.index + term.length, - }) - } - } - } - // Reverse to ensure proper order when pop()'ing. - terms.reverse() - // For each search term - let term - while ((term = terms.pop())) { - // When just a simple term - if (term.text) { - // We add it as pure text - query.text.push(term.text) - // When offsets is true, push a new offset - if (options.offsets) { - query.offsets.push(term) - } - } - // We got an advanced search syntax - else if (term.keyword) { - let key = term.keyword - // Check if the key is a registered keyword - options.keywords = options.keywords || [] - let isKeyword = false - let isExclusion = false - if (!/^-/.test(key)) { - isKeyword = !(options.keywords.indexOf(key) === -1) - } - else if (key[0] === '-') { - const _key = key.slice(1) - isKeyword = !(options.keywords.indexOf(_key) === -1) - if (isKeyword) { - key = _key - isExclusion = true - } - } - - // Check if the key is a registered range - options.ranges = options.ranges || [] - const isRange = !(options.ranges.indexOf(key) === -1) - // When the key matches a keyword - if (isKeyword) { - // When offsets is true, push a new offset - if (options.offsets) { - query.offsets.push({ - keyword: key, - value: term.value, - offsetStart: isExclusion ? term.offsetStart + 1 : term.offsetStart, - offsetEnd: term.offsetEnd, - }) - } - - const value = term.value - // When value is a thing - if (value.length) { - // Get an array of values when several are there - const values = value.split(',') - if (isExclusion) { - if (exclusion[key]) { - // ...many times... - if (exclusion[key] instanceof Array) { - // ...and got several values this time... - if (values.length > 1) { - // ... concatenate both arrays. - exclusion[key] = exclusion[key].concat(values) - } - else { - // ... append the current single value. - exclusion[key].push(value) - } - } - // We saw that keyword only once before - else { - // Put both the current value and the new - // value in an array - exclusion[key] = [exclusion[key]] - exclusion[key].push(value) - } - } - // First time we see that keyword - else { - // ...and got several values this time... - if (values.length > 1) { - // ...add all values seen. - exclusion[key] = values - } - // Got only a single value this time - else { - // Record its value as a string - if (options.alwaysArray) { - // ...but we always return an array if option alwaysArray is true - exclusion[key] = [value] - } - else { - // Record its value as a string - exclusion[key] = value - } - } - } - } - else { - // If we already have seen that keyword... - if (query[key]) { - // ...many times... - if (query[key] instanceof Array) { - // ...and got several values this time... - if (values.length > 1) { - // ... concatenate both arrays. - query[key] = query[key].concat(values) - } - else { - // ... append the current single value. - query[key].push(value) - } - } - // We saw that keyword only once before - else { - // Put both the current value and the new - // value in an array - query[key] = [query[key]] - query[key].push(value) - } - } - // First time we see that keyword - else { - // ...and got several values this time... - if (values.length > 1) { - // ...add all values seen. - query[key] = values - } - // Got only a single value this time - else { - if (options.alwaysArray) { - // ...but we always return an array if option alwaysArray is true - query[key] = [value] - } - else { - // Record its value as a string - query[key] = value - } - } - } - } - } - } - // The key allows a range - else if (isRange) { - // When offsets is true, push a new offset - if (options.offsets) { - query.offsets.push(term) - } - - const value = term.value - // Range are separated with a dash - const rangeValues = value.split('-') - // When both end of the range are specified - // keyword:XXXX-YYYY - query[key] = {} - if (rangeValues.length === 2) { - query[key].from = rangeValues[0] - query[key].to = rangeValues[1] - } - // When pairs of ranges are specified - // keyword:XXXX-YYYY,AAAA-BBBB - // else if (!rangeValues.length % 2) { - // } - // When only getting a single value, - // or an odd number of values - else { - query[key].from = value - } - } - else { - // We add it as pure text - const text = term.keyword + ':' + term.value - query.text.push(text) - - // When offsets is true, push a new offset - if (options.offsets) { - query.offsets.push({ - text: text, - offsetStart: term.offsetStart, - offsetEnd: term.offsetEnd, - }) - } - } - } - } - - // Return forged query object - query.exclude = exclusion - return query -} diff --git a/src/search.ts b/src/search.ts index f0f7993..d510afb 100644 --- a/src/search.ts +++ b/src/search.ts @@ -1,5 +1,5 @@ -import { Notice, TFile, type TAbstractFile } from 'obsidian' -import MiniSearch, { type SearchResult } from 'minisearch' +import { Notice, TAbstractFile, TFile } from 'obsidian' +import MiniSearch, { type Options, type SearchResult } from 'minisearch' import { chsRegex, SPACE_OR_PUNCTUATION, @@ -9,15 +9,31 @@ import { } from './globals' import { extractHeadingsFromCache, + getAliasesFromMetadata, + getTagsFromMetadata, + removeDiacritics, stringsToRegex, stripMarkdownCharacters, wait, } from './utils' import type { Query } from './query' import { settings } from './settings' +import { + removeNoteFromCache, + getNoteFromCache, + getNonExistingNotes, + resetNotesCache, + addNoteToCache, + removeAnchors, + getNonExistingNotesFromCache, + loadNotesCache, + saveNotesCacheToFile, + isCacheOutdated, +} from './notes' let minisearchInstance: MiniSearch -let indexedNotes: Record = {} +let isIndexChanged: boolean +const searchIndexFilePath = `${app.vault.configDir}/plugins/omnisearch/searchIndex.json` const tokenize = (text: string): string[] => { const tokens = text.split(SPACE_OR_PUNCTUATION) @@ -36,38 +52,87 @@ const tokenize = (text: string): string[] => { * and adds all the notes to the index */ export async function initGlobalSearchIndex(): Promise { - indexedNotes = {} - minisearchInstance = new MiniSearch({ + const options: Options = { tokenize, + processTerm: (term: string) => + (settings.ignoreDiacritics ? removeDiacritics(term) : term).toLowerCase(), idField: 'path', - fields: ['basename', 'content', 'headings1', 'headings2', 'headings3'], - }) + fields: [ + 'basename', + 'aliases', + 'content', + 'headings1', + 'headings2', + 'headings3', + ], + storeFields: ['tags'], + } + + if ( + settings.storeIndexInFile && + (await app.vault.adapter.exists(searchIndexFilePath)) + ) { + try { + const json = await app.vault.adapter.read(searchIndexFilePath) + minisearchInstance = MiniSearch.loadJSON(json, options) + console.log('MiniSearch index loaded from the file') + await loadNotesCache() + } + catch (e) { + console.trace('Could not load MiniSearch index from the file') + console.error(e) + } + } + + if (!minisearchInstance) { + minisearchInstance = new MiniSearch(options) + resetNotesCache() + } // Index files that are already present const start = new Date().getTime() - const files = app.vault.getMarkdownFiles() + + const allFiles = app.vault.getMarkdownFiles() + + let files + let notesSuffix + if (settings.storeIndexInFile) { + files = allFiles.filter(file => isCacheOutdated(file)) + notesSuffix = 'modified notes' + } + else { + files = allFiles + notesSuffix = 'notes' + } + + console.log(`Omnisearch - indexing ${files.length} ${notesSuffix}`) // This is basically the same behavior as MiniSearch's `addAllAsync()`. // We index files by batches of 10 - if (files.length) { - console.log('Omnisearch - indexing ' + files.length + ' files') - } for (let i = 0; i < files.length; ++i) { if (i % 10 === 0) await wait(0) const file = files[i] - if (file) await addToIndex(file) + if (file) { + if (getNoteFromCache(file.path)) { + removeFromIndex(file.path) + } + await addToIndex(file) + } } - if (files.length > 0 && settings.showIndexingNotices) { - new Notice( - `Omnisearch - Indexed ${files.length} notes in ${ - new Date().getTime() - start - }ms`, - ) - } + if (files.length > 0) { + const message = `Omnisearch - Indexed ${files.length} ${notesSuffix} in ${ + new Date().getTime() - start + }ms` - // Listen to the query input to trigger a search - // subscribeToQuery() + console.log(message) + + if (settings.showIndexingNotices) { + new Notice(message) + } + + await saveIndexToFile() + } } /** @@ -84,6 +149,7 @@ async function search(query: Query): Promise { combineWith: 'AND', boost: { basename: settings.weightBasename, + aliases: settings.weightBasename, headings1: settings.weightH1, headings2: settings.weightH2, headings3: settings.weightH3, @@ -106,19 +172,20 @@ async function search(query: Query): Promise { const exactTerms = query.getExactTerms() if (exactTerms.length) { results = results.filter(r => { + const title = getNoteFromCache(r.id)?.path.toLowerCase() ?? '' const content = stripMarkdownCharacters( - indexedNotes[r.id]?.content ?? '', + getNoteFromCache(r.id)?.content ?? '', ).toLowerCase() - return exactTerms.every(q => content.includes(q)) + return exactTerms.every(q => content.includes(q) || title.includes(q)) }) } - // // If the search query contains exclude terms, filter out results that have them + // If the search query contains exclude terms, filter out results that have them const exclusions = query.exclusions if (exclusions.length) { results = results.filter(r => { const content = stripMarkdownCharacters( - indexedNotes[r.id]?.content ?? '', + getNoteFromCache(r.id)?.content ?? '', ).toLowerCase() return exclusions.every(q => !content.includes(q.value)) }) @@ -135,7 +202,9 @@ async function search(query: Query): Promise { export function getMatches(text: string, reg: RegExp): SearchMatch[] { let match: RegExpExecArray | null = null const matches: SearchMatch[] = [] + let count = 0 // TODO: FIXME: this is a hack to avoid infinite loops while ((match = reg.exec(text)) !== null) { + if (++count > 100) break const m = match[0] if (m) matches.push({ match: m, offset: match.index }) } @@ -166,16 +235,32 @@ export async function getSuggestions( else results = [] } else { - results = results.sort((a, b) => b.score - a.score).slice(0, 50) + results = results.slice(0, 50) + + // Put the results with tags on top + const tags = query.segments + .filter(s => s.value.startsWith('#')) + .map(s => s.value) + for (const tag of tags) { + for (const result of results) { + if (result.tags.includes(tag)) { + result.score *= 100 + } + } + } } // Map the raw results to get usable suggestions const suggestions = results.map(result => { - const note = indexedNotes[result.id] + const note = getNoteFromCache(result.id) if (!note) { throw new Error(`Note "${result.id}" not indexed`) } + // Remove '#' from tags, for highlighting + query.segments.forEach(s => { + s.value = s.value.replace(/^#/, '') + }) // Clean search matches that match quoted expressions, // and inject those expressions instead const foundWords = [ @@ -184,6 +269,7 @@ export async function getSuggestions( ), ...query.segments.filter(s => s.exact).map(s => s.value), ] + const matches = getMatches(note.content, stringsToRegex(foundWords)) const resultNote: ResultNote = { score: result.score, @@ -206,34 +292,56 @@ export async function addToIndex(file: TAbstractFile): Promise { if (!(file instanceof TFile) || file.extension !== 'md') { return } + + // Check if the file was already indexed as non-existent, + // and if so, remove it from the index (before adding it again) + if (getNoteFromCache(file.path)?.doesNotExist) { + removeFromIndex(file.path) + } + try { // console.log(`Omnisearch - adding ${file.path} to index`) - const fileCache = app.metadataCache.getFileCache(file) - if (indexedNotes[file.path]) { + // Look for links that lead to non-existing files, + // and index them as well + const metadata = app.metadataCache.getFileCache(file) + if (metadata) { + const nonExisting = getNonExistingNotes(file, metadata) + for (const name of nonExisting.filter(o => !getNoteFromCache(o))) { + addNonExistingToIndex(name, file.path) + } + } + + if (getNoteFromCache(file.path)) { throw new Error(`${file.basename} is already indexed`) } // Fetch content from the cache to index it as-is - const content = await app.vault.cachedRead(file) + const content = removeDiacritics(await app.vault.cachedRead(file)) // Make the document and index it const note: IndexedNote = { basename: file.basename, content, path: file.path, - headings1: fileCache - ? extractHeadingsFromCache(fileCache, 1).join(' ') + mtime: file.stat.mtime, + + tags: getTagsFromMetadata(metadata), + aliases: getAliasesFromMetadata(metadata).join(''), + headings1: metadata + ? extractHeadingsFromCache(metadata, 1).join(' ') : '', - headings2: fileCache - ? extractHeadingsFromCache(fileCache, 2).join(' ') + headings2: metadata + ? extractHeadingsFromCache(metadata, 2).join(' ') : '', - headings3: fileCache - ? extractHeadingsFromCache(fileCache, 3).join(' ') + headings3: metadata + ? extractHeadingsFromCache(metadata, 3).join(' ') : '', } + minisearchInstance.add(note) - indexedNotes[note.path] = note + isIndexChanged = true + addNoteToCache(note.path, note) } catch (e) { console.trace('Error while indexing ' + file.basename) @@ -242,25 +350,84 @@ export async function addToIndex(file: TAbstractFile): Promise { } /** - * Removes a file from the index - * @param file - * @returns + * Index a non-existing note. + * Useful to find internal links that lead (yet) to nowhere + * @param name */ -export function removeFromIndex(file: TAbstractFile): void { - if (file instanceof TFile && file.path.endsWith('.md')) { - // console.log(`Omnisearch - removing ${file.path} from index`) - return removeFromIndexByPath(file.path) - } +export function addNonExistingToIndex(name: string, parent: string): void { + name = removeAnchors(name) + const filename = name + (name.endsWith('.md') ? '' : '.md') + if (getNoteFromCache(filename)) return + + const note = { + path: filename, + basename: name, + mtime: 0, + + content: '', + aliases: '', + headings1: '', + headings2: '', + headings3: '', + + doesNotExist: true, + parent, + } as IndexedNote + minisearchInstance.add(note) + isIndexChanged = true + addNoteToCache(filename, note) } /** * Removes a file from the index, by its path * @param path */ -export function removeFromIndexByPath(path: string): void { - const note = indexedNotes[path] +export function removeFromIndex(path: string): void { + if (!path.endsWith('.md')) { + console.info(`"${path}" is not a .md file`) + return + } + const note = getNoteFromCache(path) if (note) { minisearchInstance.remove(note) - delete indexedNotes[path] + isIndexChanged = true + removeNoteFromCache(path) + getNonExistingNotesFromCache() + .filter(n => n.parent === path) + .forEach(n => { + removeFromIndex(n.path) + }) + } + else { + console.warn(`not not found under path ${path}`) + } +} + +const notesToReindex = new Set() +export function addNoteToReindex(note: TAbstractFile): void { + notesToReindex.add(note) +} +export async function reindexNotes(): Promise { + if (settings.showIndexingNotices && notesToReindex.size > 0) { + new Notice(`Omnisearch - Reindexing ${notesToReindex.size} notes`, 2000) + } + for (const note of notesToReindex) { + removeFromIndex(note.path) + await addToIndex(note) + await wait(0) + } + notesToReindex.clear() + + await saveIndexToFile() +} + +async function saveIndexToFile(): Promise { + if (settings.storeIndexInFile && minisearchInstance && isIndexChanged) { + const json = JSON.stringify(minisearchInstance) + await app.vault.adapter.write(searchIndexFilePath, json) + console.log('Omnisearch - Index saved on disk') + + await saveNotesCacheToFile() + isIndexChanged = false } } diff --git a/src/settings.ts b/src/settings.ts index 65d8101..85a6e17 100644 --- a/src/settings.ts +++ b/src/settings.ts @@ -9,8 +9,13 @@ interface WeightingSettings { } export interface OmnisearchSettings extends WeightingSettings { - showIndexingNotices: boolean respectExcluded: boolean + ignoreDiacritics: boolean + showIndexingNotices: boolean + showShortName: boolean + CtrlJK: boolean + CtrlNP: boolean + storeIndexInFile: boolean } export class SettingsTab extends PluginSettingTab { @@ -25,21 +30,12 @@ export class SettingsTab extends PluginSettingTab { const { containerEl } = this containerEl.empty() - // Title - const title = document.createElement('h2') - title.textContent = 'Omnisearch settings' - containerEl.appendChild(title) + // Settings main title + containerEl.createEl('h2', { text: 'Omnisearch settings' }) - // Show notices - new Setting(containerEl) - .setName('Show indexing notices') - .setDesc('Show a notice when indexing is done, usually at startup.') - .addToggle(toggle => - toggle.setValue(settings.showIndexingNotices).onChange(async v => { - settings.showIndexingNotices = v - await saveSettings(this.plugin) - }), - ) + // #region Behavior + + new Setting(containerEl).setName('Behavior').setHeading() // Respect excluded files new Setting(containerEl) @@ -54,10 +50,71 @@ export class SettingsTab extends PluginSettingTab { }), ) + // Ignore diacritics + new Setting(containerEl) + .setName('Ignore diacritics') + .setDesc( + 'EXPERIMENTAL - Normalize diacritics in search terms. Words like "brûlée" or "žluťoučký" will be indexed as "brulee" and "zlutoucky". Needs a restart to take effect.', + ) + .addToggle(toggle => + toggle.setValue(settings.ignoreDiacritics).onChange(async v => { + settings.ignoreDiacritics = v + await saveSettings(this.plugin) + }), + ) + + new Setting(containerEl) + .setName('Store index in file') + .setDesc( + 'EXPERIMENTAL - index is store on disk, instead of being rebuilt on every startup.', + ) + .addToggle(toggle => + toggle.setValue(settings.storeIndexInFile).onChange(async v => { + settings.storeIndexInFile = v + await saveSettings(this.plugin) + }), + ) + + // #endregion Behavior + + // #region User Interface + + new Setting(containerEl).setName('User Interface').setHeading() + + // Show notices + new Setting(containerEl) + .setName('Show indexing notices') + .setDesc('Show a notice when indexing is done, usually at startup.') + .addToggle(toggle => + toggle.setValue(settings.showIndexingNotices).onChange(async v => { + settings.showIndexingNotices = v + await saveSettings(this.plugin) + }), + ) + + // Display note names without the full path + new Setting(containerEl) + .setName('Hide full path in results list') + .setDesc( + 'In the search results, only show the note name, without the full path.', + ) + .addToggle(toggle => + toggle.setValue(settings.showShortName).onChange(async v => { + settings.showShortName = v + await saveSettings(this.plugin) + }), + ) + + // #endregion User Interface + + // #region Results Weighting + new Setting(containerEl).setName('Results weighting').setHeading() new Setting(containerEl) - .setName(`File name (default: ${DEFAULT_SETTINGS.weightBasename})`) + .setName( + `File name & declared aliases (default: ${DEFAULT_SETTINGS.weightBasename})`, + ) .addSlider(cb => this.weightSlider(cb, 'weightBasename')) new Setting(containerEl) @@ -71,6 +128,36 @@ export class SettingsTab extends PluginSettingTab { new Setting(containerEl) .setName(`Headings level 3 (default: ${DEFAULT_SETTINGS.weightH3})`) .addSlider(cb => this.weightSlider(cb, 'weightH3')) + + // #endregion Results Weighting + + // #region Shortcuts + + new Setting(containerEl).setName('Shortcuts').setHeading() + + new Setting(containerEl) + .setName( + 'Use [Ctrl/Cmd]+j/k to navigate up/down in the results, if Vim mode is enabled', + ) + .addToggle(toggle => + toggle.setValue(settings.CtrlJK).onChange(async v => { + settings.CtrlJK = v + await saveSettings(this.plugin) + }), + ) + + new Setting(containerEl) + .setName( + 'Use [Ctrl/Cmd]+n/p to navigate up/down in the results, if Vim mode is enabled', + ) + .addToggle(toggle => + toggle.setValue(settings.CtrlNP).onChange(async v => { + settings.CtrlNP = v + await saveSettings(this.plugin) + }), + ) + + // #endregion Shortcuts } weightSlider(cb: SliderComponent, key: keyof WeightingSettings): void { @@ -85,12 +172,21 @@ export class SettingsTab extends PluginSettingTab { } export const DEFAULT_SETTINGS: OmnisearchSettings = { - showIndexingNotices: true, respectExcluded: true, + ignoreDiacritics: false, + + showIndexingNotices: false, + showShortName: false, + weightBasename: 2, weightH1: 1.5, weightH2: 1.3, weightH3: 1.1, + + CtrlJK: false, + CtrlNP: false, + + storeIndexInFile: false, } as const export let settings: OmnisearchSettings = Object.assign({}, DEFAULT_SETTINGS) diff --git a/src/types.d.ts b/src/types.d.ts index 1c3ff4c..c29e77a 100644 --- a/src/types.d.ts +++ b/src/types.d.ts @@ -1,7 +1,22 @@ -import { type MetadataCache } from 'obsidian' +import type { MetadataCache, ViewState, Vault } from 'obsidian' declare module 'obsidian' { interface MetadataCache { isUserIgnored?(path: string): boolean } + + interface FrontMatterCache { + aliases?: string[] | string + tags?: string[] | string + } + + interface ViewState { + state?: { + file?: string + } + } + + interface Vault { + getConfig(string): unknown + } } diff --git a/src/utils.ts b/src/utils.ts index 0fd1940..ba9aefc 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -61,19 +61,6 @@ export function stringsToRegex(strings: string[]): RegExp { return new RegExp(strings.map(s => `(${escapeRegex(s)})`).join('|'), 'gi') } -export function replaceAll( - text: string, - terms: string[], - cb: (t: string) => string, -): string { - terms.sort((a, b) => a.length - b.length) - const regs = terms.map(term => new RegExp(escapeRegex(term), 'gi')) - for (const reg of regs) { - text = text.replaceAll(reg, cb) - } - return text -} - export function extractHeadingsFromCache( cache: CachedMetadata, level: number, @@ -147,3 +134,33 @@ export async function filterAsync( export function stripMarkdownCharacters(text: string): string { return text.replace(/(\*|_)+(.+?)(\*|_)+/g, (match, p1, p2) => p2) } + +export function getAliasesFromMetadata( + metadata: CachedMetadata | null, +): string[] { + const arrOrString = metadata?.frontmatter?.aliases ?? [] + return (Array.isArray(arrOrString) ? arrOrString : arrOrString.split(',')) + .map(s => (s ? s.trim() : s)) + .filter(s => !!s) +} + +export function getTagsFromMetadata(metadata: CachedMetadata | null): string[] { + const arrOrString = metadata?.frontmatter?.tags ?? [] + const fromFrontMatter = ( + Array.isArray(arrOrString) ? arrOrString : arrOrString.split(',') + ) + .map(s => (s ? s.trim() : s)) + .filter(s => !!s) + const fromBody = (metadata?.tags ?? []).map(t => t.tag) + + return [...fromFrontMatter, ...fromBody].map(t => + t[0] !== '#' ? '#' + t : t, + ) +} + +/** + * https://stackoverflow.com/a/37511463 + */ +export function removeDiacritics(str: string): string { + return str.normalize('NFD').replace(/\p{Diacritic}/gu, '') +} diff --git a/src/vendor/parse-query.ts b/src/vendor/parse-query.ts new file mode 100644 index 0000000..281a13b --- /dev/null +++ b/src/vendor/parse-query.ts @@ -0,0 +1,332 @@ +/*! + * search-query-parser.js + * Original: https://github.com/nepsilon/search-query-parser + * Modified by Simon Cambier + * Copyright(c) 2014-2019 + * MIT Licensed + */ + +interface SearchParserOptions { + offsets?: boolean + tokenize: true + keywords?: string[] + ranges?: string[] + alwaysArray?: boolean +} + +interface ISearchParserDictionary { + [key: string]: any +} + +type SearchParserKeyWordOffset = { + keyword: string + value?: string +} + +type SearchParserTextOffset = { + text: string +} + +type SearchParserOffset = ( + | SearchParserKeyWordOffset + | SearchParserTextOffset +) & { + offsetStart: number + offsetEnd: number +} + +interface SearchParserResult extends ISearchParserDictionary { + text: string[] + offsets: SearchParserOffset[] + exclude: { text: string[] } +} + +export function parseQuery( + string: string, + options: SearchParserOptions, +): SearchParserResult { + // Set a default options object when none is provided + if (!options) { + options = { offsets: true, tokenize: true } + } + else { + // If options offsets was't passed, set it to true + options.offsets = + typeof options.offsets === 'undefined' ? true : options.offsets + } + + if (!string) { + string = '' + } + + // Our object to store the query object + const query: SearchParserResult = { + text: [], + offsets: [], + exclude: { text: [] }, + } + // When offsets is true, create their array + if (options.offsets) { + query.offsets = [] + } + const exclusion: ISearchParserDictionary & { text: string[] } = { text: [] } + const terms = [] + // Get a list of search terms respecting single and double quotes + const regex = + /(\S+:'(?:[^'\\]|\\.)*')|(\S+:"(?:[^"\\]|\\.)*")|(-?"(?:[^"\\]|\\.)*")|(-?'(?:[^'\\]|\\.)*')|\S+|\S+:\S+/g + let match + let count = 0 // TODO: FIXME: this is a hack to avoid infinite loops + while ((match = regex.exec(string)) !== null) { + if (++count > 100) break + let term = match[0] + const sepIndex = term.indexOf(':') + + // Terms that contain a `:` + if (sepIndex !== -1) { + const key = term.slice(0, sepIndex) + let val = term.slice(sepIndex + 1) + + // Strip backslashes respecting escapes + val = (val + '').replace(/\\(.?)/g, function (s, n1) { + switch (n1) { + case '\\': + return '\\' + case '0': + return '\u0000' + case '': + return '' + default: + return n1 + } + }) + terms.push({ + keyword: key, + value: val, + offsetStart: match.index, + offsetEnd: match.index + term.length, + }) + } + + // Other terms + else { + let isExcludedTerm = false + if (term[0] === '-') { + isExcludedTerm = true + term = term.slice(1) + } + + // Strip backslashes respecting escapes + term = (term + '').replace(/\\(.?)/g, function (s, n1) { + switch (n1) { + case '\\': + return '\\' + case '0': + return '\u0000' + case '': + return '' + default: + return n1 + } + }) + + if (isExcludedTerm) { + exclusion.text.push(term) + } + else { + terms.push({ + text: term, + offsetStart: match.index, + offsetEnd: match.index + term.length, + }) + } + } + } + // Reverse to ensure proper order when pop()'ing. + terms.reverse() + // For each search term + let term + while ((term = terms.pop())) { + // When just a simple term + if (term.text) { + // We add it as pure text + query.text.push(term.text) + // When offsets is true, push a new offset + if (options.offsets) { + query.offsets.push(term) + } + } + // We got an advanced search syntax + else if (term.keyword) { + let key = term.keyword + // Check if the key is a registered keyword + options.keywords = options.keywords || [] + let isKeyword = false + let isExclusion = false + if (!/^-/.test(key)) { + isKeyword = !(options.keywords.indexOf(key) === -1) + } + else if (key[0] === '-') { + const _key = key.slice(1) + isKeyword = !(options.keywords.indexOf(_key) === -1) + if (isKeyword) { + key = _key + isExclusion = true + } + } + + // Check if the key is a registered range + options.ranges = options.ranges || [] + const isRange = !(options.ranges.indexOf(key) === -1) + // When the key matches a keyword + if (isKeyword) { + // When offsets is true, push a new offset + if (options.offsets) { + query.offsets.push({ + keyword: key, + value: term.value, + offsetStart: isExclusion ? term.offsetStart + 1 : term.offsetStart, + offsetEnd: term.offsetEnd, + }) + } + + const value = term.value + // When value is a thing + if (value.length) { + // Get an array of values when several are there + const values = value.split(',') + if (isExclusion) { + if (exclusion[key]) { + // ...many times... + if (exclusion[key] instanceof Array) { + // ...and got several values this time... + if (values.length > 1) { + // ... concatenate both arrays. + exclusion[key] = exclusion[key].concat(values) + } + else { + // ... append the current single value. + exclusion[key].push(value) + } + } + // We saw that keyword only once before + else { + // Put both the current value and the new + // value in an array + exclusion[key] = [exclusion[key]] + exclusion[key].push(value) + } + } + // First time we see that keyword + else { + // ...and got several values this time... + if (values.length > 1) { + // ...add all values seen. + exclusion[key] = values + } + // Got only a single value this time + else { + // Record its value as a string + if (options.alwaysArray) { + // ...but we always return an array if option alwaysArray is true + exclusion[key] = [value] + } + else { + // Record its value as a string + exclusion[key] = value + } + } + } + } + else { + // If we already have seen that keyword... + if (query[key]) { + // ...many times... + if (query[key] instanceof Array) { + // ...and got several values this time... + if (values.length > 1) { + // ... concatenate both arrays. + query[key] = query[key].concat(values) + } + else { + // ... append the current single value. + query[key].push(value) + } + } + // We saw that keyword only once before + else { + // Put both the current value and the new + // value in an array + query[key] = [query[key]] + query[key].push(value) + } + } + // First time we see that keyword + else { + // ...and got several values this time... + if (values.length > 1) { + // ...add all values seen. + query[key] = values + } + // Got only a single value this time + else { + if (options.alwaysArray) { + // ...but we always return an array if option alwaysArray is true + query[key] = [value] + } + else { + // Record its value as a string + query[key] = value + } + } + } + } + } + } + // The key allows a range + else if (isRange) { + // When offsets is true, push a new offset + if (options.offsets) { + query.offsets.push(term) + } + + const value = term.value + // Range are separated with a dash + const rangeValues = value.split('-') + // When both end of the range are specified + // keyword:XXXX-YYYY + query[key] = {} + if (rangeValues.length === 2) { + query[key].from = rangeValues[0] + query[key].to = rangeValues[1] + } + // When pairs of ranges are specified + // keyword:XXXX-YYYY,AAAA-BBBB + // else if (!rangeValues.length % 2) { + // } + // When only getting a single value, + // or an odd number of values + else { + query[key].from = value + } + } + else { + // We add it as pure text + const text = term.keyword + ':' + term.value + query.text.push(text) + + // When offsets is true, push a new offset + if (options.offsets) { + query.offsets.push({ + text: text, + offsetStart: term.offsetStart, + offsetEnd: term.offsetEnd, + }) + } + } + } + } + + // Return forged query object + query.exclude = exclusion + return query +} diff --git a/tsconfig.json b/tsconfig.json index 0468412..cea71ac 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -22,6 +22,7 @@ ] }, "include": [ - "**/*.ts" -, "src/__tests__/event-bus-tests.mts" ] + "**/*.ts", + "src/__tests__/event-bus-tests.mts" + ] } \ No newline at end of file diff --git a/versions.json b/versions.json index 4d87ca5..f862abf 100644 --- a/versions.json +++ b/versions.json @@ -19,5 +19,14 @@ "1.1.0": "0.14.2", "1.1.1": "0.14.2", "1.2.0": "0.14.2", - "1.2.1": "0.14.2" + "1.2.1": "0.14.2", + "1.3.0-beta": "0.14.2", + "1.3.1-beta": "0.14.2", + "1.3.2-beta": "0.14.2", + "1.3.3-beta": "0.14.2", + "1.3.3": "0.14.2", + "1.3.4": "0.14.2", + "1.3.5-beta1": "0.14.2", + "1.3.5-beta2": "0.14.2", + "1.3.5-beta3": "0.14.2" } \ No newline at end of file