Compare commits

...

9 Commits

Author SHA1 Message Date
vorotamoroz
4f8a74107c remove unnecessary package 2026-06-09 08:11:05 +01:00
vorotamoroz
42ed0d8795 update dir notation 2026-06-09 07:25:41 +01:00
vorotamoroz
54c2b1c6db reduce no-explicit-any 2026-06-09 05:59:02 +01:00
vorotamoroz
0856693aac Add instruction 2026-06-09 02:26:07 +01:00
vorotamoroz
39d78a04ac Enhance remote database management and add --vault option
Added new commands for remote database management and introduced --vault option for daemon and mirror commands.
2026-06-08 21:12:42 +09:00
vorotamoroz
0b8d73ccd8 Merge pull request #948 from vrtmrz/adjust_overwrite_prevention
Adjust overwrite prevention
2026-06-08 19:57:20 +09:00
vorotamoroz
5921a71227 Add CI 2026-06-08 10:52:00 +00:00
vorotamoroz
a40929c9e4 fixed: enhance conflict handling by adding settings check for document writes 2026-06-08 10:47:34 +00:00
vorotamoroz
d9903bfe9e Merge pull request #947 from vrtmrz/0_25_74
Release: 0.25.74
2026-06-08 19:29:33 +09:00
50 changed files with 279 additions and 837 deletions

View File

@@ -58,16 +58,16 @@ jobs:
case "$SELECTED_TASK" in
test:ci)
TASK_MATRIX='["test:setup-put-cat","test:mirror","test:push-pull","test:sync-two-local","test:sync-locked-remote","test:e2e-matrix:couchdb-enc0","test:e2e-matrix:couchdb-enc1","test:e2e-matrix:minio-enc0","test:e2e-matrix:minio-enc1"]'
TASK_MATRIX='["test:setup-put-cat","test:mirror","test:daemon","test:push-pull","test:decoupled-vault","test:sync-two-local","test:sync-locked-remote","test:remote-commands","test:e2e-matrix:couchdb-enc0","test:e2e-matrix:couchdb-enc1","test:e2e-matrix:minio-enc0","test:e2e-matrix:minio-enc1"]'
;;
test:p2p)
TASK_MATRIX='["test:p2p-host","test:p2p-peers","test:p2p-sync","test:p2p-three-nodes","test:p2p-upload-download"]'
;;
test:all)
TASK_MATRIX='["test:setup-put-cat","test:mirror","test:push-pull","test:sync-two-local","test:sync-locked-remote","test:p2p-host","test:p2p-peers","test:p2p-sync","test:p2p-three-nodes","test:p2p-upload-download","test:e2e-matrix:couchdb-enc0","test:e2e-matrix:couchdb-enc1","test:e2e-matrix:minio-enc0","test:e2e-matrix:minio-enc1"]'
TASK_MATRIX='["test:setup-put-cat","test:mirror","test:daemon","test:push-pull","test:decoupled-vault","test:sync-two-local","test:sync-locked-remote","test:remote-commands","test:p2p-host","test:p2p-peers","test:p2p-sync","test:p2p-three-nodes","test:p2p-upload-download","test:e2e-matrix:couchdb-enc0","test:e2e-matrix:couchdb-enc1","test:e2e-matrix:minio-enc0","test:e2e-matrix:minio-enc1"]'
;;
test:local)
TASK_MATRIX='["test:setup-put-cat","test:mirror"]'
TASK_MATRIX='["test:setup-put-cat","test:mirror","test:daemon"]'
;;
test:e2e-matrix)
TASK_MATRIX='["test:e2e-matrix:couchdb-enc0","test:e2e-matrix:couchdb-enc1","test:e2e-matrix:minio-enc0","test:e2e-matrix:minio-enc1"]'

View File

@@ -32,6 +32,9 @@ Always adhere to the following stylistic and spelling rules:
- Use **'dialogue'** in documentation, user-facing messages, and general text. Use **'dialog'** only inside source code (e.g. class names, methods).
- Use the hyphenated form **'plug-in'** in user-facing text. Use **'plugin'** only in codebase files, configuration settings, or technical contexts.
5. **User Communication Language**:
- Always reply to the user in the language in which they asked the question.
---
## Technical & Architecture Rules

View File

@@ -19,11 +19,15 @@ const packageJson = JSON.parse(fs.readFileSync("./package.json") + "");
const updateInfo = JSON.stringify(fs.readFileSync("./updates.md") + "");
const PATHS_TEST_INSTALL = process.env?.PATHS_TEST_INSTALL || "";
const PATH_TEST_INSTALL = PATHS_TEST_INSTALL.split(path.delimiter).map(p => p.trim()).filter(p => p.length);
const PATH_TEST_INSTALL = PATHS_TEST_INSTALL.split(path.delimiter)
.map((p) => p.trim())
.filter((p) => p.length);
if (PATH_TEST_INSTALL) {
console.log(`Built files will be copied to ${PATH_TEST_INSTALL}`);
} else {
console.log("Development build: You can install the plug-in to Obsidian for testing by exporting the PATHS_TEST_INSTALL environment variable with the paths to your vault plugins directories separated by your system path delimiter (':' on Unix, ';' on Windows).");
console.log(
"Development build: You can install the plug-in to Obsidian for testing by exporting the PATHS_TEST_INSTALL environment variable with the paths to your vault plugins directories separated by your system path delimiter (':' on Unix, ';' on Windows)."
);
}
const moduleAliasPlugin = {
@@ -66,6 +70,34 @@ const moduleAliasPlugin = {
},
};
const removePragmaCommentsPlugin = {
name: "remove-pragma-comments",
setup(build) {
// Filter target extensions (e.g., JavaScript and TypeScript)
build.onLoad({ filter: /\.[jt]s?$/ }, async (args) => {
const source = await fs.promises.readFile(args.path, "utf8");
// Regex targeting both single-line and multi-line comments
// This regex looks for:
// - /* eslint ... */ (multi-line)
// const esLintPragmaRegexBlock = /\/\*[\s\S]*?eslint[\s\S]*?\*\/|([^\\:]|^)\/\/.*eslint.*$/gm;
// - // eslint-disable-next-line
let cleanedSource = source;
const tsIgnoreRegex = /\/\*\s*@ts-ignore\s*\*\/|([^\\:]|^)\/\/.*?@ts-ignore.*$/gm;
const esLintPragmaRegexLine = /([^\\:]|^)\/\/.*?eslint-.*$/gm;
const exps = [tsIgnoreRegex, esLintPragmaRegexLine];
for (const exp of exps) {
cleanedSource = cleanedSource.replace(exp, "$1");
}
return {
contents: cleanedSource,
loader: args.path.endsWith("ts") ? "ts" : "js",
};
});
},
};
/** @type esbuild.Plugin[] */
const plugins = [
{
@@ -177,6 +209,7 @@ const context = await esbuild.context({
preprocess: sveltePreprocess(),
compilerOptions: { css: "injected", preserveComments: false },
}),
removePragmaCommentsPlugin,
...plugins,
],
});

View File

