├── .editorconfig ├── .eslintignore ├── .eslintrc ├── .github └── workflows │ └── no-response.yml ├── .gitignore ├── .npmrc ├── LICENSE ├── README.md ├── build └── .gitignore ├── constants.ts ├── dataclass.ts ├── docs ├── Arguments.md ├── Example1.md ├── Example2.md ├── Example3.md ├── README.md ├── example1-preview.png ├── example2-preview.png └── example3-preview.png ├── esbuild.config.mjs ├── main.ts ├── manifest.json ├── package.json ├── settings.ts ├── styles.css ├── tsconfig.json ├── version-bump.mjs ├── versions.json └── yarn.lock /.editorconfig: -------------------------------------------------------------------------------- 1 | # top-most EditorConfig file 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | end_of_line = lf 7 | insert_final_newline = true 8 | indent_style = tab 9 | indent_size = 4 10 | tab_width = 4 11 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | npm node_modules 2 | build -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "parser": "@typescript-eslint/parser", 4 | "env": { "node": true }, 5 | "plugins": [ 6 | "@typescript-eslint" 7 | ], 8 | "extends": [ 9 | "eslint:recommended", 10 | "plugin:@typescript-eslint/eslint-recommended", 11 | "plugin:@typescript-eslint/recommended" 12 | ], 13 | "parserOptions": { 14 | "sourceType": "module" 15 | }, 16 | "rules": { 17 | "no-unused-vars": "off", 18 | "@typescript-eslint/no-unused-vars": ["error", { "args": "none" }], 19 | "@typescript-eslint/ban-ts-comment": "off", 20 | "no-prototype-builtins": "off", 21 | "@typescript-eslint/no-empty-function": "off" 22 | } 23 | } -------------------------------------------------------------------------------- /.github/workflows/no-response.yml: -------------------------------------------------------------------------------- 1 | name: No Response 2 | 3 | # Both `issue_comment` and `scheduled` event types are required for this Action 4 | # to work properly. 5 | on: 6 | issue_comment: 7 | types: [created] 8 | schedule: 9 | # Schedule for five minutes after the hour, every hour 10 | - cron: '5 * * * *' 11 | 12 | jobs: 13 | noResponse: 14 | runs-on: ubuntu-latest 15 | steps: 16 | - uses: lee-dohm/no-response@v0.5.0 17 | with: 18 | token: ${{ github.token }} 19 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # vscode 2 | .vscode 3 | 4 | # Intellij 5 | *.iml 6 | .idea 7 | 8 | # npm 9 | node_modules 10 | 11 | # Don't include the compiled main.js file in the repo. 12 | # They should be uploaded to GitHub releases instead. 13 | main.js 14 | 15 | # Exclude sourcemaps 16 | *.map 17 | 18 | # obsidian 19 | data.json 20 | 21 | # Exclude macOS Finder (System Explorer) View States 22 | .DS_Store 23 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | tag-version-prefix="" -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 SErAphLi 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Obsidian Aggregator 2 | 3 | This plugin helps you gather information from files, and make a summary in the file. 4 | 5 | Although it is difficult to write the regular expression, once it's done, it will help you a lot. 6 | 7 | ## Preview 8 | 9 | A collection of blocks with a tag. [Example1](docs/Example1.md) 10 | 11 | ![example1-preview](docs/example1-preview.png) 12 | 13 | A collection of annotator blocks with tags. [Example2](docs/Example2.md) 14 | 15 | ![example2-preview](docs/example2-preview.png) 16 | 17 | A stat table that is generated from a folder with custom helper supported. [Example3](docs/Example3.md) 18 | 19 | ![example3-preview](docs/example3-preview.png) 20 | 21 | 22 | ## Usage 23 | 24 | Create a code block like this, and set the language to the `aggregator`. Then this plugin will find content based on RegExp and render the result with the template. 25 | 26 | ````aggregator 27 | scope: 28 | - Current File 29 | matches: 30 | - regex: '(?<=^>%%COMMENT%%\n)(?:(?!^>%%TAGS%%).*\n)+(?=^>%%TAGS%%\n>.*#[a-zA-Z0-9\_]+)' 31 | template: '{{{result.match.[0]}}}' 32 | ```` 33 | 34 | If you want to sort the results, you can specify the fields and orders like this. 35 | 36 | ````aggregator 37 | scope: 38 | - Current File 39 | - Papers 40 | matches: 41 | - regex: '^\w[^\#]*\#[a-zA-Z0-9\_]+\s*$' 42 | template: '{{{result.match.[0]}}}' 43 | - regex: '(?<=^>%%COMMENT%%\n)(?:(?!^>%%TAGS%%).*\n)+(?=^>%%TAGS%%\n>.*#[a-zA-Z0-9\_]+)' 44 | template: '{{{result.match.[0]}}}' 45 | order: 46 | fields: filename, line 47 | orders: asc, asc 48 | ```` 49 | 50 | ## Argument Explain 51 | 52 | Check out [Arguments.md](docs/Arguments.md). 53 | 54 | ## Example Usage 55 | 56 | Check out [docs](docs/) for detailed examples. 57 | 58 | ## Settings 59 | 60 | Note the default join string is not empty. It's just the text area can not display `\n\n`. 61 | 62 | `File Indicator` is a Handlebars template. Available data: file: `TFile`, index: number starts at 1. 63 | 64 | ## See Also 65 | 66 | [obsidian-link-embed](https://github.com/Seraphli/obsidian-link-embed) 67 | 68 | ## Thanks 69 | 70 | - [tag-summary](https://github.com/macrojd/tag-summary) 71 | - [tracker](https://github.com/pyrochlore/obsidian-tracker) 72 | -------------------------------------------------------------------------------- /build/.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore everything in this directory 2 | * 3 | # Except this file 4 | !.gitignore -------------------------------------------------------------------------------- /constants.ts: -------------------------------------------------------------------------------- 1 | export const CURFILE = "Current File"; 2 | export const SELFBLOCK = /^```aggregator[\S\s]*?^```/gm; 3 | export const FIELDS = [ 4 | "path", 5 | "filename", 6 | "basename", 7 | "extension", 8 | "ctime", 9 | "mtime", 10 | "match", 11 | "index", 12 | "line", 13 | "ch", 14 | ]; 15 | export const ORDERS = ["asc", "desc"]; 16 | -------------------------------------------------------------------------------- /dataclass.ts: -------------------------------------------------------------------------------- 1 | export class Match { 2 | regex: string; 3 | template: string; 4 | } 5 | 6 | export class Order { 7 | fields: string; 8 | orders: string; 9 | } 10 | 11 | export class Register { 12 | s: string; 13 | n: number; 14 | b: boolean; 15 | } 16 | 17 | export class AggregatorArgs { 18 | scope: string[]; 19 | matches: Match[]; 20 | order: Order; 21 | fileIndecator: string; 22 | joinString: string; 23 | decorator: string; 24 | limitSearch: number; 25 | limitDisplay: number; 26 | fileLink: boolean; 27 | noCurFile: boolean; 28 | } 29 | 30 | export class Result { 31 | index: number; 32 | path: string; 33 | filename: string; 34 | basename: string; 35 | extension: string; 36 | ctime: number; 37 | mtime: number; 38 | match: RegExpMatchArray; 39 | matchIndex: number; 40 | line: number; 41 | ch: number; 42 | content: string; 43 | template: HandlebarsTemplateDelegate; 44 | register: Register; 45 | } 46 | -------------------------------------------------------------------------------- /docs/Arguments.md: -------------------------------------------------------------------------------- 1 | # Argument Explain 2 | 3 | Arguments should be written in YAML. 4 | 5 | The dataclass is defined in [dataclass.ts](../dataclass.ts) 6 | 7 | ## Processing Pipeline 8 | 9 | 1. Search files in the vault based on the `scope` argument. 10 | 2. Search the content in the files based on the `matches` argument. 11 | 3. Sort the results based on the `order` argument. 12 | 4. Render the summary based on the `template` argument. 13 | 5. Render the summary based on the `fileIndecator` argument. 14 | 6. Concatenate the results based on the `joinString` argument. 15 | 7. Decorate the summary based on the `decorator` argument. 16 | 17 | ## Support Arguments 18 | 19 | ```typescript 20 | export class AggregatorArgs { 21 | scope: string[]; 22 | matches: Match[]; 23 | order: Order; 24 | fileIndecator: string; 25 | joinString: string; 26 | decorator: string; 27 | limitSearch: number; 28 | limitDisplay: number; 29 | fileLink: boolean; 30 | noCurFile: boolean; 31 | } 32 | ``` 33 | 34 | ## Dataclass 35 | 36 | Introduce `Result` dataclass, which will be used in the template. 37 | 38 | ```typescript 39 | export class Result { 40 | index: number; 41 | path: string; 42 | filename: string; 43 | basename: string; 44 | extension: string; 45 | ctime: number; 46 | mtime: number; 47 | match: RegExpMatchArray; 48 | matchIndex: number; 49 | line: number; 50 | ch: number; 51 | content: string; 52 | template: HandlebarsTemplateDelegate; 53 | register: Register; 54 | } 55 | ``` 56 | 57 | `Register` can be used to store any data. 58 | 59 | ```typescript 60 | export class Register { 61 | s: string; 62 | n: number; 63 | b: boolean; 64 | } 65 | ``` 66 | 67 | ## scope 68 | 69 | **scope:** (list of Regular expressions) Define the search scope, which can be folder name, file name or the file path in the vault. `Current File` is a reserved keyword. 70 | 71 | Note: For better performance in a large vault, you should consider using the exact file path instead of a regular expression to avoid searching the whole vault. 72 | 73 | Example: 74 | 75 | ```yaml 76 | scope: 77 | - ReadingNotes/ 78 | - Current File 79 | ``` 80 | 81 | Search in the current file and the `ReadingNotes` folder. 82 | 83 | ## matches 84 | 85 | **matches:** (list of matches) 86 | 87 | **match** 88 | 89 | - regex: Regular expression. 90 | - template: Handlebars template. 91 | 92 | Example: 93 | 94 | ```yaml 95 | matches: 96 | - regex: '^\w[^\#]*\#[a-zA-Z0-9\_]+\s*$' 97 | template: '{{{result.match.[0]}}}' 98 | - regex: '>%%\n>```annotation-json\n>.*\n>```\n>%%\n>\*%%PREFIX%%.*\n>%%LINK%%.*\n>%%COMMENT%%\n>.*\n>%%TAGS%%\n>\#[a-zA-Z0-9\_]+\n\^[a-zA-Z0-9]*' 99 | template: '{{{result.match.[0]}}}' 100 | ``` 101 | 102 | Valid input(data) for `eval`: 103 | 104 | ```js 105 | let data: { 106 | result: Result; 107 | summaries: string[]; 108 | register: Register; 109 | } 110 | ``` 111 | 112 | - result(Result): result data 113 | - summaries(list of string): the list before being concatenatd 114 | - register(Register): global register 115 | 116 | 117 | ## order 118 | 119 | **order**: (fields and orders) Define the fields you want to sort by and the direction you want, separated by commas. When not present, the plugin will first check the default fields and orders in the setting. If the setting is not valid, the plugin will sort the results by the creating time and the line number. 120 | 121 | Valid fields: path, filename, basename, extension, ctime, mtime, match, index, line, ch, content 122 | 123 | Valid orders: asc, desc 124 | 125 | Example: 126 | 127 | ```yaml 128 | order: 129 | fields: filename, line 130 | orders: asc, asc 131 | ``` 132 | 133 | Sort the results by file name and line in ascending order. 134 | 135 | ## decorator 136 | 137 | Handlebars template that decorates the whole summary. 138 | 139 | Example: 140 | 141 | ```yaml 142 | decorator: "| ID | Note | ModifyTime | Done |\n| --- | ---- | ---------- | ----------- |\n{{templates}}" 143 | ``` 144 | Wrap the summary with table heading. 145 | 146 | Valid input(data) for `eval`: 147 | 148 | ```js 149 | let data: { 150 | templates: string; 151 | summaries: string[]; 152 | register: Register; 153 | } 154 | ``` 155 | 156 | - templates(string): concatenated string 157 | - summaries(list of string): the list before being concatenatd 158 | - register(Register): global register 159 | 160 | 161 | ## fileIndecator 162 | 163 | The leading template that provides other information like file path. 164 | 165 | Example: 166 | 167 | ```yaml 168 | fileIndecator: >- 169 | |{{index}}|[[{{result.basename}}]]|{{eval "return moment.unix(data.result.mtime/1000).format('YYYY-MM-DD')"}}|{{template}}| 170 | ``` 171 | 172 | Valid input(data) for `eval`: 173 | 174 | ```js 175 | let data: { 176 | result: Result; 177 | summaries: string[]; 178 | register: Register; 179 | template: string; 180 | } 181 | ``` 182 | 183 | - result(Result): result data 184 | - summaries(list of string): the list before being concatenatd 185 | - template(string): each summary 186 | - register(Register): global register 187 | 188 | ## Other Arguments 189 | 190 | Other arguments like `joinString`, `limitSearch`, `limitDisplay`, `fileLink`, `noCurFile` have the same effect as the value in the setting. They are used to override the default settings. 191 | 192 | ## Advanced 193 | 194 | Handlebars template supports custom helper `eval`. You can process the `data` argument with any javascript code. So you can render different contents based on some conditions. Checkout [Example3](Example3.md) for more information. 195 | -------------------------------------------------------------------------------- /docs/Example1.md: -------------------------------------------------------------------------------- 1 | ## Questions 2 | ```aggregator 3 | scope: 4 | - Current File 5 | matches: 6 | - regex: '^\w[^\#]*\#[a-zA-Z0-9\_]+\s*$' 7 | template: '{{{result.match.[0]}}}' 8 | ``` 9 | 10 | 11 | ## Contents 12 | 13 | What is love 14 | #question 15 | 16 | Who am I 17 | #question 18 | 19 | Dummy content that will not be gathered. 20 | -------------------------------------------------------------------------------- /docs/Example2.md: -------------------------------------------------------------------------------- 1 | --- 2 | alias: , liLifelongMultiAgentPath2021 3 | authors: Jiaoyang Li, Andrew Tinka, Scott Kiesel, Joseph W. Durham, T. K. Satish Kumar, Sven Koenig 4 | year: 2021 5 | URL: http://arxiv.org/abs/2005.07371 6 | annotation-target: Li et al_2021_Lifelong Multi-Agent Path Finding in Large-Scale Warehouses.pdf 7 | --- 8 | [Open in Zotero](zotero://select/items/@liLifelongMultiAgentPath2021) 9 | 10 | **Title** Lifelong Multi-Agent Path Finding in Large-Scale Warehouses 11 | 12 | **Abstract** Multi-Agent Path Finding (MAPF) is the problem of moving a team of agents to their goal locations without collisions. In this paper, we study the lifelong variant of MAPF, where agents are constantly engaged with new goal locations, such as in large-scale automated warehouses. We propose a new framework Rolling-Horizon Collision Resolution (RHCR) for solving lifelong MAPF by decomposing the problem into a sequence of Windowed MAPF instances, where a Windowed MAPF solver resolves collisions among the paths of the agents only within a bounded time horizon and ignores collisions beyond it. RHCR is particularly well suited to generating pliable plans that adapt to continually arriving new goal locations. We empirically evaluate RHCR with a variety of MAPF solvers and show that it can produce high-quality solutions for up to 1,000 agents (= 38.9\% of the empty cells on the map) for simulated warehouse instances, significantly outperforming existing work. 13 | 14 | ## Highlight 15 | ```aggregator 16 | scope: 17 | - Current File 18 | matches: 19 | - regex: '>%%\n>```annotation-json\n>.*\n>```\n>%%\n>\*%%PREFIX%%.*\n>%%LINK%%.*\n>%%COMMENT%%\n([^%]*\n)*>%%TAGS%%\n>\#[a-zA-Z0-9\_]+\n\^[a-zA-Z0-9]*' 20 | template: '{{{result.match.[0]}}}' 21 | ``` 22 | 23 | 24 | >%% 25 | >```annotation-json 26 | >{"created":"2022-11-21T22:32:47.014Z","text":"效果还可以","updated":"2022-11-21T22:32:47.014Z","document":{"title":"Li et al_2021_Lifelong Multi-Agent Path Finding in Large-Scale Warehouses.pdf","link":[{"href":"urn:x-pdf:8b485ac3ca9fc144083cc78a49427553"},{"href":"vault:/Zotero/storage/EJC9QYFB/Li et al_2021_Lifelong Multi-Agent Path Finding in Large-Scale Warehouses.pdf"}],"documentFingerprint":"8b485ac3ca9fc144083cc78a49427553"},"uri":"vault:/Zotero/storage/EJC9QYFB/Li et al_2021_Lifelong Multi-Agent Path Finding in Large-Scale Warehouses.pdf","target":[{"source":"vault:/Zotero/storage/EJC9QYFB/Li et al_2021_Lifelong Multi-Agent Path Finding in Large-Scale Warehouses.pdf","selector":[{"type":"TextPositionSelector","start":1131,"end":1190},{"type":"TextQuoteSelector","exact":"up to 1,000 agents (= 38.9% of theempty cells on the map) f","prefix":"oducehigh-quality solutions for ","suffix":"or simulated warehouse instances"}]}]} 27 | >``` 28 | >%% 29 | >*%%PREFIX%%oducehigh-quality solutions for%%HIGHLIGHT%% ==up to 1,000 agents (= 38.9% of theempty cells on the map) f== %%POSTFIX%%or simulated warehouse instances* 30 | >%%LINK%%[[#^7ah3nxihrey|show annotation]] 31 | >%%COMMENT%% 32 | >效果还可以 33 | >%%TAGS%% 34 | >#highlight 35 | ^7ah3nxihrey 36 | -------------------------------------------------------------------------------- /docs/Example3.md: -------------------------------------------------------------------------------- 1 | Aggregator Stats Table 2 | ```aggregator 3 | scope: 4 | - ReadingNotes/ 5 | matches: 6 | - regex: '[\S\s]+' 7 | template: >- 8 | {{eval "return data.result.content.contains('#summary')? '✓': 'x'"}} 9 | order: 10 | fields: mtime 11 | orders: desc 12 | fileIndecator: >- 13 | |{{result.index}}|[[{{result.basename}}]]|{{eval "return moment.unix(data.result.mtime/1000).format('YYYY-MM-DD')"}}|{{template}}| 14 | joinString: "\n" 15 | decorator: |- 16 | Notes: {{eval "return (data.templates.match(/✓/g)).length"}}/{{eval "return data.summaries.length"}} 17 | 18 | | ID | Note | ModifyTime | Done | 19 | | --- | ---- | ---------- | ----------- | 20 | {{templates}} 21 | ``` 22 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # Example Usage 2 | 3 | ## Example 1 Explain 4 | 5 | This example shows how to aggregate blocks with tags in the current file. 6 | 7 | ## Example 2 Explain 8 | 9 | This example shows how to aggregate blocks with tags in the current file. The regex is designed for [obsidian-annotator](https://github.com/elias-sundqvist/obsidian-annotator) to extract any annotations with tags. 10 | 11 | ## Example 3 Explain 12 | 13 | This example first list all notes in the `ReadingNotes` folder. Then for every note, create a row in the table. The field `Done` is automatically created based on whether there is a `#summary` tag in the note. -------------------------------------------------------------------------------- /docs/example1-preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Seraphli/obsidian-aggregator/f1e1876229624110ce0a9ea098b9bb805b4351dc/docs/example1-preview.png -------------------------------------------------------------------------------- /docs/example2-preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Seraphli/obsidian-aggregator/f1e1876229624110ce0a9ea098b9bb805b4351dc/docs/example2-preview.png -------------------------------------------------------------------------------- /docs/example3-preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Seraphli/obsidian-aggregator/f1e1876229624110ce0a9ea098b9bb805b4351dc/docs/example3-preview.png -------------------------------------------------------------------------------- /esbuild.config.mjs: -------------------------------------------------------------------------------- 1 | import esbuild from "esbuild"; 2 | import process from "process"; 3 | import builtins from 'builtin-modules' 4 | 5 | const banner = 6 | `/* 7 | THIS IS A GENERATED/BUNDLED FILE BY ESBUILD 8 | if you want to view the source, please visit the github repository of this plugin 9 | */ 10 | `; 11 | 12 | const prod = (process.argv[2] === 'production'); 13 | 14 | esbuild.build({ 15 | banner: { 16 | js: banner, 17 | }, 18 | entryPoints: ['main.ts'], 19 | bundle: true, 20 | external: [ 21 | 'obsidian', 22 | 'electron', 23 | '@codemirror/autocomplete', 24 | '@codemirror/collab', 25 | '@codemirror/commands', 26 | '@codemirror/language', 27 | '@codemirror/lint', 28 | '@codemirror/search', 29 | '@codemirror/state', 30 | '@codemirror/view', 31 | '@lezer/common', 32 | '@lezer/highlight', 33 | '@lezer/lr', 34 | ...builtins], 35 | format: 'cjs', 36 | watch: !prod, 37 | target: 'es2018', 38 | logLevel: "info", 39 | sourcemap: prod ? false : 'inline', 40 | treeShaking: true, 41 | outfile: 'main.js', 42 | }).catch(() => process.exit(1)); 43 | -------------------------------------------------------------------------------- /main.ts: -------------------------------------------------------------------------------- 1 | /* eslint no-eval: 0 */ 2 | import { 3 | Plugin, 4 | parseYaml, 5 | stringifyYaml, 6 | TFile, 7 | MarkdownRenderer, 8 | MarkdownPostProcessorContext, 9 | } from "obsidian"; 10 | import { 11 | AggregatorSettings, 12 | DEFAULT_SETTINGS, 13 | AggregatorSettingTab, 14 | } from "settings"; 15 | import { AggregatorArgs, Register, Result } from "dataclass"; 16 | import { CURFILE, SELFBLOCK, FIELDS, ORDERS } from "./constants"; 17 | import * as Handlebars from "handlebars"; 18 | import * as _ from "lodash"; 19 | import moment from "moment"; 20 | 21 | function checkOrder(fields: string[], orders: string[]) { 22 | if (fields.length != orders.length) 23 | return { flag: false, msg: "length of fields and orders must match" }; 24 | if (!fields.every((val) => FIELDS.includes(val))) 25 | return { flag: false, msg: `fields must in ${FIELDS}` }; 26 | if (!orders.every((val) => ORDERS.includes(val))) 27 | return { flag: false, msg: `orders must in ${ORDERS}` }; 28 | return { flag: true, msg: "" }; 29 | } 30 | 31 | export default class Aggregator extends Plugin { 32 | settings: AggregatorSettings; 33 | // Maximum time allowed for regex execution in ms 34 | private readonly REGEX_TIMEOUT = 10000; // 10 seconds 35 | 36 | async onload() { 37 | await this.loadSettings(); 38 | 39 | this.registerMarkdownCodeBlockProcessor( 40 | "aggregator", 41 | async (source, el, ctx) => { 42 | // Generate a unique ID for this execution instance 43 | const execId = Math.random().toString(36).substring(2, 8); // Short hash 44 | if (this.settings.debug) { 45 | console.log( 46 | `[${moment().format( 47 | "YYYY-MM-DD HH:mm:ss" 48 | )}] Aggregator[${execId}]: Starting execution` 49 | ); 50 | } 51 | const startTime = performance.now(); 52 | 53 | // Check tab 54 | source = source.trim(); 55 | const tabMatch = source.match(/\t/gm); 56 | if (tabMatch != null && tabMatch.length > 0) { 57 | await this.renderMarkdown( 58 | "**Error**: Don't use tab to indent. Replace tab with spaces.", 59 | el, 60 | ctx 61 | ); 62 | return; 63 | } 64 | 65 | // Parse yaml 66 | let args: AggregatorArgs; 67 | try { 68 | args = parseYaml(source) as AggregatorArgs; 69 | } catch (error) { 70 | await this.renderMarkdown( 71 | `**Error**: Yaml parse error.\n${error}`, 72 | el, 73 | ctx 74 | ); 75 | console.log( 76 | `[${moment().format( 77 | "YYYY-MM-DD HH:mm:ss" 78 | )}] Aggregator[${execId}]:`, 79 | error 80 | ); 81 | return; 82 | } 83 | if (this.settings.debug) { 84 | console.log( 85 | `[${moment().format( 86 | "YYYY-MM-DD HH:mm:ss" 87 | )}] Aggregator[${execId}]: args\n`, 88 | args 89 | ); 90 | } 91 | 92 | // Handle arguments 93 | let argsMatches: { 94 | regex: RegExp; 95 | template: HandlebarsTemplateDelegate; 96 | }[] = []; 97 | let argsOrders: { 98 | fields: string[]; 99 | orders: string[]; 100 | } = { fields: [], orders: [] }; 101 | try { 102 | for (const m of args.matches) { 103 | const regex = new RegExp(m.regex, "gm"); 104 | const template = Handlebars.compile(m.template, { 105 | noEscape: true, 106 | }); 107 | argsMatches.push({ regex, template }); 108 | } 109 | if (args.order) { 110 | argsOrders.fields = args.order.fields 111 | .split(",") 112 | .map((val) => val.trim()); 113 | argsOrders.orders = args.order.orders 114 | .split(",") 115 | .map((val) => val.trim()); 116 | const res = checkOrder( 117 | argsOrders.fields, 118 | argsOrders.orders 119 | ); 120 | if (!res.flag) throw Error(res.msg); 121 | } 122 | } catch (error) { 123 | await this.renderMarkdown( 124 | `**Error**: Arguments parse error.\n${error}`, 125 | el, 126 | ctx 127 | ); 128 | console.log( 129 | `[${moment().format( 130 | "YYYY-MM-DD HH:mm:ss" 131 | )}] Aggregator[${execId}]:`, 132 | error 133 | ); 134 | return; 135 | } 136 | if (argsMatches.length == 0) return; 137 | if (this.settings.debug) { 138 | console.log( 139 | `[${moment().format( 140 | "YYYY-MM-DD HH:mm:ss" 141 | )}] Aggregator[${execId}]: argsMatches\n`, 142 | argsMatches 143 | ); 144 | } 145 | 146 | // Filter files 147 | const allMDFile: TFile[] = []; 148 | const partRegexFile: string[] = []; 149 | args.scope.forEach((val) => { 150 | if (val == CURFILE) { 151 | const file = this.app.vault.getAbstractFileByPath( 152 | ctx.sourcePath 153 | ); 154 | if (file instanceof TFile) { 155 | allMDFile.push(file); 156 | } 157 | } else { 158 | const file = this.app.vault.getAbstractFileByPath(val); 159 | if (file instanceof TFile) { 160 | allMDFile.push(file); 161 | } else { 162 | partRegexFile.push(val); 163 | } 164 | } 165 | }); 166 | if (partRegexFile.length > 0) { 167 | const partRegex: RegExp[] = []; 168 | partRegexFile.forEach((val) => { 169 | partRegex.push(new RegExp(val, "g")); 170 | }); 171 | let mdFiles = this.app.vault.getMarkdownFiles(); 172 | mdFiles.forEach((val) => { 173 | const scopeMatches = partRegex.filter((regex) => { 174 | const m = val.path.match(regex); 175 | if (m == null || m.length == 0) { 176 | return false; 177 | } 178 | return true; 179 | }); 180 | if (scopeMatches.length > 0) { 181 | allMDFile.push(val); 182 | } 183 | }); 184 | } 185 | if (allMDFile.length == 0) return; 186 | if (this.settings.debug) { 187 | const files: string[] = []; 188 | for (const file of allMDFile) { 189 | files.push(file.path); 190 | } 191 | console.log( 192 | `[${moment().format( 193 | "YYYY-MM-DD HH:mm:ss" 194 | )}] Aggregator[${execId}]: files\n`, 195 | files.join("\n") 196 | ); 197 | console.log( 198 | `[${moment().format( 199 | "YYYY-MM-DD HH:mm:ss" 200 | )}] Aggregator[${execId}]: Processing ${ 201 | allMDFile.length 202 | } files` 203 | ); 204 | } 205 | 206 | // Collect results 207 | const limitSearch = !isNaN(Number(args.limitSearch)) 208 | ? Number(args.limitSearch) 209 | : this.settings.limitSearch; 210 | let results: Result[] = []; 211 | if (this.settings.debug) { 212 | console.log( 213 | `[${moment().format( 214 | "YYYY-MM-DD HH:mm:ss" 215 | )}] Aggregator[${execId}]: Starting match collection with limitSearch=${limitSearch}` 216 | ); 217 | } 218 | for (const file of allMDFile) { 219 | if (limitSearch > 0 && results.length > limitSearch) break; 220 | // Read file content 221 | const fileContent = await this.app.vault.cachedRead(file); 222 | let content = fileContent; 223 | content = content.replace(SELFBLOCK, ""); 224 | if (this.settings.excludedRegex != "") { 225 | content = content.replace( 226 | new RegExp(this.settings.excludedRegex), 227 | "" 228 | ); 229 | } 230 | for (const m of argsMatches) { 231 | if (limitSearch > 0 && results.length > limitSearch) 232 | break; 233 | if (this.settings.debug) { 234 | console.log( 235 | `[${moment().format( 236 | "YYYY-MM-DD HH:mm:ss" 237 | )}] Aggregator[${execId}]: Applying regex on file ${ 238 | file.path 239 | }` 240 | ); 241 | 242 | // Log file size and regex pattern 243 | console.log( 244 | `[${moment().format( 245 | "YYYY-MM-DD HH:mm:ss" 246 | )}] Aggregator[${execId}]: File size: ${ 247 | content.length 248 | } chars, Regex pattern: ${m.regex.toString()}` 249 | ); 250 | 251 | // Check for problematic regex patterns 252 | const regexStr = m.regex.toString(); 253 | if (this.isLikelyProblematicRegex(regexStr)) { 254 | console.log( 255 | `[${moment().format( 256 | "YYYY-MM-DD HH:mm:ss" 257 | )}] Aggregator[${execId}]: ⚠️WARNING: Potentially inefficient regex pattern detected: ${regexStr}` 258 | ); 259 | } 260 | } 261 | 262 | const regexStartTime = performance.now(); 263 | // Use a timeout to detect potentially problematic regex operations 264 | const MAX_REGEX_TIME = 5000; // 5 seconds 265 | let timeoutId: NodeJS.Timeout | null = null; 266 | let timeoutWarningDisplayed = false; 267 | 268 | if (this.settings.debug) { 269 | timeoutId = setTimeout(() => { 270 | console.log( 271 | `[${moment().format( 272 | "YYYY-MM-DD HH:mm:ss" 273 | )}] Aggregator[${execId}]: ⚠️WARNING: Regex operation taking too long (>5s) on ${ 274 | file.path 275 | }. This may be due to an inefficient regex pattern or a large file.` 276 | ); 277 | timeoutWarningDisplayed = true; 278 | }, MAX_REGEX_TIME); 279 | } 280 | 281 | let matchesArray: RegExpMatchArray[] = []; 282 | try { 283 | // For large files, process in chunks to avoid excessive runtime 284 | if (content.length > 100000) { 285 | // 100KB threshold 286 | if (this.settings.debug) { 287 | console.log( 288 | `[${moment().format( 289 | "YYYY-MM-DD HH:mm:ss" 290 | )}] Aggregator[${execId}]: Large file detected (${ 291 | content.length 292 | } chars), using chunked processing` 293 | ); 294 | } 295 | 296 | // Process in 50KB chunks with slight overlap to avoid missing matches at chunk boundaries 297 | const chunkSize = 50000; 298 | const overlap = 1000; 299 | let processedChunks = 0; 300 | 301 | for ( 302 | let i = 0; 303 | i < content.length; 304 | i += chunkSize - overlap 305 | ) { 306 | processedChunks++; 307 | if ( 308 | this.REGEX_TIMEOUT > 0 && 309 | performance.now() - regexStartTime > 310 | this.REGEX_TIMEOUT 311 | ) { 312 | if (this.settings.debug) { 313 | console.log( 314 | `[${moment().format( 315 | "YYYY-MM-DD HH:mm:ss" 316 | )}] Aggregator[${execId}]: ⚠️Regex timeout reached after processing ${processedChunks} chunks. Stopping further processing.` 317 | ); 318 | } 319 | break; 320 | } 321 | 322 | const chunk = content.substring( 323 | i, 324 | Math.min(i + chunkSize, content.length) 325 | ); 326 | const chunkMatches = Array.from( 327 | chunk.matchAll(m.regex) 328 | ); 329 | 330 | // Add matches, adjusting indices for chunk position 331 | for (const match of chunkMatches) { 332 | // Only add if it's not a duplicate from the overlap 333 | if ( 334 | match.index !== undefined && 335 | (i === 0 || match.index >= overlap) 336 | ) { 337 | // Clone the match and adjust the index 338 | const adjustedMatch = [ 339 | ...match, 340 | ] as RegExpMatchArray; 341 | if (match.index !== undefined) { 342 | adjustedMatch.index = 343 | i + 344 | match.index - 345 | (i > 0 ? overlap : 0); 346 | } 347 | matchesArray.push(adjustedMatch); 348 | } 349 | } 350 | } 351 | } else { 352 | // For smaller files, process normally 353 | let matches = content.matchAll(m.regex); 354 | matchesArray = Array.from(matches); 355 | } 356 | } catch (error) { 357 | if (this.settings.debug) { 358 | console.log( 359 | `[${moment().format( 360 | "YYYY-MM-DD HH:mm:ss" 361 | )}] Aggregator[${execId}]: ⚠️ERROR: Regex execution failed: ${error}` 362 | ); 363 | } 364 | } 365 | 366 | const regexEndTime = performance.now(); 367 | if (timeoutId) clearTimeout(timeoutId); 368 | 369 | if (this.settings.debug) { 370 | const executionTime = regexEndTime - regexStartTime; 371 | console.log( 372 | `[${moment().format( 373 | "YYYY-MM-DD HH:mm:ss" 374 | )}] Aggregator[${execId}]: Regex execution took ${executionTime}ms, found ${ 375 | matchesArray.length 376 | } matches in ${file.path}` 377 | ); 378 | 379 | // Provide specific advice if the regex is slow 380 | if (executionTime > 1000) { 381 | console.log( 382 | `[${moment().format( 383 | "YYYY-MM-DD HH:mm:ss" 384 | )}] Aggregator[${execId}]: ⚠️ Consider optimizing your regex pattern or using a more specific scope for this file.` 385 | ); 386 | } 387 | } 388 | 389 | for (let match of matchesArray) { 390 | if (limitSearch > 0 && results.length > limitSearch) 391 | break; 392 | if (this.settings.debug) { 393 | console.log( 394 | `[${moment().format( 395 | "YYYY-MM-DD HH:mm:ss" 396 | )}] Aggregator[${execId}]: Find match in\n${ 397 | file.path 398 | } -> ${match[0]}` 399 | ); 400 | } 401 | 402 | let substringStartToMatch = content.substring( 403 | 0, 404 | match.index 405 | ); 406 | let lines = substringStartToMatch.split("\n"); 407 | let numberOfLines = lines.length; 408 | let numberOfChars = lines[lines.length - 1].length; 409 | 410 | let result: Result = { 411 | index: 0, 412 | path: file.path, 413 | filename: file.basename + file.extension, 414 | basename: file.basename, 415 | extension: file.extension, 416 | ctime: file.stat.ctime, 417 | mtime: file.stat.mtime, 418 | match: match, 419 | matchIndex: match.index ? match.index : -1, 420 | line: numberOfLines, 421 | ch: numberOfChars, 422 | content: content, 423 | template: m.template, 424 | register: { 425 | s: "", 426 | n: 0, 427 | b: false, 428 | }, 429 | }; 430 | results.push(result); 431 | } 432 | } 433 | } 434 | 435 | // Sort 436 | if (argsOrders.fields.length > 0) { 437 | results = _.orderBy( 438 | results, 439 | argsOrders.fields, 440 | // @ts-ignore 441 | argsOrders.orders 442 | ); 443 | } else { 444 | if (this.settings.defaultFields.length > 0) { 445 | const fields = this.settings.defaultFields 446 | .split(",") 447 | .map((val) => val.trim()); 448 | const orders = this.settings.defaultOrders 449 | .split(",") 450 | .map((val) => val.trim()); 451 | const res = checkOrder(fields, orders); 452 | if (res.flag) { 453 | results = _.orderBy( 454 | results, 455 | fields, 456 | // @ts-ignore 457 | orders 458 | ); 459 | } else { 460 | results = _.orderBy( 461 | results, 462 | ["ctime", "line"], 463 | ["asc", "asc"] 464 | ); 465 | } 466 | } else { 467 | results = _.orderBy( 468 | results, 469 | ["ctime", "line"], 470 | ["asc", "asc"] 471 | ); 472 | } 473 | } 474 | if (this.settings.debug) { 475 | console.log( 476 | `[${moment().format( 477 | "YYYY-MM-DD HH:mm:ss" 478 | )}] Aggregator[${execId}]: results\n`, 479 | results 480 | ); 481 | } 482 | 483 | // Create summary 484 | let summaries: string[] = []; 485 | const limitDisplay = !isNaN(Number(args.limitDisplay)) 486 | ? Number(args.limitDisplay) 487 | : this.settings.limitDisplay; 488 | const fileLink = 489 | args.fileLink == null || args.fileLink == undefined 490 | ? this.settings.fileLink 491 | : args.fileLink; 492 | const noCurFile = 493 | args.noCurFile == null || args.noCurFile == undefined 494 | ? this.settings.noCurFile 495 | : args.noCurFile; 496 | const fileIndecator = 497 | args.fileIndecator == null || 498 | args.fileIndecator == undefined 499 | ? this.settings.fileIndecator 500 | : args.fileIndecator; 501 | const register: Register = { s: "", n: 0, b: false }; 502 | for (const result of results) { 503 | if (limitDisplay > 0 && summaries.length > limitDisplay) 504 | break; 505 | let template = result.template; 506 | result.index = summaries.length + 1; 507 | let data: { 508 | result: Result; 509 | summaries: string[]; 510 | register: Register; 511 | } = { result, summaries, register }; 512 | Handlebars.registerHelper("eval", (aString: string) => { 513 | const ret = new Function("data", aString)(data); 514 | return ret == null || ret == undefined ? "" : ret; 515 | }); 516 | let summary = template(data); 517 | if (fileLink) { 518 | if (!(result.path == ctx.sourcePath && noCurFile)) { 519 | let data: { 520 | result: Result; 521 | summaries: string[]; 522 | register: Register; 523 | template: string; 524 | } = { 525 | result, 526 | summaries, 527 | register, 528 | template: summary, 529 | }; 530 | Handlebars.registerHelper( 531 | "eval", 532 | (aString: string) => { 533 | const ret = new Function("data", aString)( 534 | data 535 | ); 536 | return ret == null || ret == undefined 537 | ? "" 538 | : ret; 539 | } 540 | ); 541 | summary = Handlebars.compile(fileIndecator)(data); 542 | } 543 | } 544 | summaries.push(summary); 545 | } 546 | if (this.settings.debug) { 547 | console.log( 548 | `[${moment().format( 549 | "YYYY-MM-DD HH:mm:ss" 550 | )}] Aggregator[${execId}]: summaries\n`, 551 | summaries 552 | ); 553 | } 554 | 555 | const jstr = 556 | args.joinString == null || args.joinString == undefined 557 | ? this.settings.joinString 558 | : args.joinString; 559 | let summary = summaries.join(jstr); 560 | if (this.settings.debug) { 561 | console.log( 562 | `[${moment().format( 563 | "YYYY-MM-DD HH:mm:ss" 564 | )}] Aggregator[${execId}]: summary\n${summary}` 565 | ); 566 | } 567 | if (!(args.decorator == null || args.decorator == undefined)) { 568 | let data: { 569 | templates: string; 570 | summaries: string[]; 571 | register: Register; 572 | } = { 573 | templates: summary, 574 | summaries, 575 | register, 576 | }; 577 | Handlebars.registerHelper("eval", (aString: string) => { 578 | const ret = new Function("data", aString)(data); 579 | return ret == null || ret == undefined ? "" : ret; 580 | }); 581 | summary = Handlebars.compile(args.decorator)(data); 582 | if (this.settings.debug) { 583 | console.log( 584 | `[${moment().format( 585 | "YYYY-MM-DD HH:mm:ss" 586 | )}] Aggregator[${execId}]: decorated summary\n${summary}` 587 | ); 588 | } 589 | } 590 | await this.renderMarkdown(summary, el, ctx); 591 | 592 | // Log overall execution time 593 | const endTime = performance.now(); 594 | const executionTime = endTime - startTime; 595 | if (this.settings.debug) { 596 | console.log( 597 | `[${moment().format( 598 | "YYYY-MM-DD HH:mm:ss" 599 | )}] Aggregator[${execId}]: Execution completed in ${executionTime}ms` 600 | ); 601 | 602 | // Performance advice if execution was slow 603 | if (executionTime > 10000) { 604 | // Over 10 seconds is quite slow 605 | console.log( 606 | `[${moment().format( 607 | "YYYY-MM-DD HH:mm:ss" 608 | )}] Aggregator[${execId}]: ⚠️ Performance recommendations:` 609 | ); 610 | console.log( 611 | `[${moment().format( 612 | "YYYY-MM-DD HH:mm:ss" 613 | )}] Aggregator[${execId}]: 1. Limit the scope to fewer files` 614 | ); 615 | console.log( 616 | `[${moment().format( 617 | "YYYY-MM-DD HH:mm:ss" 618 | )}] Aggregator[${execId}]: 2. Simplify regex patterns to avoid backtracking` 619 | ); 620 | console.log( 621 | `[${moment().format( 622 | "YYYY-MM-DD HH:mm:ss" 623 | )}] Aggregator[${execId}]: 3. Add more specific anchors to your patterns` 624 | ); 625 | console.log( 626 | `[${moment().format( 627 | "YYYY-MM-DD HH:mm:ss" 628 | )}] Aggregator[${execId}]: 4. Consider increasing limitSearch to stop processing earlier` 629 | ); 630 | } 631 | } 632 | } 633 | ); 634 | 635 | // This adds a settings tab so the user can configure various aspects of the plugin 636 | this.addSettingTab(new AggregatorSettingTab(this.app, this)); 637 | console.log( 638 | `[${moment().format( 639 | "YYYY-MM-DD HH:mm:ss" 640 | )}] Aggregator: Plugin loaded` 641 | ); 642 | } 643 | 644 | onunload() {} 645 | 646 | async loadSettings() { 647 | this.settings = Object.assign( 648 | {}, 649 | DEFAULT_SETTINGS, 650 | await this.loadData() 651 | ); 652 | } 653 | 654 | async saveSettings() { 655 | await this.saveData(this.settings); 656 | } 657 | 658 | /** 659 | * Checks if a regex pattern might be problematic for performance 660 | * @param regexStr The regex pattern as a string 661 | * @returns True if the pattern might cause performance issues 662 | */ 663 | private isLikelyProblematicRegex(regexStr: string): boolean { 664 | // Remove regex delimiters and flags 665 | regexStr = regexStr.replace(/^\/|\/[gimuy]*$/g, ""); 666 | 667 | // Check for patterns that might cause catastrophic backtracking 668 | const problematicPatterns = [ 669 | // Nested repetition without proper anchoring 670 | /\([^()]*\+[^()]*\)\+/, 671 | /\([^()]*\*[^()]*\)\+/, 672 | /\([^()]*\+[^()]*\)\*/, 673 | /\([^()]*\*[^()]*\)\*/, 674 | 675 | // Multiple adjacent optional patterns 676 | /\.\*\.\*/, 677 | /\.\+\.\+/, 678 | 679 | // Greedy quantifiers followed by similar content 680 | /\w+\s+\w+/, 681 | 682 | // Complex lookaheads/lookbehinds with repetition 683 | /\(\?=[^)]*\*[^)]*\)/, 684 | /\(\?<=[^)]*\*[^)]*\)/, 685 | 686 | // Extremely long alternatives 687 | /(?:[^|]*\|){10,}/, 688 | ]; 689 | 690 | return problematicPatterns.some((pattern) => pattern.test(regexStr)); 691 | } 692 | 693 | async renderMarkdown( 694 | source: string, 695 | el: HTMLElement, 696 | ctx: MarkdownPostProcessorContext 697 | ) { 698 | let summaryContainer = createEl("div"); 699 | await MarkdownRenderer.render( 700 | this.app, 701 | source, 702 | summaryContainer, 703 | ctx.sourcePath, 704 | this 705 | ); 706 | if (this.settings.debug) { 707 | console.log( 708 | `[${moment().format( 709 | "YYYY-MM-DD HH:mm:ss" 710 | )}] Aggregator: html element\n${summaryContainer.innerHTML}` 711 | ); 712 | } 713 | el.replaceWith(summaryContainer); 714 | } 715 | } 716 | -------------------------------------------------------------------------------- /manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "obsidian-aggregator", 3 | "name": "Aggregator", 4 | "version": "0.3.1", 5 | "minAppVersion": "0.15.0", 6 | "description": "This plugin helps you gather information from files, and make a summary in the file.", 7 | "author": "SErAphLi", 8 | "authorUrl": "https://github.com/Seraphli", 9 | "isDesktopOnly": false 10 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "obsidian-aggregator", 3 | "version": "0.3.1", 4 | "description": "This plugin helps you gather information from files, and make a summary in the file.", 5 | "main": "main.js", 6 | "scripts": { 7 | "dev": "node esbuild.config.mjs", 8 | "build": "tsc -noEmit -skipLibCheck && node esbuild.config.mjs production", 9 | "version": "node version-bump.mjs && git add manifest.json versions.json", 10 | "copyBuild": "rm -rf build/* && cp main.js build/ && cp manifest.json build/ && cp styles.css build/", 11 | "prepublish": "npm run build && npm run copyBuild", 12 | "publish": "npm run build && npm run version && npm run copyBuild" 13 | }, 14 | "keywords": [], 15 | "author": "", 16 | "license": "MIT", 17 | "devDependencies": { 18 | "@types/lodash": "^4.14.191", 19 | "@types/node": "^16.11.6", 20 | "@typescript-eslint/eslint-plugin": "5.29.0", 21 | "@typescript-eslint/parser": "5.29.0", 22 | "builtin-modules": "3.3.0", 23 | "esbuild": "0.14.47", 24 | "obsidian": "^1.4.11", 25 | "tslib": "2.4.0", 26 | "typescript": "4.7.4" 27 | }, 28 | "dependencies": { 29 | "handlebars": "^4.7.7", 30 | "lodash": "^4.17.21" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /settings.ts: -------------------------------------------------------------------------------- 1 | import { App, PluginSettingTab, Setting } from "obsidian"; 2 | import Aggregator from "main"; 3 | 4 | export interface AggregatorSettings { 5 | fileLink: boolean; 6 | noCurFile: boolean; 7 | defaultFields: string; 8 | defaultOrders: string; 9 | fileIndecator: string; 10 | joinString: string; 11 | limitSearch: number; 12 | limitDisplay: number; 13 | excludedRegex: string; 14 | debug: boolean; 15 | } 16 | 17 | export const DEFAULT_SETTINGS: AggregatorSettings = { 18 | fileLink: true, 19 | noCurFile: true, 20 | defaultFields: "ctime, index", 21 | defaultOrders: "asc, asc", 22 | fileIndecator: "ID {{result.index}}, From [[{{result.path}}]]\n{{template}}", 23 | joinString: "\n", 24 | limitSearch: 100, 25 | limitDisplay: -1, 26 | excludedRegex: "", 27 | debug: false, 28 | }; 29 | 30 | export class AggregatorSettingTab extends PluginSettingTab { 31 | plugin: Aggregator; 32 | 33 | constructor(app: App, plugin: Aggregator) { 34 | super(app, plugin); 35 | this.plugin = plugin; 36 | } 37 | 38 | display(): void { 39 | const { containerEl } = this; 40 | 41 | containerEl.empty(); 42 | 43 | containerEl.createEl("h2", { text: "Aggregator" }); 44 | 45 | containerEl.createEl("h3", { text: "User Option" }); 46 | 47 | new Setting(containerEl) 48 | .setName("Append File Link") 49 | .setDesc("Append file link at the end of blocks.") 50 | .addToggle((value) => { 51 | value 52 | .setValue(this.plugin.settings.fileLink) 53 | .onChange((value) => { 54 | this.plugin.settings.fileLink = value; 55 | this.plugin.saveSettings(); 56 | }); 57 | }); 58 | new Setting(containerEl) 59 | .setName("No Current File") 60 | .setDesc("Don't Append file link when aggregating in current file.") 61 | .addToggle((value) => { 62 | value 63 | .setValue(this.plugin.settings.noCurFile) 64 | .onChange((value) => { 65 | this.plugin.settings.noCurFile = value; 66 | this.plugin.saveSettings(); 67 | }); 68 | }); 69 | new Setting(containerEl) 70 | .setName("Default Fields") 71 | .setDesc("Default fields for sorting.") 72 | .addText((value) => { 73 | value 74 | .setValue(this.plugin.settings.defaultFields) 75 | .onChange((value) => { 76 | this.plugin.settings.defaultFields = value; 77 | this.plugin.saveSettings(); 78 | }); 79 | }); 80 | new Setting(containerEl) 81 | .setName("Default Orders") 82 | .setDesc("Default orders for sorting.") 83 | .addText((value) => { 84 | value 85 | .setValue(this.plugin.settings.defaultOrders) 86 | .onChange((value) => { 87 | this.plugin.settings.defaultOrders = value; 88 | this.plugin.saveSettings(); 89 | }); 90 | }); 91 | new Setting(containerEl) 92 | .setName("File Indicator") 93 | .setDesc( 94 | "Indicate which file the result is from. It will be add at the beginning of the result." 95 | ) 96 | .addTextArea((value) => { 97 | value 98 | .setValue(this.plugin.settings.fileIndecator) 99 | .onChange((value) => { 100 | this.plugin.settings.fileIndecator = value; 101 | this.plugin.saveSettings(); 102 | }); 103 | }); 104 | new Setting(containerEl) 105 | .setName("Join String") 106 | .setDesc("String for joining all results in the summary.") 107 | .addTextArea((value) => { 108 | value 109 | .setValue(this.plugin.settings.joinString) 110 | .onChange((value) => { 111 | this.plugin.settings.joinString = value; 112 | this.plugin.saveSettings(); 113 | }); 114 | }); 115 | new Setting(containerEl) 116 | .setName("Found Result Limitation") 117 | .setDesc( 118 | "Limit the number of found results when searching in the summary. Set the number larger than 0 to enable." 119 | ) 120 | .addText((value) => { 121 | value 122 | .setValue(String(this.plugin.settings.limitSearch)) 123 | .onChange((value) => { 124 | if (!isNaN(Number(value))) { 125 | this.plugin.settings.limitSearch = Number(value); 126 | this.plugin.saveSettings(); 127 | } 128 | }); 129 | }); 130 | new Setting(containerEl) 131 | .setName("Display Result Limitation") 132 | .setDesc( 133 | "Limit the number of results when displaying in the summary. Set the number larger than 0 to enable." 134 | ) 135 | .addText((value) => { 136 | value 137 | .setValue(String(this.plugin.settings.limitDisplay)) 138 | .onChange((value) => { 139 | if (!isNaN(Number(value))) { 140 | this.plugin.settings.limitDisplay = Number(value); 141 | this.plugin.saveSettings(); 142 | } 143 | }); 144 | }); 145 | new Setting(containerEl) 146 | .setName("Excluded regex") 147 | .setDesc( 148 | "The matched result of this regex will be excluded from searching content." 149 | ) 150 | .addText((value) => { 151 | value 152 | .setValue(this.plugin.settings.excludedRegex) 153 | .onChange((value) => { 154 | this.plugin.settings.excludedRegex = value; 155 | this.plugin.saveSettings(); 156 | }); 157 | }); 158 | 159 | containerEl.createEl("h3", { text: "Dev Option" }); 160 | 161 | new Setting(containerEl) 162 | .setName("Debug") 163 | .setDesc("Enable debug mode.") 164 | .addToggle((value) => { 165 | value.setValue(this.plugin.settings.debug).onChange((value) => { 166 | this.plugin.settings.debug = value; 167 | this.plugin.saveSettings(); 168 | }); 169 | }); 170 | } 171 | } 172 | -------------------------------------------------------------------------------- /styles.css: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | This CSS file will be included with your plugin, and 4 | available in the app when your plugin is enabled. 5 | 6 | If your plugin does not need CSS, delete this file. 7 | 8 | */ 9 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": ".", 4 | "inlineSourceMap": true, 5 | "inlineSources": true, 6 | "module": "ESNext", 7 | "target": "ES6", 8 | "allowJs": true, 9 | "noImplicitAny": true, 10 | "moduleResolution": "node", 11 | "importHelpers": true, 12 | "isolatedModules": true, 13 | "strictNullChecks": true, 14 | "esModuleInterop": true, 15 | "lib": [ 16 | "DOM", 17 | "ES5", 18 | "ES6", 19 | "ES7" 20 | ] 21 | }, 22 | "include": [ 23 | "**/*.ts" 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /version-bump.mjs: -------------------------------------------------------------------------------- 1 | import { readFileSync, writeFileSync } from "fs"; 2 | 3 | const targetVersion = process.env.npm_package_version; 4 | 5 | // read minAppVersion from manifest.json and bump version to target version 6 | let manifest = JSON.parse(readFileSync("manifest.json", "utf8")); 7 | const { minAppVersion } = manifest; 8 | manifest.version = targetVersion; 9 | writeFileSync("manifest.json", JSON.stringify(manifest, null, "\t")); 10 | 11 | // update versions.json with target version and minAppVersion from manifest.json 12 | let versions = JSON.parse(readFileSync("versions.json", "utf8")); 13 | versions[targetVersion] = minAppVersion; 14 | writeFileSync("versions.json", JSON.stringify(versions, null, "\t")); 15 | -------------------------------------------------------------------------------- /versions.json: -------------------------------------------------------------------------------- 1 | { 2 | "0.1.0": "0.15.0", 3 | "0.1.1": "0.15.0", 4 | "0.1.2": "0.15.0", 5 | "0.1.3": "0.15.0", 6 | "0.1.4": "0.15.0", 7 | "0.2.4": "0.15.0", 8 | "0.3.0": "0.15.0", 9 | "0.3.1": "0.15.0" 10 | } -------------------------------------------------------------------------------- /yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | "@nodelib/fs.scandir@2.1.5": 6 | version "2.1.5" 7 | resolved "https://registry.npmmirror.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" 8 | integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g== 9 | dependencies: 10 | "@nodelib/fs.stat" "2.0.5" 11 | run-parallel "^1.1.9" 12 | 13 | "@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2": 14 | version "2.0.5" 15 | resolved "https://registry.npmmirror.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b" 16 | integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== 17 | 18 | "@nodelib/fs.walk@^1.2.3": 19 | version "1.2.8" 20 | resolved "https://registry.npmmirror.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a" 21 | integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== 22 | dependencies: 23 | "@nodelib/fs.scandir" "2.1.5" 24 | fastq "^1.6.0" 25 | 26 | "@types/codemirror@5.60.8": 27 | version "5.60.8" 28 | resolved "https://registry.yarnpkg.com/@types/codemirror/-/codemirror-5.60.8.tgz#b647d04b470e8e1836dd84b2879988fc55c9de68" 29 | integrity sha512-VjFgDF/eB+Aklcy15TtOTLQeMjTo07k7KAjql8OK5Dirr7a6sJY4T1uVBDuTVG9VEmn1uUsohOpYnVfgC6/jyw== 30 | dependencies: 31 | "@types/tern" "*" 32 | 33 | "@types/estree@*": 34 | version "1.0.5" 35 | resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.5.tgz#a6ce3e556e00fd9895dd872dd172ad0d4bd687f4" 36 | integrity sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw== 37 | 38 | "@types/json-schema@^7.0.9": 39 | version "7.0.11" 40 | resolved "https://registry.npmmirror.com/@types/json-schema/-/json-schema-7.0.11.tgz#d421b6c527a3037f7c84433fd2c4229e016863d3" 41 | integrity sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ== 42 | 43 | "@types/lodash@^4.14.191": 44 | version "4.14.191" 45 | resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.191.tgz#09511e7f7cba275acd8b419ddac8da9a6a79e2fa" 46 | integrity sha512-BdZ5BCCvho3EIXw6wUCXHe7rS53AIDPLE+JzwgT+OsJk53oBfbSmZZ7CX4VaRoN78N+TJpFi9QPlfIVNmJYWxQ== 47 | 48 | "@types/node@^16.11.6": 49 | version "16.18.3" 50 | resolved "https://registry.npmmirror.com/@types/node/-/node-16.18.3.tgz#d7f7ba828ad9e540270f01ce00d391c54e6e0abc" 51 | integrity sha512-jh6m0QUhIRcZpNv7Z/rpN+ZWXOicUUQbSoWks7Htkbb9IjFQj4kzcX/xFCkjstCj5flMsN8FiSvt+q+Tcs4Llg== 52 | 53 | "@types/tern@*": 54 | version "0.23.7" 55 | resolved "https://registry.yarnpkg.com/@types/tern/-/tern-0.23.7.tgz#f3c27fb7d6db5e86d7f069e7d5124bfa677a5b2d" 56 | integrity sha512-0YS9XCZ0LAhlP11HV9SqncUYyz9Ggsgc7Om/AmchKvoeFyj0qPaJmX6rJ93mJVExizWDzUMb49gAtVpI1uHd8Q== 57 | dependencies: 58 | "@types/estree" "*" 59 | 60 | "@typescript-eslint/eslint-plugin@5.29.0": 61 | version "5.29.0" 62 | resolved "https://registry.npmmirror.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.29.0.tgz#c67794d2b0fd0b4a47f50266088acdc52a08aab6" 63 | integrity sha512-kgTsISt9pM53yRFQmLZ4npj99yGl3x3Pl7z4eA66OuTzAGC4bQB5H5fuLwPnqTKU3yyrrg4MIhjF17UYnL4c0w== 64 | dependencies: 65 | "@typescript-eslint/scope-manager" "5.29.0" 66 | "@typescript-eslint/type-utils" "5.29.0" 67 | "@typescript-eslint/utils" "5.29.0" 68 | debug "^4.3.4" 69 | functional-red-black-tree "^1.0.1" 70 | ignore "^5.2.0" 71 | regexpp "^3.2.0" 72 | semver "^7.3.7" 73 | tsutils "^3.21.0" 74 | 75 | "@typescript-eslint/parser@5.29.0": 76 | version "5.29.0" 77 | resolved "https://registry.npmmirror.com/@typescript-eslint/parser/-/parser-5.29.0.tgz#41314b195b34d44ff38220caa55f3f93cfca43cf" 78 | integrity sha512-ruKWTv+x0OOxbzIw9nW5oWlUopvP/IQDjB5ZqmTglLIoDTctLlAJpAQFpNPJP/ZI7hTT9sARBosEfaKbcFuECw== 79 | dependencies: 80 | "@typescript-eslint/scope-manager" "5.29.0" 81 | "@typescript-eslint/types" "5.29.0" 82 | "@typescript-eslint/typescript-estree" "5.29.0" 83 | debug "^4.3.4" 84 | 85 | "@typescript-eslint/scope-manager@5.29.0": 86 | version "5.29.0" 87 | resolved "https://registry.npmmirror.com/@typescript-eslint/scope-manager/-/scope-manager-5.29.0.tgz#2a6a32e3416cb133e9af8dcf54bf077a916aeed3" 88 | integrity sha512-etbXUT0FygFi2ihcxDZjz21LtC+Eps9V2xVx09zFoN44RRHPrkMflidGMI+2dUs821zR1tDS6Oc9IXxIjOUZwA== 89 | dependencies: 90 | "@typescript-eslint/types" "5.29.0" 91 | "@typescript-eslint/visitor-keys" "5.29.0" 92 | 93 | "@typescript-eslint/type-utils@5.29.0": 94 | version "5.29.0" 95 | resolved "https://registry.npmmirror.com/@typescript-eslint/type-utils/-/type-utils-5.29.0.tgz#241918001d164044020b37d26d5b9f4e37cc3d5d" 96 | integrity sha512-JK6bAaaiJozbox3K220VRfCzLa9n0ib/J+FHIwnaV3Enw/TO267qe0pM1b1QrrEuy6xun374XEAsRlA86JJnyg== 97 | dependencies: 98 | "@typescript-eslint/utils" "5.29.0" 99 | debug "^4.3.4" 100 | tsutils "^3.21.0" 101 | 102 | "@typescript-eslint/types@5.29.0": 103 | version "5.29.0" 104 | resolved "https://registry.npmmirror.com/@typescript-eslint/types/-/types-5.29.0.tgz#7861d3d288c031703b2d97bc113696b4d8c19aab" 105 | integrity sha512-X99VbqvAXOMdVyfFmksMy3u8p8yoRGITgU1joBJPzeYa0rhdf5ok9S56/itRoUSh99fiDoMtarSIJXo7H/SnOg== 106 | 107 | "@typescript-eslint/typescript-estree@5.29.0": 108 | version "5.29.0" 109 | resolved "https://registry.npmmirror.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.29.0.tgz#e83d19aa7fd2e74616aab2f25dfbe4de4f0b5577" 110 | integrity sha512-mQvSUJ/JjGBdvo+1LwC+GY2XmSYjK1nAaVw2emp/E61wEVYEyibRHCqm1I1vEKbXCpUKuW4G7u9ZCaZhJbLoNQ== 111 | dependencies: 112 | "@typescript-eslint/types" "5.29.0" 113 | "@typescript-eslint/visitor-keys" "5.29.0" 114 | debug "^4.3.4" 115 | globby "^11.1.0" 116 | is-glob "^4.0.3" 117 | semver "^7.3.7" 118 | tsutils "^3.21.0" 119 | 120 | "@typescript-eslint/utils@5.29.0": 121 | version "5.29.0" 122 | resolved "https://registry.npmmirror.com/@typescript-eslint/utils/-/utils-5.29.0.tgz#775046effd5019667bd086bcf326acbe32cd0082" 123 | integrity sha512-3Eos6uP1nyLOBayc/VUdKZikV90HahXE5Dx9L5YlSd/7ylQPXhLk1BYb29SDgnBnTp+jmSZUU0QxUiyHgW4p7A== 124 | dependencies: 125 | "@types/json-schema" "^7.0.9" 126 | "@typescript-eslint/scope-manager" "5.29.0" 127 | "@typescript-eslint/types" "5.29.0" 128 | "@typescript-eslint/typescript-estree" "5.29.0" 129 | eslint-scope "^5.1.1" 130 | eslint-utils "^3.0.0" 131 | 132 | "@typescript-eslint/visitor-keys@5.29.0": 133 | version "5.29.0" 134 | resolved "https://registry.npmmirror.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.29.0.tgz#7a4749fa7ef5160c44a451bf060ac1dc6dfb77ee" 135 | integrity sha512-Hpb/mCWsjILvikMQoZIE3voc9wtQcS0A9FUw3h8bhr9UxBdtI/tw1ZDZUOXHXLOVMedKCH5NxyzATwnU78bWCQ== 136 | dependencies: 137 | "@typescript-eslint/types" "5.29.0" 138 | eslint-visitor-keys "^3.3.0" 139 | 140 | array-union@^2.1.0: 141 | version "2.1.0" 142 | resolved "https://registry.npmmirror.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" 143 | integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== 144 | 145 | braces@^3.0.2: 146 | version "3.0.2" 147 | resolved "https://registry.npmmirror.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" 148 | integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== 149 | dependencies: 150 | fill-range "^7.0.1" 151 | 152 | builtin-modules@3.3.0: 153 | version "3.3.0" 154 | resolved "https://registry.npmmirror.com/builtin-modules/-/builtin-modules-3.3.0.tgz#cae62812b89801e9656336e46223e030386be7b6" 155 | integrity sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw== 156 | 157 | debug@^4.3.4: 158 | version "4.3.4" 159 | resolved "https://registry.npmmirror.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" 160 | integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== 161 | dependencies: 162 | ms "2.1.2" 163 | 164 | dir-glob@^3.0.1: 165 | version "3.0.1" 166 | resolved "https://registry.npmmirror.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f" 167 | integrity sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA== 168 | dependencies: 169 | path-type "^4.0.0" 170 | 171 | esbuild-android-64@0.14.47: 172 | version "0.14.47" 173 | resolved "https://registry.npmmirror.com/esbuild-android-64/-/esbuild-android-64-0.14.47.tgz#ef95b42c67bcf4268c869153fa3ad1466c4cea6b" 174 | integrity sha512-R13Bd9+tqLVFndncMHssZrPWe6/0Kpv2/dt4aA69soX4PRxlzsVpCvoJeFE8sOEoeVEiBkI0myjlkDodXlHa0g== 175 | 176 | esbuild-android-arm64@0.14.47: 177 | version "0.14.47" 178 | resolved "https://registry.npmmirror.com/esbuild-android-arm64/-/esbuild-android-arm64-0.14.47.tgz#4ebd7ce9fb250b4695faa3ee46fd3b0754ecd9e6" 179 | integrity sha512-OkwOjj7ts4lBp/TL6hdd8HftIzOy/pdtbrNA4+0oVWgGG64HrdVzAF5gxtJufAPOsEjkyh1oIYvKAUinKKQRSQ== 180 | 181 | esbuild-darwin-64@0.14.47: 182 | version "0.14.47" 183 | resolved "https://registry.npmmirror.com/esbuild-darwin-64/-/esbuild-darwin-64-0.14.47.tgz#e0da6c244f497192f951807f003f6a423ed23188" 184 | integrity sha512-R6oaW0y5/u6Eccti/TS6c/2c1xYTb1izwK3gajJwi4vIfNs1s8B1dQzI1UiC9T61YovOQVuePDcfqHLT3mUZJA== 185 | 186 | esbuild-darwin-arm64@0.14.47: 187 | version "0.14.47" 188 | resolved "https://registry.npmmirror.com/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.47.tgz#cd40fd49a672fca581ed202834239dfe540a9028" 189 | integrity sha512-seCmearlQyvdvM/noz1L9+qblC5vcBrhUaOoLEDDoLInF/VQ9IkobGiLlyTPYP5dW1YD4LXhtBgOyevoIHGGnw== 190 | 191 | esbuild-freebsd-64@0.14.47: 192 | version "0.14.47" 193 | resolved "https://registry.npmmirror.com/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.47.tgz#8da6a14c095b29c01fc8087a16cb7906debc2d67" 194 | integrity sha512-ZH8K2Q8/Ux5kXXvQMDsJcxvkIwut69KVrYQhza/ptkW50DC089bCVrJZZ3sKzIoOx+YPTrmsZvqeZERjyYrlvQ== 195 | 196 | esbuild-freebsd-arm64@0.14.47: 197 | version "0.14.47" 198 | resolved "https://registry.npmmirror.com/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.47.tgz#ad31f9c92817ff8f33fd253af7ab5122dc1b83f6" 199 | integrity sha512-ZJMQAJQsIOhn3XTm7MPQfCzEu5b9STNC+s90zMWe2afy9EwnHV7Ov7ohEMv2lyWlc2pjqLW8QJnz2r0KZmeAEQ== 200 | 201 | esbuild-linux-32@0.14.47: 202 | version "0.14.47" 203 | resolved "https://registry.npmmirror.com/esbuild-linux-32/-/esbuild-linux-32-0.14.47.tgz#de085e4db2e692ea30c71208ccc23fdcf5196c58" 204 | integrity sha512-FxZOCKoEDPRYvq300lsWCTv1kcHgiiZfNrPtEhFAiqD7QZaXrad8LxyJ8fXGcWzIFzRiYZVtB3ttvITBvAFhKw== 205 | 206 | esbuild-linux-64@0.14.47: 207 | version "0.14.47" 208 | resolved "https://registry.npmmirror.com/esbuild-linux-64/-/esbuild-linux-64-0.14.47.tgz#2a9321bbccb01f01b04cebfcfccbabeba3658ba1" 209 | integrity sha512-nFNOk9vWVfvWYF9YNYksZptgQAdstnDCMtR6m42l5Wfugbzu11VpMCY9XrD4yFxvPo9zmzcoUL/88y0lfJZJJw== 210 | 211 | esbuild-linux-arm64@0.14.47: 212 | version "0.14.47" 213 | resolved "https://registry.npmmirror.com/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.47.tgz#b9da7b6fc4b0ca7a13363a0c5b7bb927e4bc535a" 214 | integrity sha512-ywfme6HVrhWcevzmsufjd4iT3PxTfCX9HOdxA7Hd+/ZM23Y9nXeb+vG6AyA6jgq/JovkcqRHcL9XwRNpWG6XRw== 215 | 216 | esbuild-linux-arm@0.14.47: 217 | version "0.14.47" 218 | resolved "https://registry.npmmirror.com/esbuild-linux-arm/-/esbuild-linux-arm-0.14.47.tgz#56fec2a09b9561c337059d4af53625142aded853" 219 | integrity sha512-ZGE1Bqg/gPRXrBpgpvH81tQHpiaGxa8c9Rx/XOylkIl2ypLuOcawXEAo8ls+5DFCcRGt/o3sV+PzpAFZobOsmA== 220 | 221 | esbuild-linux-mips64le@0.14.47: 222 | version "0.14.47" 223 | resolved "https://registry.npmmirror.com/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.47.tgz#9db21561f8f22ed79ef2aedb7bbef082b46cf823" 224 | integrity sha512-mg3D8YndZ1LvUiEdDYR3OsmeyAew4MA/dvaEJxvyygahWmpv1SlEEnhEZlhPokjsUMfRagzsEF/d/2XF+kTQGg== 225 | 226 | esbuild-linux-ppc64le@0.14.47: 227 | version "0.14.47" 228 | resolved "https://registry.npmmirror.com/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.47.tgz#dc3a3da321222b11e96e50efafec9d2de408198b" 229 | integrity sha512-WER+f3+szmnZiWoK6AsrTKGoJoErG2LlauSmk73LEZFQ/iWC+KhhDsOkn1xBUpzXWsxN9THmQFltLoaFEH8F8w== 230 | 231 | esbuild-linux-riscv64@0.14.47: 232 | version "0.14.47" 233 | resolved "https://registry.npmmirror.com/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.14.47.tgz#9bd6dcd3dca6c0357084ecd06e1d2d4bf105335f" 234 | integrity sha512-1fI6bP3A3rvI9BsaaXbMoaOjLE3lVkJtLxsgLHqlBhLlBVY7UqffWBvkrX/9zfPhhVMd9ZRFiaqXnB1T7BsL2g== 235 | 236 | esbuild-linux-s390x@0.14.47: 237 | version "0.14.47" 238 | resolved "https://registry.npmmirror.com/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.47.tgz#a458af939b52f2cd32fc561410d441a51f69d41f" 239 | integrity sha512-eZrWzy0xFAhki1CWRGnhsHVz7IlSKX6yT2tj2Eg8lhAwlRE5E96Hsb0M1mPSE1dHGpt1QVwwVivXIAacF/G6mw== 240 | 241 | esbuild-netbsd-64@0.14.47: 242 | version "0.14.47" 243 | resolved "https://registry.npmmirror.com/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.47.tgz#6388e785d7e7e4420cb01348d7483ab511b16aa8" 244 | integrity sha512-Qjdjr+KQQVH5Q2Q1r6HBYswFTToPpss3gqCiSw2Fpq/ua8+eXSQyAMG+UvULPqXceOwpnPo4smyZyHdlkcPppQ== 245 | 246 | esbuild-openbsd-64@0.14.47: 247 | version "0.14.47" 248 | resolved "https://registry.npmmirror.com/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.47.tgz#309af806db561aa886c445344d1aacab850dbdc5" 249 | integrity sha512-QpgN8ofL7B9z8g5zZqJE+eFvD1LehRlxr25PBkjyyasakm4599iroUpaj96rdqRlO2ShuyqwJdr+oNqWwTUmQw== 250 | 251 | esbuild-sunos-64@0.14.47: 252 | version "0.14.47" 253 | resolved "https://registry.npmmirror.com/esbuild-sunos-64/-/esbuild-sunos-64-0.14.47.tgz#3f19612dcdb89ba6c65283a7ff6e16f8afbf8aaa" 254 | integrity sha512-uOeSgLUwukLioAJOiGYm3kNl+1wJjgJA8R671GYgcPgCx7QR73zfvYqXFFcIO93/nBdIbt5hd8RItqbbf3HtAQ== 255 | 256 | esbuild-windows-32@0.14.47: 257 | version "0.14.47" 258 | resolved "https://registry.npmmirror.com/esbuild-windows-32/-/esbuild-windows-32-0.14.47.tgz#a92d279c8458d5dc319abcfeb30aa49e8f2e6f7f" 259 | integrity sha512-H0fWsLTp2WBfKLBgwYT4OTfFly4Im/8B5f3ojDv1Kx//kiubVY0IQunP2Koc/fr/0wI7hj3IiBDbSrmKlrNgLQ== 260 | 261 | esbuild-windows-64@0.14.47: 262 | version "0.14.47" 263 | resolved "https://registry.npmmirror.com/esbuild-windows-64/-/esbuild-windows-64-0.14.47.tgz#2564c3fcf0c23d701edb71af8c52d3be4cec5f8a" 264 | integrity sha512-/Pk5jIEH34T68r8PweKRi77W49KwanZ8X6lr3vDAtOlH5EumPE4pBHqkCUdELanvsT14yMXLQ/C/8XPi1pAtkQ== 265 | 266 | esbuild-windows-arm64@0.14.47: 267 | version "0.14.47" 268 | resolved "https://registry.npmmirror.com/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.47.tgz#86d9db1a22d83360f726ac5fba41c2f625db6878" 269 | integrity sha512-HFSW2lnp62fl86/qPQlqw6asIwCnEsEoNIL1h2uVMgakddf+vUuMcCbtUY1i8sst7KkgHrVKCJQB33YhhOweCQ== 270 | 271 | esbuild@0.14.47: 272 | version "0.14.47" 273 | resolved "https://registry.npmmirror.com/esbuild/-/esbuild-0.14.47.tgz#0d6415f6bd8eb9e73a58f7f9ae04c5276cda0e4d" 274 | integrity sha512-wI4ZiIfFxpkuxB8ju4MHrGwGLyp1+awEHAHVpx6w7a+1pmYIq8T9FGEVVwFo0iFierDoMj++Xq69GXWYn2EiwA== 275 | optionalDependencies: 276 | esbuild-android-64 "0.14.47" 277 | esbuild-android-arm64 "0.14.47" 278 | esbuild-darwin-64 "0.14.47" 279 | esbuild-darwin-arm64 "0.14.47" 280 | esbuild-freebsd-64 "0.14.47" 281 | esbuild-freebsd-arm64 "0.14.47" 282 | esbuild-linux-32 "0.14.47" 283 | esbuild-linux-64 "0.14.47" 284 | esbuild-linux-arm "0.14.47" 285 | esbuild-linux-arm64 "0.14.47" 286 | esbuild-linux-mips64le "0.14.47" 287 | esbuild-linux-ppc64le "0.14.47" 288 | esbuild-linux-riscv64 "0.14.47" 289 | esbuild-linux-s390x "0.14.47" 290 | esbuild-netbsd-64 "0.14.47" 291 | esbuild-openbsd-64 "0.14.47" 292 | esbuild-sunos-64 "0.14.47" 293 | esbuild-windows-32 "0.14.47" 294 | esbuild-windows-64 "0.14.47" 295 | esbuild-windows-arm64 "0.14.47" 296 | 297 | eslint-scope@^5.1.1: 298 | version "5.1.1" 299 | resolved "https://registry.npmmirror.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" 300 | integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw== 301 | dependencies: 302 | esrecurse "^4.3.0" 303 | estraverse "^4.1.1" 304 | 305 | eslint-utils@^3.0.0: 306 | version "3.0.0" 307 | resolved "https://registry.npmmirror.com/eslint-utils/-/eslint-utils-3.0.0.tgz#8aebaface7345bb33559db0a1f13a1d2d48c3672" 308 | integrity sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA== 309 | dependencies: 310 | eslint-visitor-keys "^2.0.0" 311 | 312 | eslint-visitor-keys@^2.0.0: 313 | version "2.1.0" 314 | resolved "https://registry.npmmirror.com/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz#f65328259305927392c938ed44eb0a5c9b2bd303" 315 | integrity sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw== 316 | 317 | eslint-visitor-keys@^3.3.0: 318 | version "3.3.0" 319 | resolved "https://registry.npmmirror.com/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz#f6480fa6b1f30efe2d1968aa8ac745b862469826" 320 | integrity sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA== 321 | 322 | esrecurse@^4.3.0: 323 | version "4.3.0" 324 | resolved "https://registry.npmmirror.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" 325 | integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== 326 | dependencies: 327 | estraverse "^5.2.0" 328 | 329 | estraverse@^4.1.1: 330 | version "4.3.0" 331 | resolved "https://registry.npmmirror.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" 332 | integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== 333 | 334 | estraverse@^5.2.0: 335 | version "5.3.0" 336 | resolved "https://registry.npmmirror.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" 337 | integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== 338 | 339 | fast-glob@^3.2.9: 340 | version "3.2.12" 341 | resolved "https://registry.npmmirror.com/fast-glob/-/fast-glob-3.2.12.tgz#7f39ec99c2e6ab030337142da9e0c18f37afae80" 342 | integrity sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w== 343 | dependencies: 344 | "@nodelib/fs.stat" "^2.0.2" 345 | "@nodelib/fs.walk" "^1.2.3" 346 | glob-parent "^5.1.2" 347 | merge2 "^1.3.0" 348 | micromatch "^4.0.4" 349 | 350 | fastq@^1.6.0: 351 | version "1.13.0" 352 | resolved "https://registry.npmmirror.com/fastq/-/fastq-1.13.0.tgz#616760f88a7526bdfc596b7cab8c18938c36b98c" 353 | integrity sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw== 354 | dependencies: 355 | reusify "^1.0.4" 356 | 357 | fill-range@^7.0.1: 358 | version "7.0.1" 359 | resolved "https://registry.npmmirror.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" 360 | integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== 361 | dependencies: 362 | to-regex-range "^5.0.1" 363 | 364 | functional-red-black-tree@^1.0.1: 365 | version "1.0.1" 366 | resolved "https://registry.npmmirror.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" 367 | integrity sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g== 368 | 369 | glob-parent@^5.1.2: 370 | version "5.1.2" 371 | resolved "https://registry.npmmirror.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" 372 | integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== 373 | dependencies: 374 | is-glob "^4.0.1" 375 | 376 | globby@^11.1.0: 377 | version "11.1.0" 378 | resolved "https://registry.npmmirror.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b" 379 | integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g== 380 | dependencies: 381 | array-union "^2.1.0" 382 | dir-glob "^3.0.1" 383 | fast-glob "^3.2.9" 384 | ignore "^5.2.0" 385 | merge2 "^1.4.1" 386 | slash "^3.0.0" 387 | 388 | handlebars@^4.7.7: 389 | version "4.7.7" 390 | resolved "https://registry.npmmirror.com/handlebars/-/handlebars-4.7.7.tgz#9ce33416aad02dbd6c8fafa8240d5d98004945a1" 391 | integrity sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA== 392 | dependencies: 393 | minimist "^1.2.5" 394 | neo-async "^2.6.0" 395 | source-map "^0.6.1" 396 | wordwrap "^1.0.0" 397 | optionalDependencies: 398 | uglify-js "^3.1.4" 399 | 400 | ignore@^5.2.0: 401 | version "5.2.0" 402 | resolved "https://registry.npmmirror.com/ignore/-/ignore-5.2.0.tgz#6d3bac8fa7fe0d45d9f9be7bac2fc279577e345a" 403 | integrity sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ== 404 | 405 | is-extglob@^2.1.1: 406 | version "2.1.1" 407 | resolved "https://registry.npmmirror.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" 408 | integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== 409 | 410 | is-glob@^4.0.1, is-glob@^4.0.3: 411 | version "4.0.3" 412 | resolved "https://registry.npmmirror.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" 413 | integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== 414 | dependencies: 415 | is-extglob "^2.1.1" 416 | 417 | is-number@^7.0.0: 418 | version "7.0.0" 419 | resolved "https://registry.npmmirror.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" 420 | integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== 421 | 422 | lodash@^4.17.21: 423 | version "4.17.21" 424 | resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" 425 | integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== 426 | 427 | lru-cache@^6.0.0: 428 | version "6.0.0" 429 | resolved "https://registry.npmmirror.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" 430 | integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== 431 | dependencies: 432 | yallist "^4.0.0" 433 | 434 | merge2@^1.3.0, merge2@^1.4.1: 435 | version "1.4.1" 436 | resolved "https://registry.npmmirror.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" 437 | integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== 438 | 439 | micromatch@^4.0.4: 440 | version "4.0.5" 441 | resolved "https://registry.npmmirror.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6" 442 | integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA== 443 | dependencies: 444 | braces "^3.0.2" 445 | picomatch "^2.3.1" 446 | 447 | minimist@^1.2.5: 448 | version "1.2.7" 449 | resolved "https://registry.npmmirror.com/minimist/-/minimist-1.2.7.tgz#daa1c4d91f507390437c6a8bc01078e7000c4d18" 450 | integrity sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g== 451 | 452 | moment@2.29.4: 453 | version "2.29.4" 454 | resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.4.tgz#3dbe052889fe7c1b2ed966fcb3a77328964ef108" 455 | integrity sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w== 456 | 457 | ms@2.1.2: 458 | version "2.1.2" 459 | resolved "https://registry.npmmirror.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" 460 | integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== 461 | 462 | neo-async@^2.6.0: 463 | version "2.6.2" 464 | resolved "https://registry.npmmirror.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" 465 | integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== 466 | 467 | obsidian@^1.4.11: 468 | version "1.4.11" 469 | resolved "https://registry.yarnpkg.com/obsidian/-/obsidian-1.4.11.tgz#5cba594c83a74ebad58b630c610265018abdadaa" 470 | integrity sha512-BCVYTvaXxElJMl6MMbDdY/CGK+aq18SdtDY/7vH8v6BxCBQ6KF4kKxL0vG9UZ0o5qh139KpUoJHNm+6O5dllKA== 471 | dependencies: 472 | "@types/codemirror" "5.60.8" 473 | moment "2.29.4" 474 | 475 | path-type@^4.0.0: 476 | version "4.0.0" 477 | resolved "https://registry.npmmirror.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" 478 | integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== 479 | 480 | picomatch@^2.3.1: 481 | version "2.3.1" 482 | resolved "https://registry.npmmirror.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" 483 | integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== 484 | 485 | queue-microtask@^1.2.2: 486 | version "1.2.3" 487 | resolved "https://registry.npmmirror.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" 488 | integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== 489 | 490 | regexpp@^3.2.0: 491 | version "3.2.0" 492 | resolved "https://registry.npmmirror.com/regexpp/-/regexpp-3.2.0.tgz#0425a2768d8f23bad70ca4b90461fa2f1213e1b2" 493 | integrity sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg== 494 | 495 | reusify@^1.0.4: 496 | version "1.0.4" 497 | resolved "https://registry.npmmirror.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" 498 | integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== 499 | 500 | run-parallel@^1.1.9: 501 | version "1.2.0" 502 | resolved "https://registry.npmmirror.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" 503 | integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA== 504 | dependencies: 505 | queue-microtask "^1.2.2" 506 | 507 | semver@^7.3.7: 508 | version "7.3.8" 509 | resolved "https://registry.npmmirror.com/semver/-/semver-7.3.8.tgz#07a78feafb3f7b32347d725e33de7e2a2df67798" 510 | integrity sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A== 511 | dependencies: 512 | lru-cache "^6.0.0" 513 | 514 | slash@^3.0.0: 515 | version "3.0.0" 516 | resolved "https://registry.npmmirror.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" 517 | integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== 518 | 519 | source-map@^0.6.1: 520 | version "0.6.1" 521 | resolved "https://registry.npmmirror.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" 522 | integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== 523 | 524 | to-regex-range@^5.0.1: 525 | version "5.0.1" 526 | resolved "https://registry.npmmirror.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" 527 | integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== 528 | dependencies: 529 | is-number "^7.0.0" 530 | 531 | tslib@2.4.0: 532 | version "2.4.0" 533 | resolved "https://registry.npmmirror.com/tslib/-/tslib-2.4.0.tgz#7cecaa7f073ce680a05847aa77be941098f36dc3" 534 | integrity sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ== 535 | 536 | tslib@^1.8.1: 537 | version "1.14.1" 538 | resolved "https://registry.npmmirror.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" 539 | integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== 540 | 541 | tsutils@^3.21.0: 542 | version "3.21.0" 543 | resolved "https://registry.npmmirror.com/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623" 544 | integrity sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA== 545 | dependencies: 546 | tslib "^1.8.1" 547 | 548 | typescript@4.7.4: 549 | version "4.7.4" 550 | resolved "https://registry.npmmirror.com/typescript/-/typescript-4.7.4.tgz#1a88596d1cf47d59507a1bcdfb5b9dfe4d488235" 551 | integrity sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ== 552 | 553 | uglify-js@^3.1.4: 554 | version "3.17.4" 555 | resolved "https://registry.npmmirror.com/uglify-js/-/uglify-js-3.17.4.tgz#61678cf5fa3f5b7eb789bb345df29afb8257c22c" 556 | integrity sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g== 557 | 558 | wordwrap@^1.0.0: 559 | version "1.0.0" 560 | resolved "https://registry.npmmirror.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" 561 | integrity sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q== 562 | 563 | yallist@^4.0.0: 564 | version "4.0.0" 565 | resolved "https://registry.npmmirror.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" 566 | integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== 567 | --------------------------------------------------------------------------------