From 4a07f3126a7d6b2058f1104d7a5c39a1c429a9f2 Mon Sep 17 00:00:00 2001
From: Mantano <91400447+mntno@users.noreply.github.com>
Date: Tue, 4 Mar 2025 14:49:03 +0700
Subject: [PATCH] Initial commit
---
.editorconfig | 11 +
.gitattributes | 1 +
.gitignore | 20 +
.npmrc | 1 +
LICENSE | 5 +
README.md | 2 +-
esbuild.config.mjs | 49 ++
eslint.config.mjs | 46 ++
main.ts | 162 +++++
manifest.json | 10 +
package.json | 37 ++
pnpm-lock.yaml | 1397 +++++++++++++++++++++++++++++++++++++++++++
src/CacheManager.ts | 120 ++++
src/SettingTab.ts | 46 ++
tsconfig.json | 24 +
versions.json | 3 +
16 files changed, 1933 insertions(+), 1 deletion(-)
create mode 100644 .editorconfig
create mode 100644 .gitattributes
create mode 100644 .gitignore
create mode 100644 .npmrc
create mode 100644 LICENSE
create mode 100644 esbuild.config.mjs
create mode 100644 eslint.config.mjs
create mode 100644 main.ts
create mode 100644 manifest.json
create mode 100644 package.json
create mode 100644 pnpm-lock.yaml
create mode 100644 src/CacheManager.ts
create mode 100644 src/SettingTab.ts
create mode 100644 tsconfig.json
create mode 100644 versions.json
diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000..fe61c4e
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,11 @@
+# top-most EditorConfig file
+root = true
+
+[*]
+charset = utf-8
+end_of_line = lf
+insert_final_newline = true
+indent_style = tab
+indent_size = 2
+tab_width = 2
+trim_trailing_whitespace = true
diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000..6313b56
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1 @@
+* text=auto eol=lf
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..be39a5d
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,20 @@
+# vscode
+.vscode
+
+# npm
+node_modules
+
+# Hot Reload plugin
+.hotreload
+
+main.js
+
+# Exclude sourcemaps
+*.map
+
+# obsidian
+data.json
+
+# Exclude macOS Finder (System Explorer) View States
+.DS_Store
+
diff --git a/.npmrc b/.npmrc
new file mode 100644
index 0000000..b973752
--- /dev/null
+++ b/.npmrc
@@ -0,0 +1 @@
+tag-version-prefix=""
\ No newline at end of file
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..15ed5eb
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,5 @@
+Copyright (C) 2025 by Mantano Bhikkhu.
+
+Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
\ No newline at end of file
diff --git a/README.md b/README.md
index 6b234db..1c001a3 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,3 @@
# Come Down
-An Obsidian plugin for caching embedded external images.
+An Obsidian plugin that caches embedded external images to your local machine.
\ No newline at end of file
diff --git a/esbuild.config.mjs b/esbuild.config.mjs
new file mode 100644
index 0000000..a5de8b8
--- /dev/null
+++ b/esbuild.config.mjs
@@ -0,0 +1,49 @@
+import esbuild from "esbuild";
+import process from "process";
+import builtins from "builtin-modules";
+
+const banner =
+`/*
+THIS IS A GENERATED/BUNDLED FILE BY ESBUILD
+if you want to view the source, please visit the github repository of this plugin
+*/
+`;
+
+const prod = (process.argv[2] === "production");
+
+const context = await esbuild.context({
+ banner: {
+ js: banner,
+ },
+ entryPoints: ["main.ts"],
+ bundle: true,
+ external: [
+ "obsidian",
+ "electron",
+ "@codemirror/autocomplete",
+ "@codemirror/collab",
+ "@codemirror/commands",
+ "@codemirror/language",
+ "@codemirror/lint",
+ "@codemirror/search",
+ "@codemirror/state",
+ "@codemirror/view",
+ "@lezer/common",
+ "@lezer/highlight",
+ "@lezer/lr",
+ ...builtins],
+ format: "cjs",
+ target: "es2018",
+ logLevel: "info",
+ sourcemap: prod ? false : "inline",
+ treeShaking: true,
+ outfile: "main.js",
+ minify: prod,
+});
+
+if (prod) {
+ await context.rebuild();
+ process.exit(0);
+} else {
+ await context.watch();
+}
diff --git a/eslint.config.mjs b/eslint.config.mjs
new file mode 100644
index 0000000..d1baad6
--- /dev/null
+++ b/eslint.config.mjs
@@ -0,0 +1,46 @@
+
+import js from "@eslint/js";
+import globals from "globals";
+
+import typeScriptPlugin from "@typescript-eslint/eslint-plugin";
+import typeScriptParser from "@typescript-eslint/parser";
+
+// If using Svelte
+// import sveltePlugin from "eslint-plugin-svelte";
+// import svelteParser from "svelte-eslint-parser";
+
+
+export default [
+ {
+ files: ["**/*.ts", "**/*.tsx"],
+ plugins: {
+ "@typescript-eslint": typeScriptPlugin,
+ },
+ languageOptions: {
+ parser: typeScriptParser,
+ parserOptions: {
+ project: './tsconfig.json',
+ sourceType: 'module',
+ ecmaVersion: "latest",
+ },
+ globals: {
+ ...globals.browser,
+ //...globals.node
+ }
+ },
+ rules: {
+ ...js.configs.recommended.rules,
+ "no-unused-vars": "off",
+ "@typescript-eslint/no-unused-vars": ["error", { "args": "none" }],
+ "@typescript-eslint/ban-ts-comment": "off",
+ "no-prototype-builtins": "off",
+ "@typescript-eslint/no-empty-function": "off",
+ }
+ },
+ {
+ ignores: [
+ "**/*js",
+ "**/node_modules/**",
+ ],
+ },
+];
\ No newline at end of file
diff --git a/main.ts b/main.ts
new file mode 100644
index 0000000..ab8eeee
--- /dev/null
+++ b/main.ts
@@ -0,0 +1,162 @@
+import { MarkdownView, Plugin, editorLivePreviewField, editorInfoField, MarkdownFileInfo, MarkdownPostProcessorContext, Notice, debounce, normalizePath } from "obsidian";
+import { SettingTab, SettingsManager } from "src/SettingTab";
+import { CacheManager, CacheMetadataItem, CacheResourceType } from "src/CacheManager";
+import { EditorView, ViewUpdate } from "@codemirror/view";
+
+export default class PluginImplementation extends Plugin {
+ settingsManager: SettingsManager;
+ cacheManager: CacheManager;
+
+ async onload() {
+
+ const pluginDir = this.manifest.dir;
+ if (!pluginDir) {
+ const errorMsg = `${this.manifest.name}: Cannot load because plugin directory is unknown`;
+ new Notice(errorMsg, 0);
+ throw new Error(errorMsg);
+ }
+ this.cacheManager = new CacheManager(this.app, pluginDir + "/cache");
+
+ this.settingsManager = new SettingsManager(
+ await this.loadData(),
+ async (settings) => await this.saveData(settings),
+ );
+ this.addSettingTab(new SettingTab(this, this.settingsManager));
+
+ // The post processor runs after the Markdown has been processed into HTML. It lets you add, remove, or replace HTML elements to the rendered document.
+ this.registerMarkdownPostProcessor((element, context) => {
+ this.processReadingView(element, context);
+ });
+
+ // Handle live preview
+ this.registerEditorExtension(EditorView.updateListener.of((update: ViewUpdate) => {
+ this.processLivePreview(update);
+ }));
+
+ this.addCommand({
+ id: "clear-all-cache",
+ name: "Clear All Cached Files",
+ callback: () => {
+ this.cacheManager.clearCached();
+ }
+ });
+
+ this.addCommand({
+ id: 'redownload-all-images-in-this-file',
+ name: 'Redownload all images in this file.',
+ checkCallback: (checking: boolean) => {
+ const markdownView = this.app.workspace.getActiveViewOfType(MarkdownView);
+ if (markdownView) {
+ if (!checking) {
+ }
+ return true;
+ }
+ }
+ });
+ }
+
+ onunload() {
+ this.cacheManager?.cancelAllOngoing();
+ }
+
+ processLivePreview(update: ViewUpdate) {
+ const view = update.view;
+ const isInLivePreview = view.state.field(editorLivePreviewField);
+ if (!isInLivePreview)
+ return;
+
+ if (update.docChanged || update.viewportChanged) {
+
+ const imageElements = view.contentDOM.findAll("img") as HTMLImageElement[];
+
+ /* Gave up with this. Not sure if it's more efficient.
+ syntaxTree(view.state).iterate({
+ enter: ({ type, from, to }: SyntaxNodeRef) => {
+ console.log(`${view.state.doc.sliceString(from, to)}`);
+ }
+ });
+ */
+
+ const markdownView = view.state.field(editorInfoField) as MarkdownFileInfo;
+ this.handleImages(imageElements, markdownView.file?.path);
+ }
+ }
+
+ processReadingView(element: HTMLElement, context: MarkdownPostProcessorContext) {
+ const imageElements = element.findAll("img") as HTMLImageElement[];
+ this.handleImages(imageElements, context.sourcePath);
+ }
+
+ /**
+ * Initiates a call to the {@link CacheManager} for each image element and changing the src to the cached file.
+ *
+ * @param imageElements All images in the post proceessed HTML.
+ * @param filePath Path to the file containing this HTML.
+ */
+ handleImages(imageElements: HTMLImageElement[], filePath: string | undefined) {
+
+ //if (filePath)
+ // this.cacheManager.cancelOngoing(filePath);
+
+ imageElements
+ .filter((imageElement => imageElement.src.length > 0)) // In case src hasn't been set yet.
+ .filter((imageElement) => imageElement.dataset.comeDownCacheInitiated !== "true" ) // Avoid stampede.
+ .forEach((imageElement) => {
+
+ const metadata: CacheMetadataItem = {
+ key: imageElement.src,
+ filePath: filePath,
+ type: CacheResourceType.ExternalImage
+ };
+
+ const originalAltText = imageElement.alt;
+
+ imageElement.alt = "Loading...";
+ imageElement.dataset.comeDownCacheInitiated = "true";
+ imageElement.src = `data:image/svg+xml;charset=utf-8,${PluginImplementation.ENCODED_LOADING_ICON}`;
+
+ console.log(`handleImage: ${metadata.key}, alt: ${originalAltText}`);
+
+ this.cacheManager.getCache(metadata, (result) => {
+ imageElement.alt = originalAltText;
+
+ if (result.item) {
+ console.log(`Got CacheResult from cache: ${result.fromCache}, src: ${result.item?.metadata.key}`);
+ imageElement.src = result.item.filePath;
+ }
+ else
+ console.error("Failed to fetch cache", result.error);
+ });
+ });
+ }
+
+ /**
+ * @todo
+ * @see {@link https://lucide.dev/icons/loader}
+ */
+ static readonly SVG_LOADING_ICON = `
+
+ `;
+
+ static readonly ENCODED_LOADING_ICON = encodeURIComponent(PluginImplementation.SVG_LOADING_ICON);
+}
+
diff --git a/manifest.json b/manifest.json
new file mode 100644
index 0000000..c30f98d
--- /dev/null
+++ b/manifest.json
@@ -0,0 +1,10 @@
+{
+ "id": "come-down",
+ "name": "Come Down",
+ "version": "1.0.0",
+ "minAppVersion": "1.8.0",
+ "description": "Caches embedded external images to your local machine without modifying your notes.",
+ "author": "mntno",
+ "authorUrl": "https://github.com/mntno",
+ "isDesktopOnly": false
+}
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..13691ab
--- /dev/null
+++ b/package.json
@@ -0,0 +1,37 @@
+{
+ "name": "come-down",
+ "version": "1.0.0",
+ "description": "An Obsidian plugin for caching embedded external images.",
+ "main": "main.js",
+ "scripts": {
+ "dev": "node esbuild.config.mjs",
+ "build": "tsc -noEmit -skipLibCheck && node esbuild.config.mjs production",
+ "version": "node version-bump.mjs && git add manifest.json versions.json"
+ },
+ "keywords": [],
+ "author": "mntno",
+ "license": "0BSD",
+ "devDependencies": {
+ "@codemirror/language": "^6.10.8",
+ "@codemirror/state": "^6.5.2",
+ "@codemirror/view": "^6.36.4",
+ "@eslint/js": "9.21.0",
+ "@lezer/common": "^1.2.3",
+ "@types/node": "22.13.9",
+ "@typescript-eslint/eslint-plugin": "8.26.0",
+ "@typescript-eslint/parser": "8.26.0",
+ "builtin-modules": "5.0.0",
+ "esbuild": "0.25.0",
+ "globals": "16.0.0",
+ "obsidian": "latest",
+ "tslib": "2.8.1",
+ "typescript": "5.8.2",
+ "typescript-eslint": "8.26.0"
+ },
+ "type": "module",
+ "pnpm": {
+ "onlyBuiltDependencies": [
+ "esbuild"
+ ]
+ }
+}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
new file mode 100644
index 0000000..c2db707
--- /dev/null
+++ b/pnpm-lock.yaml
@@ -0,0 +1,1397 @@
+lockfileVersion: '9.0'
+
+settings:
+ autoInstallPeers: true
+ excludeLinksFromLockfile: false
+
+importers:
+
+ .:
+ devDependencies:
+ '@codemirror/language':
+ specifier: ^6.10.8
+ version: 6.10.8
+ '@codemirror/state':
+ specifier: ^6.5.2
+ version: 6.5.2
+ '@codemirror/view':
+ specifier: ^6.36.4
+ version: 6.36.4
+ '@eslint/js':
+ specifier: 9.21.0
+ version: 9.21.0
+ '@lezer/common':
+ specifier: ^1.2.3
+ version: 1.2.3
+ '@types/node':
+ specifier: 22.13.9
+ version: 22.13.9
+ '@typescript-eslint/eslint-plugin':
+ specifier: 8.26.0
+ version: 8.26.0(@typescript-eslint/parser@8.26.0(eslint@9.21.0)(typescript@5.8.2))(eslint@9.21.0)(typescript@5.8.2)
+ '@typescript-eslint/parser':
+ specifier: 8.26.0
+ version: 8.26.0(eslint@9.21.0)(typescript@5.8.2)
+ builtin-modules:
+ specifier: 5.0.0
+ version: 5.0.0
+ esbuild:
+ specifier: 0.25.0
+ version: 0.25.0
+ globals:
+ specifier: 16.0.0
+ version: 16.0.0
+ obsidian:
+ specifier: latest
+ version: 1.8.7(@codemirror/state@6.5.2)(@codemirror/view@6.36.4)
+ tslib:
+ specifier: 2.8.1
+ version: 2.8.1
+ typescript:
+ specifier: 5.8.2
+ version: 5.8.2
+ typescript-eslint:
+ specifier: 8.26.0
+ version: 8.26.0(eslint@9.21.0)(typescript@5.8.2)
+
+packages:
+
+ '@codemirror/language@6.10.8':
+ resolution: {integrity: sha512-wcP8XPPhDH2vTqf181U8MbZnW+tDyPYy0UzVOa+oHORjyT+mhhom9vBd7dApJwoDz9Nb/a8kHjJIsuA/t8vNFw==}
+
+ '@codemirror/state@6.5.2':
+ resolution: {integrity: sha512-FVqsPqtPWKVVL3dPSxy8wEF/ymIEuVzF1PK3VbUgrxXpJUSHQWWZz4JMToquRxnkw+36LTamCZG2iua2Ptq0fA==}
+
+ '@codemirror/view@6.36.4':
+ resolution: {integrity: sha512-ZQ0V5ovw/miKEXTvjgzRyjnrk9TwriUB1k4R5p7uNnHR9Hus+D1SXHGdJshijEzPFjU25xea/7nhIeSqYFKdbA==}
+
+ '@esbuild/aix-ppc64@0.25.0':
+ resolution: {integrity: sha512-O7vun9Sf8DFjH2UtqK8Ku3LkquL9SZL8OLY1T5NZkA34+wG3OQF7cl4Ql8vdNzM6fzBbYfLaiRLIOZ+2FOCgBQ==}
+ engines: {node: '>=18'}
+ cpu: [ppc64]
+ os: [aix]
+
+ '@esbuild/android-arm64@0.25.0':
+ resolution: {integrity: sha512-grvv8WncGjDSyUBjN9yHXNt+cq0snxXbDxy5pJtzMKGmmpPxeAmAhWxXI+01lU5rwZomDgD3kJwulEnhTRUd6g==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [android]
+
+ '@esbuild/android-arm@0.25.0':
+ resolution: {integrity: sha512-PTyWCYYiU0+1eJKmw21lWtC+d08JDZPQ5g+kFyxP0V+es6VPPSUhM6zk8iImp2jbV6GwjX4pap0JFbUQN65X1g==}
+ engines: {node: '>=18'}
+ cpu: [arm]
+ os: [android]
+
+ '@esbuild/android-x64@0.25.0':
+ resolution: {integrity: sha512-m/ix7SfKG5buCnxasr52+LI78SQ+wgdENi9CqyCXwjVR2X4Jkz+BpC3le3AoBPYTC9NHklwngVXvbJ9/Akhrfg==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [android]
+
+ '@esbuild/darwin-arm64@0.25.0':
+ resolution: {integrity: sha512-mVwdUb5SRkPayVadIOI78K7aAnPamoeFR2bT5nszFUZ9P8UpK4ratOdYbZZXYSqPKMHfS1wdHCJk1P1EZpRdvw==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@esbuild/darwin-x64@0.25.0':
+ resolution: {integrity: sha512-DgDaYsPWFTS4S3nWpFcMn/33ZZwAAeAFKNHNa1QN0rI4pUjgqf0f7ONmXf6d22tqTY+H9FNdgeaAa+YIFUn2Rg==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [darwin]
+
+ '@esbuild/freebsd-arm64@0.25.0':
+ resolution: {integrity: sha512-VN4ocxy6dxefN1MepBx/iD1dH5K8qNtNe227I0mnTRjry8tj5MRk4zprLEdG8WPyAPb93/e4pSgi1SoHdgOa4w==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [freebsd]
+
+ '@esbuild/freebsd-x64@0.25.0':
+ resolution: {integrity: sha512-mrSgt7lCh07FY+hDD1TxiTyIHyttn6vnjesnPoVDNmDfOmggTLXRv8Id5fNZey1gl/V2dyVK1VXXqVsQIiAk+A==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [freebsd]
+
+ '@esbuild/linux-arm64@0.25.0':
+ resolution: {integrity: sha512-9QAQjTWNDM/Vk2bgBl17yWuZxZNQIF0OUUuPZRKoDtqF2k4EtYbpyiG5/Dk7nqeK6kIJWPYldkOcBqjXjrUlmg==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [linux]
+
+ '@esbuild/linux-arm@0.25.0':
+ resolution: {integrity: sha512-vkB3IYj2IDo3g9xX7HqhPYxVkNQe8qTK55fraQyTzTX/fxaDtXiEnavv9geOsonh2Fd2RMB+i5cbhu2zMNWJwg==}
+ engines: {node: '>=18'}
+ cpu: [arm]
+ os: [linux]
+
+ '@esbuild/linux-ia32@0.25.0':
+ resolution: {integrity: sha512-43ET5bHbphBegyeqLb7I1eYn2P/JYGNmzzdidq/w0T8E2SsYL1U6un2NFROFRg1JZLTzdCoRomg8Rvf9M6W6Gg==}
+ engines: {node: '>=18'}
+ cpu: [ia32]
+ os: [linux]
+
+ '@esbuild/linux-loong64@0.25.0':
+ resolution: {integrity: sha512-fC95c/xyNFueMhClxJmeRIj2yrSMdDfmqJnyOY4ZqsALkDrrKJfIg5NTMSzVBr5YW1jf+l7/cndBfP3MSDpoHw==}
+ engines: {node: '>=18'}
+ cpu: [loong64]
+ os: [linux]
+
+ '@esbuild/linux-mips64el@0.25.0':
+ resolution: {integrity: sha512-nkAMFju7KDW73T1DdH7glcyIptm95a7Le8irTQNO/qtkoyypZAnjchQgooFUDQhNAy4iu08N79W4T4pMBwhPwQ==}
+ engines: {node: '>=18'}
+ cpu: [mips64el]
+ os: [linux]
+
+ '@esbuild/linux-ppc64@0.25.0':
+ resolution: {integrity: sha512-NhyOejdhRGS8Iwv+KKR2zTq2PpysF9XqY+Zk77vQHqNbo/PwZCzB5/h7VGuREZm1fixhs4Q/qWRSi5zmAiO4Fw==}
+ engines: {node: '>=18'}
+ cpu: [ppc64]
+ os: [linux]
+
+ '@esbuild/linux-riscv64@0.25.0':
+ resolution: {integrity: sha512-5S/rbP5OY+GHLC5qXp1y/Mx//e92L1YDqkiBbO9TQOvuFXM+iDqUNG5XopAnXoRH3FjIUDkeGcY1cgNvnXp/kA==}
+ engines: {node: '>=18'}
+ cpu: [riscv64]
+ os: [linux]
+
+ '@esbuild/linux-s390x@0.25.0':
+ resolution: {integrity: sha512-XM2BFsEBz0Fw37V0zU4CXfcfuACMrppsMFKdYY2WuTS3yi8O1nFOhil/xhKTmE1nPmVyvQJjJivgDT+xh8pXJA==}
+ engines: {node: '>=18'}
+ cpu: [s390x]
+ os: [linux]
+
+ '@esbuild/linux-x64@0.25.0':
+ resolution: {integrity: sha512-9yl91rHw/cpwMCNytUDxwj2XjFpxML0y9HAOH9pNVQDpQrBxHy01Dx+vaMu0N1CKa/RzBD2hB4u//nfc+Sd3Cw==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [linux]
+
+ '@esbuild/netbsd-arm64@0.25.0':
+ resolution: {integrity: sha512-RuG4PSMPFfrkH6UwCAqBzauBWTygTvb1nxWasEJooGSJ/NwRw7b2HOwyRTQIU97Hq37l3npXoZGYMy3b3xYvPw==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [netbsd]
+
+ '@esbuild/netbsd-x64@0.25.0':
+ resolution: {integrity: sha512-jl+qisSB5jk01N5f7sPCsBENCOlPiS/xptD5yxOx2oqQfyourJwIKLRA2yqWdifj3owQZCL2sn6o08dBzZGQzA==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [netbsd]
+
+ '@esbuild/openbsd-arm64@0.25.0':
+ resolution: {integrity: sha512-21sUNbq2r84YE+SJDfaQRvdgznTD8Xc0oc3p3iW/a1EVWeNj/SdUCbm5U0itZPQYRuRTW20fPMWMpcrciH2EJw==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [openbsd]
+
+ '@esbuild/openbsd-x64@0.25.0':
+ resolution: {integrity: sha512-2gwwriSMPcCFRlPlKx3zLQhfN/2WjJ2NSlg5TKLQOJdV0mSxIcYNTMhk3H3ulL/cak+Xj0lY1Ym9ysDV1igceg==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [openbsd]
+
+ '@esbuild/sunos-x64@0.25.0':
+ resolution: {integrity: sha512-bxI7ThgLzPrPz484/S9jLlvUAHYMzy6I0XiU1ZMeAEOBcS0VePBFxh1JjTQt3Xiat5b6Oh4x7UC7IwKQKIJRIg==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [sunos]
+
+ '@esbuild/win32-arm64@0.25.0':
+ resolution: {integrity: sha512-ZUAc2YK6JW89xTbXvftxdnYy3m4iHIkDtK3CLce8wg8M2L+YZhIvO1DKpxrd0Yr59AeNNkTiic9YLf6FTtXWMw==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [win32]
+
+ '@esbuild/win32-ia32@0.25.0':
+ resolution: {integrity: sha512-eSNxISBu8XweVEWG31/JzjkIGbGIJN/TrRoiSVZwZ6pkC6VX4Im/WV2cz559/TXLcYbcrDN8JtKgd9DJVIo8GA==}
+ engines: {node: '>=18'}
+ cpu: [ia32]
+ os: [win32]
+
+ '@esbuild/win32-x64@0.25.0':
+ resolution: {integrity: sha512-ZENoHJBxA20C2zFzh6AI4fT6RraMzjYw4xKWemRTRmRVtN9c5DcH9r/f2ihEkMjOW5eGgrwCslG/+Y/3bL+DHQ==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [win32]
+
+ '@eslint-community/eslint-utils@4.4.1':
+ resolution: {integrity: sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA==}
+ engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+ peerDependencies:
+ eslint: ^6.0.0 || ^7.0.0 || >=8.0.0
+
+ '@eslint-community/regexpp@4.12.1':
+ resolution: {integrity: sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==}
+ engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0}
+
+ '@eslint/config-array@0.19.2':
+ resolution: {integrity: sha512-GNKqxfHG2ySmJOBSHg7LxeUx4xpuCoFjacmlCoYWEbaPXLwvfIjixRI12xCQZeULksQb23uiA8F40w5TojpV7w==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ '@eslint/core@0.12.0':
+ resolution: {integrity: sha512-cmrR6pytBuSMTaBweKoGMwu3EiHiEC+DoyupPmlZ0HxBJBtIxwe+j/E4XPIKNx+Q74c8lXKPwYawBf5glsTkHg==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ '@eslint/eslintrc@3.3.0':
+ resolution: {integrity: sha512-yaVPAiNAalnCZedKLdR21GOGILMLKPyqSLWaAjQFvYA2i/ciDi8ArYVr69Anohb6cH2Ukhqti4aFnYyPm8wdwQ==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ '@eslint/js@9.21.0':
+ resolution: {integrity: sha512-BqStZ3HX8Yz6LvsF5ByXYrtigrV5AXADWLAGc7PH/1SxOb7/FIYYMszZZWiUou/GB9P2lXWk2SV4d+Z8h0nknw==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ '@eslint/object-schema@2.1.6':
+ resolution: {integrity: sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ '@eslint/plugin-kit@0.2.7':
+ resolution: {integrity: sha512-JubJ5B2pJ4k4yGxaNLdbjrnk9d/iDz6/q8wOilpIowd6PJPgaxCuHBnBszq7Ce2TyMrywm5r4PnKm6V3iiZF+g==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ '@humanfs/core@0.19.1':
+ resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==}
+ engines: {node: '>=18.18.0'}
+
+ '@humanfs/node@0.16.6':
+ resolution: {integrity: sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==}
+ engines: {node: '>=18.18.0'}
+
+ '@humanwhocodes/module-importer@1.0.1':
+ resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==}
+ engines: {node: '>=12.22'}
+
+ '@humanwhocodes/retry@0.3.1':
+ resolution: {integrity: sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==}
+ engines: {node: '>=18.18'}
+
+ '@humanwhocodes/retry@0.4.2':
+ resolution: {integrity: sha512-xeO57FpIu4p1Ri3Jq/EXq4ClRm86dVF2z/+kvFnyqVYRavTZmaFaUBbWCOuuTh0o/g7DSsk6kc2vrS4Vl5oPOQ==}
+ engines: {node: '>=18.18'}
+
+ '@lezer/common@1.2.3':
+ resolution: {integrity: sha512-w7ojc8ejBqr2REPsWxJjrMFsA/ysDCFICn8zEOR9mrqzOu2amhITYuLD8ag6XZf0CFXDrhKqw7+tW8cX66NaDA==}
+
+ '@lezer/highlight@1.2.1':
+ resolution: {integrity: sha512-Z5duk4RN/3zuVO7Jq0pGLJ3qynpxUVsh7IbUbGj88+uV2ApSAn6kWg2au3iJb+0Zi7kKtqffIESgNcRXWZWmSA==}
+
+ '@lezer/lr@1.4.2':
+ resolution: {integrity: sha512-pu0K1jCIdnQ12aWNaAVU5bzi7Bd1w54J3ECgANPmYLtQKP0HBj2cE/5coBD66MT10xbtIuUr7tg0Shbsvk0mDA==}
+
+ '@marijn/find-cluster-break@1.0.2':
+ resolution: {integrity: sha512-l0h88YhZFyKdXIFNfSWpyjStDjGHwZ/U7iobcK1cQQD8sejsONdQtTVU+1wVN1PBw40PiiHB1vA5S7VTfQiP9g==}
+
+ '@nodelib/fs.scandir@2.1.5':
+ resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
+ engines: {node: '>= 8'}
+
+ '@nodelib/fs.stat@2.0.5':
+ resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==}
+ engines: {node: '>= 8'}
+
+ '@nodelib/fs.walk@1.2.8':
+ resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==}
+ engines: {node: '>= 8'}
+
+ '@types/codemirror@5.60.8':
+ resolution: {integrity: sha512-VjFgDF/eB+Aklcy15TtOTLQeMjTo07k7KAjql8OK5Dirr7a6sJY4T1uVBDuTVG9VEmn1uUsohOpYnVfgC6/jyw==}
+
+ '@types/estree@1.0.6':
+ resolution: {integrity: sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==}
+
+ '@types/json-schema@7.0.15':
+ resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==}
+
+ '@types/node@22.13.9':
+ resolution: {integrity: sha512-acBjXdRJ3A6Pb3tqnw9HZmyR3Fiol3aGxRCK1x3d+6CDAMjl7I649wpSd+yNURCjbOUGu9tqtLKnTGxmK6CyGw==}
+
+ '@types/tern@0.23.9':
+ resolution: {integrity: sha512-ypzHFE/wBzh+BlH6rrBgS5I/Z7RD21pGhZ2rltb/+ZrVM1awdZwjx7hE5XfuYgHWk9uvV5HLZN3SloevCAp3Bw==}
+
+ '@typescript-eslint/eslint-plugin@8.26.0':
+ resolution: {integrity: sha512-cLr1J6pe56zjKYajK6SSSre6nl1Gj6xDp1TY0trpgPzjVbgDwd09v2Ws37LABxzkicmUjhEeg/fAUjPJJB1v5Q==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ peerDependencies:
+ '@typescript-eslint/parser': ^8.0.0 || ^8.0.0-alpha.0
+ eslint: ^8.57.0 || ^9.0.0
+ typescript: '>=4.8.4 <5.9.0'
+
+ '@typescript-eslint/parser@8.26.0':
+ resolution: {integrity: sha512-mNtXP9LTVBy14ZF3o7JG69gRPBK/2QWtQd0j0oH26HcY/foyJJau6pNUez7QrM5UHnSvwlQcJXKsk0I99B9pOA==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ peerDependencies:
+ eslint: ^8.57.0 || ^9.0.0
+ typescript: '>=4.8.4 <5.9.0'
+
+ '@typescript-eslint/scope-manager@8.26.0':
+ resolution: {integrity: sha512-E0ntLvsfPqnPwng8b8y4OGuzh/iIOm2z8U3S9zic2TeMLW61u5IH2Q1wu0oSTkfrSzwbDJIB/Lm8O3//8BWMPA==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ '@typescript-eslint/type-utils@8.26.0':
+ resolution: {integrity: sha512-ruk0RNChLKz3zKGn2LwXuVoeBcUMh+jaqzN461uMMdxy5H9epZqIBtYj7UiPXRuOpaALXGbmRuZQhmwHhaS04Q==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ peerDependencies:
+ eslint: ^8.57.0 || ^9.0.0
+ typescript: '>=4.8.4 <5.9.0'
+
+ '@typescript-eslint/types@8.26.0':
+ resolution: {integrity: sha512-89B1eP3tnpr9A8L6PZlSjBvnJhWXtYfZhECqlBl1D9Lme9mHO6iWlsprBtVenQvY1HMhax1mWOjhtL3fh/u+pA==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ '@typescript-eslint/typescript-estree@8.26.0':
+ resolution: {integrity: sha512-tiJ1Hvy/V/oMVRTbEOIeemA2XoylimlDQ03CgPPNaHYZbpsc78Hmngnt+WXZfJX1pjQ711V7g0H7cSJThGYfPQ==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ peerDependencies:
+ typescript: '>=4.8.4 <5.9.0'
+
+ '@typescript-eslint/utils@8.26.0':
+ resolution: {integrity: sha512-2L2tU3FVwhvU14LndnQCA2frYC8JnPDVKyQtWFPf8IYFMt/ykEN1bPolNhNbCVgOmdzTlWdusCTKA/9nKrf8Ig==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ peerDependencies:
+ eslint: ^8.57.0 || ^9.0.0
+ typescript: '>=4.8.4 <5.9.0'
+
+ '@typescript-eslint/visitor-keys@8.26.0':
+ resolution: {integrity: sha512-2z8JQJWAzPdDd51dRQ/oqIJxe99/hoLIqmf8RMCAJQtYDc535W/Jt2+RTP4bP0aKeBG1F65yjIZuczOXCmbWwg==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ acorn-jsx@5.3.2:
+ resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==}
+ peerDependencies:
+ acorn: ^6.0.0 || ^7.0.0 || ^8.0.0
+
+ acorn@8.14.1:
+ resolution: {integrity: sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==}
+ engines: {node: '>=0.4.0'}
+ hasBin: true
+
+ ajv@6.12.6:
+ resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==}
+
+ ansi-styles@4.3.0:
+ resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==}
+ engines: {node: '>=8'}
+
+ argparse@2.0.1:
+ resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}
+
+ balanced-match@1.0.2:
+ resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
+
+ brace-expansion@1.1.11:
+ resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==}
+
+ brace-expansion@2.0.1:
+ resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==}
+
+ braces@3.0.3:
+ resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==}
+ engines: {node: '>=8'}
+
+ builtin-modules@5.0.0:
+ resolution: {integrity: sha512-bkXY9WsVpY7CvMhKSR6pZilZu9Ln5WDrKVBUXf2S443etkmEO4V58heTecXcUIsNsi4Rx8JUO4NfX1IcQl4deg==}
+ engines: {node: '>=18.20'}
+
+ callsites@3.1.0:
+ resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==}
+ engines: {node: '>=6'}
+
+ chalk@4.1.2:
+ resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==}
+ engines: {node: '>=10'}
+
+ color-convert@2.0.1:
+ resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
+ engines: {node: '>=7.0.0'}
+
+ color-name@1.1.4:
+ resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
+
+ concat-map@0.0.1:
+ resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
+
+ cross-spawn@7.0.6:
+ resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==}
+ engines: {node: '>= 8'}
+
+ debug@4.4.0:
+ resolution: {integrity: sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==}
+ engines: {node: '>=6.0'}
+ peerDependencies:
+ supports-color: '*'
+ peerDependenciesMeta:
+ supports-color:
+ optional: true
+
+ deep-is@0.1.4:
+ resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==}
+
+ esbuild@0.25.0:
+ resolution: {integrity: sha512-BXq5mqc8ltbaN34cDqWuYKyNhX8D/Z0J1xdtdQ8UcIIIyJyz+ZMKUt58tF3SrZ85jcfN/PZYhjR5uDQAYNVbuw==}
+ engines: {node: '>=18'}
+ hasBin: true
+
+ escape-string-regexp@4.0.0:
+ resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==}
+ engines: {node: '>=10'}
+
+ eslint-scope@8.2.0:
+ resolution: {integrity: sha512-PHlWUfG6lvPc3yvP5A4PNyBL1W8fkDUccmI21JUu/+GKZBoH/W5u6usENXUrWFRsyoW5ACUjFGgAFQp5gUlb/A==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ eslint-visitor-keys@3.4.3:
+ resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==}
+ engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+
+ eslint-visitor-keys@4.2.0:
+ resolution: {integrity: sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ eslint@9.21.0:
+ resolution: {integrity: sha512-KjeihdFqTPhOMXTt7StsDxriV4n66ueuF/jfPNC3j/lduHwr/ijDwJMsF+wyMJethgiKi5wniIE243vi07d3pg==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ hasBin: true
+ peerDependencies:
+ jiti: '*'
+ peerDependenciesMeta:
+ jiti:
+ optional: true
+
+ espree@10.3.0:
+ resolution: {integrity: sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ esquery@1.6.0:
+ resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==}
+ engines: {node: '>=0.10'}
+
+ esrecurse@4.3.0:
+ resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==}
+ engines: {node: '>=4.0'}
+
+ estraverse@5.3.0:
+ resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==}
+ engines: {node: '>=4.0'}
+
+ esutils@2.0.3:
+ resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==}
+ engines: {node: '>=0.10.0'}
+
+ fast-deep-equal@3.1.3:
+ resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
+
+ fast-glob@3.3.3:
+ resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==}
+ engines: {node: '>=8.6.0'}
+
+ fast-json-stable-stringify@2.1.0:
+ resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==}
+
+ fast-levenshtein@2.0.6:
+ resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==}
+
+ fastq@1.19.1:
+ resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==}
+
+ file-entry-cache@8.0.0:
+ resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==}
+ engines: {node: '>=16.0.0'}
+
+ fill-range@7.1.1:
+ resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==}
+ engines: {node: '>=8'}
+
+ find-up@5.0.0:
+ resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==}
+ engines: {node: '>=10'}
+
+ flat-cache@4.0.1:
+ resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==}
+ engines: {node: '>=16'}
+
+ flatted@3.3.3:
+ resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==}
+
+ glob-parent@5.1.2:
+ resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==}
+ engines: {node: '>= 6'}
+
+ glob-parent@6.0.2:
+ resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==}
+ engines: {node: '>=10.13.0'}
+
+ globals@14.0.0:
+ resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==}
+ engines: {node: '>=18'}
+
+ globals@16.0.0:
+ resolution: {integrity: sha512-iInW14XItCXET01CQFqudPOWP2jYMl7T+QRQT+UNcR/iQncN/F0UNpgd76iFkBPgNQb4+X3LV9tLJYzwh+Gl3A==}
+ engines: {node: '>=18'}
+
+ graphemer@1.4.0:
+ resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==}
+
+ has-flag@4.0.0:
+ resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==}
+ engines: {node: '>=8'}
+
+ ignore@5.3.2:
+ resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==}
+ engines: {node: '>= 4'}
+
+ import-fresh@3.3.1:
+ resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==}
+ engines: {node: '>=6'}
+
+ imurmurhash@0.1.4:
+ resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==}
+ engines: {node: '>=0.8.19'}
+
+ is-extglob@2.1.1:
+ resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==}
+ engines: {node: '>=0.10.0'}
+
+ is-glob@4.0.3:
+ resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==}
+ engines: {node: '>=0.10.0'}
+
+ is-number@7.0.0:
+ resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==}
+ engines: {node: '>=0.12.0'}
+
+ isexe@2.0.0:
+ resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
+
+ js-yaml@4.1.0:
+ resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==}
+ hasBin: true
+
+ json-buffer@3.0.1:
+ resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==}
+
+ json-schema-traverse@0.4.1:
+ resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==}
+
+ json-stable-stringify-without-jsonify@1.0.1:
+ resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==}
+
+ keyv@4.5.4:
+ resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==}
+
+ levn@0.4.1:
+ resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==}
+ engines: {node: '>= 0.8.0'}
+
+ locate-path@6.0.0:
+ resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==}
+ engines: {node: '>=10'}
+
+ lodash.merge@4.6.2:
+ resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==}
+
+ merge2@1.4.1:
+ resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==}
+ engines: {node: '>= 8'}
+
+ micromatch@4.0.8:
+ resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==}
+ engines: {node: '>=8.6'}
+
+ minimatch@3.1.2:
+ resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==}
+
+ minimatch@9.0.5:
+ resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==}
+ engines: {node: '>=16 || 14 >=14.17'}
+
+ moment@2.29.4:
+ resolution: {integrity: sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==}
+
+ ms@2.1.3:
+ resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
+
+ natural-compare@1.4.0:
+ resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==}
+
+ obsidian@1.8.7:
+ resolution: {integrity: sha512-h4bWwNFAGRXlMlMAzdEiIM2ppTGlrh7uGOJS6w4gClrsjc+ei/3YAtU2VdFUlCiPuTHpY4aBpFJJW75S1Tl/JA==}
+ peerDependencies:
+ '@codemirror/state': ^6.0.0
+ '@codemirror/view': ^6.0.0
+
+ optionator@0.9.4:
+ resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==}
+ engines: {node: '>= 0.8.0'}
+
+ p-limit@3.1.0:
+ resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==}
+ engines: {node: '>=10'}
+
+ p-locate@5.0.0:
+ resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==}
+ engines: {node: '>=10'}
+
+ parent-module@1.0.1:
+ resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==}
+ engines: {node: '>=6'}
+
+ path-exists@4.0.0:
+ resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==}
+ engines: {node: '>=8'}
+
+ path-key@3.1.1:
+ resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==}
+ engines: {node: '>=8'}
+
+ picomatch@2.3.1:
+ resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
+ engines: {node: '>=8.6'}
+
+ prelude-ls@1.2.1:
+ resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==}
+ engines: {node: '>= 0.8.0'}
+
+ punycode@2.3.1:
+ resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==}
+ engines: {node: '>=6'}
+
+ queue-microtask@1.2.3:
+ resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
+
+ resolve-from@4.0.0:
+ resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==}
+ engines: {node: '>=4'}
+
+ reusify@1.1.0:
+ resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==}
+ engines: {iojs: '>=1.0.0', node: '>=0.10.0'}
+
+ run-parallel@1.2.0:
+ resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==}
+
+ semver@7.7.1:
+ resolution: {integrity: sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==}
+ engines: {node: '>=10'}
+ hasBin: true
+
+ shebang-command@2.0.0:
+ resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==}
+ engines: {node: '>=8'}
+
+ shebang-regex@3.0.0:
+ resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==}
+ engines: {node: '>=8'}
+
+ strip-json-comments@3.1.1:
+ resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==}
+ engines: {node: '>=8'}
+
+ style-mod@4.1.2:
+ resolution: {integrity: sha512-wnD1HyVqpJUI2+eKZ+eo1UwghftP6yuFheBqqe+bWCotBjC2K1YnteJILRMs3SM4V/0dLEW1SC27MWP5y+mwmw==}
+
+ supports-color@7.2.0:
+ resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==}
+ engines: {node: '>=8'}
+
+ to-regex-range@5.0.1:
+ resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
+ engines: {node: '>=8.0'}
+
+ ts-api-utils@2.0.1:
+ resolution: {integrity: sha512-dnlgjFSVetynI8nzgJ+qF62efpglpWRk8isUEWZGWlJYySCTD6aKvbUDu+zbPeDakk3bg5H4XpitHukgfL1m9w==}
+ engines: {node: '>=18.12'}
+ peerDependencies:
+ typescript: '>=4.8.4'
+
+ tslib@2.8.1:
+ resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==}
+
+ type-check@0.4.0:
+ resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==}
+ engines: {node: '>= 0.8.0'}
+
+ typescript-eslint@8.26.0:
+ resolution: {integrity: sha512-PtVz9nAnuNJuAVeUFvwztjuUgSnJInODAUx47VDwWPXzd5vismPOtPtt83tzNXyOjVQbPRp786D6WFW/M2koIA==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ peerDependencies:
+ eslint: ^8.57.0 || ^9.0.0
+ typescript: '>=4.8.4 <5.9.0'
+
+ typescript@5.8.2:
+ resolution: {integrity: sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==}
+ engines: {node: '>=14.17'}
+ hasBin: true
+
+ undici-types@6.20.0:
+ resolution: {integrity: sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==}
+
+ uri-js@4.4.1:
+ resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==}
+
+ w3c-keyname@2.2.8:
+ resolution: {integrity: sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==}
+
+ which@2.0.2:
+ resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==}
+ engines: {node: '>= 8'}
+ hasBin: true
+
+ word-wrap@1.2.5:
+ resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==}
+ engines: {node: '>=0.10.0'}
+
+ yocto-queue@0.1.0:
+ resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==}
+ engines: {node: '>=10'}
+
+snapshots:
+
+ '@codemirror/language@6.10.8':
+ dependencies:
+ '@codemirror/state': 6.5.2
+ '@codemirror/view': 6.36.4
+ '@lezer/common': 1.2.3
+ '@lezer/highlight': 1.2.1
+ '@lezer/lr': 1.4.2
+ style-mod: 4.1.2
+
+ '@codemirror/state@6.5.2':
+ dependencies:
+ '@marijn/find-cluster-break': 1.0.2
+
+ '@codemirror/view@6.36.4':
+ dependencies:
+ '@codemirror/state': 6.5.2
+ style-mod: 4.1.2
+ w3c-keyname: 2.2.8
+
+ '@esbuild/aix-ppc64@0.25.0':
+ optional: true
+
+ '@esbuild/android-arm64@0.25.0':
+ optional: true
+
+ '@esbuild/android-arm@0.25.0':
+ optional: true
+
+ '@esbuild/android-x64@0.25.0':
+ optional: true
+
+ '@esbuild/darwin-arm64@0.25.0':
+ optional: true
+
+ '@esbuild/darwin-x64@0.25.0':
+ optional: true
+
+ '@esbuild/freebsd-arm64@0.25.0':
+ optional: true
+
+ '@esbuild/freebsd-x64@0.25.0':
+ optional: true
+
+ '@esbuild/linux-arm64@0.25.0':
+ optional: true
+
+ '@esbuild/linux-arm@0.25.0':
+ optional: true
+
+ '@esbuild/linux-ia32@0.25.0':
+ optional: true
+
+ '@esbuild/linux-loong64@0.25.0':
+ optional: true
+
+ '@esbuild/linux-mips64el@0.25.0':
+ optional: true
+
+ '@esbuild/linux-ppc64@0.25.0':
+ optional: true
+
+ '@esbuild/linux-riscv64@0.25.0':
+ optional: true
+
+ '@esbuild/linux-s390x@0.25.0':
+ optional: true
+
+ '@esbuild/linux-x64@0.25.0':
+ optional: true
+
+ '@esbuild/netbsd-arm64@0.25.0':
+ optional: true
+
+ '@esbuild/netbsd-x64@0.25.0':
+ optional: true
+
+ '@esbuild/openbsd-arm64@0.25.0':
+ optional: true
+
+ '@esbuild/openbsd-x64@0.25.0':
+ optional: true
+
+ '@esbuild/sunos-x64@0.25.0':
+ optional: true
+
+ '@esbuild/win32-arm64@0.25.0':
+ optional: true
+
+ '@esbuild/win32-ia32@0.25.0':
+ optional: true
+
+ '@esbuild/win32-x64@0.25.0':
+ optional: true
+
+ '@eslint-community/eslint-utils@4.4.1(eslint@9.21.0)':
+ dependencies:
+ eslint: 9.21.0
+ eslint-visitor-keys: 3.4.3
+
+ '@eslint-community/regexpp@4.12.1': {}
+
+ '@eslint/config-array@0.19.2':
+ dependencies:
+ '@eslint/object-schema': 2.1.6
+ debug: 4.4.0
+ minimatch: 3.1.2
+ transitivePeerDependencies:
+ - supports-color
+
+ '@eslint/core@0.12.0':
+ dependencies:
+ '@types/json-schema': 7.0.15
+
+ '@eslint/eslintrc@3.3.0':
+ dependencies:
+ ajv: 6.12.6
+ debug: 4.4.0
+ espree: 10.3.0
+ globals: 14.0.0
+ ignore: 5.3.2
+ import-fresh: 3.3.1
+ js-yaml: 4.1.0
+ minimatch: 3.1.2
+ strip-json-comments: 3.1.1
+ transitivePeerDependencies:
+ - supports-color
+
+ '@eslint/js@9.21.0': {}
+
+ '@eslint/object-schema@2.1.6': {}
+
+ '@eslint/plugin-kit@0.2.7':
+ dependencies:
+ '@eslint/core': 0.12.0
+ levn: 0.4.1
+
+ '@humanfs/core@0.19.1': {}
+
+ '@humanfs/node@0.16.6':
+ dependencies:
+ '@humanfs/core': 0.19.1
+ '@humanwhocodes/retry': 0.3.1
+
+ '@humanwhocodes/module-importer@1.0.1': {}
+
+ '@humanwhocodes/retry@0.3.1': {}
+
+ '@humanwhocodes/retry@0.4.2': {}
+
+ '@lezer/common@1.2.3': {}
+
+ '@lezer/highlight@1.2.1':
+ dependencies:
+ '@lezer/common': 1.2.3
+
+ '@lezer/lr@1.4.2':
+ dependencies:
+ '@lezer/common': 1.2.3
+
+ '@marijn/find-cluster-break@1.0.2': {}
+
+ '@nodelib/fs.scandir@2.1.5':
+ dependencies:
+ '@nodelib/fs.stat': 2.0.5
+ run-parallel: 1.2.0
+
+ '@nodelib/fs.stat@2.0.5': {}
+
+ '@nodelib/fs.walk@1.2.8':
+ dependencies:
+ '@nodelib/fs.scandir': 2.1.5
+ fastq: 1.19.1
+
+ '@types/codemirror@5.60.8':
+ dependencies:
+ '@types/tern': 0.23.9
+
+ '@types/estree@1.0.6': {}
+
+ '@types/json-schema@7.0.15': {}
+
+ '@types/node@22.13.9':
+ dependencies:
+ undici-types: 6.20.0
+
+ '@types/tern@0.23.9':
+ dependencies:
+ '@types/estree': 1.0.6
+
+ '@typescript-eslint/eslint-plugin@8.26.0(@typescript-eslint/parser@8.26.0(eslint@9.21.0)(typescript@5.8.2))(eslint@9.21.0)(typescript@5.8.2)':
+ dependencies:
+ '@eslint-community/regexpp': 4.12.1
+ '@typescript-eslint/parser': 8.26.0(eslint@9.21.0)(typescript@5.8.2)
+ '@typescript-eslint/scope-manager': 8.26.0
+ '@typescript-eslint/type-utils': 8.26.0(eslint@9.21.0)(typescript@5.8.2)
+ '@typescript-eslint/utils': 8.26.0(eslint@9.21.0)(typescript@5.8.2)
+ '@typescript-eslint/visitor-keys': 8.26.0
+ eslint: 9.21.0
+ graphemer: 1.4.0
+ ignore: 5.3.2
+ natural-compare: 1.4.0
+ ts-api-utils: 2.0.1(typescript@5.8.2)
+ typescript: 5.8.2
+ transitivePeerDependencies:
+ - supports-color
+
+ '@typescript-eslint/parser@8.26.0(eslint@9.21.0)(typescript@5.8.2)':
+ dependencies:
+ '@typescript-eslint/scope-manager': 8.26.0
+ '@typescript-eslint/types': 8.26.0
+ '@typescript-eslint/typescript-estree': 8.26.0(typescript@5.8.2)
+ '@typescript-eslint/visitor-keys': 8.26.0
+ debug: 4.4.0
+ eslint: 9.21.0
+ typescript: 5.8.2
+ transitivePeerDependencies:
+ - supports-color
+
+ '@typescript-eslint/scope-manager@8.26.0':
+ dependencies:
+ '@typescript-eslint/types': 8.26.0
+ '@typescript-eslint/visitor-keys': 8.26.0
+
+ '@typescript-eslint/type-utils@8.26.0(eslint@9.21.0)(typescript@5.8.2)':
+ dependencies:
+ '@typescript-eslint/typescript-estree': 8.26.0(typescript@5.8.2)
+ '@typescript-eslint/utils': 8.26.0(eslint@9.21.0)(typescript@5.8.2)
+ debug: 4.4.0
+ eslint: 9.21.0
+ ts-api-utils: 2.0.1(typescript@5.8.2)
+ typescript: 5.8.2
+ transitivePeerDependencies:
+ - supports-color
+
+ '@typescript-eslint/types@8.26.0': {}
+
+ '@typescript-eslint/typescript-estree@8.26.0(typescript@5.8.2)':
+ dependencies:
+ '@typescript-eslint/types': 8.26.0
+ '@typescript-eslint/visitor-keys': 8.26.0
+ debug: 4.4.0
+ fast-glob: 3.3.3
+ is-glob: 4.0.3
+ minimatch: 9.0.5
+ semver: 7.7.1
+ ts-api-utils: 2.0.1(typescript@5.8.2)
+ typescript: 5.8.2
+ transitivePeerDependencies:
+ - supports-color
+
+ '@typescript-eslint/utils@8.26.0(eslint@9.21.0)(typescript@5.8.2)':
+ dependencies:
+ '@eslint-community/eslint-utils': 4.4.1(eslint@9.21.0)
+ '@typescript-eslint/scope-manager': 8.26.0
+ '@typescript-eslint/types': 8.26.0
+ '@typescript-eslint/typescript-estree': 8.26.0(typescript@5.8.2)
+ eslint: 9.21.0
+ typescript: 5.8.2
+ transitivePeerDependencies:
+ - supports-color
+
+ '@typescript-eslint/visitor-keys@8.26.0':
+ dependencies:
+ '@typescript-eslint/types': 8.26.0
+ eslint-visitor-keys: 4.2.0
+
+ acorn-jsx@5.3.2(acorn@8.14.1):
+ dependencies:
+ acorn: 8.14.1
+
+ acorn@8.14.1: {}
+
+ ajv@6.12.6:
+ dependencies:
+ fast-deep-equal: 3.1.3
+ fast-json-stable-stringify: 2.1.0
+ json-schema-traverse: 0.4.1
+ uri-js: 4.4.1
+
+ ansi-styles@4.3.0:
+ dependencies:
+ color-convert: 2.0.1
+
+ argparse@2.0.1: {}
+
+ balanced-match@1.0.2: {}
+
+ brace-expansion@1.1.11:
+ dependencies:
+ balanced-match: 1.0.2
+ concat-map: 0.0.1
+
+ brace-expansion@2.0.1:
+ dependencies:
+ balanced-match: 1.0.2
+
+ braces@3.0.3:
+ dependencies:
+ fill-range: 7.1.1
+
+ builtin-modules@5.0.0: {}
+
+ callsites@3.1.0: {}
+
+ chalk@4.1.2:
+ dependencies:
+ ansi-styles: 4.3.0
+ supports-color: 7.2.0
+
+ color-convert@2.0.1:
+ dependencies:
+ color-name: 1.1.4
+
+ color-name@1.1.4: {}
+
+ concat-map@0.0.1: {}
+
+ cross-spawn@7.0.6:
+ dependencies:
+ path-key: 3.1.1
+ shebang-command: 2.0.0
+ which: 2.0.2
+
+ debug@4.4.0:
+ dependencies:
+ ms: 2.1.3
+
+ deep-is@0.1.4: {}
+
+ esbuild@0.25.0:
+ optionalDependencies:
+ '@esbuild/aix-ppc64': 0.25.0
+ '@esbuild/android-arm': 0.25.0
+ '@esbuild/android-arm64': 0.25.0
+ '@esbuild/android-x64': 0.25.0
+ '@esbuild/darwin-arm64': 0.25.0
+ '@esbuild/darwin-x64': 0.25.0
+ '@esbuild/freebsd-arm64': 0.25.0
+ '@esbuild/freebsd-x64': 0.25.0
+ '@esbuild/linux-arm': 0.25.0
+ '@esbuild/linux-arm64': 0.25.0
+ '@esbuild/linux-ia32': 0.25.0
+ '@esbuild/linux-loong64': 0.25.0
+ '@esbuild/linux-mips64el': 0.25.0
+ '@esbuild/linux-ppc64': 0.25.0
+ '@esbuild/linux-riscv64': 0.25.0
+ '@esbuild/linux-s390x': 0.25.0
+ '@esbuild/linux-x64': 0.25.0
+ '@esbuild/netbsd-arm64': 0.25.0
+ '@esbuild/netbsd-x64': 0.25.0
+ '@esbuild/openbsd-arm64': 0.25.0
+ '@esbuild/openbsd-x64': 0.25.0
+ '@esbuild/sunos-x64': 0.25.0
+ '@esbuild/win32-arm64': 0.25.0
+ '@esbuild/win32-ia32': 0.25.0
+ '@esbuild/win32-x64': 0.25.0
+
+ escape-string-regexp@4.0.0: {}
+
+ eslint-scope@8.2.0:
+ dependencies:
+ esrecurse: 4.3.0
+ estraverse: 5.3.0
+
+ eslint-visitor-keys@3.4.3: {}
+
+ eslint-visitor-keys@4.2.0: {}
+
+ eslint@9.21.0:
+ dependencies:
+ '@eslint-community/eslint-utils': 4.4.1(eslint@9.21.0)
+ '@eslint-community/regexpp': 4.12.1
+ '@eslint/config-array': 0.19.2
+ '@eslint/core': 0.12.0
+ '@eslint/eslintrc': 3.3.0
+ '@eslint/js': 9.21.0
+ '@eslint/plugin-kit': 0.2.7
+ '@humanfs/node': 0.16.6
+ '@humanwhocodes/module-importer': 1.0.1
+ '@humanwhocodes/retry': 0.4.2
+ '@types/estree': 1.0.6
+ '@types/json-schema': 7.0.15
+ ajv: 6.12.6
+ chalk: 4.1.2
+ cross-spawn: 7.0.6
+ debug: 4.4.0
+ escape-string-regexp: 4.0.0
+ eslint-scope: 8.2.0
+ eslint-visitor-keys: 4.2.0
+ espree: 10.3.0
+ esquery: 1.6.0
+ esutils: 2.0.3
+ fast-deep-equal: 3.1.3
+ file-entry-cache: 8.0.0
+ find-up: 5.0.0
+ glob-parent: 6.0.2
+ ignore: 5.3.2
+ imurmurhash: 0.1.4
+ is-glob: 4.0.3
+ json-stable-stringify-without-jsonify: 1.0.1
+ lodash.merge: 4.6.2
+ minimatch: 3.1.2
+ natural-compare: 1.4.0
+ optionator: 0.9.4
+ transitivePeerDependencies:
+ - supports-color
+
+ espree@10.3.0:
+ dependencies:
+ acorn: 8.14.1
+ acorn-jsx: 5.3.2(acorn@8.14.1)
+ eslint-visitor-keys: 4.2.0
+
+ esquery@1.6.0:
+ dependencies:
+ estraverse: 5.3.0
+
+ esrecurse@4.3.0:
+ dependencies:
+ estraverse: 5.3.0
+
+ estraverse@5.3.0: {}
+
+ esutils@2.0.3: {}
+
+ fast-deep-equal@3.1.3: {}
+
+ fast-glob@3.3.3:
+ dependencies:
+ '@nodelib/fs.stat': 2.0.5
+ '@nodelib/fs.walk': 1.2.8
+ glob-parent: 5.1.2
+ merge2: 1.4.1
+ micromatch: 4.0.8
+
+ fast-json-stable-stringify@2.1.0: {}
+
+ fast-levenshtein@2.0.6: {}
+
+ fastq@1.19.1:
+ dependencies:
+ reusify: 1.1.0
+
+ file-entry-cache@8.0.0:
+ dependencies:
+ flat-cache: 4.0.1
+
+ fill-range@7.1.1:
+ dependencies:
+ to-regex-range: 5.0.1
+
+ find-up@5.0.0:
+ dependencies:
+ locate-path: 6.0.0
+ path-exists: 4.0.0
+
+ flat-cache@4.0.1:
+ dependencies:
+ flatted: 3.3.3
+ keyv: 4.5.4
+
+ flatted@3.3.3: {}
+
+ glob-parent@5.1.2:
+ dependencies:
+ is-glob: 4.0.3
+
+ glob-parent@6.0.2:
+ dependencies:
+ is-glob: 4.0.3
+
+ globals@14.0.0: {}
+
+ globals@16.0.0: {}
+
+ graphemer@1.4.0: {}
+
+ has-flag@4.0.0: {}
+
+ ignore@5.3.2: {}
+
+ import-fresh@3.3.1:
+ dependencies:
+ parent-module: 1.0.1
+ resolve-from: 4.0.0
+
+ imurmurhash@0.1.4: {}
+
+ is-extglob@2.1.1: {}
+
+ is-glob@4.0.3:
+ dependencies:
+ is-extglob: 2.1.1
+
+ is-number@7.0.0: {}
+
+ isexe@2.0.0: {}
+
+ js-yaml@4.1.0:
+ dependencies:
+ argparse: 2.0.1
+
+ json-buffer@3.0.1: {}
+
+ json-schema-traverse@0.4.1: {}
+
+ json-stable-stringify-without-jsonify@1.0.1: {}
+
+ keyv@4.5.4:
+ dependencies:
+ json-buffer: 3.0.1
+
+ levn@0.4.1:
+ dependencies:
+ prelude-ls: 1.2.1
+ type-check: 0.4.0
+
+ locate-path@6.0.0:
+ dependencies:
+ p-locate: 5.0.0
+
+ lodash.merge@4.6.2: {}
+
+ merge2@1.4.1: {}
+
+ micromatch@4.0.8:
+ dependencies:
+ braces: 3.0.3
+ picomatch: 2.3.1
+
+ minimatch@3.1.2:
+ dependencies:
+ brace-expansion: 1.1.11
+
+ minimatch@9.0.5:
+ dependencies:
+ brace-expansion: 2.0.1
+
+ moment@2.29.4: {}
+
+ ms@2.1.3: {}
+
+ natural-compare@1.4.0: {}
+
+ obsidian@1.8.7(@codemirror/state@6.5.2)(@codemirror/view@6.36.4):
+ dependencies:
+ '@codemirror/state': 6.5.2
+ '@codemirror/view': 6.36.4
+ '@types/codemirror': 5.60.8
+ moment: 2.29.4
+
+ optionator@0.9.4:
+ dependencies:
+ deep-is: 0.1.4
+ fast-levenshtein: 2.0.6
+ levn: 0.4.1
+ prelude-ls: 1.2.1
+ type-check: 0.4.0
+ word-wrap: 1.2.5
+
+ p-limit@3.1.0:
+ dependencies:
+ yocto-queue: 0.1.0
+
+ p-locate@5.0.0:
+ dependencies:
+ p-limit: 3.1.0
+
+ parent-module@1.0.1:
+ dependencies:
+ callsites: 3.1.0
+
+ path-exists@4.0.0: {}
+
+ path-key@3.1.1: {}
+
+ picomatch@2.3.1: {}
+
+ prelude-ls@1.2.1: {}
+
+ punycode@2.3.1: {}
+
+ queue-microtask@1.2.3: {}
+
+ resolve-from@4.0.0: {}
+
+ reusify@1.1.0: {}
+
+ run-parallel@1.2.0:
+ dependencies:
+ queue-microtask: 1.2.3
+
+ semver@7.7.1: {}
+
+ shebang-command@2.0.0:
+ dependencies:
+ shebang-regex: 3.0.0
+
+ shebang-regex@3.0.0: {}
+
+ strip-json-comments@3.1.1: {}
+
+ style-mod@4.1.2: {}
+
+ supports-color@7.2.0:
+ dependencies:
+ has-flag: 4.0.0
+
+ to-regex-range@5.0.1:
+ dependencies:
+ is-number: 7.0.0
+
+ ts-api-utils@2.0.1(typescript@5.8.2):
+ dependencies:
+ typescript: 5.8.2
+
+ tslib@2.8.1: {}
+
+ type-check@0.4.0:
+ dependencies:
+ prelude-ls: 1.2.1
+
+ typescript-eslint@8.26.0(eslint@9.21.0)(typescript@5.8.2):
+ dependencies:
+ '@typescript-eslint/eslint-plugin': 8.26.0(@typescript-eslint/parser@8.26.0(eslint@9.21.0)(typescript@5.8.2))(eslint@9.21.0)(typescript@5.8.2)
+ '@typescript-eslint/parser': 8.26.0(eslint@9.21.0)(typescript@5.8.2)
+ '@typescript-eslint/utils': 8.26.0(eslint@9.21.0)(typescript@5.8.2)
+ eslint: 9.21.0
+ typescript: 5.8.2
+ transitivePeerDependencies:
+ - supports-color
+
+ typescript@5.8.2: {}
+
+ undici-types@6.20.0: {}
+
+ uri-js@4.4.1:
+ dependencies:
+ punycode: 2.3.1
+
+ w3c-keyname@2.2.8: {}
+
+ which@2.0.2:
+ dependencies:
+ isexe: 2.0.0
+
+ word-wrap@1.2.5: {}
+
+ yocto-queue@0.1.0: {}
diff --git a/src/CacheManager.ts b/src/CacheManager.ts
new file mode 100644
index 0000000..16d191e
--- /dev/null
+++ b/src/CacheManager.ts
@@ -0,0 +1,120 @@
+import { App } from "obsidian";
+
+export interface CacheMetadataItem {
+ /** Unique key to identify the cached item. */
+ key: string;
+
+ /** Path to the associated file. */
+ filePath?: string;
+
+ type: CacheResourceType;
+}
+
+export interface CacheItem {
+ metadata: CacheMetadataItem;
+
+ /** Path to the cached file. */
+ filePath: string;
+}
+
+export interface CacheResult {
+ item: CacheItem | undefined;
+
+ /** Whether this `item` was fetched from the cache. */
+ fromCache: boolean;
+
+ error: Error | undefined;
+}
+
+export enum CacheResourceType {
+ Undefined = 0,
+ ExternalImage = 1,
+}
+
+export class CacheManager {
+ private cache: { [key: string]: CacheItem } = {};
+ private app: App;
+ private cacheFolderPath: string;
+
+ constructor(app: App, cacheFolderPath: string) {
+ this.app = app;
+ this.cacheFolderPath = cacheFolderPath;
+ }
+
+ async getCache(metadataItem: CacheMetadataItem, callback: (result: CacheResult) => any) {
+
+ if (metadataItem.key.length == 0) {
+ callback({
+ item: undefined,
+ fromCache: false,
+ error: new Error(`The cache key is not set.`),
+ });
+ return;
+ }
+
+ if (!CacheManager.isExternalUrl(metadataItem.key)) {
+ callback({
+ item: undefined,
+ fromCache: false,
+ error: new Error(`The cache key must be an external Url (${metadataItem.key})`),
+ });
+ return;
+ }
+
+ // TODO
+ if (metadataItem.filePath) {
+ const file = this.app.vault.getFileByPath(metadataItem.filePath);
+ if (!file)
+ return;
+ }
+
+ // Create a result from the cache, assuming it exists.
+ let cacheResult: CacheResult = {
+ item: this.cache[metadataItem.key],
+ fromCache: true,
+ error: undefined,
+ }
+
+ // Get it if it doesn't.
+ if (!cacheResult.item) {
+ await sleep(Math.floor(Math.random() * 101) + 1000);
+ const cachedItem: CacheItem = {
+ metadata: metadataItem,
+ filePath: this.app.vault.adapter.getResourcePath(this.cacheFolderPath + "/image.png"),
+ };
+ this.cache[metadataItem.key] = cachedItem;
+ cacheResult = { item: cachedItem, fromCache: false, error: undefined }
+ }
+
+ callback(cacheResult);
+ }
+
+ clearCached() {
+ this.cache = {};
+ }
+
+ /**
+ * Aborts current download requests for the specified file.
+ * @todo
+ */
+ cancelOngoing(filePath: string) {
+
+ }
+
+ /**
+ * Aborts all current download requests.
+ * @todo
+ */
+ cancelAllOngoing() {
+
+ }
+
+ private static isExternalUrl(src: string): boolean {
+ try {
+ const url = new URL(src);
+ return url.protocol === "http:" || url.protocol === "https:";
+ } catch (error) {
+ return false;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/SettingTab.ts b/src/SettingTab.ts
new file mode 100644
index 0000000..28ec0c7
--- /dev/null
+++ b/src/SettingTab.ts
@@ -0,0 +1,46 @@
+import { PluginSettingTab, Setting, Plugin } from "obsidian";
+
+interface PluginSettings {
+ /** `true` to make sure there's a .gitignore file in the cache directory; `false` to make sure otherwise. */
+ gitIgnoreCacheDir: boolean;
+}
+
+const DEFAULT_SETTINGS: PluginSettings = {
+ gitIgnoreCacheDir: true,
+}
+
+export class SettingsManager {
+ settings: PluginSettings;
+ save: () => Promise;
+
+ constructor(settings: any, save: (settings: PluginSettings) => Promise) {
+ this.settings = Object.assign({}, DEFAULT_SETTINGS, settings);
+ this.save = () => save(this.settings);
+ }
+}
+
+export class SettingTab extends PluginSettingTab {
+ settingsManager: SettingsManager;
+
+ constructor(plugin: Plugin, settingsManager: SettingsManager) {
+ super(plugin.app, plugin);
+ this.settingsManager = settingsManager;
+ }
+
+ display(): void {
+ const { containerEl } = this;
+
+ containerEl.empty();
+
+ new Setting(containerEl)
+ .setName("Exclude cache from Git")
+ .setDesc("Enable to automatically add a .gitignore file to your Obsidian cache, preventing it from being committed to Git.")
+ .addToggle((toggle) => {
+ toggle.setValue(this.settingsManager.settings.gitIgnoreCacheDir);
+ toggle.onChange(async (value) => {
+ this.settingsManager.settings.gitIgnoreCacheDir = value;
+ await this.settingsManager.save();
+ });
+ })
+ }
+}
\ No newline at end of file
diff --git a/tsconfig.json b/tsconfig.json
new file mode 100644
index 0000000..c44b729
--- /dev/null
+++ b/tsconfig.json
@@ -0,0 +1,24 @@
+{
+ "compilerOptions": {
+ "baseUrl": ".",
+ "inlineSourceMap": true,
+ "inlineSources": true,
+ "module": "ESNext",
+ "target": "ES6",
+ "allowJs": true,
+ "noImplicitAny": true,
+ "moduleResolution": "node",
+ "importHelpers": true,
+ "isolatedModules": true,
+ "strictNullChecks": true,
+ "lib": [
+ "DOM",
+ "ES5",
+ "ES6",
+ "ES7"
+ ]
+ },
+ "include": [
+ "**/*.ts"
+ ]
+}
diff --git a/versions.json b/versions.json
new file mode 100644
index 0000000..ce2cda7
--- /dev/null
+++ b/versions.json
@@ -0,0 +1,3 @@
+{
+ "1.0.0": "1.8.0"
+}