├── .editorconfig ├── .eslintignore ├── .eslintrc ├── .gitignore ├── .npmrc ├── FileNode.ts ├── LICENSE ├── README.md ├── assets ├── after.png ├── after2.png ├── before.png ├── before2.png └── behaviors.png ├── canvas.d.ts ├── esbuild.config.mjs ├── main.ts ├── manifest.json ├── package-lock.json ├── package.json ├── styles.css ├── tsconfig.json ├── version-bump.mjs └── versions.json /.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 | node_modules/ 2 | 3 | main.js 4 | -------------------------------------------------------------------------------- /.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 | } -------------------------------------------------------------------------------- /.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 | 24 | # My Notes to Self 25 | .NOTETOSELF -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | tag-version-prefix="" -------------------------------------------------------------------------------- /FileNode.ts: -------------------------------------------------------------------------------- 1 | import { CanvasFileData, CanvasGroupData } from "canvas"; 2 | import { CanvasMap, SemanticCanvasPluginSettings, ConnectionProps } from "main"; 3 | import { App, TFile } from "obsidian"; 4 | 5 | /** 6 | * Represents an instance of a node on the canvas that represents a file in the vault 7 | */ 8 | export class FileNode { 9 | filePath: string; 10 | propsOnCanvas: any; 11 | app: App; 12 | 13 | /** 14 | * A Node on the Canvas that represents a file in the vault 15 | * @param file 16 | * @param data 17 | * @param settings 18 | * @returns 19 | */ 20 | constructor(file: CanvasFileData, data: CanvasMap, settings: SemanticCanvasPluginSettings, appRef: App) { 21 | this.filePath = file.file; 22 | this.propsOnCanvas = {}; 23 | this.app = appRef; //for access to metadatacache 24 | 25 | if (file.inGroups === undefined) file.inGroups = []; 26 | 27 | let relevantIds = [file.id]; //the node ID itself... 28 | relevantIds = [file.id, ...file.inGroups.map((g: any) => g.id)]; //...+ any groups that contain it 29 | 30 | const relevantEdges = data.edges?.filter(edge => { 31 | if (relevantIds.some(id => edge.fromNode == id)) return true; 32 | /* In case link is bi-directional */ 33 | if (relevantIds.some(id => edge.toNode == id && edge.isBidirectional)) return true; 34 | return false; 35 | }); 36 | 37 | if (relevantEdges?.length === 0 && file.inGroups.length === 0) { 38 | this.propsOnCanvas = null; 39 | return; 40 | } 41 | 42 | let edges: ConnectionProps[] = (relevantEdges?.map(edge => { 43 | let newEdge: ConnectionProps = { 44 | otherSideId: edge.toNode, 45 | isBidirectional: edge.isBidirectional 46 | }; 47 | if (file.id === newEdge.otherSideId) newEdge.otherSideId = edge.fromNode; 48 | newEdge.otherSide = data.cards?.find(card => card.id === newEdge.otherSideId); 49 | newEdge.type = 'card'; 50 | newEdge.propLbl = settings.cardDefault; 51 | if (newEdge.otherSide === undefined) { 52 | newEdge.otherSide = data.urls?.find(url => url.id === newEdge.otherSideId); 53 | newEdge.type = 'url'; 54 | newEdge.propLbl = settings.urlDefault; 55 | } 56 | if (newEdge.otherSide === undefined) { 57 | newEdge.otherSide = data.files?.find(file => file.id === newEdge.otherSideId); 58 | newEdge.type = 'file'; 59 | newEdge.propLbl = settings.fileDefault; 60 | } 61 | if (newEdge.otherSide === undefined) { 62 | newEdge.otherSide = data.groups?.find(group => group.id === newEdge.otherSideId); 63 | //#TODO - can you prevent "in group" memberships here when there's an arrow to the group with the same label as the group? 64 | newEdge.type = 'group'; 65 | } 66 | if (newEdge.otherSide === undefined) throw new Error('Could not find other side of edge'); 67 | if (newEdge.type === 'card') newEdge.propVal = newEdge.otherSide.text; 68 | if (newEdge.type === 'url') newEdge.propVal = newEdge.otherSide.url; 69 | if (newEdge.type === 'file') newEdge.propVal = convertToWikilink(newEdge.otherSide as CanvasFileData, this); 70 | if (edge.label !== undefined) newEdge.propLbl = edge.label; 71 | return newEdge; 72 | }).filter(newEdge => newEdge.propLbl !== undefined && newEdge.propLbl !== ''))!; 73 | 74 | /* ALL PROPERTIES ARE ARRAYS OF STRINGS */ 75 | /* this -> contained in group */ 76 | if (file.inGroups.length > 0 && settings.useGroups) { 77 | this.propsOnCanvas[settings.groupDefault] = file.inGroups.map((group: CanvasGroupData) => group.label); 78 | } 79 | 80 | /* this -> card */ 81 | if (settings.useCards) { 82 | edges.filter(edge => edge.type === 'card').forEach(edge => { 83 | if (!this.propsOnCanvas.hasOwnProperty(edge.propLbl)) { 84 | this.propsOnCanvas[edge.propLbl!] = [edge.propVal]; 85 | return; 86 | } 87 | this.propsOnCanvas[edge.propLbl!].push(edge.propVal); 88 | }); 89 | } 90 | 91 | /* this -> url */ 92 | if (settings.useUrls) { 93 | edges.filter(edge => edge.type === 'url').forEach(edge => { 94 | if (!this.propsOnCanvas.hasOwnProperty(edge.propLbl)) { 95 | this.propsOnCanvas[edge.propLbl!] = [edge.propVal]; 96 | return; 97 | } 98 | this.propsOnCanvas[edge.propLbl!].push(edge.propVal); 99 | }); 100 | } 101 | /* this -> note */ 102 | if (settings.useFiles) { 103 | edges.filter(edge => edge.type === 'file').forEach(edge => { 104 | if (!this.propsOnCanvas.hasOwnProperty(edge.propLbl)) { 105 | this.propsOnCanvas[edge.propLbl!] = [edge.propVal]; 106 | return; 107 | } 108 | this.propsOnCanvas[edge.propLbl!].push(edge.propVal); 109 | }); 110 | } 111 | 112 | function convertToWikilink(otherSide: CanvasFileData, that: FileNode): string { 113 | const otherFile = that.app.metadataCache.getFirstLinkpathDest(otherSide.file, that.filePath) as TFile; 114 | let linkTextContent = that.app.metadataCache.fileToLinktext(otherFile, that.filePath); 115 | /* see if Subpaths were used */ 116 | if (otherSide.hasOwnProperty("subpath")) linkTextContent = linkTextContent + otherSide.subpath; 117 | return "[[" + linkTextContent + "]]"; 118 | } 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Aaron Gillespie 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 Semantic Canvas Plugin 2 | 3 | > [!tip] There is a [Demo video](https://youtu.be/fI1sWoBZ8yw)! 4 | 5 | This is a plugin for [Obsidian](https://obsidian.md) gives canvases the power to edit file properties *visually*. 6 | 7 | Set properties for all Markdown files included in your canvas based on their group membership, links to files, links to cards, and links to web embeds. Create new properties or edit existing ones on multiple markdown notes at once through the canvas. Create **semantic links** *(aka typed links or labeled links)* between notes and work with them using an intuitive graph-based approach. Use notes to create canvases. Use canvases to update notes. 8 | 9 | ### New in V1.2 10 | Now capable of more fine-grained controls: 11 | - Pull properties into existing canvases 12 | - Update properties for individual nodes, rather than all at once 13 | - Option to ignore a set of customizable set of property keys 14 | 15 | ## Example Screenshots 16 | 17 | ### Canvas → Edit Note Props 18 | One command to make this canvas... 19 | ![Before image](assets/before.png) 20 | 21 | ...update properties of all the notes it contains: 22 | ![After image](assets/after.png) 23 | 24 | ### Note → Create Canvas 25 | Another command to turn this note... 26 | ![Before image](assets/before2.png) 27 | 28 | ...into a new canvas containing its `list-type` properties: 29 | ![After image](assets/after2.png) 30 | 31 | ## Use Cases 32 | - Building & representing knowledge graphs 33 | - Mass editing properties 34 | - Venn Diagrams & Kanbans 35 | 36 | ## Usage 37 | This plugin adds functions to the command palette and note/canvas menus. 38 | 39 | Open a canvas then use command palette or file menu to run: 40 | - `Semantic Canvas: Append Note Properties based on canvas` 41 | - Will add to note properties without removing any 42 | - `Semantic Canvas: Overwrite Note Properties based on canvas` 43 | - Will replace note properties 44 | 45 | Open a note then use use command palette or file menu to run: 46 | - `Semantic Canvas: Create canvas based on note` 47 | 48 | Right click on nodes in a canvas to: 49 | - `Pull note properties in to canvas` 50 | - Will create new nodes and arrows to fully represent all the list-type properties for the selected node 51 | - `Show existing connections` 52 | - Will create new arrows, but not nodes 53 | - `Append properties in note` & `Overwrite properties in note` 54 | - Will update the note frontmatter according to the connections in the canvas 55 | 56 | ### Behaviors 57 | > 📖 Node Types 58 | > Nodes on a canvas are typed as one of `card`, `url`, `file`, or `group`. 59 | 60 | #### Canvas → Edit Note Files 61 | Semantic Canvas modifies properties of **Markdown files** based on how they're connected to nodes (i.e. `files`, `groups`, `cards`, `urls`) in the active Canvas. See the four example screenshots below to see how different canvas situations are turned into properties. 62 | 63 | ![Behaviors image](assets/behaviors.png) 64 | 65 | - Each Node Type behavior can be toggled off. 66 | - If an edge is labeled, the property set on the `file` will use that label as the property key. 67 | - If an edge is unlabeled, the property set on the `file` will use the default label for that node type. 68 | - If a group contains notes, those `files` will have their `groups` (by default) property set to the value of the title(s) of the group(s) the note is contained in. 69 | - If a note is connected to a `group`, it behaves as though the note is connected to every node contained in the group 70 | 71 | #### Note → Create Canvas 72 | 73 | > 📋 Properties are *always* **List**-typed 74 | > It is not possible to edit Text, Number, Checkbox, Date, or Date & Time properties using this plugin. 75 | 76 | Semantic Canvas creates a new canvas file based on the active note. It will be placed in a configurable location, and will graphically represent all `list-type` properties. This canvas could then be edited manually, and those changes can be pushed back to the related notes. 77 | 78 | ## Attribution 79 | 80 | Thank you to [Ben Hughes](https://github.com/benhughes), whose work on the awesome **Link Exploder** was enormously helpful. -------------------------------------------------------------------------------- /assets/after.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aarongilly/obsidian-semantic-canvas-plugin/f795d94a9aeaf5dd61a17fc084aa8d4ebc0a8529/assets/after.png -------------------------------------------------------------------------------- /assets/after2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aarongilly/obsidian-semantic-canvas-plugin/f795d94a9aeaf5dd61a17fc084aa8d4ebc0a8529/assets/after2.png -------------------------------------------------------------------------------- /assets/before.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aarongilly/obsidian-semantic-canvas-plugin/f795d94a9aeaf5dd61a17fc084aa8d4ebc0a8529/assets/before.png -------------------------------------------------------------------------------- /assets/before2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aarongilly/obsidian-semantic-canvas-plugin/f795d94a9aeaf5dd61a17fc084aa8d4ebc0a8529/assets/before2.png -------------------------------------------------------------------------------- /assets/behaviors.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aarongilly/obsidian-semantic-canvas-plugin/f795d94a9aeaf5dd61a17fc084aa8d4ebc0a8529/assets/behaviors.png -------------------------------------------------------------------------------- /canvas.d.ts: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * A color used to encode color data for nodes and edges 4 | * can be a number (like "1") representing one of the (currently 6) supported colors. 5 | * or can be a custom color using the hex format "#FFFFFFF". 6 | */ 7 | export type CanvasColor = string; 8 | 9 | /** The overall canvas file's JSON */ 10 | export interface CanvasData { 11 | nodes: AllCanvasNodeData[]; 12 | edges: CanvasEdgeData[]; 13 | 14 | /** Support arbitrary keys for forward compatibility */ 15 | [key: string]: any; 16 | } 17 | 18 | /** A node */ 19 | export interface CanvasNodeData { 20 | /** The unique ID for this node */ 21 | id: string; 22 | // The positional data 23 | x: number; 24 | y: number; 25 | width: number; 26 | height: number; 27 | /** The color of this node */ 28 | color?: CanvasColor; 29 | 30 | // Support arbitrary keys for forward compatibility 31 | [key: string]: any; 32 | } 33 | 34 | export type AllCanvasNodeData = CanvasFileData | CanvasTextData | CanvasLinkData | CanvasGroupData; 35 | 36 | /** A node that is a file, where the file is located somewhere in the vault. */ 37 | export interface CanvasFileData extends CanvasNodeData { 38 | type: 'file'; 39 | file: string; 40 | /** An optional subpath which links to a heading or a block. Always starts with a `#`. */ 41 | subpath?: string; 42 | } 43 | 44 | /** A node that is plaintext. */ 45 | export interface CanvasTextData extends CanvasNodeData { 46 | type: 'text'; 47 | text: string; 48 | } 49 | 50 | /** A node that is an external resource. */ 51 | export interface CanvasLinkData extends CanvasNodeData { 52 | type: 'link'; 53 | url: string; 54 | } 55 | 56 | /** The background image rendering style */ 57 | export type BackgroundStyle = 'cover' | 'ratio' | 'repeat'; 58 | 59 | /** A node that represents a group. */ 60 | export interface CanvasGroupData extends CanvasNodeData { 61 | type: 'group'; 62 | /** Optional label to display on top of the group. */ 63 | label?: string; 64 | /** Optional background image, stores the path to the image file in the vault. */ 65 | background?: string; 66 | /** Optional background image rendering style; defaults to 'cover'. */ 67 | backgroundStyle?: BackgroundStyle; 68 | } 69 | 70 | /** The side of the node that a connection is connected to */ 71 | export type NodeSide = 'top' | 'right' | 'bottom' | 'left'; 72 | 73 | /** What to display at the end of an edge */ 74 | export type EdgeEnd = 'none' | 'arrow'; 75 | 76 | /** An edge */ 77 | export interface CanvasEdgeData { 78 | /** The unique ID for this edge */ 79 | id: string; 80 | /** The node ID and side where this edge starts */ 81 | fromNode: string; 82 | fromSide: NodeSide; 83 | /** The starting edge end; defaults to 'none' */ 84 | fromEnd?: EdgeEnd; 85 | /** The node ID and side where this edge ends */ 86 | toNode: string; 87 | toSide: NodeSide; 88 | /** The ending edge end; defaults to 'arrow' */ 89 | toEnd?: EdgeEnd; 90 | /** The color of this edge */ 91 | color?: CanvasColor; 92 | /** The text label of this edge, if available */ 93 | label?: string; 94 | 95 | // Support arbitrary keys for forward compatibility 96 | [key: string]: any; 97 | } -------------------------------------------------------------------------------- /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 | const context = await esbuild.context({ 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 | target: "es2018", 37 | logLevel: "info", 38 | sourcemap: prod ? false : "inline", 39 | treeShaking: true, 40 | outfile: "main.js", 41 | }); 42 | 43 | if (prod) { 44 | await context.rebuild(); 45 | process.exit(0); 46 | } else { 47 | await context.watch(); 48 | } -------------------------------------------------------------------------------- /main.ts: -------------------------------------------------------------------------------- 1 | import { App, Menu, Notice, Plugin, PluginSettingTab, Setting, TFile, TFolder, TextFileView } from 'obsidian'; 2 | import { AllCanvasNodeData, CanvasData, CanvasEdgeData, CanvasFileData, CanvasGroupData, CanvasLinkData, CanvasNodeData, CanvasTextData, NodeSide } from 'canvas'; 3 | import { FileNode } from 'FileNode'; 4 | 5 | export interface SemanticCanvasPluginSettings { 6 | /* Note ➡️ canvas */ 7 | newFileLocation: Location; 8 | customFileLocation: string; 9 | /* Canvas ➡️ note */ 10 | cardDefault: string; 11 | fileDefault: string; 12 | urlDefault: string; 13 | groupDefault: string; 14 | useCards: boolean; 15 | useUrls: boolean; 16 | useFiles: boolean; 17 | useGroups: boolean; 18 | /** 19 | * List of keys to ignore when doing all the things involving note properties 20 | */ 21 | excludeKeys: string; 22 | } 23 | 24 | export enum Location { 25 | VaultFolder, 26 | SameFolder, 27 | SpecifiedFolder, 28 | } 29 | 30 | interface CanvasNodeMap { 31 | cards?: Array, 32 | files?: Array & { inGroups?: Array }, 33 | urls?: Array, 34 | groups?: Array 35 | } 36 | 37 | export interface CanvasMap extends CanvasNodeMap { 38 | edges?: Array 39 | } 40 | 41 | export type ConnectionProps = { 42 | otherSideId?: string; 43 | otherSide?: CanvasNodeData 44 | type?: 'card' | 'url' | 'file' | 'group'; 45 | isBidirectional?: boolean; 46 | propLbl?: string; 47 | propVal?: string; 48 | } 49 | 50 | type FileAndPropsToSetMap = { 51 | file: TFile, 52 | props: null | { [key: string]: string[] } 53 | } 54 | 55 | type RawCanvasObj = { 56 | nodes: Array, 57 | edges: Array 58 | } 59 | 60 | const DEFAULT_SETTINGS: SemanticCanvasPluginSettings = { 61 | newFileLocation: Location.VaultFolder, 62 | customFileLocation: '', 63 | // The default strings for unlabeled edges 64 | cardDefault: 'cards', 65 | fileDefault: 'files', 66 | urlDefault: 'urls', 67 | // The string for group containment 68 | groupDefault: 'groups', 69 | // For disabling whole types of interactions 70 | useCards: true, 71 | useUrls: true, 72 | useFiles: true, 73 | useGroups: false, 74 | excludeKeys: 'alias,aliases,tags,cssClasses' 75 | } 76 | 77 | export default class SemanticCanvasPlugin extends Plugin { 78 | settings: SemanticCanvasPluginSettings; 79 | 80 | async onload() { 81 | await this.loadSettings(); 82 | 83 | /* This command will replace the values of an already-existing property */ 84 | this.addCommand({ 85 | id: 'set-canvas-to-note-properties', 86 | name: 'Overwrite note properties based on canvas', 87 | callback: () => { 88 | this.pushCanvasDataToNotes(true); 89 | } 90 | }); 91 | 92 | /* This command will add new values onto the end of an already-existing property */ 93 | this.addCommand({ 94 | id: 'append-canvas-to-note-properties', 95 | name: 'Append note properties based on canvas', 96 | callback: () => { 97 | this.pushCanvasDataToNotes(false); 98 | } 99 | }); 100 | 101 | /* This command doesn't yet exist */ 102 | // this.addCommand({ 103 | // id: 'update-canvas-with-current-note-data', 104 | // name: 'Update canvas with current note data', 105 | // callback: () => { 106 | // console.log('yo'); 107 | // } 108 | // }) 109 | 110 | /* This command will create a canvas from a note*/ 111 | this.addCommand({ 112 | id: 'create-canvas-from-note', 113 | name: 'Create canvas based on note', 114 | callback: () => { 115 | this.createCanvasFromNote(); 116 | } 117 | }); 118 | 119 | this.addSettingTab(new SemanticCanvasSettingsTab(this.app, this)); 120 | 121 | /** 122 | * Right-clicking edges. 123 | */ 124 | this.registerEvent( 125 | //@ts-expect-error - it works, despite TypeScript not seeing the 'canvas:' methods 126 | this.app.workspace.on("canvas:edge-menu", (menu: Menu, edge: any) => { 127 | if (edge.label === '' || edge.toLineEnd === null || edge.from.node.filePath === undefined) return; 128 | const isBidirectional = edge.fromLineEnd !== null && edge.to.node.filePath !== undefined; 129 | menu.addSeparator(); 130 | menu.addItem((item: any) => { 131 | item.setTitle(isBidirectional ? "Remove property from both notes" : "Remove property from source note") 132 | .setIcon("list-minus") 133 | .onClick(() => { 134 | const file = this.app.vault.getFileByPath(edge.from.node.filePath); 135 | if (file === null) return; 136 | this.app.fileManager.processFrontMatter(file, (frontmatter) => { 137 | frontmatter[edge.label] = undefined; 138 | }) 139 | 140 | //supporting bi-directionally 141 | if (isBidirectional) { 142 | const otherFile = this.app.vault.getFileByPath(edge.to.node.filePath); 143 | if (otherFile === null) return; 144 | this.app.fileManager.processFrontMatter(otherFile, (frontmatter) => { 145 | frontmatter[edge.label] = undefined; 146 | }) 147 | } 148 | if (isBidirectional) new Notice(`Successfully removed prop in 2 files`) 149 | if (!isBidirectional) new Notice(`Successfully removed prop in 1 file`) 150 | }) 151 | }) 152 | menu.addItem((item: any) => { 153 | item.setTitle(isBidirectional ? "Update property in both notes" : "Update property in source note") 154 | .setIcon("list-restart") 155 | .onClick(() => { 156 | let toVal = edge.to.node.text; 157 | if (toVal === undefined) { 158 | const filenameAsWikiLink = "[[" + edge.to.node.filePath.split('/').pop()!.substring(0, edge.to.node.filePath.split('/').pop()!.length - 3) + "]]"; 159 | toVal = filenameAsWikiLink; 160 | } 161 | 162 | const file = this.app.vault.getFileByPath(edge.from.node.filePath); 163 | if (file === null) return; 164 | this.app.fileManager.processFrontMatter(file, (frontmatter) => { 165 | frontmatter[edge.label] = toVal; 166 | }) 167 | 168 | //supporting bi-directionally 169 | if (isBidirectional) { 170 | let otherToVal = edge.from.node.text; 171 | if (otherToVal === undefined) { 172 | const filenameAsWikiLink = "[[" + edge.from.node.filePath.split('/').pop()!.substring(0, edge.from.node.filePath.split('/').pop()!.length - 3) + "]]"; 173 | otherToVal = filenameAsWikiLink; 174 | } 175 | const otherFile = this.app.vault.getFileByPath(edge.to.node.filePath); 176 | if (otherFile === null) return; 177 | this.app.fileManager.processFrontMatter(otherFile, (frontmatter) => { 178 | frontmatter[edge.label] = otherToVal; 179 | }) 180 | } 181 | 182 | if (isBidirectional) new Notice(`Successfully set 2 props in 2 files`) 183 | if (!isBidirectional) new Notice(`Successfully set 1 prop in 1 file`) 184 | }) 185 | }) 186 | }) 187 | ) 188 | 189 | /* Dragging an arrow from a node and letting go on a blankk space */ 190 | this.registerEvent( 191 | //@ts-expect-error - it works, despite TypeScript not seeing the 'canvas:' methods 192 | this.app.workspace.on("canvas:node-connection-drop-menu", (menu: Menu, edge: any, third: any) => { 193 | if (edge.file === undefined) return; //dragging from group or card 194 | const noteProps = this.getNoteData(edge.file.path); 195 | noteProps.forEach(prop => { 196 | menu.addItem((item: any) => { 197 | const key = Object.keys(prop)[0]; 198 | item.setTitle("Property: " + key) 199 | .setIcon("down-arrow") 200 | .onClick(() => { 201 | const activeView = this.app.workspace.getActiveViewOfType(TextFileView); 202 | if (activeView === null) { 203 | new Notice('Aborted: Active view was null'); 204 | return; 205 | } 206 | if (activeView?.file?.extension !== 'canvas') { 207 | new Notice('Aborted: Active view is not a canvas'); 208 | return; 209 | } 210 | this.addNodeDataAtLocation(activeView, prop[key], key, third.to.node.x, third.to.node.y, third.from.node); 211 | }) 212 | }) 213 | }) 214 | }) 215 | ) 216 | 217 | /* Selecting a group of nodes with a click and drag */ 218 | // this.registerEvent( 219 | ////@ts-expect-error - it works, despite TypeScript not seeing the 'canvas:' methods 220 | // this.app.workspace.on("canvas:selection-menu", (menu: Menu, edge: any) => { 221 | // console.log('SUPER!!!'); 222 | // fires when a box is drawn around several items, then the context menu is invoked 223 | // can't think of useful things to add to this menu. So this is all commented. 224 | // }) 225 | // ) 226 | 227 | /* Right clicking on node in canvas*/ 228 | this.registerEvent( 229 | //@ts-expect-error - it works, despite TypeScript not seeing the 'canvas:' methods 230 | this.app.workspace.on("canvas:node-menu", (menu: Menu, node: CanvasNodeData) => { 231 | if (node.file === undefined) return 232 | menu.addItem((item: any) => { 233 | item.setTitle('Pull note properties to canvas') 234 | .setIcon('file-down') 235 | .onClick(() => { 236 | const activeView = this.app.workspace.getActiveViewOfType(TextFileView); 237 | if (!isValidActiveView(activeView)) return; 238 | this.pullNotePropertiesToCanvas(activeView!, [node], false); 239 | }); 240 | }) 241 | menu.addItem((item: any) => { 242 | item.setTitle('Show existing connections') 243 | .setIcon('git-compare-arrows') 244 | .onClick(() => { 245 | const activeView = this.app.workspace.getActiveViewOfType(TextFileView); 246 | if (!isValidActiveView(activeView)) return; 247 | this.pullNotePropertiesToCanvas(activeView!, [node], true); 248 | }); 249 | }) 250 | menu.addItem((item: any) => { 251 | item.setTitle('Append properties in note') 252 | .setIcon('list-plus') 253 | .onClick(() => { 254 | const activeView = this.app.workspace.getActiveViewOfType(TextFileView); 255 | if (!isValidActiveView(activeView)) return; 256 | this.pushCanvasDataToNotes(false, activeView!.file!, node.file.path); 257 | }); 258 | }) 259 | menu.addItem((item: any) => { 260 | item.setTitle('Overwrite properties in note') 261 | .setIcon('list-restart') 262 | .onClick(() => { 263 | const activeView = this.app.workspace.getActiveViewOfType(TextFileView); 264 | if (!isValidActiveView(activeView)) return; 265 | this.pushCanvasDataToNotes(true, activeView!.file!, node.file.path); 266 | }); 267 | }) 268 | function isValidActiveView(activeView: TextFileView | null): boolean { 269 | if (activeView === null) { 270 | new Notice('Aborted: Active view was null'); 271 | return false; 272 | } 273 | if (activeView.file === null) { 274 | new Notice('Aborted: Active view has no file property'); 275 | return false; 276 | } 277 | if (activeView?.file?.extension !== 'canvas') { 278 | new Notice('Aborted: Active view is not a canvas'); 279 | return false; 280 | } 281 | return true; 282 | } 283 | }) 284 | ) 285 | 286 | /* File Menu */ 287 | this.registerEvent( 288 | this.app.workspace.on("file-menu", (menu, file) => { 289 | /* If visible window is Canvas, then we're in a right-click on node menu */ 290 | const activeView = this.app.workspace.getActiveViewOfType(TextFileView); 291 | 292 | if (file instanceof TFolder || file === undefined) return; 293 | /* If Markdown file offer to create canvas */ 294 | if ((file).extension === 'md' && activeView?.file?.extension !== 'canvas') { 295 | menu.addItem((item) => { 296 | item.setTitle('Create canvas based on note') 297 | .setIcon('square-plus') 298 | .onClick(() => { 299 | this.createCanvasFromNote(file as TFile); 300 | }); 301 | }); 302 | } 303 | 304 | /* If Canvas offer to update notes */ 305 | if ((file).extension === 'canvas') { 306 | menu.addItem((item) => { 307 | item.setTitle('Append note properties based on canvas') 308 | .setIcon('list-plus') 309 | .onClick(() => { 310 | this.pushCanvasDataToNotes(false, file as TFile); 311 | }); 312 | }); 313 | menu.addItem((item) => { 314 | item.setTitle('Overwrite note properties based on canvas') 315 | .setIcon('list-restart') 316 | .onClick(() => { 317 | this.pushCanvasDataToNotes(true, file as TFile); 318 | }); 319 | }); 320 | /* and offer to update Canvas with current note */ 321 | menu.addItem((item) => { 322 | item.setTitle('Pull in properies for all notes on this canvas') 323 | .setIcon('file-down') 324 | .onClick(() => { 325 | const activeView = this.app.workspace.getActiveViewOfType(TextFileView); 326 | if (activeView === null) { 327 | new Notice('Aborted: Active view was null'); 328 | return; 329 | } 330 | if (activeView?.file?.extension !== 'canvas') { 331 | new Notice('Aborted: Active view is not a canvas'); 332 | return; 333 | } 334 | const nodes = JSON.parse(activeView.data)['nodes'].filter((node: any) => node.type === 'file'); 335 | this.pullNotePropertiesToCanvas(activeView, nodes, false); 336 | }); 337 | }); 338 | menu.addItem((item) => { 339 | item.setTitle('Show all connections between notes on this canvas') 340 | .setIcon('git-compare-arrows') 341 | .onClick(() => { 342 | const activeView = this.app.workspace.getActiveViewOfType(TextFileView); 343 | if (activeView === null) { 344 | new Notice('Aborted: Active view was null'); 345 | return; 346 | } 347 | if (activeView?.file?.extension !== 'canvas') { 348 | new Notice('Aborted: Active view is not a canvas'); 349 | return; 350 | } 351 | const nodes = JSON.parse(activeView.data)['nodes'].filter((node: any) => node.type === 'file'); 352 | this.pullNotePropertiesToCanvas(activeView, nodes, true); 353 | }); 354 | }); 355 | } 356 | }) 357 | ); 358 | } 359 | 360 | /** 361 | * The main function for using a note to create a new canvas. 362 | */ 363 | async createCanvasFromNote(file?: TFile) { 364 | //@ts-expect-error 365 | if (file === undefined) file = this.app.workspace.getActiveFile(); 366 | if (!file || file?.extension !== 'md') { 367 | new Notice('Aborted: Active file is not Markdown file'); 368 | return; 369 | } 370 | 371 | const name = file.basename; 372 | new Notice('Creating canvas for ' + name); 373 | 374 | let listTypeProps = this.getNoteData(file.path); 375 | 376 | const that = this; 377 | const canvasContents = buildCanvasContents(file, listTypeProps); 378 | 379 | const savePath = createSavePathBasedOnSettings(file, that); 380 | const createdCanvas = await this.app.vault.create(savePath, JSON.stringify(canvasContents)); 381 | this.app.workspace.getLeaf().openFile(createdCanvas); 382 | 383 | function buildCanvasContents(file: TFile, propsMap: Array<{ [index: string]: Array }>): CanvasData { 384 | const thisFileNodeData: CanvasFileData = { 385 | color: "1", 386 | x: 0, 387 | y: 0, 388 | id: '0', 389 | width: 400, 390 | height: 400, 391 | type: 'file', 392 | file: file.path 393 | } 394 | 395 | let canvasContents: CanvasData = { 396 | nodes: [thisFileNodeData], 397 | edges: [] 398 | } 399 | 400 | if (propsMap.length === 0) return canvasContents; 401 | 402 | const firstColumnPosition = 600; 403 | let curY = 0; 404 | let nodeCount = 1; 405 | let edgeCount = 0; 406 | 407 | /* Iterate through the props & mutate the canvasContents for each */ 408 | propsMap.forEach(propObj => { 409 | const key = Object.keys(propObj)[0]; 410 | const valArr = propObj[key]; //will be array 411 | if (!Array.isArray(valArr)) throw new Error("A non-array was passed into buildCanvasContents"); 412 | addEdge(key); 413 | if (SemanticCanvasPlugin.isGroup(valArr)) return addGroup(key, valArr); 414 | /* If it's not a group, the array is of size 1 */ 415 | const val = valArr[0]; 416 | return addNode(val, firstColumnPosition); 417 | }) 418 | 419 | thisFileNodeData.y = curY / 2 - thisFileNodeData.height / 2; 420 | 421 | return canvasContents 422 | 423 | /** 424 | * Mutates canvasContents 425 | * @param label 426 | */ 427 | function addEdge(label: string): void { 428 | edgeCount = edgeCount + 1; 429 | canvasContents.edges.push({ 430 | id: edgeCount.toString(), 431 | fromNode: '0', 432 | fromSide: 'right', 433 | toNode: (nodeCount + 1).toString(), 434 | toSide: 'left', 435 | label: label 436 | }) 437 | } 438 | 439 | /** 440 | * Mutates canvasContents 441 | * @param val card text, url, file 442 | * @param xPos also used as a flag for "is this in a group?" 443 | */ 444 | function addNode(val: string, xPos: number) { 445 | nodeCount = nodeCount + 1; 446 | const newNode: any = { 447 | id: nodeCount.toString(), 448 | x: xPos.toString(), 449 | y: curY.toString() 450 | } 451 | if (SemanticCanvasPlugin.isFile(val)) { 452 | newNode.type = 'file'; 453 | newNode.file = val.substring(2, val.length - 2); 454 | newNode.width = 400; 455 | newNode.height = 400; 456 | if (that.app.vault.getAbstractFileByPath(newNode.file) === null) { 457 | //no such file exists, search for best match 458 | const splitToBaseAndAlias = newNode.file.split('|'); 459 | const base = splitToBaseAndAlias[0]; 460 | const splitToPathAndSubpath = base.split('#'); 461 | const path = splitToPathAndSubpath[0]; 462 | if (splitToPathAndSubpath.length > 1) newNode.subpath = "#" + splitToPathAndSubpath[1]; 463 | const foundFile = that.app.metadataCache.getFirstLinkpathDest(path, file.path); 464 | if (foundFile !== null) newNode.file = foundFile.path; 465 | //else just leaving the link broken 466 | } 467 | } else if (SemanticCanvasPlugin.isURL(val)) { 468 | newNode.type = 'link'; 469 | newNode.url = val; 470 | newNode.width = 400; 471 | newNode.height = 400; 472 | } else { 473 | newNode.type = 'text'; 474 | newNode.text = val; 475 | newNode.width = val.length > 15 ? 400 : 200; 476 | newNode.height = val.length > 15 ? 200 : 100; 477 | } 478 | 479 | /* adjust curY based on height if this isn't being added in a group*/ 480 | if (xPos === firstColumnPosition) { 481 | curY = curY + parseInt(newNode.height) + 50; 482 | } 483 | 484 | canvasContents.nodes.push(newNode as CanvasTextData); 485 | // Returning for use in "addGroup" 486 | return newNode 487 | } 488 | 489 | function addGroup(key: string, valArr: Array) { 490 | nodeCount = nodeCount + 1; 491 | const newGroup: CanvasGroupData = { 492 | type: 'group', 493 | id: nodeCount.toString(), 494 | x: firstColumnPosition, 495 | y: curY, 496 | label: key, 497 | width: 50, 498 | height: 50 499 | } 500 | let xPos = firstColumnPosition + 50; 501 | curY = curY + 50; 502 | valArr.forEach(val => { 503 | let newNode = addNode(val, xPos); 504 | xPos = xPos + newNode.width + 50; 505 | newGroup.width = newGroup.width + newNode.width + 50; 506 | if (newNode.height + 100 > newGroup.height) newGroup.height = newNode.height + 100 507 | }) 508 | curY = curY + newGroup.height; 509 | canvasContents.nodes.push(newGroup); 510 | } 511 | } 512 | 513 | function createSavePathBasedOnSettings(file: TFile, that: SemanticCanvasPlugin): string { 514 | let location = ''; 515 | switch (that.settings.newFileLocation) { 516 | case Location.SameFolder: 517 | location = file.parent!.path; 518 | break; 519 | case Location.SpecifiedFolder: 520 | const fileLocationExists = that.app.vault.getAbstractFileByPath(that.settings.customFileLocation) !== null; 521 | if (fileLocationExists) { 522 | location = that.settings.customFileLocation; 523 | } else { 524 | new Notice( 525 | `folder ${that.settings.customFileLocation} does not exist, creating in root folder` 526 | ); 527 | } 528 | } 529 | let canvasPath = name + '.canvas'; 530 | if (location !== '') canvasPath = location + "/" + canvasPath; 531 | /* If the file already exists, keep appending "(new)" until it's unique */ 532 | while (that.app.vault.getAbstractFileByPath(canvasPath) !== null) { 533 | canvasPath = canvasPath.substring(0, canvasPath.length - 7) + " (new).canvas" 534 | } 535 | return canvasPath 536 | } 537 | } 538 | 539 | /** 540 | * The main function for using an existing canvas to update note properties. 541 | * @param overwrite `true` will overwrite existing values for keys 542 | */ 543 | async pushCanvasDataToNotes(overwrite: boolean, canvasFile?: TFile, onlyUpdateNoteAtPath?: string) { 544 | //@ts-expect-error 545 | if (canvasFile === undefined) canvasFile = this.app.workspace.getActiveFile(); 546 | if (!canvasFile || canvasFile?.extension !== 'canvas') { 547 | new Notice('Aborted: Active file is not Canvas'); 548 | return; 549 | } 550 | 551 | let data = await SemanticCanvasPlugin.getCanvasMap(canvasFile); 552 | 553 | if (!data) { 554 | new Notice('Aborted: No Canvas data found'); 555 | return; 556 | } 557 | 558 | let fileNodes = data?.files?.map(file => new FileNode(file, data!, this.settings, this.app)); 559 | 560 | /* De-dupe - if same file was on a canvas multiple times */ 561 | let dedupedFileNodes: FileNode[] = []; 562 | fileNodes?.forEach(fileNode => { 563 | if (fileNode.propsOnCanvas === null) return 564 | 565 | let existing = dedupedFileNodes?.find(ogNodeList => ogNodeList.filePath === fileNode.filePath); 566 | 567 | if (existing === undefined) { 568 | dedupedFileNodes.push(fileNode); 569 | return 570 | } 571 | 572 | existing.propsOnCanvas = mergeProps(existing.propsOnCanvas, fileNode.propsOnCanvas); 573 | }) 574 | 575 | /* Remove any unaffected nodes before seeking files */ 576 | dedupedFileNodes = dedupedFileNodes.filter(fileNode => fileNode.propsOnCanvas && Object.keys(fileNode.propsOnCanvas).length > 0); 577 | 578 | let actualFilesMap: Array = dedupedFileNodes.map(fileNode => { 579 | const file = this.app.vault.getFileByPath(fileNode.filePath); 580 | if (file === null) throw new Error('No file found at path ' + fileNode.filePath) 581 | return { 582 | file: file, 583 | props: fileNode.propsOnCanvas 584 | } 585 | }); 586 | 587 | /* Remove any non-markdown files before setting properties */ 588 | actualFilesMap = actualFilesMap.filter(fileMap => fileMap.file?.extension === 'md'); 589 | 590 | /* 591 | Filtering to only the single file we care about, in the case that we only want 592 | to update one file. This is being used for the right-click menu on nodes inside 593 | of canvases. It's less efficient, case we scaffolded a bunch of stuff up above 594 | only to remove it here, but it's DRY and fast enough. Could be a target for optimization 595 | if this ever gets too slow. 596 | */ 597 | if (onlyUpdateNoteAtPath !== undefined) { 598 | actualFilesMap = actualFilesMap.filter(fileMap => fileMap.file.path === onlyUpdateNoteAtPath); 599 | } 600 | 601 | let propertyAddCount = 0; 602 | actualFilesMap.forEach(fileMap => { 603 | if (fileMap.props === null) throw new Error('Cannot push canvas data to notes - fileMap.props was null'); 604 | propertyAddCount = propertyAddCount + Object.keys(fileMap.props!).length 605 | }) 606 | 607 | let modifiedFileCount = actualFilesMap.length; 608 | 609 | actualFilesMap.forEach(fileMap => this.app.fileManager.processFrontMatter(fileMap.file, (frontmatter) => { 610 | /* have to directly mutate this object, a bit tedious */ 611 | Object.keys(fileMap.props!).forEach(key => { 612 | if (overwrite || !frontmatter.hasOwnProperty(key)) { 613 | frontmatter[key] = fileMap.props![key]; 614 | return 615 | } 616 | 617 | //force array 618 | if (!Array.isArray(frontmatter[key])) frontmatter[key] = [frontmatter[key]]; 619 | /* Don't add duplicate values to existing props */ 620 | fileMap.props![key] = fileMap.props![key].filter((val: any) => !frontmatter[key].some((og: any) => og === val)) 621 | frontmatter[key] = [...frontmatter[key], ...fileMap.props![key]]; 622 | }) 623 | 624 | })); 625 | 626 | if (modifiedFileCount > 0) { 627 | new Notice(`Successfully set ${propertyAddCount} prop(s) in ${modifiedFileCount} file(s)`) 628 | } else { 629 | new Notice(`No notes connections found on canvas.`) 630 | } 631 | 632 | function mergeProps(a: any, b: any) { 633 | Object.keys(b).forEach(key => { 634 | if (a.hasOwnProperty(key)) { 635 | a[key] = [...a[key], ...b[key]]; 636 | } else { 637 | a[key] = b[key]; 638 | } 639 | }) 640 | return a; 641 | } 642 | } 643 | 644 | /** 645 | * Creates a new node in the file represented inside the passed-in File view & saves it 646 | * @param text property value from the note 647 | * @param label the key the value has in the note 648 | * @param x where to put the new node 649 | * @param y where to put the new node 650 | */ 651 | async addNodeDataAtLocation(fileView: TextFileView, textArr: string[], label: string, x: number, y: number, fromNode: CanvasNodeData) { 652 | if (fileView.file === null) throw new Error('fileView had no associated file'); 653 | const visibleCanvasData = JSON.parse(fileView.data) as CanvasData 654 | 655 | //making "that" reference for use by makeNodeOrGroupOfNodesFor 656 | const that = this; 657 | const newNodeOrGroup = makeNodeOrGroupOfNodesFor(textArr, label); 658 | visibleCanvasData.nodes.push(...newNodeOrGroup); 659 | 660 | const fromToSides = SemanticCanvasPlugin.determineSides(fromNode, newNodeOrGroup[0] as never as CanvasNodeData); 661 | visibleCanvasData.edges.push({ 662 | id: (Math.random() + 1).toString(36).substring(4), 663 | fromNode: fromNode.id, 664 | fromSide: fromToSides.from, 665 | toNode: newNodeOrGroup[0].id, 666 | toSide: fromToSides.to, 667 | label: label 668 | }) 669 | 670 | // save to file 671 | this.app.vault.process(fileView.file, () => JSON.stringify(visibleCanvasData)); 672 | 673 | function makeNodeOrGroupOfNodesFor(propVals: string[], label: string): AllCanvasNodeData[] { 674 | const returnObj: AllCanvasNodeData[] = []; 675 | 676 | propVals.forEach(text => { 677 | let newNode: any = { 678 | id: (Math.random() + 1).toString(36).substring(4), 679 | x: x - 100, 680 | y: y - 50 + (110 * returnObj.length), 681 | width: 200, 682 | height: 100, 683 | } 684 | 685 | const isfile = SemanticCanvasPlugin.isFile(text); 686 | newNode.type = isfile ? 'file' : 'text'; 687 | if (isfile) { 688 | //need to convert from wikilink style to absolute path 689 | const linkSansBrackets = text.substring(2, text.length - 2); 690 | const foundFile = that.app.metadataCache.getFirstLinkpathDest(linkSansBrackets, fileView!.file!.path); 691 | if (foundFile !== null) { 692 | newNode.file = foundFile.path; 693 | } 694 | if (foundFile === null) { 695 | //fallback to card with wikilink 696 | newNode.type = 'text'; 697 | newNode.text = text; 698 | } 699 | } 700 | if (!isfile) newNode.text = text; 701 | returnObj.push(newNode); 702 | }) 703 | //if more than one propVal got passed in, need to make a group to contain them 704 | if (propVals.length > 1) { 705 | let newNode: any = { 706 | id: (Math.random() + 1).toString(36).substring(4), 707 | x: x - 110, 708 | y: y - 60, 709 | width: 220, 710 | height: 10 + (110 * returnObj.length), 711 | type: 'group', 712 | label: label 713 | } 714 | returnObj.unshift(newNode); 715 | } 716 | return returnObj; 717 | } 718 | } 719 | 720 | /** 721 | * The main function for using an existing canvas to update note properties. 722 | * @param overwrite `true` will overwrite existing values for keys 723 | */ 724 | async pullNotePropertiesToCanvas(fileView: TextFileView, nodesToPullFrom: CanvasNodeData[], existingOnly = false) { 725 | 726 | if (fileView.file === null) throw new Error('fileView had no associated file'); 727 | const visibleCanvasData = JSON.parse(fileView.data) as CanvasData 728 | 729 | const canvasMap = await SemanticCanvasPlugin.getCanvasMap(fileView.file); 730 | 731 | if (canvasMap === undefined) throw new Error("Canvas Map was unable to be created"); 732 | 733 | let connectionTargets = SemanticCanvasPlugin.buildConnectionTargets(canvasMap); 734 | 735 | let edgesToBuild: CanvasEdgeData[] = []; 736 | let nodesToBuild: AllCanvasNodeData[] = []; 737 | nodesToPullFrom.forEach((node: any) => { 738 | let noteProps = this.getNoteData(typeof node.file === 'string' ? node.file : node.file.path); 739 | noteProps.forEach(prop => { 740 | const key = Object.keys(prop)[0]; 741 | const vals = prop[key]; 742 | vals.forEach(val => { 743 | let connection = connectionTargets.find(target => target.content === val || (target.normalizedFileName && target.normalizedFileName! === val)); 744 | //create edges & nodes when no matching node is found 745 | if (connection === undefined) { 746 | if (existingOnly) return; 747 | let newNode: any = { 748 | id: (Math.random() + 1).toString(36).substring(4), 749 | x: Number.parseFloat(node.x) + Number.parseFloat(node.width) + 20, 750 | y: Number.parseFloat(node.y) + (Number.parseFloat(node.height) + 20) * nodesToBuild.length, 751 | width: node.width, 752 | height: node.height, 753 | label: key 754 | } 755 | 756 | const isfile = SemanticCanvasPlugin.isFile(val); 757 | newNode.type = isfile ? 'file' : 'text'; 758 | if (isfile) { 759 | //need to convert from wikilink style to absolute path 760 | const linkSansBrackets = val.substring(2, val.length - 2); 761 | const foundFile = this.app.metadataCache.getFirstLinkpathDest(linkSansBrackets, fileView!.file!.path); 762 | if (foundFile !== null) newNode.file = foundFile.path; 763 | if (foundFile === null) { 764 | //fallback to card with wikilink 765 | newNode.type = 'text'; 766 | newNode.text = val; 767 | } 768 | } 769 | if (!isfile) newNode.text = val; 770 | 771 | nodesToBuild.push(newNode); 772 | edgesToBuild.push({ 773 | id: (Math.random() + 1).toString(36).substring(4), 774 | fromNode: node.id, 775 | fromSide: 'right', 776 | toNode: newNode.id, 777 | toSide: 'left', 778 | label: key 779 | }) 780 | return; 781 | } 782 | //don't create edges that already exist 783 | if (edgeAlreadyExists(node.id, connection.id, key)) return 784 | 785 | //only create edge when matching node is found 786 | const fromToSides = SemanticCanvasPlugin.determineSides(node, connection as never as CanvasNodeData); 787 | edgesToBuild.push({ 788 | id: (Math.random() + 1).toString(36).substring(4), 789 | fromNode: node.id, 790 | fromSide: fromToSides.from, 791 | toNode: connection.id, 792 | toSide: fromToSides.to, 793 | label: key 794 | }) 795 | }) 796 | }) 797 | }) 798 | 799 | // push new edges & nodes to the canvas data 800 | visibleCanvasData.edges.push(...edgesToBuild); 801 | visibleCanvasData.nodes.push(...nodesToBuild); 802 | // save to file 803 | this.app.vault.process(fileView.file, () => JSON.stringify(visibleCanvasData)); 804 | 805 | return 806 | 807 | function edgeAlreadyExists(fromId: string, toId: string, labeled: string): boolean { 808 | return visibleCanvasData.edges.some(edge => edge.fromNode === fromId && edge.toNode === toId && edge.label === labeled) 809 | } 810 | } 811 | 812 | /** 813 | * Gets the **list type** properties from the passed-in note file 814 | * @param file the path of the .md file to get properties from 815 | * @returns list-type properties map 816 | */ 817 | getNoteData(filepath: string): Array<{ [index: string]: Array }> { 818 | 819 | const allProperties = this.app.metadataCache.getCache(filepath)?.frontmatter; 820 | let listTypeProps: Array<{ [index: string]: Array }> = []; 821 | 822 | const excludeKeys = this.settings.excludeKeys.split(',').map(key => key.trim().toUpperCase()); 823 | 824 | if (allProperties !== undefined) { 825 | Object.keys(allProperties).forEach((key) => { 826 | if (excludeKeys.some(exclusion => exclusion === key.toUpperCase())) return 827 | if (Array.isArray(allProperties[key])) listTypeProps.push({ [key]: allProperties[key] }); 828 | }) 829 | } 830 | return listTypeProps; 831 | } 832 | 833 | //#region --- Static Helper Methods 834 | 835 | /** 836 | * Creates a list of pre-existing things on a canvas that a hypothetical node 837 | * *could* link to if it's looking for links. 838 | * Content is returned a key:value pair where the key is the node id 839 | * and the value is a string-based representation of the content for comparison 840 | * @param nodes 841 | */ 842 | static buildConnectionTargets(nodes: CanvasMap) { 843 | let returnArray: { 844 | nodeType: "card" | "file" | "url" | "group", 845 | id: string, 846 | content: string 847 | normalizedFileName?: string, 848 | x: number, 849 | y: number, 850 | w: number, 851 | h: number 852 | }[] = []; 853 | 854 | nodes.cards?.forEach(card => { 855 | returnArray.push({ 856 | nodeType: 'card', 857 | id: card.id, 858 | content: card.text, 859 | x: card.x, 860 | y: card.y, 861 | w: card.width, 862 | h: card.height 863 | }) 864 | }); 865 | nodes.files?.forEach(file => { 866 | const filename = file.file; 867 | const filenameAsWikiLink = "[[" + file.file.split('/').pop()!.substring(0, file.file.split('/').pop()!.length - 3) + "]]"; 868 | returnArray.push({ 869 | nodeType: 'file', 870 | id: file.id, 871 | content: filename, 872 | normalizedFileName: filenameAsWikiLink, 873 | x: file.x, 874 | y: file.y, 875 | w: file.width, 876 | h: file.height 877 | }) 878 | }); 879 | nodes.urls?.forEach(url => { 880 | returnArray.push({ 881 | nodeType: 'url', 882 | id: url.id, 883 | content: url.url, 884 | x: url.x, 885 | y: url.y, 886 | w: url.width, 887 | h: url.height 888 | }) 889 | }); 890 | 891 | /* --- actually not doing this. Groups are always janky no matter what. 892 | nodes.groups?.forEach(group => { 893 | //sort group to ensure consistency for comparison sake 894 | const sortedGroup = group.containedNodes.sort((nodeA: any, nodeB: any) => { 895 | let valueA = getDisplayValue(nodeA); 896 | let valueB = getDisplayValue(nodeB); 897 | return valueA > valueB ? 1 : -1; 898 | // function getDisplayValue(nodeData: any) { 899 | // if (nodeData.type === 'text') return nodeData.text 900 | // if (nodeData.type === 'file') return nodeData.file 901 | // if (nodeData.type === 'link') return nodeData.url 902 | // if (nodeData.type === 'group') return '' 903 | // } 904 | }) 905 | const sortedGroupVals = sortedGroup.map((member: any) => getDisplayValue(member)) as string[] 906 | returnArray.push({ 907 | nodeType: 'group', 908 | id: group.id, 909 | content: sortedGroupVals.join('|||'), 910 | x: group.x, 911 | y: group.y, 912 | w: group.width, 913 | h: group.height 914 | }) 915 | }) 916 | */ 917 | 918 | return returnArray 919 | 920 | 921 | } 922 | 923 | static determineSides(fromNode: CanvasNodeData, toNode: CanvasNodeData): { from: NodeSide, to: NodeSide } { 924 | let verticalDelta = fromNode.y - toNode.y; 925 | let horizontalDelta = fromNode.x - toNode.x; 926 | if (Math.abs(verticalDelta) > Math.abs(horizontalDelta)) { 927 | if (verticalDelta > 0) return { from: 'top', to: 'bottom' } 928 | return { from: 'bottom', to: 'top' } 929 | } 930 | if (horizontalDelta > 0) return { from: 'left', to: 'right' } 931 | return { from: 'right', to: 'left' } 932 | } 933 | 934 | 935 | static isGroup(val: Array) { 936 | return val.length > 1; 937 | } 938 | 939 | static isFile(val: string): boolean { 940 | if (val.substring(0, 2) !== '[[') return false; 941 | if (val.substring(val.length - 2) !== ']]') return false; 942 | if (val.split('[[').length !== 2) return false; 943 | return true 944 | } 945 | 946 | static isURL(val: string): boolean { 947 | if (val.toUpperCase().substring(0, 4) !== 'HTTP') return false 948 | if (!val.contains('//')) return false 949 | if (val.length < 8) return false 950 | return true 951 | } 952 | 953 | static async getCanvasData(file: TFile | null): Promise { 954 | if (file === null || file.extension !== 'canvas') return; 955 | let rawCanvasText = await file.vault.cachedRead(file); 956 | let canvas = JSON.parse(rawCanvasText); 957 | return canvas! 958 | } 959 | 960 | static getCanvasNodes(data: RawCanvasObj): CanvasMap | undefined { 961 | if (data === undefined) return undefined; 962 | let map: CanvasNodeMap = { 963 | cards: (data.nodes.filter((node) => node.type == 'text')), 964 | files: (data.nodes.filter((node) => node.type == 'file')), 965 | urls: (data.nodes.filter((node) => node.type == 'link')), 966 | groups: (data.nodes.filter((node) => node.type == 'group')), 967 | } 968 | 969 | /* Find wholly-contained file-type nodes & add to group */ 970 | map.groups?.forEach((group) => { 971 | group.containedNodes = [] as CanvasNodeData[]; 972 | map.files?.forEach((file) => { 973 | if (groupContainsNode(group, file)) { 974 | group.containedNodes.push(file); 975 | if (file.hasOwnProperty('inGroups')) { 976 | file.inGroups.push(group) 977 | } else { 978 | file.inGroups = [group]; 979 | } 980 | } 981 | }) 982 | map.cards?.forEach((cards) => { 983 | if (groupContainsNode(group, cards)) { 984 | group.containedNodes.push(cards); 985 | } 986 | }) 987 | map.urls?.forEach((urls) => { 988 | if (groupContainsNode(group, urls)) { 989 | group.containedNodes.push(urls); 990 | } 991 | }) 992 | }) 993 | 994 | /** 995 | * Returns true if the Group's outer bounds wholly contain the file's outer bounds. 996 | * Mimicks the behavior in Obsidian 997 | * @param group 998 | * @param node 999 | */ 1000 | function groupContainsNode(group: CanvasGroupData, node: CanvasNodeData): boolean { 1001 | if (group.y > node.y) return false 1002 | if (group.y + group.height < node.y + node.height) return false 1003 | if (group.x > node.x) return false 1004 | if (group.x + group.width < node.x + node.width) return false 1005 | return true; 1006 | } 1007 | 1008 | return map; 1009 | } 1010 | 1011 | static getCanvasEdges(data: RawCanvasObj): CanvasEdgeData[] | undefined { 1012 | if (data === undefined) return undefined; 1013 | data.edges.forEach(edge => { 1014 | edge.isBidirectional = (edge.fromEnd === 'arrow' || edge.toEnd === 'none') 1015 | }) 1016 | return data.edges 1017 | } 1018 | 1019 | static async getCanvasMap(file: TFile | null): Promise { 1020 | if (!file) return undefined; 1021 | 1022 | const canvasData = await SemanticCanvasPlugin.getCanvasData(file); 1023 | if (!canvasData) return undefined; 1024 | 1025 | let map = SemanticCanvasPlugin.getCanvasNodes(canvasData) 1026 | if (!map) return undefined; 1027 | 1028 | let edges = SemanticCanvasPlugin.getCanvasEdges(canvasData) 1029 | map!.edges = edges as unknown as Array; 1030 | 1031 | edges?.forEach(edge => { 1032 | const toType = getTypeOfNodeById(edge.toNode); 1033 | 1034 | if (toType === 'group') { 1035 | /* create phantom edges to group contents */ 1036 | let group = map?.groups?.find(g => g.id === edge.toNode); 1037 | if (!group) throw new Error('Unmatched group. ID: ' + edge.toNode); 1038 | makePhantomPropagatedEdgesToGroupContents(group!, edge); 1039 | } 1040 | }) 1041 | 1042 | return map 1043 | 1044 | function getTypeOfNodeById(nodeId: string) { 1045 | if (map?.cards?.some(card => card.id === nodeId)) return 'card' 1046 | if (map?.files?.some(file => file.id === nodeId)) return 'file' 1047 | if (map?.urls?.some(url => url.id === nodeId)) return 'url' 1048 | if (map?.groups?.some(group => group.id === nodeId)) return 'group' 1049 | throw new Error('No type found for id: ' + nodeId); 1050 | } 1051 | 1052 | /** 1053 | * Mutates the map to set its edges property to the passed-in edges AND 1054 | * the "phantom" edges created by links-to-groups. 1055 | * @param map 1056 | * @param edges 1057 | */ 1058 | function makePhantomPropagatedEdgesToGroupContents(group: CanvasGroupData, edge: CanvasEdgeData) { 1059 | group.containedNodes.forEach((node: CanvasNodeData) => { 1060 | if (node.type === 'group') return //if a group contains another group, that group node can be ignored 1061 | 1062 | const newEdge: CanvasEdgeData = { 1063 | id: edge.id + '-phantom', 1064 | fromNode: edge.fromNode, 1065 | fromSide: 'right', //doesn't matter 1066 | toNode: node.id, 1067 | toSide: 'left', //doesn't matter 1068 | label: edge.hasOwnProperty('label') ? edge.label : group.label 1069 | } 1070 | 1071 | edges?.push(newEdge); 1072 | }) 1073 | } 1074 | } 1075 | 1076 | //#endregion 1077 | 1078 | onunload() { 1079 | //nothing to do 1080 | } 1081 | 1082 | async loadSettings() { 1083 | this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData()); 1084 | } 1085 | 1086 | async saveSettings() { 1087 | await this.saveData(this.settings); 1088 | } 1089 | } 1090 | 1091 | class SemanticCanvasSettingsTab extends PluginSettingTab { 1092 | plugin: SemanticCanvasPlugin; 1093 | 1094 | constructor(app: App, plugin: SemanticCanvasPlugin) { 1095 | super(app, plugin); 1096 | this.plugin = plugin; 1097 | } 1098 | 1099 | display(): void { 1100 | const { containerEl } = this; 1101 | 1102 | /* Clear existing content, if any */ 1103 | containerEl.empty(); 1104 | 1105 | containerEl.createEl('h1', { text: 'Note → create canvas' }); 1106 | new Setting(containerEl) 1107 | .setName('Default location for new canvas files') 1108 | .addDropdown((dropDown) => { 1109 | dropDown 1110 | .addOption(Location[Location.VaultFolder], 'Vault folder') 1111 | .addOption( 1112 | Location[Location.SameFolder], 1113 | 'Same folder as current file' 1114 | ) 1115 | .addOption( 1116 | Location[Location.SpecifiedFolder], 1117 | 'In the folder specified below' 1118 | ) 1119 | .setValue( 1120 | Location[this.plugin.settings.newFileLocation] || 1121 | Location[Location.VaultFolder] 1122 | ) 1123 | .onChange(async (value) => { 1124 | this.plugin.settings.newFileLocation = 1125 | Location[value as keyof typeof Location]; 1126 | await this.plugin.saveSettings(); 1127 | this.display(); 1128 | }); 1129 | }); 1130 | if (this.plugin.settings.newFileLocation == Location.SpecifiedFolder) { 1131 | new Setting(containerEl) 1132 | .setName('Folder to create new canvas files in') 1133 | .addText((text) => { 1134 | text 1135 | .setPlaceholder('Example: folder 1/folder 2') 1136 | .setValue(this.plugin.settings.customFileLocation) 1137 | .onChange(async (value) => { 1138 | this.plugin.settings.customFileLocation = value; 1139 | await this.plugin.saveSettings(); 1140 | }); 1141 | }); 1142 | } 1143 | 1144 | new Setting(containerEl) 1145 | .setName('Keys to ignore') 1146 | .setDesc(`A comma-separated list of property keys to ignore (case-insensitive).`) 1147 | .addTextArea((text) => { 1148 | text 1149 | .setValue(this.plugin.settings.excludeKeys) 1150 | .onChange(async (value) => { 1151 | this.plugin.settings.excludeKeys = value; 1152 | await this.plugin.saveSettings(); 1153 | }) 1154 | }); 1155 | 1156 | containerEl.createEl('h1', { text: 'Canvas → set note properties' }); 1157 | containerEl.createEl('h2', { text: 'Toggle property setting per type' }); 1158 | new Setting(containerEl) 1159 | .setName('Set note properties for connections to cards ') 1160 | .setDesc('Default: true') 1161 | .addToggle(toggle => toggle 1162 | .setValue(this.plugin.settings.useCards) 1163 | .onChange(async (value) => { 1164 | this.plugin.settings.useCards = value; 1165 | await this.plugin.saveSettings(); 1166 | })); 1167 | 1168 | new Setting(containerEl) 1169 | .setName('Set note properties for connections to urls') 1170 | .setDesc('Default: true') 1171 | .addToggle(toggle => toggle 1172 | .setValue(this.plugin.settings.useUrls) 1173 | .onChange(async (value) => { 1174 | this.plugin.settings.useUrls = value; 1175 | await this.plugin.saveSettings(); 1176 | })); 1177 | 1178 | new Setting(containerEl) 1179 | .setName('Set note properties for connections to files') 1180 | .setDesc('Default: true') 1181 | .addToggle(toggle => toggle 1182 | .setValue(this.plugin.settings.useFiles) 1183 | .onChange(async (value) => { 1184 | this.plugin.settings.useFiles = value; 1185 | await this.plugin.saveSettings(); 1186 | })); 1187 | 1188 | new Setting(containerEl) 1189 | .setName('Set note properties based on containment in groups') 1190 | .setDesc('Default: false') 1191 | .addToggle(toggle => toggle 1192 | .setValue(this.plugin.settings.useGroups) 1193 | .onChange(async (value) => { 1194 | this.plugin.settings.useGroups = value; 1195 | await this.plugin.saveSettings(); 1196 | })); 1197 | 1198 | containerEl.createEl('h2', { text: 'Default property keys for unlabeled connections' }); 1199 | new Setting(containerEl) 1200 | .setName('Property key for unlabeled connections to: cards') 1201 | .setDesc('Leave blank to only create properties for labeled edges. Default: cards') 1202 | .addText(text => text 1203 | .setPlaceholder('Default cards key...') 1204 | .setValue(this.plugin.settings.cardDefault) 1205 | .onChange(async (value) => { 1206 | this.plugin.settings.cardDefault = value; 1207 | await this.plugin.saveSettings(); 1208 | })); 1209 | 1210 | new Setting(containerEl) 1211 | .setName('Property key for unlabeled connections to: urls') 1212 | .setDesc('Leave blank to only create properties for labeled edges. Default: urls') 1213 | .addText(text => text 1214 | .setPlaceholder('Default urls key...') 1215 | .setValue(this.plugin.settings.urlDefault) 1216 | .onChange(async (value) => { 1217 | this.plugin.settings.urlDefault = value; 1218 | await this.plugin.saveSettings(); 1219 | })); 1220 | 1221 | new Setting(containerEl) 1222 | .setName('Property key for unlabeled connections to: files') 1223 | .setDesc('Leave blank to only create properties for labeled edges. Default: files') 1224 | .addText(text => text 1225 | .setPlaceholder('Default files key...') 1226 | .setValue(this.plugin.settings.fileDefault) 1227 | .onChange(async (value) => { 1228 | this.plugin.settings.fileDefault = value; 1229 | await this.plugin.saveSettings(); 1230 | })); 1231 | 1232 | containerEl.createEl('h2', { text: 'Property keys for group containment' }); 1233 | new Setting(containerEl) 1234 | .setName('Property key for unlabeled groups') 1235 | .setDesc('Default: groups') 1236 | .addText(text => text 1237 | .setPlaceholder('Default groups key...') 1238 | .setValue(this.plugin.settings.groupDefault) 1239 | .onChange(async (value) => { 1240 | this.plugin.settings.groupDefault = value; 1241 | await this.plugin.saveSettings(); 1242 | })); 1243 | } 1244 | } 1245 | 1246 | -------------------------------------------------------------------------------- /manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "semantic-canvas", 3 | "name": "Semantic Canvas", 4 | "version": "1.2.1", 5 | "minAppVersion": "0.15.0", 6 | "description": "Create semantic knowledge graphs using Canvases to modify note properties graphically.", 7 | "author": "Aaron Gillespie", 8 | "authorUrl": "https://aarongilly.com", 9 | "fundingUrl": "https://www.buymeacoffee.com/aarongilly", 10 | "isDesktopOnly": false 11 | } -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "obsidian-sample-plugin", 3 | "version": "1.0.0", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "obsidian-sample-plugin", 9 | "version": "1.0.0", 10 | "license": "MIT", 11 | "devDependencies": { 12 | "@types/node": "^16.11.6", 13 | "@typescript-eslint/eslint-plugin": "5.29.0", 14 | "@typescript-eslint/parser": "5.29.0", 15 | "builtin-modules": "3.3.0", 16 | "esbuild": "0.17.3", 17 | "obsidian": "latest", 18 | "tslib": "2.4.0", 19 | "typescript": "4.7.4" 20 | } 21 | }, 22 | "node_modules/@aashutoshrathi/word-wrap": { 23 | "version": "1.2.6", 24 | "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", 25 | "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", 26 | "dev": true, 27 | "peer": true, 28 | "engines": { 29 | "node": ">=0.10.0" 30 | } 31 | }, 32 | "node_modules/@codemirror/state": { 33 | "version": "6.4.1", 34 | "resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.4.1.tgz", 35 | "integrity": "sha512-QkEyUiLhsJoZkbumGZlswmAhA7CBU02Wrz7zvH4SrcifbsqwlXShVXg65f3v/ts57W3dqyamEriMhij1Z3Zz4A==", 36 | "dev": true, 37 | "peer": true 38 | }, 39 | "node_modules/@codemirror/view": { 40 | "version": "6.25.1", 41 | "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.25.1.tgz", 42 | "integrity": "sha512-2LXLxsQnHDdfGzDvjzAwZh2ZviNJm7im6tGpa0IONIDnFd8RZ80D2SNi8PDi6YjKcMoMRK20v6OmKIdsrwsyoQ==", 43 | "dev": true, 44 | "peer": true, 45 | "dependencies": { 46 | "@codemirror/state": "^6.4.0", 47 | "style-mod": "^4.1.0", 48 | "w3c-keyname": "^2.2.4" 49 | } 50 | }, 51 | "node_modules/@esbuild/android-arm": { 52 | "version": "0.17.3", 53 | "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.17.3.tgz", 54 | "integrity": "sha512-1Mlz934GvbgdDmt26rTLmf03cAgLg5HyOgJN+ZGCeP3Q9ynYTNMn2/LQxIl7Uy+o4K6Rfi2OuLsr12JQQR8gNg==", 55 | "cpu": [ 56 | "arm" 57 | ], 58 | "dev": true, 59 | "optional": true, 60 | "os": [ 61 | "android" 62 | ], 63 | "engines": { 64 | "node": ">=12" 65 | } 66 | }, 67 | "node_modules/@esbuild/android-arm64": { 68 | "version": "0.17.3", 69 | "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.17.3.tgz", 70 | "integrity": "sha512-XvJsYo3dO3Pi4kpalkyMvfQsjxPWHYjoX4MDiB/FUM4YMfWcXa5l4VCwFWVYI1+92yxqjuqrhNg0CZg3gSouyQ==", 71 | "cpu": [ 72 | "arm64" 73 | ], 74 | "dev": true, 75 | "optional": true, 76 | "os": [ 77 | "android" 78 | ], 79 | "engines": { 80 | "node": ">=12" 81 | } 82 | }, 83 | "node_modules/@esbuild/android-x64": { 84 | "version": "0.17.3", 85 | "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.17.3.tgz", 86 | "integrity": "sha512-nuV2CmLS07Gqh5/GrZLuqkU9Bm6H6vcCspM+zjp9TdQlxJtIe+qqEXQChmfc7nWdyr/yz3h45Utk1tUn8Cz5+A==", 87 | "cpu": [ 88 | "x64" 89 | ], 90 | "dev": true, 91 | "optional": true, 92 | "os": [ 93 | "android" 94 | ], 95 | "engines": { 96 | "node": ">=12" 97 | } 98 | }, 99 | "node_modules/@esbuild/darwin-arm64": { 100 | "version": "0.17.3", 101 | "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.17.3.tgz", 102 | "integrity": "sha512-01Hxaaat6m0Xp9AXGM8mjFtqqwDjzlMP0eQq9zll9U85ttVALGCGDuEvra5Feu/NbP5AEP1MaopPwzsTcUq1cw==", 103 | "cpu": [ 104 | "arm64" 105 | ], 106 | "dev": true, 107 | "optional": true, 108 | "os": [ 109 | "darwin" 110 | ], 111 | "engines": { 112 | "node": ">=12" 113 | } 114 | }, 115 | "node_modules/@esbuild/darwin-x64": { 116 | "version": "0.17.3", 117 | "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.17.3.tgz", 118 | "integrity": "sha512-Eo2gq0Q/er2muf8Z83X21UFoB7EU6/m3GNKvrhACJkjVThd0uA+8RfKpfNhuMCl1bKRfBzKOk6xaYKQZ4lZqvA==", 119 | "cpu": [ 120 | "x64" 121 | ], 122 | "dev": true, 123 | "optional": true, 124 | "os": [ 125 | "darwin" 126 | ], 127 | "engines": { 128 | "node": ">=12" 129 | } 130 | }, 131 | "node_modules/@esbuild/freebsd-arm64": { 132 | "version": "0.17.3", 133 | "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.17.3.tgz", 134 | "integrity": "sha512-CN62ESxaquP61n1ZjQP/jZte8CE09M6kNn3baos2SeUfdVBkWN5n6vGp2iKyb/bm/x4JQzEvJgRHLGd5F5b81w==", 135 | "cpu": [ 136 | "arm64" 137 | ], 138 | "dev": true, 139 | "optional": true, 140 | "os": [ 141 | "freebsd" 142 | ], 143 | "engines": { 144 | "node": ">=12" 145 | } 146 | }, 147 | "node_modules/@esbuild/freebsd-x64": { 148 | "version": "0.17.3", 149 | "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.17.3.tgz", 150 | "integrity": "sha512-feq+K8TxIznZE+zhdVurF3WNJ/Sa35dQNYbaqM/wsCbWdzXr5lyq+AaTUSER2cUR+SXPnd/EY75EPRjf4s1SLg==", 151 | "cpu": [ 152 | "x64" 153 | ], 154 | "dev": true, 155 | "optional": true, 156 | "os": [ 157 | "freebsd" 158 | ], 159 | "engines": { 160 | "node": ">=12" 161 | } 162 | }, 163 | "node_modules/@esbuild/linux-arm": { 164 | "version": "0.17.3", 165 | "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.17.3.tgz", 166 | "integrity": "sha512-CLP3EgyNuPcg2cshbwkqYy5bbAgK+VhyfMU7oIYyn+x4Y67xb5C5ylxsNUjRmr8BX+MW3YhVNm6Lq6FKtRTWHQ==", 167 | "cpu": [ 168 | "arm" 169 | ], 170 | "dev": true, 171 | "optional": true, 172 | "os": [ 173 | "linux" 174 | ], 175 | "engines": { 176 | "node": ">=12" 177 | } 178 | }, 179 | "node_modules/@esbuild/linux-arm64": { 180 | "version": "0.17.3", 181 | "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.17.3.tgz", 182 | "integrity": "sha512-JHeZXD4auLYBnrKn6JYJ0o5nWJI9PhChA/Nt0G4MvLaMrvXuWnY93R3a7PiXeJQphpL1nYsaMcoV2QtuvRnF/g==", 183 | "cpu": [ 184 | "arm64" 185 | ], 186 | "dev": true, 187 | "optional": true, 188 | "os": [ 189 | "linux" 190 | ], 191 | "engines": { 192 | "node": ">=12" 193 | } 194 | }, 195 | "node_modules/@esbuild/linux-ia32": { 196 | "version": "0.17.3", 197 | "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.17.3.tgz", 198 | "integrity": "sha512-FyXlD2ZjZqTFh0sOQxFDiWG1uQUEOLbEh9gKN/7pFxck5Vw0qjWSDqbn6C10GAa1rXJpwsntHcmLqydY9ST9ZA==", 199 | "cpu": [ 200 | "ia32" 201 | ], 202 | "dev": true, 203 | "optional": true, 204 | "os": [ 205 | "linux" 206 | ], 207 | "engines": { 208 | "node": ">=12" 209 | } 210 | }, 211 | "node_modules/@esbuild/linux-loong64": { 212 | "version": "0.17.3", 213 | "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.17.3.tgz", 214 | "integrity": "sha512-OrDGMvDBI2g7s04J8dh8/I7eSO+/E7nMDT2Z5IruBfUO/RiigF1OF6xoH33Dn4W/OwAWSUf1s2nXamb28ZklTA==", 215 | "cpu": [ 216 | "loong64" 217 | ], 218 | "dev": true, 219 | "optional": true, 220 | "os": [ 221 | "linux" 222 | ], 223 | "engines": { 224 | "node": ">=12" 225 | } 226 | }, 227 | "node_modules/@esbuild/linux-mips64el": { 228 | "version": "0.17.3", 229 | "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.17.3.tgz", 230 | "integrity": "sha512-DcnUpXnVCJvmv0TzuLwKBC2nsQHle8EIiAJiJ+PipEVC16wHXaPEKP0EqN8WnBe0TPvMITOUlP2aiL5YMld+CQ==", 231 | "cpu": [ 232 | "mips64el" 233 | ], 234 | "dev": true, 235 | "optional": true, 236 | "os": [ 237 | "linux" 238 | ], 239 | "engines": { 240 | "node": ">=12" 241 | } 242 | }, 243 | "node_modules/@esbuild/linux-ppc64": { 244 | "version": "0.17.3", 245 | "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.17.3.tgz", 246 | "integrity": "sha512-BDYf/l1WVhWE+FHAW3FzZPtVlk9QsrwsxGzABmN4g8bTjmhazsId3h127pliDRRu5674k1Y2RWejbpN46N9ZhQ==", 247 | "cpu": [ 248 | "ppc64" 249 | ], 250 | "dev": true, 251 | "optional": true, 252 | "os": [ 253 | "linux" 254 | ], 255 | "engines": { 256 | "node": ">=12" 257 | } 258 | }, 259 | "node_modules/@esbuild/linux-riscv64": { 260 | "version": "0.17.3", 261 | "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.17.3.tgz", 262 | "integrity": "sha512-WViAxWYMRIi+prTJTyV1wnqd2mS2cPqJlN85oscVhXdb/ZTFJdrpaqm/uDsZPGKHtbg5TuRX/ymKdOSk41YZow==", 263 | "cpu": [ 264 | "riscv64" 265 | ], 266 | "dev": true, 267 | "optional": true, 268 | "os": [ 269 | "linux" 270 | ], 271 | "engines": { 272 | "node": ">=12" 273 | } 274 | }, 275 | "node_modules/@esbuild/linux-s390x": { 276 | "version": "0.17.3", 277 | "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.17.3.tgz", 278 | "integrity": "sha512-Iw8lkNHUC4oGP1O/KhumcVy77u2s6+KUjieUqzEU3XuWJqZ+AY7uVMrrCbAiwWTkpQHkr00BuXH5RpC6Sb/7Ug==", 279 | "cpu": [ 280 | "s390x" 281 | ], 282 | "dev": true, 283 | "optional": true, 284 | "os": [ 285 | "linux" 286 | ], 287 | "engines": { 288 | "node": ">=12" 289 | } 290 | }, 291 | "node_modules/@esbuild/linux-x64": { 292 | "version": "0.17.3", 293 | "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.17.3.tgz", 294 | "integrity": "sha512-0AGkWQMzeoeAtXQRNB3s4J1/T2XbigM2/Mn2yU1tQSmQRmHIZdkGbVq2A3aDdNslPyhb9/lH0S5GMTZ4xsjBqg==", 295 | "cpu": [ 296 | "x64" 297 | ], 298 | "dev": true, 299 | "optional": true, 300 | "os": [ 301 | "linux" 302 | ], 303 | "engines": { 304 | "node": ">=12" 305 | } 306 | }, 307 | "node_modules/@esbuild/netbsd-x64": { 308 | "version": "0.17.3", 309 | "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.17.3.tgz", 310 | "integrity": "sha512-4+rR/WHOxIVh53UIQIICryjdoKdHsFZFD4zLSonJ9RRw7bhKzVyXbnRPsWSfwybYqw9sB7ots/SYyufL1mBpEg==", 311 | "cpu": [ 312 | "x64" 313 | ], 314 | "dev": true, 315 | "optional": true, 316 | "os": [ 317 | "netbsd" 318 | ], 319 | "engines": { 320 | "node": ">=12" 321 | } 322 | }, 323 | "node_modules/@esbuild/openbsd-x64": { 324 | "version": "0.17.3", 325 | "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.17.3.tgz", 326 | "integrity": "sha512-cVpWnkx9IYg99EjGxa5Gc0XmqumtAwK3aoz7O4Dii2vko+qXbkHoujWA68cqXjhh6TsLaQelfDO4MVnyr+ODeA==", 327 | "cpu": [ 328 | "x64" 329 | ], 330 | "dev": true, 331 | "optional": true, 332 | "os": [ 333 | "openbsd" 334 | ], 335 | "engines": { 336 | "node": ">=12" 337 | } 338 | }, 339 | "node_modules/@esbuild/sunos-x64": { 340 | "version": "0.17.3", 341 | "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.17.3.tgz", 342 | "integrity": "sha512-RxmhKLbTCDAY2xOfrww6ieIZkZF+KBqG7S2Ako2SljKXRFi+0863PspK74QQ7JpmWwncChY25JTJSbVBYGQk2Q==", 343 | "cpu": [ 344 | "x64" 345 | ], 346 | "dev": true, 347 | "optional": true, 348 | "os": [ 349 | "sunos" 350 | ], 351 | "engines": { 352 | "node": ">=12" 353 | } 354 | }, 355 | "node_modules/@esbuild/win32-arm64": { 356 | "version": "0.17.3", 357 | "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.17.3.tgz", 358 | "integrity": "sha512-0r36VeEJ4efwmofxVJRXDjVRP2jTmv877zc+i+Pc7MNsIr38NfsjkQj23AfF7l0WbB+RQ7VUb+LDiqC/KY/M/A==", 359 | "cpu": [ 360 | "arm64" 361 | ], 362 | "dev": true, 363 | "optional": true, 364 | "os": [ 365 | "win32" 366 | ], 367 | "engines": { 368 | "node": ">=12" 369 | } 370 | }, 371 | "node_modules/@esbuild/win32-ia32": { 372 | "version": "0.17.3", 373 | "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.17.3.tgz", 374 | "integrity": "sha512-wgO6rc7uGStH22nur4aLFcq7Wh86bE9cOFmfTr/yxN3BXvDEdCSXyKkO+U5JIt53eTOgC47v9k/C1bITWL/Teg==", 375 | "cpu": [ 376 | "ia32" 377 | ], 378 | "dev": true, 379 | "optional": true, 380 | "os": [ 381 | "win32" 382 | ], 383 | "engines": { 384 | "node": ">=12" 385 | } 386 | }, 387 | "node_modules/@esbuild/win32-x64": { 388 | "version": "0.17.3", 389 | "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.17.3.tgz", 390 | "integrity": "sha512-FdVl64OIuiKjgXBjwZaJLKp0eaEckifbhn10dXWhysMJkWblg3OEEGKSIyhiD5RSgAya8WzP3DNkngtIg3Nt7g==", 391 | "cpu": [ 392 | "x64" 393 | ], 394 | "dev": true, 395 | "optional": true, 396 | "os": [ 397 | "win32" 398 | ], 399 | "engines": { 400 | "node": ">=12" 401 | } 402 | }, 403 | "node_modules/@eslint-community/eslint-utils": { 404 | "version": "4.4.0", 405 | "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", 406 | "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", 407 | "dev": true, 408 | "peer": true, 409 | "dependencies": { 410 | "eslint-visitor-keys": "^3.3.0" 411 | }, 412 | "engines": { 413 | "node": "^12.22.0 || ^14.17.0 || >=16.0.0" 414 | }, 415 | "peerDependencies": { 416 | "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" 417 | } 418 | }, 419 | "node_modules/@eslint-community/regexpp": { 420 | "version": "4.10.0", 421 | "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz", 422 | "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==", 423 | "dev": true, 424 | "peer": true, 425 | "engines": { 426 | "node": "^12.0.0 || ^14.0.0 || >=16.0.0" 427 | } 428 | }, 429 | "node_modules/@eslint/eslintrc": { 430 | "version": "2.1.4", 431 | "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", 432 | "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", 433 | "dev": true, 434 | "peer": true, 435 | "dependencies": { 436 | "ajv": "^6.12.4", 437 | "debug": "^4.3.2", 438 | "espree": "^9.6.0", 439 | "globals": "^13.19.0", 440 | "ignore": "^5.2.0", 441 | "import-fresh": "^3.2.1", 442 | "js-yaml": "^4.1.0", 443 | "minimatch": "^3.1.2", 444 | "strip-json-comments": "^3.1.1" 445 | }, 446 | "engines": { 447 | "node": "^12.22.0 || ^14.17.0 || >=16.0.0" 448 | }, 449 | "funding": { 450 | "url": "https://opencollective.com/eslint" 451 | } 452 | }, 453 | "node_modules/@eslint/js": { 454 | "version": "8.57.0", 455 | "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.0.tgz", 456 | "integrity": "sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==", 457 | "dev": true, 458 | "peer": true, 459 | "engines": { 460 | "node": "^12.22.0 || ^14.17.0 || >=16.0.0" 461 | } 462 | }, 463 | "node_modules/@humanwhocodes/config-array": { 464 | "version": "0.11.14", 465 | "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", 466 | "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==", 467 | "dev": true, 468 | "peer": true, 469 | "dependencies": { 470 | "@humanwhocodes/object-schema": "^2.0.2", 471 | "debug": "^4.3.1", 472 | "minimatch": "^3.0.5" 473 | }, 474 | "engines": { 475 | "node": ">=10.10.0" 476 | } 477 | }, 478 | "node_modules/@humanwhocodes/module-importer": { 479 | "version": "1.0.1", 480 | "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", 481 | "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", 482 | "dev": true, 483 | "peer": true, 484 | "engines": { 485 | "node": ">=12.22" 486 | }, 487 | "funding": { 488 | "type": "github", 489 | "url": "https://github.com/sponsors/nzakas" 490 | } 491 | }, 492 | "node_modules/@humanwhocodes/object-schema": { 493 | "version": "2.0.2", 494 | "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.2.tgz", 495 | "integrity": "sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==", 496 | "dev": true, 497 | "peer": true 498 | }, 499 | "node_modules/@nodelib/fs.scandir": { 500 | "version": "2.1.5", 501 | "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", 502 | "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", 503 | "dev": true, 504 | "dependencies": { 505 | "@nodelib/fs.stat": "2.0.5", 506 | "run-parallel": "^1.1.9" 507 | }, 508 | "engines": { 509 | "node": ">= 8" 510 | } 511 | }, 512 | "node_modules/@nodelib/fs.stat": { 513 | "version": "2.0.5", 514 | "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", 515 | "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", 516 | "dev": true, 517 | "engines": { 518 | "node": ">= 8" 519 | } 520 | }, 521 | "node_modules/@nodelib/fs.walk": { 522 | "version": "1.2.8", 523 | "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", 524 | "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", 525 | "dev": true, 526 | "dependencies": { 527 | "@nodelib/fs.scandir": "2.1.5", 528 | "fastq": "^1.6.0" 529 | }, 530 | "engines": { 531 | "node": ">= 8" 532 | } 533 | }, 534 | "node_modules/@types/codemirror": { 535 | "version": "5.60.8", 536 | "resolved": "https://registry.npmjs.org/@types/codemirror/-/codemirror-5.60.8.tgz", 537 | "integrity": "sha512-VjFgDF/eB+Aklcy15TtOTLQeMjTo07k7KAjql8OK5Dirr7a6sJY4T1uVBDuTVG9VEmn1uUsohOpYnVfgC6/jyw==", 538 | "dev": true, 539 | "dependencies": { 540 | "@types/tern": "*" 541 | } 542 | }, 543 | "node_modules/@types/estree": { 544 | "version": "1.0.5", 545 | "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", 546 | "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", 547 | "dev": true 548 | }, 549 | "node_modules/@types/json-schema": { 550 | "version": "7.0.15", 551 | "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", 552 | "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", 553 | "dev": true 554 | }, 555 | "node_modules/@types/node": { 556 | "version": "16.18.87", 557 | "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.87.tgz", 558 | "integrity": "sha512-+IzfhNirR/MDbXz6Om5eHV54D9mQlEMGag6AgEzlju0xH3M8baCXYwqQ6RKgGMpn9wSTx6Ltya/0y4Z8eSfdLw==", 559 | "dev": true 560 | }, 561 | "node_modules/@types/tern": { 562 | "version": "0.23.9", 563 | "resolved": "https://registry.npmjs.org/@types/tern/-/tern-0.23.9.tgz", 564 | "integrity": "sha512-ypzHFE/wBzh+BlH6rrBgS5I/Z7RD21pGhZ2rltb/+ZrVM1awdZwjx7hE5XfuYgHWk9uvV5HLZN3SloevCAp3Bw==", 565 | "dev": true, 566 | "dependencies": { 567 | "@types/estree": "*" 568 | } 569 | }, 570 | "node_modules/@typescript-eslint/eslint-plugin": { 571 | "version": "5.29.0", 572 | "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.29.0.tgz", 573 | "integrity": "sha512-kgTsISt9pM53yRFQmLZ4npj99yGl3x3Pl7z4eA66OuTzAGC4bQB5H5fuLwPnqTKU3yyrrg4MIhjF17UYnL4c0w==", 574 | "dev": true, 575 | "dependencies": { 576 | "@typescript-eslint/scope-manager": "5.29.0", 577 | "@typescript-eslint/type-utils": "5.29.0", 578 | "@typescript-eslint/utils": "5.29.0", 579 | "debug": "^4.3.4", 580 | "functional-red-black-tree": "^1.0.1", 581 | "ignore": "^5.2.0", 582 | "regexpp": "^3.2.0", 583 | "semver": "^7.3.7", 584 | "tsutils": "^3.21.0" 585 | }, 586 | "engines": { 587 | "node": "^12.22.0 || ^14.17.0 || >=16.0.0" 588 | }, 589 | "funding": { 590 | "type": "opencollective", 591 | "url": "https://opencollective.com/typescript-eslint" 592 | }, 593 | "peerDependencies": { 594 | "@typescript-eslint/parser": "^5.0.0", 595 | "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" 596 | }, 597 | "peerDependenciesMeta": { 598 | "typescript": { 599 | "optional": true 600 | } 601 | } 602 | }, 603 | "node_modules/@typescript-eslint/parser": { 604 | "version": "5.29.0", 605 | "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.29.0.tgz", 606 | "integrity": "sha512-ruKWTv+x0OOxbzIw9nW5oWlUopvP/IQDjB5ZqmTglLIoDTctLlAJpAQFpNPJP/ZI7hTT9sARBosEfaKbcFuECw==", 607 | "dev": true, 608 | "dependencies": { 609 | "@typescript-eslint/scope-manager": "5.29.0", 610 | "@typescript-eslint/types": "5.29.0", 611 | "@typescript-eslint/typescript-estree": "5.29.0", 612 | "debug": "^4.3.4" 613 | }, 614 | "engines": { 615 | "node": "^12.22.0 || ^14.17.0 || >=16.0.0" 616 | }, 617 | "funding": { 618 | "type": "opencollective", 619 | "url": "https://opencollective.com/typescript-eslint" 620 | }, 621 | "peerDependencies": { 622 | "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" 623 | }, 624 | "peerDependenciesMeta": { 625 | "typescript": { 626 | "optional": true 627 | } 628 | } 629 | }, 630 | "node_modules/@typescript-eslint/scope-manager": { 631 | "version": "5.29.0", 632 | "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.29.0.tgz", 633 | "integrity": "sha512-etbXUT0FygFi2ihcxDZjz21LtC+Eps9V2xVx09zFoN44RRHPrkMflidGMI+2dUs821zR1tDS6Oc9IXxIjOUZwA==", 634 | "dev": true, 635 | "dependencies": { 636 | "@typescript-eslint/types": "5.29.0", 637 | "@typescript-eslint/visitor-keys": "5.29.0" 638 | }, 639 | "engines": { 640 | "node": "^12.22.0 || ^14.17.0 || >=16.0.0" 641 | }, 642 | "funding": { 643 | "type": "opencollective", 644 | "url": "https://opencollective.com/typescript-eslint" 645 | } 646 | }, 647 | "node_modules/@typescript-eslint/type-utils": { 648 | "version": "5.29.0", 649 | "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.29.0.tgz", 650 | "integrity": "sha512-JK6bAaaiJozbox3K220VRfCzLa9n0ib/J+FHIwnaV3Enw/TO267qe0pM1b1QrrEuy6xun374XEAsRlA86JJnyg==", 651 | "dev": true, 652 | "dependencies": { 653 | "@typescript-eslint/utils": "5.29.0", 654 | "debug": "^4.3.4", 655 | "tsutils": "^3.21.0" 656 | }, 657 | "engines": { 658 | "node": "^12.22.0 || ^14.17.0 || >=16.0.0" 659 | }, 660 | "funding": { 661 | "type": "opencollective", 662 | "url": "https://opencollective.com/typescript-eslint" 663 | }, 664 | "peerDependencies": { 665 | "eslint": "*" 666 | }, 667 | "peerDependenciesMeta": { 668 | "typescript": { 669 | "optional": true 670 | } 671 | } 672 | }, 673 | "node_modules/@typescript-eslint/types": { 674 | "version": "5.29.0", 675 | "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.29.0.tgz", 676 | "integrity": "sha512-X99VbqvAXOMdVyfFmksMy3u8p8yoRGITgU1joBJPzeYa0rhdf5ok9S56/itRoUSh99fiDoMtarSIJXo7H/SnOg==", 677 | "dev": true, 678 | "engines": { 679 | "node": "^12.22.0 || ^14.17.0 || >=16.0.0" 680 | }, 681 | "funding": { 682 | "type": "opencollective", 683 | "url": "https://opencollective.com/typescript-eslint" 684 | } 685 | }, 686 | "node_modules/@typescript-eslint/typescript-estree": { 687 | "version": "5.29.0", 688 | "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.29.0.tgz", 689 | "integrity": "sha512-mQvSUJ/JjGBdvo+1LwC+GY2XmSYjK1nAaVw2emp/E61wEVYEyibRHCqm1I1vEKbXCpUKuW4G7u9ZCaZhJbLoNQ==", 690 | "dev": true, 691 | "dependencies": { 692 | "@typescript-eslint/types": "5.29.0", 693 | "@typescript-eslint/visitor-keys": "5.29.0", 694 | "debug": "^4.3.4", 695 | "globby": "^11.1.0", 696 | "is-glob": "^4.0.3", 697 | "semver": "^7.3.7", 698 | "tsutils": "^3.21.0" 699 | }, 700 | "engines": { 701 | "node": "^12.22.0 || ^14.17.0 || >=16.0.0" 702 | }, 703 | "funding": { 704 | "type": "opencollective", 705 | "url": "https://opencollective.com/typescript-eslint" 706 | }, 707 | "peerDependenciesMeta": { 708 | "typescript": { 709 | "optional": true 710 | } 711 | } 712 | }, 713 | "node_modules/@typescript-eslint/utils": { 714 | "version": "5.29.0", 715 | "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.29.0.tgz", 716 | "integrity": "sha512-3Eos6uP1nyLOBayc/VUdKZikV90HahXE5Dx9L5YlSd/7ylQPXhLk1BYb29SDgnBnTp+jmSZUU0QxUiyHgW4p7A==", 717 | "dev": true, 718 | "dependencies": { 719 | "@types/json-schema": "^7.0.9", 720 | "@typescript-eslint/scope-manager": "5.29.0", 721 | "@typescript-eslint/types": "5.29.0", 722 | "@typescript-eslint/typescript-estree": "5.29.0", 723 | "eslint-scope": "^5.1.1", 724 | "eslint-utils": "^3.0.0" 725 | }, 726 | "engines": { 727 | "node": "^12.22.0 || ^14.17.0 || >=16.0.0" 728 | }, 729 | "funding": { 730 | "type": "opencollective", 731 | "url": "https://opencollective.com/typescript-eslint" 732 | }, 733 | "peerDependencies": { 734 | "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" 735 | } 736 | }, 737 | "node_modules/@typescript-eslint/visitor-keys": { 738 | "version": "5.29.0", 739 | "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.29.0.tgz", 740 | "integrity": "sha512-Hpb/mCWsjILvikMQoZIE3voc9wtQcS0A9FUw3h8bhr9UxBdtI/tw1ZDZUOXHXLOVMedKCH5NxyzATwnU78bWCQ==", 741 | "dev": true, 742 | "dependencies": { 743 | "@typescript-eslint/types": "5.29.0", 744 | "eslint-visitor-keys": "^3.3.0" 745 | }, 746 | "engines": { 747 | "node": "^12.22.0 || ^14.17.0 || >=16.0.0" 748 | }, 749 | "funding": { 750 | "type": "opencollective", 751 | "url": "https://opencollective.com/typescript-eslint" 752 | } 753 | }, 754 | "node_modules/@ungap/structured-clone": { 755 | "version": "1.2.0", 756 | "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", 757 | "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", 758 | "dev": true, 759 | "peer": true 760 | }, 761 | "node_modules/acorn": { 762 | "version": "8.11.3", 763 | "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", 764 | "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", 765 | "dev": true, 766 | "peer": true, 767 | "bin": { 768 | "acorn": "bin/acorn" 769 | }, 770 | "engines": { 771 | "node": ">=0.4.0" 772 | } 773 | }, 774 | "node_modules/acorn-jsx": { 775 | "version": "5.3.2", 776 | "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", 777 | "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", 778 | "dev": true, 779 | "peer": true, 780 | "peerDependencies": { 781 | "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" 782 | } 783 | }, 784 | "node_modules/ajv": { 785 | "version": "6.12.6", 786 | "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", 787 | "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", 788 | "dev": true, 789 | "peer": true, 790 | "dependencies": { 791 | "fast-deep-equal": "^3.1.1", 792 | "fast-json-stable-stringify": "^2.0.0", 793 | "json-schema-traverse": "^0.4.1", 794 | "uri-js": "^4.2.2" 795 | }, 796 | "funding": { 797 | "type": "github", 798 | "url": "https://github.com/sponsors/epoberezkin" 799 | } 800 | }, 801 | "node_modules/ansi-regex": { 802 | "version": "5.0.1", 803 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", 804 | "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", 805 | "dev": true, 806 | "peer": true, 807 | "engines": { 808 | "node": ">=8" 809 | } 810 | }, 811 | "node_modules/ansi-styles": { 812 | "version": "4.3.0", 813 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", 814 | "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", 815 | "dev": true, 816 | "peer": true, 817 | "dependencies": { 818 | "color-convert": "^2.0.1" 819 | }, 820 | "engines": { 821 | "node": ">=8" 822 | }, 823 | "funding": { 824 | "url": "https://github.com/chalk/ansi-styles?sponsor=1" 825 | } 826 | }, 827 | "node_modules/argparse": { 828 | "version": "2.0.1", 829 | "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", 830 | "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", 831 | "dev": true, 832 | "peer": true 833 | }, 834 | "node_modules/array-union": { 835 | "version": "2.1.0", 836 | "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", 837 | "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", 838 | "dev": true, 839 | "engines": { 840 | "node": ">=8" 841 | } 842 | }, 843 | "node_modules/balanced-match": { 844 | "version": "1.0.2", 845 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", 846 | "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", 847 | "dev": true, 848 | "peer": true 849 | }, 850 | "node_modules/brace-expansion": { 851 | "version": "1.1.11", 852 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 853 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 854 | "dev": true, 855 | "peer": true, 856 | "dependencies": { 857 | "balanced-match": "^1.0.0", 858 | "concat-map": "0.0.1" 859 | } 860 | }, 861 | "node_modules/braces": { 862 | "version": "3.0.2", 863 | "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", 864 | "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", 865 | "dev": true, 866 | "dependencies": { 867 | "fill-range": "^7.0.1" 868 | }, 869 | "engines": { 870 | "node": ">=8" 871 | } 872 | }, 873 | "node_modules/builtin-modules": { 874 | "version": "3.3.0", 875 | "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz", 876 | "integrity": "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==", 877 | "dev": true, 878 | "engines": { 879 | "node": ">=6" 880 | }, 881 | "funding": { 882 | "url": "https://github.com/sponsors/sindresorhus" 883 | } 884 | }, 885 | "node_modules/callsites": { 886 | "version": "3.1.0", 887 | "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", 888 | "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", 889 | "dev": true, 890 | "peer": true, 891 | "engines": { 892 | "node": ">=6" 893 | } 894 | }, 895 | "node_modules/chalk": { 896 | "version": "4.1.2", 897 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", 898 | "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", 899 | "dev": true, 900 | "peer": true, 901 | "dependencies": { 902 | "ansi-styles": "^4.1.0", 903 | "supports-color": "^7.1.0" 904 | }, 905 | "engines": { 906 | "node": ">=10" 907 | }, 908 | "funding": { 909 | "url": "https://github.com/chalk/chalk?sponsor=1" 910 | } 911 | }, 912 | "node_modules/color-convert": { 913 | "version": "2.0.1", 914 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", 915 | "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", 916 | "dev": true, 917 | "peer": true, 918 | "dependencies": { 919 | "color-name": "~1.1.4" 920 | }, 921 | "engines": { 922 | "node": ">=7.0.0" 923 | } 924 | }, 925 | "node_modules/color-name": { 926 | "version": "1.1.4", 927 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", 928 | "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", 929 | "dev": true, 930 | "peer": true 931 | }, 932 | "node_modules/concat-map": { 933 | "version": "0.0.1", 934 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 935 | "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", 936 | "dev": true, 937 | "peer": true 938 | }, 939 | "node_modules/cross-spawn": { 940 | "version": "7.0.3", 941 | "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", 942 | "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", 943 | "dev": true, 944 | "peer": true, 945 | "dependencies": { 946 | "path-key": "^3.1.0", 947 | "shebang-command": "^2.0.0", 948 | "which": "^2.0.1" 949 | }, 950 | "engines": { 951 | "node": ">= 8" 952 | } 953 | }, 954 | "node_modules/debug": { 955 | "version": "4.3.4", 956 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", 957 | "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", 958 | "dev": true, 959 | "dependencies": { 960 | "ms": "2.1.2" 961 | }, 962 | "engines": { 963 | "node": ">=6.0" 964 | }, 965 | "peerDependenciesMeta": { 966 | "supports-color": { 967 | "optional": true 968 | } 969 | } 970 | }, 971 | "node_modules/deep-is": { 972 | "version": "0.1.4", 973 | "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", 974 | "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", 975 | "dev": true, 976 | "peer": true 977 | }, 978 | "node_modules/dir-glob": { 979 | "version": "3.0.1", 980 | "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", 981 | "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", 982 | "dev": true, 983 | "dependencies": { 984 | "path-type": "^4.0.0" 985 | }, 986 | "engines": { 987 | "node": ">=8" 988 | } 989 | }, 990 | "node_modules/doctrine": { 991 | "version": "3.0.0", 992 | "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", 993 | "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", 994 | "dev": true, 995 | "peer": true, 996 | "dependencies": { 997 | "esutils": "^2.0.2" 998 | }, 999 | "engines": { 1000 | "node": ">=6.0.0" 1001 | } 1002 | }, 1003 | "node_modules/esbuild": { 1004 | "version": "0.17.3", 1005 | "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.17.3.tgz", 1006 | "integrity": "sha512-9n3AsBRe6sIyOc6kmoXg2ypCLgf3eZSraWFRpnkto+svt8cZNuKTkb1bhQcitBcvIqjNiK7K0J3KPmwGSfkA8g==", 1007 | "dev": true, 1008 | "hasInstallScript": true, 1009 | "bin": { 1010 | "esbuild": "bin/esbuild" 1011 | }, 1012 | "engines": { 1013 | "node": ">=12" 1014 | }, 1015 | "optionalDependencies": { 1016 | "@esbuild/android-arm": "0.17.3", 1017 | "@esbuild/android-arm64": "0.17.3", 1018 | "@esbuild/android-x64": "0.17.3", 1019 | "@esbuild/darwin-arm64": "0.17.3", 1020 | "@esbuild/darwin-x64": "0.17.3", 1021 | "@esbuild/freebsd-arm64": "0.17.3", 1022 | "@esbuild/freebsd-x64": "0.17.3", 1023 | "@esbuild/linux-arm": "0.17.3", 1024 | "@esbuild/linux-arm64": "0.17.3", 1025 | "@esbuild/linux-ia32": "0.17.3", 1026 | "@esbuild/linux-loong64": "0.17.3", 1027 | "@esbuild/linux-mips64el": "0.17.3", 1028 | "@esbuild/linux-ppc64": "0.17.3", 1029 | "@esbuild/linux-riscv64": "0.17.3", 1030 | "@esbuild/linux-s390x": "0.17.3", 1031 | "@esbuild/linux-x64": "0.17.3", 1032 | "@esbuild/netbsd-x64": "0.17.3", 1033 | "@esbuild/openbsd-x64": "0.17.3", 1034 | "@esbuild/sunos-x64": "0.17.3", 1035 | "@esbuild/win32-arm64": "0.17.3", 1036 | "@esbuild/win32-ia32": "0.17.3", 1037 | "@esbuild/win32-x64": "0.17.3" 1038 | } 1039 | }, 1040 | "node_modules/escape-string-regexp": { 1041 | "version": "4.0.0", 1042 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", 1043 | "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", 1044 | "dev": true, 1045 | "peer": true, 1046 | "engines": { 1047 | "node": ">=10" 1048 | }, 1049 | "funding": { 1050 | "url": "https://github.com/sponsors/sindresorhus" 1051 | } 1052 | }, 1053 | "node_modules/eslint": { 1054 | "version": "8.57.0", 1055 | "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz", 1056 | "integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==", 1057 | "dev": true, 1058 | "peer": true, 1059 | "dependencies": { 1060 | "@eslint-community/eslint-utils": "^4.2.0", 1061 | "@eslint-community/regexpp": "^4.6.1", 1062 | "@eslint/eslintrc": "^2.1.4", 1063 | "@eslint/js": "8.57.0", 1064 | "@humanwhocodes/config-array": "^0.11.14", 1065 | "@humanwhocodes/module-importer": "^1.0.1", 1066 | "@nodelib/fs.walk": "^1.2.8", 1067 | "@ungap/structured-clone": "^1.2.0", 1068 | "ajv": "^6.12.4", 1069 | "chalk": "^4.0.0", 1070 | "cross-spawn": "^7.0.2", 1071 | "debug": "^4.3.2", 1072 | "doctrine": "^3.0.0", 1073 | "escape-string-regexp": "^4.0.0", 1074 | "eslint-scope": "^7.2.2", 1075 | "eslint-visitor-keys": "^3.4.3", 1076 | "espree": "^9.6.1", 1077 | "esquery": "^1.4.2", 1078 | "esutils": "^2.0.2", 1079 | "fast-deep-equal": "^3.1.3", 1080 | "file-entry-cache": "^6.0.1", 1081 | "find-up": "^5.0.0", 1082 | "glob-parent": "^6.0.2", 1083 | "globals": "^13.19.0", 1084 | "graphemer": "^1.4.0", 1085 | "ignore": "^5.2.0", 1086 | "imurmurhash": "^0.1.4", 1087 | "is-glob": "^4.0.0", 1088 | "is-path-inside": "^3.0.3", 1089 | "js-yaml": "^4.1.0", 1090 | "json-stable-stringify-without-jsonify": "^1.0.1", 1091 | "levn": "^0.4.1", 1092 | "lodash.merge": "^4.6.2", 1093 | "minimatch": "^3.1.2", 1094 | "natural-compare": "^1.4.0", 1095 | "optionator": "^0.9.3", 1096 | "strip-ansi": "^6.0.1", 1097 | "text-table": "^0.2.0" 1098 | }, 1099 | "bin": { 1100 | "eslint": "bin/eslint.js" 1101 | }, 1102 | "engines": { 1103 | "node": "^12.22.0 || ^14.17.0 || >=16.0.0" 1104 | }, 1105 | "funding": { 1106 | "url": "https://opencollective.com/eslint" 1107 | } 1108 | }, 1109 | "node_modules/eslint-scope": { 1110 | "version": "5.1.1", 1111 | "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", 1112 | "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", 1113 | "dev": true, 1114 | "dependencies": { 1115 | "esrecurse": "^4.3.0", 1116 | "estraverse": "^4.1.1" 1117 | }, 1118 | "engines": { 1119 | "node": ">=8.0.0" 1120 | } 1121 | }, 1122 | "node_modules/eslint-utils": { 1123 | "version": "3.0.0", 1124 | "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", 1125 | "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", 1126 | "dev": true, 1127 | "dependencies": { 1128 | "eslint-visitor-keys": "^2.0.0" 1129 | }, 1130 | "engines": { 1131 | "node": "^10.0.0 || ^12.0.0 || >= 14.0.0" 1132 | }, 1133 | "funding": { 1134 | "url": "https://github.com/sponsors/mysticatea" 1135 | }, 1136 | "peerDependencies": { 1137 | "eslint": ">=5" 1138 | } 1139 | }, 1140 | "node_modules/eslint-utils/node_modules/eslint-visitor-keys": { 1141 | "version": "2.1.0", 1142 | "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", 1143 | "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", 1144 | "dev": true, 1145 | "engines": { 1146 | "node": ">=10" 1147 | } 1148 | }, 1149 | "node_modules/eslint-visitor-keys": { 1150 | "version": "3.4.3", 1151 | "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", 1152 | "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", 1153 | "dev": true, 1154 | "engines": { 1155 | "node": "^12.22.0 || ^14.17.0 || >=16.0.0" 1156 | }, 1157 | "funding": { 1158 | "url": "https://opencollective.com/eslint" 1159 | } 1160 | }, 1161 | "node_modules/eslint/node_modules/eslint-scope": { 1162 | "version": "7.2.2", 1163 | "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", 1164 | "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", 1165 | "dev": true, 1166 | "peer": true, 1167 | "dependencies": { 1168 | "esrecurse": "^4.3.0", 1169 | "estraverse": "^5.2.0" 1170 | }, 1171 | "engines": { 1172 | "node": "^12.22.0 || ^14.17.0 || >=16.0.0" 1173 | }, 1174 | "funding": { 1175 | "url": "https://opencollective.com/eslint" 1176 | } 1177 | }, 1178 | "node_modules/eslint/node_modules/estraverse": { 1179 | "version": "5.3.0", 1180 | "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", 1181 | "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", 1182 | "dev": true, 1183 | "peer": true, 1184 | "engines": { 1185 | "node": ">=4.0" 1186 | } 1187 | }, 1188 | "node_modules/espree": { 1189 | "version": "9.6.1", 1190 | "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", 1191 | "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", 1192 | "dev": true, 1193 | "peer": true, 1194 | "dependencies": { 1195 | "acorn": "^8.9.0", 1196 | "acorn-jsx": "^5.3.2", 1197 | "eslint-visitor-keys": "^3.4.1" 1198 | }, 1199 | "engines": { 1200 | "node": "^12.22.0 || ^14.17.0 || >=16.0.0" 1201 | }, 1202 | "funding": { 1203 | "url": "https://opencollective.com/eslint" 1204 | } 1205 | }, 1206 | "node_modules/esquery": { 1207 | "version": "1.5.0", 1208 | "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", 1209 | "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", 1210 | "dev": true, 1211 | "peer": true, 1212 | "dependencies": { 1213 | "estraverse": "^5.1.0" 1214 | }, 1215 | "engines": { 1216 | "node": ">=0.10" 1217 | } 1218 | }, 1219 | "node_modules/esquery/node_modules/estraverse": { 1220 | "version": "5.3.0", 1221 | "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", 1222 | "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", 1223 | "dev": true, 1224 | "peer": true, 1225 | "engines": { 1226 | "node": ">=4.0" 1227 | } 1228 | }, 1229 | "node_modules/esrecurse": { 1230 | "version": "4.3.0", 1231 | "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", 1232 | "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", 1233 | "dev": true, 1234 | "dependencies": { 1235 | "estraverse": "^5.2.0" 1236 | }, 1237 | "engines": { 1238 | "node": ">=4.0" 1239 | } 1240 | }, 1241 | "node_modules/esrecurse/node_modules/estraverse": { 1242 | "version": "5.3.0", 1243 | "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", 1244 | "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", 1245 | "dev": true, 1246 | "engines": { 1247 | "node": ">=4.0" 1248 | } 1249 | }, 1250 | "node_modules/estraverse": { 1251 | "version": "4.3.0", 1252 | "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", 1253 | "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", 1254 | "dev": true, 1255 | "engines": { 1256 | "node": ">=4.0" 1257 | } 1258 | }, 1259 | "node_modules/esutils": { 1260 | "version": "2.0.3", 1261 | "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", 1262 | "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", 1263 | "dev": true, 1264 | "peer": true, 1265 | "engines": { 1266 | "node": ">=0.10.0" 1267 | } 1268 | }, 1269 | "node_modules/fast-deep-equal": { 1270 | "version": "3.1.3", 1271 | "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", 1272 | "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", 1273 | "dev": true, 1274 | "peer": true 1275 | }, 1276 | "node_modules/fast-glob": { 1277 | "version": "3.3.2", 1278 | "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", 1279 | "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", 1280 | "dev": true, 1281 | "dependencies": { 1282 | "@nodelib/fs.stat": "^2.0.2", 1283 | "@nodelib/fs.walk": "^1.2.3", 1284 | "glob-parent": "^5.1.2", 1285 | "merge2": "^1.3.0", 1286 | "micromatch": "^4.0.4" 1287 | }, 1288 | "engines": { 1289 | "node": ">=8.6.0" 1290 | } 1291 | }, 1292 | "node_modules/fast-glob/node_modules/glob-parent": { 1293 | "version": "5.1.2", 1294 | "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", 1295 | "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", 1296 | "dev": true, 1297 | "dependencies": { 1298 | "is-glob": "^4.0.1" 1299 | }, 1300 | "engines": { 1301 | "node": ">= 6" 1302 | } 1303 | }, 1304 | "node_modules/fast-json-stable-stringify": { 1305 | "version": "2.1.0", 1306 | "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", 1307 | "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", 1308 | "dev": true, 1309 | "peer": true 1310 | }, 1311 | "node_modules/fast-levenshtein": { 1312 | "version": "2.0.6", 1313 | "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", 1314 | "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", 1315 | "dev": true, 1316 | "peer": true 1317 | }, 1318 | "node_modules/fastq": { 1319 | "version": "1.17.1", 1320 | "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", 1321 | "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", 1322 | "dev": true, 1323 | "dependencies": { 1324 | "reusify": "^1.0.4" 1325 | } 1326 | }, 1327 | "node_modules/file-entry-cache": { 1328 | "version": "6.0.1", 1329 | "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", 1330 | "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", 1331 | "dev": true, 1332 | "peer": true, 1333 | "dependencies": { 1334 | "flat-cache": "^3.0.4" 1335 | }, 1336 | "engines": { 1337 | "node": "^10.12.0 || >=12.0.0" 1338 | } 1339 | }, 1340 | "node_modules/fill-range": { 1341 | "version": "7.0.1", 1342 | "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", 1343 | "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", 1344 | "dev": true, 1345 | "dependencies": { 1346 | "to-regex-range": "^5.0.1" 1347 | }, 1348 | "engines": { 1349 | "node": ">=8" 1350 | } 1351 | }, 1352 | "node_modules/find-up": { 1353 | "version": "5.0.0", 1354 | "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", 1355 | "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", 1356 | "dev": true, 1357 | "peer": true, 1358 | "dependencies": { 1359 | "locate-path": "^6.0.0", 1360 | "path-exists": "^4.0.0" 1361 | }, 1362 | "engines": { 1363 | "node": ">=10" 1364 | }, 1365 | "funding": { 1366 | "url": "https://github.com/sponsors/sindresorhus" 1367 | } 1368 | }, 1369 | "node_modules/flat-cache": { 1370 | "version": "3.2.0", 1371 | "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", 1372 | "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", 1373 | "dev": true, 1374 | "peer": true, 1375 | "dependencies": { 1376 | "flatted": "^3.2.9", 1377 | "keyv": "^4.5.3", 1378 | "rimraf": "^3.0.2" 1379 | }, 1380 | "engines": { 1381 | "node": "^10.12.0 || >=12.0.0" 1382 | } 1383 | }, 1384 | "node_modules/flatted": { 1385 | "version": "3.3.1", 1386 | "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", 1387 | "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", 1388 | "dev": true, 1389 | "peer": true 1390 | }, 1391 | "node_modules/fs.realpath": { 1392 | "version": "1.0.0", 1393 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 1394 | "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", 1395 | "dev": true, 1396 | "peer": true 1397 | }, 1398 | "node_modules/functional-red-black-tree": { 1399 | "version": "1.0.1", 1400 | "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", 1401 | "integrity": "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==", 1402 | "dev": true 1403 | }, 1404 | "node_modules/glob": { 1405 | "version": "7.2.3", 1406 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", 1407 | "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", 1408 | "dev": true, 1409 | "peer": true, 1410 | "dependencies": { 1411 | "fs.realpath": "^1.0.0", 1412 | "inflight": "^1.0.4", 1413 | "inherits": "2", 1414 | "minimatch": "^3.1.1", 1415 | "once": "^1.3.0", 1416 | "path-is-absolute": "^1.0.0" 1417 | }, 1418 | "engines": { 1419 | "node": "*" 1420 | }, 1421 | "funding": { 1422 | "url": "https://github.com/sponsors/isaacs" 1423 | } 1424 | }, 1425 | "node_modules/glob-parent": { 1426 | "version": "6.0.2", 1427 | "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", 1428 | "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", 1429 | "dev": true, 1430 | "peer": true, 1431 | "dependencies": { 1432 | "is-glob": "^4.0.3" 1433 | }, 1434 | "engines": { 1435 | "node": ">=10.13.0" 1436 | } 1437 | }, 1438 | "node_modules/globals": { 1439 | "version": "13.24.0", 1440 | "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", 1441 | "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", 1442 | "dev": true, 1443 | "peer": true, 1444 | "dependencies": { 1445 | "type-fest": "^0.20.2" 1446 | }, 1447 | "engines": { 1448 | "node": ">=8" 1449 | }, 1450 | "funding": { 1451 | "url": "https://github.com/sponsors/sindresorhus" 1452 | } 1453 | }, 1454 | "node_modules/globby": { 1455 | "version": "11.1.0", 1456 | "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", 1457 | "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", 1458 | "dev": true, 1459 | "dependencies": { 1460 | "array-union": "^2.1.0", 1461 | "dir-glob": "^3.0.1", 1462 | "fast-glob": "^3.2.9", 1463 | "ignore": "^5.2.0", 1464 | "merge2": "^1.4.1", 1465 | "slash": "^3.0.0" 1466 | }, 1467 | "engines": { 1468 | "node": ">=10" 1469 | }, 1470 | "funding": { 1471 | "url": "https://github.com/sponsors/sindresorhus" 1472 | } 1473 | }, 1474 | "node_modules/graphemer": { 1475 | "version": "1.4.0", 1476 | "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", 1477 | "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", 1478 | "dev": true, 1479 | "peer": true 1480 | }, 1481 | "node_modules/has-flag": { 1482 | "version": "4.0.0", 1483 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", 1484 | "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", 1485 | "dev": true, 1486 | "peer": true, 1487 | "engines": { 1488 | "node": ">=8" 1489 | } 1490 | }, 1491 | "node_modules/ignore": { 1492 | "version": "5.3.1", 1493 | "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", 1494 | "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", 1495 | "dev": true, 1496 | "engines": { 1497 | "node": ">= 4" 1498 | } 1499 | }, 1500 | "node_modules/import-fresh": { 1501 | "version": "3.3.0", 1502 | "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", 1503 | "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", 1504 | "dev": true, 1505 | "peer": true, 1506 | "dependencies": { 1507 | "parent-module": "^1.0.0", 1508 | "resolve-from": "^4.0.0" 1509 | }, 1510 | "engines": { 1511 | "node": ">=6" 1512 | }, 1513 | "funding": { 1514 | "url": "https://github.com/sponsors/sindresorhus" 1515 | } 1516 | }, 1517 | "node_modules/imurmurhash": { 1518 | "version": "0.1.4", 1519 | "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", 1520 | "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", 1521 | "dev": true, 1522 | "peer": true, 1523 | "engines": { 1524 | "node": ">=0.8.19" 1525 | } 1526 | }, 1527 | "node_modules/inflight": { 1528 | "version": "1.0.6", 1529 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 1530 | "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", 1531 | "dev": true, 1532 | "peer": true, 1533 | "dependencies": { 1534 | "once": "^1.3.0", 1535 | "wrappy": "1" 1536 | } 1537 | }, 1538 | "node_modules/inherits": { 1539 | "version": "2.0.4", 1540 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 1541 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", 1542 | "dev": true, 1543 | "peer": true 1544 | }, 1545 | "node_modules/is-extglob": { 1546 | "version": "2.1.1", 1547 | "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", 1548 | "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", 1549 | "dev": true, 1550 | "engines": { 1551 | "node": ">=0.10.0" 1552 | } 1553 | }, 1554 | "node_modules/is-glob": { 1555 | "version": "4.0.3", 1556 | "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", 1557 | "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", 1558 | "dev": true, 1559 | "dependencies": { 1560 | "is-extglob": "^2.1.1" 1561 | }, 1562 | "engines": { 1563 | "node": ">=0.10.0" 1564 | } 1565 | }, 1566 | "node_modules/is-number": { 1567 | "version": "7.0.0", 1568 | "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", 1569 | "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", 1570 | "dev": true, 1571 | "engines": { 1572 | "node": ">=0.12.0" 1573 | } 1574 | }, 1575 | "node_modules/is-path-inside": { 1576 | "version": "3.0.3", 1577 | "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", 1578 | "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", 1579 | "dev": true, 1580 | "peer": true, 1581 | "engines": { 1582 | "node": ">=8" 1583 | } 1584 | }, 1585 | "node_modules/isexe": { 1586 | "version": "2.0.0", 1587 | "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", 1588 | "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", 1589 | "dev": true, 1590 | "peer": true 1591 | }, 1592 | "node_modules/js-yaml": { 1593 | "version": "4.1.0", 1594 | "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", 1595 | "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", 1596 | "dev": true, 1597 | "peer": true, 1598 | "dependencies": { 1599 | "argparse": "^2.0.1" 1600 | }, 1601 | "bin": { 1602 | "js-yaml": "bin/js-yaml.js" 1603 | } 1604 | }, 1605 | "node_modules/json-buffer": { 1606 | "version": "3.0.1", 1607 | "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", 1608 | "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", 1609 | "dev": true, 1610 | "peer": true 1611 | }, 1612 | "node_modules/json-schema-traverse": { 1613 | "version": "0.4.1", 1614 | "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", 1615 | "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", 1616 | "dev": true, 1617 | "peer": true 1618 | }, 1619 | "node_modules/json-stable-stringify-without-jsonify": { 1620 | "version": "1.0.1", 1621 | "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", 1622 | "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", 1623 | "dev": true, 1624 | "peer": true 1625 | }, 1626 | "node_modules/keyv": { 1627 | "version": "4.5.4", 1628 | "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", 1629 | "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", 1630 | "dev": true, 1631 | "peer": true, 1632 | "dependencies": { 1633 | "json-buffer": "3.0.1" 1634 | } 1635 | }, 1636 | "node_modules/levn": { 1637 | "version": "0.4.1", 1638 | "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", 1639 | "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", 1640 | "dev": true, 1641 | "peer": true, 1642 | "dependencies": { 1643 | "prelude-ls": "^1.2.1", 1644 | "type-check": "~0.4.0" 1645 | }, 1646 | "engines": { 1647 | "node": ">= 0.8.0" 1648 | } 1649 | }, 1650 | "node_modules/locate-path": { 1651 | "version": "6.0.0", 1652 | "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", 1653 | "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", 1654 | "dev": true, 1655 | "peer": true, 1656 | "dependencies": { 1657 | "p-locate": "^5.0.0" 1658 | }, 1659 | "engines": { 1660 | "node": ">=10" 1661 | }, 1662 | "funding": { 1663 | "url": "https://github.com/sponsors/sindresorhus" 1664 | } 1665 | }, 1666 | "node_modules/lodash.merge": { 1667 | "version": "4.6.2", 1668 | "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", 1669 | "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", 1670 | "dev": true, 1671 | "peer": true 1672 | }, 1673 | "node_modules/lru-cache": { 1674 | "version": "6.0.0", 1675 | "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", 1676 | "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", 1677 | "dev": true, 1678 | "dependencies": { 1679 | "yallist": "^4.0.0" 1680 | }, 1681 | "engines": { 1682 | "node": ">=10" 1683 | } 1684 | }, 1685 | "node_modules/merge2": { 1686 | "version": "1.4.1", 1687 | "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", 1688 | "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", 1689 | "dev": true, 1690 | "engines": { 1691 | "node": ">= 8" 1692 | } 1693 | }, 1694 | "node_modules/micromatch": { 1695 | "version": "4.0.5", 1696 | "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", 1697 | "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", 1698 | "dev": true, 1699 | "dependencies": { 1700 | "braces": "^3.0.2", 1701 | "picomatch": "^2.3.1" 1702 | }, 1703 | "engines": { 1704 | "node": ">=8.6" 1705 | } 1706 | }, 1707 | "node_modules/minimatch": { 1708 | "version": "3.1.2", 1709 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", 1710 | "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", 1711 | "dev": true, 1712 | "peer": true, 1713 | "dependencies": { 1714 | "brace-expansion": "^1.1.7" 1715 | }, 1716 | "engines": { 1717 | "node": "*" 1718 | } 1719 | }, 1720 | "node_modules/moment": { 1721 | "version": "2.29.4", 1722 | "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz", 1723 | "integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==", 1724 | "dev": true, 1725 | "engines": { 1726 | "node": "*" 1727 | } 1728 | }, 1729 | "node_modules/ms": { 1730 | "version": "2.1.2", 1731 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 1732 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", 1733 | "dev": true 1734 | }, 1735 | "node_modules/natural-compare": { 1736 | "version": "1.4.0", 1737 | "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", 1738 | "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", 1739 | "dev": true, 1740 | "peer": true 1741 | }, 1742 | "node_modules/obsidian": { 1743 | "version": "1.5.7-1", 1744 | "resolved": "https://registry.npmjs.org/obsidian/-/obsidian-1.5.7-1.tgz", 1745 | "integrity": "sha512-T5ZRuQ1FnfXqEoakTTHVDYvzUEEoT8zSPnQCW31PVgYwG4D4tZCQfKHN2hTz1ifnCe8upvwa6mBTAP2WUA5Vng==", 1746 | "dev": true, 1747 | "dependencies": { 1748 | "@types/codemirror": "5.60.8", 1749 | "moment": "2.29.4" 1750 | }, 1751 | "peerDependencies": { 1752 | "@codemirror/state": "^6.0.0", 1753 | "@codemirror/view": "^6.0.0" 1754 | } 1755 | }, 1756 | "node_modules/once": { 1757 | "version": "1.4.0", 1758 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 1759 | "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", 1760 | "dev": true, 1761 | "peer": true, 1762 | "dependencies": { 1763 | "wrappy": "1" 1764 | } 1765 | }, 1766 | "node_modules/optionator": { 1767 | "version": "0.9.3", 1768 | "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", 1769 | "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", 1770 | "dev": true, 1771 | "peer": true, 1772 | "dependencies": { 1773 | "@aashutoshrathi/word-wrap": "^1.2.3", 1774 | "deep-is": "^0.1.3", 1775 | "fast-levenshtein": "^2.0.6", 1776 | "levn": "^0.4.1", 1777 | "prelude-ls": "^1.2.1", 1778 | "type-check": "^0.4.0" 1779 | }, 1780 | "engines": { 1781 | "node": ">= 0.8.0" 1782 | } 1783 | }, 1784 | "node_modules/p-limit": { 1785 | "version": "3.1.0", 1786 | "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", 1787 | "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", 1788 | "dev": true, 1789 | "peer": true, 1790 | "dependencies": { 1791 | "yocto-queue": "^0.1.0" 1792 | }, 1793 | "engines": { 1794 | "node": ">=10" 1795 | }, 1796 | "funding": { 1797 | "url": "https://github.com/sponsors/sindresorhus" 1798 | } 1799 | }, 1800 | "node_modules/p-locate": { 1801 | "version": "5.0.0", 1802 | "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", 1803 | "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", 1804 | "dev": true, 1805 | "peer": true, 1806 | "dependencies": { 1807 | "p-limit": "^3.0.2" 1808 | }, 1809 | "engines": { 1810 | "node": ">=10" 1811 | }, 1812 | "funding": { 1813 | "url": "https://github.com/sponsors/sindresorhus" 1814 | } 1815 | }, 1816 | "node_modules/parent-module": { 1817 | "version": "1.0.1", 1818 | "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", 1819 | "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", 1820 | "dev": true, 1821 | "peer": true, 1822 | "dependencies": { 1823 | "callsites": "^3.0.0" 1824 | }, 1825 | "engines": { 1826 | "node": ">=6" 1827 | } 1828 | }, 1829 | "node_modules/path-exists": { 1830 | "version": "4.0.0", 1831 | "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", 1832 | "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", 1833 | "dev": true, 1834 | "peer": true, 1835 | "engines": { 1836 | "node": ">=8" 1837 | } 1838 | }, 1839 | "node_modules/path-is-absolute": { 1840 | "version": "1.0.1", 1841 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 1842 | "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", 1843 | "dev": true, 1844 | "peer": true, 1845 | "engines": { 1846 | "node": ">=0.10.0" 1847 | } 1848 | }, 1849 | "node_modules/path-key": { 1850 | "version": "3.1.1", 1851 | "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", 1852 | "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", 1853 | "dev": true, 1854 | "peer": true, 1855 | "engines": { 1856 | "node": ">=8" 1857 | } 1858 | }, 1859 | "node_modules/path-type": { 1860 | "version": "4.0.0", 1861 | "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", 1862 | "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", 1863 | "dev": true, 1864 | "engines": { 1865 | "node": ">=8" 1866 | } 1867 | }, 1868 | "node_modules/picomatch": { 1869 | "version": "2.3.1", 1870 | "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", 1871 | "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", 1872 | "dev": true, 1873 | "engines": { 1874 | "node": ">=8.6" 1875 | }, 1876 | "funding": { 1877 | "url": "https://github.com/sponsors/jonschlinkert" 1878 | } 1879 | }, 1880 | "node_modules/prelude-ls": { 1881 | "version": "1.2.1", 1882 | "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", 1883 | "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", 1884 | "dev": true, 1885 | "peer": true, 1886 | "engines": { 1887 | "node": ">= 0.8.0" 1888 | } 1889 | }, 1890 | "node_modules/punycode": { 1891 | "version": "2.3.1", 1892 | "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", 1893 | "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", 1894 | "dev": true, 1895 | "peer": true, 1896 | "engines": { 1897 | "node": ">=6" 1898 | } 1899 | }, 1900 | "node_modules/queue-microtask": { 1901 | "version": "1.2.3", 1902 | "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", 1903 | "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", 1904 | "dev": true, 1905 | "funding": [ 1906 | { 1907 | "type": "github", 1908 | "url": "https://github.com/sponsors/feross" 1909 | }, 1910 | { 1911 | "type": "patreon", 1912 | "url": "https://www.patreon.com/feross" 1913 | }, 1914 | { 1915 | "type": "consulting", 1916 | "url": "https://feross.org/support" 1917 | } 1918 | ] 1919 | }, 1920 | "node_modules/regexpp": { 1921 | "version": "3.2.0", 1922 | "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", 1923 | "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", 1924 | "dev": true, 1925 | "engines": { 1926 | "node": ">=8" 1927 | }, 1928 | "funding": { 1929 | "url": "https://github.com/sponsors/mysticatea" 1930 | } 1931 | }, 1932 | "node_modules/resolve-from": { 1933 | "version": "4.0.0", 1934 | "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", 1935 | "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", 1936 | "dev": true, 1937 | "peer": true, 1938 | "engines": { 1939 | "node": ">=4" 1940 | } 1941 | }, 1942 | "node_modules/reusify": { 1943 | "version": "1.0.4", 1944 | "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", 1945 | "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", 1946 | "dev": true, 1947 | "engines": { 1948 | "iojs": ">=1.0.0", 1949 | "node": ">=0.10.0" 1950 | } 1951 | }, 1952 | "node_modules/rimraf": { 1953 | "version": "3.0.2", 1954 | "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", 1955 | "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", 1956 | "dev": true, 1957 | "peer": true, 1958 | "dependencies": { 1959 | "glob": "^7.1.3" 1960 | }, 1961 | "bin": { 1962 | "rimraf": "bin.js" 1963 | }, 1964 | "funding": { 1965 | "url": "https://github.com/sponsors/isaacs" 1966 | } 1967 | }, 1968 | "node_modules/run-parallel": { 1969 | "version": "1.2.0", 1970 | "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", 1971 | "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", 1972 | "dev": true, 1973 | "funding": [ 1974 | { 1975 | "type": "github", 1976 | "url": "https://github.com/sponsors/feross" 1977 | }, 1978 | { 1979 | "type": "patreon", 1980 | "url": "https://www.patreon.com/feross" 1981 | }, 1982 | { 1983 | "type": "consulting", 1984 | "url": "https://feross.org/support" 1985 | } 1986 | ], 1987 | "dependencies": { 1988 | "queue-microtask": "^1.2.2" 1989 | } 1990 | }, 1991 | "node_modules/semver": { 1992 | "version": "7.6.0", 1993 | "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", 1994 | "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", 1995 | "dev": true, 1996 | "dependencies": { 1997 | "lru-cache": "^6.0.0" 1998 | }, 1999 | "bin": { 2000 | "semver": "bin/semver.js" 2001 | }, 2002 | "engines": { 2003 | "node": ">=10" 2004 | } 2005 | }, 2006 | "node_modules/shebang-command": { 2007 | "version": "2.0.0", 2008 | "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", 2009 | "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", 2010 | "dev": true, 2011 | "peer": true, 2012 | "dependencies": { 2013 | "shebang-regex": "^3.0.0" 2014 | }, 2015 | "engines": { 2016 | "node": ">=8" 2017 | } 2018 | }, 2019 | "node_modules/shebang-regex": { 2020 | "version": "3.0.0", 2021 | "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", 2022 | "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", 2023 | "dev": true, 2024 | "peer": true, 2025 | "engines": { 2026 | "node": ">=8" 2027 | } 2028 | }, 2029 | "node_modules/slash": { 2030 | "version": "3.0.0", 2031 | "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", 2032 | "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", 2033 | "dev": true, 2034 | "engines": { 2035 | "node": ">=8" 2036 | } 2037 | }, 2038 | "node_modules/strip-ansi": { 2039 | "version": "6.0.1", 2040 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", 2041 | "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", 2042 | "dev": true, 2043 | "peer": true, 2044 | "dependencies": { 2045 | "ansi-regex": "^5.0.1" 2046 | }, 2047 | "engines": { 2048 | "node": ">=8" 2049 | } 2050 | }, 2051 | "node_modules/strip-json-comments": { 2052 | "version": "3.1.1", 2053 | "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", 2054 | "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", 2055 | "dev": true, 2056 | "peer": true, 2057 | "engines": { 2058 | "node": ">=8" 2059 | }, 2060 | "funding": { 2061 | "url": "https://github.com/sponsors/sindresorhus" 2062 | } 2063 | }, 2064 | "node_modules/style-mod": { 2065 | "version": "4.1.2", 2066 | "resolved": "https://registry.npmjs.org/style-mod/-/style-mod-4.1.2.tgz", 2067 | "integrity": "sha512-wnD1HyVqpJUI2+eKZ+eo1UwghftP6yuFheBqqe+bWCotBjC2K1YnteJILRMs3SM4V/0dLEW1SC27MWP5y+mwmw==", 2068 | "dev": true, 2069 | "peer": true 2070 | }, 2071 | "node_modules/supports-color": { 2072 | "version": "7.2.0", 2073 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", 2074 | "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", 2075 | "dev": true, 2076 | "peer": true, 2077 | "dependencies": { 2078 | "has-flag": "^4.0.0" 2079 | }, 2080 | "engines": { 2081 | "node": ">=8" 2082 | } 2083 | }, 2084 | "node_modules/text-table": { 2085 | "version": "0.2.0", 2086 | "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", 2087 | "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", 2088 | "dev": true, 2089 | "peer": true 2090 | }, 2091 | "node_modules/to-regex-range": { 2092 | "version": "5.0.1", 2093 | "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", 2094 | "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", 2095 | "dev": true, 2096 | "dependencies": { 2097 | "is-number": "^7.0.0" 2098 | }, 2099 | "engines": { 2100 | "node": ">=8.0" 2101 | } 2102 | }, 2103 | "node_modules/tslib": { 2104 | "version": "2.4.0", 2105 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", 2106 | "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==", 2107 | "dev": true 2108 | }, 2109 | "node_modules/tsutils": { 2110 | "version": "3.21.0", 2111 | "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", 2112 | "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", 2113 | "dev": true, 2114 | "dependencies": { 2115 | "tslib": "^1.8.1" 2116 | }, 2117 | "engines": { 2118 | "node": ">= 6" 2119 | }, 2120 | "peerDependencies": { 2121 | "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" 2122 | } 2123 | }, 2124 | "node_modules/tsutils/node_modules/tslib": { 2125 | "version": "1.14.1", 2126 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", 2127 | "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", 2128 | "dev": true 2129 | }, 2130 | "node_modules/type-check": { 2131 | "version": "0.4.0", 2132 | "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", 2133 | "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", 2134 | "dev": true, 2135 | "peer": true, 2136 | "dependencies": { 2137 | "prelude-ls": "^1.2.1" 2138 | }, 2139 | "engines": { 2140 | "node": ">= 0.8.0" 2141 | } 2142 | }, 2143 | "node_modules/type-fest": { 2144 | "version": "0.20.2", 2145 | "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", 2146 | "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", 2147 | "dev": true, 2148 | "peer": true, 2149 | "engines": { 2150 | "node": ">=10" 2151 | }, 2152 | "funding": { 2153 | "url": "https://github.com/sponsors/sindresorhus" 2154 | } 2155 | }, 2156 | "node_modules/typescript": { 2157 | "version": "4.7.4", 2158 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.7.4.tgz", 2159 | "integrity": "sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==", 2160 | "dev": true, 2161 | "bin": { 2162 | "tsc": "bin/tsc", 2163 | "tsserver": "bin/tsserver" 2164 | }, 2165 | "engines": { 2166 | "node": ">=4.2.0" 2167 | } 2168 | }, 2169 | "node_modules/uri-js": { 2170 | "version": "4.4.1", 2171 | "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", 2172 | "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", 2173 | "dev": true, 2174 | "peer": true, 2175 | "dependencies": { 2176 | "punycode": "^2.1.0" 2177 | } 2178 | }, 2179 | "node_modules/w3c-keyname": { 2180 | "version": "2.2.8", 2181 | "resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.8.tgz", 2182 | "integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==", 2183 | "dev": true, 2184 | "peer": true 2185 | }, 2186 | "node_modules/which": { 2187 | "version": "2.0.2", 2188 | "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", 2189 | "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", 2190 | "dev": true, 2191 | "peer": true, 2192 | "dependencies": { 2193 | "isexe": "^2.0.0" 2194 | }, 2195 | "bin": { 2196 | "node-which": "bin/node-which" 2197 | }, 2198 | "engines": { 2199 | "node": ">= 8" 2200 | } 2201 | }, 2202 | "node_modules/wrappy": { 2203 | "version": "1.0.2", 2204 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 2205 | "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", 2206 | "dev": true, 2207 | "peer": true 2208 | }, 2209 | "node_modules/yallist": { 2210 | "version": "4.0.0", 2211 | "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", 2212 | "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", 2213 | "dev": true 2214 | }, 2215 | "node_modules/yocto-queue": { 2216 | "version": "0.1.0", 2217 | "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", 2218 | "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", 2219 | "dev": true, 2220 | "peer": true, 2221 | "engines": { 2222 | "node": ">=10" 2223 | }, 2224 | "funding": { 2225 | "url": "https://github.com/sponsors/sindresorhus" 2226 | } 2227 | } 2228 | } 2229 | } 2230 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "obsidian-semantic-canvas-plugin", 3 | "version": "1.2.1", 4 | "description": "This is a plugin for Obsidian (https://obsidian.md) to use canvases to link notes together and edit their properties", 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 | }, 11 | "keywords": ["obsidian", "canvas"], 12 | "author": "", 13 | "license": "MIT", 14 | "fundingUrl": "https://www.buymeacoffee.com/aarongilly", 15 | "devDependencies": { 16 | "@types/node": "^16.11.6", 17 | "@typescript-eslint/eslint-plugin": "5.29.0", 18 | "@typescript-eslint/parser": "5.29.0", 19 | "builtin-modules": "3.3.0", 20 | "esbuild": "0.17.3", 21 | "obsidian": "latest", 22 | "tslib": "2.4.0", 23 | "typescript": "4.7.4" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /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 | "lib": [ 15 | "DOM", 16 | "ES5", 17 | "ES6", 18 | "ES7" 19 | ] 20 | }, 21 | "include": [ 22 | "**/*.ts" 23 | ] 24 | } 25 | -------------------------------------------------------------------------------- /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 | "1.0.0": "0.15.0", 3 | "1.2.0": "0.15.0", 4 | "1.2.1": "0.15.0" 5 | } --------------------------------------------------------------------------------