@@ -18,11 +18,11 @@ export default defineConfig([
"**/*.json",
"**/.eslintrc.js.bak",
// Files from linked dependencies (those files should not exist for most people).
"modules/octagonal-wheels/dist/**/*",
"modules/octagonal-wheels/dist",
// Sub-projects (Exclude from root linting as they have different environments)
"src/apps/**/*",
"utils/**/*",
"src/apps",
"utils",
// Specific exclusions from common library (src/lib)
"src/lib/coverage",
@@ -73,11 +73,13 @@ export default defineConfig([
"no-unused-labels": "off",
"no-prototype-builtins": "off",
"require-await": "off",
// -- TypeScript specific rules (Gradual adoption of stricter rules, currently set to 'warn' for a while).
"@typescript-eslint/no-explicit-any": "warn",
"@typescript-eslint/no-redundant-type-constituents": "warn",
// -- TypeScript specific rules
// @typescript-eslint/no-unsafe-* rules and @typescript-eslint/no-explicit-any:
// This project contains a lot of library-sh code where the use of `any` is often necessary and justified.
// Rules is now set to 'off' for a while.
"@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/no-unsafe-argument": "off",
"@typescript-eslint/no-unsafe-call": "off",
"@typescript-eslint/no-unsafe-member-access": "off",

666
package-lock.json generated
View File

@@ -15,15 +15,14 @@
"@smithy/middleware-apply-body-checksum": "^4.3.9",
"@smithy/protocol-http": "^5.3.9",
"@smithy/querystring-builder": "^4.2.9",
"@smithy/types": "^4.14.3",
"@smithy/util-retry": "^4.4.5",
"@trystero-p2p/nostr": "^0.24.0",
"chokidar": "^4.0.0",
"commander": "^14.0.3",
"diff-match-patch": "^1.0.5",
"fflate": "^0.8.2",
"idb": "^8.0.3",
"markdown-it": "^14.1.1",
"micromatch": "^4.0.0",
"minimatch": "^10.2.2",
"obsidian": "^1.12.3",
"octagonal-wheels": "^0.1.46",
@@ -33,7 +32,6 @@
"xxhash-wasm-102": "npm:xxhash-wasm@^1.0.2"
},
"devDependencies": {
"@chialab/esbuild-plugin-worker": "^0.19.0",
"@eslint/js": "^9.39.3",
"@sveltejs/vite-plugin-svelte": "^6.2.4",
"@tsconfig/svelte": "^5.0.8",
@@ -50,7 +48,6 @@
"@types/pouchdb-mapreduce": "^6.1.10",
"@types/pouchdb-replication": "^6.4.7",
"@types/transform-pouch": "^1.0.6",
"@typescript-eslint/eslint-plugin": "8.56.1",
"@typescript-eslint/parser": "8.56.1",
"@vitest/browser": "^4.1.8",
"@vitest/browser-playwright": "^4.1.8",
@@ -66,7 +63,6 @@
"globals": "^14.0.0",
"playwright": "^1.58.2",
"postcss": "^8.5.6",
"postcss-load-config": "^6.0.1",
"pouchdb-adapter-http": "^9.0.0",
"pouchdb-adapter-idb": "^9.0.0",
"pouchdb-adapter-indexeddb": "^9.0.0",
@@ -82,11 +78,11 @@
"rollup-plugin-copy": "^3.5.0",
"svelte": "5.41.1",
"svelte-check": "^4.4.3",
"svelte-eslint-parser": "^1.8.0",
"svelte-preprocess": "^6.0.3",
"terser": "^5.39.0",
"tinyglobby": "^0.2.15",
"transform-pouch": "^2.0.0",
"tslib": "^2.8.1",
"tsx": "^4.21.0",
"typescript": "5.9.3",
"vite": "^7.3.1",
@@ -1264,77 +1260,6 @@
"dev": true,
"license": "MIT"
},
"node_modules/@chialab/esbuild-plugin-meta-url": {
"version": "0.19.1",
"resolved": "https://registry.npmjs.org/@chialab/esbuild-plugin-meta-url/-/esbuild-plugin-meta-url-0.19.1.tgz",
"integrity": "sha512-psYdhXG5CTA16PkOc4RhWj7XJQWONXJIrRTp3xkxKW0A7d0n/B0W+TABMR3zohboyoC6Uqv1zO8jf62zx2Xh6Q==",
"dev": true,
"license": "MIT",
"dependencies": {
"@chialab/esbuild-rna": "^0.19.1",
"@chialab/estransform": "^0.19.1",
"mime-types": "^2.1.35"
},
"engines": {
"node": ">=18"
}
},
"node_modules/@chialab/esbuild-plugin-worker": {
"version": "0.19.1",
"resolved": "https://registry.npmjs.org/@chialab/esbuild-plugin-worker/-/esbuild-plugin-worker-0.19.1.tgz",
"integrity": "sha512-eZeOMzPmT3LyEryS8GlUJ69NDcWEYT4JDHEYMAiAtNsN+ftFTSUkEeVFgP1zyebgmZApPc4Gdpo162nPBG2rSw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@chialab/esbuild-plugin-meta-url": "^0.19.1",
"@chialab/esbuild-rna": "^0.19.1",
"@chialab/estransform": "^0.19.1"
},
"engines": {
"node": ">=18"
}
},
"node_modules/@chialab/esbuild-rna": {
"version": "0.19.1",
"resolved": "https://registry.npmjs.org/@chialab/esbuild-rna/-/esbuild-rna-0.19.1.tgz",
"integrity": "sha512-v8dpllvqWmYsAvDkfVRRqz1jwxUZyfLYAR0MgsiifnI+C95OPdV3qhubvwebav8s8YUVz2Jr6J8bWpU+GW7TfA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@chialab/estransform": "^0.19.1",
"@chialab/node-resolve": "^0.19.1"
},
"engines": {
"node": ">=18"
}
},
"node_modules/@chialab/estransform": {
"version": "0.19.1",
"resolved": "https://registry.npmjs.org/@chialab/estransform/-/estransform-0.19.1.tgz",
"integrity": "sha512-Op0TvQxnzdcnBriFUIjgg3V3MpOB9Cfs4S7TvIuypPegFOSvuFAOcPl5V02NJ9dyGoOc8W6ORbSldc5PYKhOCQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@napi-rs/magic-string": "^0.3.4",
"@parcel/source-map": "^2.0.0",
"cjs-module-lexer": "^1.2.2",
"es-module-lexer": "^1.0.0",
"oxc-parser": "^0.8.0"
},
"engines": {
"node": ">=18"
}
},
"node_modules/@chialab/node-resolve": {
"version": "0.19.1",
"resolved": "https://registry.npmjs.org/@chialab/node-resolve/-/node-resolve-0.19.1.tgz",
"integrity": "sha512-J4i4YJNaFuYG6UWpum9y8XfICWsWxoCawy6HQtU2lDqp915oboxXvpZ3lBdA5Llb8VexCKQZYufY8QXPyzU62Q==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=18"
}
},
"node_modules/@codemirror/state": {
"version": "6.5.0",
"resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.5.0.tgz",
@@ -2411,252 +2336,6 @@
"integrity": "sha512-eFrYUPDVHeuwWHluTG1kwNQUEUcFjVKYwPkU8z9DR1JH3AW7JtJsG9cRVGmwz809kKtGfwGJj58juCZxEvnI/g==",
"license": "MIT"
},
"node_modules/@napi-rs/magic-string": {
"version": "0.3.4",
"resolved": "https://registry.npmjs.org/@napi-rs/magic-string/-/magic-string-0.3.4.tgz",
"integrity": "sha512-DEWl/B99RQsyMT3F9bvrXuhL01/eIQp/dtNSE3G1jQ4mTGRcP4iHWxoPZ577WrbjUinrNgvRA5+08g8fkPgimQ==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 10"
},
"optionalDependencies": {
"@napi-rs/magic-string-android-arm-eabi": "0.3.4",
"@napi-rs/magic-string-android-arm64": "0.3.4",
"@napi-rs/magic-string-darwin-arm64": "0.3.4",
"@napi-rs/magic-string-darwin-x64": "0.3.4",
"@napi-rs/magic-string-freebsd-x64": "0.3.4",
"@napi-rs/magic-string-linux-arm-gnueabihf": "0.3.4",
"@napi-rs/magic-string-linux-arm64-gnu": "0.3.4",
"@napi-rs/magic-string-linux-arm64-musl": "0.3.4",
"@napi-rs/magic-string-linux-x64-gnu": "0.3.4",
"@napi-rs/magic-string-linux-x64-musl": "0.3.4",
"@napi-rs/magic-string-win32-arm64-msvc": "0.3.4",
"@napi-rs/magic-string-win32-ia32-msvc": "0.3.4",
"@napi-rs/magic-string-win32-x64-msvc": "0.3.4"
}
},
"node_modules/@napi-rs/magic-string-android-arm-eabi": {
"version": "0.3.4",
"resolved": "https://registry.npmjs.org/@napi-rs/magic-string-android-arm-eabi/-/magic-string-android-arm-eabi-0.3.4.tgz",
"integrity": "sha512-sszAYxqtzzJ4FDerDNHcqL9NhqPhj8W4DNiOanXYy50mA5oojlRtaAFPiB5ZMrWDBM32v5Q30LrmxQ4eTtu2Dg==",
"cpu": [
"arm"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@napi-rs/magic-string-android-arm64": {
"version": "0.3.4",
"resolved": "https://registry.npmjs.org/@napi-rs/magic-string-android-arm64/-/magic-string-android-arm64-0.3.4.tgz",
"integrity": "sha512-jdQ6HuO0X5rkX4MauTcWR4HWdgjakTOmmzqXg8L26+jOHVVG1LZE+Su5qvV4bP8vMb2h+vPE+JsnwqSmWymu3Q==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@napi-rs/magic-string-darwin-arm64": {
"version": "0.3.4",
"resolved": "https://registry.npmjs.org/@napi-rs/magic-string-darwin-arm64/-/magic-string-darwin-arm64-0.3.4.tgz",
"integrity": "sha512-6NmMtvURce9/oq09XBZmuIeI6lPLGtEJ2ZPO/QzL3nLZa6wygiCnO/sFACKYNg5/73ET5HMMTeuogE1JI+r2Lw==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@napi-rs/magic-string-darwin-x64": {
"version": "0.3.4",
"resolved": "https://registry.npmjs.org/@napi-rs/magic-string-darwin-x64/-/magic-string-darwin-x64-0.3.4.tgz",
"integrity": "sha512-f9LmfMiUAKDOtl0meOuLYeVb6OERrgGzrTg1Tn3R3fTAShM2kxRbfAuPE9ljuXxIFzOv/uqRNLSl/LqCJwpREA==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@napi-rs/magic-string-freebsd-x64": {
"version": "0.3.4",
"resolved": "https://registry.npmjs.org/@napi-rs/magic-string-freebsd-x64/-/magic-string-freebsd-x64-0.3.4.tgz",
"integrity": "sha512-rqduQ4odiDK4QdM45xHWRTU4wtFIfpp8g8QGpz+3qqg7ivldDqbbNOrBaf6Oeu77uuEvWggnkyuChotfKgJdJQ==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"freebsd"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@napi-rs/magic-string-linux-arm-gnueabihf": {
"version": "0.3.4",
"resolved": "https://registry.npmjs.org/@napi-rs/magic-string-linux-arm-gnueabihf/-/magic-string-linux-arm-gnueabihf-0.3.4.tgz",
"integrity": "sha512-pVaJEdEpiPqIfq3M4+yMAATS7Z9muDcWYn8H7GFH1ygh8GwgLgKfy/n/lG2M6zp18Mwd0x7E2E/qg9GgCyUzoQ==",
"cpu": [
"arm"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@napi-rs/magic-string-linux-arm64-gnu": {
"version": "0.3.4",
"resolved": "https://registry.npmjs.org/@napi-rs/magic-string-linux-arm64-gnu/-/magic-string-linux-arm64-gnu-0.3.4.tgz",
"integrity": "sha512-9FwoAih/0tzEZx0BjYYIxWkSRMjonIn91RFM3q3MBs/evmThXUYXUqLNa1PPIkK1JoksswtDi48qWWLt8nGflQ==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@napi-rs/magic-string-linux-arm64-musl": {
"version": "0.3.4",
"resolved": "https://registry.npmjs.org/@napi-rs/magic-string-linux-arm64-musl/-/magic-string-linux-arm64-musl-0.3.4.tgz",
"integrity": "sha512-wCR7R+WPOcAKmVQc1s6h6HwfwW1vL9pM8BjUY9Ljkdb8wt1LmZEmV2Sgfc1SfbRQzbyl+pKeufP6adRRQVzYDA==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@napi-rs/magic-string-linux-x64-gnu": {
"version": "0.3.4",
"resolved": "https://registry.npmjs.org/@napi-rs/magic-string-linux-x64-gnu/-/magic-string-linux-x64-gnu-0.3.4.tgz",
"integrity": "sha512-sbxFDpYnt5WFbxQ1xozwOvh5A7IftqSI0WnE9O7KsQIOi0ej2dvFbfOW4tmFkvH/YP8KJELo5AhP2+kEq1DpYA==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@napi-rs/magic-string-linux-x64-musl": {
"version": "0.3.4",
"resolved": "https://registry.npmjs.org/@napi-rs/magic-string-linux-x64-musl/-/magic-string-linux-x64-musl-0.3.4.tgz",
"integrity": "sha512-jN4h/7e2Ul8v3UK5IZu38NXLMdzVWhY4uEDlnwuUAhwRh26wBQ1/pLD97Uy/Z3dFNBQPcsv60XS9fOM1YDNT6w==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@napi-rs/magic-string-win32-arm64-msvc": {
"version": "0.3.4",
"resolved": "https://registry.npmjs.org/@napi-rs/magic-string-win32-arm64-msvc/-/magic-string-win32-arm64-msvc-0.3.4.tgz",
"integrity": "sha512-gMUyTRHLWpzX2ntJFCbW2Gnla9Y/WUmbkZuW5SBAo/Jo8QojHn76Y4PNgnoXdzcsV9b/45RBxurYKAfFg9WTyg==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@napi-rs/magic-string-win32-ia32-msvc": {
"version": "0.3.4",
"resolved": "https://registry.npmjs.org/@napi-rs/magic-string-win32-ia32-msvc/-/magic-string-win32-ia32-msvc-0.3.4.tgz",
"integrity": "sha512-QIMauMOvEHgL00K9np/c9CT/CRtLOz3mRTQqcZ9XGzSoAMrpxH71KSpDJrKl7h7Ro6TZ+hJ0C3T+JVuTCZNv4A==",
"cpu": [
"ia32"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@napi-rs/magic-string-win32-x64-msvc": {
"version": "0.3.4",
"resolved": "https://registry.npmjs.org/@napi-rs/magic-string-win32-x64-msvc/-/magic-string-win32-x64-msvc-0.3.4.tgz",
"integrity": "sha512-V8FMSf828MzOI3P6/765MR7zHU6CUZqiyPhmAnwYoKFNxfv7oCviN/G6NcENeCdcYOvNgh5fYzaNLB96ndId5A==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@noble/curves": {
"version": "1.9.7",
"resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.9.7.tgz",
@@ -2743,131 +2422,6 @@
"node": ">= 8"
}
},
"node_modules/@oxc-parser/binding-darwin-arm64": {
"version": "0.8.0",
"resolved": "https://registry.npmjs.org/@oxc-parser/binding-darwin-arm64/-/binding-darwin-arm64-0.8.0.tgz",
"integrity": "sha512-3Dws5Wzj9efojjqvhS4ZF+Abh0EoiI5ciOE2kdLifMzSg4fnmYAIOktoUnPEo87TNIb4SiFJ5JgPBgEyq42Eow==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
]
},
"node_modules/@oxc-parser/binding-darwin-x64": {
"version": "0.8.0",
"resolved": "https://registry.npmjs.org/@oxc-parser/binding-darwin-x64/-/binding-darwin-x64-0.8.0.tgz",
"integrity": "sha512-DAUJ/mfq0Jn2VDYn69bhHTsIWj+aZ/viamexFwaLL7ntkIFmGpzAJZUlWofpY1IRJynKWW+P5AOLYXMllw4qUw==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
]
},
"node_modules/@oxc-parser/binding-linux-arm64-gnu": {
"version": "0.8.0",
"resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-0.8.0.tgz",
"integrity": "sha512-ZHQVey/O4K3zTIKtpfsbtJIE8MPTRHRxgY3dejaoeFQGf9C3HasgF132Yp4zN/jOUx+x8czKPVa/Af40ViyhGQ==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
]
},
"node_modules/@oxc-parser/binding-linux-arm64-musl": {
"version": "0.8.0",
"resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm64-musl/-/binding-linux-arm64-musl-0.8.0.tgz",
"integrity": "sha512-Diw+Tnf5v+zAYXzDoSKCZsMaroU6GoqZMS7smfDtFnZYTHWZrsTmPBLUQe7AFiG7O7tkhsCdcWjOYgbVkrSVOA==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
]
},
"node_modules/@oxc-parser/binding-linux-x64-gnu": {
"version": "0.8.0",
"resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-x64-gnu/-/binding-linux-x64-gnu-0.8.0.tgz",
"integrity": "sha512-WloqcRrtQUVEP/Sy8ZeEgF0HgBKQjOv3zLFZqbC5ipkerKriGcVbsq3fOIMOi/55AM6/UhIAjeZGnoeco72JjQ==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
]
},
"node_modules/@oxc-parser/binding-linux-x64-musl": {
"version": "0.8.0",
"resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-x64-musl/-/binding-linux-x64-musl-0.8.0.tgz",
"integrity": "sha512-2j7BD9szwSXTvSj0Q8VE98UHGYvrgZzdLy4EyB0FilhQnopEfz+YV674rWGY2Il1VYxHJwGctrTJHvARolu37g==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
]
},
"node_modules/@oxc-parser/binding-win32-arm64-msvc": {
"version": "0.8.0",
"resolved": "https://registry.npmjs.org/@oxc-parser/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-0.8.0.tgz",
"integrity": "sha512-mcomr1og17yCmnwn8Q7CRzrH9Va0HccWe4Ld3/u/elBsw0SEzYGVvECRzCyRglYAbKTtusz7as9Jee0RiMOMmg==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
]
},
"node_modules/@oxc-parser/binding-win32-x64-msvc": {
"version": "0.8.0",
"resolved": "https://registry.npmjs.org/@oxc-parser/binding-win32-x64-msvc/-/binding-win32-x64-msvc-0.8.0.tgz",
"integrity": "sha512-nIBkc1KZOVYUaHT3+U+gM354P3byMAIXMvlmLMbs0kWVRcI4vrzL8qwWpC6QdBQxWKZGqPEqGolv8H4dDYA9nQ==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
]
},
"node_modules/@parcel/source-map": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/@parcel/source-map/-/source-map-2.1.1.tgz",
"integrity": "sha512-Ejx1P/mj+kMjQb8/y5XxDUn4reGdr+WyKYloBljpppUy8gs42T+BNoEOuRYqDVdgPc6NxduzIDoJS9pOFfV5Ew==",
"dev": true,
"license": "MIT",
"dependencies": {
"detect-libc": "^1.0.3"
},
"engines": {
"node": "^12.18.3 || >=14"
}
},
"node_modules/@peculiar/asn1-cms": {
"version": "2.6.1",
"resolved": "https://registry.npmjs.org/@peculiar/asn1-cms/-/asn1-cms-2.6.1.tgz",
@@ -4749,52 +4303,12 @@
"@types/node": "*"
}
},
"node_modules/@typescript-eslint/eslint-plugin": {
"version": "8.56.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.56.1.tgz",
"integrity": "sha512-Jz9ZztpB37dNC+HU2HI28Bs9QXpzCz+y/twHOwhyrIRdbuVDxSytJNDl6z/aAKlaRIwC7y8wJdkBv7FxYGgi0A==",
"dev": true,
"license": "MIT",
"dependencies": {
"@eslint-community/regexpp": "^4.12.2",
"@typescript-eslint/scope-manager": "8.56.1",
"@typescript-eslint/type-utils": "8.56.1",
"@typescript-eslint/utils": "8.56.1",
"@typescript-eslint/visitor-keys": "8.56.1",
"ignore": "^7.0.5",
"natural-compare": "^1.4.0",
"ts-api-utils": "^2.4.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
"@typescript-eslint/parser": "^8.56.1",
"eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
"typescript": ">=4.8.4 <6.0.0"
}
},
"node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": {
"version": "7.0.5",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz",
"integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 4"
}
},
"node_modules/@typescript-eslint/parser": {
"version": "8.56.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.56.1.tgz",
"integrity": "sha512-klQbnPAAiGYFyI02+znpBRLyjL4/BrBd0nyWkdC0s/6xFLkXYQ8OoRrSkqacS1ddVxf/LDyODIKbQ5TgKAf/Fg==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@typescript-eslint/scope-manager": "8.56.1",
"@typescript-eslint/types": "8.56.1",
@@ -4871,31 +4385,6 @@
"typescript": ">=4.8.4 <6.0.0"
}
},
"node_modules/@typescript-eslint/type-utils": {
"version": "8.56.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.56.1.tgz",
"integrity": "sha512-yB/7dxi7MgTtGhZdaHCemf7PuwrHMenHjmzgUW1aJpO+bBU43OycnM3Wn+DdvDO/8zzA9HlhaJ0AUGuvri4oGg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/types": "8.56.1",
"@typescript-eslint/typescript-estree": "8.56.1",
"@typescript-eslint/utils": "8.56.1",
"debug": "^4.4.3",
"ts-api-utils": "^2.4.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
"eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
"typescript": ">=4.8.4 <6.0.0"
}
},
"node_modules/@typescript-eslint/types": {
"version": "8.56.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.56.1.tgz",
@@ -6207,6 +5696,7 @@
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
"integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
"dev": true,
"license": "MIT",
"dependencies": {
"fill-range": "^7.1.1"
@@ -6476,13 +5966,6 @@
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/cjs-module-lexer": {
"version": "1.4.3",
"resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.4.3.tgz",
"integrity": "sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==",
"dev": true,
"license": "MIT"
},
"node_modules/cliui": {
"version": "8.0.1",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
@@ -6558,15 +6041,6 @@
"dev": true,
"license": "MIT"
},
"node_modules/commander": {
"version": "14.0.3",
"resolved": "https://registry.npmjs.org/commander/-/commander-14.0.3.tgz",
"integrity": "sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==",
"license": "MIT",
"engines": {
"node": ">=20"
}
},
"node_modules/commondir": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz",
@@ -7001,19 +6475,6 @@
"node": ">= 14"
}
},
"node_modules/detect-libc": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz",
"integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==",
"dev": true,
"license": "Apache-2.0",
"bin": {
"detect-libc": "bin/detect-libc.js"
},
"engines": {
"node": ">=0.10"
}
},
"node_modules/diff-match-patch": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/diff-match-patch/-/diff-match-patch-1.0.5.tgz",
@@ -7497,13 +6958,6 @@
"node": ">= 0.4"
}
},
"node_modules/es-module-lexer": {
"version": "1.7.0",
"resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz",
"integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==",
"dev": true,
"license": "MIT"
},
"node_modules/es-object-atoms": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
@@ -8788,6 +8242,7 @@
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
"integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
"dev": true,
"license": "MIT",
"dependencies": {
"to-regex-range": "^5.0.1"
@@ -9890,6 +9345,7 @@
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=0.12.0"
@@ -10802,19 +10258,6 @@
"dev": true,
"license": "MIT"
},
"node_modules/lilconfig": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz",
"integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=14"
},
"funding": {
"url": "https://github.com/sponsors/antonk52"
}
},
"node_modules/linkify-it": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz",
@@ -11075,6 +10518,7 @@
"version": "4.0.8",
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
"integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==",
"dev": true,
"license": "MIT",
"dependencies": {
"braces": "^3.0.3",
@@ -11084,29 +10528,6 @@
"node": ">=8.6"
}
},
"node_modules/mime-db": {
"version": "1.52.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/mime-types": {
"version": "2.1.35",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
"dev": true,
"license": "MIT",
"dependencies": {
"mime-db": "1.52.0"
},
"engines": {
"node": ">= 0.6"
}
},
"node_modules/minimatch": {
"version": "10.2.5",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz",
@@ -11555,26 +10976,6 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/oxc-parser": {
"version": "0.8.0",
"resolved": "https://registry.npmjs.org/oxc-parser/-/oxc-parser-0.8.0.tgz",
"integrity": "sha512-ObPeMkbDX7igb7NyyAC8CbVC3fY+YmlMsxsRQ2oyFBkpQtI5tjoyqSDKbS9A9EcJvt2q89C4UoC+HjVBdLYYJg==",
"dev": true,
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/Boshen"
},
"optionalDependencies": {
"@oxc-parser/binding-darwin-arm64": "0.8.0",
"@oxc-parser/binding-darwin-x64": "0.8.0",
"@oxc-parser/binding-linux-arm64-gnu": "0.8.0",
"@oxc-parser/binding-linux-arm64-musl": "0.8.0",
"@oxc-parser/binding-linux-x64-gnu": "0.8.0",
"@oxc-parser/binding-linux-x64-musl": "0.8.0",
"@oxc-parser/binding-win32-arm64-msvc": "0.8.0",
"@oxc-parser/binding-win32-x64-msvc": "0.8.0"
}
},
"node_modules/p-cancelable": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz",
@@ -11844,6 +11245,7 @@
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz",
"integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=8.6"
@@ -12004,50 +11406,6 @@
"node": "^10 || ^12 || >=14"
}
},
"node_modules/postcss-load-config": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-6.0.1.tgz",
"integrity": "sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==",
"dev": true,
"funding": [
{
"type": "opencollective",
"url": "https://opencollective.com/postcss/"
},
{
"type": "github",
"url": "https://github.com/sponsors/ai"
}
],
"license": "MIT",
"peer": true,
"dependencies": {
"lilconfig": "^3.1.1"
},
"engines": {
"node": ">= 18"
},
"peerDependencies": {
"jiti": ">=1.21.0",
"postcss": ">=8.0.9",
"tsx": "^4.8.1",
"yaml": "^2.4.2"
},
"peerDependenciesMeta": {
"jiti": {
"optional": true
},
"postcss": {
"optional": true
},
"tsx": {
"optional": true
},
"yaml": {
"optional": true
}
}
},
"node_modules/postcss-safe-parser": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/postcss-safe-parser/-/postcss-safe-parser-7.0.1.tgz",
@@ -13900,9 +13258,9 @@
}
},
"node_modules/svelte-eslint-parser": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/svelte-eslint-parser/-/svelte-eslint-parser-1.6.0.tgz",
"integrity": "sha512-qoB1ehychT6OxEtQAqc/guSqLS20SlA53Uijl7x375s8nlUT0lb9ol/gzraEEatQwsyPTJo87s2CmKL9Xab+Uw==",
"version": "1.8.0",
"resolved": "https://registry.npmjs.org/svelte-eslint-parser/-/svelte-eslint-parser-1.8.0.tgz",
"integrity": "sha512-mikR1qwIVy3t5WthUoAXkMwxkXvabZP9FJgdx35Ei7EbGWmctva1Pih16Koeor/bdNNq8NXHlwKGS6NkYTawLg==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -13916,7 +13274,7 @@
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0",
"pnpm": "10.30.3"
"pnpm": "10.34.1"
},
"funding": {
"url": "https://github.com/sponsors/ota-meshi"
@@ -14200,6 +13558,7 @@
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"is-number": "^7.0.0"
@@ -14312,7 +13671,6 @@
"integrity": "sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"esbuild": "~0.27.0",
"get-tsconfig": "^4.7.5"

View File

@@ -61,7 +61,6 @@
"author": "vorotamoroz",
"license": "MIT",
"devDependencies": {
"@chialab/esbuild-plugin-worker": "^0.19.0",
"@eslint/js": "^9.39.3",
"@sveltejs/vite-plugin-svelte": "^6.2.4",
"@tsconfig/svelte": "^5.0.8",
@@ -78,7 +77,6 @@
"@types/pouchdb-mapreduce": "^6.1.10",
"@types/pouchdb-replication": "^6.4.7",
"@types/transform-pouch": "^1.0.6",
"@typescript-eslint/eslint-plugin": "8.56.1",
"@typescript-eslint/parser": "8.56.1",
"@vitest/browser": "^4.1.8",
"@vitest/browser-playwright": "^4.1.8",
@@ -94,7 +92,6 @@
"globals": "^14.0.0",
"playwright": "^1.58.2",
"postcss": "^8.5.6",
"postcss-load-config": "^6.0.1",
"pouchdb-adapter-http": "^9.0.0",
"pouchdb-adapter-idb": "^9.0.0",
"pouchdb-adapter-indexeddb": "^9.0.0",
@@ -114,14 +111,14 @@
"terser": "^5.39.0",
"tinyglobby": "^0.2.15",
"transform-pouch": "^2.0.0",
"tslib": "^2.8.1",
"tsx": "^4.21.0",
"typescript": "5.9.3",
"vite": "^7.3.1",
"vite-plugin-istanbul": "^8.0.0",
"vitest": "^4.1.8",
"webdriverio": "^9.27.0",
"yaml": "^2.8.2"
"yaml": "^2.8.2",
"svelte-eslint-parser": "^1.8.0"
},
"dependencies": {
"@aws-sdk/client-s3": "^3.808.0",
@@ -130,15 +127,14 @@
"@smithy/middleware-apply-body-checksum": "^4.3.9",
"@smithy/protocol-http": "^5.3.9",
"@smithy/querystring-builder": "^4.2.9",
"@smithy/types": "^4.14.3",
"@smithy/util-retry": "^4.4.5",
"@trystero-p2p/nostr": "^0.24.0",
"chokidar": "^4.0.0",
"commander": "^14.0.3",
"diff-match-patch": "^1.0.5",
"fflate": "^0.8.2",
"idb": "^8.0.3",
"markdown-it": "^14.1.1",
"micromatch": "^4.0.0",
"minimatch": "^10.2.2",
"obsidian": "^1.12.3",
"octagonal-wheels": "^0.1.46",

View File

@@ -120,6 +120,7 @@ export class LiveSyncBaseCore<
* @param constructor
* @returns
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
getModule<T extends AbstractModule>(constructor: new (...args: any[]) => T): T {
for (const module of this.modules) {
if (module.constructor === constructor) return module as T;

View File

@@ -43,6 +43,15 @@ cli_test_init_settings_file "$SETTINGS_FILE"
# isConfigured=true is required for mirror (canProceedScan checks this)
cli_test_mark_settings_configured "$SETTINGS_FILE"
# Enable writeDocumentsIfConflicted to resolve unsynced conflicts during mirror
node -e '
const fs = require("fs");
const file = process.argv[1];
const data = JSON.parse(fs.readFileSync(file, "utf-8"));
data.writeDocumentsIfConflicted = true;
fs.writeFileSync(file, JSON.stringify(data, null, 2));
' "$SETTINGS_FILE"
# Preparation: Sync settings and files logic
DB_SETTINGS="$DB_DIR/settings.json"
cp "$SETTINGS_FILE" "$DB_SETTINGS"

View File

@@ -109,7 +109,11 @@ Deno.test("daemon: ignore rules behaviour", async (t) => {
await runCliOrFail(vaultDir, "--settings", settingsFile, "mirror");
const dbList = await runCliOrFail(vaultDir, "--settings", settingsFile, "ls");
assertNotContains(dbList, "debug.log", "debug.log (ignored via .gitignore import) was unexpectedly synced to database");
assertNotContains(
dbList,
"debug.log",
"debug.log (ignored via .gitignore import) was unexpectedly synced to database"
);
assertContains(dbList, "regular.md", "regular.md was not synced normally alongside .gitignore import rules");
console.log("[PASS] Case 3 verified successfully");
});

View File

@@ -46,9 +46,7 @@ Deno.test("decoupled database and vault", async () => {
console.log("[INFO] applying CouchDB environment variables to settings");
await applyCouchdbSettings(settingsFile, uri, user, password, dbname);
} else {
console.warn(
"[WARN] CouchDB environment variables are not fully set. Push and pull operations may fail."
);
console.warn("[WARN] CouchDB environment variables are not fully set. Push and pull operations may fail.");
await markSettingsConfigured(settingsFile);
}
@@ -59,29 +57,11 @@ Deno.test("decoupled database and vault", async () => {
// 1. Test push command with decoupled vault directory
console.log(`[INFO] push with decoupled vault -> ${REMOTE_PATH}`);
await runCliOrFail(
dbDir,
"--vault",
vaultDir,
"--settings",
settingsFile,
"push",
srcFile,
REMOTE_PATH
);
await runCliOrFail(dbDir, "--vault", vaultDir, "--settings", settingsFile, "push", srcFile, REMOTE_PATH);
// 2. Test pull command with decoupled vault directory
console.log(`[INFO] pull with decoupled vault <- ${REMOTE_PATH}`);
await runCliOrFail(
dbDir,
"--vault",
vaultDir,
"--settings",
settingsFile,
"pull",
REMOTE_PATH,
pulledFile
);
await runCliOrFail(dbDir, "--vault", vaultDir, "--settings", settingsFile, "pull", REMOTE_PATH, pulledFile);
const pulled = await Deno.readTextFile(pulledFile);
assertEquals(pulled, content, "push/pull roundtrip with decoupled vault content mismatch");
@@ -93,14 +73,7 @@ Deno.test("decoupled database and vault", async () => {
// 4. Test mirror command with decoupled vault directory
console.log("[INFO] mirror with decoupled vault");
await runCliOrFail(
dbDir,
"--vault",
vaultDir,
"--settings",
settingsFile,
"mirror"
);
await runCliOrFail(dbDir, "--vault", vaultDir, "--settings", settingsFile, "mirror");
const restoredFile = join(vaultDir, REMOTE_PATH);
const restored = await Deno.readTextFile(restoredFile);

View File

@@ -39,6 +39,10 @@ Deno.test("mirror: storage <-> DB synchronisation", async (t) => {
// isConfigured=true is required for canProceedScan in the mirror command.
await markSettingsConfigured(settingsFile);
const data = JSON.parse(await Deno.readTextFile(settingsFile));
data.writeDocumentsIfConflicted = true;
await Deno.writeTextFile(settingsFile, JSON.stringify(data, null, 2));
// Copy settings to the DB directory (separated-path mode)
const dbSettings = workDir.join("db", "settings.json");
await Deno.copyFile(settingsFile, dbSettings);

View File

@@ -14,7 +14,7 @@ import { getOptimalLoopbackIp } from "./helpers/net.ts";
Deno.test("p2p-peers: discovers host through local relay", async () => {
const loopbackIp = await getOptimalLoopbackIp();
const loopbackHost = loopbackIp === "::1" ? "[::1]" : loopbackIp;
const relay = Deno.env.get("RELAY") ?? `ws://${loopbackHost}:4000/`;
const roomId = Deno.env.get("ROOM_ID") ?? `room-${Date.now()}`;
const passphrase = Deno.env.get("PASSPHRASE") ?? "test";

View File

@@ -15,7 +15,7 @@ import { getOptimalLoopbackIp } from "./helpers/net.ts";
Deno.test("p2p-sync: discovers peer and completes sync", async () => {
const loopbackIp = await getOptimalLoopbackIp();
const loopbackHost = loopbackIp === "::1" ? "[::1]" : loopbackIp;
const relay = Deno.env.get("RELAY") ?? `ws://${loopbackHost}:4000/`;
const roomId = Deno.env.get("ROOM_ID") ?? `room-${Date.now()}`;
const passphrase = Deno.env.get("PASSPHRASE") ?? "test";

View File

@@ -15,7 +15,7 @@ import { getOptimalLoopbackIp } from "./helpers/net.ts";
Deno.test("p2p: three nodes detect and resolve conflicts", async () => {
const loopbackIp = await getOptimalLoopbackIp();
const loopbackHost = loopbackIp === "::1" ? "[::1]" : loopbackIp;
const relay = Deno.env.get("RELAY") ?? `ws://${loopbackHost}:4000/`;
const roomId = Deno.env.get("ROOM_ID") ?? `room-${Date.now()}`;
const passphrase = Deno.env.get("PASSPHRASE") ?? "test";

View File

@@ -61,11 +61,7 @@ Deno.test("remote management commands", async () => {
// 1. remote-status outputs valid JSON with CouchDB details
console.log("[CASE] remote-status outputs valid JSON with CouchDB details");
const statusOutput = await runCliCombinedOrFail(vaultDir, "--settings", settingsFile, "remote-status");
assertContains(
statusOutput,
`"db_name": "${dbname}"`,
"remote-status should return JSON containing db_name"
);
assertContains(statusOutput, `"db_name": "${dbname}"`, "remote-status should return JSON containing db_name");
console.log("[PASS] remote-status verified");
// 2. lock-remote locks and verifies state

View File

@@ -2,7 +2,7 @@ import { deleteDB, type IDBPDatabase, openDB } from "idb";
import type { KeyValueDatabase } from "../lib/src/interfaces/KeyValueDatabase.ts";
import { serialized } from "octagonal-wheels/concurrency/lock";
import { Logger } from "octagonal-wheels/common/logger";
const databaseCache: { [key: string]: IDBPDatabase<any> } = {};
const databaseCache: { [key: string]: IDBPDatabase<unknown> } = {};
export { OpenKeyValueDatabase } from "./KeyValueDBv2.ts";
export const _OpenKeyValueDatabase = async (dbKey: string): Promise<KeyValueDatabase> => {
@@ -11,7 +11,7 @@ export const _OpenKeyValueDatabase = async (dbKey: string): Promise<KeyValueData
delete databaseCache[dbKey];
}
const storeKey = dbKey;
let db: IDBPDatabase<any> | null = null;
let db: IDBPDatabase<unknown> | null = null;
const _openDB = () => {
return serialized("keyvaluedb-" + dbKey, async () => {
const dbInstance = await openDB(dbKey, 1, {

View File

@@ -28,7 +28,7 @@ export async function OpenKeyValueDatabase(dbKey: string): Promise<KeyValueDatab
}
export class IDBKeyValueDatabase implements KeyValueDatabase {
protected _dbPromise: Promise<IDBPDatabase<any>> | null = null;
protected _dbPromise: Promise<IDBPDatabase<unknown>> | null = null;
protected dbKey: string;
protected storeKey: string;
protected _isDestroyed: boolean = false;
@@ -104,7 +104,7 @@ export class IDBKeyValueDatabase implements KeyValueDatabase {
this.destroyedPromise = Promise.resolve();
}
}
get DB(): Promise<IDBPDatabase<any>> {
get DB(): Promise<IDBPDatabase<unknown>> {
if (this._isDestroyed) {
return Promise.reject(new Error("Database is destroyed"));
}

View File

@@ -4,10 +4,10 @@ import { eventHub, EVENT_PLUGIN_UNLOADED } from "./events";
import type { NecessaryServices } from "@lib/interfaces/ServiceModule";
type PeriodicProcessorHost = NecessaryServices<"API" | "control", never>;
export class PeriodicProcessor {
_process: () => Promise<any>;
_process: () => Promise<unknown>;
_timer?: number = undefined;
_core: PeriodicProcessorHost;
constructor(core: PeriodicProcessorHost, process: () => Promise<any>) {
constructor(core: PeriodicProcessorHost, process: () => Promise<unknown>) {
// this._plugin = plugin;
this._core = core;
this._process = process;

View File

@@ -9,16 +9,15 @@ import { isCloudantURI } from "@lib/pouchdb/utils_couchdb";
import { compatGlobal } from "@lib/common/coreEnvFunctions";
import { manifestVersion, packageVersion } from "@lib/common/coreEnvVars";
import type { LiveSyncBaseCore } from "@/LiveSyncBaseCore";
function redactObject(obj: Record<string, any>, dotted: string, redactedValue = "REDACTED") {
function redactObject(obj: Record<string, unknown>, dotted: string, redactedValue = "REDACTED") {
const keys = dotted.split(".");
let current = obj;
for (let i = 0; i < keys.length - 1; i++) {
const key = keys[i];
if (!(key in current)) {
current[key] = {} as Record<string, any>;
current[key] = {};
}
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
current = current[key];
current = current[key] as Record<string, unknown>;
}
const lastKey = keys[keys.length - 1];
if (lastKey in current) {
@@ -27,7 +26,7 @@ function redactObject(obj: Record<string, any>, dotted: string, redactedValue =
return obj;
}
export async function generateReport(settings: ObsidianLiveSyncSettings, core: LiveSyncBaseCore) {
let responseConfig: Record<string, any> = {};
let responseConfig: Record<string, unknown> = {};
const REDACTED = "𝑅𝐸𝐷𝐴𝐶𝑇𝐸𝐷";
if (settings.remoteType == REMOTE_COUCHDB) {
try {
@@ -42,7 +41,7 @@ export async function generateReport(settings: ObsidianLiveSyncSettings, core: L
undefined,
customHeaders
);
responseConfig = r.json as Record<string, any>;
responseConfig = r.json as Record<string, unknown>;
redactObject(responseConfig, "couch_httpd_auth.secret");
redactObject(responseConfig, "couch_httpd_auth.authentication_db");
redactObject(responseConfig, "couch_httpd_auth.authentication_redirect");

View File

@@ -72,7 +72,7 @@ import {
} from "@lib/common/typeUtils.ts";
export { isInternalFile, getPathFromUXFileInfo, getStoragePathFromUXFileInfo, getDatabasePathFromUXFileInfo };
const memos: { [key: string]: any } = {};
const memos: Record<string, unknown> = {};
export function memoObject<T>(key: string, obj: T): T {
memos[key] = obj;
return memos[key] as T;
@@ -87,7 +87,7 @@ export async function memoIfNotExist<T>(key: string, func: () => T | Promise<T>)
}
export function retrieveMemoObject<T>(key: string): T | false {
if (key in memos) {
return memos[key];
return memos[key] as T;
} else {
return false;
}
@@ -128,7 +128,7 @@ export const _requestToCouchDBFetch = async (
username: string,
password: string,
path?: string,
body?: any,
body?: unknown,
method?: string
) => {
const utf8str = String.fromCharCode.apply(null, [...writeString(`${username}:${password}`)]);
@@ -154,7 +154,7 @@ export const _requestToCouchDB = async (
credentials: CouchDBCredentials,
origin: string,
path?: string,
body?: any,
body?: unknown,
method?: string,
customHeaders?: Record<string, string>
) => {
@@ -263,27 +263,27 @@ export function compareFileFreshness(
const _cached = new Map<
string,
{
value: any;
context: Map<string, any>;
value: unknown;
context: Map<string, unknown>;
}
>();
export type MemoOption = {
key: string;
forceUpdate?: boolean;
validator?: (context: Map<string, any>) => boolean;
validator?: (context: Map<string, unknown>) => boolean;
};
export function useMemo<T>(
{ key, forceUpdate, validator }: MemoOption,
updateFunc: (context: Map<string, any>, prev: T) => T
updateFunc: (context: Map<string, unknown>, prev: T) => T
): T {
const cached = _cached.get(key);
const context = cached?.context || new Map<string, any>();
const context = cached?.context || new Map<string, unknown>();
if (cached && !forceUpdate && (!validator || (validator && !validator(context)))) {
return cached.value;
return cached.value as T;
}
const value = updateFunc(context, cached?.value);
const value = updateFunc(context, cached?.value as T);
if (value !== cached?.value) {
_cached.set(key, { value, context });
}
@@ -294,7 +294,7 @@ export function useMemo<T>(
const _staticObj = new Map<
string,
{
value: any;
value: unknown;
}
>();
@@ -306,7 +306,7 @@ export function useStatic<T>(key: string, initial?: T) {
// }
const obj = _staticObj.get(key);
if (obj !== undefined) {
return obj;
return obj as { value: T };
} else {
// let buf = initial;
const obj = {
@@ -390,7 +390,7 @@ export async function autosaveCache<K, V>(db: KeyValueDatabase, mapKey: string):
};
}
export function onlyInNTimes(n: number, proc: (progress: number) => any) {
export function onlyInNTimes(n: number, proc: (progress: number) => unknown) {
let counter = 0;
return function () {
if (counter++ % n == 0) {

View File

@@ -1100,10 +1100,16 @@ export class ConfigSync extends LiveSyncCommands {
await delay(100);
this._log(`Config ${data.displayName || data.name} has been applied`, LOG_LEVEL_NOTICE);
if (data.category == "PLUGIN_DATA" || data.category == "PLUGIN_MAIN") {
//@ts-ignore
const manifests = Object.values(this.app.plugins.manifests) as any as PluginManifest[];
//@ts-ignore
const enabledPlugins = this.app.plugins.enabledPlugins as Set<string>;
const appWithPlugins = this.app as unknown as {
plugins: {
manifests: Record<string, PluginManifest>;
enabledPlugins: Set<string>;
unloadPlugin(id: string): Promise<void>;
loadPlugin(id: string): Promise<void>;
};
};
const manifests = Object.values(appWithPlugins.plugins.manifests);
const enabledPlugins = appWithPlugins.plugins.enabledPlugins;
const pluginManifest = manifests.find(
(manifest) => enabledPlugins.has(manifest.id) && manifest.dir == `${baseDir}/plugins/${data.name}`
);
@@ -1113,10 +1119,8 @@ export class ConfigSync extends LiveSyncCommands {
LOG_LEVEL_NOTICE,
"plugin-reload-" + pluginManifest.id
);
// @ts-ignore
await this.app.plugins.unloadPlugin(pluginManifest.id);
// @ts-ignore
await this.app.plugins.loadPlugin(pluginManifest.id);
await appWithPlugins.plugins.unloadPlugin(pluginManifest.id);
await appWithPlugins.plugins.loadPlugin(pluginManifest.id);
this._log(
`Plugin reloaded: ${pluginManifest.name}`,
LOG_LEVEL_NOTICE,

View File

@@ -1224,10 +1224,16 @@ Offline Changed files: ${files.length}`;
const updatedFolders = [...this.queuedNotificationFiles];
this.queuedNotificationFiles.clear();
try {
//@ts-ignore
const manifests = Object.values(this.app.plugins.manifests) as any as PluginManifest[];
//@ts-ignore
const enabledPlugins = this.app.plugins.enabledPlugins as Set<string>;
const appWithPlugins = this.app as unknown as {
plugins: {
manifests: Record<string, PluginManifest>;
enabledPlugins: Set<string>;
unloadPlugin(id: string): Promise<void>;
loadPlugin(id: string): Promise<void>;
};
};
const manifests = Object.values(appWithPlugins.plugins.manifests);
const enabledPlugins = appWithPlugins.plugins.enabledPlugins;
const enabledPluginManifests = manifests.filter((e) => enabledPlugins.has(e.id));
const modifiedManifests = enabledPluginManifests.filter((e) => updatedFolders.indexOf(e?.dir ?? "") >= 0);
for (const manifest of modifiedManifests) {
@@ -1246,10 +1252,8 @@ Offline Changed files: ${files.length}`;
LOG_LEVEL_NOTICE,
"plugin-reload-" + updatePluginId
);
// @ts-ignore
await this.app.plugins.unloadPlugin(updatePluginId);
// @ts-ignore
await this.app.plugins.loadPlugin(updatePluginId);
await appWithPlugins.plugins.unloadPlugin(updatePluginId);
await appWithPlugins.plugins.loadPlugin(updatePluginId);
this._log(
`Plugin reloaded: ${updatePluginName}`,
LOG_LEVEL_NOTICE,

View File

@@ -67,25 +67,25 @@ export abstract class LiveSyncCommands {
_log: ReturnType<typeof createInstanceLogFunction>;
_verbose = (msg: any, key?: string) => {
_verbose = (msg: unknown, key?: string) => {
this._log(msg, LOG_LEVEL_VERBOSE, key);
};
_info = (msg: any, key?: string) => {
_info = (msg: unknown, key?: string) => {
this._log(msg, LOG_LEVEL_INFO, key);
};
_notice = (msg: any, key?: string) => {
_notice = (msg: unknown, key?: string) => {
this._log(msg, LOG_LEVEL_NOTICE, key);
};
_progress = (prefix: string = "", level: LOG_LEVEL = LOG_LEVEL_NOTICE) => {
const key = `keepalive-progress-${noticeIndex++}`;
return {
log: (msg: any) => {
this._log(prefix + msg, level, key);
log: (msg: unknown) => {
this._log(prefix + String(msg), level, key);
},
once: (msg: any) => {
this._log(prefix + msg, level);
once: (msg: unknown) => {
this._log(prefix + String(msg), level);
},
done: (msg: string = "Done") => {
this._log(prefix + msg + MARK_DONE, level, key);
@@ -93,7 +93,7 @@ export abstract class LiveSyncCommands {
};
};
_debug = (msg: any, key?: string) => {
_debug = (msg: unknown, key?: string) => {
this._log(msg, LOG_LEVEL_VERBOSE, key);
};

View File

@@ -17,6 +17,7 @@ import { arrayToChunkedArray } from "octagonal-wheels/collection";
import { EVENT_ANALYSE_DB_USAGE, EVENT_REQUEST_PERFORM_GC_V3, eventHub } from "@/common/events";
import type { LiveSyncCouchDBReplicator } from "@/lib/src/replication/couchdb/LiveSyncReplicator";
import { delay } from "@/lib/src/common/utils";
import { isNotFoundError } from "@lib/common/utils.doc.ts";
// import { _requestToCouchDB } from "@/common/utils";
const DB_KEY_SEQ = "gc-seq";
const DB_KEY_CHUNK_SET = "chunk-set";
@@ -393,8 +394,8 @@ Note: **Make sure to synchronise all devices before deletion.**
await processDoc(oldDoc, false);
}
}
} catch (ex) {
if ((ex as any)?.status == 404) {
} catch (ex: unknown) {
if (ex && typeof ex === "object" && isNotFoundError(ex)) {
this._log(`No revisions found for ${doc._id}`, LOG_LEVEL_VERBOSE);
} else {
this._log(`Error finding revisions for ${doc._id}`);
@@ -473,15 +474,23 @@ Are you ready to delete unused chunks?`;
keys: [...unusedSet],
include_docs: true,
});
interface PouchDBRow {
id: string;
key: string;
value: { rev: string; deleted?: boolean };
doc?: EntryDoc;
}
for (const chunk of deleteChunks.rows) {
if ((chunk as any)?.value?.deleted) {
chunkSet.delete(chunk.key as DocumentID);
const c = chunk as unknown as PouchDBRow;
if (c.value?.deleted) {
chunkSet.delete(c.key as DocumentID);
}
}
const deleteDocs = deleteChunks.rows
.filter((e) => "doc" in e)
.map((e) => e as unknown as PouchDBRow)
.filter((e) => e.doc != null)
.map((e) => ({
...(e as any).doc!,
...e.doc!,
_deleted: true,
}));
@@ -625,8 +634,19 @@ Success: ${successCount}, Errored: ${errored}`;
}
}
}
interface DatabaseAnalysisResultItem {
title: string;
path: string;
rev: string;
revHash: string;
id?: string;
uniqueChunkCount: number;
sharedChunkCount: number;
uniqueChunkSize: number;
sharedChunkSize: number;
}
// Prepare results
const result = [];
const result: DatabaseAnalysisResultItem[] = [];
// Calculate total size of chunks in the given set.
const getTotalSize = (ids: Set<DocumentID>) => {
return [...ids].reduce((acc, chunkId) => {
@@ -698,7 +718,7 @@ Success: ${successCount}, Errored: ${errored}`;
sharedChunkCount: 0,
uniqueChunkSize: orphanChunkSize,
sharedChunkSize: 0,
} as any);
});
const csvSrc = result.map((e) => {
return [

Submodule src/lib updated: 82e15f2b9d...3cae3909e1

View File

@@ -20,7 +20,7 @@ import { InternalFileToUXFileInfoStub, TFileToUXFileInfoStub } from "@/modules/c
* Obsidian-specific type guard adapter
*/
class ObsidianTypeGuardAdapter implements IStorageEventTypeGuardAdapter<TFile, TFolder> {
isFile(file: any): file is TFile {
isFile(file: unknown): file is TFile {
if (file instanceof TFile) {
return true;
}
@@ -30,7 +30,7 @@ class ObsidianTypeGuardAdapter implements IStorageEventTypeGuardAdapter<TFile, T
return false;
}
isFolder(item: any): item is TFolder {
isFolder(item: unknown): item is TFolder {
if (item instanceof TFolder) {
return true;
}

View File

@@ -61,18 +61,19 @@ export abstract class AbstractModule<
return this.testDone(false);
}
async _test(key: string, process: () => Promise<any>) {
async _test(key: string, process: () => Promise<unknown>) {
this._log(`Testing ${key}`, LOG_LEVEL_VERBOSE);
try {
const ret = await process();
if (ret !== true) {
this.addTestResult(key, false, ret.toString());
return this.testFail(`${key} failed: ${ret}`);
this.addTestResult(key, false, String(ret));
return this.testFail(`${key} failed: ${String(ret)}`);
}
this.addTestResult(key, true, "");
} catch (ex: any) {
this.addTestResult(key, false, "Failed by Exception", ex.toString());
return this.testFail(`${key} failed: ${ex}`);
} catch (ex) {
const exStr = String(ex);
this.addTestResult(key, false, "Failed by Exception", exStr);
return this.testFail(`${key} failed: ${exStr}`);
}
return this.testDone();
}

View File

@@ -7,7 +7,8 @@ export type OverridableFunctionsKeys<T> = {
export type ChainableExecuteFunction<T> = {
[K in keyof T as K extends `$${string}`
? T[K] extends (...args: any) => ChainableFunctionResult
? // eslint-disable-next-line @typescript-eslint/no-explicit-any
T[K] extends (...args: any) => ChainableFunctionResult
? K
: never
: never]: T[K];
@@ -26,26 +27,30 @@ export type ChainableFunctionResultOrAll = Promise<boolean | undefined | string
type AllExecuteFunction<T> = {
[K in keyof T as K extends `$all${string}`
? T[K] extends (...args: any[]) => ChainableFunctionResultOrAll
? // eslint-disable-next-line @typescript-eslint/no-explicit-any
T[K] extends (...args: any[]) => ChainableFunctionResultOrAll
? K
: never
: never]: T[K];
};
type EveryExecuteFunction<T> = {
[K in keyof T as K extends `$every${string}`
? T[K] extends (...args: any[]) => ChainableFunctionResult
? // eslint-disable-next-line @typescript-eslint/no-explicit-any
T[K] extends (...args: any[]) => ChainableFunctionResult
? K
: never
: never]: T[K];
};
type AnyExecuteFunction<T> = {
[K in keyof T as K extends `$any${string}`
? T[K] extends (...args: any[]) => ChainableFunctionResult
? // eslint-disable-next-line @typescript-eslint/no-explicit-any
T[K] extends (...args: any[]) => ChainableFunctionResult
? K
: never
: never]: T[K];
};
type InjectableFunction<T> = {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
[K in keyof T as K extends `$$${string}` ? (T[K] extends (...args: any[]) => any ? K : never) : never]: T[K];
};
export type AllExecuteProps = AllExecuteFunction<LiveSyncCore>;

View File

@@ -21,7 +21,7 @@ import { MARK_LOG_NETWORK_ERROR } from "@lib/services/lib/logUtils";
function isOnlineAndCanReplicate(
errorManager: UnresolvedErrorManager,
host: NecessaryServices<"API", any>,
host: NecessaryServices<"API", never>,
showMessage: boolean
): Promise<boolean> {
const errorMessage = "Network is offline";
@@ -34,7 +34,7 @@ function isOnlineAndCanReplicate(
}
async function canReplicateWithPBKDF2(
errorManager: UnresolvedErrorManager,
host: NecessaryServices<"replicator" | "setting", any>,
host: NecessaryServices<"replicator" | "setting", never>,
showMessage: boolean
): Promise<boolean> {
const currentSettings = host.services.setting.currentSettings();

View File

@@ -18,6 +18,7 @@ import {
type LOG_LEVEL,
} from "@lib/common/logger";
import { fireAndForget, isAnyNote, throttle } from "@lib/common/utils";
import { isNotFoundError } from "@lib/common/utils.doc.ts";
import { Semaphore } from "octagonal-wheels/concurrency/semaphore_v2";
import { serialized } from "octagonal-wheels/concurrency/lock";
import type { ReactiveSource } from "octagonal-wheels/dataobject/reactive_v2";
@@ -39,7 +40,7 @@ export class ReplicateResultProcessor {
private log(message: string, level: LOG_LEVEL = LOG_LEVEL_INFO) {
Logger(`[ReplicateResultProcessor] ${message}`, level);
}
private logError(e: any) {
private logError(e: unknown) {
Logger(e, LOG_LEVEL_VERBOSE);
}
private replicator: ModuleReplicator;
@@ -466,8 +467,8 @@ export class ReplicateResultProcessor {
return false; // This means that the document already processed (While no conflict existed).
}
return true; // This mostly should not happen, but we have to process it just in case.
} catch (e: any) {
if ("status" in e && e.status == 404) {
} catch (e: unknown) {
if (e && typeof e === "object" && isNotFoundError(e)) {
// getRaw failed due to not existing, it may not be happened normally especially on replication.
// If the process caused by some other reason, we **probably** have to process it.
// Note that this is not a common case.

View File

@@ -18,14 +18,14 @@ import type { InjectableServiceHub } from "../../lib/src/services/InjectableServ
import type { LiveSyncCore } from "../../main.ts";
import { REMOTE_P2P } from "@lib/common/models/setting.const.ts";
function valueToString(value: any) {
function valueToString(value: unknown) {
if (typeof value === "boolean") {
return value ? "true" : "false";
}
if (typeof value === "object") {
if (typeof value === "object" && value !== null) {
return JSON.stringify(value);
}
return `${value}`;
return String(value);
}
export class ModuleResolvingMismatchedTweaks extends AbstractModule {

View File

@@ -68,9 +68,13 @@ export class ObsHttpHandler extends FetchHttpHandler {
contentType = transformedHeaders["content-type"];
}
let transformedBody: any = body;
if (ArrayBuffer.isView(body)) {
transformedBody = new Uint8Array(body.buffer).buffer;
let transformedBody: string | ArrayBuffer | undefined = undefined;
if (typeof body === "string" || body instanceof ArrayBuffer) {
transformedBody = body;
} else if (ArrayBuffer.isView(body)) {
transformedBody = body.buffer as ArrayBuffer;
} else if (body != null) {
transformedBody = body as string | ArrayBuffer;
}
const param: RequestUrlParam = {

View File

@@ -36,13 +36,14 @@ export class ModuleObsidianEvents extends AbstractObsidianModule {
this.services.appLifecycle.performRestart();
}
initialCallback: any;
initialCallback: (() => unknown) | undefined;
swapSaveCommand() {
this._log("Modifying callback of the save command", LOG_LEVEL_VERBOSE);
const saveCommandDefinition = (this.app as any).commands?.commands?.["editor:save-file"];
const app = this.app as unknown as { commands?: { commands?: Record<string, { callback?: () => unknown }> } };
const saveCommandDefinition = app.commands?.commands?.["editor:save-file"];
const save = saveCommandDefinition?.callback;
if (typeof save === "function") {
if (typeof save === "function" && saveCommandDefinition) {
this.initialCallback = save;
saveCommandDefinition.callback = () => {
scheduleTask("syncOnEditorSave", 250, () => {

View File

@@ -6,7 +6,7 @@ let plugin: ObsidianLiveSyncPlugin;
export function enableTestFunction(plugin_: ObsidianLiveSyncPlugin) {
plugin = plugin_;
}
export function addDebugFileLog(message: any, stackLog = false) {
export function addDebugFileLog(message: unknown, stackLog = false) {
fireAndForget(
serialized("debug-log", async () => {
const now = new Date();
@@ -16,7 +16,7 @@ export function addDebugFileLog(message: any, stackLog = false) {
// const messageContent = typeof message == "string" ? message : message instanceof Error ? `${message.name}:${message.message}` : JSON.stringify(message, null, 2);
const timestamp = now.toLocaleString();
const timestampEpoch = now;
let out = { timestamp: timestamp, epoch: timestampEpoch } as Record<string, any>;
let out = { timestamp: timestamp, epoch: timestampEpoch } as Record<string, unknown>;
if (message instanceof Error) {
// debugger;
// console.dir(message.stack);

View File

@@ -139,7 +139,7 @@ export class DocumentHistoryModal extends Modal {
this.range.value = `${this.revs_info.length - 1 - rIndex}`;
}
}
const index = this.revs_info.length - 1 - (this.range.value as any) / 1;
const index = this.revs_info.length - 1 - Number(this.range.value);
const rev = this.revs_info[index];
await this.showExactRev(rev.rev);
}
@@ -251,7 +251,7 @@ export class DocumentHistoryModal extends Modal {
}
let rendered = false;
if (this.showDiff) {
const prevRevIdx = this.revs_info.length - 1 - ((this.range.value as any) / 1 - 1);
const prevRevIdx = this.revs_info.length - 1 - (Number(this.range.value) - 1);
if (prevRevIdx >= 0 && prevRevIdx < this.revs_info.length) {
const oldRev = this.revs_info[prevRevIdx].rev;
const w2 = await db.getDBEntry(this.file, { rev: oldRev }, false, false, true);
@@ -550,7 +550,7 @@ export class DocumentHistoryModal extends Modal {
if (this.showDiff) {
checkbox.checked = true;
}
checkbox.addEventListener("input", (evt: any) => {
checkbox.addEventListener("input", (evt) => {
this.showDiff = checkbox.checked;
this.app.saveLocalStorage("ols-history-highlightdiff", this.showDiff == true ? "1" : null);
this.updateDiffNavVisibility();
@@ -565,7 +565,7 @@ export class DocumentHistoryModal extends Modal {
if (this.diffOnly) {
checkbox.checked = true;
}
checkbox.addEventListener("input", (evt: any) => {
checkbox.addEventListener("input", (evt) => {
this.diffOnly = checkbox.checked;
this.app.saveLocalStorage("ols-history-diffonly", this.diffOnly == true ? "1" : null);
void scheduleOnceIfDuplicated("loadRevs", () => this.loadRevs());

View File

@@ -48,7 +48,7 @@ import { generateReport } from "@/common/reportTool.ts";
// DI the log again.
const recentLogEntries = reactiveSource<LogEntry[]>([]);
const globalLogFunction = (message: any, level?: number, key?: string) => {
const globalLogFunction = (message: unknown, level?: number, key?: string) => {
const messageX =
message instanceof Error
? new LiveSyncError("[Error Logged]: " + message.message, { cause: message })
@@ -501,7 +501,7 @@ ${stringifyYaml(info)}
})
);
}
__addLog(message: any, level: LOG_LEVEL = LOG_LEVEL_INFO, key = ""): void {
__addLog(message: unknown, level: LOG_LEVEL = LOG_LEVEL_INFO, key = ""): void {
if (level == LOG_LEVEL_DEBUG && !showDebugLog) {
return;
}

View File

@@ -35,7 +35,7 @@ export class LiveSyncSetting extends Setting {
hasPassword: boolean = false;
invalidateValue?: () => void;
setValue?: (value: any) => void;
setValue?: (value: unknown) => void;
constructor(containerEl: HTMLElement) {
super(containerEl);
LiveSyncSetting.env.settingComponents.push(this);
@@ -102,6 +102,7 @@ export class LiveSyncSetting extends Setting {
}
return conf;
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
autoWireComponent(component: ValueComponent<any>, conf?: ConfigurationItem, opt?: AutoWireOption) {
this.placeHolderBuf = conf?.placeHolder || opt?.placeHolder || "";
if (conf?.level == LEVEL_ADVANCED) {

View File

@@ -287,6 +287,7 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
// UI Element Wrapper -->
settingComponents = [] as Setting[];
controlledElementFunc = [] as UpdateFunction[];
// eslint-disable-next-line @typescript-eslint/no-explicit-any
onSavedHandlers = [] as OnSavedHandler<any>[];
inWizard: boolean = false;

View File

@@ -442,7 +442,9 @@ export function paneHatch(this: ObsidianLiveSyncSettingTab, paneEl: HTMLElement,
...e.doc,
_deleted: true,
}));
const r = await this.core.localDatabase.bulkDocsRaw(newData as any[]);
const r = await this.core.localDatabase.bulkDocsRaw(
newData as Parameters<typeof this.core.localDatabase.bulkDocsRaw>[0]
);
// Do not care about the result.
Logger(
`${r.length} items have been removed, to confirm how many items are left, please perform it again.`,

View File

@@ -7,7 +7,7 @@ import {
type ObsidianLiveSyncSettings,
LOG_LEVEL_VERBOSE,
} from "../../../lib/src/common/types.ts";
import { Menu } from "@/deps.ts";
import { Menu, ButtonComponent } from "@/deps.ts";
import { $msg } from "../../../lib/src/common/i18n.ts";
import { LiveSyncSetting as Setting } from "./LiveSyncSetting.ts";
import type { ObsidianLiveSyncSettingTab } from "./ObsidianLiveSyncSettingTab.ts";
@@ -39,7 +39,7 @@ function getSettingsFromEditingSettings(editingSettings: AllSettings): ObsidianL
const workObj = { ...editingSettings } as ObsidianLiveSyncSettings;
const keys = Object.keys(OnDialogSettingsDefault);
for (const k of keys) {
delete (workObj as any)[k];
delete (workObj as unknown as Record<string, unknown>)[k];
}
return workObj;
}
@@ -72,7 +72,7 @@ function serializeRemoteConfiguration(settings: ObsidianLiveSyncSettings): strin
return ConnectionStringParser.serialize({ type: "couchdb", settings });
}
function setEmojiButton(button: any, emoji: string, tooltip: string) {
function setEmojiButton(button: ButtonComponent, emoji: string, tooltip: string) {
button.setButtonText(emoji);
button.setTooltip(tooltip, { delay: 10, placement: "top" });
// button.buttonEl.addClass("clickable-icon");

View File

@@ -145,8 +145,8 @@ export function paneSetup(
let remoteTroubleShootMDSrc = "";
try {
remoteTroubleShootMDSrc = await request(`${rawRepoURI}${basePath}/${filename}`);
} catch (ex: any) {
remoteTroubleShootMDSrc = `${$msg("obsidianLiveSyncSettingTab.logErrorOccurred")}\n${ex.toString()}`;
} catch (ex) {
remoteTroubleShootMDSrc = `${$msg("obsidianLiveSyncSettingTab.logErrorOccurred")}\n${String(ex)}`;
}
const remoteTroubleShootMD = remoteTroubleShootMDSrc.replace(
/\((.*?(.png)|(.jpg))\)/g,

View File

@@ -5,7 +5,7 @@ import { type Writable, writable, get } from "svelte/store";
* Props passed to Svelte panels, containing a writable port
* to communicate with the panel
*/
export type SveltePanelProps<T = any> = {
export type SveltePanelProps<T = unknown> = {
port: Writable<T | undefined>;
};
@@ -13,7 +13,7 @@ export type SveltePanelProps<T = any> = {
* A class to manage a Svelte panel within Obsidian
* Especially useful for settings panels
*/
export class SveltePanel<T = any> {
export class SveltePanel<T = unknown> {
private _mountedComponent: ReturnType<typeof mount>;
private _componentValue = writable<T | undefined>(undefined);
/**

View File

@@ -13,6 +13,8 @@ import {
} from "../../../lib/src/common/utils";
import { getConfig, type AllSettingItemKey } from "./settingConstants";
import { LOG_LEVEL_NOTICE, Logger } from "octagonal-wheels/common/logger";
import { isNotFoundError } from "@lib/common/utils.doc.ts";
import { LiveSyncError } from "@lib/common/LSError.ts";
/**
* Generates a summary of P2P configuration settings
@@ -95,13 +97,13 @@ export function getSummaryFromPartialSettings(setting: Partial<ObsidianLiveSyncS
export async function copyMigrationDocs(docName: string, dbFrom: PouchDB.Database, dbTo: PouchDB.Database) {
try {
const doc = await dbFrom.get(docName);
delete (doc as any)._rev;
delete (doc as { _rev?: string })._rev;
await dbTo.put(doc);
} catch (e) {
if ((e as any).status === 404) {
} catch (e: unknown) {
if (e && typeof e === "object" && isNotFoundError(e)) {
return;
}
throw e;
throw LiveSyncError.fromError(e);
}
}

View File

@@ -259,8 +259,8 @@ export const checkConfig = async (
addResult($msg("obsidianLiveSyncSettingTab.msgDone"), ["ob-btn-config-head"]);
addResult($msg("obsidianLiveSyncSettingTab.msgConnectionProxyNote"), ["ob-btn-config-info"]);
Logger($msg("obsidianLiveSyncSettingTab.logCheckingConfigDone"), LOG_LEVEL_INFO);
} catch (ex: any) {
if (ex?.status == 401) {
} catch (ex) {
if (ex && typeof ex === "object" && "status" in ex && ex.status == 401) {
isSuccessful = false;
addResult($msg("obsidianLiveSyncSettingTab.errAccessForbidden"));
addResult($msg("obsidianLiveSyncSettingTab.errCannotContinueTest"));

View File

@@ -7,8 +7,14 @@ import { isCloudantURI } from "../../../../lib/src/pouchdb/utils_couchdb";
import { generateCredentialObject } from "../../../../lib/src/replication/httplib";
export type ResultMessage = { message: string; classes: string[] };
export type ResultErrorMessage = { message: string; result: "error"; classes: string[] };
export type ResultOk = { message: string; result: "ok"; value?: any };
export type ResultError = { message: string; result: "error"; value: any; fixMessage: string; fix(): Promise<void> };
export type ResultOk = { message: string; result: "ok"; value?: unknown };
export type ResultError = {
message: string;
result: "error";
value?: string;
fixMessage: string;
fix(): Promise<void>;
};
export type ConfigCheckResult = ResultOk | ResultError | ResultMessage | ResultErrorMessage;
/**
* Compares two version strings to determine if the baseVersion is greater than or equal to the version.
@@ -35,7 +41,7 @@ function isGreaterThanOrEqual(baseVersion: string, version: string) {
* @param value setting value to update
* @returns true if the update was successful, false otherwise
*/
async function updateRemoteSetting(setting: ObsidianLiveSyncSettings, key: string, value: any) {
async function updateRemoteSetting(setting: ObsidianLiveSyncSettings, key: string, value: string) {
const customHeaders = parseHeaderValues(setting.couchDB_CustomHeaders);
const credential = generateCredentialObject(setting);
const res = await requestToCouchDBWithCredentials(
@@ -64,17 +70,17 @@ export const checkConfig = async (editingSettings: ObsidianLiveSyncSettings) =>
const addMessage = (msg: string, classes: string[] = []) => {
result.push({ message: msg, classes });
};
const addSuccess = (msg: string, value?: any) => {
const addSuccess = (msg: string, value?: unknown) => {
result.push({ message: msg, result: "ok", value });
};
const _addError = (message: string, fixMessage: string, fix: () => Promise<void>, value?: any) => {
const _addError = (message: string, fixMessage: string, fix: () => Promise<void>, value?: string) => {
result.push({ message, result: "error", fixMessage, fix, value });
};
const addErrorMessage = (msg: string, classes: string[] = []) => {
result.push({ message: msg, result: "error", classes });
};
const addError = (message: string, fixMessage: string, key: string, expected: any) => {
const addError = (message: string, fixMessage: string, key: string, expected: string) => {
_addError(message, fixMessage, async () => {
await updateRemoteSetting(editingSettings, key, expected);
});
@@ -279,8 +285,8 @@ export const checkConfig = async (editingSettings: ObsidianLiveSyncSettings) =>
addMessage($msg("obsidianLiveSyncSettingTab.msgDone"), ["ob-btn-config-head"]);
addMessage($msg("obsidianLiveSyncSettingTab.msgConnectionProxyNote"), ["ob-btn-config-info"]);
addMessage($msg("obsidianLiveSyncSettingTab.logCheckingConfigDone"));
} catch (ex: any) {
if (ex?.status == 401) {
} catch (ex) {
if (ex && typeof ex === "object" && "status" in ex && ex.status == 401) {
addErrorMessage($msg("obsidianLiveSyncSettingTab.errAccessForbidden"));
addErrorMessage($msg("obsidianLiveSyncSettingTab.errCannotContinueTest"));
addMessage($msg("obsidianLiveSyncSettingTab.logCheckingConfigDone"));

View File

@@ -125,11 +125,11 @@ export class ObsidianAPIService extends InjectableAPIService<ObsidianServiceCont
registerWindow(type: string, factory: ViewCreator): void {
return this.context.plugin.registerView(type, factory);
}
addRibbonIcon(icon: string, title: string, callback: (evt: MouseEvent) => any): HTMLElement {
addRibbonIcon(icon: string, title: string, callback: (evt: MouseEvent) => void): HTMLElement {
return this.context.plugin.addRibbonIcon(icon, title, callback);
}
registerProtocolHandler(action: string, handler: (params: Record<string, string>) => any): void {
registerProtocolHandler(action: string, handler: (params: Record<string, string>) => void): void {
return this.context.plugin.registerObsidianProtocolHandler(action, handler);
}

View File

@@ -50,7 +50,7 @@ export async function deleteFlagFile(host: NecessaryServices<never, "storageAcce
}
const REMOTE_KEEP_CURRENT = "Use active remote";
const REMOTE_CANCEL = "Cancel";
async function askAndActivateRemoteDatabase(host: NecessaryServices<"UI" | "setting", any>, log: LogFunction) {
async function askAndActivateRemoteDatabase(host: NecessaryServices<"UI" | "setting", never>, log: LogFunction) {
const settings = host.services.setting.currentSettings();
if (settings.remoteConfigurations && Object.keys(settings.remoteConfigurations).length > 1) {
const message =
@@ -216,7 +216,7 @@ export function createFetchAllFlagHandler(
* @returns updated configuration if applied, otherwise null.
*/
export async function adjustSettingToRemote(
host: NecessaryServices<"tweakValue" | "UI" | "setting", any>,
host: NecessaryServices<"tweakValue" | "UI" | "setting", never>,
log: LogFunction,
config: ObsidianLiveSyncSettings
) {
@@ -243,7 +243,7 @@ export async function adjustSettingToRemote(
const necessary = extractObject(TweakValuesShouldMatchedTemplate, remoteTweaks);
// Check if any necessary tweak value is different from current config.
const differentItems = Object.entries(necessary).filter(([key, value]) => {
return (config as any)[key] !== value;
return config[key as keyof ObsidianLiveSyncSettings] !== value;
});
if (differentItems.length === 0) {
log("Remote configuration matches local configuration. No changes applied.", LOG_LEVEL_NOTICE);
@@ -261,7 +261,7 @@ export async function adjustSettingToRemote(
config = {
...config,
...Object.fromEntries(differentItems),
} satisfies ObsidianLiveSyncSettings;
};
await host.services.setting.applyExternalSettings(config, true);
log("Remote configuration applied.", LOG_LEVEL_NOTICE);
canProceed = true;
@@ -277,7 +277,7 @@ export async function adjustSettingToRemote(
* @param config current configuration to retrieve remote preferred config
*/
export async function adjustSettingToRemoteIfNeeded(
host: NecessaryServices<"tweakValue" | "UI" | "setting", any>,
host: NecessaryServices<"tweakValue" | "UI" | "setting", never>,
log: LogFunction,
extra: { preventFetchingConfig: boolean },
config: ObsidianLiveSyncSettings
@@ -309,7 +309,7 @@ export async function adjustSettingToRemoteIfNeeded(
* @returns result of the process, or false if error occurs.
*/
export async function processVaultInitialisation(
host: NecessaryServices<"setting", any>,
host: NecessaryServices<"setting", never>,
log: LogFunction,
proc: () => Promise<boolean>,
keepSuspending = false
@@ -341,7 +341,7 @@ export async function processVaultInitialisation(
}
export async function verifyAndUnlockSuspension(
host: NecessaryServices<"setting" | "appLifecycle" | "UI", any>,
host: NecessaryServices<"setting" | "appLifecycle" | "UI", never>,
log: LogFunction
) {
if (!host.services.setting.currentSettings().suspendFileWatching) {

View File

@@ -6,11 +6,11 @@ import { TFile, TFolder } from "obsidian";
*/
export class ObsidianTypeGuardAdapter implements ITypeGuardAdapter<TFile, TFolder> {
isFile(file: any): file is TFile {
isFile(file: unknown): file is TFile {
return file instanceof TFile;
}
isFolder(item: any): item is TFolder {
isFolder(item: unknown): item is TFolder {
return item instanceof TFolder;
}
}

View File

@@ -55,6 +55,7 @@ export class ObsidianVaultAdapter implements IVaultAdapter<TFile> {
return await this.app.vault.trash(file, force);
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
trigger(name: string, ...data: any[]): any {
return this.app.vault.trigger(name, ...data);
}

View File

@@ -3,6 +3,16 @@ Since 19th July, 2025 (beta1 in 0.25.0-beta1, 13th July, 2025)
The head note of 0.25 is now in [updates_old.md](https://github.com/vrtmrz/obsidian-livesync/blob/main/updates_old.md). Because 0.25 got a lot of updates, thankfully, compatibility is kept and we do not need breaking changes! In other words, when get enough stabled. The next version will be v1.0.0. Even though it my hope.
## unreleased
### Fixed (CLI, automated)
- Fixed an issue where the mirror command could fail to apply updates when conflict preservation checks prevented overwriting unsynchronised local changes, even when the `force` parameter or `writeDocumentsIfConflicted` setting was enabled.
### Improved
- (CLI) Ported the remaining bash regression tests (`test-daemon-linux.sh`, `test-decoupled-vault-linux.sh`, and `test-remote-commands-linux.sh`) to Deno for cross-platform validation.
## 0.25.74
8th June, 2026
@@ -22,6 +32,7 @@ I should also consider the version numbering for the CLI...
### Improved
- Added new remote database management commands: `remote-status`, `unlock-remote`, `lock-remote`, and `mark-resolved`.
- --vault option is now available for daemon and mirror commands! (Thank you so much for @starskyzheng)!
- Decoupled the database directory path from the actual vault directory path using the `--vault` (or `-V`) option.
### Fixed (preventive)