From 697ee1855bbe8dbfa4a27aeac0ef8b7e67a81c08 Mon Sep 17 00:00:00 2001 From: vorotamoroz Date: Wed, 31 Jul 2024 02:13:25 +0100 Subject: [PATCH 01/12] Fixed: - Customisation Sync now checks the difference while storing or applying the configuration. - Time difference in the dialogue has been fixed. --- src/features/CmdConfigSync.ts | 60 +++++++++++++++++++++++----- src/ui/components/PluginCombo.svelte | 2 +- 2 files changed, 51 insertions(+), 11 deletions(-) diff --git a/src/features/CmdConfigSync.ts b/src/features/CmdConfigSync.ts index 5ab5c97..13ae532 100644 --- a/src/features/CmdConfigSync.ts +++ b/src/features/CmdConfigSync.ts @@ -2,7 +2,7 @@ import { writable } from 'svelte/store'; import { Notice, type PluginManifest, parseYaml, normalizePath, type ListedFiles, diff_match_patch } from "../deps.ts"; import type { EntryDoc, LoadedEntry, InternalFileEntry, FilePathWithPrefix, FilePath, AnyEntry, SavingEntry, diff_result } from "../lib/src/common/types.ts"; -import { CANCELLED, LEAVE_TO_SUBSEQUENT, LOG_LEVEL_INFO, LOG_LEVEL_NOTICE, LOG_LEVEL_VERBOSE, MODE_SELECTIVE, MODE_SHINY } from "../lib/src/common/types.ts"; +import { CANCELLED, LEAVE_TO_SUBSEQUENT, LOG_LEVEL_DEBUG, LOG_LEVEL_INFO, LOG_LEVEL_NOTICE, LOG_LEVEL_VERBOSE, MODE_SELECTIVE, MODE_SHINY } from "../lib/src/common/types.ts"; import { ICXHeader, PERIODIC_PLUGIN_SWEEP, } from "../common/types.ts"; import { createBlob, createSavingEntryFromLoadedEntry, createTextBlob, delay, fireAndForget, getDocData, getDocDataAsArray, isDocContentSame, isLoadedEntry, isObjectDifferent } from "../lib/src/common/utils.ts"; import { Logger } from "../lib/src/common/logger.ts"; @@ -11,7 +11,7 @@ import { arrayBufferToBase64, decodeBinary, readString } from 'src/lib/src/strin import { serialized, shareRunningResult } from "../lib/src/concurrency/lock.ts"; import { LiveSyncCommands } from "./LiveSyncCommands.ts"; import { stripAllPrefixes } from "../lib/src/string_and_binary/path.ts"; -import { PeriodicProcessor, disposeMemoObject, memoIfNotExist, memoObject, retrieveMemoObject, scheduleTask } from "../common/utils.ts"; +import { EVEN, PeriodicProcessor, disposeMemoObject, isMarkedAsSameChanges, markChangesAreSame, memoIfNotExist, memoObject, retrieveMemoObject, scheduleTask } from "../common/utils.ts"; import { PluginDialogModal } from "../common/dialogs.ts"; import { JsonResolveModal } from "../ui/JsonResolveModal.ts"; import { QueueProcessor } from '../lib/src/concurrency/processor.ts'; @@ -272,8 +272,10 @@ export class PluginDataExDisplayV2 { this.confKey = `${categoryToFolder(this.category, this.term)}${this.name}`; this.applyLoadedManifest(); } - setFile(file: LoadedEntryPluginDataExFile) { - if (this.files.find(e => e.filename == file.filename)) { + async setFile(file: LoadedEntryPluginDataExFile) { + const old = this.files.find(e => e.filename == file.filename); + if (old) { + if (old.mtime == file.mtime && await isDocContentSame(old.data, file.data)) return; this.files = this.files.filter(e => e.filename != file.filename); } this.files.push(file); @@ -319,6 +321,7 @@ export type PluginDataEx = { version?: string, mtime: number, }; + export class ConfigSync extends LiveSyncCommands { constructor(plugin: ObsidianLiveSyncPlugin) { super(plugin); @@ -637,7 +640,7 @@ export class ConfigSync extends LiveSyncCommands { if (!entry) return; const file = await this.createPluginDataExFileV2(unifiedFilenameWithKey); if (file) { - entry.setFile(file); + await entry.setFile(file); } else { entry.deleteFile(unifiedFilenameWithKey); if (entry.files.length == 0) { @@ -841,22 +844,46 @@ export class ConfigSync extends LiveSyncCommands { Logger(`Applying ${filename} of ${data.displayName || data.name}..`); const path = `${baseDir}/${filename}` as FilePath; await this.vaultAccess.ensureDirectory(path); + // If the content has applied, modified time will be updated to the current time. await this.vaultAccess.adapterWrite(path, content); await this.storeCustomisationFileV2(path, this.plugin.deviceAndVaultName); } else { const files = data.files; for (const f of files) { + // If files have applied, modified time will be updated to the current time. + const stat = { mtime: f.mtime, ctime: f.ctime }; const path = `${baseDir}/${f.filename}` as FilePath; Logger(`Applying ${f.filename} of ${data.displayName || data.name}..`); // const contentEach = createBlob(f.data); this.vaultAccess.ensureDirectory(path); + if (f.datatype == "newnote") { + let oldData; + try { + oldData = await this.vaultAccess.adapterReadBinary(path); + } catch (ex) { + oldData = new ArrayBuffer(0); + } const content = base64ToArrayBuffer(f.data); - await this.vaultAccess.adapterWrite(path, content); + if (await isDocContentSame(oldData, content)) { + Logger(`The file ${f.filename} is already up-to-date`, LOG_LEVEL_VERBOSE); + continue; + } + await this.vaultAccess.adapterWrite(path, content, stat); } else { + let oldData; + try { + oldData = await this.vaultAccess.adapterRead(path); + } catch (ex) { + oldData = ""; + } const content = getDocData(f.data); - await this.vaultAccess.adapterWrite(path, content); + if (await isDocContentSame(oldData, content)) { + Logger(`The file ${f.filename} is already up-to-date`, LOG_LEVEL_VERBOSE); + continue; + } + await this.vaultAccess.adapterWrite(path, content, stat); } Logger(`Applied ${f.filename} of ${data.displayName || data.name}..`); await this.storeCustomisationFileV2(path, this.plugin.deviceAndVaultName); @@ -1063,7 +1090,7 @@ export class ConfigSync extends LiveSyncCommands { } - async storeCustomisationFileV2(path: FilePath, term: string, saveRelatives = false) { + async storeCustomisationFileV2(path: FilePath, term: string, force = false) { const vf = this.filenameWithUnifiedKey(path, term); return await serialized(`plugin-${vf}`, async () => { const prefixedFileName = vf; @@ -1095,8 +1122,21 @@ export class ConfigSync extends LiveSyncCommands { eden: {} }; } else { - if (old.mtime == mtime) { - // Logger(`STORAGE --> DB:${prefixedFileName}: (config) Skipped (Same time)`, LOG_LEVEL_VERBOSE); + if (isMarkedAsSameChanges(prefixedFileName, [old.mtime, mtime + 1]) == EVEN) { + Logger(`STORAGE --> DB:${prefixedFileName}: (config) Skipped (Already checked the same)`, LOG_LEVEL_DEBUG); + return; + } + const docXDoc = await this.localDatabase.getDBEntryFromMeta(old, {}, false, false); + if (docXDoc == false) { + throw "Could not load the document"; + } + const dataSrc = getDocData(docXDoc.data); + const dataStart = dataSrc.indexOf(DUMMY_END); + const oldContent = dataSrc.substring(dataStart + DUMMY_END.length); + const oldContentArray = base64ToArrayBuffer(oldContent); + if (await isDocContentSame(oldContentArray, content)) { + Logger(`STORAGE --> DB:${prefixedFileName}: (config) Skipped (the same content)`, LOG_LEVEL_VERBOSE); + markChangesAreSame(prefixedFileName, old.mtime, mtime + 1); return true; } saveData = diff --git a/src/ui/components/PluginCombo.svelte b/src/ui/components/PluginCombo.svelte index 5704ffb..3624061 100644 --- a/src/ui/components/PluginCombo.svelte +++ b/src/ui/components/PluginCombo.svelte @@ -53,7 +53,7 @@ canApply = true; } else { const dtDiff = (local?.mtime ?? 0) - (remote?.mtime ?? 0); - const diff = timeDeltaToHumanReadable(Math.abs(dtDiff / 1000)); + const diff = timeDeltaToHumanReadable(Math.abs(dtDiff)); if (dtDiff / 1000 < -10) { // freshness = "✓ Newer"; freshness = `Newer (${diff})`; From 86d5582f372d9da8e8036b0a461542ce7a71234b Mon Sep 17 00:00:00 2001 From: vorotamoroz Date: Wed, 31 Jul 2024 02:14:11 +0100 Subject: [PATCH 02/12] bump --- manifest.json | 2 +- package-lock.json | 4 ++-- package.json | 2 +- updates.md | 25 +++++-------------------- updates_old.md | 19 +++++++++++++++++++ 5 files changed, 28 insertions(+), 24 deletions(-) diff --git a/manifest.json b/manifest.json index 5176bee..0b250dc 100644 --- a/manifest.json +++ b/manifest.json @@ -1,7 +1,7 @@ { "id": "obsidian-livesync", "name": "Self-hosted LiveSync", - "version": "0.23.18", + "version": "0.23.19", "minAppVersion": "0.9.12", "description": "Community implementation of self-hosted livesync. Reflect your vault changes to some other devices immediately. Please make sure to disable other synchronize solutions to avoid content corruption or duplication.", "author": "vorotamoroz", diff --git a/package-lock.json b/package-lock.json index b8c49e5..cf82d25 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "obsidian-livesync", - "version": "0.23.18", + "version": "0.23.19", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "obsidian-livesync", - "version": "0.23.18", + "version": "0.23.19", "license": "MIT", "dependencies": { "@aws-sdk/client-s3": "^3.614.0", diff --git a/package.json b/package.json index 3fd682b..4a1fd86 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "obsidian-livesync", - "version": "0.23.18", + "version": "0.23.19", "description": "Reflect your vault changes to some other devices immediately. Please make sure to disable other synchronize solutions to avoid content corruption or duplication.", "main": "main.js", "type": "module", diff --git a/updates.md b/updates.md index 79d17c1..17742a0 100644 --- a/updates.md +++ b/updates.md @@ -18,6 +18,11 @@ I have a lot of respect for that plugin, even though it is sometimes treated as Hooray for open source, and generous licences, and the sharing of knowledge by experts. #### Version history +- 0.23.19: + - Fixed: + - Customisation Sync now checks the difference while storing or applying the configuration. + - No longer storing the same configuration multiple times. + - Time difference in the dialogue has been fixed. - 0.23.18: - New feature: - Per-file-saved customization sync has been shipped. @@ -47,25 +52,5 @@ Hooray for open source, and generous licences, and the sharing of knowledge by e - We can get a notification about the storage usage of the remote database. - Default: We will be asked. - If the remote storage usage approaches the configured value, we will be asked whether we want to Rebuild or increase the limit. -- 0.23.16: - - Maintenance Update: - - Library refining (Phase 1 - step 2). There are no significant changes on the user side. - - Including the following fixes of potentially problems: - - the problem which the path had been obfuscating twice has been resolved. - - Note: Potential problems of the library; which has not happened in Self-hosted LiveSync for some reasons. -- 0.23.15: - - Maintenance Update: - - Library refining (Phase 1). There are no significant changes on the user side. -- 0.23.14: - - Fixed: - - No longer batch-saving ignores editor inputs. - - The file-watching and serialisation processes have been changed to the one which is similar to previous implementations. - - We can configure the settings (Especially about text-boxes) even if we have configured the device name. - - Improved: - - We can configure the delay of batch-saving. - - Default: 5 seconds, the same as the previous hard-coded value. (Note: also, the previous behaviour was not correct). - - Also, we can configure the limit of delaying batch-saving. - - The performance of showing status indicators has been improved. - Older notes is in [updates_old.md](https://github.com/vrtmrz/obsidian-livesync/blob/main/updates_old.md). \ No newline at end of file diff --git a/updates_old.md b/updates_old.md index 69d063d..5f634e5 100644 --- a/updates_old.md +++ b/updates_old.md @@ -18,6 +18,25 @@ I have a lot of respect for that plugin, even though it is sometimes treated as Hooray for open source, and generous licences, and the sharing of knowledge by experts. #### Version history +- 0.23.16: + - Maintenance Update: + - Library refining (Phase 1 - step 2). There are no significant changes on the user side. + - Including the following fixes of potentially problems: + - the problem which the path had been obfuscating twice has been resolved. + - Note: Potential problems of the library; which has not happened in Self-hosted LiveSync for some reasons. +- 0.23.15: + - Maintenance Update: + - Library refining (Phase 1). There are no significant changes on the user side. +- 0.23.14: + - Fixed: + - No longer batch-saving ignores editor inputs. + - The file-watching and serialisation processes have been changed to the one which is similar to previous implementations. + - We can configure the settings (Especially about text-boxes) even if we have configured the device name. + - Improved: + - We can configure the delay of batch-saving. + - Default: 5 seconds, the same as the previous hard-coded value. (Note: also, the previous behaviour was not correct). + - Also, we can configure the limit of delaying batch-saving. + - The performance of showing status indicators has been improved. - 0.23.13: - Fixed: - No longer files have been trimmed even delimiters have been continuous. From e46714e0f9577561b57a313e388e98667385e974 Mon Sep 17 00:00:00 2001 From: vorotamoroz Date: Wed, 31 Jul 2024 02:31:13 +0100 Subject: [PATCH 03/12] Fixed: - Remote Storage Limit Notification dialogue has been fixed, now the chosen value is saved. Improved: - The Enlarging button on the enlarging threshold dialogue now displays the new value. --- src/main.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/main.ts b/src/main.ts index cff9d4c..a8322c2 100644 --- a/src/main.ts +++ b/src/main.ts @@ -663,10 +663,13 @@ And if your actual storage size exceeds the threshold after the setup, you may w const ret = await confirmWithMessage(this, "Remote storage size threshold", message, [ANSWER_0, ANSWER_800, ANSWER_2000], ANSWER_800, 40); if (ret == ANSWER_0) { this.settings.notifyThresholdOfRemoteStorageSize = 0; + await this.saveSettings(); } else if (ret == ANSWER_800) { this.settings.notifyThresholdOfRemoteStorageSize = 800; + await this.saveSettings(); } else { this.settings.notifyThresholdOfRemoteStorageSize = 2000; + await this.saveSettings(); } } if (this.settings.notifyThresholdOfRemoteStorageSize > 0) { @@ -686,7 +689,8 @@ If you have enough space on the remote storage, you can enlarge the threshold. O However, **Please make sure that all devices have been synchronised**. \n \n`; - const ANSWER_ENLARGE_LIMIT = "Enlarge the limit"; + const newMax = ~~(estimatedSize / 1024 / 1024) + 100; + const ANSWER_ENLARGE_LIMIT = `Enlarge to ${newMax}MB`; const ANSWER_REBUILD = "Rebuild now"; const ANSWER_IGNORE = "Dismiss"; const ret = await confirmWithMessage(this, "Remote storage size exceeded", message, [ANSWER_ENLARGE_LIMIT, ANSWER_REBUILD, ANSWER_IGNORE,], ANSWER_IGNORE, 20); @@ -708,7 +712,7 @@ However, **Please make sure that all devices have been synchronised**. \n // Dismiss or Close the dialog } - Logger(`Remote storage size: ${sizeToHumanReadable(estimatedSize)} exceeded ${sizeToHumanReadable(this.settings.notifyThresholdOfRemoteStorageSize)} `, LOG_LEVEL_INFO); + Logger(`Remote storage size: ${sizeToHumanReadable(estimatedSize)} exceeded ${sizeToHumanReadable(this.settings.notifyThresholdOfRemoteStorageSize * 1024 * 1024)} `, LOG_LEVEL_INFO); } else { Logger(`Remote storage size: ${sizeToHumanReadable(estimatedSize)}`, LOG_LEVEL_INFO); } From 630889680e6da80a58c55f44ca764eb265758f4c Mon Sep 17 00:00:00 2001 From: vorotamoroz Date: Wed, 31 Jul 2024 02:32:02 +0100 Subject: [PATCH 04/12] bump --- manifest.json | 2 +- package-lock.json | 4 ++-- package.json | 2 +- updates.md | 7 ++++++- 4 files changed, 10 insertions(+), 5 deletions(-) diff --git a/manifest.json b/manifest.json index 0b250dc..f4ab502 100644 --- a/manifest.json +++ b/manifest.json @@ -1,7 +1,7 @@ { "id": "obsidian-livesync", "name": "Self-hosted LiveSync", - "version": "0.23.19", + "version": "0.23.20", "minAppVersion": "0.9.12", "description": "Community implementation of self-hosted livesync. Reflect your vault changes to some other devices immediately. Please make sure to disable other synchronize solutions to avoid content corruption or duplication.", "author": "vorotamoroz", diff --git a/package-lock.json b/package-lock.json index cf82d25..56559f4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "obsidian-livesync", - "version": "0.23.19", + "version": "0.23.20", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "obsidian-livesync", - "version": "0.23.19", + "version": "0.23.20", "license": "MIT", "dependencies": { "@aws-sdk/client-s3": "^3.614.0", diff --git a/package.json b/package.json index 4a1fd86..a72d3cd 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "obsidian-livesync", - "version": "0.23.19", + "version": "0.23.20", "description": "Reflect your vault changes to some other devices immediately. Please make sure to disable other synchronize solutions to avoid content corruption or duplication.", "main": "main.js", "type": "module", diff --git a/updates.md b/updates.md index 17742a0..39fa331 100644 --- a/updates.md +++ b/updates.md @@ -18,11 +18,16 @@ I have a lot of respect for that plugin, even though it is sometimes treated as Hooray for open source, and generous licences, and the sharing of knowledge by experts. #### Version history -- 0.23.19: +- 0.23.20: - Fixed: - Customisation Sync now checks the difference while storing or applying the configuration. - No longer storing the same configuration multiple times. - Time difference in the dialogue has been fixed. + - Remote Storage Limit Notification dialogue has been fixed, now the chosen value is saved. + - Improved: + - The Enlarging button on the enlarging threshold dialogue now displays the new value. +- 0.23.19: + - Not released. - 0.23.18: - New feature: - Per-file-saved customization sync has been shipped. From f778107727523632e521d994c33ed124efc1b110 Mon Sep 17 00:00:00 2001 From: "Stephen J. Fuhry" Date: Wed, 7 Aug 2024 14:43:38 -0400 Subject: [PATCH 05/12] add username and password to setup URI instructions --- docs/setup_own_server.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/setup_own_server.md b/docs/setup_own_server.md index 6c5bbba..193f469 100644 --- a/docs/setup_own_server.md +++ b/docs/setup_own_server.md @@ -106,6 +106,8 @@ Now `https://tiles-photograph-routine-groundwater.trycloudflare.com` is our serv $ export hostname=https://tiles-photograph-routine-groundwater.trycloudflare.com #Point to your vault $ export database=obsidiannotes #Please change as you like $ export passphrase=dfsapkdjaskdjasdas #Please change as you like +$ export username=johndoe +$ export password=abc123 $ deno run -A https://raw.githubusercontent.com/vrtmrz/obsidian-livesync/main/utils/flyio/generate_setupuri.ts obsidian://setuplivesync?settings=%5B%22tm2DpsOE74nJAryprZO2M93wF%2Fvg.......4b26ed33230729%22%5D @@ -206,4 +208,4 @@ entryPoints: address: ":443" ... -``` \ No newline at end of file +``` From ede126d7d4442c913373133ba5d2385b3458aae0 Mon Sep 17 00:00:00 2001 From: vorotamoroz Date: Sat, 7 Sep 2024 01:43:21 +0900 Subject: [PATCH 06/12] - 0.23.21: - New Features: - Case-insensitive file handling - Files can now be handled case-insensitively. - This behaviour can be modified in the settings under `Handle files as Case-Sensitive` (Default: Prompt, Enabled for previous behaviour). - Improved chunk revision fixing - Revisions for chunks can now be fixed for faster chunk creation. - This can be adjusted in the settings under `Compute revisions for chunks` (Default: Prompt, Enabled for previous behaviour). - Bulk chunk transfer - Chunks can now be transferred in bulk during uploads. - This feature is enabled by default through `Send chunks in bulk`. - Creation of missing chunks without - Missing chunks can be created without storing notes, enhancing efficiency for first synchronisation or after prolonged periods without synchronisation. - Improvements: - File status scanning on the startup - Quite significant performance improvements. - No more missing scans of some files. - Status in editor enhancements - Significant performance improvements in the status display within the editor. - Notifications for files that will not be synchronised will now be properly communicated. - Encryption and Decryption - These processes are now performed in background threads to ensure fast and stable transfers. - Verify and repair all files - Got faster through parallel checking. - Migration on update - Migration messages and wizards have become more helpful. - Behavioural changes: - Chunk size adjustments - Large chunks will no longer be created for older, stable files, addressing storage consumption issues. - Flag file automation - Confirmation will be shown and we can cancel it. - Fixed: - Database File Scanning - All files in the database will now be enumerated correctly. - Miscellaneous - Dependency updated. - Now, tree shaking is left to terser, from esbuild. --- esbuild.config.mjs | 8 +- package-lock.json | 2796 ++++++++++++++----------- package.json | 35 +- src/common/events.ts | 16 + src/common/utils.ts | 35 +- src/features/CmdSetupLiveSync.ts | 3 +- src/features/CmdStatusInsideEditor.ts | 234 +++ src/lib | 2 +- src/main.ts | 735 ++++--- src/storages/SerializedFileAccess.ts | 30 +- src/ui/ObsidianLiveSyncSettingTab.ts | 141 +- src/ui/settingConstants.ts | 14 +- styles.css | 103 +- 13 files changed, 2576 insertions(+), 1576 deletions(-) create mode 100644 src/common/events.ts create mode 100644 src/features/CmdStatusInsideEditor.ts diff --git a/esbuild.config.mjs b/esbuild.config.mjs index d288e18..2824175 100644 --- a/esbuild.config.mjs +++ b/esbuild.config.mjs @@ -23,8 +23,8 @@ const keepTest = !prod || dev; const terserOpt = { sourceMap: !prod ? { - url: "inline", - } + url: "inline", + } : {}, format: { indent_level: 2, @@ -41,6 +41,7 @@ const terserOpt = { // compress options defaults: false, evaluate: true, + dead_code: true, inline: 3, join_vars: true, loops: true, @@ -57,6 +58,7 @@ const terserOpt = { ecma: 2018, unused: true, }, + // mangle: false, ecma: 2018, // specify one of: 5, 2015, 2016, etc. enclose: false, // or specify true, or "args:values" @@ -122,7 +124,7 @@ const context = await esbuild.context({ logLevel: "info", platform: "browser", sourcemap: prod ? false : "inline", - treeShaking: true, + treeShaking: false, outfile: "main_org.js", mainFields: ["browser", "module", "main"], minifyWhitespace: false, diff --git a/package-lock.json b/package-lock.json index 56559f4..91afd53 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,42 +9,43 @@ "version": "0.23.20", "license": "MIT", "dependencies": { - "@aws-sdk/client-s3": "^3.614.0", - "@smithy/fetch-http-handler": "^3.2.1", - "@smithy/protocol-http": "^4.0.3", + "@aws-sdk/client-s3": "^3.645.0", + "@smithy/fetch-http-handler": "^3.2.4", + "@smithy/protocol-http": "^4.1.0", "@smithy/querystring-builder": "^3.0.3", "diff-match-patch": "^1.0.5", "esbuild-plugin-inline-worker": "^0.1.1", "fflate": "^0.8.2", "idb": "^8.0.0", "minimatch": "^10.0.1", - "octagonal-wheels": "^0.1.13", + "octagonal-wheels": "^0.1.14", "xxhash-wasm": "0.4.2", "xxhash-wasm-102": "npm:xxhash-wasm@^1.0.2" }, "devDependencies": { + "@chialab/esbuild-plugin-worker": "^0.18.1", "@tsconfig/svelte": "^5.0.4", "@types/diff-match-patch": "^1.0.36", - "@types/node": "^20.14.10", + "@types/node": "^22.5.4", "@types/pouchdb": "^6.4.2", "@types/pouchdb-adapter-http": "^6.1.6", "@types/pouchdb-adapter-idb": "^6.1.7", "@types/pouchdb-browser": "^6.1.5", - "@types/pouchdb-core": "^7.0.14", + "@types/pouchdb-core": "^7.0.15", "@types/pouchdb-mapreduce": "^6.1.10", "@types/pouchdb-replication": "^6.4.7", "@types/transform-pouch": "^1.0.6", - "@typescript-eslint/eslint-plugin": "^7.16.0", - "@typescript-eslint/parser": "^7.16.0", + "@typescript-eslint/eslint-plugin": "^8.4.0", + "@typescript-eslint/parser": "^8.4.0", "builtin-modules": "^4.0.0", - "esbuild": "0.23.0", + "esbuild": "0.23.1", "esbuild-svelte": "^0.8.1", "eslint": "^8.57.0", "eslint-config-airbnb-base": "^15.0.0", - "eslint-plugin-import": "^2.29.1", + "eslint-plugin-import": "^2.30.0", "events": "^3.3.0", - "obsidian": "^1.5.7", - "postcss": "^8.4.39", + "obsidian": "^1.6.6", + "postcss": "^8.4.45", "postcss-load-config": "^6.0.1", "pouchdb-adapter-http": "^9.0.0", "pouchdb-adapter-idb": "^9.0.0", @@ -56,12 +57,12 @@ "pouchdb-merge": "^9.0.0", "pouchdb-replication": "^9.0.0", "pouchdb-utils": "^9.0.0", - "svelte": "^4.2.18", + "svelte": "^4.2.19", "svelte-preprocess": "^6.0.2", - "terser": "^5.31.2", + "terser": "^5.31.6", "transform-pouch": "^2.0.0", - "tslib": "^2.6.3", - "typescript": "^5.5.3" + "tslib": "^2.7.0", + "typescript": "^5.5.4" } }, "node_modules/@aashutoshrathi/word-wrap": { @@ -90,6 +91,7 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/@aws-crypto/crc32/-/crc32-5.2.0.tgz", "integrity": "sha512-nLbCWqQNgUiwwtFsen1AdzAtvuLRsQS8rYgMuxCrdKf9kOssamGLuPwyTY9wyYblNr9+1XM8v6zoDTPPSIeANg==", + "license": "Apache-2.0", "dependencies": { "@aws-crypto/util": "^5.2.0", "@aws-sdk/types": "^3.222.0", @@ -103,6 +105,7 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/@aws-crypto/crc32c/-/crc32c-5.2.0.tgz", "integrity": "sha512-+iWb8qaHLYKrNvGRbiYRHSdKRWhto5XlZUEBwDjYNf+ly5SVYG6zEoYIdxvf5R3zyeP16w4PLBn3rH1xc74Rag==", + "license": "Apache-2.0", "dependencies": { "@aws-crypto/util": "^5.2.0", "@aws-sdk/types": "^3.222.0", @@ -161,6 +164,7 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-5.2.0.tgz", "integrity": "sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw==", + "license": "Apache-2.0", "dependencies": { "@aws-crypto/sha256-js": "^5.2.0", "@aws-crypto/supports-web-crypto": "^5.2.0", @@ -175,6 +179,7 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", + "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" }, @@ -186,6 +191,7 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", + "license": "Apache-2.0", "dependencies": { "@smithy/is-array-buffer": "^2.2.0", "tslib": "^2.6.2" @@ -198,6 +204,7 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", + "license": "Apache-2.0", "dependencies": { "@smithy/util-buffer-from": "^2.2.0", "tslib": "^2.6.2" @@ -210,6 +217,7 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-5.2.0.tgz", "integrity": "sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA==", + "license": "Apache-2.0", "dependencies": { "@aws-crypto/util": "^5.2.0", "@aws-sdk/types": "^3.222.0", @@ -273,65 +281,66 @@ } }, "node_modules/@aws-sdk/client-s3": { - "version": "3.614.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-s3/-/client-s3-3.614.0.tgz", - "integrity": "sha512-9BlhfeBegvyjOqHtcr9kvrT80wiy7EVUiqYyTFiiDv/hJIcG88XHQCZdLU7658XBkQ7aFrr5b8rF2HRD1oroxw==", + "version": "3.645.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-s3/-/client-s3-3.645.0.tgz", + "integrity": "sha512-RjT/mfNv4yr1uv/+aEXgSIxC5EB+yHPSU7hH0KZOZrvZEFASLl0i4FeoHzbMEOH5KdKGAi0uu3zRP3D1y45sKg==", + "license": "Apache-2.0", "dependencies": { "@aws-crypto/sha1-browser": "5.2.0", "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/client-sso-oidc": "3.614.0", - "@aws-sdk/client-sts": "3.614.0", - "@aws-sdk/core": "3.614.0", - "@aws-sdk/credential-provider-node": "3.614.0", - "@aws-sdk/middleware-bucket-endpoint": "3.614.0", - "@aws-sdk/middleware-expect-continue": "3.609.0", - "@aws-sdk/middleware-flexible-checksums": "3.614.0", - "@aws-sdk/middleware-host-header": "3.609.0", + "@aws-sdk/client-sso-oidc": "3.645.0", + "@aws-sdk/client-sts": "3.645.0", + "@aws-sdk/core": "3.635.0", + "@aws-sdk/credential-provider-node": "3.645.0", + "@aws-sdk/middleware-bucket-endpoint": "3.620.0", + "@aws-sdk/middleware-expect-continue": "3.620.0", + "@aws-sdk/middleware-flexible-checksums": "3.620.0", + "@aws-sdk/middleware-host-header": "3.620.0", "@aws-sdk/middleware-location-constraint": "3.609.0", "@aws-sdk/middleware-logger": "3.609.0", - "@aws-sdk/middleware-recursion-detection": "3.609.0", - "@aws-sdk/middleware-sdk-s3": "3.614.0", - "@aws-sdk/middleware-signing": "3.609.0", + "@aws-sdk/middleware-recursion-detection": "3.620.0", + "@aws-sdk/middleware-sdk-s3": "3.635.0", "@aws-sdk/middleware-ssec": "3.609.0", - "@aws-sdk/middleware-user-agent": "3.614.0", + "@aws-sdk/middleware-user-agent": "3.645.0", "@aws-sdk/region-config-resolver": "3.614.0", - "@aws-sdk/signature-v4-multi-region": "3.614.0", + "@aws-sdk/signature-v4-multi-region": "3.635.0", "@aws-sdk/types": "3.609.0", - "@aws-sdk/util-endpoints": "3.614.0", + "@aws-sdk/util-endpoints": "3.645.0", "@aws-sdk/util-user-agent-browser": "3.609.0", "@aws-sdk/util-user-agent-node": "3.614.0", "@aws-sdk/xml-builder": "3.609.0", "@smithy/config-resolver": "^3.0.5", - "@smithy/core": "^2.2.6", - "@smithy/eventstream-serde-browser": "^3.0.4", + "@smithy/core": "^2.4.0", + "@smithy/eventstream-serde-browser": "^3.0.6", "@smithy/eventstream-serde-config-resolver": "^3.0.3", - "@smithy/eventstream-serde-node": "^3.0.4", - "@smithy/fetch-http-handler": "^3.2.1", + "@smithy/eventstream-serde-node": "^3.0.5", + "@smithy/fetch-http-handler": "^3.2.4", "@smithy/hash-blob-browser": "^3.1.2", "@smithy/hash-node": "^3.0.3", "@smithy/hash-stream-node": "^3.1.2", "@smithy/invalid-dependency": "^3.0.3", "@smithy/md5-js": "^3.0.3", - "@smithy/middleware-content-length": "^3.0.3", - "@smithy/middleware-endpoint": "^3.0.5", - "@smithy/middleware-retry": "^3.0.9", + "@smithy/middleware-content-length": "^3.0.5", + "@smithy/middleware-endpoint": "^3.1.0", + "@smithy/middleware-retry": "^3.0.15", "@smithy/middleware-serde": "^3.0.3", "@smithy/middleware-stack": "^3.0.3", "@smithy/node-config-provider": "^3.1.4", - "@smithy/node-http-handler": "^3.1.2", - "@smithy/protocol-http": "^4.0.3", - "@smithy/smithy-client": "^3.1.7", + "@smithy/node-http-handler": "^3.1.4", + "@smithy/protocol-http": "^4.1.0", + "@smithy/smithy-client": "^3.2.0", "@smithy/types": "^3.3.0", "@smithy/url-parser": "^3.0.3", "@smithy/util-base64": "^3.0.0", "@smithy/util-body-length-browser": "^3.0.0", "@smithy/util-body-length-node": "^3.0.0", - "@smithy/util-defaults-mode-browser": "^3.0.9", - "@smithy/util-defaults-mode-node": "^3.0.9", + "@smithy/util-defaults-mode-browser": "^3.0.15", + "@smithy/util-defaults-mode-node": "^3.0.15", "@smithy/util-endpoints": "^2.0.5", + "@smithy/util-middleware": "^3.0.3", "@smithy/util-retry": "^3.0.3", - "@smithy/util-stream": "^3.0.6", + "@smithy/util-stream": "^3.1.3", "@smithy/util-utf8": "^3.0.0", "@smithy/util-waiter": "^3.1.2", "tslib": "^2.6.2" @@ -341,43 +350,44 @@ } }, "node_modules/@aws-sdk/client-sso": { - "version": "3.614.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.614.0.tgz", - "integrity": "sha512-p5pyYaxRzBttjBkqfc8i3K7DzBdTg3ECdVgBo6INIUxfvDy0J8QUE8vNtCgvFIkq+uPw/8M+Eo4zzln7anuO0Q==", + "version": "3.645.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.645.0.tgz", + "integrity": "sha512-2rc8TjnsNddOeKQ/pfNN7deNvGLXAeKeYtHtGDAiM2qfTKxd2sNcAsZ+JCDLyshuD4xLM5fpUyR0X8As9EAouQ==", + "license": "Apache-2.0", "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.614.0", - "@aws-sdk/middleware-host-header": "3.609.0", + "@aws-sdk/core": "3.635.0", + "@aws-sdk/middleware-host-header": "3.620.0", "@aws-sdk/middleware-logger": "3.609.0", - "@aws-sdk/middleware-recursion-detection": "3.609.0", - "@aws-sdk/middleware-user-agent": "3.614.0", + "@aws-sdk/middleware-recursion-detection": "3.620.0", + "@aws-sdk/middleware-user-agent": "3.645.0", "@aws-sdk/region-config-resolver": "3.614.0", "@aws-sdk/types": "3.609.0", - "@aws-sdk/util-endpoints": "3.614.0", + "@aws-sdk/util-endpoints": "3.645.0", "@aws-sdk/util-user-agent-browser": "3.609.0", "@aws-sdk/util-user-agent-node": "3.614.0", "@smithy/config-resolver": "^3.0.5", - "@smithy/core": "^2.2.6", - "@smithy/fetch-http-handler": "^3.2.1", + "@smithy/core": "^2.4.0", + "@smithy/fetch-http-handler": "^3.2.4", "@smithy/hash-node": "^3.0.3", "@smithy/invalid-dependency": "^3.0.3", - "@smithy/middleware-content-length": "^3.0.3", - "@smithy/middleware-endpoint": "^3.0.5", - "@smithy/middleware-retry": "^3.0.9", + "@smithy/middleware-content-length": "^3.0.5", + "@smithy/middleware-endpoint": "^3.1.0", + "@smithy/middleware-retry": "^3.0.15", "@smithy/middleware-serde": "^3.0.3", "@smithy/middleware-stack": "^3.0.3", "@smithy/node-config-provider": "^3.1.4", - "@smithy/node-http-handler": "^3.1.2", - "@smithy/protocol-http": "^4.0.3", - "@smithy/smithy-client": "^3.1.7", + "@smithy/node-http-handler": "^3.1.4", + "@smithy/protocol-http": "^4.1.0", + "@smithy/smithy-client": "^3.2.0", "@smithy/types": "^3.3.0", "@smithy/url-parser": "^3.0.3", "@smithy/util-base64": "^3.0.0", "@smithy/util-body-length-browser": "^3.0.0", "@smithy/util-body-length-node": "^3.0.0", - "@smithy/util-defaults-mode-browser": "^3.0.9", - "@smithy/util-defaults-mode-node": "^3.0.9", + "@smithy/util-defaults-mode-browser": "^3.0.15", + "@smithy/util-defaults-mode-node": "^3.0.15", "@smithy/util-endpoints": "^2.0.5", "@smithy/util-middleware": "^3.0.3", "@smithy/util-retry": "^3.0.3", @@ -389,44 +399,45 @@ } }, "node_modules/@aws-sdk/client-sso-oidc": { - "version": "3.614.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso-oidc/-/client-sso-oidc-3.614.0.tgz", - "integrity": "sha512-BI1NWcpppbHg/28zbUg54dZeckork8BItZIcjls12vxasy+p3iEzrJVG60jcbUTTsk3Qc1tyxNfrdcVqx0y7Ww==", + "version": "3.645.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso-oidc/-/client-sso-oidc-3.645.0.tgz", + "integrity": "sha512-X9ULtdk3cO+1ysurEkJ1MSnu6U00qodXx+IVual+1jXX4RYY1WmQmfo7uDKf6FFkz7wW1DAqU+GJIBNQr0YH8A==", + "license": "Apache-2.0", "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.614.0", - "@aws-sdk/credential-provider-node": "3.614.0", - "@aws-sdk/middleware-host-header": "3.609.0", + "@aws-sdk/core": "3.635.0", + "@aws-sdk/credential-provider-node": "3.645.0", + "@aws-sdk/middleware-host-header": "3.620.0", "@aws-sdk/middleware-logger": "3.609.0", - "@aws-sdk/middleware-recursion-detection": "3.609.0", - "@aws-sdk/middleware-user-agent": "3.614.0", + "@aws-sdk/middleware-recursion-detection": "3.620.0", + "@aws-sdk/middleware-user-agent": "3.645.0", "@aws-sdk/region-config-resolver": "3.614.0", "@aws-sdk/types": "3.609.0", - "@aws-sdk/util-endpoints": "3.614.0", + "@aws-sdk/util-endpoints": "3.645.0", "@aws-sdk/util-user-agent-browser": "3.609.0", "@aws-sdk/util-user-agent-node": "3.614.0", "@smithy/config-resolver": "^3.0.5", - "@smithy/core": "^2.2.6", - "@smithy/fetch-http-handler": "^3.2.1", + "@smithy/core": "^2.4.0", + "@smithy/fetch-http-handler": "^3.2.4", "@smithy/hash-node": "^3.0.3", "@smithy/invalid-dependency": "^3.0.3", - "@smithy/middleware-content-length": "^3.0.3", - "@smithy/middleware-endpoint": "^3.0.5", - "@smithy/middleware-retry": "^3.0.9", + "@smithy/middleware-content-length": "^3.0.5", + "@smithy/middleware-endpoint": "^3.1.0", + "@smithy/middleware-retry": "^3.0.15", "@smithy/middleware-serde": "^3.0.3", "@smithy/middleware-stack": "^3.0.3", "@smithy/node-config-provider": "^3.1.4", - "@smithy/node-http-handler": "^3.1.2", - "@smithy/protocol-http": "^4.0.3", - "@smithy/smithy-client": "^3.1.7", + "@smithy/node-http-handler": "^3.1.4", + "@smithy/protocol-http": "^4.1.0", + "@smithy/smithy-client": "^3.2.0", "@smithy/types": "^3.3.0", "@smithy/url-parser": "^3.0.3", "@smithy/util-base64": "^3.0.0", "@smithy/util-body-length-browser": "^3.0.0", "@smithy/util-body-length-node": "^3.0.0", - "@smithy/util-defaults-mode-browser": "^3.0.9", - "@smithy/util-defaults-mode-node": "^3.0.9", + "@smithy/util-defaults-mode-browser": "^3.0.15", + "@smithy/util-defaults-mode-node": "^3.0.15", "@smithy/util-endpoints": "^2.0.5", "@smithy/util-middleware": "^3.0.3", "@smithy/util-retry": "^3.0.3", @@ -437,49 +448,50 @@ "node": ">=16.0.0" }, "peerDependencies": { - "@aws-sdk/client-sts": "^3.614.0" + "@aws-sdk/client-sts": "^3.645.0" } }, "node_modules/@aws-sdk/client-sts": { - "version": "3.614.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.614.0.tgz", - "integrity": "sha512-i6QmaVA1KHHYNnI2VYQy/sc31rLm4+jSp8b/YbQpFnD0w3aXsrEEHHlxek45uSkHb4Nrj1omFBVy/xp1WVYx2Q==", + "version": "3.645.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.645.0.tgz", + "integrity": "sha512-6azXYtvtnAsPf2ShN9vKynIYVcJOpo6IoVmoMAVgNaBJyllP+s/RORzranYZzckqfmrudSxtct4rVapjLWuAMg==", + "license": "Apache-2.0", "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/client-sso-oidc": "3.614.0", - "@aws-sdk/core": "3.614.0", - "@aws-sdk/credential-provider-node": "3.614.0", - "@aws-sdk/middleware-host-header": "3.609.0", + "@aws-sdk/client-sso-oidc": "3.645.0", + "@aws-sdk/core": "3.635.0", + "@aws-sdk/credential-provider-node": "3.645.0", + "@aws-sdk/middleware-host-header": "3.620.0", "@aws-sdk/middleware-logger": "3.609.0", - "@aws-sdk/middleware-recursion-detection": "3.609.0", - "@aws-sdk/middleware-user-agent": "3.614.0", + "@aws-sdk/middleware-recursion-detection": "3.620.0", + "@aws-sdk/middleware-user-agent": "3.645.0", "@aws-sdk/region-config-resolver": "3.614.0", "@aws-sdk/types": "3.609.0", - "@aws-sdk/util-endpoints": "3.614.0", + "@aws-sdk/util-endpoints": "3.645.0", "@aws-sdk/util-user-agent-browser": "3.609.0", "@aws-sdk/util-user-agent-node": "3.614.0", "@smithy/config-resolver": "^3.0.5", - "@smithy/core": "^2.2.6", - "@smithy/fetch-http-handler": "^3.2.1", + "@smithy/core": "^2.4.0", + "@smithy/fetch-http-handler": "^3.2.4", "@smithy/hash-node": "^3.0.3", "@smithy/invalid-dependency": "^3.0.3", - "@smithy/middleware-content-length": "^3.0.3", - "@smithy/middleware-endpoint": "^3.0.5", - "@smithy/middleware-retry": "^3.0.9", + "@smithy/middleware-content-length": "^3.0.5", + "@smithy/middleware-endpoint": "^3.1.0", + "@smithy/middleware-retry": "^3.0.15", "@smithy/middleware-serde": "^3.0.3", "@smithy/middleware-stack": "^3.0.3", "@smithy/node-config-provider": "^3.1.4", - "@smithy/node-http-handler": "^3.1.2", - "@smithy/protocol-http": "^4.0.3", - "@smithy/smithy-client": "^3.1.7", + "@smithy/node-http-handler": "^3.1.4", + "@smithy/protocol-http": "^4.1.0", + "@smithy/smithy-client": "^3.2.0", "@smithy/types": "^3.3.0", "@smithy/url-parser": "^3.0.3", "@smithy/util-base64": "^3.0.0", "@smithy/util-body-length-browser": "^3.0.0", "@smithy/util-body-length-node": "^3.0.0", - "@smithy/util-defaults-mode-browser": "^3.0.9", - "@smithy/util-defaults-mode-node": "^3.0.9", + "@smithy/util-defaults-mode-browser": "^3.0.15", + "@smithy/util-defaults-mode-node": "^3.0.15", "@smithy/util-endpoints": "^2.0.5", "@smithy/util-middleware": "^3.0.3", "@smithy/util-retry": "^3.0.3", @@ -491,16 +503,20 @@ } }, "node_modules/@aws-sdk/core": { - "version": "3.614.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.614.0.tgz", - "integrity": "sha512-BUuS5/1YkgmKc4J0bg83XEtMyDHVyqG2QDzfmhYe8gbOIZabUl1FlrFVwhCAthtrrI6MPGTQcERB4BtJKUSplw==", + "version": "3.635.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.635.0.tgz", + "integrity": "sha512-i1x/E/sgA+liUE1XJ7rj1dhyXpAKO1UKFUcTTHXok2ARjWTvszHnSXMOsB77aPbmn0fUp1JTx2kHUAZ1LVt5Bg==", + "license": "Apache-2.0", "dependencies": { - "@smithy/core": "^2.2.6", - "@smithy/protocol-http": "^4.0.3", - "@smithy/signature-v4": "^3.1.2", - "@smithy/smithy-client": "^3.1.7", + "@smithy/core": "^2.4.0", + "@smithy/node-config-provider": "^3.1.4", + "@smithy/property-provider": "^3.1.3", + "@smithy/protocol-http": "^4.1.0", + "@smithy/signature-v4": "^4.1.0", + "@smithy/smithy-client": "^3.2.0", "@smithy/types": "^3.3.0", - "fast-xml-parser": "4.2.5", + "@smithy/util-middleware": "^3.0.3", + "fast-xml-parser": "4.4.1", "tslib": "^2.6.2" }, "engines": { @@ -508,9 +524,10 @@ } }, "node_modules/@aws-sdk/credential-provider-env": { - "version": "3.609.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.609.0.tgz", - "integrity": "sha512-v69ZCWcec2iuV9vLVJMa6fAb5xwkzN4jYIT8yjo2c4Ia/j976Q+TPf35Pnz5My48Xr94EFcaBazrWedF+kwfuQ==", + "version": "3.620.1", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.620.1.tgz", + "integrity": "sha512-ExuILJ2qLW5ZO+rgkNRj0xiAipKT16Rk77buvPP8csR7kkCflT/gXTyzRe/uzIiETTxM7tr8xuO9MP/DQXqkfg==", + "license": "Apache-2.0", "dependencies": { "@aws-sdk/types": "3.609.0", "@smithy/property-provider": "^3.1.3", @@ -522,18 +539,19 @@ } }, "node_modules/@aws-sdk/credential-provider-http": { - "version": "3.614.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.614.0.tgz", - "integrity": "sha512-YIEjlNUKb3Vo/iTnGAPdsiDC3FUUnNoex2OwU8LmR7AkYZiWdB8nx99DfgkkY+OFMUpw7nKD2PCOtuFONelfGA==", + "version": "3.635.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.635.0.tgz", + "integrity": "sha512-iJyRgEjOCQlBMXqtwPLIKYc7Bsc6nqjrZybdMDenPDa+kmLg7xh8LxHsu9088e+2/wtLicE34FsJJIfzu3L82g==", + "license": "Apache-2.0", "dependencies": { "@aws-sdk/types": "3.609.0", - "@smithy/fetch-http-handler": "^3.2.1", - "@smithy/node-http-handler": "^3.1.2", + "@smithy/fetch-http-handler": "^3.2.4", + "@smithy/node-http-handler": "^3.1.4", "@smithy/property-provider": "^3.1.3", - "@smithy/protocol-http": "^4.0.3", - "@smithy/smithy-client": "^3.1.7", + "@smithy/protocol-http": "^4.1.0", + "@smithy/smithy-client": "^3.2.0", "@smithy/types": "^3.3.0", - "@smithy/util-stream": "^3.0.6", + "@smithy/util-stream": "^3.1.3", "tslib": "^2.6.2" }, "engines": { @@ -541,17 +559,18 @@ } }, "node_modules/@aws-sdk/credential-provider-ini": { - "version": "3.614.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.614.0.tgz", - "integrity": "sha512-KfLuLFGwlvFSZ2MuzYwWGPb1y5TeiwX5okIDe0aQ1h10oD3924FXbN+mabOnUHQ8EFcGAtCaWbrC86mI7ktC6A==", + "version": "3.645.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.645.0.tgz", + "integrity": "sha512-LlZW0qwUwNlTaAIDCNpLbPsyXvS42pRIwF92fgtCQedmdnpN3XRUC6hcwSYI7Xru3GGKp3RnceOvsdOaRJORsw==", + "license": "Apache-2.0", "dependencies": { - "@aws-sdk/credential-provider-env": "3.609.0", - "@aws-sdk/credential-provider-http": "3.614.0", - "@aws-sdk/credential-provider-process": "3.614.0", - "@aws-sdk/credential-provider-sso": "3.614.0", - "@aws-sdk/credential-provider-web-identity": "3.609.0", + "@aws-sdk/credential-provider-env": "3.620.1", + "@aws-sdk/credential-provider-http": "3.635.0", + "@aws-sdk/credential-provider-process": "3.620.1", + "@aws-sdk/credential-provider-sso": "3.645.0", + "@aws-sdk/credential-provider-web-identity": "3.621.0", "@aws-sdk/types": "3.609.0", - "@smithy/credential-provider-imds": "^3.1.4", + "@smithy/credential-provider-imds": "^3.2.0", "@smithy/property-provider": "^3.1.3", "@smithy/shared-ini-file-loader": "^3.1.4", "@smithy/types": "^3.3.0", @@ -561,22 +580,23 @@ "node": ">=16.0.0" }, "peerDependencies": { - "@aws-sdk/client-sts": "^3.614.0" + "@aws-sdk/client-sts": "^3.645.0" } }, "node_modules/@aws-sdk/credential-provider-node": { - "version": "3.614.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.614.0.tgz", - "integrity": "sha512-4J6gPEuFZP0mkWq5E//oMS1vrmMM88iNNcv7TEljYnsc6JTAlKejCyFwx6CN+nkIhmIZsl06SXIhBemzBdBPfg==", + "version": "3.645.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.645.0.tgz", + "integrity": "sha512-eGFFuNvLeXjCJf5OCIuSEflxUowmK+bCS+lK4M8ofsYOEGAivdx7C0UPxNjHpvM8wKd8vpMl5phTeS9BWX5jMQ==", + "license": "Apache-2.0", "dependencies": { - "@aws-sdk/credential-provider-env": "3.609.0", - "@aws-sdk/credential-provider-http": "3.614.0", - "@aws-sdk/credential-provider-ini": "3.614.0", - "@aws-sdk/credential-provider-process": "3.614.0", - "@aws-sdk/credential-provider-sso": "3.614.0", - "@aws-sdk/credential-provider-web-identity": "3.609.0", + "@aws-sdk/credential-provider-env": "3.620.1", + "@aws-sdk/credential-provider-http": "3.635.0", + "@aws-sdk/credential-provider-ini": "3.645.0", + "@aws-sdk/credential-provider-process": "3.620.1", + "@aws-sdk/credential-provider-sso": "3.645.0", + "@aws-sdk/credential-provider-web-identity": "3.621.0", "@aws-sdk/types": "3.609.0", - "@smithy/credential-provider-imds": "^3.1.4", + "@smithy/credential-provider-imds": "^3.2.0", "@smithy/property-provider": "^3.1.3", "@smithy/shared-ini-file-loader": "^3.1.4", "@smithy/types": "^3.3.0", @@ -587,9 +607,10 @@ } }, "node_modules/@aws-sdk/credential-provider-process": { - "version": "3.614.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.614.0.tgz", - "integrity": "sha512-Q0SI0sTRwi8iNODLs5+bbv8vgz8Qy2QdxbCHnPk/6Cx6LMf7i3dqmWquFbspqFRd8QiqxStrblwxrUYZi09tkA==", + "version": "3.620.1", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.620.1.tgz", + "integrity": "sha512-hWqFMidqLAkaV9G460+1at6qa9vySbjQKKc04p59OT7lZ5cO5VH5S4aI05e+m4j364MBROjjk2ugNvfNf/8ILg==", + "license": "Apache-2.0", "dependencies": { "@aws-sdk/types": "3.609.0", "@smithy/property-provider": "^3.1.3", @@ -602,11 +623,12 @@ } }, "node_modules/@aws-sdk/credential-provider-sso": { - "version": "3.614.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.614.0.tgz", - "integrity": "sha512-55+gp0JY4451cWI1qXmVMFM0GQaBKiQpXv2P0xmd9P3qLDyeFUSEW8XPh0d2lb1ICr6x4s47ynXVdGCIv2mXMg==", + "version": "3.645.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.645.0.tgz", + "integrity": "sha512-d6XuChAl5NCsCrUexc6AFb4efPmb9+66iwPylKG+iMTMYgO1ackfy1Q2/f35jdn0jolkPkzKsVyfzsEVoID6ew==", + "license": "Apache-2.0", "dependencies": { - "@aws-sdk/client-sso": "3.614.0", + "@aws-sdk/client-sso": "3.645.0", "@aws-sdk/token-providers": "3.614.0", "@aws-sdk/types": "3.609.0", "@smithy/property-provider": "^3.1.3", @@ -619,9 +641,10 @@ } }, "node_modules/@aws-sdk/credential-provider-web-identity": { - "version": "3.609.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.609.0.tgz", - "integrity": "sha512-U+PG8NhlYYF45zbr1km3ROtBMYqyyj/oK8NRp++UHHeuavgrP+4wJ4wQnlEaKvJBjevfo3+dlIBcaeQ7NYejWg==", + "version": "3.621.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.621.0.tgz", + "integrity": "sha512-w7ASSyfNvcx7+bYGep3VBgC3K6vEdLmlpjT7nSIHxxQf+WSdvy+HynwJosrpZax0sK5q0D1Jpn/5q+r5lwwW6w==", + "license": "Apache-2.0", "dependencies": { "@aws-sdk/types": "3.609.0", "@smithy/property-provider": "^3.1.3", @@ -632,18 +655,19 @@ "node": ">=16.0.0" }, "peerDependencies": { - "@aws-sdk/client-sts": "^3.609.0" + "@aws-sdk/client-sts": "^3.621.0" } }, "node_modules/@aws-sdk/middleware-bucket-endpoint": { - "version": "3.614.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-bucket-endpoint/-/middleware-bucket-endpoint-3.614.0.tgz", - "integrity": "sha512-TqEY8KcZeZ0LIxXaqG9RSSNnDHvD8RAFP4Xenwsxqnyad0Yn7LgCoFwRByelJ0t54ROYL1/ETJleWE4U4TOXdg==", + "version": "3.620.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-bucket-endpoint/-/middleware-bucket-endpoint-3.620.0.tgz", + "integrity": "sha512-eGLL0W6L3HDb3OACyetZYOWpHJ+gLo0TehQKeQyy2G8vTYXqNTeqYhuI6up9HVjBzU9eQiULVQETmgQs7TFaRg==", + "license": "Apache-2.0", "dependencies": { "@aws-sdk/types": "3.609.0", "@aws-sdk/util-arn-parser": "3.568.0", "@smithy/node-config-provider": "^3.1.4", - "@smithy/protocol-http": "^4.0.3", + "@smithy/protocol-http": "^4.1.0", "@smithy/types": "^3.3.0", "@smithy/util-config-provider": "^3.0.0", "tslib": "^2.6.2" @@ -653,12 +677,13 @@ } }, "node_modules/@aws-sdk/middleware-expect-continue": { - "version": "3.609.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-expect-continue/-/middleware-expect-continue-3.609.0.tgz", - "integrity": "sha512-+zeg//mSer4JZRxOB/4mUOMUJyuYPwATnIC5moBB8P8Xe+mJaVRFy8qlCtzYNj2TycnlsBPzTK0j7P1yvDh97w==", + "version": "3.620.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-expect-continue/-/middleware-expect-continue-3.620.0.tgz", + "integrity": "sha512-QXeRFMLfyQ31nAHLbiTLtk0oHzG9QLMaof5jIfqcUwnOkO8YnQdeqzakrg1Alpy/VQ7aqzIi8qypkBe2KXZz0A==", + "license": "Apache-2.0", "dependencies": { "@aws-sdk/types": "3.609.0", - "@smithy/protocol-http": "^4.0.3", + "@smithy/protocol-http": "^4.1.0", "@smithy/types": "^3.3.0", "tslib": "^2.6.2" }, @@ -667,15 +692,16 @@ } }, "node_modules/@aws-sdk/middleware-flexible-checksums": { - "version": "3.614.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-flexible-checksums/-/middleware-flexible-checksums-3.614.0.tgz", - "integrity": "sha512-ZLpxVXMboDeMT7p2Kdp5m1uLVKOktkZoMgLvvbe3zbrU4Ji5IU5xVE0aa4X7H28BtuODCs6SLESnPs19bhMKlA==", + "version": "3.620.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-flexible-checksums/-/middleware-flexible-checksums-3.620.0.tgz", + "integrity": "sha512-ftz+NW7qka2sVuwnnO1IzBku5ccP+s5qZGeRTPgrKB7OzRW85gthvIo1vQR2w+OwHFk7WJbbhhWwbCbktnP4UA==", + "license": "Apache-2.0", "dependencies": { "@aws-crypto/crc32": "5.2.0", "@aws-crypto/crc32c": "5.2.0", "@aws-sdk/types": "3.609.0", "@smithy/is-array-buffer": "^3.0.0", - "@smithy/protocol-http": "^4.0.3", + "@smithy/protocol-http": "^4.1.0", "@smithy/types": "^3.3.0", "@smithy/util-utf8": "^3.0.0", "tslib": "^2.6.2" @@ -685,12 +711,13 @@ } }, "node_modules/@aws-sdk/middleware-host-header": { - "version": "3.609.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.609.0.tgz", - "integrity": "sha512-iTKfo158lc4jLDfYeZmYMIBHsn8m6zX+XB6birCSNZ/rrlzAkPbGE43CNdKfvjyWdqgLMRXF+B+OcZRvqhMXPQ==", + "version": "3.620.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.620.0.tgz", + "integrity": "sha512-VMtPEZwqYrII/oUkffYsNWY9PZ9xpNJpMgmyU0rlDQ25O1c0Hk3fJmZRe6pEkAJ0omD7kLrqGl1DUjQVxpd/Rg==", + "license": "Apache-2.0", "dependencies": { "@aws-sdk/types": "3.609.0", - "@smithy/protocol-http": "^4.0.3", + "@smithy/protocol-http": "^4.1.0", "@smithy/types": "^3.3.0", "tslib": "^2.6.2" }, @@ -715,6 +742,7 @@ "version": "3.609.0", "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.609.0.tgz", "integrity": "sha512-S62U2dy4jMDhDFDK5gZ4VxFdWzCtLzwbYyFZx2uvPYTECkepLUfzLic2BHg2Qvtu4QjX+oGE3P/7fwaGIsGNuQ==", + "license": "Apache-2.0", "dependencies": { "@aws-sdk/types": "3.609.0", "@smithy/types": "^3.3.0", @@ -725,12 +753,13 @@ } }, "node_modules/@aws-sdk/middleware-recursion-detection": { - "version": "3.609.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.609.0.tgz", - "integrity": "sha512-6sewsYB7/o/nbUfA99Aa/LokM+a/u4Wpm/X2o0RxOsDtSB795ObebLJe2BxY5UssbGaWkn7LswyfvrdZNXNj1w==", + "version": "3.620.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.620.0.tgz", + "integrity": "sha512-nh91S7aGK3e/o1ck64sA/CyoFw+gAYj2BDOnoNa6ouyCrVJED96ZXWbhye/fz9SgmNUZR2g7GdVpiLpMKZoI5w==", + "license": "Apache-2.0", "dependencies": { "@aws-sdk/types": "3.609.0", - "@smithy/protocol-http": "^4.0.3", + "@smithy/protocol-http": "^4.1.0", "@smithy/types": "^3.3.0", "tslib": "^2.6.2" }, @@ -739,35 +768,24 @@ } }, "node_modules/@aws-sdk/middleware-sdk-s3": { - "version": "3.614.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.614.0.tgz", - "integrity": "sha512-9fJTaiuuOfFV4FqmUEhPYzrtv7JOfYpB7q65oG3uayVH4ngWHIJkjnnX79zRhNZKdPGta+XIsnZzjEghg82ngA==", + "version": "3.635.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.635.0.tgz", + "integrity": "sha512-RLdYJPEV4JL/7NBoFUs7VlP90X++5FlJdxHz0DzCjmiD3qCviKy+Cym3qg1gBgHwucs5XisuClxDrGokhAdTQw==", + "license": "Apache-2.0", "dependencies": { + "@aws-sdk/core": "3.635.0", "@aws-sdk/types": "3.609.0", "@aws-sdk/util-arn-parser": "3.568.0", + "@smithy/core": "^2.4.0", "@smithy/node-config-provider": "^3.1.4", - "@smithy/protocol-http": "^4.0.3", - "@smithy/signature-v4": "^3.1.2", - "@smithy/smithy-client": "^3.1.7", + "@smithy/protocol-http": "^4.1.0", + "@smithy/signature-v4": "^4.1.0", + "@smithy/smithy-client": "^3.2.0", "@smithy/types": "^3.3.0", "@smithy/util-config-provider": "^3.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/middleware-signing": { - "version": "3.609.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-signing/-/middleware-signing-3.609.0.tgz", - "integrity": "sha512-2w3dBLjQVKIajYzokO4hduq8/0hSMUYHHmIo1Kdl+MSY8uwRBt12bLL6pyreobTcRMxizvn2ph/CQ9I1ST/WGQ==", - "dependencies": { - "@aws-sdk/types": "3.609.0", - "@smithy/property-provider": "^3.1.3", - "@smithy/protocol-http": "^4.0.3", - "@smithy/signature-v4": "^3.1.2", - "@smithy/types": "^3.3.0", "@smithy/util-middleware": "^3.0.3", + "@smithy/util-stream": "^3.1.3", + "@smithy/util-utf8": "^3.0.0", "tslib": "^2.6.2" }, "engines": { @@ -788,13 +806,14 @@ } }, "node_modules/@aws-sdk/middleware-user-agent": { - "version": "3.614.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.614.0.tgz", - "integrity": "sha512-xUxh0UPQiMTG6E31Yvu6zVYlikrIcFDKljM11CaatInzvZubGTGiX0DjpqRlfGzUNsuPc/zNrKwRP2+wypgqIw==", + "version": "3.645.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.645.0.tgz", + "integrity": "sha512-NpTAtqWK+49lRuxfz7st9for80r4NriCMK0RfdJSoPFVntjsSQiQ7+2nW2XL05uVY633e9DvCAw8YatX3zd1mw==", + "license": "Apache-2.0", "dependencies": { "@aws-sdk/types": "3.609.0", - "@aws-sdk/util-endpoints": "3.614.0", - "@smithy/protocol-http": "^4.0.3", + "@aws-sdk/util-endpoints": "3.645.0", + "@smithy/protocol-http": "^4.1.0", "@smithy/types": "^3.3.0", "tslib": "^2.6.2" }, @@ -806,6 +825,7 @@ "version": "3.614.0", "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.614.0.tgz", "integrity": "sha512-vDCeMXvic/LU0KFIUjpC3RiSTIkkvESsEfbVHiHH0YINfl8HnEqR5rj+L8+phsCeVg2+LmYwYxd5NRz4PHxt5g==", + "license": "Apache-2.0", "dependencies": { "@aws-sdk/types": "3.609.0", "@smithy/node-config-provider": "^3.1.4", @@ -819,14 +839,15 @@ } }, "node_modules/@aws-sdk/signature-v4-multi-region": { - "version": "3.614.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.614.0.tgz", - "integrity": "sha512-6mW3ONW4oLzxrePznYhz7sNT9ji9Am9ufLeV722tbOVS5lArBOZ6E1oPz0uYBhisUPznWKhcLRMggt7vIJWMng==", + "version": "3.635.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.635.0.tgz", + "integrity": "sha512-J6QY4/invOkpogCHjSaDON1hF03viPpOnsrzVuCvJMmclS/iG62R4EY0wq1alYll0YmSdmKlpJwHMWwGtqK63Q==", + "license": "Apache-2.0", "dependencies": { - "@aws-sdk/middleware-sdk-s3": "3.614.0", + "@aws-sdk/middleware-sdk-s3": "3.635.0", "@aws-sdk/types": "3.609.0", - "@smithy/protocol-http": "^4.0.3", - "@smithy/signature-v4": "^3.1.2", + "@smithy/protocol-http": "^4.1.0", + "@smithy/signature-v4": "^4.1.0", "@smithy/types": "^3.3.0", "tslib": "^2.6.2" }, @@ -838,6 +859,7 @@ "version": "3.614.0", "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.614.0.tgz", "integrity": "sha512-okItqyY6L9IHdxqs+Z116y5/nda7rHxLvROxtAJdLavWTYDydxrZstImNgGWTeVdmc0xX2gJCI77UYUTQWnhRw==", + "license": "Apache-2.0", "dependencies": { "@aws-sdk/types": "3.609.0", "@smithy/property-provider": "^3.1.3", @@ -868,6 +890,7 @@ "version": "3.568.0", "resolved": "https://registry.npmjs.org/@aws-sdk/util-arn-parser/-/util-arn-parser-3.568.0.tgz", "integrity": "sha512-XUKJWWo+KOB7fbnPP0+g/o5Ulku/X53t7i/h+sPHr5xxYTJJ9CYnbToo95mzxe7xWvkLrsNtJ8L+MnNn9INs2w==", + "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" }, @@ -876,9 +899,10 @@ } }, "node_modules/@aws-sdk/util-endpoints": { - "version": "3.614.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.614.0.tgz", - "integrity": "sha512-wK2cdrXHH4oz4IomV/yrGkftU9A+ITB6nFL+rxxyO78is2ifHJpFdV4aqk4LSkXYPi6CXWNru/Dqc7yiKXgJPw==", + "version": "3.645.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.645.0.tgz", + "integrity": "sha512-Oe+xaU4ic4PB1k3pb5VTC1/MWES13IlgpaQw01bVHGfwP6Yv6zZOxizRzca2Y3E+AyR+nKD7vXtHRY+w3bi4bg==", + "license": "Apache-2.0", "dependencies": { "@aws-sdk/types": "3.609.0", "@smithy/types": "^3.3.0", @@ -904,6 +928,7 @@ "version": "3.609.0", "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.609.0.tgz", "integrity": "sha512-fojPU+mNahzQ0YHYBsx0ZIhmMA96H+ZIZ665ObU9tl+SGdbLneVZVikGve+NmHTQwHzwkFsZYYnVKAkreJLAtA==", + "license": "Apache-2.0", "dependencies": { "@aws-sdk/types": "3.609.0", "@smithy/types": "^3.3.0", @@ -915,6 +940,7 @@ "version": "3.614.0", "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.614.0.tgz", "integrity": "sha512-15ElZT88peoHnq5TEoEtZwoXTXRxNrk60TZNdpl/TUBJ5oNJ9Dqb5Z4ryb8ofN6nm9aFf59GVAerFDz8iUoHBA==", + "license": "Apache-2.0", "dependencies": { "@aws-sdk/types": "3.609.0", "@smithy/node-config-provider": "^3.1.4", @@ -945,6 +971,73 @@ "node": ">=16.0.0" } }, + "node_modules/@chialab/esbuild-plugin-meta-url": { + "version": "0.18.2", + "resolved": "https://registry.npmjs.org/@chialab/esbuild-plugin-meta-url/-/esbuild-plugin-meta-url-0.18.2.tgz", + "integrity": "sha512-uIRIdLvYnw5mLrTRXY0BTgeZx6ANL2/OHkWFl8FaiTYNb7cyXmwEDRE1mh6kBXPRPtGuqv6XSpNX+koEkElu4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@chialab/esbuild-rna": "^0.18.1", + "@chialab/estransform": "^0.18.1", + "mime-types": "^2.1.35" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@chialab/esbuild-plugin-worker": { + "version": "0.18.1", + "resolved": "https://registry.npmjs.org/@chialab/esbuild-plugin-worker/-/esbuild-plugin-worker-0.18.1.tgz", + "integrity": "sha512-FCpdhMQkrwBejY+uWo3xLdqHhUK3hbn0ICedyqo97hzRX98ErB2fhRq4LEEPMEaiplF2se2ToYTQaoxHDpkouw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@chialab/esbuild-plugin-meta-url": "^0.18.2", + "@chialab/esbuild-rna": "^0.18.0", + "@chialab/estransform": "^0.18.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@chialab/esbuild-rna": { + "version": "0.18.2", + "resolved": "https://registry.npmjs.org/@chialab/esbuild-rna/-/esbuild-rna-0.18.2.tgz", + "integrity": "sha512-ckzskez7bxstVQ4c5cxbx0DRP2teldzrcSGQl2KPh1VJGdO2ZmRrb6vNkBBD5K3dx9tgTyvskWp4dV+Fbg07Ag==", + "dev": true, + "license": "MIT", + "dependencies": { + "@chialab/estransform": "^0.18.0", + "@chialab/node-resolve": "^0.18.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@chialab/estransform": { + "version": "0.18.1", + "resolved": "https://registry.npmjs.org/@chialab/estransform/-/estransform-0.18.1.tgz", + "integrity": "sha512-W/WmjpQL2hndD0/XfR0FcPBAUj+aLNeoAVehOjV/Q9bSnioz0GVSAXXhzp59S33ZynxJBBfn8DNiMTVNJmk4Aw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@parcel/source-map": "^2.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@chialab/node-resolve": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/@chialab/node-resolve/-/node-resolve-0.18.0.tgz", + "integrity": "sha512-eV1m70Qn9pLY9xwFmZ2FlcOzwiaUywsJ7NB/ud8VB7DouvCQtIHkQ3Om7uPX0ojXGEG1LCyO96kZkvbNTxNu0Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/@codemirror/state": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.1.0.tgz", @@ -965,12 +1058,13 @@ } }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.23.0.tgz", - "integrity": "sha512-3sG8Zwa5fMcA9bgqB8AfWPQ+HFke6uD3h1s3RIwUNK8EG7a4buxvuFTs3j1IMs2NXAk9F30C/FF4vxRgQCcmoQ==", + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.23.1.tgz", + "integrity": "sha512-6VhYk1diRqrhBAqpJEdjASR/+WVRtfjpqKuNw11cLiaWpAT/Uu+nokB+UJnevzy/P9C/ty6AOe0dwueMrGh/iQ==", "cpu": [ "ppc64" ], + "license": "MIT", "optional": true, "os": [ "aix" @@ -980,12 +1074,13 @@ } }, "node_modules/@esbuild/android-arm": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.23.0.tgz", - "integrity": "sha512-+KuOHTKKyIKgEEqKbGTK8W7mPp+hKinbMBeEnNzjJGyFcWsfrXjSTNluJHCY1RqhxFurdD8uNXQDei7qDlR6+g==", + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.23.1.tgz", + "integrity": "sha512-uz6/tEy2IFm9RYOyvKl88zdzZfwEfKZmnX9Cj1BHjeSGNuGLuMD1kR8y5bteYmwqKm1tj8m4cb/aKEorr6fHWQ==", "cpu": [ "arm" ], + "license": "MIT", "optional": true, "os": [ "android" @@ -995,12 +1090,13 @@ } }, "node_modules/@esbuild/android-arm64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.23.0.tgz", - "integrity": "sha512-EuHFUYkAVfU4qBdyivULuu03FhJO4IJN9PGuABGrFy4vUuzk91P2d+npxHcFdpUnfYKy0PuV+n6bKIpHOB3prQ==", + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.23.1.tgz", + "integrity": "sha512-xw50ipykXcLstLeWH7WRdQuysJqejuAGPd30vd1i5zSyKK3WE+ijzHmLKxdiCMtH1pHz78rOg0BKSYOSB/2Khw==", "cpu": [ "arm64" ], + "license": "MIT", "optional": true, "os": [ "android" @@ -1010,12 +1106,13 @@ } }, "node_modules/@esbuild/android-x64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.23.0.tgz", - "integrity": "sha512-WRrmKidLoKDl56LsbBMhzTTBxrsVwTKdNbKDalbEZr0tcsBgCLbEtoNthOW6PX942YiYq8HzEnb4yWQMLQuipQ==", + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.23.1.tgz", + "integrity": "sha512-nlN9B69St9BwUoB+jkyU090bru8L0NA3yFvAd7k8dNsVH8bi9a8cUAUSEcEEgTp2z3dbEDGJGfP6VUnkQnlReg==", "cpu": [ "x64" ], + "license": "MIT", "optional": true, "os": [ "android" @@ -1025,12 +1122,13 @@ } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.23.0.tgz", - "integrity": "sha512-YLntie/IdS31H54Ogdn+v50NuoWF5BDkEUFpiOChVa9UnKpftgwzZRrI4J132ETIi+D8n6xh9IviFV3eXdxfow==", + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.23.1.tgz", + "integrity": "sha512-YsS2e3Wtgnw7Wq53XXBLcV6JhRsEq8hkfg91ESVadIrzr9wO6jJDMZnCQbHm1Guc5t/CdDiFSSfWP58FNuvT3Q==", "cpu": [ "arm64" ], + "license": "MIT", "optional": true, "os": [ "darwin" @@ -1040,12 +1138,13 @@ } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.23.0.tgz", - "integrity": "sha512-IMQ6eme4AfznElesHUPDZ+teuGwoRmVuuixu7sv92ZkdQcPbsNHzutd+rAfaBKo8YK3IrBEi9SLLKWJdEvJniQ==", + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.23.1.tgz", + "integrity": "sha512-aClqdgTDVPSEGgoCS8QDG37Gu8yc9lTHNAQlsztQ6ENetKEO//b8y31MMu2ZaPbn4kVsIABzVLXYLhCGekGDqw==", "cpu": [ "x64" ], + "license": "MIT", "optional": true, "os": [ "darwin" @@ -1055,12 +1154,13 @@ } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.23.0.tgz", - "integrity": "sha512-0muYWCng5vqaxobq6LB3YNtevDFSAZGlgtLoAc81PjUfiFz36n4KMpwhtAd4he8ToSI3TGyuhyx5xmiWNYZFyw==", + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.23.1.tgz", + "integrity": "sha512-h1k6yS8/pN/NHlMl5+v4XPfikhJulk4G+tKGFIOwURBSFzE8bixw1ebjluLOjfwtLqY0kewfjLSrO6tN2MgIhA==", "cpu": [ "arm64" ], + "license": "MIT", "optional": true, "os": [ "freebsd" @@ -1070,12 +1170,13 @@ } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.23.0.tgz", - "integrity": "sha512-XKDVu8IsD0/q3foBzsXGt/KjD/yTKBCIwOHE1XwiXmrRwrX6Hbnd5Eqn/WvDekddK21tfszBSrE/WMaZh+1buQ==", + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.23.1.tgz", + "integrity": "sha512-lK1eJeyk1ZX8UklqFd/3A60UuZ/6UVfGT2LuGo3Wp4/z7eRTRYY+0xOu2kpClP+vMTi9wKOfXi2vjUpO1Ro76g==", "cpu": [ "x64" ], + "license": "MIT", "optional": true, "os": [ "freebsd" @@ -1085,12 +1186,13 @@ } }, "node_modules/@esbuild/linux-arm": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.23.0.tgz", - "integrity": "sha512-SEELSTEtOFu5LPykzA395Mc+54RMg1EUgXP+iw2SJ72+ooMwVsgfuwXo5Fn0wXNgWZsTVHwY2cg4Vi/bOD88qw==", + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.23.1.tgz", + "integrity": "sha512-CXXkzgn+dXAPs3WBwE+Kvnrf4WECwBdfjfeYHpMeVxWE0EceB6vhWGShs6wi0IYEqMSIzdOF1XjQ/Mkm5d7ZdQ==", "cpu": [ "arm" ], + "license": "MIT", "optional": true, "os": [ "linux" @@ -1100,12 +1202,13 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.23.0.tgz", - "integrity": "sha512-j1t5iG8jE7BhonbsEg5d9qOYcVZv/Rv6tghaXM/Ug9xahM0nX/H2gfu6X6z11QRTMT6+aywOMA8TDkhPo8aCGw==", + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.23.1.tgz", + "integrity": "sha512-/93bf2yxencYDnItMYV/v116zff6UyTjo4EtEQjUBeGiVpMmffDNUyD9UN2zV+V3LRV3/on4xdZ26NKzn6754g==", "cpu": [ "arm64" ], + "license": "MIT", "optional": true, "os": [ "linux" @@ -1115,12 +1218,13 @@ } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.23.0.tgz", - "integrity": "sha512-P7O5Tkh2NbgIm2R6x1zGJJsnacDzTFcRWZyTTMgFdVit6E98LTxO+v8LCCLWRvPrjdzXHx9FEOA8oAZPyApWUA==", + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.23.1.tgz", + "integrity": "sha512-VTN4EuOHwXEkXzX5nTvVY4s7E/Krz7COC8xkftbbKRYAl96vPiUssGkeMELQMOnLOJ8k3BY1+ZY52tttZnHcXQ==", "cpu": [ "ia32" ], + "license": "MIT", "optional": true, "os": [ "linux" @@ -1130,12 +1234,13 @@ } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.23.0.tgz", - "integrity": "sha512-InQwepswq6urikQiIC/kkx412fqUZudBO4SYKu0N+tGhXRWUqAx+Q+341tFV6QdBifpjYgUndV1hhMq3WeJi7A==", + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.23.1.tgz", + "integrity": "sha512-Vx09LzEoBa5zDnieH8LSMRToj7ir/Jeq0Gu6qJ/1GcBq9GkfoEAoXvLiW1U9J1qE/Y/Oyaq33w5p2ZWrNNHNEw==", "cpu": [ "loong64" ], + "license": "MIT", "optional": true, "os": [ "linux" @@ -1145,12 +1250,13 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.23.0.tgz", - "integrity": "sha512-J9rflLtqdYrxHv2FqXE2i1ELgNjT+JFURt/uDMoPQLcjWQA5wDKgQA4t/dTqGa88ZVECKaD0TctwsUfHbVoi4w==", + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.23.1.tgz", + "integrity": "sha512-nrFzzMQ7W4WRLNUOU5dlWAqa6yVeI0P78WKGUo7lg2HShq/yx+UYkeNSE0SSfSure0SqgnsxPvmAUu/vu0E+3Q==", "cpu": [ "mips64el" ], + "license": "MIT", "optional": true, "os": [ "linux" @@ -1160,12 +1266,13 @@ } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.23.0.tgz", - "integrity": "sha512-cShCXtEOVc5GxU0fM+dsFD10qZ5UpcQ8AM22bYj0u/yaAykWnqXJDpd77ublcX6vdDsWLuweeuSNZk4yUxZwtw==", + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.23.1.tgz", + "integrity": "sha512-dKN8fgVqd0vUIjxuJI6P/9SSSe/mB9rvA98CSH2sJnlZ/OCZWO1DJvxj8jvKTfYUdGfcq2dDxoKaC6bHuTlgcw==", "cpu": [ "ppc64" ], + "license": "MIT", "optional": true, "os": [ "linux" @@ -1175,12 +1282,13 @@ } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.23.0.tgz", - "integrity": "sha512-HEtaN7Y5UB4tZPeQmgz/UhzoEyYftbMXrBCUjINGjh3uil+rB/QzzpMshz3cNUxqXN7Vr93zzVtpIDL99t9aRw==", + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.23.1.tgz", + "integrity": "sha512-5AV4Pzp80fhHL83JM6LoA6pTQVWgB1HovMBsLQ9OZWLDqVY8MVobBXNSmAJi//Csh6tcY7e7Lny2Hg1tElMjIA==", "cpu": [ "riscv64" ], + "license": "MIT", "optional": true, "os": [ "linux" @@ -1190,12 +1298,13 @@ } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.23.0.tgz", - "integrity": "sha512-WDi3+NVAuyjg/Wxi+o5KPqRbZY0QhI9TjrEEm+8dmpY9Xir8+HE/HNx2JoLckhKbFopW0RdO2D72w8trZOV+Wg==", + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.23.1.tgz", + "integrity": "sha512-9ygs73tuFCe6f6m/Tb+9LtYxWR4c9yg7zjt2cYkjDbDpV/xVn+68cQxMXCjUpYwEkze2RcU/rMnfIXNRFmSoDw==", "cpu": [ "s390x" ], + "license": "MIT", "optional": true, "os": [ "linux" @@ -1205,12 +1314,13 @@ } }, "node_modules/@esbuild/linux-x64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.23.0.tgz", - "integrity": "sha512-a3pMQhUEJkITgAw6e0bWA+F+vFtCciMjW/LPtoj99MhVt+Mfb6bbL9hu2wmTZgNd994qTAEw+U/r6k3qHWWaOQ==", + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.23.1.tgz", + "integrity": "sha512-EV6+ovTsEXCPAp58g2dD68LxoP/wK5pRvgy0J/HxPGB009omFPv3Yet0HiaqvrIrgPTBuC6wCH1LTOY91EO5hQ==", "cpu": [ "x64" ], + "license": "MIT", "optional": true, "os": [ "linux" @@ -1220,12 +1330,13 @@ } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.23.0.tgz", - "integrity": "sha512-cRK+YDem7lFTs2Q5nEv/HHc4LnrfBCbH5+JHu6wm2eP+d8OZNoSMYgPZJq78vqQ9g+9+nMuIsAO7skzphRXHyw==", + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.23.1.tgz", + "integrity": "sha512-aevEkCNu7KlPRpYLjwmdcuNz6bDFiE7Z8XC4CPqExjTvrHugh28QzUXVOZtiYghciKUacNktqxdpymplil1beA==", "cpu": [ "x64" ], + "license": "MIT", "optional": true, "os": [ "netbsd" @@ -1235,12 +1346,13 @@ } }, "node_modules/@esbuild/openbsd-arm64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.23.0.tgz", - "integrity": "sha512-suXjq53gERueVWu0OKxzWqk7NxiUWSUlrxoZK7usiF50C6ipColGR5qie2496iKGYNLhDZkPxBI3erbnYkU0rQ==", + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.23.1.tgz", + "integrity": "sha512-3x37szhLexNA4bXhLrCC/LImN/YtWis6WXr1VESlfVtVeoFJBRINPJ3f0a/6LV8zpikqoUg4hyXw0sFBt5Cr+Q==", "cpu": [ "arm64" ], + "license": "MIT", "optional": true, "os": [ "openbsd" @@ -1250,12 +1362,13 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.23.0.tgz", - "integrity": "sha512-6p3nHpby0DM/v15IFKMjAaayFhqnXV52aEmv1whZHX56pdkK+MEaLoQWj+H42ssFarP1PcomVhbsR4pkz09qBg==", + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.23.1.tgz", + "integrity": "sha512-aY2gMmKmPhxfU+0EdnN+XNtGbjfQgwZj43k8G3fyrDM/UdZww6xrWxmDkuz2eCZchqVeABjV5BpildOrUbBTqA==", "cpu": [ "x64" ], + "license": "MIT", "optional": true, "os": [ "openbsd" @@ -1265,12 +1378,13 @@ } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.23.0.tgz", - "integrity": "sha512-BFelBGfrBwk6LVrmFzCq1u1dZbG4zy/Kp93w2+y83Q5UGYF1d8sCzeLI9NXjKyujjBBniQa8R8PzLFAUrSM9OA==", + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.23.1.tgz", + "integrity": "sha512-RBRT2gqEl0IKQABT4XTj78tpk9v7ehp+mazn2HbUeZl1YMdaGAQqhapjGTCe7uw7y0frDi4gS0uHzhvpFuI1sA==", "cpu": [ "x64" ], + "license": "MIT", "optional": true, "os": [ "sunos" @@ -1280,12 +1394,13 @@ } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.23.0.tgz", - "integrity": "sha512-lY6AC8p4Cnb7xYHuIxQ6iYPe6MfO2CC43XXKo9nBXDb35krYt7KGhQnOkRGar5psxYkircpCqfbNDB4uJbS2jQ==", + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.23.1.tgz", + "integrity": "sha512-4O+gPR5rEBe2FpKOVyiJ7wNDPA8nGzDuJ6gN4okSA1gEOYZ67N8JPk58tkWtdtPeLz7lBnY6I5L3jdsr3S+A6A==", "cpu": [ "arm64" ], + "license": "MIT", "optional": true, "os": [ "win32" @@ -1295,12 +1410,13 @@ } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.23.0.tgz", - "integrity": "sha512-7L1bHlOTcO4ByvI7OXVI5pNN6HSu6pUQq9yodga8izeuB1KcT2UkHaH6118QJwopExPn0rMHIseCTx1CRo/uNA==", + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.23.1.tgz", + "integrity": "sha512-BcaL0Vn6QwCwre3Y717nVHZbAa4UBEigzFm6VdsVdT/MbZ38xoj1X9HPkZhbmaBGUD1W8vxAfffbDe8bA6AKnQ==", "cpu": [ "ia32" ], + "license": "MIT", "optional": true, "os": [ "win32" @@ -1310,12 +1426,13 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.23.0.tgz", - "integrity": "sha512-Arm+WgUFLUATuoxCJcahGuk6Yj9Pzxd6l11Zb/2aAuv5kWWvvfhLFo2fni4uSK5vzlUdCGZ/BdV5tH8klj8p8g==", + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.23.1.tgz", + "integrity": "sha512-BHpFFeslkWrXWyUPnbKm+xYYVYruCinGcftSBaa8zoF9hZO4BcSCFUvHVTtzpIY6YzUnYtuEhZ+C9iEXjxnasg==", "cpu": [ "x64" ], + "license": "MIT", "optional": true, "os": [ "win32" @@ -1550,6 +1667,26 @@ "node": ">= 8" } }, + "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/@rtsao/scc": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", + "integrity": "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==", + "dev": true, + "license": "MIT" + }, "node_modules/@smithy/abort-controller": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-3.1.1.tgz", @@ -1583,6 +1720,7 @@ "version": "3.0.5", "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-3.0.5.tgz", "integrity": "sha512-SkW5LxfkSI1bUC74OtfBbdz+grQXYiPYolyu8VfpLIjEoN/sHVBlLeGXMQ1vX4ejkgfv6sxVbQJ32yF2cl1veA==", + "license": "Apache-2.0", "dependencies": { "@smithy/node-config-provider": "^3.1.4", "@smithy/types": "^3.3.0", @@ -1595,17 +1733,20 @@ } }, "node_modules/@smithy/core": { - "version": "2.2.6", - "resolved": "https://registry.npmjs.org/@smithy/core/-/core-2.2.6.tgz", - "integrity": "sha512-tBbVIv/ui7/lLTKayYJJvi8JLVL2SwOQTbNFEOrvzSE3ktByvsa1erwBOnAMo8N5Vu30g7lN4lLStrU75oDGuw==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@smithy/core/-/core-2.4.0.tgz", + "integrity": "sha512-cHXq+FneIF/KJbt4q4pjN186+Jf4ZB0ZOqEaZMBhT79srEyGDDBV31NqBRBjazz8ppQ1bJbDJMY9ba5wKFV36w==", + "license": "Apache-2.0", "dependencies": { - "@smithy/middleware-endpoint": "^3.0.5", - "@smithy/middleware-retry": "^3.0.9", + "@smithy/middleware-endpoint": "^3.1.0", + "@smithy/middleware-retry": "^3.0.15", "@smithy/middleware-serde": "^3.0.3", - "@smithy/protocol-http": "^4.0.3", - "@smithy/smithy-client": "^3.1.7", + "@smithy/protocol-http": "^4.1.0", + "@smithy/smithy-client": "^3.2.0", "@smithy/types": "^3.3.0", + "@smithy/util-body-length-browser": "^3.0.0", "@smithy/util-middleware": "^3.0.3", + "@smithy/util-utf8": "^3.0.0", "tslib": "^2.6.2" }, "engines": { @@ -1613,9 +1754,10 @@ } }, "node_modules/@smithy/credential-provider-imds": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-3.1.4.tgz", - "integrity": "sha512-NKyH01m97Xa5xf3pB2QOF3lnuE8RIK0hTVNU5zvZAwZU8uspYO4DHQVlK+Y5gwSrujTfHvbfd1D9UFJAc0iYKQ==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-3.2.0.tgz", + "integrity": "sha512-0SCIzgd8LYZ9EJxUjLXBmEKSZR/P/w6l7Rz/pab9culE/RWuqelAKGJvn5qUOl8BgX8Yj5HWM50A5hiB/RzsgA==", + "license": "Apache-2.0", "dependencies": { "@smithy/node-config-provider": "^3.1.4", "@smithy/property-provider": "^3.1.3", @@ -1631,6 +1773,7 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/@smithy/eventstream-codec/-/eventstream-codec-3.1.2.tgz", "integrity": "sha512-0mBcu49JWt4MXhrhRAlxASNy0IjDRFU+aWNDRal9OtUJvJNiwDuyKMUONSOjLjSCeGwZaE0wOErdqULer8r7yw==", + "license": "Apache-2.0", "dependencies": { "@aws-crypto/crc32": "5.2.0", "@smithy/types": "^3.3.0", @@ -1639,11 +1782,12 @@ } }, "node_modules/@smithy/eventstream-serde-browser": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-browser/-/eventstream-serde-browser-3.0.4.tgz", - "integrity": "sha512-Eo4anLZX6ltGJTZ5yJMc80gZPYYwBn44g0h7oFq6et+TYr5dUsTpIcDbz2evsOKIZhZ7zBoFWHtBXQ4QQeb5xA==", + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-browser/-/eventstream-serde-browser-3.0.6.tgz", + "integrity": "sha512-2hM54UWQUOrki4BtsUI1WzmD13/SeaqT/AB3EUJKbcver/WgKNaiJ5y5F5XXuVe6UekffVzuUDrBZVAA3AWRpQ==", + "license": "Apache-2.0", "dependencies": { - "@smithy/eventstream-serde-universal": "^3.0.4", + "@smithy/eventstream-serde-universal": "^3.0.5", "@smithy/types": "^3.3.0", "tslib": "^2.6.2" }, @@ -1664,11 +1808,12 @@ } }, "node_modules/@smithy/eventstream-serde-node": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-node/-/eventstream-serde-node-3.0.4.tgz", - "integrity": "sha512-mjlG0OzGAYuUpdUpflfb9zyLrBGgmQmrobNT8b42ZTsGv/J03+t24uhhtVEKG/b2jFtPIHF74Bq+VUtbzEKOKg==", + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-node/-/eventstream-serde-node-3.0.5.tgz", + "integrity": "sha512-+upXvnHNyZP095s11jF5dhGw/Ihzqwl5G+/KtMnoQOpdfC3B5HYCcDVG9EmgkhJMXJlM64PyN5gjJl0uXFQehQ==", + "license": "Apache-2.0", "dependencies": { - "@smithy/eventstream-serde-universal": "^3.0.4", + "@smithy/eventstream-serde-universal": "^3.0.5", "@smithy/types": "^3.3.0", "tslib": "^2.6.2" }, @@ -1677,9 +1822,10 @@ } }, "node_modules/@smithy/eventstream-serde-universal": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-universal/-/eventstream-serde-universal-3.0.4.tgz", - "integrity": "sha512-Od9dv8zh3PgOD7Vj4T3HSuox16n0VG8jJIM2gvKASL6aCtcS8CfHZDWe1Ik3ZXW6xBouU+45Q5wgoliWDZiJ0A==", + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-universal/-/eventstream-serde-universal-3.0.5.tgz", + "integrity": "sha512-5u/nXbyoh1s4QxrvNre9V6vfyoLWuiVvvd5TlZjGThIikc3G+uNiG9uOTCWweSRjv1asdDIWK7nOmN7le4RYHQ==", + "license": "Apache-2.0", "dependencies": { "@smithy/eventstream-codec": "^3.1.2", "@smithy/types": "^3.3.0", @@ -1690,11 +1836,12 @@ } }, "node_modules/@smithy/fetch-http-handler": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-3.2.1.tgz", - "integrity": "sha512-0w0bgUvZmfa0vHN8a+moByhCJT07WN6AHKEhFSOLsDpnszm+5dLVv5utGaqbhOrZ/aF5x3xuPMs/oMCd+4O5xg==", + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-3.2.4.tgz", + "integrity": "sha512-kBprh5Gs5h7ug4nBWZi1FZthdqSM+T7zMmsZxx0IBvWUn7dK3diz2SHn7Bs4dQGFDk8plDv375gzenDoNwrXjg==", + "license": "Apache-2.0", "dependencies": { - "@smithy/protocol-http": "^4.0.3", + "@smithy/protocol-http": "^4.1.0", "@smithy/querystring-builder": "^3.0.3", "@smithy/types": "^3.3.0", "@smithy/util-base64": "^3.0.0", @@ -1716,6 +1863,7 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-3.0.3.tgz", "integrity": "sha512-2ctBXpPMG+B3BtWSGNnKELJ7SH9e4TNefJS0cd2eSkOOROeBnnVBnAy9LtJ8tY4vUEoe55N4CNPxzbWvR39iBw==", + "license": "Apache-2.0", "dependencies": { "@smithy/types": "^3.3.0", "@smithy/util-buffer-from": "^3.0.0", @@ -1743,6 +1891,7 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-3.0.3.tgz", "integrity": "sha512-ID1eL/zpDULmHJbflb864k72/SNOZCADRc9i7Exq3RUNJw6raWUSlFEQ+3PX3EYs++bTxZB2dE9mEHTQLv61tw==", + "license": "Apache-2.0", "dependencies": { "@smithy/types": "^3.3.0", "tslib": "^2.6.2" @@ -1770,11 +1919,12 @@ } }, "node_modules/@smithy/middleware-content-length": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-3.0.3.tgz", - "integrity": "sha512-Dbz2bzexReYIQDWMr+gZhpwBetNXzbhnEMhYKA6urqmojO14CsXjnsoPYO8UL/xxcawn8ZsuVU61ElkLSltIUQ==", + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-3.0.5.tgz", + "integrity": "sha512-ILEzC2eyxx6ncej3zZSwMpB5RJ0zuqH7eMptxC4KN3f+v9bqT8ohssKbhNR78k/2tWW+KS5Spw+tbPF4Ejyqvw==", + "license": "Apache-2.0", "dependencies": { - "@smithy/protocol-http": "^4.0.3", + "@smithy/protocol-http": "^4.1.0", "@smithy/types": "^3.3.0", "tslib": "^2.6.2" }, @@ -1783,9 +1933,10 @@ } }, "node_modules/@smithy/middleware-endpoint": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-3.0.5.tgz", - "integrity": "sha512-V4acqqrh5tDxUEGVTOgf2lYMZqPQsoGntCrjrJZEeBzEzDry2d2vcI1QCXhGltXPPY+BMc6eksZMguA9fIY8vA==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-3.1.0.tgz", + "integrity": "sha512-5y5aiKCEwg9TDPB4yFE7H6tYvGFf1OJHNczeY10/EFF8Ir8jZbNntQJxMWNfeQjC1mxPsaQ6mR9cvQbf+0YeMw==", + "license": "Apache-2.0", "dependencies": { "@smithy/middleware-serde": "^3.0.3", "@smithy/node-config-provider": "^3.1.4", @@ -1800,14 +1951,15 @@ } }, "node_modules/@smithy/middleware-retry": { - "version": "3.0.9", - "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-3.0.9.tgz", - "integrity": "sha512-Mrv9omExU1gA7Y0VEJG2LieGfPYtwwcEiOnVGZ54a37NEMr66TJ0glFslOJFuKWG6izg5DpKIUmDV9rRxjm47Q==", + "version": "3.0.15", + "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-3.0.15.tgz", + "integrity": "sha512-iTMedvNt1ApdvkaoE8aSDuwaoc+BhvHqttbA/FO4Ty+y/S5hW6Ci/CTScG7vam4RYJWZxdTElc3MEfHRVH6cgQ==", + "license": "Apache-2.0", "dependencies": { "@smithy/node-config-provider": "^3.1.4", - "@smithy/protocol-http": "^4.0.3", + "@smithy/protocol-http": "^4.1.0", "@smithy/service-error-classification": "^3.0.3", - "@smithy/smithy-client": "^3.1.7", + "@smithy/smithy-client": "^3.2.0", "@smithy/types": "^3.3.0", "@smithy/util-middleware": "^3.0.3", "@smithy/util-retry": "^3.0.3", @@ -1826,6 +1978,7 @@ "https://github.com/sponsors/broofa", "https://github.com/sponsors/ctavan" ], + "license": "MIT", "bin": { "uuid": "dist/bin/uuid" } @@ -1834,6 +1987,7 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-3.0.3.tgz", "integrity": "sha512-puUbyJQBcg9eSErFXjKNiGILJGtiqmuuNKEYNYfUD57fUl4i9+mfmThtQhvFXU0hCVG0iEJhvQUipUf+/SsFdA==", + "license": "Apache-2.0", "dependencies": { "@smithy/types": "^3.3.0", "tslib": "^2.6.2" @@ -1846,6 +2000,7 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-3.0.3.tgz", "integrity": "sha512-r4klY9nFudB0r9UdSMaGSyjyQK5adUyPnQN/ZM6M75phTxOdnc/AhpvGD1fQUvgmqjQEBGCwpnPbDm8pH5PapA==", + "license": "Apache-2.0", "dependencies": { "@smithy/types": "^3.3.0", "tslib": "^2.6.2" @@ -1858,6 +2013,7 @@ "version": "3.1.4", "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-3.1.4.tgz", "integrity": "sha512-YvnElQy8HR4vDcAjoy7Xkx9YT8xZP4cBXcbJSgm/kxmiQu08DwUwj8rkGnyoJTpfl/3xYHH+d8zE+eHqoDCSdQ==", + "license": "Apache-2.0", "dependencies": { "@smithy/property-provider": "^3.1.3", "@smithy/shared-ini-file-loader": "^3.1.4", @@ -1869,12 +2025,13 @@ } }, "node_modules/@smithy/node-http-handler": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-3.1.2.tgz", - "integrity": "sha512-Td3rUNI7qqtoSLTsJBtsyfoG4cF/XMFmJr6Z2dX8QNzIi6tIW6YmuyFml8mJ2cNpyWNqITKbROMOFrvQjmsOvw==", + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-3.1.4.tgz", + "integrity": "sha512-+UmxgixgOr/yLsUxcEKGH0fMNVteJFGkmRltYFHnBMlogyFdpzn2CwqWmxOrfJELhV34v0WSlaqG1UtE1uXlJg==", + "license": "Apache-2.0", "dependencies": { "@smithy/abort-controller": "^3.1.1", - "@smithy/protocol-http": "^4.0.3", + "@smithy/protocol-http": "^4.1.0", "@smithy/querystring-builder": "^3.0.3", "@smithy/types": "^3.3.0", "tslib": "^2.6.2" @@ -1887,6 +2044,7 @@ "version": "3.1.3", "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-3.1.3.tgz", "integrity": "sha512-zahyOVR9Q4PEoguJ/NrFP4O7SMAfYO1HLhB18M+q+Z4KFd4V2obiMnlVoUFzFLSPeVt1POyNWneHHrZaTMoc/g==", + "license": "Apache-2.0", "dependencies": { "@smithy/types": "^3.3.0", "tslib": "^2.6.2" @@ -1896,9 +2054,10 @@ } }, "node_modules/@smithy/protocol-http": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-4.0.3.tgz", - "integrity": "sha512-x5jmrCWwQlx+Zv4jAtc33ijJ+vqqYN+c/ZkrnpvEe/uDas7AT7A/4Rc2CdfxgWv4WFGmEqODIrrUToPN6DDkGw==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-4.1.0.tgz", + "integrity": "sha512-dPVoHYQ2wcHooGXg3LQisa1hH0e4y0pAddPMeeUPipI1tEOqL6A4N0/G7abeq+K8wrwSgjk4C0wnD1XZpJm5aA==", + "license": "Apache-2.0", "dependencies": { "@smithy/types": "^3.3.0", "tslib": "^2.6.2" @@ -1924,6 +2083,7 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-3.0.3.tgz", "integrity": "sha512-zahM1lQv2YjmznnfQsWbYojFe55l0SLG/988brlLv1i8z3dubloLF+75ATRsqPBboUXsW6I9CPGE5rQgLfY0vQ==", + "license": "Apache-2.0", "dependencies": { "@smithy/types": "^3.3.0", "tslib": "^2.6.2" @@ -1936,6 +2096,7 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-3.0.3.tgz", "integrity": "sha512-Jn39sSl8cim/VlkLsUhRFq/dKDnRUFlfRkvhOJaUbLBXUsLRLNf9WaxDv/z9BjuQ3A6k/qE8af1lsqcwm7+DaQ==", + "license": "Apache-2.0", "dependencies": { "@smithy/types": "^3.3.0" }, @@ -1947,6 +2108,7 @@ "version": "3.1.4", "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-3.1.4.tgz", "integrity": "sha512-qMxS4hBGB8FY2GQqshcRUy1K6k8aBWP5vwm8qKkCT3A9K2dawUwOIJfqh9Yste/Bl0J2lzosVyrXDj68kLcHXQ==", + "license": "Apache-2.0", "dependencies": { "@smithy/types": "^3.3.0", "tslib": "^2.6.2" @@ -1956,11 +2118,13 @@ } }, "node_modules/@smithy/signature-v4": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-3.1.2.tgz", - "integrity": "sha512-3BcPylEsYtD0esM4Hoyml/+s7WP2LFhcM3J2AGdcL2vx9O60TtfpDOL72gjb4lU8NeRPeKAwR77YNyyGvMbuEA==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-4.1.0.tgz", + "integrity": "sha512-aRryp2XNZeRcOtuJoxjydO6QTaVhxx/vjaR+gx7ZjaFgrgPRyZ3HCTbfwqYj6ZWEBHkCSUfcaymKPURaByukag==", + "license": "Apache-2.0", "dependencies": { "@smithy/is-array-buffer": "^3.0.0", + "@smithy/protocol-http": "^4.1.0", "@smithy/types": "^3.3.0", "@smithy/util-hex-encoding": "^3.0.0", "@smithy/util-middleware": "^3.0.3", @@ -1973,15 +2137,16 @@ } }, "node_modules/@smithy/smithy-client": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-3.1.7.tgz", - "integrity": "sha512-nZbJZB0XI3YnaFBWGDBr7kjaew6O0oNYNmopyIz6gKZEbxzrtH7rwvU1GcVxcSFoOwWecLJEe79fxEMljHopFQ==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-3.2.0.tgz", + "integrity": "sha512-pDbtxs8WOhJLJSeaF/eAbPgXg4VVYFlRcL/zoNYA5WbG3wBL06CHtBSg53ppkttDpAJ/hdiede+xApip1CwSLw==", + "license": "Apache-2.0", "dependencies": { - "@smithy/middleware-endpoint": "^3.0.5", + "@smithy/middleware-endpoint": "^3.1.0", "@smithy/middleware-stack": "^3.0.3", - "@smithy/protocol-http": "^4.0.3", + "@smithy/protocol-http": "^4.1.0", "@smithy/types": "^3.3.0", - "@smithy/util-stream": "^3.0.6", + "@smithy/util-stream": "^3.1.3", "tslib": "^2.6.2" }, "engines": { @@ -2003,6 +2168,7 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-3.0.3.tgz", "integrity": "sha512-pw3VtZtX2rg+s6HMs6/+u9+hu6oY6U7IohGhVNnjbgKy86wcIsSZwgHrFR+t67Uyxvp4Xz3p3kGXXIpTNisq8A==", + "license": "Apache-2.0", "dependencies": { "@smithy/querystring-parser": "^3.0.3", "@smithy/types": "^3.3.0", @@ -2026,6 +2192,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-3.0.0.tgz", "integrity": "sha512-cbjJs2A1mLYmqmyVl80uoLTJhAcfzMOyPgjwAYusWKMdLeNtzmMz9YxNl3/jRLoxSS3wkqkf0jwNdtXWtyEBaQ==", + "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" } @@ -2034,6 +2201,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-3.0.0.tgz", "integrity": "sha512-Tj7pZ4bUloNUP6PzwhN7K386tmSmEET9QtQg0TgdNOnxhZvCssHji+oZTUIuzxECRfG8rdm2PMw2WCFs6eIYkA==", + "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" }, @@ -2057,6 +2225,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-3.0.0.tgz", "integrity": "sha512-pbjk4s0fwq3Di/ANL+rCvJMKM5bzAQdE5S/6RL5NXgMExFAi6UgQMPOm5yPaIWPpr+EOXKXRonJ3FoxKf4mCJQ==", + "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" }, @@ -2065,12 +2234,13 @@ } }, "node_modules/@smithy/util-defaults-mode-browser": { - "version": "3.0.9", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-3.0.9.tgz", - "integrity": "sha512-WKPcElz92MAQG09miBdb0GxEH/MwD5GfE8g07WokITq5g6J1ROQfYCKC1wNnkqAGfrSywT7L0rdvvqlBplqiyA==", + "version": "3.0.15", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-3.0.15.tgz", + "integrity": "sha512-FZ4Psa3vjp8kOXcd3HJOiDPBCWtiilLl57r0cnNtq/Ga9RSDrM5ERL6xt+tO43+2af6Pn5Yp92x2n5vPuduNfg==", + "license": "Apache-2.0", "dependencies": { "@smithy/property-provider": "^3.1.3", - "@smithy/smithy-client": "^3.1.7", + "@smithy/smithy-client": "^3.2.0", "@smithy/types": "^3.3.0", "bowser": "^2.11.0", "tslib": "^2.6.2" @@ -2080,15 +2250,16 @@ } }, "node_modules/@smithy/util-defaults-mode-node": { - "version": "3.0.9", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-3.0.9.tgz", - "integrity": "sha512-dQLrUqFxqpf0GvEKEuFdgXcdZwz6oFm752h4d6C7lQz+RLddf761L2r7dSwGWzESMMB3wKj0jL+skRhEGlecjw==", + "version": "3.0.15", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-3.0.15.tgz", + "integrity": "sha512-KSyAAx2q6d0t6f/S4XB2+3+6aQacm3aLMhs9aLMqn18uYGUepbdssfogW5JQZpc6lXNBnp0tEnR5e9CEKmEd7A==", + "license": "Apache-2.0", "dependencies": { "@smithy/config-resolver": "^3.0.5", - "@smithy/credential-provider-imds": "^3.1.4", + "@smithy/credential-provider-imds": "^3.2.0", "@smithy/node-config-provider": "^3.1.4", "@smithy/property-provider": "^3.1.3", - "@smithy/smithy-client": "^3.1.7", + "@smithy/smithy-client": "^3.2.0", "@smithy/types": "^3.3.0", "tslib": "^2.6.2" }, @@ -2100,6 +2271,7 @@ "version": "2.0.5", "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-2.0.5.tgz", "integrity": "sha512-ReQP0BWihIE68OAblC/WQmDD40Gx+QY1Ez8mTdFMXpmjfxSyz2fVQu3A4zXRfQU9sZXtewk3GmhfOHswvX+eNg==", + "license": "Apache-2.0", "dependencies": { "@smithy/node-config-provider": "^3.1.4", "@smithy/types": "^3.3.0", @@ -2113,6 +2285,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-3.0.0.tgz", "integrity": "sha512-eFndh1WEK5YMUYvy3lPlVmYY/fZcQE1D8oSf41Id2vCeIkKJXPcYDCZD+4+xViI6b1XSd7tE+s5AmXzz5ilabQ==", + "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" }, @@ -2124,6 +2297,7 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-3.0.3.tgz", "integrity": "sha512-l+StyYYK/eO3DlVPbU+4Bi06Jjal+PFLSMmlWM1BEwyLxZ3aKkf1ROnoIakfaA7mC6uw3ny7JBkau4Yc+5zfWw==", + "license": "Apache-2.0", "dependencies": { "@smithy/types": "^3.3.0", "tslib": "^2.6.2" @@ -2136,6 +2310,7 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-3.0.3.tgz", "integrity": "sha512-AFw+hjpbtVApzpNDhbjNG5NA3kyoMs7vx0gsgmlJF4s+yz1Zlepde7J58zpIRIsdjc+emhpAITxA88qLkPF26w==", + "license": "Apache-2.0", "dependencies": { "@smithy/service-error-classification": "^3.0.3", "@smithy/types": "^3.3.0", @@ -2146,12 +2321,13 @@ } }, "node_modules/@smithy/util-stream": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-3.0.6.tgz", - "integrity": "sha512-w9i//7egejAIvplX821rPWWgaiY1dxsQUw0hXX7qwa/uZ9U3zplqTQ871jWadkcVB9gFDhkPWYVZf4yfFbZ0xA==", + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-3.1.3.tgz", + "integrity": "sha512-FIv/bRhIlAxC0U7xM1BCnF2aDRPq0UaelqBHkM2lsCp26mcBbgI0tCVTv+jGdsQLUmAMybua/bjDsSu8RQHbmw==", + "license": "Apache-2.0", "dependencies": { - "@smithy/fetch-http-handler": "^3.2.1", - "@smithy/node-http-handler": "^3.1.2", + "@smithy/fetch-http-handler": "^3.2.4", + "@smithy/node-http-handler": "^3.1.4", "@smithy/types": "^3.3.0", "@smithy/util-base64": "^3.0.0", "@smithy/util-buffer-from": "^3.0.0", @@ -2248,12 +2424,13 @@ "dev": true }, "node_modules/@types/node": { - "version": "20.14.10", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.10.tgz", - "integrity": "sha512-MdiXf+nDuMvY0gJKxyfZ7/6UFsETO7mGKF54MVD/ekJS6HdFtpZFBgrh6Pseu64XTb2MLyFPlbW6hj8HYRQNOQ==", + "version": "22.5.4", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.5.4.tgz", + "integrity": "sha512-FDuKUJQm/ju9fT/SeX/6+gBzoPzlVCzfzmGkwKvRHQVxi4BntVbyIwf6a4Xn62mrvndLiml6z/UBXIdEVjQLXg==", "dev": true, + "license": "MIT", "dependencies": { - "undici-types": "~5.26.4" + "undici-types": "~6.19.2" } }, "node_modules/@types/pouchdb": { @@ -2376,10 +2553,11 @@ } }, "node_modules/@types/pouchdb-core": { - "version": "7.0.14", - "resolved": "https://registry.npmjs.org/@types/pouchdb-core/-/pouchdb-core-7.0.14.tgz", - "integrity": "sha512-ec63FAtmc1ForP4oNgLZoPYqjaBrkGTWA7jm+rQ7SEtIpOPpN4NCWF2/JyrhO64jjIzO5qkV/YNEpZlEZFxCcw==", + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/pouchdb-core/-/pouchdb-core-7.0.15.tgz", + "integrity": "sha512-gq1Qbqn9nCaAKRRv6fRHZ4/ER+QYEwSXBZlDQcxwdbPrtZO8EhIn2Bct0AlguaSEdFcABfbaxxyQwFINkNQ9dQ==", "dev": true, + "license": "MIT", "dependencies": { "@types/debug": "*", "@types/pouchdb-find": "*" @@ -2455,31 +2633,32 @@ } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.16.0.tgz", - "integrity": "sha512-py1miT6iQpJcs1BiJjm54AMzeuMPBSPuKPlnT8HlfudbcS5rYeX5jajpLf3mrdRh9dA/Ec2FVUY0ifeVNDIhZw==", + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.4.0.tgz", + "integrity": "sha512-rg8LGdv7ri3oAlenMACk9e+AR4wUV0yrrG+XKsGKOK0EVgeEDqurkXMPILG2836fW4ibokTB5v4b6Z9+GYQDEw==", "dev": true, + "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "7.16.0", - "@typescript-eslint/type-utils": "7.16.0", - "@typescript-eslint/utils": "7.16.0", - "@typescript-eslint/visitor-keys": "7.16.0", + "@typescript-eslint/scope-manager": "8.4.0", + "@typescript-eslint/type-utils": "8.4.0", + "@typescript-eslint/utils": "8.4.0", + "@typescript-eslint/visitor-keys": "8.4.0", "graphemer": "^1.4.0", "ignore": "^5.3.1", "natural-compare": "^1.4.0", "ts-api-utils": "^1.3.0" }, "engines": { - "node": "^18.18.0 || >=20.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^7.0.0", - "eslint": "^8.56.0" + "@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0", + "eslint": "^8.57.0 || ^9.0.0" }, "peerDependenciesMeta": { "typescript": { @@ -2488,26 +2667,27 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.16.0.tgz", - "integrity": "sha512-ar9E+k7CU8rWi2e5ErzQiC93KKEFAXA2Kky0scAlPcxYblLt8+XZuHUZwlyfXILyQa95P6lQg+eZgh/dDs3+Vw==", + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.4.0.tgz", + "integrity": "sha512-NHgWmKSgJk5K9N16GIhQ4jSobBoJwrmURaLErad0qlLjrpP5bECYg+wxVTGlGZmJbU03jj/dfnb6V9bw+5icsA==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { - "@typescript-eslint/scope-manager": "7.16.0", - "@typescript-eslint/types": "7.16.0", - "@typescript-eslint/typescript-estree": "7.16.0", - "@typescript-eslint/visitor-keys": "7.16.0", + "@typescript-eslint/scope-manager": "8.4.0", + "@typescript-eslint/types": "8.4.0", + "@typescript-eslint/typescript-estree": "8.4.0", + "@typescript-eslint/visitor-keys": "8.4.0", "debug": "^4.3.4" }, "engines": { - "node": "^18.18.0 || >=20.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^8.56.0" + "eslint": "^8.57.0 || ^9.0.0" }, "peerDependenciesMeta": { "typescript": { @@ -2516,16 +2696,17 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.16.0.tgz", - "integrity": "sha512-8gVv3kW6n01Q6TrI1cmTZ9YMFi3ucDT7i7aI5lEikk2ebk1AEjrwX8MDTdaX5D7fPXMBLvnsaa0IFTAu+jcfOw==", + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.4.0.tgz", + "integrity": "sha512-n2jFxLeY0JmKfUqy3P70rs6vdoPjHK8P/w+zJcV3fk0b0BwRXC/zxRTEnAsgYT7MwdQDt/ZEbtdzdVC+hcpF0A==", "dev": true, + "license": "MIT", "dependencies": { - "@typescript-eslint/types": "7.16.0", - "@typescript-eslint/visitor-keys": "7.16.0" + "@typescript-eslint/types": "8.4.0", + "@typescript-eslint/visitor-keys": "8.4.0" }, "engines": { - "node": "^18.18.0 || >=20.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", @@ -2533,26 +2714,24 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.16.0.tgz", - "integrity": "sha512-j0fuUswUjDHfqV/UdW6mLtOQQseORqfdmoBNDFOqs9rvNVR2e+cmu6zJu/Ku4SDuqiJko6YnhwcL8x45r8Oqxg==", + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.4.0.tgz", + "integrity": "sha512-pu2PAmNrl9KX6TtirVOrbLPLwDmASpZhK/XU7WvoKoCUkdtq9zF7qQ7gna0GBZFN0hci0vHaSusiL2WpsQk37A==", "dev": true, + "license": "MIT", "dependencies": { - "@typescript-eslint/typescript-estree": "7.16.0", - "@typescript-eslint/utils": "7.16.0", + "@typescript-eslint/typescript-estree": "8.4.0", + "@typescript-eslint/utils": "8.4.0", "debug": "^4.3.4", "ts-api-utils": "^1.3.0" }, "engines": { - "node": "^18.18.0 || >=20.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, - "peerDependencies": { - "eslint": "^8.56.0" - }, "peerDependenciesMeta": { "typescript": { "optional": true @@ -2560,12 +2739,13 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.16.0.tgz", - "integrity": "sha512-fecuH15Y+TzlUutvUl9Cc2XJxqdLr7+93SQIbcZfd4XRGGKoxyljK27b+kxKamjRkU7FYC6RrbSCg0ALcZn/xw==", + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.4.0.tgz", + "integrity": "sha512-T1RB3KQdskh9t3v/qv7niK6P8yvn7ja1mS7QK7XfRVL6wtZ8/mFs/FHf4fKvTA0rKnqnYxl/uHFNbnEt0phgbw==", "dev": true, + "license": "MIT", "engines": { - "node": "^18.18.0 || >=20.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", @@ -2573,22 +2753,23 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.16.0.tgz", - "integrity": "sha512-a5NTvk51ZndFuOLCh5OaJBELYc2O3Zqxfl3Js78VFE1zE46J2AaVuW+rEbVkQznjkmlzWsUI15BG5tQMixzZLw==", + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.4.0.tgz", + "integrity": "sha512-kJ2OIP4dQw5gdI4uXsaxUZHRwWAGpREJ9Zq6D5L0BweyOrWsL6Sz0YcAZGWhvKnH7fm1J5YFE1JrQL0c9dd53A==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { - "@typescript-eslint/types": "7.16.0", - "@typescript-eslint/visitor-keys": "7.16.0", + "@typescript-eslint/types": "8.4.0", + "@typescript-eslint/visitor-keys": "8.4.0", "debug": "^4.3.4", - "globby": "^11.1.0", + "fast-glob": "^3.3.2", "is-glob": "^4.0.3", "minimatch": "^9.0.4", "semver": "^7.6.0", "ts-api-utils": "^1.3.0" }, "engines": { - "node": "^18.18.0 || >=20.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", @@ -2605,6 +2786,7 @@ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", "dev": true, + "license": "ISC", "dependencies": { "brace-expansion": "^2.0.1" }, @@ -2616,38 +2798,40 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.16.0.tgz", - "integrity": "sha512-PqP4kP3hb4r7Jav+NiRCntlVzhxBNWq6ZQ+zQwII1y/G/1gdIPeYDCKr2+dH6049yJQsWZiHU6RlwvIFBXXGNA==", + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.4.0.tgz", + "integrity": "sha512-swULW8n1IKLjRAgciCkTCafyTHHfwVQFt8DovmaF69sKbOxTSFMmIZaSHjqO9i/RV0wIblaawhzvtva8Nmm7lQ==", "dev": true, + "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "7.16.0", - "@typescript-eslint/types": "7.16.0", - "@typescript-eslint/typescript-estree": "7.16.0" + "@typescript-eslint/scope-manager": "8.4.0", + "@typescript-eslint/types": "8.4.0", + "@typescript-eslint/typescript-estree": "8.4.0" }, "engines": { - "node": "^18.18.0 || >=20.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^8.56.0" + "eslint": "^8.57.0 || ^9.0.0" } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.16.0.tgz", - "integrity": "sha512-rMo01uPy9C7XxG7AFsxa8zLnWXTF8N3PYclekWSrurvhwiw1eW88mrKiAYe6s53AUY57nTRz8dJsuuXdkAhzCg==", + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.4.0.tgz", + "integrity": "sha512-zTQD6WLNTre1hj5wp09nBIDiOc2U5r/qmzo7wxPn4ZgAjHql09EofqhF9WF+fZHzL5aCyaIpPcT2hyxl73kr9A==", "dev": true, + "license": "MIT", "dependencies": { - "@typescript-eslint/types": "7.16.0", + "@typescript-eslint/types": "8.4.0", "eslint-visitor-keys": "^3.4.3" }, "engines": { - "node": "^18.18.0 || >=20.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", @@ -2753,43 +2937,17 @@ } }, "node_modules/array-includes": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.7.tgz", - "integrity": "sha512-dlcsNBIiWhPkHdOEEKnehA+RNUWDc4UqFtnIXU4uuYDPtA4LDkr7qip2p0VvFAEXNDr0yWZ9PJyIRiGjRLQzwQ==", + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.8.tgz", + "integrity": "sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "get-intrinsic": "^1.2.1", - "is-string": "^1.0.7" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/array.prototype.filter": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/array.prototype.filter/-/array.prototype.filter-1.0.3.tgz", - "integrity": "sha512-VizNcj/RGJiUyQBgzwxzE5oHdeuXY5hSbbmKMlphj1cy1Vl7Pn2asCGbSrru6hSQjmCzqTBPVWAF/whmEOVHbw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "es-array-method-boxes-properly": "^1.0.0", + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.4", "is-string": "^1.0.7" }, "engines": { @@ -2800,15 +2958,17 @@ } }, "node_modules/array.prototype.findlastindex": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.4.tgz", - "integrity": "sha512-hzvSHUshSpCflDR1QMUBLHGHP1VIEBegT4pix9H/Z92Xw3ySoy6c2qh7lJWTJnRJ8JCZ9bJNCgTyYaJGcJu6xQ==", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.5.tgz", + "integrity": "sha512-zfETvRFA8o7EiNn++N5f/kaCw221hrpGsDmcpndVupkPzEc1Wuf3VgC0qby1BbHs7f5DVYjgtEU2LLh5bqeGfQ==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.5", + "call-bind": "^1.0.7", "define-properties": "^1.2.1", - "es-abstract": "^1.22.3", + "es-abstract": "^1.23.2", "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", "es-shim-unscopables": "^1.0.2" }, "engines": { @@ -2908,7 +3068,8 @@ "node_modules/bowser": { "version": "2.11.0", "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.11.0.tgz", - "integrity": "sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA==" + "integrity": "sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA==", + "license": "MIT" }, "node_modules/brace-expansion": { "version": "2.0.1", @@ -2923,6 +3084,7 @@ "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" }, @@ -3073,6 +3235,60 @@ "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" } }, + "node_modules/data-view-buffer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.1.tgz", + "integrity": "sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.6", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/data-view-byte-length": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.1.tgz", + "integrity": "sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/data-view-byte-offset": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.0.tgz", + "integrity": "sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.6", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/debug": { "version": "4.3.5", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz", @@ -3139,23 +3355,24 @@ "node": ">=6" } }, + "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", "integrity": "sha512-IayShXAgj/QMXgB0IWmKx+rOPuGMhqm5w6jvFxmVenXKIzRqTAAsbBPT3kWQeGANj3jGgvcvv4yK6SxqYmikgw==" }, - "node_modules/dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", - "dev": true, - "dependencies": { - "path-type": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/doctrine": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", @@ -3169,17 +3386,22 @@ } }, "node_modules/es-abstract": { - "version": "1.22.5", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.5.tgz", - "integrity": "sha512-oW69R+4q2wG+Hc3KZePPZxOiisRIqfKBVo/HLx94QcJeWGU/8sZhCvc829rd1kS366vlJbzBfXf9yWwf0+Ko7w==", + "version": "1.23.3", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.3.tgz", + "integrity": "sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A==", "dev": true, + "license": "MIT", "dependencies": { "array-buffer-byte-length": "^1.0.1", "arraybuffer.prototype.slice": "^1.0.3", "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.7", + "data-view-buffer": "^1.0.1", + "data-view-byte-length": "^1.0.1", + "data-view-byte-offset": "^1.0.0", "es-define-property": "^1.0.0", "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", "es-set-tostringtag": "^2.0.3", "es-to-primitive": "^1.2.1", "function.prototype.name": "^1.1.6", @@ -3190,10 +3412,11 @@ "has-property-descriptors": "^1.0.2", "has-proto": "^1.0.3", "has-symbols": "^1.0.3", - "hasown": "^2.0.1", + "hasown": "^2.0.2", "internal-slot": "^1.0.7", "is-array-buffer": "^3.0.4", "is-callable": "^1.2.7", + "is-data-view": "^1.0.1", "is-negative-zero": "^2.0.3", "is-regex": "^1.1.4", "is-shared-array-buffer": "^1.0.3", @@ -3204,17 +3427,17 @@ "object-keys": "^1.1.1", "object.assign": "^4.1.5", "regexp.prototype.flags": "^1.5.2", - "safe-array-concat": "^1.1.0", + "safe-array-concat": "^1.1.2", "safe-regex-test": "^1.0.3", - "string.prototype.trim": "^1.2.8", - "string.prototype.trimend": "^1.0.7", - "string.prototype.trimstart": "^1.0.7", + "string.prototype.trim": "^1.2.9", + "string.prototype.trimend": "^1.0.8", + "string.prototype.trimstart": "^1.0.8", "typed-array-buffer": "^1.0.2", "typed-array-byte-length": "^1.0.1", "typed-array-byte-offset": "^1.0.2", - "typed-array-length": "^1.0.5", + "typed-array-length": "^1.0.6", "unbox-primitive": "^1.0.2", - "which-typed-array": "^1.1.14" + "which-typed-array": "^1.1.15" }, "engines": { "node": ">= 0.4" @@ -3223,12 +3446,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/es-array-method-boxes-properly": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz", - "integrity": "sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA==", - "dev": true - }, "node_modules/es-define-property": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", @@ -3250,6 +3467,19 @@ "node": ">= 0.4" } }, + "node_modules/es-object-atoms": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.0.0.tgz", + "integrity": "sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/es-set-tostringtag": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.3.tgz", @@ -3291,10 +3521,11 @@ } }, "node_modules/esbuild": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.23.0.tgz", - "integrity": "sha512-1lvV17H2bMYda/WaFb2jLPeHU3zml2k4/yagNMG8Q/YtfMjCwEUZa2eXXMgZTVSL5q1n4H7sQ0X6CdJDqqeCFA==", + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.23.1.tgz", + "integrity": "sha512-VVNz/9Sa0bs5SELtn3f7qhJCDPCF5oMEl5cO9/SSinpE9hbPVvxbd572HH5AKiP7WD8INO53GgfDDhRjkylHEg==", "hasInstallScript": true, + "license": "MIT", "bin": { "esbuild": "bin/esbuild" }, @@ -3302,30 +3533,30 @@ "node": ">=18" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.23.0", - "@esbuild/android-arm": "0.23.0", - "@esbuild/android-arm64": "0.23.0", - "@esbuild/android-x64": "0.23.0", - "@esbuild/darwin-arm64": "0.23.0", - "@esbuild/darwin-x64": "0.23.0", - "@esbuild/freebsd-arm64": "0.23.0", - "@esbuild/freebsd-x64": "0.23.0", - "@esbuild/linux-arm": "0.23.0", - "@esbuild/linux-arm64": "0.23.0", - "@esbuild/linux-ia32": "0.23.0", - "@esbuild/linux-loong64": "0.23.0", - "@esbuild/linux-mips64el": "0.23.0", - "@esbuild/linux-ppc64": "0.23.0", - "@esbuild/linux-riscv64": "0.23.0", - "@esbuild/linux-s390x": "0.23.0", - "@esbuild/linux-x64": "0.23.0", - "@esbuild/netbsd-x64": "0.23.0", - "@esbuild/openbsd-arm64": "0.23.0", - "@esbuild/openbsd-x64": "0.23.0", - "@esbuild/sunos-x64": "0.23.0", - "@esbuild/win32-arm64": "0.23.0", - "@esbuild/win32-ia32": "0.23.0", - "@esbuild/win32-x64": "0.23.0" + "@esbuild/aix-ppc64": "0.23.1", + "@esbuild/android-arm": "0.23.1", + "@esbuild/android-arm64": "0.23.1", + "@esbuild/android-x64": "0.23.1", + "@esbuild/darwin-arm64": "0.23.1", + "@esbuild/darwin-x64": "0.23.1", + "@esbuild/freebsd-arm64": "0.23.1", + "@esbuild/freebsd-x64": "0.23.1", + "@esbuild/linux-arm": "0.23.1", + "@esbuild/linux-arm64": "0.23.1", + "@esbuild/linux-ia32": "0.23.1", + "@esbuild/linux-loong64": "0.23.1", + "@esbuild/linux-mips64el": "0.23.1", + "@esbuild/linux-ppc64": "0.23.1", + "@esbuild/linux-riscv64": "0.23.1", + "@esbuild/linux-s390x": "0.23.1", + "@esbuild/linux-x64": "0.23.1", + "@esbuild/netbsd-x64": "0.23.1", + "@esbuild/openbsd-arm64": "0.23.1", + "@esbuild/openbsd-x64": "0.23.1", + "@esbuild/sunos-x64": "0.23.1", + "@esbuild/win32-arm64": "0.23.1", + "@esbuild/win32-ia32": "0.23.1", + "@esbuild/win32-x64": "0.23.1" } }, "node_modules/esbuild-plugin-inline-worker": { @@ -3469,10 +3700,11 @@ } }, "node_modules/eslint-module-utils": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.8.0.tgz", - "integrity": "sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw==", + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.11.0.tgz", + "integrity": "sha512-gbBE5Hitek/oG6MUVj6sFuzEjA/ClzNflVrLovHi/JgLdC7fiN5gLAY1WIPW1a0V5I999MnsrvVrCOGmmVqDBQ==", "dev": true, + "license": "MIT", "dependencies": { "debug": "^3.2.7" }, @@ -3490,31 +3722,34 @@ "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", "dev": true, + "license": "MIT", "dependencies": { "ms": "^2.1.1" } }, "node_modules/eslint-plugin-import": { - "version": "2.29.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.29.1.tgz", - "integrity": "sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw==", + "version": "2.30.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.30.0.tgz", + "integrity": "sha512-/mHNE9jINJfiD2EKkg1BKyPyUk4zdnT54YgbOgfjSakWT5oyX/qQLVNTkehyfpcMxZXMy1zyonZ2v7hZTX43Yw==", "dev": true, + "license": "MIT", "dependencies": { - "array-includes": "^3.1.7", - "array.prototype.findlastindex": "^1.2.3", + "@rtsao/scc": "^1.1.0", + "array-includes": "^3.1.8", + "array.prototype.findlastindex": "^1.2.5", "array.prototype.flat": "^1.3.2", "array.prototype.flatmap": "^1.3.2", "debug": "^3.2.7", "doctrine": "^2.1.0", "eslint-import-resolver-node": "^0.3.9", - "eslint-module-utils": "^2.8.0", - "hasown": "^2.0.0", - "is-core-module": "^2.13.1", + "eslint-module-utils": "^2.9.0", + "hasown": "^2.0.2", + "is-core-module": "^2.15.1", "is-glob": "^4.0.3", "minimatch": "^3.1.2", - "object.fromentries": "^2.0.7", - "object.groupby": "^1.0.1", - "object.values": "^1.1.7", + "object.fromentries": "^2.0.8", + "object.groupby": "^1.0.3", + "object.values": "^1.2.0", "semver": "^6.3.1", "tsconfig-paths": "^3.15.0" }, @@ -3715,6 +3950,7 @@ "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", "dev": true, + "license": "MIT", "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", @@ -3731,6 +3967,7 @@ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "dev": true, + "license": "ISC", "dependencies": { "is-glob": "^4.0.1" }, @@ -3751,19 +3988,20 @@ "dev": true }, "node_modules/fast-xml-parser": { - "version": "4.2.5", - "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.2.5.tgz", - "integrity": "sha512-B9/wizE4WngqQftFPmdaMYlXoJlJOYxGQOanC77fq9k8+Z0v5dDSVh+3glErdIROP//s/jgb7ZuxKfB8nVyo0g==", + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.4.1.tgz", + "integrity": "sha512-xkjOecfnKGkSsOwtZ5Pz7Us/T6mrbPQrq0nh+aCO5V9nk5NLWmasAHumTKjiPJPWANe+kAZ84Jc8ooJkzZ88Sw==", "funding": [ - { - "type": "paypal", - "url": "https://paypal.me/naturalintelligence" - }, { "type": "github", "url": "https://github.com/sponsors/NaturalIntelligence" + }, + { + "type": "paypal", + "url": "https://paypal.me/naturalintelligence" } ], + "license": "MIT", "dependencies": { "strnum": "^1.0.5" }, @@ -3812,6 +4050,7 @@ "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" }, @@ -4041,26 +4280,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", - "dev": true, - "dependencies": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/gopd": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", @@ -4286,12 +4505,32 @@ } }, "node_modules/is-core-module": { - "version": "2.13.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", - "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", + "version": "2.15.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.1.tgz", + "integrity": "sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==", "dev": true, + "license": "MIT", "dependencies": { - "hasown": "^2.0.0" + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-data-view": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.1.tgz", + "integrity": "sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -4350,6 +4589,7 @@ "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" } @@ -4620,15 +4860,17 @@ "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", "dev": true, + "license": "MIT", "engines": { "node": ">= 8" } }, "node_modules/micromatch": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.7.tgz", - "integrity": "sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==", + "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", "picomatch": "^2.3.1" @@ -4637,6 +4879,29 @@ "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.0.1", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.1.tgz", @@ -4767,14 +5032,16 @@ } }, "node_modules/object.fromentries": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.7.tgz", - "integrity": "sha512-UPbPHML6sL8PI/mOqPwsH4G6iyXcCGzLin8KvEPenOZN5lpCNBZZQ+V62vdjB1mQHrmqGQt5/OJzemUA+KJmEA==", + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz", + "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0" }, "engines": { "node": ">= 0.4" @@ -4784,27 +5051,30 @@ } }, "node_modules/object.groupby": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.2.tgz", - "integrity": "sha512-bzBq58S+x+uo0VjurFT0UktpKHOZmv4/xePiOA1nbB9pMqpGK7rUPNgf+1YC+7mE+0HzhTMqNUuCqvKhj6FnBw==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.3.tgz", + "integrity": "sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==", "dev": true, + "license": "MIT", "dependencies": { - "array.prototype.filter": "^1.0.3", - "call-bind": "^1.0.5", + "call-bind": "^1.0.7", "define-properties": "^1.2.1", - "es-abstract": "^1.22.3", - "es-errors": "^1.0.0" + "es-abstract": "^1.23.2" + }, + "engines": { + "node": ">= 0.4" } }, "node_modules/object.values": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.7.tgz", - "integrity": "sha512-aU6xnDFYT3x17e/f0IiiwlGPTy2jzMySGfUB4fq6z7CV8l85CWHDk5ErhyhpfDHhrOMwGFhSQkhMGHaIotA6Ng==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.0.tgz", + "integrity": "sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" }, "engines": { "node": ">= 0.4" @@ -4814,10 +5084,11 @@ } }, "node_modules/obsidian": { - "version": "1.5.7", - "resolved": "https://registry.npmjs.org/obsidian/-/obsidian-1.5.7.tgz", - "integrity": "sha512-DNcvQJ6TvMflHZqWfO9cLGbOUbKTy2KBi6B6vjo5RG8XsftKZZq1zS/OQFhII2BnXK/DWan/lUcb2JYxfM3p5A==", + "version": "1.6.6", + "resolved": "https://registry.npmjs.org/obsidian/-/obsidian-1.6.6.tgz", + "integrity": "sha512-GZHzeOiwmw/wBjB5JwrsxAZBLqxGQmqtEKSvJJvT0LtTcqeOFnV8jv0ZK5kO7hBb44WxJc+LdS7mZgLXbb+qXQ==", "dev": true, + "license": "MIT", "dependencies": { "@types/codemirror": "5.60.8", "moment": "2.29.4" @@ -4828,9 +5099,10 @@ } }, "node_modules/octagonal-wheels": { - "version": "0.1.13", - "resolved": "https://registry.npmjs.org/octagonal-wheels/-/octagonal-wheels-0.1.13.tgz", - "integrity": "sha512-jnkOBiNjnQfbWM+shseVbpPONZ4T65TqlT/+4kLZMb01Xh2cyAgA4wiDiQnK4zZZwYqi++tX86SDReQoLrD38w==", + "version": "0.1.14", + "resolved": "https://registry.npmjs.org/octagonal-wheels/-/octagonal-wheels-0.1.14.tgz", + "integrity": "sha512-W0DQL5YNL7oJH2Dcb7mOE/P7MNrM7PXWJZHJpPGzbrKHAT14OtklbBFNJvM26v8nizwlb5WHQA+W/Tg1CIDSGQ==", + "license": "MIT", "dependencies": { "idb": "^8.0.0", "xxhash-wasm": "0.4.2", @@ -4945,15 +5217,6 @@ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "dev": true }, - "node_modules/path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/periscopic": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/periscopic/-/periscopic-3.1.0.tgz", @@ -4976,6 +5239,7 @@ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true, + "license": "MIT", "engines": { "node": ">=8.6" }, @@ -5052,9 +5316,9 @@ } }, "node_modules/postcss": { - "version": "8.4.39", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.39.tgz", - "integrity": "sha512-0vzE+lAiG7hZl1/9I8yzKLx3aR9Xbof3fBHKunvMfOCYAtMhrsnccJY2iTURb9EZd5+pLuiNV9/c/GZJOHsgIw==", + "version": "8.4.45", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.45.tgz", + "integrity": "sha512-7KTLTdzdZZYscUc65XmjFiB73vBhBfbPztCYdUNvlaso9PrzjzcmjqBPR0lNGkcVlcO4BjiO5rK/qNz+XAen1Q==", "dev": true, "funding": [ { @@ -5070,6 +5334,7 @@ "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "dependencies": { "nanoid": "^3.3.7", "picocolors": "^1.0.1", @@ -5598,10 +5863,11 @@ } }, "node_modules/semver": { - "version": "7.6.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", - "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", "dev": true, + "license": "ISC", "bin": { "semver": "bin/semver.js" }, @@ -5686,15 +5952,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -5730,14 +5987,16 @@ "dev": true }, "node_modules/string.prototype.trim": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.8.tgz", - "integrity": "sha512-lfjY4HcixfQXOfaqCvcBuOIapyaroTXhbkfJN3gcB1OtyupngWK4sEET9Knd0cXd28kTUqu/kHoV4HKSJdnjiQ==", + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.9.tgz", + "integrity": "sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.0", + "es-object-atoms": "^1.0.0" }, "engines": { "node": ">= 0.4" @@ -5747,28 +6006,33 @@ } }, "node_modules/string.prototype.trimend": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.7.tgz", - "integrity": "sha512-Ni79DqeB72ZFq1uH/L6zJ+DKZTkOtPIHovb3YZHQViE+HDouuU4mBrLOLDn5Dde3RF8qw5qVETEjhu9locMLvA==", + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.8.tgz", + "integrity": "sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/string.prototype.trimstart": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.7.tgz", - "integrity": "sha512-NGhtDFu3jCEm7B4Fy0DpLewdJQOZcQ0rGbwQ/+stjnrp2i+rlKeCvos9hOIeCmqwratM47OBxY7uFZzjxHXmrg==", + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", + "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -5810,7 +6074,8 @@ "node_modules/strnum": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.0.5.tgz", - "integrity": "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==" + "integrity": "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==", + "license": "MIT" }, "node_modules/style-mod": { "version": "4.0.0", @@ -5844,10 +6109,11 @@ } }, "node_modules/svelte": { - "version": "4.2.18", - "resolved": "https://registry.npmjs.org/svelte/-/svelte-4.2.18.tgz", - "integrity": "sha512-d0FdzYIiAePqRJEb90WlJDkjUEx42xhivxN8muUBmfZnP+tzUgz12DJ2hRJi8sIHCME7jeK1PTMgKPSfTd8JrA==", + "version": "4.2.19", + "resolved": "https://registry.npmjs.org/svelte/-/svelte-4.2.19.tgz", + "integrity": "sha512-IY1rnGr6izd10B0A8LqsBfmlT5OILVuZ7XsI0vdGPEvuonFV7NYEUK4dAkm9Zg2q0Um92kYjTpS1CAP3Nh/KWw==", "dev": true, + "license": "MIT", "dependencies": { "@ampproject/remapping": "^2.2.1", "@jridgewell/sourcemap-codec": "^1.4.15", @@ -5924,10 +6190,11 @@ } }, "node_modules/terser": { - "version": "5.31.2", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.31.2.tgz", - "integrity": "sha512-LGyRZVFm/QElZHy/CPr/O4eNZOZIzsrQ92y4v9UJe/pFJjypje2yI3C2FmPtvUEnhadlSbmG2nXtdcjHOjCfxw==", + "version": "5.31.6", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.31.6.tgz", + "integrity": "sha512-PQ4DAriWzKj+qgehQ7LK5bQqCFNMmlhjR2PFFLuqGCpuCAauxemVBWwWOxo3UIwWQx8+Pr61Df++r76wDmkQBg==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "@jridgewell/source-map": "^0.3.3", "acorn": "^8.8.2", @@ -5952,6 +6219,7 @@ "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" }, @@ -5994,6 +6262,7 @@ "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz", "integrity": "sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=16" }, @@ -6014,9 +6283,10 @@ } }, "node_modules/tslib": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", - "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", + "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==", + "license": "0BSD" }, "node_modules/type-check": { "version": "0.4.0", @@ -6096,10 +6366,11 @@ } }, "node_modules/typed-array-length": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.5.tgz", - "integrity": "sha512-yMi0PlwuznKHxKmcpoOdeLwxBoVPkqZxd7q2FgMkmD3bNwvF5VW0+UlUQ1k1vmktTu4Yu13Q0RIxEP8+B+wloA==", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.6.tgz", + "integrity": "sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.7", "for-each": "^0.3.3", @@ -6116,10 +6387,11 @@ } }, "node_modules/typescript": { - "version": "5.5.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.3.tgz", - "integrity": "sha512-/hreyEujaB0w76zKo6717l3L0o/qEUtRgdvUBvlkhoWeOVMjMuHNHk0BRBzikzuGDqNmPQbg5ifMEqsHLiIUcQ==", + "version": "5.5.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.4.tgz", + "integrity": "sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==", "dev": true, + "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -6144,10 +6416,11 @@ } }, "node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", - "dev": true + "version": "6.19.8", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", + "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", + "dev": true, + "license": "MIT" }, "node_modules/universalify": { "version": "0.2.0", @@ -6485,108 +6758,108 @@ } }, "@aws-sdk/client-s3": { - "version": "3.614.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-s3/-/client-s3-3.614.0.tgz", - "integrity": "sha512-9BlhfeBegvyjOqHtcr9kvrT80wiy7EVUiqYyTFiiDv/hJIcG88XHQCZdLU7658XBkQ7aFrr5b8rF2HRD1oroxw==", + "version": "3.645.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-s3/-/client-s3-3.645.0.tgz", + "integrity": "sha512-RjT/mfNv4yr1uv/+aEXgSIxC5EB+yHPSU7hH0KZOZrvZEFASLl0i4FeoHzbMEOH5KdKGAi0uu3zRP3D1y45sKg==", "requires": { "@aws-crypto/sha1-browser": "5.2.0", "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/client-sso-oidc": "3.614.0", - "@aws-sdk/client-sts": "3.614.0", - "@aws-sdk/core": "3.614.0", - "@aws-sdk/credential-provider-node": "3.614.0", - "@aws-sdk/middleware-bucket-endpoint": "3.614.0", - "@aws-sdk/middleware-expect-continue": "3.609.0", - "@aws-sdk/middleware-flexible-checksums": "3.614.0", - "@aws-sdk/middleware-host-header": "3.609.0", + "@aws-sdk/client-sso-oidc": "3.645.0", + "@aws-sdk/client-sts": "3.645.0", + "@aws-sdk/core": "3.635.0", + "@aws-sdk/credential-provider-node": "3.645.0", + "@aws-sdk/middleware-bucket-endpoint": "3.620.0", + "@aws-sdk/middleware-expect-continue": "3.620.0", + "@aws-sdk/middleware-flexible-checksums": "3.620.0", + "@aws-sdk/middleware-host-header": "3.620.0", "@aws-sdk/middleware-location-constraint": "3.609.0", "@aws-sdk/middleware-logger": "3.609.0", - "@aws-sdk/middleware-recursion-detection": "3.609.0", - "@aws-sdk/middleware-sdk-s3": "3.614.0", - "@aws-sdk/middleware-signing": "3.609.0", + "@aws-sdk/middleware-recursion-detection": "3.620.0", + "@aws-sdk/middleware-sdk-s3": "3.635.0", "@aws-sdk/middleware-ssec": "3.609.0", - "@aws-sdk/middleware-user-agent": "3.614.0", + "@aws-sdk/middleware-user-agent": "3.645.0", "@aws-sdk/region-config-resolver": "3.614.0", - "@aws-sdk/signature-v4-multi-region": "3.614.0", + "@aws-sdk/signature-v4-multi-region": "3.635.0", "@aws-sdk/types": "3.609.0", - "@aws-sdk/util-endpoints": "3.614.0", + "@aws-sdk/util-endpoints": "3.645.0", "@aws-sdk/util-user-agent-browser": "3.609.0", "@aws-sdk/util-user-agent-node": "3.614.0", "@aws-sdk/xml-builder": "3.609.0", "@smithy/config-resolver": "^3.0.5", - "@smithy/core": "^2.2.6", - "@smithy/eventstream-serde-browser": "^3.0.4", + "@smithy/core": "^2.4.0", + "@smithy/eventstream-serde-browser": "^3.0.6", "@smithy/eventstream-serde-config-resolver": "^3.0.3", - "@smithy/eventstream-serde-node": "^3.0.4", - "@smithy/fetch-http-handler": "^3.2.1", + "@smithy/eventstream-serde-node": "^3.0.5", + "@smithy/fetch-http-handler": "^3.2.4", "@smithy/hash-blob-browser": "^3.1.2", "@smithy/hash-node": "^3.0.3", "@smithy/hash-stream-node": "^3.1.2", "@smithy/invalid-dependency": "^3.0.3", "@smithy/md5-js": "^3.0.3", - "@smithy/middleware-content-length": "^3.0.3", - "@smithy/middleware-endpoint": "^3.0.5", - "@smithy/middleware-retry": "^3.0.9", + "@smithy/middleware-content-length": "^3.0.5", + "@smithy/middleware-endpoint": "^3.1.0", + "@smithy/middleware-retry": "^3.0.15", "@smithy/middleware-serde": "^3.0.3", "@smithy/middleware-stack": "^3.0.3", "@smithy/node-config-provider": "^3.1.4", - "@smithy/node-http-handler": "^3.1.2", - "@smithy/protocol-http": "^4.0.3", - "@smithy/smithy-client": "^3.1.7", + "@smithy/node-http-handler": "^3.1.4", + "@smithy/protocol-http": "^4.1.0", + "@smithy/smithy-client": "^3.2.0", "@smithy/types": "^3.3.0", "@smithy/url-parser": "^3.0.3", "@smithy/util-base64": "^3.0.0", "@smithy/util-body-length-browser": "^3.0.0", "@smithy/util-body-length-node": "^3.0.0", - "@smithy/util-defaults-mode-browser": "^3.0.9", - "@smithy/util-defaults-mode-node": "^3.0.9", + "@smithy/util-defaults-mode-browser": "^3.0.15", + "@smithy/util-defaults-mode-node": "^3.0.15", "@smithy/util-endpoints": "^2.0.5", + "@smithy/util-middleware": "^3.0.3", "@smithy/util-retry": "^3.0.3", - "@smithy/util-stream": "^3.0.6", + "@smithy/util-stream": "^3.1.3", "@smithy/util-utf8": "^3.0.0", "@smithy/util-waiter": "^3.1.2", "tslib": "^2.6.2" } }, "@aws-sdk/client-sso": { - "version": "3.614.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.614.0.tgz", - "integrity": "sha512-p5pyYaxRzBttjBkqfc8i3K7DzBdTg3ECdVgBo6INIUxfvDy0J8QUE8vNtCgvFIkq+uPw/8M+Eo4zzln7anuO0Q==", + "version": "3.645.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.645.0.tgz", + "integrity": "sha512-2rc8TjnsNddOeKQ/pfNN7deNvGLXAeKeYtHtGDAiM2qfTKxd2sNcAsZ+JCDLyshuD4xLM5fpUyR0X8As9EAouQ==", "requires": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.614.0", - "@aws-sdk/middleware-host-header": "3.609.0", + "@aws-sdk/core": "3.635.0", + "@aws-sdk/middleware-host-header": "3.620.0", "@aws-sdk/middleware-logger": "3.609.0", - "@aws-sdk/middleware-recursion-detection": "3.609.0", - "@aws-sdk/middleware-user-agent": "3.614.0", + "@aws-sdk/middleware-recursion-detection": "3.620.0", + "@aws-sdk/middleware-user-agent": "3.645.0", "@aws-sdk/region-config-resolver": "3.614.0", "@aws-sdk/types": "3.609.0", - "@aws-sdk/util-endpoints": "3.614.0", + "@aws-sdk/util-endpoints": "3.645.0", "@aws-sdk/util-user-agent-browser": "3.609.0", "@aws-sdk/util-user-agent-node": "3.614.0", "@smithy/config-resolver": "^3.0.5", - "@smithy/core": "^2.2.6", - "@smithy/fetch-http-handler": "^3.2.1", + "@smithy/core": "^2.4.0", + "@smithy/fetch-http-handler": "^3.2.4", "@smithy/hash-node": "^3.0.3", "@smithy/invalid-dependency": "^3.0.3", - "@smithy/middleware-content-length": "^3.0.3", - "@smithy/middleware-endpoint": "^3.0.5", - "@smithy/middleware-retry": "^3.0.9", + "@smithy/middleware-content-length": "^3.0.5", + "@smithy/middleware-endpoint": "^3.1.0", + "@smithy/middleware-retry": "^3.0.15", "@smithy/middleware-serde": "^3.0.3", "@smithy/middleware-stack": "^3.0.3", "@smithy/node-config-provider": "^3.1.4", - "@smithy/node-http-handler": "^3.1.2", - "@smithy/protocol-http": "^4.0.3", - "@smithy/smithy-client": "^3.1.7", + "@smithy/node-http-handler": "^3.1.4", + "@smithy/protocol-http": "^4.1.0", + "@smithy/smithy-client": "^3.2.0", "@smithy/types": "^3.3.0", "@smithy/url-parser": "^3.0.3", "@smithy/util-base64": "^3.0.0", "@smithy/util-body-length-browser": "^3.0.0", "@smithy/util-body-length-node": "^3.0.0", - "@smithy/util-defaults-mode-browser": "^3.0.9", - "@smithy/util-defaults-mode-node": "^3.0.9", + "@smithy/util-defaults-mode-browser": "^3.0.15", + "@smithy/util-defaults-mode-node": "^3.0.15", "@smithy/util-endpoints": "^2.0.5", "@smithy/util-middleware": "^3.0.3", "@smithy/util-retry": "^3.0.3", @@ -6595,44 +6868,44 @@ } }, "@aws-sdk/client-sso-oidc": { - "version": "3.614.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso-oidc/-/client-sso-oidc-3.614.0.tgz", - "integrity": "sha512-BI1NWcpppbHg/28zbUg54dZeckork8BItZIcjls12vxasy+p3iEzrJVG60jcbUTTsk3Qc1tyxNfrdcVqx0y7Ww==", + "version": "3.645.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso-oidc/-/client-sso-oidc-3.645.0.tgz", + "integrity": "sha512-X9ULtdk3cO+1ysurEkJ1MSnu6U00qodXx+IVual+1jXX4RYY1WmQmfo7uDKf6FFkz7wW1DAqU+GJIBNQr0YH8A==", "requires": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.614.0", - "@aws-sdk/credential-provider-node": "3.614.0", - "@aws-sdk/middleware-host-header": "3.609.0", + "@aws-sdk/core": "3.635.0", + "@aws-sdk/credential-provider-node": "3.645.0", + "@aws-sdk/middleware-host-header": "3.620.0", "@aws-sdk/middleware-logger": "3.609.0", - "@aws-sdk/middleware-recursion-detection": "3.609.0", - "@aws-sdk/middleware-user-agent": "3.614.0", + "@aws-sdk/middleware-recursion-detection": "3.620.0", + "@aws-sdk/middleware-user-agent": "3.645.0", "@aws-sdk/region-config-resolver": "3.614.0", "@aws-sdk/types": "3.609.0", - "@aws-sdk/util-endpoints": "3.614.0", + "@aws-sdk/util-endpoints": "3.645.0", "@aws-sdk/util-user-agent-browser": "3.609.0", "@aws-sdk/util-user-agent-node": "3.614.0", "@smithy/config-resolver": "^3.0.5", - "@smithy/core": "^2.2.6", - "@smithy/fetch-http-handler": "^3.2.1", + "@smithy/core": "^2.4.0", + "@smithy/fetch-http-handler": "^3.2.4", "@smithy/hash-node": "^3.0.3", "@smithy/invalid-dependency": "^3.0.3", - "@smithy/middleware-content-length": "^3.0.3", - "@smithy/middleware-endpoint": "^3.0.5", - "@smithy/middleware-retry": "^3.0.9", + "@smithy/middleware-content-length": "^3.0.5", + "@smithy/middleware-endpoint": "^3.1.0", + "@smithy/middleware-retry": "^3.0.15", "@smithy/middleware-serde": "^3.0.3", "@smithy/middleware-stack": "^3.0.3", "@smithy/node-config-provider": "^3.1.4", - "@smithy/node-http-handler": "^3.1.2", - "@smithy/protocol-http": "^4.0.3", - "@smithy/smithy-client": "^3.1.7", + "@smithy/node-http-handler": "^3.1.4", + "@smithy/protocol-http": "^4.1.0", + "@smithy/smithy-client": "^3.2.0", "@smithy/types": "^3.3.0", "@smithy/url-parser": "^3.0.3", "@smithy/util-base64": "^3.0.0", "@smithy/util-body-length-browser": "^3.0.0", "@smithy/util-body-length-node": "^3.0.0", - "@smithy/util-defaults-mode-browser": "^3.0.9", - "@smithy/util-defaults-mode-node": "^3.0.9", + "@smithy/util-defaults-mode-browser": "^3.0.15", + "@smithy/util-defaults-mode-node": "^3.0.15", "@smithy/util-endpoints": "^2.0.5", "@smithy/util-middleware": "^3.0.3", "@smithy/util-retry": "^3.0.3", @@ -6641,45 +6914,45 @@ } }, "@aws-sdk/client-sts": { - "version": "3.614.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.614.0.tgz", - "integrity": "sha512-i6QmaVA1KHHYNnI2VYQy/sc31rLm4+jSp8b/YbQpFnD0w3aXsrEEHHlxek45uSkHb4Nrj1omFBVy/xp1WVYx2Q==", + "version": "3.645.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.645.0.tgz", + "integrity": "sha512-6azXYtvtnAsPf2ShN9vKynIYVcJOpo6IoVmoMAVgNaBJyllP+s/RORzranYZzckqfmrudSxtct4rVapjLWuAMg==", "requires": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/client-sso-oidc": "3.614.0", - "@aws-sdk/core": "3.614.0", - "@aws-sdk/credential-provider-node": "3.614.0", - "@aws-sdk/middleware-host-header": "3.609.0", + "@aws-sdk/client-sso-oidc": "3.645.0", + "@aws-sdk/core": "3.635.0", + "@aws-sdk/credential-provider-node": "3.645.0", + "@aws-sdk/middleware-host-header": "3.620.0", "@aws-sdk/middleware-logger": "3.609.0", - "@aws-sdk/middleware-recursion-detection": "3.609.0", - "@aws-sdk/middleware-user-agent": "3.614.0", + "@aws-sdk/middleware-recursion-detection": "3.620.0", + "@aws-sdk/middleware-user-agent": "3.645.0", "@aws-sdk/region-config-resolver": "3.614.0", "@aws-sdk/types": "3.609.0", - "@aws-sdk/util-endpoints": "3.614.0", + "@aws-sdk/util-endpoints": "3.645.0", "@aws-sdk/util-user-agent-browser": "3.609.0", "@aws-sdk/util-user-agent-node": "3.614.0", "@smithy/config-resolver": "^3.0.5", - "@smithy/core": "^2.2.6", - "@smithy/fetch-http-handler": "^3.2.1", + "@smithy/core": "^2.4.0", + "@smithy/fetch-http-handler": "^3.2.4", "@smithy/hash-node": "^3.0.3", "@smithy/invalid-dependency": "^3.0.3", - "@smithy/middleware-content-length": "^3.0.3", - "@smithy/middleware-endpoint": "^3.0.5", - "@smithy/middleware-retry": "^3.0.9", + "@smithy/middleware-content-length": "^3.0.5", + "@smithy/middleware-endpoint": "^3.1.0", + "@smithy/middleware-retry": "^3.0.15", "@smithy/middleware-serde": "^3.0.3", "@smithy/middleware-stack": "^3.0.3", "@smithy/node-config-provider": "^3.1.4", - "@smithy/node-http-handler": "^3.1.2", - "@smithy/protocol-http": "^4.0.3", - "@smithy/smithy-client": "^3.1.7", + "@smithy/node-http-handler": "^3.1.4", + "@smithy/protocol-http": "^4.1.0", + "@smithy/smithy-client": "^3.2.0", "@smithy/types": "^3.3.0", "@smithy/url-parser": "^3.0.3", "@smithy/util-base64": "^3.0.0", "@smithy/util-body-length-browser": "^3.0.0", "@smithy/util-body-length-node": "^3.0.0", - "@smithy/util-defaults-mode-browser": "^3.0.9", - "@smithy/util-defaults-mode-node": "^3.0.9", + "@smithy/util-defaults-mode-browser": "^3.0.15", + "@smithy/util-defaults-mode-node": "^3.0.15", "@smithy/util-endpoints": "^2.0.5", "@smithy/util-middleware": "^3.0.3", "@smithy/util-retry": "^3.0.3", @@ -6688,23 +6961,26 @@ } }, "@aws-sdk/core": { - "version": "3.614.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.614.0.tgz", - "integrity": "sha512-BUuS5/1YkgmKc4J0bg83XEtMyDHVyqG2QDzfmhYe8gbOIZabUl1FlrFVwhCAthtrrI6MPGTQcERB4BtJKUSplw==", + "version": "3.635.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.635.0.tgz", + "integrity": "sha512-i1x/E/sgA+liUE1XJ7rj1dhyXpAKO1UKFUcTTHXok2ARjWTvszHnSXMOsB77aPbmn0fUp1JTx2kHUAZ1LVt5Bg==", "requires": { - "@smithy/core": "^2.2.6", - "@smithy/protocol-http": "^4.0.3", - "@smithy/signature-v4": "^3.1.2", - "@smithy/smithy-client": "^3.1.7", + "@smithy/core": "^2.4.0", + "@smithy/node-config-provider": "^3.1.4", + "@smithy/property-provider": "^3.1.3", + "@smithy/protocol-http": "^4.1.0", + "@smithy/signature-v4": "^4.1.0", + "@smithy/smithy-client": "^3.2.0", "@smithy/types": "^3.3.0", - "fast-xml-parser": "4.2.5", + "@smithy/util-middleware": "^3.0.3", + "fast-xml-parser": "4.4.1", "tslib": "^2.6.2" } }, "@aws-sdk/credential-provider-env": { - "version": "3.609.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.609.0.tgz", - "integrity": "sha512-v69ZCWcec2iuV9vLVJMa6fAb5xwkzN4jYIT8yjo2c4Ia/j976Q+TPf35Pnz5My48Xr94EFcaBazrWedF+kwfuQ==", + "version": "3.620.1", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.620.1.tgz", + "integrity": "sha512-ExuILJ2qLW5ZO+rgkNRj0xiAipKT16Rk77buvPP8csR7kkCflT/gXTyzRe/uzIiETTxM7tr8xuO9MP/DQXqkfg==", "requires": { "@aws-sdk/types": "3.609.0", "@smithy/property-provider": "^3.1.3", @@ -6713,33 +6989,33 @@ } }, "@aws-sdk/credential-provider-http": { - "version": "3.614.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.614.0.tgz", - "integrity": "sha512-YIEjlNUKb3Vo/iTnGAPdsiDC3FUUnNoex2OwU8LmR7AkYZiWdB8nx99DfgkkY+OFMUpw7nKD2PCOtuFONelfGA==", + "version": "3.635.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.635.0.tgz", + "integrity": "sha512-iJyRgEjOCQlBMXqtwPLIKYc7Bsc6nqjrZybdMDenPDa+kmLg7xh8LxHsu9088e+2/wtLicE34FsJJIfzu3L82g==", "requires": { "@aws-sdk/types": "3.609.0", - "@smithy/fetch-http-handler": "^3.2.1", - "@smithy/node-http-handler": "^3.1.2", + "@smithy/fetch-http-handler": "^3.2.4", + "@smithy/node-http-handler": "^3.1.4", "@smithy/property-provider": "^3.1.3", - "@smithy/protocol-http": "^4.0.3", - "@smithy/smithy-client": "^3.1.7", + "@smithy/protocol-http": "^4.1.0", + "@smithy/smithy-client": "^3.2.0", "@smithy/types": "^3.3.0", - "@smithy/util-stream": "^3.0.6", + "@smithy/util-stream": "^3.1.3", "tslib": "^2.6.2" } }, "@aws-sdk/credential-provider-ini": { - "version": "3.614.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.614.0.tgz", - "integrity": "sha512-KfLuLFGwlvFSZ2MuzYwWGPb1y5TeiwX5okIDe0aQ1h10oD3924FXbN+mabOnUHQ8EFcGAtCaWbrC86mI7ktC6A==", + "version": "3.645.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.645.0.tgz", + "integrity": "sha512-LlZW0qwUwNlTaAIDCNpLbPsyXvS42pRIwF92fgtCQedmdnpN3XRUC6hcwSYI7Xru3GGKp3RnceOvsdOaRJORsw==", "requires": { - "@aws-sdk/credential-provider-env": "3.609.0", - "@aws-sdk/credential-provider-http": "3.614.0", - "@aws-sdk/credential-provider-process": "3.614.0", - "@aws-sdk/credential-provider-sso": "3.614.0", - "@aws-sdk/credential-provider-web-identity": "3.609.0", + "@aws-sdk/credential-provider-env": "3.620.1", + "@aws-sdk/credential-provider-http": "3.635.0", + "@aws-sdk/credential-provider-process": "3.620.1", + "@aws-sdk/credential-provider-sso": "3.645.0", + "@aws-sdk/credential-provider-web-identity": "3.621.0", "@aws-sdk/types": "3.609.0", - "@smithy/credential-provider-imds": "^3.1.4", + "@smithy/credential-provider-imds": "^3.2.0", "@smithy/property-provider": "^3.1.3", "@smithy/shared-ini-file-loader": "^3.1.4", "@smithy/types": "^3.3.0", @@ -6747,18 +7023,18 @@ } }, "@aws-sdk/credential-provider-node": { - "version": "3.614.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.614.0.tgz", - "integrity": "sha512-4J6gPEuFZP0mkWq5E//oMS1vrmMM88iNNcv7TEljYnsc6JTAlKejCyFwx6CN+nkIhmIZsl06SXIhBemzBdBPfg==", + "version": "3.645.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.645.0.tgz", + "integrity": "sha512-eGFFuNvLeXjCJf5OCIuSEflxUowmK+bCS+lK4M8ofsYOEGAivdx7C0UPxNjHpvM8wKd8vpMl5phTeS9BWX5jMQ==", "requires": { - "@aws-sdk/credential-provider-env": "3.609.0", - "@aws-sdk/credential-provider-http": "3.614.0", - "@aws-sdk/credential-provider-ini": "3.614.0", - "@aws-sdk/credential-provider-process": "3.614.0", - "@aws-sdk/credential-provider-sso": "3.614.0", - "@aws-sdk/credential-provider-web-identity": "3.609.0", + "@aws-sdk/credential-provider-env": "3.620.1", + "@aws-sdk/credential-provider-http": "3.635.0", + "@aws-sdk/credential-provider-ini": "3.645.0", + "@aws-sdk/credential-provider-process": "3.620.1", + "@aws-sdk/credential-provider-sso": "3.645.0", + "@aws-sdk/credential-provider-web-identity": "3.621.0", "@aws-sdk/types": "3.609.0", - "@smithy/credential-provider-imds": "^3.1.4", + "@smithy/credential-provider-imds": "^3.2.0", "@smithy/property-provider": "^3.1.3", "@smithy/shared-ini-file-loader": "^3.1.4", "@smithy/types": "^3.3.0", @@ -6766,9 +7042,9 @@ } }, "@aws-sdk/credential-provider-process": { - "version": "3.614.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.614.0.tgz", - "integrity": "sha512-Q0SI0sTRwi8iNODLs5+bbv8vgz8Qy2QdxbCHnPk/6Cx6LMf7i3dqmWquFbspqFRd8QiqxStrblwxrUYZi09tkA==", + "version": "3.620.1", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.620.1.tgz", + "integrity": "sha512-hWqFMidqLAkaV9G460+1at6qa9vySbjQKKc04p59OT7lZ5cO5VH5S4aI05e+m4j364MBROjjk2ugNvfNf/8ILg==", "requires": { "@aws-sdk/types": "3.609.0", "@smithy/property-provider": "^3.1.3", @@ -6778,11 +7054,11 @@ } }, "@aws-sdk/credential-provider-sso": { - "version": "3.614.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.614.0.tgz", - "integrity": "sha512-55+gp0JY4451cWI1qXmVMFM0GQaBKiQpXv2P0xmd9P3qLDyeFUSEW8XPh0d2lb1ICr6x4s47ynXVdGCIv2mXMg==", + "version": "3.645.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.645.0.tgz", + "integrity": "sha512-d6XuChAl5NCsCrUexc6AFb4efPmb9+66iwPylKG+iMTMYgO1ackfy1Q2/f35jdn0jolkPkzKsVyfzsEVoID6ew==", "requires": { - "@aws-sdk/client-sso": "3.614.0", + "@aws-sdk/client-sso": "3.645.0", "@aws-sdk/token-providers": "3.614.0", "@aws-sdk/types": "3.609.0", "@smithy/property-provider": "^3.1.3", @@ -6792,9 +7068,9 @@ } }, "@aws-sdk/credential-provider-web-identity": { - "version": "3.609.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.609.0.tgz", - "integrity": "sha512-U+PG8NhlYYF45zbr1km3ROtBMYqyyj/oK8NRp++UHHeuavgrP+4wJ4wQnlEaKvJBjevfo3+dlIBcaeQ7NYejWg==", + "version": "3.621.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.621.0.tgz", + "integrity": "sha512-w7ASSyfNvcx7+bYGep3VBgC3K6vEdLmlpjT7nSIHxxQf+WSdvy+HynwJosrpZax0sK5q0D1Jpn/5q+r5lwwW6w==", "requires": { "@aws-sdk/types": "3.609.0", "@smithy/property-provider": "^3.1.3", @@ -6803,52 +7079,52 @@ } }, "@aws-sdk/middleware-bucket-endpoint": { - "version": "3.614.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-bucket-endpoint/-/middleware-bucket-endpoint-3.614.0.tgz", - "integrity": "sha512-TqEY8KcZeZ0LIxXaqG9RSSNnDHvD8RAFP4Xenwsxqnyad0Yn7LgCoFwRByelJ0t54ROYL1/ETJleWE4U4TOXdg==", + "version": "3.620.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-bucket-endpoint/-/middleware-bucket-endpoint-3.620.0.tgz", + "integrity": "sha512-eGLL0W6L3HDb3OACyetZYOWpHJ+gLo0TehQKeQyy2G8vTYXqNTeqYhuI6up9HVjBzU9eQiULVQETmgQs7TFaRg==", "requires": { "@aws-sdk/types": "3.609.0", "@aws-sdk/util-arn-parser": "3.568.0", "@smithy/node-config-provider": "^3.1.4", - "@smithy/protocol-http": "^4.0.3", + "@smithy/protocol-http": "^4.1.0", "@smithy/types": "^3.3.0", "@smithy/util-config-provider": "^3.0.0", "tslib": "^2.6.2" } }, "@aws-sdk/middleware-expect-continue": { - "version": "3.609.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-expect-continue/-/middleware-expect-continue-3.609.0.tgz", - "integrity": "sha512-+zeg//mSer4JZRxOB/4mUOMUJyuYPwATnIC5moBB8P8Xe+mJaVRFy8qlCtzYNj2TycnlsBPzTK0j7P1yvDh97w==", + "version": "3.620.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-expect-continue/-/middleware-expect-continue-3.620.0.tgz", + "integrity": "sha512-QXeRFMLfyQ31nAHLbiTLtk0oHzG9QLMaof5jIfqcUwnOkO8YnQdeqzakrg1Alpy/VQ7aqzIi8qypkBe2KXZz0A==", "requires": { "@aws-sdk/types": "3.609.0", - "@smithy/protocol-http": "^4.0.3", + "@smithy/protocol-http": "^4.1.0", "@smithy/types": "^3.3.0", "tslib": "^2.6.2" } }, "@aws-sdk/middleware-flexible-checksums": { - "version": "3.614.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-flexible-checksums/-/middleware-flexible-checksums-3.614.0.tgz", - "integrity": "sha512-ZLpxVXMboDeMT7p2Kdp5m1uLVKOktkZoMgLvvbe3zbrU4Ji5IU5xVE0aa4X7H28BtuODCs6SLESnPs19bhMKlA==", + "version": "3.620.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-flexible-checksums/-/middleware-flexible-checksums-3.620.0.tgz", + "integrity": "sha512-ftz+NW7qka2sVuwnnO1IzBku5ccP+s5qZGeRTPgrKB7OzRW85gthvIo1vQR2w+OwHFk7WJbbhhWwbCbktnP4UA==", "requires": { "@aws-crypto/crc32": "5.2.0", "@aws-crypto/crc32c": "5.2.0", "@aws-sdk/types": "3.609.0", "@smithy/is-array-buffer": "^3.0.0", - "@smithy/protocol-http": "^4.0.3", + "@smithy/protocol-http": "^4.1.0", "@smithy/types": "^3.3.0", "@smithy/util-utf8": "^3.0.0", "tslib": "^2.6.2" } }, "@aws-sdk/middleware-host-header": { - "version": "3.609.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.609.0.tgz", - "integrity": "sha512-iTKfo158lc4jLDfYeZmYMIBHsn8m6zX+XB6birCSNZ/rrlzAkPbGE43CNdKfvjyWdqgLMRXF+B+OcZRvqhMXPQ==", + "version": "3.620.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.620.0.tgz", + "integrity": "sha512-VMtPEZwqYrII/oUkffYsNWY9PZ9xpNJpMgmyU0rlDQ25O1c0Hk3fJmZRe6pEkAJ0omD7kLrqGl1DUjQVxpd/Rg==", "requires": { "@aws-sdk/types": "3.609.0", - "@smithy/protocol-http": "^4.0.3", + "@smithy/protocol-http": "^4.1.0", "@smithy/types": "^3.3.0", "tslib": "^2.6.2" } @@ -6874,43 +7150,34 @@ } }, "@aws-sdk/middleware-recursion-detection": { - "version": "3.609.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.609.0.tgz", - "integrity": "sha512-6sewsYB7/o/nbUfA99Aa/LokM+a/u4Wpm/X2o0RxOsDtSB795ObebLJe2BxY5UssbGaWkn7LswyfvrdZNXNj1w==", + "version": "3.620.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.620.0.tgz", + "integrity": "sha512-nh91S7aGK3e/o1ck64sA/CyoFw+gAYj2BDOnoNa6ouyCrVJED96ZXWbhye/fz9SgmNUZR2g7GdVpiLpMKZoI5w==", "requires": { "@aws-sdk/types": "3.609.0", - "@smithy/protocol-http": "^4.0.3", + "@smithy/protocol-http": "^4.1.0", "@smithy/types": "^3.3.0", "tslib": "^2.6.2" } }, "@aws-sdk/middleware-sdk-s3": { - "version": "3.614.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.614.0.tgz", - "integrity": "sha512-9fJTaiuuOfFV4FqmUEhPYzrtv7JOfYpB7q65oG3uayVH4ngWHIJkjnnX79zRhNZKdPGta+XIsnZzjEghg82ngA==", + "version": "3.635.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.635.0.tgz", + "integrity": "sha512-RLdYJPEV4JL/7NBoFUs7VlP90X++5FlJdxHz0DzCjmiD3qCviKy+Cym3qg1gBgHwucs5XisuClxDrGokhAdTQw==", "requires": { + "@aws-sdk/core": "3.635.0", "@aws-sdk/types": "3.609.0", "@aws-sdk/util-arn-parser": "3.568.0", + "@smithy/core": "^2.4.0", "@smithy/node-config-provider": "^3.1.4", - "@smithy/protocol-http": "^4.0.3", - "@smithy/signature-v4": "^3.1.2", - "@smithy/smithy-client": "^3.1.7", + "@smithy/protocol-http": "^4.1.0", + "@smithy/signature-v4": "^4.1.0", + "@smithy/smithy-client": "^3.2.0", "@smithy/types": "^3.3.0", "@smithy/util-config-provider": "^3.0.0", - "tslib": "^2.6.2" - } - }, - "@aws-sdk/middleware-signing": { - "version": "3.609.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-signing/-/middleware-signing-3.609.0.tgz", - "integrity": "sha512-2w3dBLjQVKIajYzokO4hduq8/0hSMUYHHmIo1Kdl+MSY8uwRBt12bLL6pyreobTcRMxizvn2ph/CQ9I1ST/WGQ==", - "requires": { - "@aws-sdk/types": "3.609.0", - "@smithy/property-provider": "^3.1.3", - "@smithy/protocol-http": "^4.0.3", - "@smithy/signature-v4": "^3.1.2", - "@smithy/types": "^3.3.0", "@smithy/util-middleware": "^3.0.3", + "@smithy/util-stream": "^3.1.3", + "@smithy/util-utf8": "^3.0.0", "tslib": "^2.6.2" } }, @@ -6925,13 +7192,13 @@ } }, "@aws-sdk/middleware-user-agent": { - "version": "3.614.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.614.0.tgz", - "integrity": "sha512-xUxh0UPQiMTG6E31Yvu6zVYlikrIcFDKljM11CaatInzvZubGTGiX0DjpqRlfGzUNsuPc/zNrKwRP2+wypgqIw==", + "version": "3.645.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.645.0.tgz", + "integrity": "sha512-NpTAtqWK+49lRuxfz7st9for80r4NriCMK0RfdJSoPFVntjsSQiQ7+2nW2XL05uVY633e9DvCAw8YatX3zd1mw==", "requires": { "@aws-sdk/types": "3.609.0", - "@aws-sdk/util-endpoints": "3.614.0", - "@smithy/protocol-http": "^4.0.3", + "@aws-sdk/util-endpoints": "3.645.0", + "@smithy/protocol-http": "^4.1.0", "@smithy/types": "^3.3.0", "tslib": "^2.6.2" } @@ -6950,14 +7217,14 @@ } }, "@aws-sdk/signature-v4-multi-region": { - "version": "3.614.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.614.0.tgz", - "integrity": "sha512-6mW3ONW4oLzxrePznYhz7sNT9ji9Am9ufLeV722tbOVS5lArBOZ6E1oPz0uYBhisUPznWKhcLRMggt7vIJWMng==", + "version": "3.635.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.635.0.tgz", + "integrity": "sha512-J6QY4/invOkpogCHjSaDON1hF03viPpOnsrzVuCvJMmclS/iG62R4EY0wq1alYll0YmSdmKlpJwHMWwGtqK63Q==", "requires": { - "@aws-sdk/middleware-sdk-s3": "3.614.0", + "@aws-sdk/middleware-sdk-s3": "3.635.0", "@aws-sdk/types": "3.609.0", - "@smithy/protocol-http": "^4.0.3", - "@smithy/signature-v4": "^3.1.2", + "@smithy/protocol-http": "^4.1.0", + "@smithy/signature-v4": "^4.1.0", "@smithy/types": "^3.3.0", "tslib": "^2.6.2" } @@ -6992,9 +7259,9 @@ } }, "@aws-sdk/util-endpoints": { - "version": "3.614.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.614.0.tgz", - "integrity": "sha512-wK2cdrXHH4oz4IomV/yrGkftU9A+ITB6nFL+rxxyO78is2ifHJpFdV4aqk4LSkXYPi6CXWNru/Dqc7yiKXgJPw==", + "version": "3.645.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.645.0.tgz", + "integrity": "sha512-Oe+xaU4ic4PB1k3pb5VTC1/MWES13IlgpaQw01bVHGfwP6Yv6zZOxizRzca2Y3E+AyR+nKD7vXtHRY+w3bi4bg==", "requires": { "@aws-sdk/types": "3.609.0", "@smithy/types": "^3.3.0", @@ -7041,6 +7308,53 @@ "tslib": "^2.6.2" } }, + "@chialab/esbuild-plugin-meta-url": { + "version": "0.18.2", + "resolved": "https://registry.npmjs.org/@chialab/esbuild-plugin-meta-url/-/esbuild-plugin-meta-url-0.18.2.tgz", + "integrity": "sha512-uIRIdLvYnw5mLrTRXY0BTgeZx6ANL2/OHkWFl8FaiTYNb7cyXmwEDRE1mh6kBXPRPtGuqv6XSpNX+koEkElu4g==", + "dev": true, + "requires": { + "@chialab/esbuild-rna": "^0.18.1", + "@chialab/estransform": "^0.18.1", + "mime-types": "^2.1.35" + } + }, + "@chialab/esbuild-plugin-worker": { + "version": "0.18.1", + "resolved": "https://registry.npmjs.org/@chialab/esbuild-plugin-worker/-/esbuild-plugin-worker-0.18.1.tgz", + "integrity": "sha512-FCpdhMQkrwBejY+uWo3xLdqHhUK3hbn0ICedyqo97hzRX98ErB2fhRq4LEEPMEaiplF2se2ToYTQaoxHDpkouw==", + "dev": true, + "requires": { + "@chialab/esbuild-plugin-meta-url": "^0.18.2", + "@chialab/esbuild-rna": "^0.18.0", + "@chialab/estransform": "^0.18.1" + } + }, + "@chialab/esbuild-rna": { + "version": "0.18.2", + "resolved": "https://registry.npmjs.org/@chialab/esbuild-rna/-/esbuild-rna-0.18.2.tgz", + "integrity": "sha512-ckzskez7bxstVQ4c5cxbx0DRP2teldzrcSGQl2KPh1VJGdO2ZmRrb6vNkBBD5K3dx9tgTyvskWp4dV+Fbg07Ag==", + "dev": true, + "requires": { + "@chialab/estransform": "^0.18.0", + "@chialab/node-resolve": "^0.18.0" + } + }, + "@chialab/estransform": { + "version": "0.18.1", + "resolved": "https://registry.npmjs.org/@chialab/estransform/-/estransform-0.18.1.tgz", + "integrity": "sha512-W/WmjpQL2hndD0/XfR0FcPBAUj+aLNeoAVehOjV/Q9bSnioz0GVSAXXhzp59S33ZynxJBBfn8DNiMTVNJmk4Aw==", + "dev": true, + "requires": { + "@parcel/source-map": "^2.0.0" + } + }, + "@chialab/node-resolve": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/@chialab/node-resolve/-/node-resolve-0.18.0.tgz", + "integrity": "sha512-eV1m70Qn9pLY9xwFmZ2FlcOzwiaUywsJ7NB/ud8VB7DouvCQtIHkQ3Om7uPX0ojXGEG1LCyO96kZkvbNTxNu0Q==", + "dev": true + }, "@codemirror/state": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.1.0.tgz", @@ -7061,147 +7375,147 @@ } }, "@esbuild/aix-ppc64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.23.0.tgz", - "integrity": "sha512-3sG8Zwa5fMcA9bgqB8AfWPQ+HFke6uD3h1s3RIwUNK8EG7a4buxvuFTs3j1IMs2NXAk9F30C/FF4vxRgQCcmoQ==", + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.23.1.tgz", + "integrity": "sha512-6VhYk1diRqrhBAqpJEdjASR/+WVRtfjpqKuNw11cLiaWpAT/Uu+nokB+UJnevzy/P9C/ty6AOe0dwueMrGh/iQ==", "optional": true }, "@esbuild/android-arm": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.23.0.tgz", - "integrity": "sha512-+KuOHTKKyIKgEEqKbGTK8W7mPp+hKinbMBeEnNzjJGyFcWsfrXjSTNluJHCY1RqhxFurdD8uNXQDei7qDlR6+g==", + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.23.1.tgz", + "integrity": "sha512-uz6/tEy2IFm9RYOyvKl88zdzZfwEfKZmnX9Cj1BHjeSGNuGLuMD1kR8y5bteYmwqKm1tj8m4cb/aKEorr6fHWQ==", "optional": true }, "@esbuild/android-arm64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.23.0.tgz", - "integrity": "sha512-EuHFUYkAVfU4qBdyivULuu03FhJO4IJN9PGuABGrFy4vUuzk91P2d+npxHcFdpUnfYKy0PuV+n6bKIpHOB3prQ==", + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.23.1.tgz", + "integrity": "sha512-xw50ipykXcLstLeWH7WRdQuysJqejuAGPd30vd1i5zSyKK3WE+ijzHmLKxdiCMtH1pHz78rOg0BKSYOSB/2Khw==", "optional": true }, "@esbuild/android-x64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.23.0.tgz", - "integrity": "sha512-WRrmKidLoKDl56LsbBMhzTTBxrsVwTKdNbKDalbEZr0tcsBgCLbEtoNthOW6PX942YiYq8HzEnb4yWQMLQuipQ==", + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.23.1.tgz", + "integrity": "sha512-nlN9B69St9BwUoB+jkyU090bru8L0NA3yFvAd7k8dNsVH8bi9a8cUAUSEcEEgTp2z3dbEDGJGfP6VUnkQnlReg==", "optional": true }, "@esbuild/darwin-arm64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.23.0.tgz", - "integrity": "sha512-YLntie/IdS31H54Ogdn+v50NuoWF5BDkEUFpiOChVa9UnKpftgwzZRrI4J132ETIi+D8n6xh9IviFV3eXdxfow==", + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.23.1.tgz", + "integrity": "sha512-YsS2e3Wtgnw7Wq53XXBLcV6JhRsEq8hkfg91ESVadIrzr9wO6jJDMZnCQbHm1Guc5t/CdDiFSSfWP58FNuvT3Q==", "optional": true }, "@esbuild/darwin-x64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.23.0.tgz", - "integrity": "sha512-IMQ6eme4AfznElesHUPDZ+teuGwoRmVuuixu7sv92ZkdQcPbsNHzutd+rAfaBKo8YK3IrBEi9SLLKWJdEvJniQ==", + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.23.1.tgz", + "integrity": "sha512-aClqdgTDVPSEGgoCS8QDG37Gu8yc9lTHNAQlsztQ6ENetKEO//b8y31MMu2ZaPbn4kVsIABzVLXYLhCGekGDqw==", "optional": true }, "@esbuild/freebsd-arm64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.23.0.tgz", - "integrity": "sha512-0muYWCng5vqaxobq6LB3YNtevDFSAZGlgtLoAc81PjUfiFz36n4KMpwhtAd4he8ToSI3TGyuhyx5xmiWNYZFyw==", + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.23.1.tgz", + "integrity": "sha512-h1k6yS8/pN/NHlMl5+v4XPfikhJulk4G+tKGFIOwURBSFzE8bixw1ebjluLOjfwtLqY0kewfjLSrO6tN2MgIhA==", "optional": true }, "@esbuild/freebsd-x64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.23.0.tgz", - "integrity": "sha512-XKDVu8IsD0/q3foBzsXGt/KjD/yTKBCIwOHE1XwiXmrRwrX6Hbnd5Eqn/WvDekddK21tfszBSrE/WMaZh+1buQ==", + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.23.1.tgz", + "integrity": "sha512-lK1eJeyk1ZX8UklqFd/3A60UuZ/6UVfGT2LuGo3Wp4/z7eRTRYY+0xOu2kpClP+vMTi9wKOfXi2vjUpO1Ro76g==", "optional": true }, "@esbuild/linux-arm": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.23.0.tgz", - "integrity": "sha512-SEELSTEtOFu5LPykzA395Mc+54RMg1EUgXP+iw2SJ72+ooMwVsgfuwXo5Fn0wXNgWZsTVHwY2cg4Vi/bOD88qw==", + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.23.1.tgz", + "integrity": "sha512-CXXkzgn+dXAPs3WBwE+Kvnrf4WECwBdfjfeYHpMeVxWE0EceB6vhWGShs6wi0IYEqMSIzdOF1XjQ/Mkm5d7ZdQ==", "optional": true }, "@esbuild/linux-arm64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.23.0.tgz", - "integrity": "sha512-j1t5iG8jE7BhonbsEg5d9qOYcVZv/Rv6tghaXM/Ug9xahM0nX/H2gfu6X6z11QRTMT6+aywOMA8TDkhPo8aCGw==", + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.23.1.tgz", + "integrity": "sha512-/93bf2yxencYDnItMYV/v116zff6UyTjo4EtEQjUBeGiVpMmffDNUyD9UN2zV+V3LRV3/on4xdZ26NKzn6754g==", "optional": true }, "@esbuild/linux-ia32": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.23.0.tgz", - "integrity": "sha512-P7O5Tkh2NbgIm2R6x1zGJJsnacDzTFcRWZyTTMgFdVit6E98LTxO+v8LCCLWRvPrjdzXHx9FEOA8oAZPyApWUA==", + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.23.1.tgz", + "integrity": "sha512-VTN4EuOHwXEkXzX5nTvVY4s7E/Krz7COC8xkftbbKRYAl96vPiUssGkeMELQMOnLOJ8k3BY1+ZY52tttZnHcXQ==", "optional": true }, "@esbuild/linux-loong64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.23.0.tgz", - "integrity": "sha512-InQwepswq6urikQiIC/kkx412fqUZudBO4SYKu0N+tGhXRWUqAx+Q+341tFV6QdBifpjYgUndV1hhMq3WeJi7A==", + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.23.1.tgz", + "integrity": "sha512-Vx09LzEoBa5zDnieH8LSMRToj7ir/Jeq0Gu6qJ/1GcBq9GkfoEAoXvLiW1U9J1qE/Y/Oyaq33w5p2ZWrNNHNEw==", "optional": true }, "@esbuild/linux-mips64el": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.23.0.tgz", - "integrity": "sha512-J9rflLtqdYrxHv2FqXE2i1ELgNjT+JFURt/uDMoPQLcjWQA5wDKgQA4t/dTqGa88ZVECKaD0TctwsUfHbVoi4w==", + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.23.1.tgz", + "integrity": "sha512-nrFzzMQ7W4WRLNUOU5dlWAqa6yVeI0P78WKGUo7lg2HShq/yx+UYkeNSE0SSfSure0SqgnsxPvmAUu/vu0E+3Q==", "optional": true }, "@esbuild/linux-ppc64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.23.0.tgz", - "integrity": "sha512-cShCXtEOVc5GxU0fM+dsFD10qZ5UpcQ8AM22bYj0u/yaAykWnqXJDpd77ublcX6vdDsWLuweeuSNZk4yUxZwtw==", + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.23.1.tgz", + "integrity": "sha512-dKN8fgVqd0vUIjxuJI6P/9SSSe/mB9rvA98CSH2sJnlZ/OCZWO1DJvxj8jvKTfYUdGfcq2dDxoKaC6bHuTlgcw==", "optional": true }, "@esbuild/linux-riscv64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.23.0.tgz", - "integrity": "sha512-HEtaN7Y5UB4tZPeQmgz/UhzoEyYftbMXrBCUjINGjh3uil+rB/QzzpMshz3cNUxqXN7Vr93zzVtpIDL99t9aRw==", + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.23.1.tgz", + "integrity": "sha512-5AV4Pzp80fhHL83JM6LoA6pTQVWgB1HovMBsLQ9OZWLDqVY8MVobBXNSmAJi//Csh6tcY7e7Lny2Hg1tElMjIA==", "optional": true }, "@esbuild/linux-s390x": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.23.0.tgz", - "integrity": "sha512-WDi3+NVAuyjg/Wxi+o5KPqRbZY0QhI9TjrEEm+8dmpY9Xir8+HE/HNx2JoLckhKbFopW0RdO2D72w8trZOV+Wg==", + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.23.1.tgz", + "integrity": "sha512-9ygs73tuFCe6f6m/Tb+9LtYxWR4c9yg7zjt2cYkjDbDpV/xVn+68cQxMXCjUpYwEkze2RcU/rMnfIXNRFmSoDw==", "optional": true }, "@esbuild/linux-x64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.23.0.tgz", - "integrity": "sha512-a3pMQhUEJkITgAw6e0bWA+F+vFtCciMjW/LPtoj99MhVt+Mfb6bbL9hu2wmTZgNd994qTAEw+U/r6k3qHWWaOQ==", + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.23.1.tgz", + "integrity": "sha512-EV6+ovTsEXCPAp58g2dD68LxoP/wK5pRvgy0J/HxPGB009omFPv3Yet0HiaqvrIrgPTBuC6wCH1LTOY91EO5hQ==", "optional": true }, "@esbuild/netbsd-x64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.23.0.tgz", - "integrity": "sha512-cRK+YDem7lFTs2Q5nEv/HHc4LnrfBCbH5+JHu6wm2eP+d8OZNoSMYgPZJq78vqQ9g+9+nMuIsAO7skzphRXHyw==", + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.23.1.tgz", + "integrity": "sha512-aevEkCNu7KlPRpYLjwmdcuNz6bDFiE7Z8XC4CPqExjTvrHugh28QzUXVOZtiYghciKUacNktqxdpymplil1beA==", "optional": true }, "@esbuild/openbsd-arm64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.23.0.tgz", - "integrity": "sha512-suXjq53gERueVWu0OKxzWqk7NxiUWSUlrxoZK7usiF50C6ipColGR5qie2496iKGYNLhDZkPxBI3erbnYkU0rQ==", + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.23.1.tgz", + "integrity": "sha512-3x37szhLexNA4bXhLrCC/LImN/YtWis6WXr1VESlfVtVeoFJBRINPJ3f0a/6LV8zpikqoUg4hyXw0sFBt5Cr+Q==", "optional": true }, "@esbuild/openbsd-x64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.23.0.tgz", - "integrity": "sha512-6p3nHpby0DM/v15IFKMjAaayFhqnXV52aEmv1whZHX56pdkK+MEaLoQWj+H42ssFarP1PcomVhbsR4pkz09qBg==", + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.23.1.tgz", + "integrity": "sha512-aY2gMmKmPhxfU+0EdnN+XNtGbjfQgwZj43k8G3fyrDM/UdZww6xrWxmDkuz2eCZchqVeABjV5BpildOrUbBTqA==", "optional": true }, "@esbuild/sunos-x64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.23.0.tgz", - "integrity": "sha512-BFelBGfrBwk6LVrmFzCq1u1dZbG4zy/Kp93w2+y83Q5UGYF1d8sCzeLI9NXjKyujjBBniQa8R8PzLFAUrSM9OA==", + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.23.1.tgz", + "integrity": "sha512-RBRT2gqEl0IKQABT4XTj78tpk9v7ehp+mazn2HbUeZl1YMdaGAQqhapjGTCe7uw7y0frDi4gS0uHzhvpFuI1sA==", "optional": true }, "@esbuild/win32-arm64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.23.0.tgz", - "integrity": "sha512-lY6AC8p4Cnb7xYHuIxQ6iYPe6MfO2CC43XXKo9nBXDb35krYt7KGhQnOkRGar5psxYkircpCqfbNDB4uJbS2jQ==", + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.23.1.tgz", + "integrity": "sha512-4O+gPR5rEBe2FpKOVyiJ7wNDPA8nGzDuJ6gN4okSA1gEOYZ67N8JPk58tkWtdtPeLz7lBnY6I5L3jdsr3S+A6A==", "optional": true }, "@esbuild/win32-ia32": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.23.0.tgz", - "integrity": "sha512-7L1bHlOTcO4ByvI7OXVI5pNN6HSu6pUQq9yodga8izeuB1KcT2UkHaH6118QJwopExPn0rMHIseCTx1CRo/uNA==", + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.23.1.tgz", + "integrity": "sha512-BcaL0Vn6QwCwre3Y717nVHZbAa4UBEigzFm6VdsVdT/MbZ38xoj1X9HPkZhbmaBGUD1W8vxAfffbDe8bA6AKnQ==", "optional": true }, "@esbuild/win32-x64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.23.0.tgz", - "integrity": "sha512-Arm+WgUFLUATuoxCJcahGuk6Yj9Pzxd6l11Zb/2aAuv5kWWvvfhLFo2fni4uSK5vzlUdCGZ/BdV5tH8klj8p8g==", + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.23.1.tgz", + "integrity": "sha512-BHpFFeslkWrXWyUPnbKm+xYYVYruCinGcftSBaa8zoF9hZO4BcSCFUvHVTtzpIY6YzUnYtuEhZ+C9iEXjxnasg==", "optional": true }, "@eslint-community/eslint-utils": { @@ -7382,6 +7696,21 @@ "fastq": "^1.6.0" } }, + "@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, + "requires": { + "detect-libc": "^1.0.3" + } + }, + "@rtsao/scc": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", + "integrity": "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==", + "dev": true + }, "@smithy/abort-controller": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-3.1.1.tgz", @@ -7421,24 +7750,26 @@ } }, "@smithy/core": { - "version": "2.2.6", - "resolved": "https://registry.npmjs.org/@smithy/core/-/core-2.2.6.tgz", - "integrity": "sha512-tBbVIv/ui7/lLTKayYJJvi8JLVL2SwOQTbNFEOrvzSE3ktByvsa1erwBOnAMo8N5Vu30g7lN4lLStrU75oDGuw==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@smithy/core/-/core-2.4.0.tgz", + "integrity": "sha512-cHXq+FneIF/KJbt4q4pjN186+Jf4ZB0ZOqEaZMBhT79srEyGDDBV31NqBRBjazz8ppQ1bJbDJMY9ba5wKFV36w==", "requires": { - "@smithy/middleware-endpoint": "^3.0.5", - "@smithy/middleware-retry": "^3.0.9", + "@smithy/middleware-endpoint": "^3.1.0", + "@smithy/middleware-retry": "^3.0.15", "@smithy/middleware-serde": "^3.0.3", - "@smithy/protocol-http": "^4.0.3", - "@smithy/smithy-client": "^3.1.7", + "@smithy/protocol-http": "^4.1.0", + "@smithy/smithy-client": "^3.2.0", "@smithy/types": "^3.3.0", + "@smithy/util-body-length-browser": "^3.0.0", "@smithy/util-middleware": "^3.0.3", + "@smithy/util-utf8": "^3.0.0", "tslib": "^2.6.2" } }, "@smithy/credential-provider-imds": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-3.1.4.tgz", - "integrity": "sha512-NKyH01m97Xa5xf3pB2QOF3lnuE8RIK0hTVNU5zvZAwZU8uspYO4DHQVlK+Y5gwSrujTfHvbfd1D9UFJAc0iYKQ==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-3.2.0.tgz", + "integrity": "sha512-0SCIzgd8LYZ9EJxUjLXBmEKSZR/P/w6l7Rz/pab9culE/RWuqelAKGJvn5qUOl8BgX8Yj5HWM50A5hiB/RzsgA==", "requires": { "@smithy/node-config-provider": "^3.1.4", "@smithy/property-provider": "^3.1.3", @@ -7459,11 +7790,11 @@ } }, "@smithy/eventstream-serde-browser": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-browser/-/eventstream-serde-browser-3.0.4.tgz", - "integrity": "sha512-Eo4anLZX6ltGJTZ5yJMc80gZPYYwBn44g0h7oFq6et+TYr5dUsTpIcDbz2evsOKIZhZ7zBoFWHtBXQ4QQeb5xA==", + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-browser/-/eventstream-serde-browser-3.0.6.tgz", + "integrity": "sha512-2hM54UWQUOrki4BtsUI1WzmD13/SeaqT/AB3EUJKbcver/WgKNaiJ5y5F5XXuVe6UekffVzuUDrBZVAA3AWRpQ==", "requires": { - "@smithy/eventstream-serde-universal": "^3.0.4", + "@smithy/eventstream-serde-universal": "^3.0.5", "@smithy/types": "^3.3.0", "tslib": "^2.6.2" } @@ -7478,19 +7809,19 @@ } }, "@smithy/eventstream-serde-node": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-node/-/eventstream-serde-node-3.0.4.tgz", - "integrity": "sha512-mjlG0OzGAYuUpdUpflfb9zyLrBGgmQmrobNT8b42ZTsGv/J03+t24uhhtVEKG/b2jFtPIHF74Bq+VUtbzEKOKg==", + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-node/-/eventstream-serde-node-3.0.5.tgz", + "integrity": "sha512-+upXvnHNyZP095s11jF5dhGw/Ihzqwl5G+/KtMnoQOpdfC3B5HYCcDVG9EmgkhJMXJlM64PyN5gjJl0uXFQehQ==", "requires": { - "@smithy/eventstream-serde-universal": "^3.0.4", + "@smithy/eventstream-serde-universal": "^3.0.5", "@smithy/types": "^3.3.0", "tslib": "^2.6.2" } }, "@smithy/eventstream-serde-universal": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-universal/-/eventstream-serde-universal-3.0.4.tgz", - "integrity": "sha512-Od9dv8zh3PgOD7Vj4T3HSuox16n0VG8jJIM2gvKASL6aCtcS8CfHZDWe1Ik3ZXW6xBouU+45Q5wgoliWDZiJ0A==", + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-universal/-/eventstream-serde-universal-3.0.5.tgz", + "integrity": "sha512-5u/nXbyoh1s4QxrvNre9V6vfyoLWuiVvvd5TlZjGThIikc3G+uNiG9uOTCWweSRjv1asdDIWK7nOmN7le4RYHQ==", "requires": { "@smithy/eventstream-codec": "^3.1.2", "@smithy/types": "^3.3.0", @@ -7498,11 +7829,11 @@ } }, "@smithy/fetch-http-handler": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-3.2.1.tgz", - "integrity": "sha512-0w0bgUvZmfa0vHN8a+moByhCJT07WN6AHKEhFSOLsDpnszm+5dLVv5utGaqbhOrZ/aF5x3xuPMs/oMCd+4O5xg==", + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-3.2.4.tgz", + "integrity": "sha512-kBprh5Gs5h7ug4nBWZi1FZthdqSM+T7zMmsZxx0IBvWUn7dK3diz2SHn7Bs4dQGFDk8plDv375gzenDoNwrXjg==", "requires": { - "@smithy/protocol-http": "^4.0.3", + "@smithy/protocol-http": "^4.1.0", "@smithy/querystring-builder": "^3.0.3", "@smithy/types": "^3.3.0", "@smithy/util-base64": "^3.0.0", @@ -7569,19 +7900,19 @@ } }, "@smithy/middleware-content-length": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-3.0.3.tgz", - "integrity": "sha512-Dbz2bzexReYIQDWMr+gZhpwBetNXzbhnEMhYKA6urqmojO14CsXjnsoPYO8UL/xxcawn8ZsuVU61ElkLSltIUQ==", + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-3.0.5.tgz", + "integrity": "sha512-ILEzC2eyxx6ncej3zZSwMpB5RJ0zuqH7eMptxC4KN3f+v9bqT8ohssKbhNR78k/2tWW+KS5Spw+tbPF4Ejyqvw==", "requires": { - "@smithy/protocol-http": "^4.0.3", + "@smithy/protocol-http": "^4.1.0", "@smithy/types": "^3.3.0", "tslib": "^2.6.2" } }, "@smithy/middleware-endpoint": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-3.0.5.tgz", - "integrity": "sha512-V4acqqrh5tDxUEGVTOgf2lYMZqPQsoGntCrjrJZEeBzEzDry2d2vcI1QCXhGltXPPY+BMc6eksZMguA9fIY8vA==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-3.1.0.tgz", + "integrity": "sha512-5y5aiKCEwg9TDPB4yFE7H6tYvGFf1OJHNczeY10/EFF8Ir8jZbNntQJxMWNfeQjC1mxPsaQ6mR9cvQbf+0YeMw==", "requires": { "@smithy/middleware-serde": "^3.0.3", "@smithy/node-config-provider": "^3.1.4", @@ -7593,14 +7924,14 @@ } }, "@smithy/middleware-retry": { - "version": "3.0.9", - "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-3.0.9.tgz", - "integrity": "sha512-Mrv9omExU1gA7Y0VEJG2LieGfPYtwwcEiOnVGZ54a37NEMr66TJ0glFslOJFuKWG6izg5DpKIUmDV9rRxjm47Q==", + "version": "3.0.15", + "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-3.0.15.tgz", + "integrity": "sha512-iTMedvNt1ApdvkaoE8aSDuwaoc+BhvHqttbA/FO4Ty+y/S5hW6Ci/CTScG7vam4RYJWZxdTElc3MEfHRVH6cgQ==", "requires": { "@smithy/node-config-provider": "^3.1.4", - "@smithy/protocol-http": "^4.0.3", + "@smithy/protocol-http": "^4.1.0", "@smithy/service-error-classification": "^3.0.3", - "@smithy/smithy-client": "^3.1.7", + "@smithy/smithy-client": "^3.2.0", "@smithy/types": "^3.3.0", "@smithy/util-middleware": "^3.0.3", "@smithy/util-retry": "^3.0.3", @@ -7645,12 +7976,12 @@ } }, "@smithy/node-http-handler": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-3.1.2.tgz", - "integrity": "sha512-Td3rUNI7qqtoSLTsJBtsyfoG4cF/XMFmJr6Z2dX8QNzIi6tIW6YmuyFml8mJ2cNpyWNqITKbROMOFrvQjmsOvw==", + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-3.1.4.tgz", + "integrity": "sha512-+UmxgixgOr/yLsUxcEKGH0fMNVteJFGkmRltYFHnBMlogyFdpzn2CwqWmxOrfJELhV34v0WSlaqG1UtE1uXlJg==", "requires": { "@smithy/abort-controller": "^3.1.1", - "@smithy/protocol-http": "^4.0.3", + "@smithy/protocol-http": "^4.1.0", "@smithy/querystring-builder": "^3.0.3", "@smithy/types": "^3.3.0", "tslib": "^2.6.2" @@ -7666,9 +7997,9 @@ } }, "@smithy/protocol-http": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-4.0.3.tgz", - "integrity": "sha512-x5jmrCWwQlx+Zv4jAtc33ijJ+vqqYN+c/ZkrnpvEe/uDas7AT7A/4Rc2CdfxgWv4WFGmEqODIrrUToPN6DDkGw==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-4.1.0.tgz", + "integrity": "sha512-dPVoHYQ2wcHooGXg3LQisa1hH0e4y0pAddPMeeUPipI1tEOqL6A4N0/G7abeq+K8wrwSgjk4C0wnD1XZpJm5aA==", "requires": { "@smithy/types": "^3.3.0", "tslib": "^2.6.2" @@ -7711,11 +8042,12 @@ } }, "@smithy/signature-v4": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-3.1.2.tgz", - "integrity": "sha512-3BcPylEsYtD0esM4Hoyml/+s7WP2LFhcM3J2AGdcL2vx9O60TtfpDOL72gjb4lU8NeRPeKAwR77YNyyGvMbuEA==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-4.1.0.tgz", + "integrity": "sha512-aRryp2XNZeRcOtuJoxjydO6QTaVhxx/vjaR+gx7ZjaFgrgPRyZ3HCTbfwqYj6ZWEBHkCSUfcaymKPURaByukag==", "requires": { "@smithy/is-array-buffer": "^3.0.0", + "@smithy/protocol-http": "^4.1.0", "@smithy/types": "^3.3.0", "@smithy/util-hex-encoding": "^3.0.0", "@smithy/util-middleware": "^3.0.3", @@ -7725,15 +8057,15 @@ } }, "@smithy/smithy-client": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-3.1.7.tgz", - "integrity": "sha512-nZbJZB0XI3YnaFBWGDBr7kjaew6O0oNYNmopyIz6gKZEbxzrtH7rwvU1GcVxcSFoOwWecLJEe79fxEMljHopFQ==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-3.2.0.tgz", + "integrity": "sha512-pDbtxs8WOhJLJSeaF/eAbPgXg4VVYFlRcL/zoNYA5WbG3wBL06CHtBSg53ppkttDpAJ/hdiede+xApip1CwSLw==", "requires": { - "@smithy/middleware-endpoint": "^3.0.5", + "@smithy/middleware-endpoint": "^3.1.0", "@smithy/middleware-stack": "^3.0.3", - "@smithy/protocol-http": "^4.0.3", + "@smithy/protocol-http": "^4.1.0", "@smithy/types": "^3.3.0", - "@smithy/util-stream": "^3.0.6", + "@smithy/util-stream": "^3.1.3", "tslib": "^2.6.2" } }, @@ -7799,27 +8131,27 @@ } }, "@smithy/util-defaults-mode-browser": { - "version": "3.0.9", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-3.0.9.tgz", - "integrity": "sha512-WKPcElz92MAQG09miBdb0GxEH/MwD5GfE8g07WokITq5g6J1ROQfYCKC1wNnkqAGfrSywT7L0rdvvqlBplqiyA==", + "version": "3.0.15", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-3.0.15.tgz", + "integrity": "sha512-FZ4Psa3vjp8kOXcd3HJOiDPBCWtiilLl57r0cnNtq/Ga9RSDrM5ERL6xt+tO43+2af6Pn5Yp92x2n5vPuduNfg==", "requires": { "@smithy/property-provider": "^3.1.3", - "@smithy/smithy-client": "^3.1.7", + "@smithy/smithy-client": "^3.2.0", "@smithy/types": "^3.3.0", "bowser": "^2.11.0", "tslib": "^2.6.2" } }, "@smithy/util-defaults-mode-node": { - "version": "3.0.9", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-3.0.9.tgz", - "integrity": "sha512-dQLrUqFxqpf0GvEKEuFdgXcdZwz6oFm752h4d6C7lQz+RLddf761L2r7dSwGWzESMMB3wKj0jL+skRhEGlecjw==", + "version": "3.0.15", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-3.0.15.tgz", + "integrity": "sha512-KSyAAx2q6d0t6f/S4XB2+3+6aQacm3aLMhs9aLMqn18uYGUepbdssfogW5JQZpc6lXNBnp0tEnR5e9CEKmEd7A==", "requires": { "@smithy/config-resolver": "^3.0.5", - "@smithy/credential-provider-imds": "^3.1.4", + "@smithy/credential-provider-imds": "^3.2.0", "@smithy/node-config-provider": "^3.1.4", "@smithy/property-provider": "^3.1.3", - "@smithy/smithy-client": "^3.1.7", + "@smithy/smithy-client": "^3.2.0", "@smithy/types": "^3.3.0", "tslib": "^2.6.2" } @@ -7862,12 +8194,12 @@ } }, "@smithy/util-stream": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-3.0.6.tgz", - "integrity": "sha512-w9i//7egejAIvplX821rPWWgaiY1dxsQUw0hXX7qwa/uZ9U3zplqTQ871jWadkcVB9gFDhkPWYVZf4yfFbZ0xA==", + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-3.1.3.tgz", + "integrity": "sha512-FIv/bRhIlAxC0U7xM1BCnF2aDRPq0UaelqBHkM2lsCp26mcBbgI0tCVTv+jGdsQLUmAMybua/bjDsSu8RQHbmw==", "requires": { - "@smithy/fetch-http-handler": "^3.2.1", - "@smithy/node-http-handler": "^3.1.2", + "@smithy/fetch-http-handler": "^3.2.4", + "@smithy/node-http-handler": "^3.1.4", "@smithy/types": "^3.3.0", "@smithy/util-base64": "^3.0.0", "@smithy/util-buffer-from": "^3.0.0", @@ -7952,12 +8284,12 @@ "dev": true }, "@types/node": { - "version": "20.14.10", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.10.tgz", - "integrity": "sha512-MdiXf+nDuMvY0gJKxyfZ7/6UFsETO7mGKF54MVD/ekJS6HdFtpZFBgrh6Pseu64XTb2MLyFPlbW6hj8HYRQNOQ==", + "version": "22.5.4", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.5.4.tgz", + "integrity": "sha512-FDuKUJQm/ju9fT/SeX/6+gBzoPzlVCzfzmGkwKvRHQVxi4BntVbyIwf6a4Xn62mrvndLiml6z/UBXIdEVjQLXg==", "dev": true, "requires": { - "undici-types": "~5.26.4" + "undici-types": "~6.19.2" } }, "@types/pouchdb": { @@ -8080,9 +8412,9 @@ } }, "@types/pouchdb-core": { - "version": "7.0.14", - "resolved": "https://registry.npmjs.org/@types/pouchdb-core/-/pouchdb-core-7.0.14.tgz", - "integrity": "sha512-ec63FAtmc1ForP4oNgLZoPYqjaBrkGTWA7jm+rQ7SEtIpOPpN4NCWF2/JyrhO64jjIzO5qkV/YNEpZlEZFxCcw==", + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/pouchdb-core/-/pouchdb-core-7.0.15.tgz", + "integrity": "sha512-gq1Qbqn9nCaAKRRv6fRHZ4/ER+QYEwSXBZlDQcxwdbPrtZO8EhIn2Bct0AlguaSEdFcABfbaxxyQwFINkNQ9dQ==", "dev": true, "requires": { "@types/debug": "*", @@ -8159,16 +8491,16 @@ } }, "@typescript-eslint/eslint-plugin": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.16.0.tgz", - "integrity": "sha512-py1miT6iQpJcs1BiJjm54AMzeuMPBSPuKPlnT8HlfudbcS5rYeX5jajpLf3mrdRh9dA/Ec2FVUY0ifeVNDIhZw==", + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.4.0.tgz", + "integrity": "sha512-rg8LGdv7ri3oAlenMACk9e+AR4wUV0yrrG+XKsGKOK0EVgeEDqurkXMPILG2836fW4ibokTB5v4b6Z9+GYQDEw==", "dev": true, "requires": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "7.16.0", - "@typescript-eslint/type-utils": "7.16.0", - "@typescript-eslint/utils": "7.16.0", - "@typescript-eslint/visitor-keys": "7.16.0", + "@typescript-eslint/scope-manager": "8.4.0", + "@typescript-eslint/type-utils": "8.4.0", + "@typescript-eslint/utils": "8.4.0", + "@typescript-eslint/visitor-keys": "8.4.0", "graphemer": "^1.4.0", "ignore": "^5.3.1", "natural-compare": "^1.4.0", @@ -8176,56 +8508,56 @@ } }, "@typescript-eslint/parser": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.16.0.tgz", - "integrity": "sha512-ar9E+k7CU8rWi2e5ErzQiC93KKEFAXA2Kky0scAlPcxYblLt8+XZuHUZwlyfXILyQa95P6lQg+eZgh/dDs3+Vw==", + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.4.0.tgz", + "integrity": "sha512-NHgWmKSgJk5K9N16GIhQ4jSobBoJwrmURaLErad0qlLjrpP5bECYg+wxVTGlGZmJbU03jj/dfnb6V9bw+5icsA==", "dev": true, "requires": { - "@typescript-eslint/scope-manager": "7.16.0", - "@typescript-eslint/types": "7.16.0", - "@typescript-eslint/typescript-estree": "7.16.0", - "@typescript-eslint/visitor-keys": "7.16.0", + "@typescript-eslint/scope-manager": "8.4.0", + "@typescript-eslint/types": "8.4.0", + "@typescript-eslint/typescript-estree": "8.4.0", + "@typescript-eslint/visitor-keys": "8.4.0", "debug": "^4.3.4" } }, "@typescript-eslint/scope-manager": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.16.0.tgz", - "integrity": "sha512-8gVv3kW6n01Q6TrI1cmTZ9YMFi3ucDT7i7aI5lEikk2ebk1AEjrwX8MDTdaX5D7fPXMBLvnsaa0IFTAu+jcfOw==", + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.4.0.tgz", + "integrity": "sha512-n2jFxLeY0JmKfUqy3P70rs6vdoPjHK8P/w+zJcV3fk0b0BwRXC/zxRTEnAsgYT7MwdQDt/ZEbtdzdVC+hcpF0A==", "dev": true, "requires": { - "@typescript-eslint/types": "7.16.0", - "@typescript-eslint/visitor-keys": "7.16.0" + "@typescript-eslint/types": "8.4.0", + "@typescript-eslint/visitor-keys": "8.4.0" } }, "@typescript-eslint/type-utils": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.16.0.tgz", - "integrity": "sha512-j0fuUswUjDHfqV/UdW6mLtOQQseORqfdmoBNDFOqs9rvNVR2e+cmu6zJu/Ku4SDuqiJko6YnhwcL8x45r8Oqxg==", + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.4.0.tgz", + "integrity": "sha512-pu2PAmNrl9KX6TtirVOrbLPLwDmASpZhK/XU7WvoKoCUkdtq9zF7qQ7gna0GBZFN0hci0vHaSusiL2WpsQk37A==", "dev": true, "requires": { - "@typescript-eslint/typescript-estree": "7.16.0", - "@typescript-eslint/utils": "7.16.0", + "@typescript-eslint/typescript-estree": "8.4.0", + "@typescript-eslint/utils": "8.4.0", "debug": "^4.3.4", "ts-api-utils": "^1.3.0" } }, "@typescript-eslint/types": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.16.0.tgz", - "integrity": "sha512-fecuH15Y+TzlUutvUl9Cc2XJxqdLr7+93SQIbcZfd4XRGGKoxyljK27b+kxKamjRkU7FYC6RrbSCg0ALcZn/xw==", + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.4.0.tgz", + "integrity": "sha512-T1RB3KQdskh9t3v/qv7niK6P8yvn7ja1mS7QK7XfRVL6wtZ8/mFs/FHf4fKvTA0rKnqnYxl/uHFNbnEt0phgbw==", "dev": true }, "@typescript-eslint/typescript-estree": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.16.0.tgz", - "integrity": "sha512-a5NTvk51ZndFuOLCh5OaJBELYc2O3Zqxfl3Js78VFE1zE46J2AaVuW+rEbVkQznjkmlzWsUI15BG5tQMixzZLw==", + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.4.0.tgz", + "integrity": "sha512-kJ2OIP4dQw5gdI4uXsaxUZHRwWAGpREJ9Zq6D5L0BweyOrWsL6Sz0YcAZGWhvKnH7fm1J5YFE1JrQL0c9dd53A==", "dev": true, "requires": { - "@typescript-eslint/types": "7.16.0", - "@typescript-eslint/visitor-keys": "7.16.0", + "@typescript-eslint/types": "8.4.0", + "@typescript-eslint/visitor-keys": "8.4.0", "debug": "^4.3.4", - "globby": "^11.1.0", + "fast-glob": "^3.3.2", "is-glob": "^4.0.3", "minimatch": "^9.0.4", "semver": "^7.6.0", @@ -8244,24 +8576,24 @@ } }, "@typescript-eslint/utils": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.16.0.tgz", - "integrity": "sha512-PqP4kP3hb4r7Jav+NiRCntlVzhxBNWq6ZQ+zQwII1y/G/1gdIPeYDCKr2+dH6049yJQsWZiHU6RlwvIFBXXGNA==", + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.4.0.tgz", + "integrity": "sha512-swULW8n1IKLjRAgciCkTCafyTHHfwVQFt8DovmaF69sKbOxTSFMmIZaSHjqO9i/RV0wIblaawhzvtva8Nmm7lQ==", "dev": true, "requires": { "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "7.16.0", - "@typescript-eslint/types": "7.16.0", - "@typescript-eslint/typescript-estree": "7.16.0" + "@typescript-eslint/scope-manager": "8.4.0", + "@typescript-eslint/types": "8.4.0", + "@typescript-eslint/typescript-estree": "8.4.0" } }, "@typescript-eslint/visitor-keys": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.16.0.tgz", - "integrity": "sha512-rMo01uPy9C7XxG7AFsxa8zLnWXTF8N3PYclekWSrurvhwiw1eW88mrKiAYe6s53AUY57nTRz8dJsuuXdkAhzCg==", + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.4.0.tgz", + "integrity": "sha512-zTQD6WLNTre1hj5wp09nBIDiOc2U5r/qmzo7wxPn4ZgAjHql09EofqhF9WF+fZHzL5aCyaIpPcT2hyxl73kr9A==", "dev": true, "requires": { - "@typescript-eslint/types": "7.16.0", + "@typescript-eslint/types": "8.4.0", "eslint-visitor-keys": "^3.4.3" } }, @@ -8337,47 +8669,30 @@ } }, "array-includes": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.7.tgz", - "integrity": "sha512-dlcsNBIiWhPkHdOEEKnehA+RNUWDc4UqFtnIXU4uuYDPtA4LDkr7qip2p0VvFAEXNDr0yWZ9PJyIRiGjRLQzwQ==", + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.8.tgz", + "integrity": "sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ==", "dev": true, "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "get-intrinsic": "^1.2.1", - "is-string": "^1.0.7" - } - }, - "array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "dev": true - }, - "array.prototype.filter": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/array.prototype.filter/-/array.prototype.filter-1.0.3.tgz", - "integrity": "sha512-VizNcj/RGJiUyQBgzwxzE5oHdeuXY5hSbbmKMlphj1cy1Vl7Pn2asCGbSrru6hSQjmCzqTBPVWAF/whmEOVHbw==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "es-array-method-boxes-properly": "^1.0.0", + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.4", "is-string": "^1.0.7" } }, "array.prototype.findlastindex": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.4.tgz", - "integrity": "sha512-hzvSHUshSpCflDR1QMUBLHGHP1VIEBegT4pix9H/Z92Xw3ySoy6c2qh7lJWTJnRJ8JCZ9bJNCgTyYaJGcJu6xQ==", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.5.tgz", + "integrity": "sha512-zfETvRFA8o7EiNn++N5f/kaCw221hrpGsDmcpndVupkPzEc1Wuf3VgC0qby1BbHs7f5DVYjgtEU2LLh5bqeGfQ==", "dev": true, "requires": { - "call-bind": "^1.0.5", + "call-bind": "^1.0.7", "define-properties": "^1.2.1", - "es-abstract": "^1.22.3", + "es-abstract": "^1.23.2", "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", "es-shim-unscopables": "^1.0.2" } }, @@ -8579,6 +8894,39 @@ "source-map-js": "^1.0.1" } }, + "data-view-buffer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.1.tgz", + "integrity": "sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==", + "dev": true, + "requires": { + "call-bind": "^1.0.6", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + } + }, + "data-view-byte-length": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.1.tgz", + "integrity": "sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ==", + "dev": true, + "requires": { + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + } + }, + "data-view-byte-offset": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.0.tgz", + "integrity": "sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==", + "dev": true, + "requires": { + "call-bind": "^1.0.6", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + } + }, "debug": { "version": "4.3.5", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz", @@ -8622,20 +8970,17 @@ "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", "dev": true }, + "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 + }, "diff-match-patch": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/diff-match-patch/-/diff-match-patch-1.0.5.tgz", "integrity": "sha512-IayShXAgj/QMXgB0IWmKx+rOPuGMhqm5w6jvFxmVenXKIzRqTAAsbBPT3kWQeGANj3jGgvcvv4yK6SxqYmikgw==" }, - "dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", - "dev": true, - "requires": { - "path-type": "^4.0.0" - } - }, "doctrine": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", @@ -8646,17 +8991,21 @@ } }, "es-abstract": { - "version": "1.22.5", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.5.tgz", - "integrity": "sha512-oW69R+4q2wG+Hc3KZePPZxOiisRIqfKBVo/HLx94QcJeWGU/8sZhCvc829rd1kS366vlJbzBfXf9yWwf0+Ko7w==", + "version": "1.23.3", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.3.tgz", + "integrity": "sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A==", "dev": true, "requires": { "array-buffer-byte-length": "^1.0.1", "arraybuffer.prototype.slice": "^1.0.3", "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.7", + "data-view-buffer": "^1.0.1", + "data-view-byte-length": "^1.0.1", + "data-view-byte-offset": "^1.0.0", "es-define-property": "^1.0.0", "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", "es-set-tostringtag": "^2.0.3", "es-to-primitive": "^1.2.1", "function.prototype.name": "^1.1.6", @@ -8667,10 +9016,11 @@ "has-property-descriptors": "^1.0.2", "has-proto": "^1.0.3", "has-symbols": "^1.0.3", - "hasown": "^2.0.1", + "hasown": "^2.0.2", "internal-slot": "^1.0.7", "is-array-buffer": "^3.0.4", "is-callable": "^1.2.7", + "is-data-view": "^1.0.1", "is-negative-zero": "^2.0.3", "is-regex": "^1.1.4", "is-shared-array-buffer": "^1.0.3", @@ -8681,25 +9031,19 @@ "object-keys": "^1.1.1", "object.assign": "^4.1.5", "regexp.prototype.flags": "^1.5.2", - "safe-array-concat": "^1.1.0", + "safe-array-concat": "^1.1.2", "safe-regex-test": "^1.0.3", - "string.prototype.trim": "^1.2.8", - "string.prototype.trimend": "^1.0.7", - "string.prototype.trimstart": "^1.0.7", + "string.prototype.trim": "^1.2.9", + "string.prototype.trimend": "^1.0.8", + "string.prototype.trimstart": "^1.0.8", "typed-array-buffer": "^1.0.2", "typed-array-byte-length": "^1.0.1", "typed-array-byte-offset": "^1.0.2", - "typed-array-length": "^1.0.5", + "typed-array-length": "^1.0.6", "unbox-primitive": "^1.0.2", - "which-typed-array": "^1.1.14" + "which-typed-array": "^1.1.15" } }, - "es-array-method-boxes-properly": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz", - "integrity": "sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA==", - "dev": true - }, "es-define-property": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", @@ -8715,6 +9059,15 @@ "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", "dev": true }, + "es-object-atoms": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.0.0.tgz", + "integrity": "sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==", + "dev": true, + "requires": { + "es-errors": "^1.3.0" + } + }, "es-set-tostringtag": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.3.tgz", @@ -8747,34 +9100,34 @@ } }, "esbuild": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.23.0.tgz", - "integrity": "sha512-1lvV17H2bMYda/WaFb2jLPeHU3zml2k4/yagNMG8Q/YtfMjCwEUZa2eXXMgZTVSL5q1n4H7sQ0X6CdJDqqeCFA==", + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.23.1.tgz", + "integrity": "sha512-VVNz/9Sa0bs5SELtn3f7qhJCDPCF5oMEl5cO9/SSinpE9hbPVvxbd572HH5AKiP7WD8INO53GgfDDhRjkylHEg==", "requires": { - "@esbuild/aix-ppc64": "0.23.0", - "@esbuild/android-arm": "0.23.0", - "@esbuild/android-arm64": "0.23.0", - "@esbuild/android-x64": "0.23.0", - "@esbuild/darwin-arm64": "0.23.0", - "@esbuild/darwin-x64": "0.23.0", - "@esbuild/freebsd-arm64": "0.23.0", - "@esbuild/freebsd-x64": "0.23.0", - "@esbuild/linux-arm": "0.23.0", - "@esbuild/linux-arm64": "0.23.0", - "@esbuild/linux-ia32": "0.23.0", - "@esbuild/linux-loong64": "0.23.0", - "@esbuild/linux-mips64el": "0.23.0", - "@esbuild/linux-ppc64": "0.23.0", - "@esbuild/linux-riscv64": "0.23.0", - "@esbuild/linux-s390x": "0.23.0", - "@esbuild/linux-x64": "0.23.0", - "@esbuild/netbsd-x64": "0.23.0", - "@esbuild/openbsd-arm64": "0.23.0", - "@esbuild/openbsd-x64": "0.23.0", - "@esbuild/sunos-x64": "0.23.0", - "@esbuild/win32-arm64": "0.23.0", - "@esbuild/win32-ia32": "0.23.0", - "@esbuild/win32-x64": "0.23.0" + "@esbuild/aix-ppc64": "0.23.1", + "@esbuild/android-arm": "0.23.1", + "@esbuild/android-arm64": "0.23.1", + "@esbuild/android-x64": "0.23.1", + "@esbuild/darwin-arm64": "0.23.1", + "@esbuild/darwin-x64": "0.23.1", + "@esbuild/freebsd-arm64": "0.23.1", + "@esbuild/freebsd-x64": "0.23.1", + "@esbuild/linux-arm": "0.23.1", + "@esbuild/linux-arm64": "0.23.1", + "@esbuild/linux-ia32": "0.23.1", + "@esbuild/linux-loong64": "0.23.1", + "@esbuild/linux-mips64el": "0.23.1", + "@esbuild/linux-ppc64": "0.23.1", + "@esbuild/linux-riscv64": "0.23.1", + "@esbuild/linux-s390x": "0.23.1", + "@esbuild/linux-x64": "0.23.1", + "@esbuild/netbsd-x64": "0.23.1", + "@esbuild/openbsd-arm64": "0.23.1", + "@esbuild/openbsd-x64": "0.23.1", + "@esbuild/sunos-x64": "0.23.1", + "@esbuild/win32-arm64": "0.23.1", + "@esbuild/win32-ia32": "0.23.1", + "@esbuild/win32-x64": "0.23.1" } }, "esbuild-plugin-inline-worker": { @@ -8911,9 +9264,9 @@ } }, "eslint-module-utils": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.8.0.tgz", - "integrity": "sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw==", + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.11.0.tgz", + "integrity": "sha512-gbBE5Hitek/oG6MUVj6sFuzEjA/ClzNflVrLovHi/JgLdC7fiN5gLAY1WIPW1a0V5I999MnsrvVrCOGmmVqDBQ==", "dev": true, "requires": { "debug": "^3.2.7" @@ -8931,26 +9284,27 @@ } }, "eslint-plugin-import": { - "version": "2.29.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.29.1.tgz", - "integrity": "sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw==", + "version": "2.30.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.30.0.tgz", + "integrity": "sha512-/mHNE9jINJfiD2EKkg1BKyPyUk4zdnT54YgbOgfjSakWT5oyX/qQLVNTkehyfpcMxZXMy1zyonZ2v7hZTX43Yw==", "dev": true, "requires": { - "array-includes": "^3.1.7", - "array.prototype.findlastindex": "^1.2.3", + "@rtsao/scc": "^1.1.0", + "array-includes": "^3.1.8", + "array.prototype.findlastindex": "^1.2.5", "array.prototype.flat": "^1.3.2", "array.prototype.flatmap": "^1.3.2", "debug": "^3.2.7", "doctrine": "^2.1.0", "eslint-import-resolver-node": "^0.3.9", - "eslint-module-utils": "^2.8.0", - "hasown": "^2.0.0", - "is-core-module": "^2.13.1", + "eslint-module-utils": "^2.9.0", + "hasown": "^2.0.2", + "is-core-module": "^2.15.1", "is-glob": "^4.0.3", "minimatch": "^3.1.2", - "object.fromentries": "^2.0.7", - "object.groupby": "^1.0.1", - "object.values": "^1.1.7", + "object.fromentries": "^2.0.8", + "object.groupby": "^1.0.3", + "object.values": "^1.2.0", "semver": "^6.3.1", "tsconfig-paths": "^3.15.0" }, @@ -9115,9 +9469,9 @@ "dev": true }, "fast-xml-parser": { - "version": "4.2.5", - "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.2.5.tgz", - "integrity": "sha512-B9/wizE4WngqQftFPmdaMYlXoJlJOYxGQOanC77fq9k8+Z0v5dDSVh+3glErdIROP//s/jgb7ZuxKfB8nVyo0g==", + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.4.1.tgz", + "integrity": "sha512-xkjOecfnKGkSsOwtZ5Pz7Us/T6mrbPQrq0nh+aCO5V9nk5NLWmasAHumTKjiPJPWANe+kAZ84Jc8ooJkzZ88Sw==", "requires": { "strnum": "^1.0.5" } @@ -9325,20 +9679,6 @@ "define-properties": "^1.1.3" } }, - "globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", - "dev": true, - "requires": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" - } - }, "gopd": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", @@ -9495,12 +9835,21 @@ "dev": true }, "is-core-module": { - "version": "2.13.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", - "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", + "version": "2.15.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.1.tgz", + "integrity": "sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==", "dev": true, "requires": { - "hasown": "^2.0.0" + "hasown": "^2.0.2" + } + }, + "is-data-view": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.1.tgz", + "integrity": "sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w==", + "dev": true, + "requires": { + "is-typed-array": "^1.1.13" } }, "is-date-object": { @@ -9734,15 +10083,30 @@ "dev": true }, "micromatch": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.7.tgz", - "integrity": "sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==", + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", "dev": true, "requires": { "braces": "^3.0.3", "picomatch": "^2.3.1" } }, + "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 + }, + "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, + "requires": { + "mime-db": "1.52.0" + } + }, "minimatch": { "version": "10.0.1", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.1.tgz", @@ -9826,44 +10190,43 @@ } }, "object.fromentries": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.7.tgz", - "integrity": "sha512-UPbPHML6sL8PI/mOqPwsH4G6iyXcCGzLin8KvEPenOZN5lpCNBZZQ+V62vdjB1mQHrmqGQt5/OJzemUA+KJmEA==", + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz", + "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==", "dev": true, "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0" } }, "object.groupby": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.2.tgz", - "integrity": "sha512-bzBq58S+x+uo0VjurFT0UktpKHOZmv4/xePiOA1nbB9pMqpGK7rUPNgf+1YC+7mE+0HzhTMqNUuCqvKhj6FnBw==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.3.tgz", + "integrity": "sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==", "dev": true, "requires": { - "array.prototype.filter": "^1.0.3", - "call-bind": "^1.0.5", + "call-bind": "^1.0.7", "define-properties": "^1.2.1", - "es-abstract": "^1.22.3", - "es-errors": "^1.0.0" + "es-abstract": "^1.23.2" } }, "object.values": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.7.tgz", - "integrity": "sha512-aU6xnDFYT3x17e/f0IiiwlGPTy2jzMySGfUB4fq6z7CV8l85CWHDk5ErhyhpfDHhrOMwGFhSQkhMGHaIotA6Ng==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.0.tgz", + "integrity": "sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ==", "dev": true, "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" } }, "obsidian": { - "version": "1.5.7", - "resolved": "https://registry.npmjs.org/obsidian/-/obsidian-1.5.7.tgz", - "integrity": "sha512-DNcvQJ6TvMflHZqWfO9cLGbOUbKTy2KBi6B6vjo5RG8XsftKZZq1zS/OQFhII2BnXK/DWan/lUcb2JYxfM3p5A==", + "version": "1.6.6", + "resolved": "https://registry.npmjs.org/obsidian/-/obsidian-1.6.6.tgz", + "integrity": "sha512-GZHzeOiwmw/wBjB5JwrsxAZBLqxGQmqtEKSvJJvT0LtTcqeOFnV8jv0ZK5kO7hBb44WxJc+LdS7mZgLXbb+qXQ==", "dev": true, "requires": { "@types/codemirror": "5.60.8", @@ -9871,9 +10234,9 @@ } }, "octagonal-wheels": { - "version": "0.1.13", - "resolved": "https://registry.npmjs.org/octagonal-wheels/-/octagonal-wheels-0.1.13.tgz", - "integrity": "sha512-jnkOBiNjnQfbWM+shseVbpPONZ4T65TqlT/+4kLZMb01Xh2cyAgA4wiDiQnK4zZZwYqi++tX86SDReQoLrD38w==", + "version": "0.1.14", + "resolved": "https://registry.npmjs.org/octagonal-wheels/-/octagonal-wheels-0.1.14.tgz", + "integrity": "sha512-W0DQL5YNL7oJH2Dcb7mOE/P7MNrM7PXWJZHJpPGzbrKHAT14OtklbBFNJvM26v8nizwlb5WHQA+W/Tg1CIDSGQ==", "requires": { "idb": "^8.0.0", "xxhash-wasm": "0.4.2", @@ -9958,12 +10321,6 @@ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "dev": true }, - "path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true - }, "periscopic": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/periscopic/-/periscopic-3.1.0.tgz", @@ -10037,9 +10394,9 @@ "dev": true }, "postcss": { - "version": "8.4.39", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.39.tgz", - "integrity": "sha512-0vzE+lAiG7hZl1/9I8yzKLx3aR9Xbof3fBHKunvMfOCYAtMhrsnccJY2iTURb9EZd5+pLuiNV9/c/GZJOHsgIw==", + "version": "8.4.45", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.45.tgz", + "integrity": "sha512-7KTLTdzdZZYscUc65XmjFiB73vBhBfbPztCYdUNvlaso9PrzjzcmjqBPR0lNGkcVlcO4BjiO5rK/qNz+XAen1Q==", "dev": true, "requires": { "nanoid": "^3.3.7", @@ -10474,9 +10831,9 @@ } }, "semver": { - "version": "7.6.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", - "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", "dev": true }, "set-cookie-parser": { @@ -10538,12 +10895,6 @@ "object-inspect": "^1.13.1" } }, - "slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true - }, "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -10573,36 +10924,37 @@ "dev": true }, "string.prototype.trim": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.8.tgz", - "integrity": "sha512-lfjY4HcixfQXOfaqCvcBuOIapyaroTXhbkfJN3gcB1OtyupngWK4sEET9Knd0cXd28kTUqu/kHoV4HKSJdnjiQ==", + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.9.tgz", + "integrity": "sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw==", "dev": true, "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.0", + "es-object-atoms": "^1.0.0" } }, "string.prototype.trimend": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.7.tgz", - "integrity": "sha512-Ni79DqeB72ZFq1uH/L6zJ+DKZTkOtPIHovb3YZHQViE+HDouuU4mBrLOLDn5Dde3RF8qw5qVETEjhu9locMLvA==", + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.8.tgz", + "integrity": "sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ==", "dev": true, "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" } }, "string.prototype.trimstart": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.7.tgz", - "integrity": "sha512-NGhtDFu3jCEm7B4Fy0DpLewdJQOZcQ0rGbwQ/+stjnrp2i+rlKeCvos9hOIeCmqwratM47OBxY7uFZzjxHXmrg==", + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", + "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", "dev": true, "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" } }, "strip-ansi": { @@ -10654,9 +11006,9 @@ "dev": true }, "svelte": { - "version": "4.2.18", - "resolved": "https://registry.npmjs.org/svelte/-/svelte-4.2.18.tgz", - "integrity": "sha512-d0FdzYIiAePqRJEb90WlJDkjUEx42xhivxN8muUBmfZnP+tzUgz12DJ2hRJi8sIHCME7jeK1PTMgKPSfTd8JrA==", + "version": "4.2.19", + "resolved": "https://registry.npmjs.org/svelte/-/svelte-4.2.19.tgz", + "integrity": "sha512-IY1rnGr6izd10B0A8LqsBfmlT5OILVuZ7XsI0vdGPEvuonFV7NYEUK4dAkm9Zg2q0Um92kYjTpS1CAP3Nh/KWw==", "dev": true, "requires": { "@ampproject/remapping": "^2.2.1", @@ -10683,9 +11035,9 @@ "requires": {} }, "terser": { - "version": "5.31.2", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.31.2.tgz", - "integrity": "sha512-LGyRZVFm/QElZHy/CPr/O4eNZOZIzsrQ92y4v9UJe/pFJjypje2yI3C2FmPtvUEnhadlSbmG2nXtdcjHOjCfxw==", + "version": "5.31.6", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.31.6.tgz", + "integrity": "sha512-PQ4DAriWzKj+qgehQ7LK5bQqCFNMmlhjR2PFFLuqGCpuCAauxemVBWwWOxo3UIwWQx8+Pr61Df++r76wDmkQBg==", "dev": true, "requires": { "@jridgewell/source-map": "^0.3.3", @@ -10756,9 +11108,9 @@ } }, "tslib": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", - "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", + "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" }, "type-check": { "version": "0.4.0", @@ -10814,9 +11166,9 @@ } }, "typed-array-length": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.5.tgz", - "integrity": "sha512-yMi0PlwuznKHxKmcpoOdeLwxBoVPkqZxd7q2FgMkmD3bNwvF5VW0+UlUQ1k1vmktTu4Yu13Q0RIxEP8+B+wloA==", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.6.tgz", + "integrity": "sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==", "dev": true, "requires": { "call-bind": "^1.0.7", @@ -10828,9 +11180,9 @@ } }, "typescript": { - "version": "5.5.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.3.tgz", - "integrity": "sha512-/hreyEujaB0w76zKo6717l3L0o/qEUtRgdvUBvlkhoWeOVMjMuHNHk0BRBzikzuGDqNmPQbg5ifMEqsHLiIUcQ==", + "version": "5.5.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.4.tgz", + "integrity": "sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==", "dev": true }, "unbox-primitive": { @@ -10846,9 +11198,9 @@ } }, "undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "version": "6.19.8", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", + "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", "dev": true }, "universalify": { diff --git a/package.json b/package.json index a72d3cd..23c535d 100644 --- a/package.json +++ b/package.json @@ -14,28 +14,29 @@ "author": "vorotamoroz", "license": "MIT", "devDependencies": { + "@chialab/esbuild-plugin-worker": "^0.18.1", "@tsconfig/svelte": "^5.0.4", "@types/diff-match-patch": "^1.0.36", - "@types/node": "^20.14.10", + "@types/node": "^22.5.4", "@types/pouchdb": "^6.4.2", "@types/pouchdb-adapter-http": "^6.1.6", "@types/pouchdb-adapter-idb": "^6.1.7", "@types/pouchdb-browser": "^6.1.5", - "@types/pouchdb-core": "^7.0.14", + "@types/pouchdb-core": "^7.0.15", "@types/pouchdb-mapreduce": "^6.1.10", "@types/pouchdb-replication": "^6.4.7", "@types/transform-pouch": "^1.0.6", - "@typescript-eslint/eslint-plugin": "^7.16.0", - "@typescript-eslint/parser": "^7.16.0", + "@typescript-eslint/eslint-plugin": "^8.4.0", + "@typescript-eslint/parser": "^8.4.0", "builtin-modules": "^4.0.0", - "esbuild": "0.23.0", + "esbuild": "0.23.1", "esbuild-svelte": "^0.8.1", "eslint": "^8.57.0", "eslint-config-airbnb-base": "^15.0.0", - "eslint-plugin-import": "^2.29.1", + "eslint-plugin-import": "^2.30.0", "events": "^3.3.0", - "obsidian": "^1.5.7", - "postcss": "^8.4.39", + "obsidian": "^1.6.6", + "postcss": "^8.4.45", "postcss-load-config": "^6.0.1", "pouchdb-adapter-http": "^9.0.0", "pouchdb-adapter-idb": "^9.0.0", @@ -47,25 +48,25 @@ "pouchdb-merge": "^9.0.0", "pouchdb-replication": "^9.0.0", "pouchdb-utils": "^9.0.0", - "svelte": "^4.2.18", + "svelte": "^4.2.19", "svelte-preprocess": "^6.0.2", - "terser": "^5.31.2", + "terser": "^5.31.6", "transform-pouch": "^2.0.0", - "tslib": "^2.6.3", - "typescript": "^5.5.3" + "tslib": "^2.7.0", + "typescript": "^5.5.4" }, "dependencies": { - "@aws-sdk/client-s3": "^3.614.0", - "@smithy/fetch-http-handler": "^3.2.1", - "@smithy/protocol-http": "^4.0.3", + "@aws-sdk/client-s3": "^3.645.0", + "@smithy/fetch-http-handler": "^3.2.4", + "@smithy/protocol-http": "^4.1.0", "@smithy/querystring-builder": "^3.0.3", "diff-match-patch": "^1.0.5", "esbuild-plugin-inline-worker": "^0.1.1", "fflate": "^0.8.2", "idb": "^8.0.0", "minimatch": "^10.0.1", - "octagonal-wheels": "^0.1.13", + "octagonal-wheels": "^0.1.14", "xxhash-wasm": "0.4.2", "xxhash-wasm-102": "npm:xxhash-wasm@^1.0.2" } -} +} \ No newline at end of file diff --git a/src/common/events.ts b/src/common/events.ts new file mode 100644 index 0000000..15baf5d --- /dev/null +++ b/src/common/events.ts @@ -0,0 +1,16 @@ +export const EVENT_LAYOUT_READY = "layout-ready"; +export const EVENT_PLUGIN_LOADED = "plugin-loaded"; +export const EVENT_PLUGIN_UNLOADED = "plugin-unloaded"; +export const EVENT_SETTING_SAVED = "setting-saved"; +export const EVENT_FILE_RENAMED = "file-renamed"; + +export const EVENT_LEAF_ACTIVE_CHANGED = "leaf-active-changed"; + + +// export const EVENT_FILE_CHANGED = "file-changed"; + +import { eventHub } from "../lib/src/hub/hub"; +// TODO: Add overloads for the emit method to allow for type checking + +export { eventHub }; + diff --git a/src/common/utils.ts b/src/common/utils.ts index 8d6a1c3..36de1c1 100644 --- a/src/common/utils.ts +++ b/src/common/utils.ts @@ -15,14 +15,14 @@ export { scheduleTask, setPeriodicTask, cancelTask, cancelAllTasks, cancelPeriod // For backward compatibility, using the path for determining id. // Only CouchDB unacceptable ID (that starts with an underscore) has been prefixed with "/". // The first slash will be deleted when the path is normalized. -export async function path2id(filename: FilePathWithPrefix | FilePath, obfuscatePassphrase: string | false): Promise { +export async function path2id(filename: FilePathWithPrefix | FilePath, obfuscatePassphrase: string | false, caseInsensitive: boolean): Promise { const temp = filename.split(":"); const path = temp.pop(); const normalizedPath = normalizePath(path as FilePath); temp.push(normalizedPath); const fixedPath = temp.join(":") as FilePathWithPrefix; - const out = await path2id_base(fixedPath, obfuscatePassphrase); + const out = await path2id_base(fixedPath, obfuscatePassphrase, caseInsensitive); return out; } export function id2path(id: DocumentID, entry?: EntryHasPath): FilePathWithPrefix { @@ -465,3 +465,34 @@ export function compareFileFreshness(baseFile: TFile | AnyEntry | undefined, che return compareMTime(modifiedBase, modifiedTarget); } +const _cached = new Map; +}>(); + +export type MemoOption = { + key: string; + forceUpdate?: boolean; + validator?: () => boolean; +} + +export function useMemo({ key, forceUpdate, validator }: MemoOption, updateFunc: (context: Map, prev: T) => T): T { + const cached = _cached.get(key); + if (cached && !forceUpdate && (!validator || validator && !validator())) { + return cached.value; + } + const context = cached?.context || new Map(); + const value = updateFunc(context, cached?.value); + if (value !== cached?.value) { + _cached.set(key, { value, context }); + } + return value; +} + +export function disposeMemo(key: string) { + _cached.delete(key); +} + +export function disposeAllMemo() { + _cached.clear(); +} \ No newline at end of file diff --git a/src/features/CmdSetupLiveSync.ts b/src/features/CmdSetupLiveSync.ts index 28ee615..268a702 100644 --- a/src/features/CmdSetupLiveSync.ts +++ b/src/features/CmdSetupLiveSync.ts @@ -374,7 +374,7 @@ Of course, we are able to disable these features.` await this.plugin.openDatabase(); this.plugin.isReady = true; if (makeLocalChunkBeforeSync) { - await this.plugin.initializeDatabase(true); + await this.plugin.createAllChunks(true); } await this.plugin.markRemoteResolved(); await delay(500); @@ -390,6 +390,7 @@ Of course, we are able to disable these features.` async rebuildRemote() { this.suspendExtraSync(); this.plugin.settings.isConfigured = true; + await this.plugin.realizeSettingSyncMode(); await this.plugin.markRemoteLocked(); await this.plugin.tryResetRemoteDatabase(); diff --git a/src/features/CmdStatusInsideEditor.ts b/src/features/CmdStatusInsideEditor.ts new file mode 100644 index 0000000..bf9980e --- /dev/null +++ b/src/features/CmdStatusInsideEditor.ts @@ -0,0 +1,234 @@ +import { computed, reactive, reactiveSource, type ReactiveValue } from "octagonal-wheels/dataobject/reactive"; +import type { DatabaseConnectingStatus, EntryDoc } from "../lib/src/common/types"; +import { LiveSyncCommands } from "./LiveSyncCommands"; +import { scheduleTask } from "octagonal-wheels/concurrency/task"; +import { isDirty, throttle } from "../lib/src/common/utils"; +import { collectingChunks, pluginScanningCount, hiddenFilesEventCount, hiddenFilesProcessingCount } from "../lib/src/mock_and_interop/stores"; +import { eventHub } from "../lib/src/hub/hub"; +import { EVENT_FILE_RENAMED, EVENT_LAYOUT_READY, EVENT_LEAF_ACTIVE_CHANGED } from "../common/events"; + +export class LogAddOn extends LiveSyncCommands { + + statusBar?: HTMLElement; + + statusDiv?: HTMLElement; + statusLine?: HTMLDivElement; + logMessage?: HTMLDivElement; + logHistory?: HTMLDivElement; + messageArea?: HTMLDivElement; + + statusBarLabels!: ReactiveValue<{ message: string, status: string }>; + + observeForLogs() { + const padSpaces = `\u{2007}`.repeat(10); + // const emptyMark = `\u{2003}`; + function padLeftSpComputed(numI: ReactiveValue, mark: string) { + const formatted = reactiveSource(""); + let timer: ReturnType | undefined = undefined; + let maxLen = 1; + numI.onChanged(numX => { + const num = numX.value; + const numLen = `${Math.abs(num)}`.length + 1; + maxLen = maxLen < numLen ? numLen : maxLen; + if (timer) clearTimeout(timer); + if (num == 0) { + timer = setTimeout(() => { + formatted.value = ""; + maxLen = 1; + }, 3000); + } + formatted.value = ` ${mark}${`${padSpaces}${num}`.slice(-(maxLen))}`; + }) + return computed(() => formatted.value); + } + const labelReplication = padLeftSpComputed(this.plugin.replicationResultCount, `📥`); + const labelDBCount = padLeftSpComputed(this.plugin.databaseQueueCount, `📄`); + const labelStorageCount = padLeftSpComputed(this.plugin.storageApplyingCount, `💾`); + const labelChunkCount = padLeftSpComputed(collectingChunks, `🧩`); + const labelPluginScanCount = padLeftSpComputed(pluginScanningCount, `🔌`); + const labelConflictProcessCount = padLeftSpComputed(this.plugin.conflictProcessQueueCount, `🔩`); + const hiddenFilesCount = reactive(() => hiddenFilesEventCount.value + hiddenFilesProcessingCount.value); + const labelHiddenFilesCount = padLeftSpComputed(hiddenFilesCount, `⚙️`) + const queueCountLabelX = reactive(() => { + return `${labelReplication()}${labelDBCount()}${labelStorageCount()}${labelChunkCount()}${labelPluginScanCount()}${labelHiddenFilesCount()}${labelConflictProcessCount()}`; + }) + const queueCountLabel = () => queueCountLabelX.value; + + const requestingStatLabel = computed(() => { + const diff = this.plugin.requestCount.value - this.plugin.responseCount.value; + return diff != 0 ? "📲 " : ""; + }) + + const replicationStatLabel = computed(() => { + const e = this.plugin.replicationStat.value; + const sent = e.sent; + const arrived = e.arrived; + const maxPullSeq = e.maxPullSeq; + const maxPushSeq = e.maxPushSeq; + const lastSyncPullSeq = e.lastSyncPullSeq; + const lastSyncPushSeq = e.lastSyncPushSeq; + let pushLast = ""; + let pullLast = ""; + let w = ""; + const labels: Partial> = { + "CONNECTED": "⚡", + "JOURNAL_SEND": "📦↑", + "JOURNAL_RECEIVE": "📦↓", + } + switch (e.syncStatus) { + case "CLOSED": + case "COMPLETED": + case "NOT_CONNECTED": + w = "⏹"; + break; + case "STARTED": + w = "🌀"; + break; + case "PAUSED": + w = "💤"; + break; + case "CONNECTED": + case "JOURNAL_SEND": + case "JOURNAL_RECEIVE": + w = labels[e.syncStatus] || "⚡"; + pushLast = ((lastSyncPushSeq == 0) ? "" : (lastSyncPushSeq >= maxPushSeq ? " (LIVE)" : ` (${maxPushSeq - lastSyncPushSeq})`)); + pullLast = ((lastSyncPullSeq == 0) ? "" : (lastSyncPullSeq >= maxPullSeq ? " (LIVE)" : ` (${maxPullSeq - lastSyncPullSeq})`)); + break; + case "ERRORED": + w = "⚠"; + break; + default: + w = "?"; + } + return { w, sent, pushLast, arrived, pullLast }; + }) + const labelProc = padLeftSpComputed(this.plugin.vaultManager.processing, `⏳`); + const labelPend = padLeftSpComputed(this.plugin.vaultManager.totalQueued, `🛫`); + const labelInBatchDelay = padLeftSpComputed(this.plugin.vaultManager.batched, `📬`); + const waitingLabel = computed(() => { + return `${labelProc()}${labelPend()}${labelInBatchDelay()}`; + }) + const statusLineLabel = computed(() => { + const { w, sent, pushLast, arrived, pullLast } = replicationStatLabel(); + const queued = queueCountLabel(); + const waiting = waitingLabel(); + const networkActivity = requestingStatLabel(); + return { + message: `${networkActivity}Sync: ${w} ↑ ${sent}${pushLast} ↓ ${arrived}${pullLast}${waiting}${queued}`, + }; + }) + const statusBarLabels = reactive(() => { + const scheduleMessage = this.plugin.isReloadingScheduled ? `WARNING! RESTARTING OBSIDIAN IS SCHEDULED!\n` : ""; + const { message } = statusLineLabel(); + const status = scheduleMessage + this.plugin.statusLog.value; + + return { + message, status + } + }) + this.statusBarLabels = statusBarLabels; + + const applyToDisplay = throttle((label: typeof statusBarLabels.value) => { + const v = label; + this.applyStatusBarText(); + + }, 20); + statusBarLabels.onChanged(label => applyToDisplay(label.value)) + } + + adjustStatusDivPosition() { + const mdv = this.app.workspace.getMostRecentLeaf(); + if (mdv && this.statusDiv) { + this.statusDiv.remove(); + // this.statusDiv.pa(); + const container = mdv.view.containerEl; + container.insertBefore(this.statusDiv, container.lastChild); + } + } + + onunload() { + if (this.statusDiv) { + this.statusDiv.remove(); + } + document.querySelectorAll(`.livesync-status`)?.forEach(e => e.remove()); + } + async setFileStatus() { + this.messageArea!.innerText = await this.plugin.getActiveFileStatus(); + } + onActiveLeafChange() { + this.adjustStatusDivPosition(); + this.setFileStatus(); + + } + onload(): void | Promise { + eventHub.on(EVENT_FILE_RENAMED, (evt: CustomEvent<{ oldPath: string, newPath: string }>) => { + this.setFileStatus(); + }); + eventHub.on(EVENT_LEAF_ACTIVE_CHANGED, () => this.onActiveLeafChange()); + const w = document.querySelectorAll(`.livesync-status`); + w.forEach(e => e.remove()); + + this.observeForLogs(); + this.adjustStatusDivPosition(); + this.statusDiv = this.app.workspace.containerEl.createDiv({ cls: "livesync-status" }); + this.statusLine = this.statusDiv.createDiv({ cls: "livesync-status-statusline" }); + this.messageArea = this.statusDiv.createDiv({ cls: "livesync-status-messagearea" }); + this.logMessage = this.statusDiv.createDiv({ cls: "livesync-status-logmessage" }); + this.logHistory = this.statusDiv.createDiv({ cls: "livesync-status-loghistory" }); + eventHub.on(EVENT_LAYOUT_READY, () => this.adjustStatusDivPosition()); + if (this.settings.showStatusOnStatusbar) { + this.statusBar = this.plugin.addStatusBarItem(); + this.statusBar.addClass("syncstatusbar"); + } + } + nextFrameQueue: ReturnType | undefined = undefined; + logLines: { ttl: number, message: string }[] = []; + + applyStatusBarText() { + if (this.nextFrameQueue) { + return; + } + this.nextFrameQueue = requestAnimationFrame(() => { + this.nextFrameQueue = undefined; + const { message, status } = this.statusBarLabels.value; + // const recent = logMessages.value; + const newMsg = message; + const newLog = this.settings.showOnlyIconsOnEditor ? "" : status; + + this.statusBar?.setText(newMsg.split("\n")[0]); + if (this.settings.showStatusOnEditor && this.statusDiv) { + // const root = activeDocument.documentElement; + // root.style.setProperty("--sls-log-text", "'" + (newMsg + "\\A " + newLog) + "'"); + // this.statusDiv.innerText = newMsg + "\\A " + newLog; + if (this.settings.showLongerLogInsideEditor) { + const now = new Date().getTime(); + this.logLines = this.logLines.filter(e => e.ttl > now); + const minimumNext = this.logLines.reduce((a, b) => a < b.ttl ? a : b.ttl, Number.MAX_SAFE_INTEGER); + if (this.logLines.length > 0) setTimeout(() => this.applyStatusBarText(), minimumNext - now); + const recent = this.logLines.map(e => e.message); + const recentLogs = recent.reverse().join("\n"); + if (isDirty("recentLogs", recentLogs)) this.logHistory!.innerText = recentLogs; + } + if (isDirty("newMsg", newMsg)) this.statusLine!.innerText = newMsg; + if (isDirty("newLog", newLog)) this.logMessage!.innerText = newLog; + } else { + // const root = activeDocument.documentElement; + // root.style.setProperty("--log-text", "'" + (newMsg + "\\A " + newLog) + "'"); + } + }); + + scheduleTask("log-hide", 3000, () => { this.plugin.statusLog.value = "" }); + } + + + onInitializeDatabase(showNotice: boolean) { } + beforeReplicate(showNotice: boolean) { } + onResume() { } + parseReplicationResultItem(docs: PouchDB.Core.ExistingDocument): boolean | Promise { + return false; + } + async realizeSettingSyncMode() { } + + +} + diff --git a/src/lib b/src/lib index f0253a8..c9af24e 160000 --- a/src/lib +++ b/src/lib @@ -1 +1 @@ -Subproject commit f0253a854837ffb97c94e31927ec01f8ab834ac6 +Subproject commit c9af24ecd6457f23e8fd3c454b7621a70742399b diff --git a/src/main.ts b/src/main.ts index a8322c2..614f25f 100644 --- a/src/main.ts +++ b/src/main.ts @@ -2,17 +2,17 @@ const isDebug = false; import { type Diff, DIFF_DELETE, DIFF_EQUAL, DIFF_INSERT, diff_match_patch, stringifyYaml, parseYaml } from "./deps"; import { Notice, Plugin, TFile, addIcon, TFolder, normalizePath, TAbstractFile, Editor, MarkdownView, type RequestUrlParam, type RequestUrlResponse, requestUrl, type MarkdownFileInfo } from "./deps"; -import { type EntryDoc, type LoadedEntry, type ObsidianLiveSyncSettings, type diff_check_result, type diff_result_leaf, type EntryBody, type LOG_LEVEL, VER, DEFAULT_SETTINGS, type diff_result, FLAGMD_REDFLAG, SYNCINFO_ID, SALT_OF_PASSPHRASE, type ConfigPassphraseStore, type CouchDBConnection, FLAGMD_REDFLAG2, FLAGMD_REDFLAG3, PREFIXMD_LOGFILE, type DatabaseConnectingStatus, type EntryHasPath, type DocumentID, type FilePathWithPrefix, type FilePath, type AnyEntry, LOG_LEVEL_DEBUG, LOG_LEVEL_INFO, LOG_LEVEL_NOTICE, LOG_LEVEL_URGENT, LOG_LEVEL_VERBOSE, type SavingEntry, MISSING_OR_ERROR, NOT_CONFLICTED, AUTO_MERGED, CANCELLED, LEAVE_TO_SUBSEQUENT, FLAGMD_REDFLAG2_HR, FLAGMD_REDFLAG3_HR, REMOTE_MINIO, REMOTE_COUCHDB, type BucketSyncSetting, TweakValuesShouldMatchedTemplate, confName, type TweakValues, } from "./lib/src/common/types.ts"; +import { type EntryDoc, type LoadedEntry, type ObsidianLiveSyncSettings, type diff_check_result, type diff_result_leaf, type EntryBody, type LOG_LEVEL, VER, DEFAULT_SETTINGS, type diff_result, FLAGMD_REDFLAG, SYNCINFO_ID, SALT_OF_PASSPHRASE, type ConfigPassphraseStore, type CouchDBConnection, FLAGMD_REDFLAG2, FLAGMD_REDFLAG3, PREFIXMD_LOGFILE, type DatabaseConnectingStatus, type EntryHasPath, type DocumentID, type FilePathWithPrefix, type FilePath, type AnyEntry, LOG_LEVEL_DEBUG, LOG_LEVEL_INFO, LOG_LEVEL_NOTICE, LOG_LEVEL_URGENT, LOG_LEVEL_VERBOSE, type SavingEntry, MISSING_OR_ERROR, NOT_CONFLICTED, AUTO_MERGED, CANCELLED, LEAVE_TO_SUBSEQUENT, FLAGMD_REDFLAG2_HR, FLAGMD_REDFLAG3_HR, REMOTE_MINIO, REMOTE_COUCHDB, type BucketSyncSetting, TweakValuesShouldMatchedTemplate, confName, type TweakValues, type HasSettings, SETTING_VERSION_SUPPORT_CASE_INSENSITIVE, CompatibilityBreakingTweakValues, type FilePathWithPrefixLC, type MetaEntry, isMetaEntry, } from "./lib/src/common/types.ts"; import { type InternalFileInfo, type CacheData, type FileEventItem } from "./common/types.ts"; -import { arrayToChunkedArray, createBlob, delay, determineTypeFromBlob, escapeMarkdownValue, extractObject, fireAndForget, getDocData, isAnyNote, isDocContentSame, isObjectDifferent, readContent, sendValue, sizeToHumanReadable, throttle, type SimpleStore } from "./lib/src/common/utils.ts"; +import { arrayToChunkedArray, createBlob, delay, determineTypeFromBlob, escapeMarkdownValue, extractObject, fireAndForget, getDocData, isAnyNote, isDocContentSame, isObjectDifferent, readContent, sendValue, sizeToHumanReadable, throttle, unique, type SimpleStore } from "./lib/src/common/utils.ts"; import { Logger, setGlobalLogFunction } from "./lib/src/common/logger.ts"; import { PouchDB } from "./lib/src/pouchdb/pouchdb-browser.js"; import { ConflictResolveModal } from "./ui/ConflictResolveModal.ts"; import { ObsidianLiveSyncSettingTab } from "./ui/ObsidianLiveSyncSettingTab.ts"; import { DocumentHistoryModal } from "./ui/DocumentHistoryModal.ts"; -import { applyPatch, cancelAllPeriodicTask, cancelAllTasks, cancelTask, generatePatchObj, id2path, isObjectMargeApplicable, isSensibleMargeApplicable, flattenObject, path2id, scheduleTask, tryParseJSON, isValidPath, isInternalMetadata, isPluginMetadata, stripInternalMetadataPrefix, isChunk, askSelectString, askYesNo, askString, PeriodicProcessor, getPath, getPathWithoutPrefix, getPathFromTFile, performRebuildDB, memoIfNotExist, memoObject, retrieveMemoObject, disposeMemoObject, isCustomisationSyncMetadata, compareFileFreshness, BASE_IS_NEW, TARGET_IS_NEW, EVEN, compareMTime, markChangesAreSame } from "./common/utils.ts"; +import { applyPatch, cancelAllPeriodicTask, cancelAllTasks, cancelTask, generatePatchObj, id2path, isObjectMargeApplicable, isSensibleMargeApplicable, flattenObject, path2id, scheduleTask, tryParseJSON, isValidPath, isInternalMetadata, isPluginMetadata, stripInternalMetadataPrefix, isChunk, askSelectString, askYesNo, askString, PeriodicProcessor, getPath, getPathWithoutPrefix, getPathFromTFile, performRebuildDB, memoIfNotExist, memoObject, retrieveMemoObject, disposeMemoObject, isCustomisationSyncMetadata, compareFileFreshness, BASE_IS_NEW, TARGET_IS_NEW, EVEN, compareMTime, markChangesAreSame, useMemo } from "./common/utils.ts"; import { encrypt, tryDecrypt } from "./lib/src/encryption/e2ee_v2.ts"; -import { balanceChunkPurgedDBs, enableCompression, enableEncryption, isCloudantURI, isErrorOfMissingDoc, isValidRemoteCouchDBURI, purgeUnreferencedChunks } from "./lib/src/pouchdb/utils_couchdb.ts"; +import { balanceChunkPurgedDBs, replicationFilter, enableEncryption, isCloudantURI, isErrorOfMissingDoc, isValidRemoteCouchDBURI, purgeUnreferencedChunks, disableEncryption } from "./lib/src/pouchdb/utils_couchdb.ts"; import { logStore, type LogEntry, collectingChunks, pluginScanningCount, hiddenFilesProcessingCount, hiddenFilesEventCount, logMessages } from "./lib/src/mock_and_interop/stores.ts"; import { setNoticeClass } from "./lib/src/mock_and_interop/wrapper.ts"; import { versionNumberString2Number, writeString, decodeBinary, readString } from "./lib/src/string_and_binary/convert.ts"; @@ -32,7 +32,7 @@ import { LogPaneView, VIEW_TYPE_LOG } from "./ui/LogPaneView.ts"; import { LRUCache } from "./lib/src/memory/LRUCache.ts"; import { SerializedFileAccess } from "./storages/SerializedFileAccess.js"; import { QueueProcessor, stopAllRunningProcessors } from "./lib/src/concurrency/processor.js"; -import { computed, reactive, reactiveSource, type ReactiveValue } from "./lib/src/dataobject/reactive.js"; +import { reactive, reactiveSource, type ReactiveValue } from "./lib/src/dataobject/reactive.js"; import { initializeStores } from "./common/stores.js"; import { JournalSyncMinio } from "./lib/src/replication/journal/objectstore/JournalSyncMinio.js"; import { LiveSyncJournalReplicator, type LiveSyncJournalReplicatorEnv } from "./lib/src/replication/journal/LiveSyncJournalReplicator.js"; @@ -42,13 +42,16 @@ import { ObsHttpHandler } from "./common/ObsHttpHandler.js"; import { TestPaneView, VIEW_TYPE_TEST } from "./tests/TestPaneView.js" import { $f, __onMissingTranslation, setLang } from "./lib/src/common/i18n.ts"; import { enableTestFunction } from "./tests/testUtils.ts"; -import { terminateWorker } from "./lib/src/worker/splitWorker.ts"; +import { LogAddOn } from "./features/CmdStatusInsideEditor.ts"; +import { eventHub } from "./lib/src/hub/hub.ts"; +import { EVENT_FILE_RENAMED, EVENT_LAYOUT_READY, EVENT_LEAF_ACTIVE_CHANGED, EVENT_PLUGIN_LOADED, EVENT_PLUGIN_UNLOADED, EVENT_SETTING_SAVED } from "./common/events.ts"; +import { Semaphore } from "octagonal-wheels/concurrency/semaphore"; setNoticeClass(Notice); // DI the log again. -setGlobalLogFunction((message: any, level?: LOG_LEVEL, key?: string) => { +setGlobalLogFunction((message: any, level?: number, key?: string) => { const entry = { message, level, key } as LogEntry; logStore.enqueue(entry); }); @@ -79,7 +82,7 @@ const SETTING_HEADER = "````yaml:livesync-setting\n"; const SETTING_FOOTER = "\n````"; export default class ObsidianLiveSyncPlugin extends Plugin - implements LiveSyncLocalDBEnv, LiveSyncReplicatorEnv, LiveSyncJournalReplicatorEnv, LiveSyncCouchDBReplicatorEnv { + implements LiveSyncLocalDBEnv, LiveSyncReplicatorEnv, LiveSyncJournalReplicatorEnv, LiveSyncCouchDBReplicatorEnv, HasSettings { _customHandler!: ObsHttpHandler; customFetchHandler() { if (!this._customHandler) this._customHandler = new ObsHttpHandler(undefined, undefined); @@ -91,7 +94,6 @@ export default class ObsidianLiveSyncPlugin extends Plugin replicator!: LiveSyncAbstractReplicator; settingTab!: ObsidianLiveSyncSettingTab; - statusBar?: HTMLElement; _suspended = false; get suspended() { return this._suspended || !this.settings?.isConfigured; @@ -116,7 +118,8 @@ export default class ObsidianLiveSyncPlugin extends Plugin addOnHiddenFileSync = new HiddenFileSync(this); addOnSetup = new SetupLiveSync(this); addOnConfigSync = new ConfigSync(this); - addOns = [this.addOnHiddenFileSync, this.addOnSetup, this.addOnConfigSync] as LiveSyncCommands[]; + addOnLogs = new LogAddOn(this); + addOns = [this.addOnHiddenFileSync, this.addOnSetup, this.addOnConfigSync, this.addOnLogs] as LiveSyncCommands[]; periodicSyncProcessor = new PeriodicProcessor(this, async () => await this.replicate()); @@ -127,7 +130,7 @@ export default class ObsidianLiveSyncPlugin extends Plugin return !this.last_successful_post; } - vaultAccess: SerializedFileAccess = new SerializedFileAccess(this.app); + vaultAccess: SerializedFileAccess = new SerializedFileAccess(this.app, this); _unloaded = false; @@ -141,6 +144,11 @@ export default class ObsidianLiveSyncPlugin extends Plugin return this.isMobile; } + get shouldCheckCaseInsensitive() { + if (this.vaultAccess.isStorageInsensitive()) return false; + return !this.settings.handleFilenameCaseSensitive; + } + requestCount = reactiveSource(0); responseCount = reactiveSource(0); processReplication = (e: PouchDB.Core.ExistingDocument[]) => this.parseReplicationResult(e); @@ -267,7 +275,8 @@ export default class ObsidianLiveSyncPlugin extends Plugin }; const db: PouchDB.Database = new PouchDB(uri, conf); - enableCompression(db, compression); + replicationFilter(db, compression); + disableEncryption(); if (passphrase !== "false" && typeof passphrase === "string") { enableEncryption(db, passphrase, useDynamicIterationCount, false); } @@ -298,6 +307,18 @@ export default class ObsidianLiveSyncPlugin extends Plugin getActiveFile() { return this.app.workspace.getActiveFile(); } + async getActiveFileStatus() { + const thisFile = this.app.workspace.getActiveFile(); + if (!thisFile) return ""; + // Case Sensitivity + if (this.shouldCheckCaseInsensitive) { + const f = this.vaultAccess.getFiles().map(e => e.path).filter(e => e.toLowerCase() == thisFile.path.toLowerCase()); + if (f.length > 1) return "Not synchronised: There are multiple files with the same name"; + } + if (!await this.isTargetFile(thisFile)) return "Not synchronised: not a target file"; + if (this.isFileSizeExceeded(thisFile.stat.size)) return "Not synchronised: File size exceeded"; + return ""; + } get appId() { return `${("appId" in this.app ? this.app.appId : "")}`; @@ -319,7 +340,7 @@ export default class ObsidianLiveSyncPlugin extends Plugin } async path2id(filename: FilePathWithPrefix | FilePath, prefix?: string): Promise { const destPath = addPrefix(filename, prefix ?? ""); - return await path2id(destPath, this.settings.usePathObfuscation ? this.settings.passphrase : ""); + return await path2id(destPath, this.settings.usePathObfuscation ? this.settings.passphrase : "", !this.settings.handleFilenameCaseSensitive); } createPouchDBInstance(name?: string, options?: PouchDB.Configuration.DatabaseConfiguration): PouchDB.Database { @@ -460,6 +481,7 @@ export default class ObsidianLiveSyncPlugin extends Plugin } } + // TODO Check async collectDeletedFiles() { const limitDays = this.settings.automaticallyDeleteMetadataOfDeletedFiles; if (limitDays <= 0) return; @@ -534,13 +556,160 @@ export default class ObsidianLiveSyncPlugin extends Plugin const minioJournal = this.getMinioJournalSyncClient(); await minioJournal.sync(); } + async migrateToCaseInsensitive(old: number, current: number) { + if (this.settings.handleFilenameCaseSensitive !== undefined && this.settings.doNotUseFixedRevisionForChunks !== undefined) { + if (current < SETTING_VERSION_SUPPORT_CASE_INSENSITIVE) { + this.settings.settingVersion = SETTING_VERSION_SUPPORT_CASE_INSENSITIVE; + await this.saveSettings(); + } + return true; + } + if (old >= SETTING_VERSION_SUPPORT_CASE_INSENSITIVE && this.settings.handleFilenameCaseSensitive !== undefined && this.settings.doNotUseFixedRevisionForChunks !== undefined) { + return true; + } + + let remoteHandleFilenameCaseSensitive: undefined | boolean = undefined; + let remoteDoNotUseFixedRevisionForChunks: undefined | boolean = undefined; + let remoteChecked = false; + try { + const remoteInfo = await this.replicator.getRemotePreferredTweakValues(this.settings); + if (remoteInfo) { + remoteHandleFilenameCaseSensitive = remoteInfo?.handleFilenameCaseSensitive; + remoteDoNotUseFixedRevisionForChunks = remoteInfo?.doNotUseFixedRevisionForChunks; + if (remoteHandleFilenameCaseSensitive !== undefined || remoteDoNotUseFixedRevisionForChunks !== undefined) { + remoteChecked = true; + } + } else { + Logger("Failed to fetch remote tweak values", LOG_LEVEL_INFO); + } + } catch (ex) { + Logger("Could not get remote tweak values", LOG_LEVEL_INFO); + Logger(ex, LOG_LEVEL_VERBOSE); + } + + if (remoteChecked) { + // The case that the remote could be checked. + if (remoteHandleFilenameCaseSensitive && remoteDoNotUseFixedRevisionForChunks) { + // Migrated, but configured as same as old behaviour. + this.settings.handleFilenameCaseSensitive = true; + this.settings.doNotUseFixedRevisionForChunks = true; + this.settings.settingVersion = SETTING_VERSION_SUPPORT_CASE_INSENSITIVE; + Logger(`Migrated to db:${current} with the same behaviour as before`, LOG_LEVEL_INFO); + await this.saveSettings(); + return true; + } + const message = `As you may already know, the self-hosted LiveSync has changed its default behaviour and database structure. + +And thankfully, with your time and efforts, the remote database appears to have already been migrated. Congratulations! + +However, we need a bit more. The configuration of this device is not compatible with the remote database. We will need to fetch the remote database again. Should we fetch from the remote again now? + +___Note: We cannot synchronise until the configuration has been changed and the database has been fetched again.___ +___Note2: The chunks are completely immutable, we can fetch only the metadata and difference.___ +`; + const OPTION_FETCH = "Yes, fetch again"; + const DISMISS = "No, please ask again"; + const options = [OPTION_FETCH, DISMISS]; + const ret = await confirmWithMessage(this, "Case Sensitivity", message, options, "No, please ask again", 40); + if (ret == OPTION_FETCH) { + this.settings.handleFilenameCaseSensitive = remoteHandleFilenameCaseSensitive || false; + this.settings.doNotUseFixedRevisionForChunks = remoteDoNotUseFixedRevisionForChunks || false; + this.settings.settingVersion = SETTING_VERSION_SUPPORT_CASE_INSENSITIVE; + await this.saveSettings(); + try { + await this.scheduleFetch(); + return; + } catch (ex) { + Logger("Failed to create redflag2", LOG_LEVEL_VERBOSE); + Logger(ex, LOG_LEVEL_VERBOSE); + } + return false; + } else { + return false; + } + + } + + const ENABLE_BOTH = "Enable both"; + const ENABLE_FILENAME_CASE_INSENSITIVE = "Enable only #1"; + const ENABLE_FIXED_REVISION_FOR_CHUNKS = "Enable only #2"; + const ADJUST_TO_REMOTE = "Adjust to remote"; + const DISMISS = "Decide it later"; + const KEEP = "Keep previous behaviour"; + const message = `Since v0.23.21, the self-hosted LiveSync has changed the default behaviour and database structure. The following changes have been made: + +1. **Case sensitivity of filenames** + The handling of filenames is now case-insensitive. This is a beneficial change for most platforms, other than Linux and iOS, which do not manage filename case sensitivity effectively. + (On These, a warning will be displayed for files with the same name but different cases). + +2. **Revision handling of the chunks** + Chunks are immutable, which allows their revisions to be fixed. This change will enhance the performance of file saving. + +___However, to enable either of these changes, both remote and local databases need to be rebuilt. This process takes a few minutes, and we recommend doing it when you have ample time.___ + +- If you wish to maintain the previous behaviour, you can skip this process by using \`${KEEP}\`. +- If you do not have enough time, please choose \`${DISMISS}\`. You will be prompted again later. +- If you have rebuilt the database on another device, please select \`${DISMISS}\` and try synchronizing again. Since a difference has been detected, you will be prompted again. +`; + const options = [ENABLE_BOTH, ENABLE_FILENAME_CASE_INSENSITIVE, ENABLE_FIXED_REVISION_FOR_CHUNKS]; + if (remoteChecked) { + options.push(ADJUST_TO_REMOTE); + } + options.push(KEEP, DISMISS); + const ret = await confirmWithMessage(this, "Case Sensitivity", message, options, DISMISS, 40); + console.dir(ret); + switch (ret) { + case ENABLE_BOTH: + this.settings.handleFilenameCaseSensitive = false; + this.settings.doNotUseFixedRevisionForChunks = false; + break; + case ENABLE_FILENAME_CASE_INSENSITIVE: + this.settings.handleFilenameCaseSensitive = false; + this.settings.doNotUseFixedRevisionForChunks = true; + break; + case ENABLE_FIXED_REVISION_FOR_CHUNKS: + this.settings.doNotUseFixedRevisionForChunks = false; + this.settings.handleFilenameCaseSensitive = true; + break; + case KEEP: + this.settings.handleFilenameCaseSensitive = true; + this.settings.doNotUseFixedRevisionForChunks = true; + this.settings.settingVersion = SETTING_VERSION_SUPPORT_CASE_INSENSITIVE; + await this.saveSettings(); + return true; + case DISMISS: + default: + return false; + + } + this.settings.settingVersion = SETTING_VERSION_SUPPORT_CASE_INSENSITIVE; + await this.saveSettings(); + await this.scheduleRebuild(); + await this.performAppReload(); + + } + async migrationCheck() { + const old = this.settings.settingVersion; + const current = SETTING_VERSION_SUPPORT_CASE_INSENSITIVE; + // Check each migrations(old -> current) + if (!await this.migrateToCaseInsensitive(old, current)) { + Logger(`Migration failed or cancelled from ${old} to ${current}`, LOG_LEVEL_NOTICE); + return; + } + + } async onLayoutReady() { + eventHub.emit(EVENT_LAYOUT_READY); this.registerFileWatchEvents(); if (!this.localDatabase.isReady) { Logger(`Something went wrong! The local database is not ready`, LOG_LEVEL_NOTICE); return; } + if (this.settings.isConfigured) { + await this.migrationCheck(); + } if (!this.settings.isConfigured) { + // Case sensitivity const message = `Hello and welcome to Self-hosted LiveSync. Your device seems to **not be configured yet**. Please finish the setup and synchronise your vaults! @@ -577,6 +746,20 @@ Click anywhere to stop counting down. try { if (this.isRedFlagRaised() || this.isRedFlag2Raised() || this.isRedFlag3Raised()) { + if (this.isRedFlag2Raised()) { + if (await this.askYesNo("Rebuild everything has been scheduled! Are you sure to rebuild everything?") !== "yes") { + await this.deleteRedFlag2(); + await this.performAppReload(); + return; + } + } + if (this.isRedFlag3Raised()) { + if (await this.askYesNo("Fetch again has been scheduled! Are you sure?") !== "yes") { + await this.deleteRedFlag3(); + await this.performAppReload(); + return; + } + } this.settings.batchSave = false; this.addOnSetup.suspendAllSync(); this.addOnSetup.suspendExtraSync(); @@ -593,7 +776,8 @@ Click anywhere to stop counting down. } } else if (this.isRedFlag3Raised()) { Logger(`${FLAGMD_REDFLAG3} or ${FLAGMD_REDFLAG3_HR} has been detected! Self-hosted LiveSync will discard the local database and fetch everything from the remote once again.`, LOG_LEVEL_NOTICE); - await this.addOnSetup.fetchLocal(); + const makeLocalChunkBeforeSync = ((await this.askYesNo("Do you want to create local chunks before fetching?")) == "yes"); + await this.addOnSetup.fetchLocal(makeLocalChunkBeforeSync); await this.deleteRedFlag3(); if (this.settings.suspendFileWatching) { if (await this.askYesNo("Do you want to disable Suspend file watching and restart obsidian now?") == "yes") { @@ -701,8 +885,7 @@ However, **Please make sure that all devices have been synchronised**. \n await this.replicateAllFromServer(true); await delay(3000); Logger(`Obsidian will be reloaded to rebuild everything.`, LOG_LEVEL_NOTICE); - await this.vaultAccess.vaultCreate(FLAGMD_REDFLAG2_HR, ""); - this.performAppReload(); + await this.scheduleRebuild(); } } else if (ret == ANSWER_ENLARGE_LIMIT) { this.settings.notifyThresholdOfRemoteStorageSize = ~~(estimatedSize / 1024 / 1024) + 100; @@ -965,7 +1148,35 @@ Note: We can always able to read V1 format. It will be progressively converted. } } + wireUpEvents() { + eventHub.on(EVENT_SETTING_SAVED, (evt: CustomEvent) => { + const settings = evt.detail; + this.localDatabase.settings = settings; + setLang(settings.displayLanguage); + this.settingTab.requestReload(); + this.ignoreFiles = settings.ignoreFiles.split(",").map(e => e.trim()); + }); + eventHub.on(EVENT_SETTING_SAVED, (evt: CustomEvent) => { + const settings = evt.detail; + if (settings.settingSyncFile != "") { + fireAndForget(() => this.saveSettingToMarkdown(settings.settingSyncFile)); + } + }) + eventHub.on(EVENT_SETTING_SAVED, (evt: CustomEvent) => { + fireAndForget(() => this.realizeSettingSyncMode()); + }) + } + connectObsidianEvents() { + // this.registerEvent(this.app.workspace.on("editor-change", )); + this.registerEvent(this.app.vault.on("rename", (file, oldPath) => { + eventHub.emit(EVENT_FILE_RENAMED, { newPath: file.path, old: oldPath }); + })); + this.registerEvent(this.app.workspace.on("active-leaf-change", () => eventHub.emit(EVENT_LEAF_ACTIVE_CHANGED))); + } async onload() { + this.wireUpEvents(); + this.connectObsidianEvents(); + eventHub.emit(EVENT_PLUGIN_LOADED, this); logStore.pipeTo(new QueueProcessor(logs => logs.forEach(e => this.addLog(e.message, e.level, e.key)), { suspended: false, batchSize: 20, concurrentLimit: 1, delay: 0 })).startPipeline(); Logger("loading plugin"); __onMissingTranslation(() => { }); @@ -1008,11 +1219,7 @@ Note: We can always able to read V1 format. It will be progressively converted. await this.loadSettings(); const lsKey = "obsidian-live-sync-ver" + this.getVaultName(); const last_version = localStorage.getItem(lsKey); - this.observeForLogs(); - if (this.settings.showStatusOnStatusbar) { - this.statusBar = this.addStatusBarItem(); - this.statusBar.addClass("syncstatusbar"); - } + const lastVersion = ~~(versionNumberString2Number(manifestVersion) / 1000); if (lastVersion > this.settings.lastReadUpdates && this.settings.isConfigured) { Logger($f`Self-hosted LiveSync has undergone a major upgrade. Please open the setting dialog, and check the information pane.`, LOG_LEVEL_NOTICE); @@ -1074,7 +1281,7 @@ Note: We can always able to read V1 format. It will be progressively converted. } onunload() { - terminateWorker(); + eventHub.emit(EVENT_PLUGIN_UNLOADED); cancelAllPeriodicTask(); cancelAllTasks(); stopAllRunningProcessors(); @@ -1278,13 +1485,7 @@ Note: We can always able to read V1 format. It will be progressively converted. } await this.saveData(settings); - this.localDatabase.settings = this.settings; - setLang(this.settings.displayLanguage); - this.settingTab.requestReload(); - this.ignoreFiles = this.settings.ignoreFiles.split(",").map(e => e.trim()); - if (this.settings.settingSyncFile != "") { - fireAndForget(() => this.saveSettingToMarkdown(this.settings.settingSyncFile)); - } + eventHub.emit(EVENT_SETTING_SAVED, settings); } extractSettingFromWholeText(data: string): { preamble: string, body: string, postscript: string } { @@ -1376,10 +1577,10 @@ Note: We can always able to read V1 format. It will be progressively converted. return; } if (result == APPLY_AND_REBUILD) { - await this.vaultAccess.vaultCreate(FLAGMD_REDFLAG2_HR, ""); + await this.scheduleRebuild(); } if (result == APPLY_AND_FETCH) { - await this.vaultAccess.vaultCreate(FLAGMD_REDFLAG3_HR, ""); + await this.scheduleFetch(); } this.performAppReload(); } @@ -1387,6 +1588,24 @@ Note: We can always able to read V1 format. It will be progressively converted. ) }) } + async scheduleRebuild() { + try { + await this.vaultAccess.vaultCreate(FLAGMD_REDFLAG2_HR, ""); + } catch (ex) { + Logger("Could not create red_flag_rebuild.md", LOG_LEVEL_NOTICE); + } + this.performAppReload(); + } + async scheduleFetch() { + try { + await this.vaultAccess.vaultCreate(FLAGMD_REDFLAG3_HR, ""); + } catch (ex) { + Logger("Could not create red_flag_fetch.md", LOG_LEVEL_NOTICE); + } + this.performAppReload(); + } + + generateSettingForMarkdown(settings?: ObsidianLiveSyncSettings, keepCredential?: boolean): Partial { const saveData = { ...(settings ? settings : this.settings) } as Partial; delete saveData.encryptedCouchDBConnection; @@ -1440,7 +1659,6 @@ We can perform a command in this file. async saveSettings() { await this.saveSettingData(); - fireAndForget(() => this.realizeSettingSyncMode()); } vaultManager: StorageEventManager = new StorageEventManagerObsidian(this); @@ -1730,6 +1948,7 @@ We can perform a command in this file. this.vaultAccess.adapterAppend(normalizePath(logDate), vaultName + ":" + newMessage + "\n"); } recentLogProcessor.enqueue(newMessage); + this.addOnLogs.logLines.push({ ttl: now.getTime() + 3000, message: newMessage }); if (level >= LOG_LEVEL_NOTICE) { if (!key) key = messageContent; @@ -2047,136 +2266,10 @@ We can perform a command in this file. lastMessage = ""; - observeForLogs() { - const padSpaces = `\u{2007}`.repeat(10); - // const emptyMark = `\u{2003}`; - function padLeftSpComputed(numI: ReactiveValue, mark: string) { - const formatted = reactiveSource(""); - let timer: ReturnType | undefined = undefined; - let maxLen = 1; - numI.onChanged(numX => { - const num = numX.value; - const numLen = `${Math.abs(num)}`.length + 1; - maxLen = maxLen < numLen ? numLen : maxLen; - if (timer) clearTimeout(timer); - if (num == 0) { - timer = setTimeout(() => { - formatted.value = ""; - maxLen = 1; - }, 3000); - } - formatted.value = ` ${mark}${`${padSpaces}${num}`.slice(-(maxLen))}`; - }) - return computed(() => formatted.value); - } - const labelReplication = padLeftSpComputed(this.replicationResultCount, `📥`); - const labelDBCount = padLeftSpComputed(this.databaseQueueCount, `📄`); - const labelStorageCount = padLeftSpComputed(this.storageApplyingCount, `💾`); - const labelChunkCount = padLeftSpComputed(collectingChunks, `🧩`); - const labelPluginScanCount = padLeftSpComputed(pluginScanningCount, `🔌`); - const labelConflictProcessCount = padLeftSpComputed(this.conflictProcessQueueCount, `🔩`); - const hiddenFilesCount = reactive(() => hiddenFilesEventCount.value + hiddenFilesProcessingCount.value); - const labelHiddenFilesCount = padLeftSpComputed(hiddenFilesCount, `⚙️`) - const queueCountLabelX = reactive(() => { - return `${labelReplication()}${labelDBCount()}${labelStorageCount()}${labelChunkCount()}${labelPluginScanCount()}${labelHiddenFilesCount()}${labelConflictProcessCount()}`; - }) - const queueCountLabel = () => queueCountLabelX.value; - const requestingStatLabel = computed(() => { - const diff = this.requestCount.value - this.responseCount.value; - return diff != 0 ? "📲 " : ""; - }) - const replicationStatLabel = computed(() => { - const e = this.replicationStat.value; - const sent = e.sent; - const arrived = e.arrived; - const maxPullSeq = e.maxPullSeq; - const maxPushSeq = e.maxPushSeq; - const lastSyncPullSeq = e.lastSyncPullSeq; - const lastSyncPushSeq = e.lastSyncPushSeq; - let pushLast = ""; - let pullLast = ""; - let w = ""; - const labels: Partial> = { - "CONNECTED": "⚡", - "JOURNAL_SEND": "📦↑", - "JOURNAL_RECEIVE": "📦↓", - } - switch (e.syncStatus) { - case "CLOSED": - case "COMPLETED": - case "NOT_CONNECTED": - w = "⏹"; - break; - case "STARTED": - w = "🌀"; - break; - case "PAUSED": - w = "💤"; - break; - case "CONNECTED": - case "JOURNAL_SEND": - case "JOURNAL_RECEIVE": - w = labels[e.syncStatus] || "⚡"; - pushLast = ((lastSyncPushSeq == 0) ? "" : (lastSyncPushSeq >= maxPushSeq ? " (LIVE)" : ` (${maxPushSeq - lastSyncPushSeq})`)); - pullLast = ((lastSyncPullSeq == 0) ? "" : (lastSyncPullSeq >= maxPullSeq ? " (LIVE)" : ` (${maxPullSeq - lastSyncPullSeq})`)); - break; - case "ERRORED": - w = "⚠"; - break; - default: - w = "?"; - } - return { w, sent, pushLast, arrived, pullLast }; - }) - const labelProc = padLeftSpComputed(this.vaultManager.processing, `⏳`); - const labelPend = padLeftSpComputed(this.vaultManager.totalQueued, `🛫`); - const labelInBatchDelay = padLeftSpComputed(this.vaultManager.batched, `📬`); - const waitingLabel = computed(() => { - return `${labelProc()}${labelPend()}${labelInBatchDelay()}`; - }) - const statusLineLabel = computed(() => { - const { w, sent, pushLast, arrived, pullLast } = replicationStatLabel(); - const queued = queueCountLabel(); - const waiting = waitingLabel(); - const networkActivity = requestingStatLabel(); - return { - message: `${networkActivity}Sync: ${w} ↑ ${sent}${pushLast} ↓ ${arrived}${pullLast}${waiting}${queued}`, - }; - }) - const statusBarLabels = reactive(() => { - const scheduleMessage = this.isReloadingScheduled ? `WARNING! RESTARTING OBSIDIAN IS SCHEDULED!\n` : ""; - const { message } = statusLineLabel(); - const status = scheduleMessage + this.statusLog.value; - return { - message, status - } - }) - const applyToDisplay = throttle((label: typeof statusBarLabels.value) => { - const v = label; - this.applyStatusBarText(v.message, v.status); - }, 20); - statusBarLabels.onChanged(label => applyToDisplay(label.value)) - } - - applyStatusBarText(message: string, log: string) { - const newMsg = message.replace(/\n/g, "\\A "); - const newLog = log.replace(/\n/g, "\\A "); - - this.statusBar?.setText(newMsg.split("\n")[0]); - if (this.settings.showStatusOnEditor) { - const root = activeDocument.documentElement; - root.style.setProperty("--sls-log-text", "'" + (newMsg + "\\A " + newLog) + "'"); - } else { - // const root = activeDocument.documentElement; - // root.style.setProperty("--log-text", "'" + (newMsg + "\\A " + newLog) + "'"); - } - - scheduleTask("log-hide", 3000, () => { this.statusLog.value = "" }); - } async askResolvingMismatchedTweaks(): Promise<"OK" | "CHECKAGAIN" | "IGNORE"> { if (!this.replicator.tweakSettingsMismatched) { return "OK"; @@ -2184,6 +2277,8 @@ We can perform a command in this file. const preferred = extractObject(TweakValuesShouldMatchedTemplate, this.replicator.preferredTweakValue!); const mine = extractObject(TweakValuesShouldMatchedTemplate, this.settings); const items = Object.entries(TweakValuesShouldMatchedTemplate); + let rebuildRequired = false; + // Making tables: let table = `| Value name | This device | Configured | \n` + `|: --- |: --- :|: ---- :| \n`; @@ -2194,9 +2289,18 @@ We can perform a command in this file. const valueMine = escapeMarkdownValue(mine[key]); const valuePreferred = escapeMarkdownValue(preferred[key]); if (valueMine == valuePreferred) continue; + if (CompatibilityBreakingTweakValues.indexOf(key) !== -1) { + rebuildRequired = true; + } table += `| ${confName(key)} | ${valueMine} | ${valuePreferred} | \n`; } + const additionalMessage = rebuildRequired ? ` + +**Note**: We have detected that some of the values are different to make incompatible the local database with the remote database. +If you choose to use the configured values, the local database will be rebuilt, and if you choose to use the values of this device, the remote database will be rebuilt. +Both of them takes a few minutes. Please choose after considering the situation.` : ""; + const message = ` Your configuration has not been matched with the one on the remote server. (Which you had decided once before, or set by initially synchronised device). @@ -2208,11 +2312,11 @@ ${table} Please select which one you want to use. - Use configured: Update settings of this device by configured one on the remote server. - You should select this if you have changed the settings on **another device**. + You should select this if you have changed the settings on ** another device **. - Update with mine: Update settings on the remote server by the settings of this device. - You should select this if you have changed the settings on **this device**. + You should select this if you have changed the settings on ** this device **. - Dismiss: Ignore this message and keep the current settings. - You cannot synchronise until you resolve this issue without enabling \`Do not check configuration mismatch before replication\`.`; + You cannot synchronise until you resolve this issue without enabling \`Do not check configuration mismatch before replication\`.${additionalMessage}`; const CHOICE_USE_REMOTE = "Use configured"; const CHOICE_USR_MINE = "Update with mine"; @@ -2229,6 +2333,9 @@ Please select which one you want to use. if (conf === true) { await this.replicator.setPreferredRemoteTweakSettings(this.settings); + if (rebuildRequired) { + await this.addOnSetup.rebuildRemote(); + } Logger(`Tweak values on the remote server have been updated. Your other device will see this message.`, LOG_LEVEL_NOTICE); return "CHECKAGAIN"; } @@ -2236,6 +2343,9 @@ Please select which one you want to use. this.settings = { ...this.settings, ...conf }; await this.replicator.setPreferredRemoteTweakSettings(this.settings); await this.saveSettingData(); + if (rebuildRequired) { + await this.addOnSetup.fetchLocal(); + } Logger(`Configuration has been updated as configured by the other device.`, LOG_LEVEL_NOTICE); return "CHECKAGAIN"; } @@ -2387,7 +2497,66 @@ Or if you are sure know what had been happened, we can unlock the database from return false; } + async createAllChunks(showingNotice?: boolean) { + Logger("Collecting local files on the storage", LOG_LEVEL_VERBOSE); + const semaphore = Semaphore(10); + + let processed = 0; + const filesStorageSrc = this.vaultAccess.getFiles(); + const incProcessed = () => { + processed++; + if (processed % 25 == 0) Logger(`Creating missing chunks: ${processed} of ${total} files`, showingNotice ? LOG_LEVEL_NOTICE : LOG_LEVEL_INFO, "chunkCreation"); + } + const total = filesStorageSrc.length; + const procAllChunks = filesStorageSrc.map(async (file) => { + if (!await this.isTargetFile(file)) { + incProcessed(); + return true; + } + if (shouldBeIgnored(file.path)) { + incProcessed(); + return true; + } + const release = await semaphore.acquire(); + incProcessed(); + try { + Logger(`Chunk Creation: Started ${file.path}`, LOG_LEVEL_VERBOSE); + const content = createBlob(await this.vaultAccess.vaultReadAuto(file)); + const datatype = determineTypeFromBlob(content); + const fullPath = getPathFromTFile(file); + const id = await this.path2id(fullPath); + const d: SavingEntry = { + _id: id, + path: getPathFromTFile(file), + data: content, + ctime: file.stat.ctime, + mtime: file.stat.mtime, + size: file.stat.size, + children: [], + datatype: datatype, + type: datatype, + eden: {}, + }; + if (await this.localDatabase.putDBEntry(d, true) !== false) { + Logger(`Chunk Creation: Done ${file.path}`, LOG_LEVEL_VERBOSE); + } else { + Logger(`Chunk Creation: Failed ${file.path}`, LOG_LEVEL_NOTICE); + } + } catch (ex) { + Logger(ex, LOG_LEVEL_VERBOSE); + } finally { + release(); + } + }); + await Promise.all(procAllChunks); + Logger(`Creating chunks Done: ${processed} of ${total} files`, showingNotice ? LOG_LEVEL_NOTICE : LOG_LEVEL_INFO, "chunkCreation"); + } + + async syncAllFiles(showingNotice?: boolean) { + + Logger("Opening the key-value database", LOG_LEVEL_VERBOSE); + const isInitialized = await (this.kvDB.get("initialized")) || false; // synchronize all files between database and storage. if (!this.settings.isConfigured) { if (showingNotice) { @@ -2395,6 +2564,7 @@ Or if you are sure know what had been happened, we can unlock the database from } return; } + if (showingNotice) { Logger("Initializing", LOG_LEVEL_NOTICE, "syncAll"); } @@ -2406,39 +2576,73 @@ Or if you are sure know what had been happened, we can unlock the database from Logger("Collecting local files on the storage", LOG_LEVEL_VERBOSE); const filesStorageSrc = this.vaultAccess.getFiles(); - const filesStorage = [] as typeof filesStorageSrc; + const _filesStorage = [] as typeof filesStorageSrc; + for (const f of filesStorageSrc) { - if (await this.isTargetFile(f.path)) { - filesStorage.push(f); + if (await this.isTargetFile(f.path, f != filesStorageSrc[0])) { + _filesStorage.push(f); } } - const filesStorageName = filesStorage.map((e) => e.path); + const convertCase = (path: FilePathWithPrefix): FilePathWithPrefixLC => { + if (this.settings.handleFilenameCaseSensitive) { + return path as FilePathWithPrefixLC; + } + return (path as string).toLowerCase() as FilePathWithPrefixLC; + } + + // If handleFilenameCaseSensitive is enabled, `FilePathWithPrefixLC` is the same as `FilePathWithPrefix`. + + const storageFileNameMap = Object.fromEntries(_filesStorage.map((e) => [e.path as FilePathWithPrefix, e])); + + const storageFileNames = Object.keys(storageFileNameMap) as FilePathWithPrefix[]; + + const storageFileNameCapsPair = storageFileNames.map((e) => [e, convertCase(e)] as [FilePathWithPrefix, FilePathWithPrefixLC]); + + // const storageFileNameCS2CI = Object.fromEntries(storageFileNameCapsPair) as Record; + const storageFileNameCI2CS = Object.fromEntries(storageFileNameCapsPair.map(e => [e[1], e[0]])) as Record; + + Logger("Collecting local files on the DB", LOG_LEVEL_VERBOSE); - const filesDatabase = [] as FilePathWithPrefix[] + const _DBEntries = [] as MetaEntry[]; + // const _DBEntriesTask = [] as (() => Promise)[]; let count = 0; for await (const doc of this.localDatabase.findAllNormalDocs()) { count++; if (count % 25 == 0) Logger(`Collecting local files on the DB: ${count}`, showingNotice ? LOG_LEVEL_NOTICE : LOG_LEVEL_INFO, "syncAll"); const path = getPath(doc); - // const docPath = doc.path; - // if (path != docPath) { - // debugger; - // } - if (isValidPath(path) && await this.isTargetFile(path)) { - filesDatabase.push(path); + + if (isValidPath(path) && await this.isTargetFile(path, true)) { + if (!isMetaEntry(doc)) { + Logger(`Invalid entry: ${path}`, LOG_LEVEL_INFO); + continue; + } + _DBEntries.push(doc); } } - Logger("Opening the key-value database", LOG_LEVEL_VERBOSE); - const isInitialized = await (this.kvDB.get("initialized")) || false; - const onlyInStorage = filesStorage.filter((e) => filesDatabase.indexOf(getPathFromTFile(e)) == -1); - const onlyInDatabase = filesDatabase.filter((e) => filesStorageName.indexOf(e) == -1); + const databaseFileNameMap = Object.fromEntries(_DBEntries.map((e) => [getPath(e), e])); + const databaseFileNames = Object.keys(databaseFileNameMap) as FilePathWithPrefix[]; + const databaseFileNameCapsPair = databaseFileNames.map((e) => [e, convertCase(e)] as [FilePathWithPrefix, FilePathWithPrefixLC]); + // const databaseFileNameCS2CI = Object.fromEntries(databaseFileNameCapsPair) as Record; + const databaseFileNameCI2CS = Object.fromEntries(databaseFileNameCapsPair.map(e => [e[1], e[0]])) as Record; - const onlyInStorageNames = onlyInStorage.map((e) => e.path); + const allFiles = unique([...Object.keys(databaseFileNameCI2CS), ...Object.keys(storageFileNameCI2CS)]) as FilePathWithPrefixLC[]; - const syncFiles = filesStorage.filter((e) => onlyInStorageNames.indexOf(e.path) == -1); - Logger("Updating database by new files"); + Logger(`Total files in the database: ${databaseFileNames.length}`, LOG_LEVEL_VERBOSE, "syncAll"); + Logger(`Total files in the storage: ${storageFileNames.length}`, LOG_LEVEL_VERBOSE, "syncAll"); + Logger(`Total files: ${allFiles.length}`, LOG_LEVEL_VERBOSE, "syncAll"); + + + const filesExistOnlyInStorage = allFiles.filter((e) => !databaseFileNameCI2CS[e]); + const filesExistOnlyInDatabase = allFiles.filter((e) => !storageFileNameCI2CS[e]); + const filesExistBoth = allFiles.filter((e) => databaseFileNameCI2CS[e] && storageFileNameCI2CS[e]); + + Logger(`Files exist only in storage: ${filesExistOnlyInStorage.length}`, LOG_LEVEL_VERBOSE, "syncAll"); + Logger(`Files exist only in database: ${filesExistOnlyInDatabase.length}`, LOG_LEVEL_VERBOSE, "syncAll"); + Logger(`Files exist both in storage and database: ${filesExistBoth.length}`, LOG_LEVEL_VERBOSE, "syncAll"); + + Logger("Synchronising..."); const processStatus = {} as Record; const logLevel = showingNotice ? LOG_LEVEL_NOTICE : LOG_LEVEL_INFO; const updateLog = throttle((key: string, msg: string) => { @@ -2473,24 +2677,26 @@ Or if you are sure know what had been happened, we can unlock the database from updateLog(procedureName, msg); } return; - }, { batchSize: 1, concurrentLimit: 10, delay: 0, suspended: true }, objects) + }, { batchSize: 1, concurrentLimit: 10, delay: 0, suspended: true, maintainDelay: false, interval: 0 }, objects) await processor.waitForAllDoneAndTerminate(); const msg = `${procedureName} All done: DONE:${success}, FAILED:${failed}`; updateLog(procedureName, msg) } - initProcess.push(runAll("UPDATE DATABASE", onlyInStorage, async (e) => { - if (!this.isFileSizeExceeded(e.stat.size)) { - await this.updateIntoDB(e); - fireAndForget(() => this.checkAndApplySettingFromMarkdown(e.path, true)); + initProcess.push(runAll("UPDATE DATABASE", filesExistOnlyInStorage, async (e) => { + // console.warn("UPDATE DATABASE", e); + const file = storageFileNameMap[storageFileNameCI2CS[e]]; + if (!this.isFileSizeExceeded(file.stat.size)) { + await this.updateIntoDB(file); + fireAndForget(() => this.checkAndApplySettingFromMarkdown(e, true)); } else { - Logger(`UPDATE DATABASE: ${e.path} has been skipped due to file size exceeding the limit`, logLevel); + Logger(`UPDATE DATABASE: ${e} has been skipped due to file size exceeding the limit`, logLevel); } })); - initProcess.push(runAll("UPDATE STORAGE", onlyInDatabase, async (e) => { - const w = await this.localDatabase.getDBEntryMeta(e, {}, true); + initProcess.push(runAll("UPDATE STORAGE", filesExistOnlyInDatabase, async (e) => { + const w = databaseFileNameMap[databaseFileNameCI2CS[e]]; if (w && !(w.deleted || w._deleted)) { if (!this.isFileSizeExceeded(w.size)) { - await this.pullFile(e, filesStorage, false, undefined, false); + await this.pullFile(e, undefined, false, undefined, false); fireAndForget(() => this.checkAndApplySettingFromMarkdown(e, true)); Logger(`Check or pull from db:${e} OK`); } else { @@ -2502,53 +2708,67 @@ Or if you are sure know what had been happened, we can unlock the database from Logger(`entry not found: ${e}`); } })); - type FileDocPair = { file: TFile, id: DocumentID }; - const processPrepareSyncFile = new QueueProcessor( - async (files) => { - const file = files[0]; - const id = await this.path2id(getPathFromTFile(file)); - const pair: FileDocPair = { file, id }; - return [pair]; + const fileMap = filesExistBoth.map(path => { + const file = storageFileNameMap[storageFileNameCI2CS[path]]; + const doc = databaseFileNameMap[databaseFileNameCI2CS[path]]; + return { file, doc } + }) + initProcess.push(runAll("SYNC DATABASE AND STORAGE", fileMap, async (e) => { + const { file, doc } = e; + if (!this.isFileSizeExceeded(file.stat.size) && !this.isFileSizeExceeded(doc.size)) { + await this.syncFileBetweenDBandStorage(file, doc); + fireAndForget(() => this.checkAndApplySettingFromMarkdown(getPath(doc), true)); + } else { + Logger(`SYNC DATABASE AND STORAGE: ${getPath(doc)} has been skipped due to file size exceeding the limit`, logLevel); } - , { batchSize: 1, concurrentLimit: 10, delay: 0, suspended: true }, syncFiles); - processPrepareSyncFile - .pipeTo( - new QueueProcessor( - async (pairs) => { - const docs = await this.localDatabase.allDocsRaw({ keys: pairs.map(e => e.id), include_docs: true }); - const docsMap = Object.fromEntries(docs.rows.map(e => [e.id, e.doc])); - const syncFilesToSync = pairs.map((e) => ({ file: e.file, doc: docsMap[e.id] as LoadedEntry })); - return syncFilesToSync; - } - , { batchSize: 100, concurrentLimit: 1, delay: 10, suspended: false, maintainDelay: true, yieldThreshold: 100 })) - .pipeTo( - new QueueProcessor( - async (loadedPairs) => { - for (const pair of loadedPairs) - try { - const e = pair; - await this.syncFileBetweenDBandStorage(e.file, e.doc); - } catch (ex) { - Logger("Error while syncFileBetweenDBandStorage", LOG_LEVEL_NOTICE); - Logger(ex, LOG_LEVEL_VERBOSE); - } - return; - }, { batchSize: 5, concurrentLimit: 10, delay: 10, suspended: false, yieldThreshold: 10, maintainDelay: true } - )) + })) - const allSyncFiles = syncFiles.length; - let lastRemain = allSyncFiles; - const step = 25; - const remainLog = (remain: number) => { - if (lastRemain - remain > step) { - const msg = ` CHECK AND SYNC: ${allSyncFiles - remain} / ${allSyncFiles}`; - updateLog("sync", msg); - lastRemain = remain; - } - } - processPrepareSyncFile.startPipeline().onUpdateProgress(() => remainLog(processPrepareSyncFile.totalRemaining + processPrepareSyncFile.nowProcessing)) - initProcess.push(processPrepareSyncFile.waitForAllDoneAndTerminate()); + // const processPrepareSyncFile = new QueueProcessor( + // async (files) => { + // const file = files[0]; + // const id = await this.path2id(getPathFromTFile(file)); + // const pair: FileDocPair = { file, id }; + // return [pair]; + // } + // , { batchSize: 1, concurrentLimit: 10, delay: 0, suspended: true }, syncFiles); + // processPrepareSyncFile + // .pipeTo( + // new QueueProcessor( + // async (pairs) => { + // const docs = await this.localDatabase.allDocsRaw({ keys: pairs.map(e => e.id), include_docs: true }); + // const docsMap = Object.fromEntries(docs.rows.map(e => [e.id, e.doc])); + // const syncFilesToSync = pairs.map((e) => ({ file: e.file, doc: docsMap[e.id] as LoadedEntry })); + // return syncFilesToSync; + // } + // , { batchSize: 100, concurrentLimit: 1, delay: 10, suspended: false, maintainDelay: true, yieldThreshold: 100 })) + // .pipeTo( + // new QueueProcessor( + // async (loadedPairs) => { + // for (const pair of loadedPairs) + // try { + // const e = pair; + // await this.syncFileBetweenDBandStorage(e.file, e.doc); + // } catch (ex) { + // Logger("Error while syncFileBetweenDBandStorage", LOG_LEVEL_NOTICE); + // Logger(ex, LOG_LEVEL_VERBOSE); + // } + // return; + // }, { batchSize: 5, concurrentLimit: 10, delay: 10, suspended: false, yieldThreshold: 10, maintainDelay: true } + // )) + + // const allSyncFiles = syncFiles.length; + // let lastRemain = allSyncFiles; + // const step = 25; + // const remainLog = (remain: number) => { + // if (lastRemain - remain > step) { + // const msg = ` CHECK AND SYNC: ${allSyncFiles - remain} / ${allSyncFiles}`; + // updateLog("sync", msg); + // lastRemain = remain; + // } + // } + // processPrepareSyncFile.startPipeline().onUpdateProgress(() => remainLog(processPrepareSyncFile.totalRemaining + processPrepareSyncFile.nowProcessing)) + // initProcess.push(processPrepareSyncFile.waitForAllDoneAndTerminate()); await Promise.all(initProcess); // this.setStatusBarText(`NOW TRACKING!`); @@ -3015,7 +3235,7 @@ Or if you are sure know what had been happened, we can unlock the database from return false; } - async pullFile(filename: FilePathWithPrefix, fileList?: TFile[], force?: boolean, rev?: string, waitForReady = true) { + async pullFile(filename: FilePathWithPrefix, _: void, force?: boolean, rev?: string, waitForReady = true) { const targetFile = this.vaultAccess.getAbstractFileByPath(stripAllPrefixes(filename)); if (!await this.isTargetFile(filename)) return; if (targetFile == null) { @@ -3042,7 +3262,7 @@ Or if you are sure know what had been happened, we can unlock the database from //when to opened file; } - async syncFileBetweenDBandStorage(file: TFile, doc: LoadedEntry) { + async syncFileBetweenDBandStorage(file: TFile, doc: MetaEntry) { if (!doc) { throw new Error(`Missing doc:${(file as any).path}`) } @@ -3258,12 +3478,49 @@ Or if you are sure know what had been happened, we can unlock the database from return false; } - async isTargetFile(file: string | TAbstractFile) { + + + async isTargetFile(file: string | TAbstractFile, keepFileCheckList = false) { + const fileCount = useMemo>({ + key: "fileCount", + forceUpdate: !keepFileCheckList, + }, (ctx, prev) => { + const fileList = (ctx.get("fileList") ?? []) as FilePathWithPrefix[]; + const vaultFiles = this.vaultAccess.getFiles().map(e => e.path); + if (prev && vaultFiles.length == fileList.length) { + const fl3 = new Set([...fileList, ...vaultFiles]); + if (fileList.length == fl3.size && vaultFiles.length == fl3.size) { + return prev; + } + } + ctx.set("fileList", vaultFiles); + + const fileCount: Record = {}; + for (const file of vaultFiles) { + const lc = file.toLowerCase(); + if (!fileCount[lc]) { + fileCount[lc] = 1; + } else { + fileCount[lc]++; + } + } + return fileCount; + }) + const filepath = file instanceof TFile ? file.path : file as string; + const lc = filepath.toLowerCase(); + if (this.shouldCheckCaseInsensitive) { + if (lc in fileCount && fileCount[lc] > 1) { + return false; + } + } if (this.settings.useIgnoreFiles && await this.isIgnoredByIgnoreFiles(file)) { return false; } - return this.localDatabase.isTargetFile(filepath); + if (!this.localDatabase.isTargetFile(filepath)) return false; + return true + + } async dryRunGC() { await skipIfDuplicated("cleanup", async () => { @@ -3302,6 +3559,11 @@ Or if you are sure know what had been happened, we can unlock the database from askYesNo(message: string): Promise<"yes" | "no"> { return askYesNo(this.app, message); + + } + async askYesNoDialog(message: string, opt: { title: string, defaultOption: "Yes" | "No", timeout?: number } = { title: "Confirmation", defaultOption: "No" }): Promise<"yes" | "no"> { + const ret = await confirmWithMessage(this, opt.title, message, ["Yes", "No"], opt.defaultOption, opt.timeout); + return ret == "Yes" ? "yes" : "no"; } askSelectString(message: string, items: string[]): Promise { return askSelectString(this.app, message, items); @@ -3411,3 +3673,4 @@ Or if you are sure know what had been happened, we can unlock the database from } } + diff --git a/src/storages/SerializedFileAccess.ts b/src/storages/SerializedFileAccess.ts index 2f5d066..8df71ce 100644 --- a/src/storages/SerializedFileAccess.ts +++ b/src/storages/SerializedFileAccess.ts @@ -2,7 +2,7 @@ import { type App, TFile, type DataWriteOptions, TFolder, TAbstractFile } from " import { serialized } from "../lib/src/concurrency/lock.ts"; import { Logger } from "../lib/src/common/logger.ts"; import { isPlainText } from "../lib/src/string_and_binary/path.ts"; -import type { FilePath } from "../lib/src/common/types.ts"; +import type { FilePath, HasSettings } from "../lib/src/common/types.ts"; import { createBinaryBlob, isDocContentSame } from "../lib/src/common/utils.ts"; import type { InternalFileInfo } from "../common/types.ts"; import { markChangesAreSame } from "../common/utils.ts"; @@ -31,8 +31,10 @@ async function processWriteFile(file: TFile | TFolder | string, proc: () => P } export class SerializedFileAccess { app: App - constructor(app: App) { + plugin: HasSettings<{ handleFilenameCaseSensitive: boolean }> + constructor(app: App, plugin: typeof this["plugin"]) { this.app = app; + this.plugin = plugin; } async adapterStat(file: TFile | string) { @@ -138,17 +140,23 @@ export class SerializedFileAccess { return await processWriteFile(file, () => this.app.vault.trash(file, force)); } + + + isStorageInsensitive(): boolean { + //@ts-ignore + return this.app.vault.adapter.insensitive ?? true; + } + + getAbstractFileByPathInsensitive(path: FilePath | string): TAbstractFile | null { + //@ts-ignore + return app.vault.getAbstractFileByPathInsensitive(path); + } + getAbstractFileByPath(path: FilePath | string): TAbstractFile | null { - // Disabled temporary. + if (!this.plugin.settings.handleFilenameCaseSensitive || this.isStorageInsensitive()) { + return this.getAbstractFileByPathInsensitive(path); + } return this.app.vault.getAbstractFileByPath(path); - // // Hidden API but so useful. - // // @ts-ignore - // if ("getAbstractFileByPathInsensitive" in app.vault && (app.vault.adapter?.insensitive ?? false)) { - // // @ts-ignore - // return app.vault.getAbstractFileByPathInsensitive(path); - // } else { - // return app.vault.getAbstractFileByPath(path); - // } } getFiles() { diff --git a/src/ui/ObsidianLiveSyncSettingTab.ts b/src/ui/ObsidianLiveSyncSettingTab.ts index 3e52acc..769ed04 100644 --- a/src/ui/ObsidianLiveSyncSettingTab.ts +++ b/src/ui/ObsidianLiveSyncSettingTab.ts @@ -35,6 +35,7 @@ import { LiveSyncCouchDBReplicator } from "../lib/src/replication/couchdb/LiveSy import { type AllSettingItemKey, type AllStringItemKey, type AllNumericItemKey, type AllBooleanItemKey, type AllSettings, OnDialogSettingsDefault, getConfig, type OnDialogSettings, getConfName } from "./settingConstants.ts"; import { SUPPORTED_I18N_LANGS, type I18N_LANGS } from "src/lib/src/common/rosetta.ts"; import { $t } from "src/lib/src/common/i18n.ts"; +import { Semaphore } from "octagonal-wheels/concurrency/semaphore"; type OnUpdateResult = { visibility?: boolean, @@ -272,11 +273,11 @@ class Setting extends SettingOrg { }) return this; } - addApplyButton(keys: AllSettingItemKey[]) { + addApplyButton(keys: AllSettingItemKey[], text?: string) { this.addButton((button) => { this.applyButtonComponent = button; this.watchDirtyKeys = unique([...keys, ...this.watchDirtyKeys]); - button.setButtonText("Apply") + button.setButtonText(text ?? "Apply") button.onClick(async () => { await Setting.env.saveSettings(keys); Setting.env.reloadAllSettings(); @@ -1438,6 +1439,7 @@ However, your report is needed to stabilise this. I appreciate you for your grea .addApplyButton(["configPassphrase", "configPassphraseStore"]) .setClass("wizardHidden") + addScreenElement("20", containerGeneralSettingsEl); const containerSyncSettingEl = containerEl.createDiv(); this.createEl(containerSyncSettingEl, "h3", { text: "Sync Settings" }); @@ -1776,6 +1778,13 @@ However, your report is needed to stabilise this. I appreciate you for your grea new Setting(containerSyncSettingEl) .setClass("wizardHidden") .autoWireToggle("readChunksOnline", { onUpdate: onlyOnCouchDB }) + new Setting(containerSyncSettingEl) + .setClass("wizardHidden") + .autoWireToggle("sendChunksBulk", { onUpdate: onlyOnCouchDB }) + new Setting(containerSyncSettingEl) + .setClass("wizardHidden") + .autoWireNumeric("sendChunksBulkMaxSize", { clampMax: 100, clampMin: 1, onUpdate: onlyOnCouchDB }) + new Setting(containerSyncSettingEl) .setClass("wizardHidden") @@ -1916,7 +1925,8 @@ However, your report is needed to stabilise this. I appreciate you for your grea const endpointScheme = pluginConfig.endpoint.startsWith("http:") ? "(HTTP)" : (pluginConfig.endpoint.startsWith("https:")) ? "(HTTPS)" : ""; pluginConfig.endpoint = `${endpoint.indexOf(".r2.cloudflarestorage.") !== -1 ? "R2" : "self-hosted?"}(${endpointScheme})`; } - const obsidianInfo = navigator.userAgent; + const obsidianInfo = `Navigator: ${navigator.userAgent} +FileSystem: ${this.plugin.vaultAccess.isStorageInsensitive() ? "insensitive" : "sensitive"}`; const msgConfig = `---- Obsidian info ---- ${obsidianInfo} ---- remote config ---- @@ -1998,7 +2008,7 @@ ${stringifyYaml(pluginConfig)}`; if (fileOnDB) { el.appendChild(this.createEl(el, "button", { text: "Database -> Storage" }, buttonEl => { buttonEl.onClickEvent(() => { - this.plugin.pullFile(this.plugin.getPath(fileOnDB), [], true, undefined, false); + this.plugin.pullFile(this.plugin.getPath(fileOnDB), undefined, true, undefined, false); el.remove(); }) })) @@ -2017,6 +2027,18 @@ ${stringifyYaml(pluginConfig)}`; addResult(file.path, file, fileOnDB) } } + new Setting(containerHatchEl) + .setName("Recreate missing chunks for all files") + .setDesc("This will recreate chunks for all files. If there were missing chunks, this may fix the errors.") + .addButton((button) => + button. + setButtonText("Recreate all") + .setCta() + .onClick(async () => { + await this.plugin.createAllChunks(true); + }) + ) + new Setting(containerHatchEl) .setName("Verify and repair all files") .setDesc("Compare the content of files between on local database and storage. If not matched, you will be asked which one you want to keep.") @@ -2024,8 +2046,9 @@ ${stringifyYaml(pluginConfig)}`; button .setButtonText("Verify all") .setDisabled(false) - .setWarning() + .setCta() .onClick(async () => { + Logger("Start verifying all files", LOG_LEVEL_NOTICE, "verify"); const files = this.app.vault.getFiles(); const documents = [] as FilePathWithPrefix[]; @@ -2033,33 +2056,53 @@ ${stringifyYaml(pluginConfig)}`; for await (const i of adn) documents.push(this.plugin.getPath(i)); const allPaths = [...new Set([...documents, ...files.map(e => e.path as FilePathWithPrefix)])]; let i = 0; - for (const path of allPaths) { + const incProc = () => { i++; - Logger(`${i}/${files.length}\n${path}`, LOG_LEVEL_NOTICE, "verify"); - if (shouldBeIgnored(path)) continue; - const abstractFile = this.plugin.vaultAccess.getAbstractFileByPath(path); - const fileOnStorage = abstractFile instanceof TFile ? abstractFile : false; - if (!await this.plugin.isTargetFile(path)) continue; - - if (fileOnStorage && this.plugin.isFileSizeExceeded(fileOnStorage.stat.size)) continue; - const fileOnDB = await this.plugin.localDatabase.getDBEntry(path); - if (fileOnDB && this.plugin.isFileSizeExceeded(fileOnDB.size)) continue; - - if (!fileOnDB && fileOnStorage) { - Logger(`Compare: Not found on the local database: ${path}`, LOG_LEVEL_NOTICE); - addResult(path, fileOnStorage, false) - continue; - } - if (fileOnDB && !fileOnStorage) { - Logger(`Compare: Not found on the storage: ${path}`, LOG_LEVEL_NOTICE); - addResult(path, false, fileOnDB) - continue; - } - if (fileOnStorage && fileOnDB) { - await checkBetweenStorageAndDatabase(fileOnStorage, fileOnDB) - } + if (i % 25 == 0) Logger(`Checking ${i}/${files.length} files \n`, LOG_LEVEL_NOTICE, "verify-processed"); } + const semaphore = Semaphore(10); + const processes = allPaths.map(async path => { + try { + if (shouldBeIgnored(path)) { + return incProc(); + } + const abstractFile = this.plugin.vaultAccess.getAbstractFileByPath(path); + const fileOnStorage = abstractFile instanceof TFile ? abstractFile : false; + if (!await this.plugin.isTargetFile(path)) return incProc(); + const releaser = await semaphore.acquire(1) + if (fileOnStorage && this.plugin.isFileSizeExceeded(fileOnStorage.stat.size)) return incProc(); + try { + const fileOnDB = await this.plugin.localDatabase.getDBEntry(path); + if (fileOnDB && this.plugin.isFileSizeExceeded(fileOnDB.size)) return incProc(); + + if (!fileOnDB && fileOnStorage) { + Logger(`Compare: Not found on the local database: ${path}`, LOG_LEVEL_NOTICE); + addResult(path, fileOnStorage, false) + return incProc(); + } + if (fileOnDB && !fileOnStorage) { + Logger(`Compare: Not found on the storage: ${path}`, LOG_LEVEL_NOTICE); + addResult(path, false, fileOnDB) + return incProc(); + } + if (fileOnStorage && fileOnDB) { + await checkBetweenStorageAndDatabase(fileOnStorage, fileOnDB) + } + } catch (ex) { + Logger(`Error while processing ${path}`, LOG_LEVEL_NOTICE); + Logger(ex, LOG_LEVEL_VERBOSE); + } finally { + releaser(); + incProc(); + } + } catch (ex) { + Logger(`Error while processing without semaphore ${path}`, LOG_LEVEL_NOTICE); + Logger(ex, LOG_LEVEL_VERBOSE); + } + }); + await Promise.all(processes); Logger("done", LOG_LEVEL_NOTICE, "verify"); + // Logger(`${i}/${files.length}\n`, LOG_LEVEL_NOTICE, "verify-processed"); }) ); const resultArea = containerHatchEl.createDiv({ text: "" }); @@ -2181,6 +2224,34 @@ ${stringifyYaml(pluginConfig)}`; new Setting(containerHatchEl) .autoWireToggle("useIndexedDBAdapter", { invert: true }) + new Setting(containerHatchEl) + .autoWireToggle("doNotUseFixedRevisionForChunks", { holdValue: true }) + .setClass("wizardHidden") + new Setting(containerHatchEl) + .autoWireToggle("handleFilenameCaseSensitive", { holdValue: true }) + .setClass("wizardHidden") + + new Setting(containerHatchEl) + .setName("Apply") + .setDesc("These configurations require a database rebuild.") + .setClass("wizardHidden") + .addButton((button) => + button + .setButtonText("Apply and rebuild") + .setWarning() + .setDisabled(false) + .onClick(async () => { + await this.saveAllDirtySettings(); + // await this.applySetting(["doNotUseFixedRevisionForChunks", "handleFilenameCaseSensitive"]); + // await this.saveSettings(["doNotUseFixedRevisionForChunks", "handleFilenameCaseSensitive"]); + // debugger; + await rebuildDB("rebuildBothByThisDevice"); + }) + ) + .addOnUpdate(() => ({ + isCta: this.isSomeDirty(["doNotUseFixedRevisionForChunks", "handleFilenameCaseSensitive"]), + disabled: !this.isSomeDirty(["doNotUseFixedRevisionForChunks", "handleFilenameCaseSensitive"]), + })) this.addOnSaved("useIndexedDBAdapter", async () => { await this.saveAllDirtySettings(); await rebuildDB("localOnly"); @@ -2328,7 +2399,17 @@ ${stringifyYaml(pluginConfig)}`; await rebuildDB("remoteOnly"); }) ) - + .addButton((button) => + button + .setButtonText("Send chunks") + .setWarning() + .setDisabled(false) + .onClick(async () => { + if (this.plugin.replicator instanceof LiveSyncCouchDBReplicator) { + await this.plugin.replicator.sendChunks(this.plugin.settings, undefined, true, 0); + } + }) + ) new Setting(containerMaintenanceEl) .setName("Reset journal received history") .setDesc("Initialise journal received history. On the next sync, every item except this device sent will be downloaded again.") diff --git a/src/ui/settingConstants.ts b/src/ui/settingConstants.ts index 985bd88..0f6237d 100644 --- a/src/ui/settingConstants.ts +++ b/src/ui/settingConstants.ts @@ -328,7 +328,19 @@ export const SettingInformation: Partial .cm-editor::before { - right: 16px; -} - .sls-setting-tab { display: none; } @@ -171,8 +122,8 @@ div.sls-setting-menu-btn { /* width: 100%; */ } -.sls-setting-tab:hover ~ div.sls-setting-menu-btn, -.sls-setting-label.selected .sls-setting-tab:checked ~ div.sls-setting-menu-btn { +.sls-setting-tab:hover~div.sls-setting-menu-btn, +.sls-setting-label.selected .sls-setting-tab:checked~div.sls-setting-menu-btn { background-color: var(--interactive-accent); color: var(--text-on-accent); } @@ -291,7 +242,7 @@ div.sls-setting-menu-btn { display: none; } -.password-input > .setting-item-control > input { +.password-input>.setting-item-control>input { -webkit-text-security: disc; } @@ -321,6 +272,7 @@ span.ls-mark-cr::after { top: 0; left: 0; } + .ls-imgdiff-wrap .overlay .img-overlay { -webkit-filter: invert(100%) opacity(50%); filter: invert(100%) opacity(50%); @@ -329,14 +281,61 @@ span.ls-mark-cr::after { left: 0; animation: ls-blink-diff 0.5s cubic-bezier(0.4, 0, 1, 1) infinite alternate; } + @keyframes ls-blink-diff { 0% { opacity: 0; } + 50% { opacity: 0; } + 100% { opacity: 1; } } + + +.livesync-status { + user-select: none; + pointer-events: none; + height: auto; + min-height: 1em; + position: absolute; + background-color: transparent; + width: 100%; + padding: 10px; + padding-right: 16px; + top: var(--header-height); + z-index: calc(var(--layer-cover) + 1); + + font-variant-numeric: tabular-nums; + font-variant-emoji: emoji; + tab-size: 4; + text-align: right; + white-space: pre-wrap; + display: inline-block; + color: var(--text-normal); + font-size: 80%; +} + +.livesync-status div { + opacity: 0.6; + -webkit-filter: grayscale(100%); + filter: grayscale(100%); +} + +.livesync-status .livesync-status-loghistory { + text-align: left; + opacity: 0.4; + +} + +.livesync-status div.livesync-status-messagearea { + opacity: 0.6; + color: var(--text-on-accent); + background: var(--background-modifier-error); + -webkit-filter: unset; + filter: unset; +} \ No newline at end of file From 3285c1694bcd4d36ed2edcc9ded39546234c4796 Mon Sep 17 00:00:00 2001 From: vorotamoroz Date: Sat, 7 Sep 2024 01:45:12 +0900 Subject: [PATCH 07/12] bump --- manifest.json | 2 +- package-lock.json | 4 ++-- package.json | 2 +- updates.md | 37 +++++++++++++++++++++++++++++++++++++ 4 files changed, 41 insertions(+), 4 deletions(-) diff --git a/manifest.json b/manifest.json index f4ab502..d80053f 100644 --- a/manifest.json +++ b/manifest.json @@ -1,7 +1,7 @@ { "id": "obsidian-livesync", "name": "Self-hosted LiveSync", - "version": "0.23.20", + "version": "0.23.21", "minAppVersion": "0.9.12", "description": "Community implementation of self-hosted livesync. Reflect your vault changes to some other devices immediately. Please make sure to disable other synchronize solutions to avoid content corruption or duplication.", "author": "vorotamoroz", diff --git a/package-lock.json b/package-lock.json index 91afd53..4408507 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "obsidian-livesync", - "version": "0.23.20", + "version": "0.23.21", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "obsidian-livesync", - "version": "0.23.20", + "version": "0.23.21", "license": "MIT", "dependencies": { "@aws-sdk/client-s3": "^3.645.0", diff --git a/package.json b/package.json index 23c535d..2fb8712 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "obsidian-livesync", - "version": "0.23.20", + "version": "0.23.21", "description": "Reflect your vault changes to some other devices immediately. Please make sure to disable other synchronize solutions to avoid content corruption or duplication.", "main": "main.js", "type": "module", diff --git a/updates.md b/updates.md index 39fa331..dc74a3f 100644 --- a/updates.md +++ b/updates.md @@ -18,6 +18,43 @@ I have a lot of respect for that plugin, even though it is sometimes treated as Hooray for open source, and generous licences, and the sharing of knowledge by experts. #### Version history +- 0.23.21: + - New Features: + - Case-insensitive file handling + - Files can now be handled case-insensitively. + - This behaviour can be modified in the settings under `Handle files as Case-Sensitive` (Default: Prompt, Enabled for previous behaviour). + - Improved chunk revision fixing + - Revisions for chunks can now be fixed for faster chunk creation. + - This can be adjusted in the settings under `Compute revisions for chunks` (Default: Prompt, Enabled for previous behaviour). + - Bulk chunk transfer + - Chunks can now be transferred in bulk during uploads. + - This feature is enabled by default through `Send chunks in bulk`. + - Creation of missing chunks without + - Missing chunks can be created without storing notes, enhancing efficiency for first synchronisation or after prolonged periods without synchronisation. + - Improvements: + - File status scanning on the startup + - Quite significant performance improvements. + - No more missing scans of some files. + - Status in editor enhancements + - Significant performance improvements in the status display within the editor. + - Notifications for files that will not be synchronised will now be properly communicated. + - Encryption and Decryption + - These processes are now performed in background threads to ensure fast and stable transfers. + - Verify and repair all files + - Got faster through parallel checking. + - Migration on update + - Migration messages and wizards have become more helpful. + - Behavioural changes: + - Chunk size adjustments + - Large chunks will no longer be created for older, stable files, addressing storage consumption issues. + - Flag file automation + - Confirmation will be shown and we can cancel it. + - Fixed: + - Database File Scanning + - All files in the database will now be enumerated correctly. + - Miscellaneous + - Dependency updated. + - Now, tree shaking is left to terser, from esbuild. - 0.23.20: - Fixed: - Customisation Sync now checks the difference while storing or applying the configuration. From 7eae25edd04a468da5a45cf028a6fa912a99ad2a Mon Sep 17 00:00:00 2001 From: vorotamoroz Date: Sun, 8 Sep 2024 17:55:04 +0900 Subject: [PATCH 08/12] - Fixed: - Case-insensitive file handling - Full-lower-case files are no longer created during database checking. - Bulk chunk transfer - The default value will automatically adjust to an acceptable size when using IBM Cloudant. --- src/lib | 2 +- src/main.ts | 25 +++++++++++++++++++------ 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/src/lib b/src/lib index c9af24e..633af44 160000 --- a/src/lib +++ b/src/lib @@ -1 +1 @@ -Subproject commit c9af24ecd6457f23e8fd3c454b7621a70742399b +Subproject commit 633af447d24339718d0f6e16e950a124b46b3896 diff --git a/src/main.ts b/src/main.ts index 614f25f..f6457a1 100644 --- a/src/main.ts +++ b/src/main.ts @@ -688,6 +688,17 @@ ___However, to enable either of these changes, both remote and local databases n await this.performAppReload(); } + async migrateCheckBulkSize() { + if (this.settings.sendChunksBulk) { + if (this.settings.couchDB_URI != "" && isCloudantURI(this.settings.couchDB_URI)) { + if (this.settings.sendChunksBulkMaxSize > 1) { + Logger("Cloudant does not support bulk size more than 1MB, Automatically fixed", LOG_LEVEL_NOTICE); + this.settings.sendChunksBulkMaxSize = 1; + await this.saveSettings(); + } + } + } + } async migrationCheck() { const old = this.settings.settingVersion; const current = SETTING_VERSION_SUPPORT_CASE_INSENSITIVE; @@ -2687,25 +2698,27 @@ Or if you are sure know what had been happened, we can unlock the database from const file = storageFileNameMap[storageFileNameCI2CS[e]]; if (!this.isFileSizeExceeded(file.stat.size)) { await this.updateIntoDB(file); - fireAndForget(() => this.checkAndApplySettingFromMarkdown(e, true)); + const path = getPathFromTFile(file); + fireAndForget(() => this.checkAndApplySettingFromMarkdown(path, true)); } else { Logger(`UPDATE DATABASE: ${e} has been skipped due to file size exceeding the limit`, logLevel); } })); initProcess.push(runAll("UPDATE STORAGE", filesExistOnlyInDatabase, async (e) => { const w = databaseFileNameMap[databaseFileNameCI2CS[e]]; + const path = getPath(w) ?? e; if (w && !(w.deleted || w._deleted)) { if (!this.isFileSizeExceeded(w.size)) { - await this.pullFile(e, undefined, false, undefined, false); + await this.pullFile(path, undefined, false, undefined, false); fireAndForget(() => this.checkAndApplySettingFromMarkdown(e, true)); - Logger(`Check or pull from db:${e} OK`); + Logger(`Check or pull from db:${path} OK`); } else { - Logger(`UPDATE STORAGE: ${e} has been skipped due to file size exceeding the limit`, logLevel); + Logger(`UPDATE STORAGE: ${path} has been skipped due to file size exceeding the limit`, logLevel); } } else if (w) { - Logger(`Deletion history skipped: ${e}`, LOG_LEVEL_VERBOSE); + Logger(`Deletion history skipped: ${path}`, LOG_LEVEL_VERBOSE); } else { - Logger(`entry not found: ${e}`); + Logger(`entry not found: ${path}`); } })); From 48e4d5727800f3251a98beb19fc63c4350df73b7 Mon Sep 17 00:00:00 2001 From: vorotamoroz Date: Sun, 8 Sep 2024 17:58:10 +0900 Subject: [PATCH 09/12] bump --- manifest.json | 2 +- package-lock.json | 4 ++-- package.json | 2 +- updates.md | 6 ++++++ 4 files changed, 10 insertions(+), 4 deletions(-) diff --git a/manifest.json b/manifest.json index d80053f..62875c6 100644 --- a/manifest.json +++ b/manifest.json @@ -1,7 +1,7 @@ { "id": "obsidian-livesync", "name": "Self-hosted LiveSync", - "version": "0.23.21", + "version": "0.23.22", "minAppVersion": "0.9.12", "description": "Community implementation of self-hosted livesync. Reflect your vault changes to some other devices immediately. Please make sure to disable other synchronize solutions to avoid content corruption or duplication.", "author": "vorotamoroz", diff --git a/package-lock.json b/package-lock.json index 4408507..66ae060 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "obsidian-livesync", - "version": "0.23.21", + "version": "0.23.22", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "obsidian-livesync", - "version": "0.23.21", + "version": "0.23.22", "license": "MIT", "dependencies": { "@aws-sdk/client-s3": "^3.645.0", diff --git a/package.json b/package.json index 2fb8712..89d8be6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "obsidian-livesync", - "version": "0.23.21", + "version": "0.23.22", "description": "Reflect your vault changes to some other devices immediately. Please make sure to disable other synchronize solutions to avoid content corruption or duplication.", "main": "main.js", "type": "module", diff --git a/updates.md b/updates.md index dc74a3f..e4567cb 100644 --- a/updates.md +++ b/updates.md @@ -18,6 +18,12 @@ I have a lot of respect for that plugin, even though it is sometimes treated as Hooray for open source, and generous licences, and the sharing of knowledge by experts. #### Version history +- 0.23.22: + - Fixed: + - Case-insensitive file handling + - Full-lower-case files are no longer created during database checking. + - Bulk chunk transfer + - The default value will automatically adjust to an acceptable size when using IBM Cloudant. - 0.23.21: - New Features: - Case-insensitive file handling From b73ca73776636b8d1f19160ae5e649d561ba6a67 Mon Sep 17 00:00:00 2001 From: vorotamoroz Date: Tue, 24 Sep 2024 14:00:44 +0100 Subject: [PATCH 10/12] Refined: - Setting dialogue very slightly refined. - The hodgepodge inside the `Hatch` pane has been sorted into more explicit categorised panes. - Applying the settings will now be more informative. New features: - Word-segmented chunk building on users language. Fixed: - Sending chunks on `Send chunk in bulk` are now buffered to avoid the out-of-memory error. - `Send chunk in bulk` is back to default disabled. - Merging conflicts of JSON files are now works fine even if it contains `null`. Development: - Implemented the logic for automatically generating the stub of document for the setting dialogue. --- .eslintrc | 6 +- docs/settings.md | 845 ++++-- esbuild.config.mjs | 67 +- package-lock.json | 15 +- package.json | 2 +- src/common/utils.ts | 42 +- src/features/CmdStatusInsideEditor.ts | 8 +- src/lib | 2 +- src/main.ts | 61 +- src/ui/ObsidianLiveSyncSettingTab.ts | 4033 ++++++++++++------------- src/ui/components/LiveSyncSetting.ts | 339 +++ src/ui/settingConstants.ts | 17 +- styles.css | 90 +- terser.config.mjs | 61 + tsconfig.json | 1 + 15 files changed, 3274 insertions(+), 2315 deletions(-) create mode 100644 src/ui/components/LiveSyncSetting.ts create mode 100644 terser.config.mjs diff --git a/.eslintrc b/.eslintrc index 6a8172a..08e619d 100644 --- a/.eslintrc +++ b/.eslintrc @@ -23,12 +23,14 @@ "args": "none" } ], + "no-unused-labels": "off", "@typescript-eslint/ban-ts-comment": "off", "no-prototype-builtins": "off", "@typescript-eslint/no-empty-function": "off", "require-await": "warn", "no-async-promise-executor": "off", "@typescript-eslint/no-explicit-any": "off", - "@typescript-eslint/no-unnecessary-type-assertion": "error" + "@typescript-eslint/no-unnecessary-type-assertion": "error", + "no-constant-condition": ["error", { "checkLoops": false }] + } } -} \ No newline at end of file diff --git a/docs/settings.md b/docs/settings.md index cc15dd0..c065c2e 100644 --- a/docs/settings.md +++ b/docs/settings.md @@ -1,267 +1,750 @@ -NOTE: This document surely became outdated. I'll improve this doc in a while. but your contributions are always welcome. +NOTE: This document not completed. I'll improve this doc in a while. but your contributions are always welcome. -# Settings of this plugin +# Settings of Self-hosted LiveSync -The settings dialog has been quite long, so I split each configuration into tabs. -If you feel something, please feel free to inform me. +There are many settings in Self-hosted LiveSync. This document describes each setting in detail (not how-to). Configuration and settings are divided into several categories and indicated by icons. The icon is as follows: -| icon | description | -| :---: | ----------------------------------------------------------------- | -| 🛰️ | [Remote Database Configurations](#remote-database-configurations) | -| 📦 | [Local Database Configurations](#local-database-configurations) | -| ⚙️ | [General Settings](#general-settings) | -| 🔁 | [Sync Settings](#sync-settings) | -| 🔧 | [Miscellaneous](#miscellaneous) | -| 🧰 | [Hatch](#miscellaneous) | -| 🔌 | [Plugin and its settings](#plugin-and-its-settings) | -| 🚑 | [Corrupted data](#corrupted-data) | +| Icon | Description | +| :--: | ------------------------------------------------------------------ | +| 💬 | [0. Update Information](#0-update-information) | +| 🧙‍♂️ | [1. Setup](#1-setup) | +| ⚙️ | [2. General Settings](#2-general-settings) | +| 🛰️ | [3. Remote Configuration](#3-remote-configuration) | +| 🔄 | [4. Sync Settings](#4-sync-settings) | +| 🚦 | [5. Selector (Advanced)](#5-selector-advanced) | +| 🔌 | [6. Customization sync (Advanced)](#6-customization-sync-advanced) | +| 🧰 | [7. Hatch](#7-hatch) | +| 🔧 | [8. Advanced (Advanced)](#8-advanced-advanced) | +| 💪 | [9. Power users (Power User)](#9-power-users-power-user) | +| 🩹 | [10. Patches (Edge Case)](#10-patches-edge-case) | +| 🎛️ | [11. Maintenance](#11-maintenance) | -## Remote Database Configurations -Configure the settings of synchronize server. If any synchronization is enabled, you can't edit this section. Please disable all synchronization to change. +## 0. Update Information -### URI -URI of CouchDB. In the case of Cloudant, It's "External Endpoint(preferred)". -**Do not end it up with a slash** when it doesn't contain the database name. +This pane shows version up information. You can check what has been changed in recent versions. -### Username -Your CouchDB's Username. Administrator's privilege is preferred. +## 1. Setup -### Password -Your CouchDB's Password. -Note: This password is saved into your Obsidian's vault in plain text. +This pane is used for setting up Self-hosted LiveSync. There are several options to set up Self-hosted LiveSync. -### Database Name -The Database name to synchronize. -⚠️If not exist, created automatically. +### 1. Quick Setup +Most preferred method to setup Self-hosted LiveSync. You can setup Self-hosted LiveSync with a few clicks. -### End to End Encryption -Encrypt your database. It affects only the database, your files are left as plain. +#### Use the copied setup URI -The encryption algorithm is AES-GCM. +Setup the Self-hosted LiveSync with the `setup URI` which is [copied from another device](#copy-current-settings-as-a-new-setup-uri) or the setup script. -Note: If you want to use "Plugins and their settings", you have to enable this. +#### Minimal setup -### Passphrase -The passphrase to used as the key of encryption. Please use the long text. +Step-by-step setup for Self-hosted LiveSync. You can setup Self-hosted LiveSync manually with Minimal setting items. -### Apply -Set the End to End encryption enabled and its passphrase for use in replication. -If you change the passphrase of an existing database, overwriting the remote database is strongly recommended. +#### Enable LiveSync on this device as the setup was completed manually +This button only appears when the setup was not completed. If you have completed the setup manually, you can enable LiveSync on this device by this button. -### Overwrite remote database -Overwrite the remote database with the local database using the passphrase you applied. +### 2. To setup the other devices +#### Copy current settings as a new setup URI -### Rebuild -Rebuild remote and local databases with local files. It will delete all document history and retained chunks, and shrink the database. +You can copy the current settings as a new setup URI. And this URI can be used to setup the other devices as [Use the copied setup URI](#use-the-copied-setup-uri). -### Test Database connection -You can check the connection by clicking this button. +### 3. Reset -### Check database configuration -You can check and modify your CouchDB configuration from here directly. +#### Discard existing settings and databases -### Lock remote database. -Other devices are banned from the database when you have locked the database. -If you have something troubled with other devices, you can protect the vault and remote database with your device. +Reset the Self-hosted LiveSync settings and databases. +**Hazardous operation. Please be careful when using this.** -## Local Database Configurations -"Local Database" is created inside your obsidian. +### 4. Enable extra and advanced features -### Batch database update -Delay database update until raise replication, open another file, window visibility changes, or file events except for file modification. -This option can not be used with LiveSync at the same time. +To keep the set-up dialogue simple, some panes are hidden in default. You can enable them here. +#### Enable advanced features -### Fetch rebuilt DB. -If one device rebuilds or locks the remote database, every other device will be locked out from the remote database until it fetches rebuilt DB. +Setting key: useAdvancedMode -### minimum chunk size and LongLine threshold -The configuration of chunk splitting. +Following panes will be shown when you enable this setting. +| Icon | Description | +| :--: | ------------------------------------------------------------------ | +| 🚦 | [5. Selector (Advanced)](#5-selector-advanced) | +| 🔌 | [6. Customization sync (Advanced)](#6-customization-sync-advanced) | +| 🔧 | [8. Advanced (Advanced)](#8-advanced-advanced) | -Self-hosted LiveSync splits the note into chunks for efficient synchronization. This chunk should be longer than the "Minimum chunk size". +#### Enable power user features -Specifically, the length of the chunk is determined by the following orders. +Setting key: usePowerUserMode -1. Find the nearest newline character, and if it is farther than LongLineThreshold, this piece becomes an independent chunk. +Following panes will be shown when you enable this setting. +| Icon | Description | +| :--: | ------------------------------------------------------------------ | +| 💪 | [9. Power users (Power User)](#9-power-users-power-user) | -2. If not, find the nearest to these items. - 1. A newline character - 2. An empty line (Windows style) - 3. An empty line (non-Windows style) -3. Compare the farther in these 3 positions and the next "newline\]#" position, and pick a shorter piece as a chunk. +#### Enable edge case treatment features -This rule was made empirically from my dataset. If this rule acts as badly on your data. Please give me the information. +Setting key: useEdgeCaseMode -You can dump saved note structure to `Dump informations of this doc`. Replace every character with x except newline and "#" when sending information to me. +Following panes will be shown when you enable this setting. +| Icon | Description | +| :--: | ------------------------------------------------------------------ | +| 🩹 | [10. Patches (Edge Case)](#10-patches-edge-case) | -The default values are 20 letters and 250 letters. +## 2. General Settings -## General Settings +### 1. Appearance -### Do not show low-priority log -If you enable this option, log only the entries with the popup. +#### Display Language -### Verbose log +Setting key: displayLanguage -## Sync Settings +You can change the display language. It is independent of the system language and/or Obsidian's language. +Note: Not all messages have been translated. And, please revert to "Default" when reporting errors. Of course, your contribution to translation is always welcome! -### LiveSync -Do LiveSync. +#### Show status inside the editor -It is the one of raison d'être of this plugin. +Setting key: showStatusOnEditor -Useful, but this method drains many batteries on the mobile and uses not the ignorable amount of data transfer. +We can show the status of synchronisation inside the editor. -This method is exclusive to other synchronization methods. +Reflected after reboot -### Periodic Sync -Synchronize periodically. +#### Show status as icons only -### Periodic Sync Interval -Unit is seconds. +Setting key: showOnlyIconsOnEditor -### Sync on Save -Synchronize when the note has been modified or created. +Show status as icons only. This is useful when you want to save space on the status bar. -### Sync on File Open -Synchronize when the note is opened. +#### Show status on the status bar -### Sync on Start -Synchronize when Obsidian started. +Setting key: showStatusOnStatusbar -### Use Trash for deleted files -When the file has been deleted on remote devices, deletion will be replicated to the local device and the file will be deleted. +We can show the status of synchronisation on the status bar. (Default: On) -If this option is enabled, move deleted files into the trash instead delete actually. +### 2. Logging -### Do not delete empty folder -Self-hosted LiveSync will delete the folder when the folder becomes empty. If this option is enabled, leave it as an empty folder. +#### Show only notifications -### Use newer file if conflicted (beta) -Always use the newer file to resolve and overwrite when conflict has occurred. +Setting key: lessInformationInLog +Prevent logging and show only notification. Please disable when you report the logs -### Experimental. -### Sync hidden files +#### Verbose Log -Synchronize hidden files. +Setting key: showVerboseLog -- Scan hidden files before replication. -If you enable this option, all hidden files are scanned once before replication. +Show verbose log. Please enable when you report the logs -- Scan hidden files periodicaly. -If you enable this option, all hidden files will be scanned each [n] seconds. +## 3. Remote Configuration -Hidden files are not actively detected, so we need scanning. +### 1. Remote Server -Each scan stores the file with their modification time. And if the file has been disappeared, the fact is also stored. Then, When the entry of the hidden file has been replicated, it will be reflected in the storage if the entry is newer than storage. +#### Remote Type -Therefore, the clock must be adjusted. If the modification time is determined to be older, the changeset will be skipped or cancelled (It means, **deleted**), even if the file spawned in a hidden folder. +Setting key: remoteType -### Advanced settings -Self-hosted LiveSync using PouchDB and synchronizes with the remote by [this protocol](https://docs.couchdb.org/en/stable/replication/protocol.html). -So, it splits every entry into chunks to be acceptable by the database with limited payload size and document size. +Remote server type -However, it was not enough. -According to [2.4.2.5.2. Upload Batch of Changed Documents](https://docs.couchdb.org/en/stable/replication/protocol.html#upload-batch-of-changed-documents) in [Replicate Changes](https://docs.couchdb.org/en/stable/replication/protocol.html#replicate-changes), it might become a bigger request. +### 2. Notification -Unfortunately, there is no way to deal with this automatically by size for every request. -Therefore, I made it possible to configure this. +#### Notify when the estimated remote storage size exceeds on start up -Note: If you set these values lower number, the number of requests will increase. -Therefore, if you are far from the server, the total throughput will be low, and the traffic will increase. +Setting key: notifyThresholdOfRemoteStorageSize -### Batch size -Number of change feed items to process at a time. Defaults to 250. +MB (0 to disable). We can get a notification when the estimated remote storage size exceeds this value. -### Batch limit -Number of batches to process at a time. Defaults to 40. This along with batch size controls how many docs are kept in memory at a time. +### 3. Confidentiality -## Miscellaneous +#### End-to-End Encryption -### Show status inside editor -Show information inside the editor pane. -It would be useful for mobile. +Setting key: encrypt -### Check integrity on saving -Check all chunks are correctly saved on saving. +Enable end-to-end encryption. enabling this is recommend. If you change the passphrase, you need to rebuild databases (You will be informed). -### Presets -You can set synchronization method at once as these pattern: -- LiveSync - - LiveSync : enabled - - Batch database update : disabled - - Periodic Sync : disabled - - Sync on Save : disabled - - Sync on File Open : disabled - - Sync on Start : disabled -- Periodic w/ batch - - LiveSync : disabled - - Batch database update : enabled - - Periodic Sync : enabled - - Sync on Save : disabled - - Sync on File Open : enabled - - Sync on Start : enabled -- Disable all sync - - LiveSync : disabled - - Batch database update : disabled - - Periodic Sync : disabled - - Sync on Save : disabled - - Sync on File Open : disabled - - Sync on Start : disabled +#### Passphrase +Setting key: passphrase -## Hatch -From here, everything is under the hood. Please handle it with care. +Encrypting passphrase. If you change the passphrase, you need to rebuild databases (You will be informed). -When there are problems with synchronization, the warning message is shown Under this section header. +#### Path Obfuscation -- Pattern 1 -![CorruptedData](../images/lock_pattern1.png) -This message is shown when the remote database is locked and your device is not marked as "resolved". -Almost it is happened by enabling End-to-End encryption or History has been dropped. -If you enabled End-to-End encryption, you can unlock the remote database by "Apply and receive" automatically. Or "Drop and receive" when you dropped. If you want to unlock manually, click "mark this device as resolved". +Setting key: usePathObfuscation -- Pattern 2 -![CorruptedData](../images/lock_pattern2.png) -The remote database indicates that has been unlocked Pattern 1. -When you mark all devices as resolved, you can unlock the database. -But, there's no problem even if you leave it as it is. +In default, the path of the file is not obfuscated to improve the performance. If you enable this, the path of the file will be obfuscated. This is useful when you want to hide the path of the file. -### Verify and repair all files -read all files in the vault, and update them into the database if there's diff or could not read from the database. +#### Use dynamic iteration count (Experimental) -### Suspend file watching -If enable this option, Self-hosted LiveSync dismisses every file change or deletes the event. +Setting key: useDynamicIterationCount -From here, these commands are used inside applying encryption passphrases or dropping histories. +This is an experimental feature and not recommended. If you enable this, the iteration count of the encryption will be dynamically determined. This is useful when you want to improve the performance. -Usually, doesn't use it so much. But sometimes it could be handy. +--- -## Plugins and settings (beta) +**now writing from here onwards, sorry** -### Enable plugin synchronization -If you want to use this feature, you have to activate this feature by this switch. +--- -### Sweep plugins automatically -Plugin sweep will run before replication automatically. +### 4. Minio,S3,R2 -### Sweep plugins periodically -Plugin sweep will run each 1 minute. +#### Endpoint URL -### Notify updates -When replication is complete, a message will be notified if a newer version of the plugin applied to this device is configured on another device. +Setting key: endpoint -### Device and Vault name -To save the plugins, you have to set a unique name every each device. +#### Access Key -### Open -Open the "Plugins and their settings" dialog. +Setting key: accessKey -### Corrupted or missing data -![CorruptedData](../images/corrupted_data.png) +#### Secret Key -When Self-hosted LiveSync could not write to the file on the storage, the files are shown here. If you have the old data in your vault, change it once, it will be cured. Or you can use the "File History" plugin. +Setting key: secretKey + +#### Region + +Setting key: region + +#### Bucket Name + +Setting key: bucket + +#### Use Custom HTTP Handler + +Setting key: useCustomRequestHandler +If your Object Storage could not configured accepting CORS, enable this. + +#### Test Connection + +#### Apply Settings + +### 5. CouchDB + +#### URI + +Setting key: couchDB_URI + +#### Username + +Setting key: couchDB_USER +username + +#### Password + +Setting key: couchDB_PASSWORD +password + +#### Database name + +Setting key: couchDB_DBNAME + +#### Test Database Connection + +Open database connection. If the remote database is not found and you have the privilege to create a database, the database will be created. + +#### Check and fix database configuration + +Check the database configuration, and fix if there are any problems. + +#### Apply Settings + +## 4. Sync Settings + +### 1. Synchronization Preset + +#### Presets + +Setting key: preset +Apply preset configuration + +### 2. Synchronization Methods + +#### Sync Mode + +Setting key: syncMode + +#### Periodic Sync interval + +Setting key: periodicReplicationInterval +Interval (sec) + +#### Sync on Save + +Setting key: syncOnSave +When you save a file, sync automatically + +#### Sync on Editor Save + +Setting key: syncOnEditorSave +When you save a file in the editor, sync automatically + +#### Sync on File Open + +Setting key: syncOnFileOpen +When you open a file, sync automatically + +#### Sync on Start + +Setting key: syncOnStart +Start synchronization after launching Obsidian. + +#### Sync after merging file + +Setting key: syncAfterMerge +Sync automatically after merging files + +### 3. Update thinning + +#### Batch database update + +Setting key: batchSave +Reducing the frequency with which on-disk changes are reflected into the DB + +#### Minimum delay for batch database updating + +Setting key: batchSaveMinimumDelay +Seconds. Saving to the local database will be delayed until this value after we stop typing or saving. + +#### Maximum delay for batch database updating + +Setting key: batchSaveMaximumDelay +Saving will be performed forcefully after this number of seconds. + +### 4. Deletion Propagation (Advanced) + +#### Use the trash bin + +Setting key: trashInsteadDelete +Do not delete files that are deleted in remote, just move to trash. + +#### Keep empty folder + +Setting key: doNotDeleteFolder +Normally, a folder is deleted when it becomes empty after a synchronization. Enabling this will prevent it from getting deleted + +### 5. Conflict resolution (Advanced) + +#### Always overwrite with a newer file (beta) + +Setting key: resolveConflictsByNewerFile +(Def off) Resolve conflicts by newer files automatically. + +#### Postpone resolution of inactive files + +Setting key: checkConflictOnlyOnOpen + +#### Postpone manual resolution of inactive files + +Setting key: showMergeDialogOnlyOnActive + +### 6. Sync settings via markdown (Advanced) + +#### Filename + +Setting key: settingSyncFile +If you set this, all settings are saved in a markdown file. You will be notified when new settings arrive. You can set different files by the platform. + +#### Write credentials in the file + +Setting key: writeCredentialsForSettingSync +(Not recommended) If set, credentials will be stored in the file. + +#### Notify all setting files + +Setting key: notifyAllSettingSyncFile + +### 7. Hidden files (Advanced) + +#### Hidden file synchronization + +#### Enable Hidden files sync + +#### Scan for hidden files before replication + +Setting key: syncInternalFilesBeforeReplication + +#### Scan hidden files periodically + +Setting key: syncInternalFilesInterval +Seconds, 0 to disable + +## 5. Selector (Advanced) + +### 1. Normal Files + +#### Synchronising files + +(RegExp) Empty to sync all files. Set filter as a regular expression to limit synchronising files. + +#### Non-Synchronising files + +(RegExp) If this is set, any changes to local and remote files that match this will be skipped. + +#### Maximum file size + +Setting key: syncMaxSizeInMB +(MB) If this is set, changes to local and remote files that are larger than this will be skipped. If the file becomes smaller again, a newer one will be used. + +#### (Beta) Use ignore files + +Setting key: useIgnoreFiles +If this is set, changes to local files which are matched by the ignore files will be skipped. Remote changes are determined using local ignore files. + +#### Ignore files + +Setting key: ignoreFiles +We can use multiple ignore files, e.g.) `.gitignore, .dockerignore` + +### 2. Hidden Files (Advanced) + +#### Ignore patterns + +#### Add default patterns + +## 6. Customization sync (Advanced) + +### 1. Customization Sync + +#### Device name + +Setting key: deviceAndVaultName +Unique name between all synchronized devices. To edit this setting, please disable customization sync once. + +#### Per-file-saved customization sync + +Setting key: usePluginSyncV2 +If enabled per-filed efficient customization sync will be used. We need a small migration when enabling this. And all devices should be updated to v0.23.18. Once we enabled this, we lost a compatibility with old versions. + +#### Enable customization sync + +Setting key: usePluginSync + +#### Scan customization automatically + +Setting key: autoSweepPlugins +Scan customization before replicating. + +#### Scan customization periodically + +Setting key: autoSweepPluginsPeriodic +Scan customization every 1 minute. + +#### Notify customized + +Setting key: notifyPluginOrSettingUpdated +Notify when other device has newly customized. + +#### Open + +Open the dialog + +## 7. Hatch + +### 1. Reporting Issue + +#### Make report to inform the issue + +#### Write logs into the file + +Setting key: writeLogToTheFile +Warning! This will have a serious impact on performance. And the logs will not be synchronised under the default name. Please be careful with logs; they often contain your confidential information. + +### 2. Scram Switches + +#### Suspend file watching + +Setting key: suspendFileWatching +Stop watching for file change. + +#### Suspend database reflecting + +Setting key: suspendParseReplicationResult +Stop reflecting database changes to storage files. + +### 3. Recovery and Repair + +#### Recreate missing chunks for all files + +This will recreate chunks for all files. If there were missing chunks, this may fix the errors. + +#### Verify and repair all files + +Compare the content of files between on local database and storage. If not matched, you will be asked which one you want to keep. + +#### Check and convert non-path-obfuscated files + +### 4. Reset + +#### Back to non-configured + +#### Delete all customization sync data + +## 8. Advanced (Advanced) + +### 1. Memory cache + +#### Memory cache size (by total items) + +Setting key: hashCacheMaxCount + +#### Memory cache size (by total characters) + +Setting key: hashCacheMaxAmount +(Mega chars) + +### 2. Local Database Tweak + +#### Enhance chunk size + +Setting key: customChunkSize + +#### Use splitting-limit-capped chunk splitter + +Setting key: enableChunkSplitterV2 +If enabled, chunks will be split into no more than 100 items. However, dedupe is slightly weaker. + +#### Use Segmented-splitter + +Setting key: useSegmenter +If this enabled, chunks will be split into semantically meaningful segments. Not all platforms support this feature. + +### 3. Transfer Tweak + +#### Fetch chunks on demand + +Setting key: readChunksOnline +(ex. Read chunks online) If this option is enabled, LiveSync reads chunks online directly instead of replicating them locally. Increasing Custom chunk size is recommended. + +#### Batch size of on-demand fetching + +Setting key: concurrencyOfReadChunksOnline + +#### The delay for consecutive on-demand fetches + +Setting key: minimumIntervalOfReadChunksOnline + +#### Send chunks in bulk + +Setting key: sendChunksBulk +If this enabled, all chunks will be sent in bulk. This is useful for the environment that has a high latency. + +#### Maximum size of chunks to send in one request + +Setting key: sendChunksBulkMaxSize +MB + +## 9. Power users (Power User) + +### 1. Remote Database Tweak + +#### Incubate Chunks in Document (Beta) + +Setting key: useEden +If enabled, newly created chunks are temporarily kept within the document, and graduated to become independent chunks once stabilised. + +#### Maximum Incubating Chunks + +Setting key: maxChunksInEden +The maximum number of chunks that can be incubated within the document. Chunks exceeding this number will immediately graduate to independent chunks. + +#### Maximum Incubating Chunk Size + +Setting key: maxTotalLengthInEden +The maximum total size of chunks that can be incubated within the document. Chunks exceeding this size will immediately graduate to independent chunks. + +#### Maximum Incubation Period + +Setting key: maxAgeInEden +The maximum duration for which chunks can be incubated within the document. Chunks exceeding this period will graduate to independent chunks. + +#### Data Compression (Experimental) + +Setting key: enableCompression + +### 2. CouchDB Connection Tweak + +#### Batch size + +Setting key: batch_size +Number of change feed items to process at a time. Defaults to 50. Minimum is 2. + +#### Batch limit + +Setting key: batches_limit +Number of batches to process at a time. Defaults to 40. Minimum is 2. This along with batch size controls how many docs are kept in memory at a time. + +#### Use timeouts instead of heartbeats + +Setting key: useTimeouts +If this option is enabled, PouchDB will hold the connection open for 60 seconds, and if no change arrives in that time, close and reopen the socket, instead of holding it open indefinitely. Useful when a proxy limits request duration but can increase resource usage. + +### 3. Configuration Encryption + +#### Encrypting sensitive configuration items + +Setting key: configPassphraseStore + +#### Passphrase of sensitive configuration items + +Setting key: configPassphrase +This passphrase will not be copied to another device. It will be set to `Default` until you configure it again. + +## 10. Patches (Edge Case) + +### 1. Compatibility (Metadata) + +#### Do not keep metadata of deleted files. + +Setting key: deleteMetadataOfDeletedFiles + +#### Delete old metadata of deleted files on start-up + +Setting key: automaticallyDeleteMetadataOfDeletedFiles +(Days passed, 0 to disable automatic-deletion) + +### 2. Compatibility (Conflict Behaviour) + +#### Always resolve conflicts manually + +Setting key: disableMarkdownAutoMerge +If this switch is turned on, a merge dialog will be displayed, even if the sensible-merge is possible automatically. (Turn on to previous behavior) + +#### Always reflect synchronized changes even if the note has a conflict + +Setting key: writeDocumentsIfConflicted +Turn on to previous behavior + +### 3. Compatibility (Database structure) + +#### (Obsolete) Use an old adapter for compatibility (obsolete) + +Setting key: useIndexedDBAdapter +Before v0.17.16, we used an old adapter for the local database. Now the new adapter is preferred. However, it needs local database rebuilding. Please disable this toggle when you have enough time. If leave it enabled, also while fetching from the remote database, you will be asked to disable this. + +#### Compute revisions for chunks (Previous behaviour) + +Setting key: doNotUseFixedRevisionForChunks +If this enabled, all chunks will be stored with the revision made from its content. (Previous behaviour) + +#### Handle files as Case-Sensitive + +Setting key: handleFilenameCaseSensitive +If this enabled, All files are handled as case-Sensitive (Previous behaviour). + +### 4. Compatibility (Internal API Usage) + +#### Scan changes on customization sync + +Setting key: watchInternalFileChanges +Do not use internal API + +### 5. Edge case addressing (Database) + +#### Database suffix + +Setting key: additionalSuffixOfDatabaseName +LiveSync could not handle multiple vaults which have same name without different prefix, This should be automatically configured. + +#### The Hash algorithm for chunk IDs (Experimental) + +Setting key: hashAlg + +### 6. Edge case addressing (Behaviour) + +#### Fetch database with previous behaviour + +Setting key: doNotSuspendOnFetching + +#### Keep empty folder + +Setting key: doNotDeleteFolder +Normally, a folder is deleted when it becomes empty after a synchronization. Enabling this will prevent it from getting deleted + +### 7. Edge case addressing (Processing) + +#### Do not split chunks in the background + +Setting key: disableWorkerForGeneratingChunks +If disabled(toggled), chunks will be split on the UI thread (Previous behaviour). + +#### Process small files in the foreground + +Setting key: processSmallFilesInUIThread +If enabled, the file under 1kb will be processed in the UI thread. + +### 8. Compatibility (Trouble addressed) + +#### Do not check configuration mismatch before replication + +Setting key: disableCheckingConfigMismatch + +## 11. Maintenance + +### 1. Scram! + +#### Lock remote + +Lock remote to prevent synchronization with other devices. + +#### Emergency restart + +place the flag file to prevent all operation and restart. + +### 2. Data-complementary Operations + +#### Resend + +Resend all chunks to the remote. + +#### Reset journal received history + +Initialise journal received history. On the next sync, every item except this device sent will be downloaded again. + +#### Reset journal sent history + +Initialise journal sent history. On the next sync, every item except this device received will be sent again. + +### 3. Rebuilding Operations (Local) + +#### Fetch from remote + +Restore or reconstruct local database from remote. + +#### Fetch rebuilt DB (Save local documents before) + +Restore or reconstruct local database from remote database but use local chunks. + +### 4. Total Overhaul + +#### Rebuild everything + +Rebuild local and remote database with local files. + +### 5. Rebuilding Operations (Remote Only) + +#### Perform compaction + +Compaction discards all of Eden in the non-latest revisions, reducing the storage usage. However, this operation requires the same free space on the remote as the current database. + +#### Overwrite remote + +Overwrite remote with local DB and passphrase. + +#### Reset all journal counter + +Initialise all journal history, On the next sync, every item will be received and sent. + +#### Purge all journal counter + +Purge all sending and downloading cache. + +#### Make empty the bucket + +Delete all data on the remote. + +### 6. Niches + +#### (Obsolete) Clean up databases + +Delete unused chunks to shrink the database. However, this feature could be not effective in some cases. Please use rebuild everything instead. + +### 7. Reset + +#### Discard local database to reset or uninstall Self-hosted LiveSync diff --git a/esbuild.config.mjs b/esbuild.config.mjs index 2824175..1bfcd9e 100644 --- a/esbuild.config.mjs +++ b/esbuild.config.mjs @@ -9,67 +9,10 @@ import fs from "node:fs"; // import terser from "terser"; import { minify } from "terser"; import inlineWorkerPlugin from "esbuild-plugin-inline-worker"; -const banner = `/* -THIS IS A GENERATED/BUNDLED FILE BY ESBUILD AND TERSER -if you want to view the source, please visit the github repository of this plugin -*/ -`; +import { terserOption } from "./terser.config.mjs"; const prod = process.argv[2] === "production"; -const dev = process.argv[2] === "dev"; - -const keepTest = !prod || dev; - -const terserOpt = { - sourceMap: !prod - ? { - url: "inline", - } - : {}, - format: { - indent_level: 2, - beautify: true, - comments: "some", - ecma: 2018, - preamble: banner, - webkit: true, - }, - parse: { - // parse options - }, - compress: { - // compress options - defaults: false, - evaluate: true, - dead_code: true, - inline: 3, - join_vars: true, - loops: true, - passes: prod ? 4 : 1, - reduce_vars: true, - reduce_funcs: true, - arrows: true, - collapse_vars: true, - comparisons: true, - lhs_constants: true, - hoist_props: true, - side_effects: true, - if_return: true, - ecma: 2018, - unused: true, - }, - // mangle: false, - - ecma: 2018, // specify one of: 5, 2015, 2016, etc. - enclose: false, // or specify true, or "args:values" - keep_classnames: true, - keep_fnames: true, - ie8: false, - module: false, - // nameCache: null, // or specify a name cache object - safari10: false, - toplevel: false, -}; +const keepTest = !prod; const manifestJson = JSON.parse(fs.readFileSync("./manifest.json") + ""); const packageJson = JSON.parse(fs.readFileSync("./package.json") + ""); @@ -91,7 +34,7 @@ const plugins = [ console.log("Performing terser"); const src = fs.readFileSync("./main_org.js").toString(); // @ts-ignore - const ret = await minify(src, terserOpt); + const ret = await minify(src, terserOption); if (ret && ret.code) { fs.writeFileSync("./main.js", ret.code); } @@ -107,7 +50,7 @@ const plugins = [ const externals = ["obsidian", "electron", "crypto", "@codemirror/autocomplete", "@codemirror/collab", "@codemirror/commands", "@codemirror/language", "@codemirror/lint", "@codemirror/search", "@codemirror/state", "@codemirror/view", "@lezer/common", "@lezer/highlight", "@lezer/lr"]; const context = await esbuild.context({ banner: { - js: banner, + js: "// Leave it all to terser", }, entryPoints: ["src/main.ts"], bundle: true, @@ -146,7 +89,7 @@ const context = await esbuild.context({ ], }); -if (prod || dev) { +if (prod) { await context.rebuild(); process.exit(0); } else { diff --git a/package-lock.json b/package-lock.json index 66ae060..11113e1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,7 +18,7 @@ "fflate": "^0.8.2", "idb": "^8.0.0", "minimatch": "^10.0.1", - "octagonal-wheels": "^0.1.14", + "octagonal-wheels": "^0.1.15", "xxhash-wasm": "0.4.2", "xxhash-wasm-102": "npm:xxhash-wasm@^1.0.2" }, @@ -5099,10 +5099,9 @@ } }, "node_modules/octagonal-wheels": { - "version": "0.1.14", - "resolved": "https://registry.npmjs.org/octagonal-wheels/-/octagonal-wheels-0.1.14.tgz", - "integrity": "sha512-W0DQL5YNL7oJH2Dcb7mOE/P7MNrM7PXWJZHJpPGzbrKHAT14OtklbBFNJvM26v8nizwlb5WHQA+W/Tg1CIDSGQ==", - "license": "MIT", + "version": "0.1.15", + "resolved": "https://registry.npmjs.org/octagonal-wheels/-/octagonal-wheels-0.1.15.tgz", + "integrity": "sha512-rl6y/3/+tqeiDmBmW2RgYQh85D4xVHQIGg14DyC4/sUnO6UDLuVUULIO+00E2jfA/o4coKnPOPEB8Tt6BE9SEA==", "dependencies": { "idb": "^8.0.0", "xxhash-wasm": "0.4.2", @@ -10234,9 +10233,9 @@ } }, "octagonal-wheels": { - "version": "0.1.14", - "resolved": "https://registry.npmjs.org/octagonal-wheels/-/octagonal-wheels-0.1.14.tgz", - "integrity": "sha512-W0DQL5YNL7oJH2Dcb7mOE/P7MNrM7PXWJZHJpPGzbrKHAT14OtklbBFNJvM26v8nizwlb5WHQA+W/Tg1CIDSGQ==", + "version": "0.1.15", + "resolved": "https://registry.npmjs.org/octagonal-wheels/-/octagonal-wheels-0.1.15.tgz", + "integrity": "sha512-rl6y/3/+tqeiDmBmW2RgYQh85D4xVHQIGg14DyC4/sUnO6UDLuVUULIO+00E2jfA/o4coKnPOPEB8Tt6BE9SEA==", "requires": { "idb": "^8.0.0", "xxhash-wasm": "0.4.2", diff --git a/package.json b/package.json index 89d8be6..3a7d0e2 100644 --- a/package.json +++ b/package.json @@ -65,7 +65,7 @@ "fflate": "^0.8.2", "idb": "^8.0.0", "minimatch": "^10.0.1", - "octagonal-wheels": "^0.1.14", + "octagonal-wheels": "^0.1.15", "xxhash-wasm": "0.4.2", "xxhash-wasm-102": "npm:xxhash-wasm@^1.0.2" } diff --git a/src/common/utils.ts b/src/common/utils.ts index 36de1c1..38cc920 100644 --- a/src/common/utils.ts +++ b/src/common/utils.ts @@ -134,7 +134,13 @@ export function generatePatchObj(from: Record, to //if type is not match, replace completely. ret[key] = { [MARK_SWAPPED]: value }; } else { - if (typeof (v) == "object" && typeof (value) == "object" && !Array.isArray(v) && !Array.isArray(value)) { + if (v === null && value === null) { + // NO OP. + } else if (v === null && value !== null) { + ret[key] = { [MARK_SWAPPED]: value }; + } else if (v !== null && value === null) { + ret[key] = { [MARK_SWAPPED]: value }; + } else if (typeof (v) == "object" && typeof (value) == "object" && !Array.isArray(v) && !Array.isArray(value)) { const wk = generatePatchObj(v, value); if (Object.keys(wk).length > 0) ret[key] = wk; } else if (typeof (v) == "object" && typeof (value) == "object" && Array.isArray(v) && Array.isArray(value)) { @@ -169,6 +175,10 @@ export function applyPatch(from: Record, patch: R delete ret[key]; continue; } + if (value === null) { + ret[key] = null; + continue; + } if (typeof (value) == "object") { if (MARK_SWAPPED in value) { ret[key] = value[MARK_SWAPPED]; @@ -251,6 +261,7 @@ export function mergeObject( export function flattenObject(obj: Record, path: string[] = []): [string, any][] { if (typeof (obj) != "object") return [[path.join("."), obj]]; + if (obj === null) return [[path.join("."), null]]; if (Array.isArray(obj)) return [[path.join("."), JSON.stringify(obj)]]; const e = Object.entries(obj); const ret = [] @@ -489,6 +500,35 @@ export function useMemo({ key, forceUpdate, validator }: MemoOption, updateFu return value; } +// const _static = new Map(); +const _staticObj = new Map(); + +export function useStatic(key: string): { value: (T | undefined) }; +export function useStatic(key: string, initial: T): { value: T }; +export function useStatic(key: string, initial?: T) { + // if (!_static.has(key) && initial) { + // _static.set(key, initial); + // } + const obj = _staticObj.get(key); + if (obj !== undefined) { + return obj; + } else { + // let buf = initial; + const obj = { + _buf: initial, + get value() { + return this._buf as T; + }, + set value(value: T) { + this._buf = value + } + } + _staticObj.set(key, obj); + return obj; + } +} export function disposeMemo(key: string) { _cached.delete(key); } diff --git a/src/features/CmdStatusInsideEditor.ts b/src/features/CmdStatusInsideEditor.ts index bf9980e..978ffeb 100644 --- a/src/features/CmdStatusInsideEditor.ts +++ b/src/features/CmdStatusInsideEditor.ts @@ -129,7 +129,7 @@ export class LogAddOn extends LiveSyncCommands { this.statusBarLabels = statusBarLabels; const applyToDisplay = throttle((label: typeof statusBarLabels.value) => { - const v = label; + // const v = label; this.applyStatusBarText(); }, 20); @@ -161,10 +161,10 @@ export class LogAddOn extends LiveSyncCommands { } onload(): void | Promise { - eventHub.on(EVENT_FILE_RENAMED, (evt: CustomEvent<{ oldPath: string, newPath: string }>) => { + eventHub.onEvent(EVENT_FILE_RENAMED, (evt: CustomEvent<{ oldPath: string, newPath: string }>) => { this.setFileStatus(); }); - eventHub.on(EVENT_LEAF_ACTIVE_CHANGED, () => this.onActiveLeafChange()); + eventHub.onEvent(EVENT_LEAF_ACTIVE_CHANGED, () => this.onActiveLeafChange()); const w = document.querySelectorAll(`.livesync-status`); w.forEach(e => e.remove()); @@ -175,7 +175,7 @@ export class LogAddOn extends LiveSyncCommands { this.messageArea = this.statusDiv.createDiv({ cls: "livesync-status-messagearea" }); this.logMessage = this.statusDiv.createDiv({ cls: "livesync-status-logmessage" }); this.logHistory = this.statusDiv.createDiv({ cls: "livesync-status-loghistory" }); - eventHub.on(EVENT_LAYOUT_READY, () => this.adjustStatusDivPosition()); + eventHub.onEvent(EVENT_LAYOUT_READY, () => this.adjustStatusDivPosition()); if (this.settings.showStatusOnStatusbar) { this.statusBar = this.plugin.addStatusBarItem(); this.statusBar.addClass("syncstatusbar"); diff --git a/src/lib b/src/lib index 633af44..3108e3e 160000 --- a/src/lib +++ b/src/lib @@ -1 +1 @@ -Subproject commit 633af447d24339718d0f6e16e950a124b46b3896 +Subproject commit 3108e3e3dba518d4dd85c18982bb2bab7ecee9ee diff --git a/src/main.ts b/src/main.ts index f6457a1..72e08df 100644 --- a/src/main.ts +++ b/src/main.ts @@ -46,7 +46,7 @@ import { LogAddOn } from "./features/CmdStatusInsideEditor.ts"; import { eventHub } from "./lib/src/hub/hub.ts"; import { EVENT_FILE_RENAMED, EVENT_LAYOUT_READY, EVENT_LEAF_ACTIVE_CHANGED, EVENT_PLUGIN_LOADED, EVENT_PLUGIN_UNLOADED, EVENT_SETTING_SAVED } from "./common/events.ts"; import { Semaphore } from "octagonal-wheels/concurrency/semaphore"; - +import { yieldMicrotask } from "octagonal-wheels/promises"; setNoticeClass(Notice); @@ -354,10 +354,10 @@ export default class ObsidianLiveSyncPlugin extends Plugin return new PouchDB(name, optionPass); } beforeOnUnload(db: LiveSyncLocalDB): void { - this.kvDB.close(); + if (this.kvDB) this.kvDB.close(); } onClose(db: LiveSyncLocalDB): void { - this.kvDB.close(); + if (this.kvDB) this.kvDB.close(); } getNewReplicator(settingOverride: Partial = {}): LiveSyncAbstractReplicator { const settings = { ...this.settings, ...settingOverride }; @@ -367,8 +367,8 @@ export default class ObsidianLiveSyncPlugin extends Plugin return new LiveSyncCouchDBReplicator(this); } async onInitializeDatabase(db: LiveSyncLocalDB): Promise { + await delay(10); this.kvDB = await OpenKeyValueDatabase(db.dbname + "-livesync-kv"); - // this.trench = new Trench(this.simpleStore); this.replicator = this.getNewReplicator(); } async onResetDatabase(db: LiveSyncLocalDB): Promise { @@ -376,8 +376,9 @@ export default class ObsidianLiveSyncPlugin extends Plugin this.kvDB.del(kvDBKey); // localStorage.removeItem(lsKey); await this.kvDB.destroy(); + await yieldMicrotask(); this.kvDB = await OpenKeyValueDatabase(db.dbname + "-livesync-kv"); - // this.trench = new Trench(this.simpleStore); + await yieldMicrotask(); this.replicator = this.getNewReplicator() } getReplicator() { @@ -710,7 +711,7 @@ ___However, to enable either of these changes, both remote and local databases n } async onLayoutReady() { - eventHub.emit(EVENT_LAYOUT_READY); + eventHub.emitEvent(EVENT_LAYOUT_READY); this.registerFileWatchEvents(); if (!this.localDatabase.isReady) { Logger(`Something went wrong! The local database is not ready`, LOG_LEVEL_NOTICE); @@ -1160,34 +1161,34 @@ Note: We can always able to read V1 format. It will be progressively converted. } wireUpEvents() { - eventHub.on(EVENT_SETTING_SAVED, (evt: CustomEvent) => { + eventHub.onEvent(EVENT_SETTING_SAVED, (evt: CustomEvent) => { const settings = evt.detail; this.localDatabase.settings = settings; setLang(settings.displayLanguage); this.settingTab.requestReload(); this.ignoreFiles = settings.ignoreFiles.split(",").map(e => e.trim()); }); - eventHub.on(EVENT_SETTING_SAVED, (evt: CustomEvent) => { + eventHub.onEvent(EVENT_SETTING_SAVED, (evt: CustomEvent) => { const settings = evt.detail; if (settings.settingSyncFile != "") { fireAndForget(() => this.saveSettingToMarkdown(settings.settingSyncFile)); } }) - eventHub.on(EVENT_SETTING_SAVED, (evt: CustomEvent) => { + eventHub.onEvent(EVENT_SETTING_SAVED, (evt: CustomEvent) => { fireAndForget(() => this.realizeSettingSyncMode()); }) } connectObsidianEvents() { // this.registerEvent(this.app.workspace.on("editor-change", )); this.registerEvent(this.app.vault.on("rename", (file, oldPath) => { - eventHub.emit(EVENT_FILE_RENAMED, { newPath: file.path, old: oldPath }); + eventHub.emitEvent(EVENT_FILE_RENAMED, { newPath: file.path, old: oldPath }); })); - this.registerEvent(this.app.workspace.on("active-leaf-change", () => eventHub.emit(EVENT_LEAF_ACTIVE_CHANGED))); + this.registerEvent(this.app.workspace.on("active-leaf-change", () => eventHub.emitEvent(EVENT_LEAF_ACTIVE_CHANGED))); } async onload() { this.wireUpEvents(); this.connectObsidianEvents(); - eventHub.emit(EVENT_PLUGIN_LOADED, this); + eventHub.emitEvent(EVENT_PLUGIN_LOADED, this); logStore.pipeTo(new QueueProcessor(logs => logs.forEach(e => this.addLog(e.message, e.level, e.key)), { suspended: false, batchSize: 20, concurrentLimit: 1, delay: 0 })).startPipeline(); Logger("loading plugin"); __onMissingTranslation(() => { }); @@ -1214,6 +1215,36 @@ Note: We can always able to read V1 format. It will be progressively converted. } }); }) + type STUB = { + toc: Set, + stub: { [key: string]: { [key: string]: Map> } } + }; + eventHub.onEvent("document-stub-created", async (e: CustomEvent) => { + const stub = e.detail.stub; + const toc = e.detail.toc; + + const stubDocX = + Object.entries(stub).map(([key, value]) => { + return [`## ${key}`, Object.entries(value). + map(([key2, value2]) => { + return [`### ${key2}`, + ([...(value2.entries())].map(([key3, value3]) => { + // return `#### ${key3}` + "\n" + JSON.stringify(value3); + const isObsolete = value3["is_obsolete"] ? " (obsolete)" : ""; + const desc = value3["desc"] ?? ""; + const key = value3["key"] ? "Setting key: " + value3["key"] + "\n" : ""; + return `#### ${key3}${isObsolete}\n${key}${desc}\n` + }))].flat() + }).flat()].flat() + }).flat(); + const stubDocMD = ` +| Icon | Description | +| :---: | ----------------------------------------------------------------- | +` + + [...toc.values()].map(e => `${e}`).join("\n") + "\n\n" + + stubDocX.join("\n"); + await this.vaultAccess.adapterWrite(this.app.vault.configDir + "/ls-debug/stub-doc.md", stubDocMD); + }) } this.settingTab = new ObsidianLiveSyncSettingTab(this.app, this); this.addSettingTab(this.settingTab); @@ -1292,7 +1323,7 @@ Note: We can always able to read V1 format. It will be progressively converted. } onunload() { - eventHub.emit(EVENT_PLUGIN_UNLOADED); + eventHub.emitEvent(EVENT_PLUGIN_UNLOADED); cancelAllPeriodicTask(); cancelAllTasks(); stopAllRunningProcessors(); @@ -1305,7 +1336,7 @@ Note: We can always able to read V1 format. It will be progressively converted. } this.periodicSyncProcessor?.disable(); if (this.localDatabase != null) { - this.replicator.closeReplication(); + this.replicator?.closeReplication(); this.localDatabase.close(); } Logger($f`unloading plugin`); @@ -1496,7 +1527,7 @@ Note: We can always able to read V1 format. It will be progressively converted. } await this.saveData(settings); - eventHub.emit(EVENT_SETTING_SAVED, settings); + eventHub.emitEvent(EVENT_SETTING_SAVED, settings); } extractSettingFromWholeText(data: string): { preamble: string, body: string, postscript: string } { diff --git a/src/ui/ObsidianLiveSyncSettingTab.ts b/src/ui/ObsidianLiveSyncSettingTab.ts index 769ed04..c8bee79 100644 --- a/src/ui/ObsidianLiveSyncSettingTab.ts +++ b/src/ui/ObsidianLiveSyncSettingTab.ts @@ -1,4 +1,4 @@ -import { App, PluginSettingTab, Setting as SettingOrg, sanitizeHTMLToDom, MarkdownRenderer, stringifyYaml } from "../deps.ts"; +import { App, PluginSettingTab, MarkdownRenderer, stringifyYaml } from "../deps.ts"; import { DEFAULT_SETTINGS, type ObsidianLiveSyncSettings, @@ -18,26 +18,33 @@ import { REMOTE_COUCHDB, REMOTE_MINIO, PREFERRED_JOURNAL_SYNC, - statusDisplay, - type ConfigurationItem + FLAGMD_REDFLAG, + type ConfigLevel, + LEVEL_POWER_USER, + LEVEL_ADVANCED, + LEVEL_EDGE_CASE } from "../lib/src/common/types.ts"; -import { createBlob, delay, isDocContentSame, isObjectDifferent, readAsBlob, sizeToHumanReadable, unique } from "../lib/src/common/utils.ts"; +import { createBlob, delay, isDocContentSame, isObjectDifferent, readAsBlob, sizeToHumanReadable } from "../lib/src/common/utils.ts"; import { versionNumberString2Number } from "../lib/src/string_and_binary/convert.ts"; import { Logger } from "../lib/src/common/logger.ts"; import { checkSyncInfo, isCloudantURI } from "../lib/src/pouchdb/utils_couchdb.ts"; import { testCrypt } from "../lib/src/encryption/e2ee_v2.ts"; import ObsidianLiveSyncPlugin from "../main.ts"; import { askYesNo, performRebuildDB, requestToCouchDB, scheduleTask } from "../common/utils.ts"; -import { request, ButtonComponent, TFile, TextComponent, ToggleComponent, DropdownComponent, ValueComponent, TextAreaComponent } from "obsidian"; +import { request, TFile } from "obsidian"; import { shouldBeIgnored } from "../lib/src/string_and_binary/path.ts"; import MultipleRegExpControl from './components/MultipleRegExpControl.svelte'; import { LiveSyncCouchDBReplicator } from "../lib/src/replication/couchdb/LiveSyncReplicator.ts"; -import { type AllSettingItemKey, type AllStringItemKey, type AllNumericItemKey, type AllBooleanItemKey, type AllSettings, OnDialogSettingsDefault, getConfig, type OnDialogSettings, getConfName } from "./settingConstants.ts"; +import { type AllSettingItemKey, type AllStringItemKey, type AllNumericItemKey, type AllBooleanItemKey, type AllSettings, OnDialogSettingsDefault, type OnDialogSettings, getConfName } from "./settingConstants.ts"; import { SUPPORTED_I18N_LANGS, type I18N_LANGS } from "src/lib/src/common/rosetta.ts"; import { $t } from "src/lib/src/common/i18n.ts"; import { Semaphore } from "octagonal-wheels/concurrency/semaphore"; +import { LiveSyncSetting as Setting } from "./components/LiveSyncSetting.ts"; +import { yieldNextAnimationFrame } from "octagonal-wheels/promises"; +import { confirmWithMessage } from "src/common/dialogs.ts"; +import { eventHub } from "../common/events.ts"; -type OnUpdateResult = { +export type OnUpdateResult = { visibility?: boolean, disabled?: boolean, classes?: string[], @@ -47,12 +54,13 @@ type OnUpdateResult = { type OnUpdateFunc = () => OnUpdateResult; type UpdateFunction = () => void; -type AutoWireOption = { +export type AutoWireOption = { placeHolder?: string, holdValue?: boolean, isPassword?: boolean, invert?: boolean, onUpdate?: OnUpdateFunc; + obsolete?: boolean; } function visibleOnly(cond: () => boolean): OnUpdateFunc { @@ -72,7 +80,44 @@ type OnSavedHandler = { handler: OnSavedHandlerFunc, } -function wrapMemo(func: (arg: T) => void) { +function getLevelStr(level: ConfigLevel) { + return level == LEVEL_POWER_USER ? " (Power User)" : + level == LEVEL_ADVANCED ? " (Advanced)" : + level == LEVEL_EDGE_CASE ? " (Edge Case)" : ""; +} + +export function findAttrFromParent(el: HTMLElement, attr: string): string { + let current: HTMLElement | null = el; + while (current) { + const value = current.getAttribute(attr); + if (value) { + return value; + } + current = current.parentElement; + } + return ""; +} + +// For creating a document +const toc = new Set(); +const stubs = {} as { [key: string]: { [key: string]: Map> } }; +export function createStub(name: string, key: string, value: string, panel: string, pane: string) { + DEV: { + if (!(pane in stubs)) { + stubs[pane] = {}; + } + if (!(panel in stubs[pane])) { + stubs[pane][panel] = new Map>(); + } + const old = stubs[pane][panel].get(name) ?? {}; + stubs[pane][panel].set(name, { ...old, [key]: value }); + scheduleTask("update-stub", 100, () => { + eventHub.emitEvent("document-stub-created", { toc: toc, stub: stubs }); + }); + } +} + +export function wrapMemo(func: (arg: T) => void) { let buf: T | undefined = undefined; return (arg: T) => { if (buf !== arg) { @@ -82,310 +127,6 @@ function wrapMemo(func: (arg: T) => void) { } } -class Setting extends SettingOrg { - autoWiredComponent?: TextComponent | ToggleComponent | DropdownComponent | ButtonComponent | TextAreaComponent; - applyButtonComponent?: ButtonComponent; - selfKey?: AllSettingItemKey; - watchDirtyKeys = [] as AllSettingItemKey[]; - holdValue: boolean = false; - static env: ObsidianLiveSyncSettingTab; - - descBuf: string | DocumentFragment = ""; - nameBuf: string | DocumentFragment = ""; - placeHolderBuf: string = ""; - hasPassword: boolean = false; - - invalidateValue?: () => void; - setValue?: (value: any) => void; - constructor(containerEl: HTMLElement) { - super(containerEl); - Setting.env.settingComponents.push(this); - } - - setDesc(desc: string | DocumentFragment): this { - this.descBuf = desc; - super.setDesc(desc); - return this; - } - setName(name: string | DocumentFragment): this { - this.nameBuf = name; - super.setName(name); - return this; - } - setAuto(key: AllSettingItemKey, opt?: AutoWireOption) { - this.autoWireSetting(key, opt); - return this; - } - autoWireSetting(key: AllSettingItemKey, opt?: AutoWireOption) { - const conf = getConfig(key); - if (!conf) { - // throw new Error(`No such setting item :${key}`) - return; - } - const name = `${conf.name}${statusDisplay(conf.status)}`; - this.setName(name); - if (conf.desc) { - this.setDesc(conf.desc); - } - this.holdValue = opt?.holdValue || this.holdValue; - this.selfKey = key; - if (opt?.onUpdate) this.addOnUpdate(opt.onUpdate); - const stat = this._getComputedStatus(); - if (stat.visibility === false) { - this.settingEl.toggleClass("sls-setting-hidden", !stat.visibility); - } - return conf; - } - autoWireComponent(component: ValueComponent, conf?: ConfigurationItem, opt?: AutoWireOption) { - this.placeHolderBuf = conf?.placeHolder || opt?.placeHolder || ""; - if (this.placeHolderBuf && component instanceof TextComponent) { - component.setPlaceholder(this.placeHolderBuf) - } - if (opt?.onUpdate) this.addOnUpdate(opt.onUpdate); - } - async commitValue(value: AllSettings[T]) { - const key = this.selfKey as T; - if (key !== undefined) { - if (value != Setting.env.editingSettings[key]) { - Setting.env.editingSettings[key] = value; - if (!this.holdValue) { - await Setting.env.saveSettings([key]); - } - } - } - Setting.env.requestUpdate() - } - autoWireText(key: AllStringItemKey, opt?: AutoWireOption) { - const conf = this.autoWireSetting(key, opt); - this.addText(text => { - this.autoWiredComponent = text; - const setValue = wrapMemo((value: string) => text.setValue(value)); - this.invalidateValue = () => setValue(`${Setting.env.editingSettings[key]}`); - this.invalidateValue(); - text.onChange(async value => { - await this.commitValue(value); - }) - if (opt?.isPassword) { - text.inputEl.setAttribute("type", "password") - this.hasPassword = true; - } - this.autoWireComponent(this.autoWiredComponent, conf, opt); - }) - return this; - } - autoWireTextArea(key: AllStringItemKey, opt?: AutoWireOption) { - const conf = this.autoWireSetting(key, opt); - this.addTextArea(text => { - this.autoWiredComponent = text; - const setValue = wrapMemo((value: string) => text.setValue(value)); - this.invalidateValue = () => setValue(`${Setting.env.editingSettings[key]}`); - this.invalidateValue(); - text.onChange(async value => { - await this.commitValue(value); - }) - if (opt?.isPassword) { - text.inputEl.setAttribute("type", "password") - this.hasPassword = true; - } - this.autoWireComponent(this.autoWiredComponent, conf, opt); - }) - return this; - } - autoWireNumeric(key: AllNumericItemKey, opt: AutoWireOption & { clampMin?: number, clampMax?: number, acceptZero?: boolean }) { - const conf = this.autoWireSetting(key, opt); - this.addText(text => { - this.autoWiredComponent = text; - if (opt.clampMin) { - text.inputEl.setAttribute("min", `${opt.clampMin}`); - } - if (opt.clampMax) { - text.inputEl.setAttribute("max", `${opt.clampMax}`); - } - let lastError = false; - const setValue = wrapMemo((value: string) => text.setValue(value)); - this.invalidateValue = () => { - if (!lastError) setValue(`${Setting.env.editingSettings[key]}`); - } - this.invalidateValue(); - text.onChange(async TextValue => { - const parsedValue = Number(TextValue); - const value = parsedValue; - let hasError = false; - if (isNaN(value)) hasError = true; - if (opt.clampMax && opt.clampMax < value) hasError = true; - if (opt.clampMin && opt.clampMin > value) { - if (opt.acceptZero && value == 0) { - // This is ok. - } else { - hasError = true; - } - } - if (!hasError) { - lastError = false; - this.setTooltip(``); - text.inputEl.toggleClass("sls-item-invalid-value", false); - await this.commitValue(value); - } else { - this.setTooltip(`The value should ${opt.clampMin || "~"} < value < ${opt.clampMax || "~"}`); - text.inputEl.toggleClass("sls-item-invalid-value", true); - lastError = true; - return false; - } - }) - text.inputEl.setAttr("type", "number"); - this.autoWireComponent(this.autoWiredComponent, conf, opt); - }) - return this; - } - autoWireToggle(key: AllBooleanItemKey, opt?: AutoWireOption) { - const conf = this.autoWireSetting(key, opt); - this.addToggle(toggle => { - this.autoWiredComponent = toggle; - const setValue = wrapMemo((value: boolean) => toggle.setValue(opt?.invert ? !value : value)); - this.invalidateValue = () => setValue(Setting.env.editingSettings[key] ?? false); - this.invalidateValue(); - - toggle.onChange(async value => { - await this.commitValue(opt?.invert ? !value : value); - }) - - this.autoWireComponent(this.autoWiredComponent, conf, opt); - }) - return this; - } - autoWireDropDown(key: AllStringItemKey, opt: AutoWireOption & { options: Record }) { - const conf = this.autoWireSetting(key, opt); - this.addDropdown(dropdown => { - this.autoWiredComponent = dropdown; - const setValue = wrapMemo((value: string) => { - dropdown.setValue(value) - }); - - dropdown - .addOptions(opt.options) - - this.invalidateValue = () => setValue(Setting.env.editingSettings[key] || ""); - this.invalidateValue(); - dropdown.onChange(async value => { - await this.commitValue(value); - }) - this.autoWireComponent(this.autoWiredComponent, conf, opt); - }) - return this; - } - addApplyButton(keys: AllSettingItemKey[], text?: string) { - this.addButton((button) => { - this.applyButtonComponent = button; - this.watchDirtyKeys = unique([...keys, ...this.watchDirtyKeys]); - button.setButtonText(text ?? "Apply") - button.onClick(async () => { - await Setting.env.saveSettings(keys); - Setting.env.reloadAllSettings(); - }) - Setting.env.requestUpdate() - }) - return this; - } - addOnUpdate(func: () => OnUpdateResult) { - this.updateHandlers.add(func); - // this._applyOnUpdateHandlers(); - return this; - } - updateHandlers = new Set<() => OnUpdateResult>(); - - prevStatus: OnUpdateResult = {}; - - _getComputedStatus() { - let newConf = {} as OnUpdateResult; - for (const handler of this.updateHandlers) { - newConf = { - ...newConf, - ...handler(), - } - } - return newConf; - } - _applyOnUpdateHandlers() { - if (this.updateHandlers.size > 0) { - const newConf = this._getComputedStatus(); - const keys = Object.keys(newConf) as [keyof OnUpdateResult]; - for (const k of keys) { - - if (k in this.prevStatus && this.prevStatus[k] == newConf[k]) { - continue; - } - // const newValue = newConf[k]; - switch (k) { - case "visibility": - this.settingEl.toggleClass("sls-setting-hidden", !(newConf[k] || false)) - this.prevStatus[k] = newConf[k]; - break - case "classes": - break - case "disabled": - this.setDisabled((newConf[k] || false)) - this.settingEl.toggleClass("sls-setting-disabled", (newConf[k] || false)) - this.prevStatus[k] = newConf[k]; - break - case "isCta": - { - const component = this.autoWiredComponent; - if (component instanceof ButtonComponent) { - if (newConf[k]) { - component.setCta(); - } else { - component.removeCta(); - } - } - this.prevStatus[k] = newConf[k]; - } - break - case "isWarning": - { - const component = this.autoWiredComponent; - if (component instanceof ButtonComponent) { - if (newConf[k]) { - component.setWarning(); - } else { - //TODO:IMPLEMENT - // component.removeCta(); - } - } - this.prevStatus[k] = newConf[k]; - } - break - } - } - } - } - _onUpdate() { - if (this.applyButtonComponent) { - const isDirty = Setting.env.isSomeDirty(this.watchDirtyKeys); - this.applyButtonComponent.setDisabled(!isDirty); - if (isDirty) { - this.applyButtonComponent.setCta(); - } else { - this.applyButtonComponent.removeCta(); - } - } - if (this.selfKey && !Setting.env.isDirty(this.selfKey) && this.invalidateValue) { - this.invalidateValue(); - } - if (this.holdValue && this.selfKey) { - const isDirty = Setting.env.isDirty(this.selfKey); - const alt = isDirty ? `Original: ${Setting.env.initialSettings![this.selfKey]}` : "" - this.controlEl.toggleClass("sls-item-dirty", isDirty); - if (!this.hasPassword) { - this.nameEl.toggleClass("sls-item-dirty-help", isDirty); - this.setTooltip(alt, { delay: 10, placement: "right" }); - } - } - this._applyOnUpdateHandlers(); - } - -} - - export class ObsidianLiveSyncSettingTab extends PluginSettingTab { plugin: ObsidianLiveSyncPlugin; selectedScreen = ""; @@ -577,6 +318,8 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab { controlledElementFunc = [] as UpdateFunction[]; onSavedHandlers = [] as OnSavedHandler[]; + inWizard: boolean = false; + constructor(app: App, plugin: ObsidianLiveSyncPlugin) { super(app, plugin); this.plugin = plugin; @@ -632,6 +375,15 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab { return element; } + addEl(el: HTMLElement, + tag: T, + o?: string | DomElementInfo | undefined, + callback?: ((el: HTMLElementTagNameMap[T]) => void), + func?: OnUpdateFunc) { + const elm = this.createEl(el, tag, o, callback, func); + return Promise.resolve(elm); + } + addOnSaved(key: T, func: (value: AllSettings[T]) => (Promise | void)) { this.onSavedHandlers.push({ key, handler: func }); } @@ -694,12 +446,23 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab { this.isShown = true; containerEl.empty(); - this.createEl(containerEl, "h2", { text: "Settings for Self-hosted LiveSync." }); + containerEl.addClass("sls-setting"); containerEl.removeClass("isWizard"); + const setStyle = (el: HTMLElement, styleHead: string, condition: () => boolean) => { + if (condition()) { + el.addClass(`${styleHead}-enabled`); + el.removeClass(`${styleHead}-disabled`); + } else { + el.addClass(`${styleHead}-disabled`); + el.removeClass(`${styleHead}-enabled`); + } + } + setStyle(containerEl, "menu-setting-poweruser", () => this.isConfiguredAs("usePowerUserMode", true)); + setStyle(containerEl, "menu-setting-advanced", () => this.isConfiguredAs("useAdvancedMode", true)); + setStyle(containerEl, "menu-setting-edgecase", () => this.isConfiguredAs("useEdgeCaseMode", true)); - const w = containerEl.createDiv(""); const screenElements: { [key: string]: HTMLElement[] } = {}; const addScreenElement = (key: string, element: HTMLElement) => { if (!(key in screenElements)) { @@ -707,18 +470,181 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab { } screenElements[key].push(element); }; + const menuWrapper = this.createEl(containerEl, "div", { cls: "sls-setting-menu-wrapper" }); + + const w = menuWrapper.createDiv(""); w.addClass("sls-setting-menu"); - w.innerHTML = ` - - - - - - - - - `; const menuTabs = w.querySelectorAll(".sls-setting-label"); + const selectPane = (event: Event) => { + const target = event.target as HTMLElement; + if (target.tagName == "INPUT") { + const value = target.getAttribute("value"); + if (value && this.selectedScreen != value) { + changeDisplay(value); + // target.parentElement?.parentElement?.querySelector(".sls-setting-label.selected")?.removeClass("selected"); + // target.parentElement?.addClass("selected"); + } + } + } + const isNeedRebuildLocal = () => { + return this.isSomeDirty( + [ + "useIndexedDBAdapter", + "doNotUseFixedRevisionForChunks", + "handleFilenameCaseSensitive", + "passphrase", + "useDynamicIterationCount", + "usePathObfuscation", + "encrypt", + // "remoteType", + ]) + } + const isNeedRebuildRemote = () => { + return this.isSomeDirty( + [ + "doNotUseFixedRevisionForChunks", + "handleFilenameCaseSensitive", + "passphrase", + "useDynamicIterationCount", + "usePathObfuscation", + "encrypt" + ]) + }; + const confirmRebuild = async () => { + if (!await isPassphraseValid()) { + Logger(`Passphrase is not valid, please fix it.`, LOG_LEVEL_NOTICE); + return; + } + const OPTION_FETCH = `Fetch from Remote`; + const OPTION_REBUILD_BOTH = `Rebuild Both from This Device`; + const OPTION_ONLY_SETTING = `(Danger) Save Only Settings`; + const OPTION_CANCEL = `Cancel`; + const title = `Rebuild Required`; + const note = `Rebuilding Databases are required to apply the changes.. Please select the method to apply the changes. + +
+Legends + +| Symbol | Meaning | +|: ------ :| ------- | +| ⇔ | Synchronised or well balanced | +| ⇄ | Synchronise to balance | +| ⇐,⇒ | Transfer to overwrite | +| ⇠,⇢ | Transfer to overwrite from other side | + +
+ +## ${OPTION_REBUILD_BOTH} +At a glance: 📄 ⇒¹ 💻 ⇒² 🛰️ ⇢ⁿ 💻 ⇄ⁿ⁺¹ 📄 +Reconstruct both the local and remote databases using existing files from this device. +This causes a lockout other devices, and they need to perform fetching. +## ${OPTION_FETCH} +At a glance: 📄 ⇄² 💻 ⇐¹ 🛰️ ⇔ 💻 ⇔ 📄 +Initialise the local database and reconstruct it using data fetched from the remote database. +This case includes the case which you have rebuilt the remote database. +## ${OPTION_ONLY_SETTING} +Store only the settings. **Caution: This may lead to data corruption**; database reconstruction is generally necessary.`; + const buttons = [ + OPTION_FETCH, + OPTION_REBUILD_BOTH, + // OPTION_REBUILD_REMOTE, + OPTION_ONLY_SETTING, + OPTION_CANCEL + ]; + const result = await confirmWithMessage(this.plugin, + title, note, + buttons, + OPTION_CANCEL, 0 + ) + if (result == OPTION_CANCEL) return; + if (result == OPTION_FETCH) { + if (!await checkWorkingPassphrase()) { + if (await askYesNo(this.app, "Are you sure to proceed?") != "yes") return; + } + } + if (!this.editingSettings.encrypt) { + this.editingSettings.passphrase = ""; + } + await this.saveAllDirtySettings(); + await this.applyAllSettings(); + if (result == OPTION_FETCH) { + await this.plugin.vaultAccess.vaultCreate(FLAGMD_REDFLAG3_HR, ""); + this.plugin.scheduleAppReload(); + this.closeSetting(); + // await rebuildDB("localOnly"); + } else if (result == OPTION_REBUILD_BOTH) { + await this.plugin.vaultAccess.vaultCreate(FLAGMD_REDFLAG2_HR, ""); + this.plugin.scheduleAppReload(); + this.closeSetting(); + } else if (result == OPTION_ONLY_SETTING) { + await this.plugin.saveSettings(); + } + + } + this.createEl(menuWrapper, "div", { cls: "sls-setting-menu-buttons" }, (el) => { + el.addClass("wizardHidden"); + el.createEl("label", { text: "Changes need to be applied!" }); + this.addEl(el, "button", { text: "Apply", cls: "mod-warning" }, buttonEl => { + buttonEl.addEventListener("click", async () => await confirmRebuild()) + }) + }, visibleOnly(() => isNeedRebuildLocal() || isNeedRebuildRemote())); + const setLevelClass = (el: HTMLElement, level?: ConfigLevel) => { + switch (level) { + case LEVEL_POWER_USER: el.addClass("sls-setting-poweruser"); + break; + case LEVEL_ADVANCED: el.addClass("sls-setting-advanced"); + break; + case LEVEL_EDGE_CASE: el.addClass("sls-setting-edgecase"); + break; + default: + // NO OP. + } + } + let paneNo = 0; + const addPane = (parentEl: HTMLElement, title: string, icon: string, order: number, wizardHidden: boolean, level?: ConfigLevel) => { + const el = this.createEl(parentEl, "div", { text: "" }); + DEV: { + const mdTitle = `${paneNo++}. ${title}${getLevelStr(level ?? "")}`; + el.setAttribute("data-pane", mdTitle); + toc.add(`| ${icon} | [${mdTitle}](#${mdTitle.toLowerCase().replace(/ /g, "-").replace(/[^\w\s-]/g, "")}) | `); + } + setLevelClass(el, level) + el.createEl("h3", { text: title, cls: "sls-setting-pane-title" }); + w.createEl("label", { cls: `sls-setting-label c-${order} ${wizardHidden ? "wizardHidden" : ""}` }, el => { + setLevelClass(el, level) + const inputEl = el.createEl("input", { type: "radio", name: "disp", value: `${order}`, cls: "sls-setting-tab" } as DomElementInfo); + el.createEl("div", { cls: "sls-setting-menu-btn", text: icon, title: title }); + inputEl.addEventListener("change", selectPane); + inputEl.addEventListener("click", selectPane); + }) + addScreenElement(`${order}`, el); + const p = Promise.resolve(el) + p.finally(() => { + // Recap at the end. + }); + return p; + } + const panelNoMap = {} as { [key: string]: number }; + const addPanel = (parentEl: HTMLElement, title: string, callback?: ((el: HTMLDivElement) => void), func?: OnUpdateFunc, level?: ConfigLevel) => { + const el = this.createEl(parentEl, "div", { text: "" }, callback, func); + DEV: { + const paneNo = findAttrFromParent(parentEl, "data-pane"); + if (!(paneNo in panelNoMap)) { + panelNoMap[paneNo] = 0; + } + panelNoMap[paneNo] += 1; + const panelNo = panelNoMap[paneNo]; + el.setAttribute("data-panel", `${panelNo}. ${title}${getLevelStr(level ?? "")}`); + } + setLevelClass(el, level) + this.createEl(el, "h4", { text: title, cls: "sls-setting-panel-title" }); + const p = Promise.resolve(el); + p.finally(() => { + // Recap at the end. + }) + return p; + } + const changeDisplay = (screen: string) => { for (const k in screenElements) { if (k == screen) { @@ -728,13 +654,15 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab { } } w.querySelectorAll(`.sls-setting-label`).forEach((element) => { - element.removeClass("selected"); - (element.querySelector("input[type=radio]"))!.checked = false; - }); - w.querySelectorAll(`.sls-setting-label.c-${screen}`).forEach((element) => { - element.addClass("selected"); - (element.querySelector("input[type=radio]"))!.checked = true; - }); + if (element.hasClass(`c-${screen}`)) { + element.addClass("selected"); + (element.querySelector("input[type=radio]"))!.checked = true; + } else { + element.removeClass("selected"); + (element.querySelector("input[type=radio]"))!.checked = false; + } + } + ); this.selectedScreen = screen; }; menuTabs.forEach((element) => { @@ -747,10 +675,6 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab { }); }); - const containerInformationEl = containerEl.createDiv(); - const h3El = this.createEl(containerInformationEl, "h3", { text: "Updates" }); - const informationDivEl = this.createEl(containerInformationEl, "div", { text: "" }); - //@ts-ignore const manifestVersion: string = MANIFEST_VERSION || "-"; //@ts-ignore @@ -758,23 +682,6 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab { const lastVersion = ~~(versionNumberString2Number(manifestVersion) / 1000); - const tmpDiv = createSpan(); - tmpDiv.addClass("sls-header-button"); - tmpDiv.innerHTML = ``; - if (lastVersion > (this.editingSettings?.lastReadUpdates || 0)) { - const informationButtonDiv = h3El.appendChild(tmpDiv); - informationButtonDiv.querySelector("button")?.addEventListener("click", async () => { - this.editingSettings.lastReadUpdates = lastVersion; - await this.saveAllDirtySettings(); - informationButtonDiv.remove(); - }); - - } - - MarkdownRenderer.render(this.plugin.app, updateInformation, informationDivEl, "/", this.plugin); - - - addScreenElement("100", containerInformationEl); const isAnySyncEnabled = (): boolean => { if (this.isConfiguredAs("isConfigured", false)) return false; if (this.isConfiguredAs("liveSync", true)) return true; @@ -789,145 +696,7 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab { if (this.plugin?.replicator?.syncStatus == "PAUSED") return true; return false; }; - // const visibleOnlySyncDisabled = visibleOnly(() => !isAnySyncEnabled()) - // const visibleOnlySyncDisabled = visibleOnly(() => !isAnySyncEnabled()) const enableOnlySyncDisabled = enableOnly(() => !isAnySyncEnabled()) - - let inWizard = false; - if (containerEl.hasClass("inWizard")) { - inWizard = true; - } - - const setupWizardEl = containerEl.createDiv(); - this.createEl(setupWizardEl, "h3", { text: "Setup wizard" }); - new Setting(setupWizardEl) - .setName("Use the copied setup URI") - .setDesc("To setup Self-hosted LiveSync, this method is the most preferred one.") - .addButton((text) => { - text.setButtonText("Use").onClick(async () => { - this.closeSetting(); - await this.plugin.addOnSetup.command_openSetupURI(); - }) - }) - if (this.editingSettings.isConfigured) { - new Setting(setupWizardEl) - .setName("Copy current settings as a new setup URI") - .addButton((text) => { - text.setButtonText("Copy").onClick(async () => { - await this.plugin.addOnSetup.command_copySetupURI(); - }) - }) - } - new Setting(setupWizardEl) - .setName("Minimal setup") - .addButton((text) => { - text.setButtonText("Start").onClick(async () => { - this.editingSettings.liveSync = false; - this.editingSettings.periodicReplication = false; - this.editingSettings.syncOnSave = false; - this.editingSettings.syncOnEditorSave = false; - this.editingSettings.syncOnStart = false; - this.editingSettings.syncOnFileOpen = false; - this.editingSettings.syncAfterMerge = false; - this.plugin.replicator.closeReplication(); - await this.saveAllDirtySettings(); - containerEl.addClass("isWizard"); - inWizard = true; - changeDisplay("0") - }) - }) - new Setting(setupWizardEl) - .setName("Enable LiveSync on this device as the setup was completed manually") - .addButton((text) => { - text.setButtonText("Enable").onClick(async () => { - this.editingSettings.isConfigured = true; - await this.saveAllDirtySettings(); - this.plugin.askReload(); - }) - }) - .addOnUpdate(visibleOnly(() => !this.isConfiguredAs("isConfigured", true))) - - new Setting(setupWizardEl) - .setName("Discard existing settings and databases") - .addButton((text) => { - text.setButtonText("Discard").onClick(async () => { - if (await askYesNo(this.plugin.app, "Do you really want to discard existing settings and databases?") == "yes") { - this.editingSettings = { ...this.editingSettings, ...DEFAULT_SETTINGS }; - await this.plugin.saveSettingData(); - await this.plugin.resetLocalDatabase(); - // await this.plugin.initializeDatabase(); - this.plugin.askReload(); - } - }).setWarning() - }).addOnUpdate(visibleOnly(() => this.isConfiguredAs("isConfigured", true))) - // } - this.createEl(setupWizardEl, "h3", { text: "Online Tips" }); - const repo = "vrtmrz/obsidian-livesync"; - const topPath = "/docs/troubleshooting.md"; - const rawRepoURI = `https://raw.githubusercontent.com/${repo}/main`; - this.createEl(setupWizardEl, "div", "", el => el.innerHTML = `Open in browser`); - const troubleShootEl = this.createEl(setupWizardEl, "div", { text: "", cls: "sls-troubleshoot-preview" }); - const loadMarkdownPage = async (pathAll: string, basePathParam: string = "") => { - troubleShootEl.style.minHeight = troubleShootEl.clientHeight + "px"; - troubleShootEl.empty(); - const fullPath = pathAll.startsWith("/") ? pathAll : `${basePathParam}/${pathAll}`; - - const directoryArr = fullPath.split("/"); - const filename = directoryArr.pop(); - const directly = directoryArr.join("/"); - const basePath = directly; - - let remoteTroubleShootMDSrc = ""; - try { - remoteTroubleShootMDSrc = await request(`${rawRepoURI}${basePath}/${filename}`); - } catch (ex: any) { - remoteTroubleShootMDSrc = "An error occurred!!\n" + ex.toString(); - } - const remoteTroubleShootMD = remoteTroubleShootMDSrc.replace(/\((.*?(.png)|(.jpg))\)/g, `(${rawRepoURI}${basePath}/$1)`) - // Render markdown - await MarkdownRenderer.render(this.plugin.app, ` [Tips and Troubleshooting](${topPath}) [PageTop](${filename})\n\n${remoteTroubleShootMD}`, troubleShootEl, `${rawRepoURI}`, this.plugin); - // Menu - troubleShootEl.querySelector(".sls-troubleshoot-anchor")?.parentElement?.setCssStyles({ - position: "sticky", - top: "-1em", - backgroundColor: "var(--modal-background)" - }); - // Trap internal links. - troubleShootEl.querySelectorAll("a.internal-link").forEach((anchorEl) => { - anchorEl.addEventListener("click", async (evt) => { - const uri = anchorEl.getAttr("data-href"); - if (!uri) return; - if (uri.startsWith("#")) { - evt.preventDefault(); - const elements = Array.from(troubleShootEl.querySelectorAll("[data-heading]")) - const p = elements.find(e => e.getAttr("data-heading")?.toLowerCase().split(" ").join("-") == uri.substring(1).toLowerCase()); - if (p) { - p.setCssStyles({ scrollMargin: "3em" }); - p.scrollIntoView({ behavior: "instant", block: "start" }); - } - } else { - evt.preventDefault(); - await loadMarkdownPage(uri, basePath); - troubleShootEl.setCssStyles({ scrollMargin: "1em" }); - troubleShootEl.scrollIntoView({ behavior: "instant", block: "start" }); - } - }) - }) - troubleShootEl.style.minHeight = ""; - } - loadMarkdownPage(topPath); - addScreenElement("110", setupWizardEl); - - const containerRemoteDatabaseEl = containerEl.createDiv(); - this.createEl(containerRemoteDatabaseEl, "h3", { text: "Remote configuration" }); - - new Setting(containerRemoteDatabaseEl) - .autoWireDropDown("remoteType", { - holdValue: true, options: { - [REMOTE_COUCHDB]: "CouchDB", [REMOTE_MINIO]: "Minio,S3,R2", - }, onUpdate: enableOnlySyncDisabled - }) - const onlyOnCouchDB = () => ({ visibility: this.isConfiguredAs('remoteType', REMOTE_COUCHDB) }) as OnUpdateResult; @@ -935,390 +704,44 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab { visibility: this.isConfiguredAs('remoteType', REMOTE_MINIO) }) as OnUpdateResult; - this.createEl(containerRemoteDatabaseEl, "div", undefined, containerRemoteDatabaseEl => { - - const syncWarnMinio = this.createEl(containerRemoteDatabaseEl, "div", { - text: "" - }); - const ObjectStorageMessage = `Kindly notice: this is a pretty experimental feature, hence we have some limitations. -- Append only architecture. It will not shrink used storage if we do not perform a rebuild. -- A bit fragile. -- During the first synchronization, the entire history to date will be transferred. For this reason, it is preferable to do this while connected to a Wi-Fi network. -- From the second, we always transfer only differences. - -However, your report is needed to stabilise this. I appreciate you for your great dedication. -`; - - MarkdownRenderer.render(this.plugin.app, ObjectStorageMessage, syncWarnMinio, "/", this.plugin); - syncWarnMinio.addClass("op-warn-info"); - - new Setting(containerRemoteDatabaseEl).autoWireText("endpoint", { holdValue: true }) - new Setting(containerRemoteDatabaseEl).autoWireText("accessKey", { holdValue: true }); - - new Setting(containerRemoteDatabaseEl).autoWireText("secretKey", { holdValue: true, isPassword: true }); - - new Setting(containerRemoteDatabaseEl).autoWireText("region", { holdValue: true }); - - new Setting(containerRemoteDatabaseEl).autoWireText("bucket", { holdValue: true }); - - new Setting(containerRemoteDatabaseEl).autoWireToggle("useCustomRequestHandler", { holdValue: true }); - new Setting(containerRemoteDatabaseEl) - .setName("Test Connection") - .addButton((button) => - button - .setButtonText("Test") - .setDisabled(false) - .onClick(async () => { - await this.testConnection(this.editingSettings); - }) - ); - new Setting(containerRemoteDatabaseEl) - .setName("Apply Settings") - .setClass("wizardHidden") - .addApplyButton(["remoteType", "endpoint", "region", "accessKey", "secretKey", "bucket", "useCustomRequestHandler"]) - .addOnUpdate(onlyOnMinIO) - - }, onlyOnMinIO); - - - this.createEl(containerRemoteDatabaseEl, "div", undefined, containerRemoteDatabaseEl => { - if (this.plugin.isMobile) { - this.createEl(containerRemoteDatabaseEl, "div", { - text: `Configured as using non-HTTPS. We cannot connect to the remote. Please set up the credentials and use HTTPS for the remote URI.`, - }, undefined, visibleOnly(() => !this.editingSettings.couchDB_URI.startsWith("https://"))) - .addClass("op-warn"); - } else { - this.createEl(containerRemoteDatabaseEl, "div", { - text: `Configured as using non-HTTPS. We might fail on mobile devices.` - }, undefined, visibleOnly(() => !this.editingSettings.couchDB_URI.startsWith("https://"))) - .addClass("op-warn-info"); - } - - this.createEl(containerRemoteDatabaseEl, "div", { text: `These settings are kept locked while any synchronization options are enabled. Disable these options in the "Sync Settings" tab to unlock.` }, - undefined, visibleOnly(() => isAnySyncEnabled()) - ).addClass("sls-setting-hidden"); - - new Setting(containerRemoteDatabaseEl).autoWireText("couchDB_URI", { holdValue: true, onUpdate: enableOnlySyncDisabled }); - new Setting(containerRemoteDatabaseEl).autoWireText("couchDB_USER", { holdValue: true, onUpdate: enableOnlySyncDisabled }); - new Setting(containerRemoteDatabaseEl).autoWireText("couchDB_PASSWORD", { holdValue: true, isPassword: true, onUpdate: enableOnlySyncDisabled }); - new Setting(containerRemoteDatabaseEl).autoWireText("couchDB_DBNAME", { holdValue: true, onUpdate: enableOnlySyncDisabled }); - - - new Setting(containerRemoteDatabaseEl) - .setName("Test Database Connection") - .setClass("wizardHidden") - .setDesc("Open database connection. If the remote database is not found and you have the privilege to create a database, the database will be created.") - .addButton((button) => - button - .setButtonText("Test") - .setDisabled(false) - .onClick(async () => { - await this.testConnection(); - }) - ); - - new Setting(containerRemoteDatabaseEl) - .setName("Check and fix database configuration") - .setDesc("Check the database configuration, and fix if there are any problems.") - .addButton((button) => - button - .setButtonText("Check") - .setDisabled(false) - .onClick(async () => { - const checkConfig = async () => { - Logger(`Checking database configuration`, LOG_LEVEL_INFO); - - const emptyDiv = createDiv(); - emptyDiv.innerHTML = ""; - checkResultDiv.replaceChildren(...[emptyDiv]); - const addResult = (msg: string, classes?: string[]) => { - const tmpDiv = createDiv(); - tmpDiv.addClass("ob-btn-config-fix"); - if (classes) { - tmpDiv.addClasses(classes); - } - tmpDiv.innerHTML = `${msg}`; - checkResultDiv.appendChild(tmpDiv); - }; - try { - - if (isCloudantURI(this.editingSettings.couchDB_URI)) { - Logger("This feature cannot be used with IBM Cloudant.", LOG_LEVEL_NOTICE); - return; - } - const r = await requestToCouchDB(this.editingSettings.couchDB_URI, this.editingSettings.couchDB_USER, this.editingSettings.couchDB_PASSWORD, window.origin); - const responseConfig = r.json; - - const addConfigFixButton = (title: string, key: string, value: string) => { - const tmpDiv = createDiv(); - tmpDiv.addClass("ob-btn-config-fix"); - tmpDiv.innerHTML = ``; - const x = checkResultDiv.appendChild(tmpDiv); - x.querySelector("button")?.addEventListener("click", async () => { - Logger(`CouchDB Configuration: ${title} -> Set ${key} to ${value}`) - const res = await requestToCouchDB(this.editingSettings.couchDB_URI, this.editingSettings.couchDB_USER, this.editingSettings.couchDB_PASSWORD, undefined, key, value); - if (res.status == 200) { - Logger(`CouchDB Configuration: ${title} successfully updated`, LOG_LEVEL_NOTICE); - checkResultDiv.removeChild(x); - checkConfig(); - } else { - Logger(`CouchDB Configuration: ${title} failed`, LOG_LEVEL_NOTICE); - Logger(res.text, LOG_LEVEL_VERBOSE); - } - }); - }; - addResult("---Notice---", ["ob-btn-config-head"]); - addResult( - "If the server configuration is not persistent (e.g., running on docker), the values set from here will also be volatile. Once you are able to connect, please reflect the settings in the server's local.ini.", - ["ob-btn-config-info"] - ); - - addResult("--Config check--", ["ob-btn-config-head"]); - - // Admin check - // for database creation and deletion - if (!(this.editingSettings.couchDB_USER in responseConfig.admins)) { - addResult(`⚠ You do not have administrative privileges.`); - } else { - addResult("✔ You have administrative privileges."); - } - // HTTP user-authorization check - if (responseConfig?.chttpd?.require_valid_user != "true") { - addResult("❗ chttpd.require_valid_user is wrong."); - addConfigFixButton("Set chttpd.require_valid_user = true", "chttpd/require_valid_user", "true"); - } else { - addResult("✔ chttpd.require_valid_user is ok."); - } - if (responseConfig?.chttpd_auth?.require_valid_user != "true") { - addResult("❗ chttpd_auth.require_valid_user is wrong."); - addConfigFixButton("Set chttpd_auth.require_valid_user = true", "chttpd_auth/require_valid_user", "true"); - } else { - addResult("✔ chttpd_auth.require_valid_user is ok."); - } - // HTTPD check - // Check Authentication header - if (!responseConfig?.httpd["WWW-Authenticate"]) { - addResult("❗ httpd.WWW-Authenticate is missing"); - addConfigFixButton("Set httpd.WWW-Authenticate", "httpd/WWW-Authenticate", 'Basic realm="couchdb"'); - } else { - addResult("✔ httpd.WWW-Authenticate is ok."); - } - if (responseConfig?.httpd?.enable_cors != "true") { - addResult("❗ httpd.enable_cors is wrong"); - addConfigFixButton("Set httpd.enable_cors", "httpd/enable_cors", "true"); - } else { - addResult("✔ httpd.enable_cors is ok."); - } - // If the server is not cloudant, configure request size - if (!isCloudantURI(this.editingSettings.couchDB_URI)) { - // REQUEST SIZE - if (Number(responseConfig?.chttpd?.max_http_request_size ?? 0) < 4294967296) { - addResult("❗ chttpd.max_http_request_size is low)"); - addConfigFixButton("Set chttpd.max_http_request_size", "chttpd/max_http_request_size", "4294967296"); - } else { - addResult("✔ chttpd.max_http_request_size is ok."); - } - if (Number(responseConfig?.couchdb?.max_document_size ?? 0) < 50000000) { - addResult("❗ couchdb.max_document_size is low)"); - addConfigFixButton("Set couchdb.max_document_size", "couchdb/max_document_size", "50000000"); - } else { - addResult("✔ couchdb.max_document_size is ok."); - } - } - // CORS check - // checking connectivity for mobile - if (responseConfig?.cors?.credentials != "true") { - addResult("❗ cors.credentials is wrong"); - addConfigFixButton("Set cors.credentials", "cors/credentials", "true"); - } else { - addResult("✔ cors.credentials is ok."); - } - const ConfiguredOrigins = ((responseConfig?.cors?.origins ?? "") + "").split(","); - if ( - responseConfig?.cors?.origins == "*" || - (ConfiguredOrigins.indexOf("app://obsidian.md") !== -1 && ConfiguredOrigins.indexOf("capacitor://localhost") !== -1 && ConfiguredOrigins.indexOf("http://localhost") !== -1) - ) { - addResult("✔ cors.origins is ok."); - } else { - addResult("❗ cors.origins is wrong"); - addConfigFixButton("Set cors.origins", "cors/origins", "app://obsidian.md,capacitor://localhost,http://localhost"); - } - addResult("--Connection check--", ["ob-btn-config-head"]); - addResult(`Current origin:${window.location.origin}`); - - // Request header check - const origins = ["app://obsidian.md", "capacitor://localhost", "http://localhost"]; - for (const org of origins) { - const rr = await requestToCouchDB(this.editingSettings.couchDB_URI, this.editingSettings.couchDB_USER, this.editingSettings.couchDB_PASSWORD, org); - const responseHeaders = Object.fromEntries(Object.entries(rr.headers) - .map((e) => { - e[0] = `${e[0]}`.toLowerCase(); - return e; - })); - addResult(`Origin check:${org}`); - if (responseHeaders["access-control-allow-credentials"] != "true") { - addResult("❗ CORS is not allowing credentials"); - } else { - addResult("✔ CORS credentials OK"); - } - if (responseHeaders["access-control-allow-origin"] != org) { - addResult(`❗ CORS Origin is unmatched:${origin}->${responseHeaders["access-control-allow-origin"]}`); - } else { - addResult("✔ CORS origin OK"); - } - } - addResult("--Done--", ["ob-btn-config-head"]); - addResult("If you have some trouble with Connection-check even though all Config-check has been passed, please check your reverse proxy's configuration.", ["ob-btn-config-info"]); - Logger(`Checking configuration done`, LOG_LEVEL_INFO); - } catch (ex: any) { - if (ex?.status == 401) { - addResult(`❗ Access forbidden.`); - addResult(`We could not continue the test.`); - Logger(`Checking configuration done`, LOG_LEVEL_INFO); - } else { - Logger(`Checking configuration failed`, LOG_LEVEL_NOTICE); - Logger(ex); - } - } - }; - await checkConfig(); - }) - ); - const checkResultDiv = this.createEl(containerRemoteDatabaseEl, "div", { - text: "", - }); - - new Setting(containerRemoteDatabaseEl) - .setName("Apply Settings") - .setClass("wizardHidden") - .addApplyButton(["remoteType", "couchDB_URI", "couchDB_USER", "couchDB_PASSWORD", "couchDB_DBNAME"]) - .addOnUpdate(onlyOnCouchDB) - }, onlyOnCouchDB); - - this.createEl(containerRemoteDatabaseEl, "h4", { text: "Notification" }).addClass("wizardHidden") - new Setting(containerRemoteDatabaseEl).autoWireNumeric("notifyThresholdOfRemoteStorageSize", {}).setClass("wizardHidden"); - - - this.createEl(containerRemoteDatabaseEl, "h4", { text: "Effective Storage Using" }).addClass("wizardHidden") - - new Setting(containerRemoteDatabaseEl).autoWireToggle("useEden").setClass("wizardHidden"); - const onlyUsingEden = visibleOnly(() => this.isConfiguredAs("useEden", true)); - new Setting(containerRemoteDatabaseEl).autoWireNumeric("maxChunksInEden", { onUpdate: onlyUsingEden }).setClass("wizardHidden"); - new Setting(containerRemoteDatabaseEl).autoWireNumeric("maxTotalLengthInEden", { onUpdate: onlyUsingEden }).setClass("wizardHidden"); - new Setting(containerRemoteDatabaseEl).autoWireNumeric("maxAgeInEden", { onUpdate: onlyUsingEden }).setClass("wizardHidden"); - - new Setting(containerRemoteDatabaseEl).autoWireToggle("enableCompression").setClass("wizardHidden"); - - this.createEl(containerRemoteDatabaseEl, "h4", { text: "Confidentiality" }); - - new Setting(containerRemoteDatabaseEl) - .autoWireToggle("encrypt", { holdValue: true }) - - const isEncryptEnabled = visibleOnly(() => this.isConfiguredAs("encrypt", true)) - - new Setting(containerRemoteDatabaseEl) - .autoWireText("passphrase", { holdValue: true, isPassword: true, onUpdate: isEncryptEnabled }) - - new Setting(containerRemoteDatabaseEl) - .autoWireToggle("usePathObfuscation", { holdValue: true, onUpdate: isEncryptEnabled }) - new Setting(containerRemoteDatabaseEl) - .autoWireToggle("useDynamicIterationCount", { holdValue: true, onUpdate: isEncryptEnabled }).setClass("wizardHidden"); - - new Setting(containerRemoteDatabaseEl) - .setName("Apply") - .setDesc("Apply encryption settings") - .setClass("wizardHidden") - .addButton((button) => - button - .setButtonText("Just apply") - .setWarning() - .setDisabled(false) - .onClick(async () => { - await applyEncryption(false); - }) - ) - .addButton((button) => - button - .setButtonText("Apply and fetch") - .setWarning() - .setDisabled(false) - .onClick(async () => { - await rebuildDB("localOnly"); - }) - ) - .addButton((button) => - button - .setButtonText("Apply and rebuild") - .setWarning() - .setDisabled(false) - .onClick(async () => { - await rebuildDB("rebuildBothByThisDevice"); - }) - ) - .addOnUpdate(() => ({ - isCta: this.isSomeDirty(["passphrase", "useDynamicIterationCount", "usePathObfuscation", "encrypt"]), - disabled: !this.isSomeDirty(["passphrase", "useDynamicIterationCount", "usePathObfuscation", "encrypt"]), - })) + // E2EE Function const checkWorkingPassphrase = async (): Promise => { if (this.editingSettings.remoteType == REMOTE_MINIO) return true; const settingForCheck: RemoteDBSettings = { ...this.editingSettings, - // encrypt: encrypt, - // passphrase: passphrase, - // useDynamicIterationCount: useDynamicIterationCount, }; - const replicator = this.plugin.getReplicator(); + const replicator = this.plugin.getNewReplicator( + settingForCheck + ); if (!(replicator instanceof LiveSyncCouchDBReplicator)) return true; const db = await replicator.connectRemoteCouchDBWithSetting(settingForCheck, this.plugin.isMobile, true); if (typeof db === "string") { - Logger("Could not connect to the database.", LOG_LEVEL_NOTICE); + Logger(`ERROR: Failed to check passphrase with the remote server: \n${db}.`, LOG_LEVEL_NOTICE); return false; } else { if (await checkSyncInfo(db.db)) { // Logger("Database connected", LOG_LEVEL_NOTICE); return true; } else { - Logger("Failed to read remote database", LOG_LEVEL_NOTICE); + Logger("ERROR: Passphrase is not compatible with the remote server! Please confirm it again!", LOG_LEVEL_NOTICE); return false; } } - }; - const applyEncryption = async (sendToServer: boolean) => { + } + const isPassphraseValid = async () => { if (this.editingSettings.encrypt && this.editingSettings.passphrase == "") { Logger("If you enable encryption, you have to set the passphrase", LOG_LEVEL_NOTICE); - return; + return false; } if (this.editingSettings.encrypt && !(await testCrypt())) { - Logger("WARNING! Your device does not support encryption.", LOG_LEVEL_NOTICE); - return; + Logger("Your device does not support encryption.", LOG_LEVEL_NOTICE); + return false; } - if (!(await checkWorkingPassphrase()) && !sendToServer) { - return; - } - if (!this.editingSettings.encrypt) { - this.editingSettings.passphrase = ""; - } - // this.applyAllSettings(); - this.saveAllDirtySettings(); - this.plugin.addOnSetup.suspendAllSync(); - this.plugin.addOnSetup.suspendExtraSync(); - this.reloadAllSettings(); - // this.editingSettings.encrypt = encrypt; - // this.editingSettings.passphrase = passphrase; - // this.editingSettings.useDynamicIterationCount = useDynamicIterationCount; - // this.editingSettings.usePathObfuscation = usePathObfuscation; - this.editingSettings.isConfigured = true; - await this.saveAllDirtySettings(); - if (sendToServer) { - await this.plugin.addOnSetup.rebuildRemote() - } else { - await this.plugin.markRemoteResolved(); - await this.plugin.replicate(true); - } - }; + return true; + } const rebuildDB = async (method: "localOnly" | "remoteOnly" | "rebuildBothByThisDevice" | "localOnlyWithChunks") => { if (this.editingSettings.encrypt && this.editingSettings.passphrase == "") { @@ -1342,1274 +765,1810 @@ However, your report is needed to stabilise this. I appreciate you for your grea this.closeSetting(); await delay(2000); await performRebuildDB(this.plugin, method); - // this.resetEditingSettings(); } + // Panes + addPane(containerEl, "Update Information", "💬", 100, false).then((paneEl) => { + const informationDivEl = this.createEl(paneEl, "div", { text: "" }); - new Setting(containerRemoteDatabaseEl) - .setClass("wizardOnly") - .addButton((button) => - button - .setButtonText("Next") - .setCta() - .setDisabled(false) - .onClick(() => { - if (!this.editingSettings.encrypt) { - this.editingSettings.passphrase = ""; - } - if (isCloudantURI(this.editingSettings.couchDB_URI)) { - this.editingSettings = { ...this.editingSettings, ...PREFERRED_SETTING_CLOUDANT }; - } else if (this.editingSettings.remoteType == REMOTE_MINIO) { - this.editingSettings = { ...this.editingSettings, ...PREFERRED_JOURNAL_SYNC }; - } else { - this.editingSettings = { ...this.editingSettings, ...PREFERRED_SETTING_SELF_HOSTED }; - } - changeDisplay("30") - }) - ) - ; - - addScreenElement("0", containerRemoteDatabaseEl); - - const containerGeneralSettingsEl = containerEl.createDiv(); - this.createEl(containerGeneralSettingsEl, "h3", { text: "General Settings" }); - - this.createEl(containerGeneralSettingsEl, "h4", { text: "Appearance" }); - - - const languages = Object.fromEntries([["", "Default"], ...SUPPORTED_I18N_LANGS.map(e => [e, $t(`lang-${e}`)])]) as Record; - new Setting(containerGeneralSettingsEl).autoWireDropDown( - "displayLanguage", - { - options: languages - } - ) - this.addOnSaved("displayLanguage", () => this.display()); - new Setting(containerGeneralSettingsEl).autoWireToggle("showStatusOnEditor"); - new Setting(containerGeneralSettingsEl).autoWireToggle("showOnlyIconsOnEditor", - { onUpdate: visibleOnly(() => this.isConfiguredAs("showStatusOnEditor", true)) } - ); - new Setting(containerGeneralSettingsEl).autoWireToggle("showStatusOnStatusbar"); - - this.createEl(containerGeneralSettingsEl, "h4", { text: "Logging" }); - - new Setting(containerGeneralSettingsEl).autoWireToggle("lessInformationInLog"); - - new Setting(containerGeneralSettingsEl) - .autoWireToggle("showVerboseLog", { onUpdate: visibleOnly(() => this.isConfiguredAs("lessInformationInLog", false)) }); - - this.createEl(containerGeneralSettingsEl, "h4", { text: "Performance tweaks" }); - - new Setting(containerGeneralSettingsEl) - .autoWireNumeric("hashCacheMaxCount", { clampMin: 10 }); - new Setting(containerGeneralSettingsEl) - .autoWireNumeric("hashCacheMaxAmount", { clampMin: 1 }); - - this.createEl(containerGeneralSettingsEl, "h4", { text: "Share settings via markdown" }); - new Setting(containerGeneralSettingsEl) - .autoWireText("settingSyncFile", { holdValue: true }) - .addApplyButton(["settingSyncFile"]) - - new Setting(containerGeneralSettingsEl) - .autoWireToggle("writeCredentialsForSettingSync"); - - new Setting(containerGeneralSettingsEl) - .autoWireToggle("notifyAllSettingSyncFile") - - this.createEl(containerGeneralSettingsEl, "h4", { text: "Advanced Confidentiality" }); - - const passphrase_options: Record = { - "": "Default", - LOCALSTORAGE: "Use a custom passphrase", - ASK_AT_LAUNCH: "Ask an passphrase at every launch", - } - - new Setting(containerGeneralSettingsEl) - .setName("Encrypting sensitive configuration items") - .autoWireDropDown("configPassphraseStore", { options: passphrase_options, holdValue: true }) - .setClass("wizardHidden"); - - new Setting(containerGeneralSettingsEl) - .autoWireText("configPassphrase", { isPassword: true, holdValue: true }) - .setClass("wizardHidden") - .addOnUpdate(() => ({ - disabled: !this.isConfiguredAs("configPassphraseStore", "LOCALSTORAGE") - })) - new Setting(containerGeneralSettingsEl) - .addApplyButton(["configPassphrase", "configPassphraseStore"]) - .setClass("wizardHidden") - - - addScreenElement("20", containerGeneralSettingsEl); - const containerSyncSettingEl = containerEl.createDiv(); - this.createEl(containerSyncSettingEl, "h3", { text: "Sync Settings" }); - // containerSyncSettingEl.addClass("wizardHidden") - - if (this.editingSettings.versionUpFlash != "") { - const c = this.createEl(containerSyncSettingEl, "div", { - text: this.editingSettings.versionUpFlash, - cls: "op-warn sls-setting-hidden" - }, el => { - this.createEl(el, "button", { text: "I got it and updated." }, (e) => { - e.addClass("mod-cta"); - e.addEventListener("click", async () => { - this.editingSettings.versionUpFlash = ""; - await this.saveAllDirtySettings(); - c.remove(); - }); - }) - }, visibleOnly(() => !this.isConfiguredAs("versionUpFlash", ""))); - } - - this.createEl(containerSyncSettingEl, "div", - { - text: `Please select any preset to complete the wizard.`, - cls: "wizardOnly" - } - ).addClasses(["op-warn-info"]); - - - - const options: Record = this.editingSettings.remoteType == REMOTE_COUCHDB ? { - NONE: "", - LIVESYNC: "LiveSync", - PERIODIC: "Periodic w/ batch", - DISABLE: "Disable all automatic" - } : { NONE: "", PERIODIC: "Periodic w/ batch", DISABLE: "Disable all automatic" }; - - new Setting(containerSyncSettingEl) - .autoWireDropDown("preset", { - options: options, holdValue: true, - }).addButton(button => { - button.setButtonText("Apply"); - button.onClick(async () => { - await this.saveSettings(["preset"]); - }) - }) - - this.addOnSaved("preset", async (currentPreset) => { - if (currentPreset == "") { - Logger("Select any preset.", LOG_LEVEL_NOTICE); - return; - } - const presetAllDisabled = { - batchSave: false, - liveSync: false, - periodicReplication: false, - syncOnSave: false, - syncOnEditorSave: false, - syncOnStart: false, - syncOnFileOpen: false, - syncAfterMerge: false, - } as Partial; - const presetLiveSync = { - ...presetAllDisabled, - liveSync: true - } as Partial; - const presetPeriodic = { - ...presetAllDisabled, - batchSave: true, - periodicReplication: true, - syncOnSave: false, - syncOnEditorSave: false, - syncOnStart: true, - syncOnFileOpen: true, - syncAfterMerge: true, - } as Partial; - - if (currentPreset == "LIVESYNC") { - this.editingSettings = { - ...this.editingSettings, - ...presetLiveSync - } - Logger("Synchronization setting configured as LiveSync.", LOG_LEVEL_NOTICE); - } else if (currentPreset == "PERIODIC") { - this.editingSettings = { - ...this.editingSettings, - ...presetPeriodic - } - Logger("Synchronization setting configured as Periodic sync with batch database update.", LOG_LEVEL_NOTICE); - } else { - Logger("All synchronizations disabled.", LOG_LEVEL_NOTICE); - this.editingSettings = { - ...this.editingSettings, - ...presetAllDisabled - } - } - await this.saveAllDirtySettings(); - await this.plugin.realizeSettingSyncMode(); - if (inWizard) { - this.closeSetting(); - if (!this.editingSettings.isConfigured) { - this.editingSettings.isConfigured = true; + const tmpDiv = createSpan(); + tmpDiv.addClass("sls-header-button"); + tmpDiv.innerHTML = ``; + if (lastVersion > (this.editingSettings?.lastReadUpdates || 0)) { + const informationButtonDiv = paneEl.appendChild(tmpDiv); + informationButtonDiv.querySelector("button")?.addEventListener("click", async () => { + this.editingSettings.lastReadUpdates = lastVersion; await this.saveAllDirtySettings(); - await rebuildDB("localOnly"); - // this.resetEditingSettings(); - Logger("All done! Please set up subsequent devices with 'Copy current settings as a new setup URI' and 'Use the copied setup URI'.", LOG_LEVEL_NOTICE); - await this.plugin.addOnSetup.command_copySetupURI(); - } else { - this.plugin.askReload(); - } - } - }) - - this.createEl(containerSyncSettingEl, "h4", { text: "Synchronization Methods" }).addClass("wizardHidden"); - - // const onlyOnLiveSync = visibleOnly(() => this.isConfiguredAs("syncMode", "LIVESYNC")); - const onlyOnNonLiveSync = visibleOnly(() => !this.isConfiguredAs("syncMode", "LIVESYNC")); - const onlyOnPeriodic = visibleOnly(() => this.isConfiguredAs("syncMode", "PERIODIC")); - - const optionsSyncMode = this.editingSettings.remoteType == REMOTE_COUCHDB ? { - "ONEVENTS": "On events", - PERIODIC: "Periodic and On events", - "LIVESYNC": "LiveSync" - } : { "ONEVENTS": "On events", PERIODIC: "Periodic and On events" } - - - new Setting(containerSyncSettingEl) - .autoWireDropDown("syncMode", { - //@ts-ignore - options: optionsSyncMode - }) - .setClass("wizardHidden") - this.addOnSaved("syncMode", async (value) => { - // debugger; - this.editingSettings.liveSync = false; - this.editingSettings.periodicReplication = false; - if (value == "LIVESYNC") { - this.editingSettings.liveSync = true; - } else if (value == "PERIODIC") { - this.editingSettings.periodicReplication = true; - } - await this.saveSettings(["liveSync", "periodicReplication"]); - - await this.plugin.realizeSettingSyncMode(); - }) - - new Setting(containerSyncSettingEl) - .autoWireNumeric("periodicReplicationInterval", - { clampMax: 5000, onUpdate: onlyOnPeriodic } - ).setClass("wizardHidden") - - - new Setting(containerSyncSettingEl) - .setClass("wizardHidden") - .autoWireToggle("syncOnSave", { onUpdate: onlyOnNonLiveSync }) - new Setting(containerSyncSettingEl) - .setClass("wizardHidden") - .autoWireToggle("syncOnEditorSave", { onUpdate: onlyOnNonLiveSync }) - new Setting(containerSyncSettingEl) - .setClass("wizardHidden") - .autoWireToggle("syncOnFileOpen", { onUpdate: onlyOnNonLiveSync }) - new Setting(containerSyncSettingEl) - .setClass("wizardHidden") - .autoWireToggle("syncOnStart", { onUpdate: onlyOnNonLiveSync }) - new Setting(containerSyncSettingEl) - .setClass("wizardHidden") - .autoWireToggle("syncAfterMerge", { onUpdate: onlyOnNonLiveSync }) - this.createEl(containerSyncSettingEl, "h4", { text: "Deletions propagation" }).addClass("wizardHidden") - new Setting(containerSyncSettingEl) - .setClass("wizardHidden") - .autoWireToggle("trashInsteadDelete") - - new Setting(containerSyncSettingEl) - .setClass("wizardHidden") - .autoWireToggle("doNotDeleteFolder") - - this.createEl(containerSyncSettingEl, "h4", { text: "Conflict resolution" }).addClass("wizardHidden"); - - new Setting(containerSyncSettingEl) - .setClass("wizardHidden") - .autoWireToggle("resolveConflictsByNewerFile") - - new Setting(containerSyncSettingEl) - .setClass("wizardHidden") - .autoWireToggle("checkConflictOnlyOnOpen") - - new Setting(containerSyncSettingEl) - .setClass("wizardHidden") - .autoWireToggle("showMergeDialogOnlyOnActive") - this.createEl(containerSyncSettingEl, "h4", { text: "Compatibility" }).addClass("wizardHidden"); - new Setting(containerSyncSettingEl) - .setClass("wizardHidden") - .autoWireToggle("disableMarkdownAutoMerge") - new Setting(containerSyncSettingEl) - .setClass("wizardHidden") - .autoWireToggle("writeDocumentsIfConflicted") - - this.createEl(containerSyncSettingEl, "h4", { text: "Hidden files" }).addClass("wizardHidden"); - const LABEL_ENABLED = "🔁 : Enabled"; - const LABEL_DISABLED = "⏹️ : Disabled" - - const hiddenFileSyncSetting = new Setting(containerSyncSettingEl) - .setName("Hidden file synchronization").setClass("wizardHidden") - const hiddenFileSyncSettingEl = hiddenFileSyncSetting.settingEl - const hiddenFileSyncSettingDiv = hiddenFileSyncSettingEl.createDiv(""); - hiddenFileSyncSettingDiv.innerText = this.editingSettings.syncInternalFiles ? LABEL_ENABLED : LABEL_DISABLED; - - if (this.editingSettings.syncInternalFiles) { - new Setting(containerSyncSettingEl) - .setName("Disable Hidden files sync") - .setClass("wizardHidden") - .addButton((button) => { - button.setButtonText("Disable") - .onClick(async () => { - this.editingSettings.syncInternalFiles = false; - await this.saveAllDirtySettings(); - this.display(); - }) - }) - } else { - - new Setting(containerSyncSettingEl) - .setName("Enable Hidden files sync") - .setClass("wizardHidden") - .addButton((button) => { - button.setButtonText("Merge") - .onClick(async () => { - this.closeSetting() - // this.resetEditingSettings(); - await this.plugin.addOnSetup.configureHiddenFileSync("MERGE"); - }) - }) - .addButton((button) => { - button.setButtonText("Fetch") - .onClick(async () => { - this.closeSetting() - // this.resetEditingSettings(); - await this.plugin.addOnSetup.configureHiddenFileSync("FETCH"); - }) - }) - .addButton((button) => { - button.setButtonText("Overwrite") - .onClick(async () => { - this.closeSetting() - // this.resetEditingSettings(); - await this.plugin.addOnSetup.configureHiddenFileSync("OVERWRITE"); - }) + informationButtonDiv.remove(); }); - } - new Setting(containerSyncSettingEl) - .setClass("wizardHidden") - .autoWireToggle("syncInternalFilesBeforeReplication", - { onUpdate: visibleOnly(() => this.isConfiguredAs("watchInternalFileChanges", false)) } - ) - - // } - new Setting(containerSyncSettingEl) - .setClass("wizardHidden") - .autoWireNumeric("syncInternalFilesInterval", { clampMin: 10, acceptZero: true }) - - const defaultSkipPattern = "\\/node_modules\\/, \\/\\.git\\/, ^\\.git\\/, \\/obsidian-livesync\\/"; - const defaultSkipPatternXPlat = defaultSkipPattern + ",\\/workspace$ ,\\/workspace.json$,\\/workspace-mobile.json$"; - - const pat = this.editingSettings.syncInternalFilesIgnorePatterns.split(",").map(x => x.trim()).filter(x => x != ""); - const patSetting = new Setting(containerSyncSettingEl) - .setName("Hidden files ignore patterns") - .setClass("wizardHidden") - .setDesc(""); - - new MultipleRegExpControl( - { - target: patSetting.controlEl, - props: { - patterns: pat, originals: [...pat], apply: async (newPatterns: string[]) => { - this.editingSettings.syncInternalFilesIgnorePatterns = newPatterns.map(e => e.trim()).filter(e => e != "").join(", "); - await this.saveAllDirtySettings(); - this.display(); - } - } } - ) - const addDefaultPatterns = async (patterns: string) => { - const oldList = this.editingSettings.syncInternalFilesIgnorePatterns.split(",").map(x => x.trim()).filter(x => x != ""); - const newList = patterns.split(",").map(x => x.trim()).filter(x => x != ""); - const allSet = new Set([...oldList, ...newList]); - this.editingSettings.syncInternalFilesIgnorePatterns = [...allSet].join(", "); - await this.saveAllDirtySettings(); - this.display(); - } + MarkdownRenderer.render(this.plugin.app, updateInformation, informationDivEl, "/", this.plugin); + }); - new Setting(containerSyncSettingEl) - .setName("Add default patterns") - .setClass("wizardHidden") - .addButton((button) => { - button.setButtonText("Default") - .onClick(async () => { - await addDefaultPatterns(defaultSkipPattern); + addPane(containerEl, "Setup", "🧙‍♂️", 110, false).then((paneEl) => { + addPanel(paneEl, "Quick Setup").then(paneEl => { + new Setting(paneEl) + .setName("Use the copied setup URI") + .setDesc("To setup Self-hosted LiveSync, this method is the most preferred one.") + .addButton((text) => { + text.setButtonText("Use").onClick(async () => { + this.closeSetting(); + await this.plugin.addOnSetup.command_openSetupURI(); + }) }) - }).addButton((button) => { - button.setButtonText("Cross-platform") - .onClick(async () => { - await addDefaultPatterns(defaultSkipPatternXPlat); + + new Setting(paneEl) + .setName("Minimal setup") + .addButton((text) => { + text.setButtonText("Start").onClick(async () => { + this.editingSettings.liveSync = false; + this.editingSettings.periodicReplication = false; + this.editingSettings.syncOnSave = false; + this.editingSettings.syncOnEditorSave = false; + this.editingSettings.syncOnStart = false; + this.editingSettings.syncOnFileOpen = false; + this.editingSettings.syncAfterMerge = false; + this.plugin.replicator.closeReplication(); + await this.saveAllDirtySettings(); + containerEl.addClass("isWizard"); + this.inWizard = true; + changeDisplay("0") + }) }) - }) + new Setting(paneEl) + .setName("Enable LiveSync on this device as the setup was completed manually") + .addOnUpdate(visibleOnly(() => !this.isConfiguredAs("isConfigured", true))) + .addButton((text) => { + text.setButtonText("Enable").onClick(async () => { + this.editingSettings.isConfigured = true; + await this.saveAllDirtySettings(); + this.plugin.askReload(); + }) + }) + }); + + addPanel(paneEl, "To setup the other devices", undefined, visibleOnly(() => this.isConfiguredAs("isConfigured", true))).then(paneEl => { + new Setting(paneEl) + .setName("Copy current settings as a new setup URI") + .addButton((text) => { + text.setButtonText("Copy").onClick(async () => { + await this.plugin.addOnSetup.command_copySetupURI(); + }) + }) + }); + addPanel(paneEl, "Reset").then(paneEl => { - this.createEl(containerSyncSettingEl, "h4", { text: "Performance tweaks" }).addClass("wizardHidden"); - new Setting(containerSyncSettingEl) - .setClass("wizardHidden") - .autoWireToggle("batchSave") - new Setting(containerSyncSettingEl) - .setClass("wizardHidden") - .autoWireNumeric("batchSaveMinimumDelay", - { - acceptZero: true, - onUpdate: visibleOnly(() => this.isConfiguredAs("batchSave", true)) - } - ) - new Setting(containerSyncSettingEl) - .setClass("wizardHidden") - .autoWireNumeric("batchSaveMaximumDelay", - { - acceptZero: true, - onUpdate: visibleOnly(() => this.isConfiguredAs("batchSave", true)) - } - ) - - - - new Setting(containerSyncSettingEl) - .setClass("wizardHidden") - .autoWireNumeric("customChunkSize", { clampMin: 0 }) - - new Setting(containerSyncSettingEl) - .setClass("wizardHidden") - .autoWireToggle("readChunksOnline", { onUpdate: onlyOnCouchDB }) - new Setting(containerSyncSettingEl) - .setClass("wizardHidden") - .autoWireToggle("sendChunksBulk", { onUpdate: onlyOnCouchDB }) - new Setting(containerSyncSettingEl) - .setClass("wizardHidden") - .autoWireNumeric("sendChunksBulkMaxSize", { clampMax: 100, clampMin: 1, onUpdate: onlyOnCouchDB }) - - - new Setting(containerSyncSettingEl) - .setClass("wizardHidden") - .autoWireToggle("enableChunkSplitterV2") - - this.createEl(containerSyncSettingEl, "h4", { - text: sanitizeHTMLToDom(`Targets`), - }).addClass("wizardHidden"); - - const syncFilesSetting = new Setting(containerSyncSettingEl) - .setName("Synchronising files") - .setDesc("(RegExp) Empty to sync all files. Set filter as a regular expression to limit synchronising files.") - .setClass("wizardHidden") - new MultipleRegExpControl( - { - target: syncFilesSetting.controlEl, - props: { - patterns: this.editingSettings.syncOnlyRegEx.split("|[]|"), - originals: [...this.editingSettings.syncOnlyRegEx.split("|[]|")], - apply: async (newPatterns: string[]) => { - this.editingSettings.syncOnlyRegEx = newPatterns.map((e: string) => e.trim()).filter(e => e != "").join("|[]|"); - await this.saveAllDirtySettings(); - this.display(); - } - } - } - ) - - const nonSyncFilesSetting = new Setting(containerSyncSettingEl) - .setName("Non-Synchronising files") - .setDesc("(RegExp) If this is set, any changes to local and remote files that match this will be skipped.") - .setClass("wizardHidden"); - - new MultipleRegExpControl( - { - target: nonSyncFilesSetting.controlEl, - props: { - patterns: this.editingSettings.syncIgnoreRegEx.split("|[]|"), - originals: [...this.editingSettings.syncIgnoreRegEx.split("|[]|")], - apply: async (newPatterns: string[]) => { - this.editingSettings.syncIgnoreRegEx = newPatterns.map(e => e.trim()).filter(e => e != "").join("|[]|"); - await this.saveAllDirtySettings(); - this.display(); - } - } - } - ) - new Setting(containerSyncSettingEl) - .setClass("wizardHidden") - .autoWireNumeric("syncMaxSizeInMB", { clampMin: 0 }) - - new Setting(containerSyncSettingEl) - .setClass("wizardHidden") - .autoWireToggle("useIgnoreFiles") - new Setting(containerSyncSettingEl) - .setClass("wizardHidden") - .autoWireTextArea("ignoreFiles", { onUpdate: visibleOnly(() => this.isConfiguredAs("useIgnoreFiles", true)) }); - - this.createEl(containerSyncSettingEl, "h4", { - text: sanitizeHTMLToDom(`Advanced settings`), - }, undefined, onlyOnCouchDB).addClass("wizardHidden"); - - this.createEl(containerSyncSettingEl, "div", { - text: `If you reached the payload size limit when using IBM Cloudant, please decrease batch size and batch limit to a lower value.`, - }, undefined, onlyOnCouchDB).addClass("wizardHidden"); - - new Setting(containerSyncSettingEl) - .setClass("wizardHidden") - .autoWireNumeric("batch_size", { clampMin: 2, onUpdate: onlyOnCouchDB }) - new Setting(containerSyncSettingEl) - .setClass("wizardHidden") - .autoWireNumeric("batches_limit", { clampMin: 2, onUpdate: onlyOnCouchDB }) - new Setting(containerSyncSettingEl) - .setClass("wizardHidden") - .autoWireToggle("useTimeouts", { onUpdate: onlyOnCouchDB }); - - new Setting(containerSyncSettingEl) - .setClass("wizardHidden") - .autoWireNumeric("concurrencyOfReadChunksOnline", { clampMin: 10, onUpdate: onlyOnCouchDB }) - - new Setting(containerSyncSettingEl) - .setClass("wizardHidden") - .autoWireNumeric("minimumIntervalOfReadChunksOnline", { clampMin: 10, onUpdate: onlyOnCouchDB }) - - addScreenElement("30", containerSyncSettingEl); - const containerHatchEl = containerEl.createDiv(); - - this.createEl(containerHatchEl, "h3", { text: "Hatch" }); - - - new Setting(containerHatchEl) - .setName("Make report to inform the issue") - .addButton((button) => - button - .setButtonText("Make report") - .setDisabled(false) - .onClick(async () => { - let responseConfig: any = {}; - const REDACTED = "𝑅𝐸𝐷𝐴𝐶𝑇𝐸𝐷"; - if (this.editingSettings.remoteType == REMOTE_COUCHDB) { - try { - const r = await requestToCouchDB(this.editingSettings.couchDB_URI, this.editingSettings.couchDB_USER, this.editingSettings.couchDB_PASSWORD, window.origin); - - Logger(JSON.stringify(r.json, null, 2)); - - responseConfig = r.json; - responseConfig["couch_httpd_auth"].secret = REDACTED; - responseConfig["couch_httpd_auth"].authentication_db = REDACTED; - responseConfig["couch_httpd_auth"].authentication_redirect = REDACTED; - responseConfig["couchdb"].uuid = REDACTED; - responseConfig["admins"] = REDACTED; - - } catch (ex) { - responseConfig = "Requesting information from the remote CouchDB has failed. If you are using IBM Cloudant, this is normal behaviour." + new Setting(paneEl) + .setName("Discard existing settings and databases") + .addButton((text) => { + text.setButtonText("Discard").onClick(async () => { + if (await askYesNo(this.plugin.app, "Do you really want to discard existing settings and databases?") == "yes") { + this.editingSettings = { ...this.editingSettings, ...DEFAULT_SETTINGS }; + await this.plugin.saveSettingData(); + await this.plugin.resetLocalDatabase(); + // await this.plugin.initializeDatabase(); + this.plugin.askReload(); } - } else if (this.editingSettings.remoteType == REMOTE_MINIO) { - responseConfig = "Object Storage Synchronisation"; - // + }).setWarning() + }).addOnUpdate(visibleOnly(() => this.isConfiguredAs("isConfigured", true))) + // } + }); + addPanel(paneEl, "Enable extra and advanced features").then((paneEl) => { + new Setting(paneEl) + .autoWireToggle("useAdvancedMode") + + new Setting(paneEl) + .autoWireToggle("usePowerUserMode") + new Setting(paneEl) + .autoWireToggle("useEdgeCaseMode") + + this.addOnSaved("useAdvancedMode", () => this.display()); + this.addOnSaved("usePowerUserMode", () => this.display()); + this.addOnSaved("useEdgeCaseMode", () => this.display()); + }); + addPanel(paneEl, "Online Tips").then((paneEl) => { + // this.createEl(paneEl, "h3", { text: "Online Tips" }); + const repo = "vrtmrz/obsidian-livesync"; + const topPath = "/docs/troubleshooting.md"; + const rawRepoURI = `https://raw.githubusercontent.com/${repo}/main`; + this.createEl(paneEl, "div", "", el => el.innerHTML = `Open in browser`); + const troubleShootEl = this.createEl(paneEl, "div", { text: "", cls: "sls-troubleshoot-preview" }); + const loadMarkdownPage = async (pathAll: string, basePathParam: string = "") => { + troubleShootEl.style.minHeight = troubleShootEl.clientHeight + "px"; + troubleShootEl.empty(); + const fullPath = pathAll.startsWith("/") ? pathAll : `${basePathParam}/${pathAll}`; + + const directoryArr = fullPath.split("/"); + const filename = directoryArr.pop(); + const directly = directoryArr.join("/"); + const basePath = directly; + + let remoteTroubleShootMDSrc = ""; + try { + remoteTroubleShootMDSrc = await request(`${rawRepoURI}${basePath}/${filename}`); + } catch (ex: any) { + remoteTroubleShootMDSrc = "An error occurred!!\n" + ex.toString(); + } + const remoteTroubleShootMD = remoteTroubleShootMDSrc.replace(/\((.*?(.png)|(.jpg))\)/g, `(${rawRepoURI}${basePath}/$1)`) + // Render markdown + await MarkdownRenderer.render(this.plugin.app, ` [Tips and Troubleshooting](${topPath}) [PageTop](${filename})\n\n${remoteTroubleShootMD}`, troubleShootEl, `${rawRepoURI}`, this.plugin); + // Menu + troubleShootEl.querySelector(".sls-troubleshoot-anchor")?.parentElement?.setCssStyles({ + position: "sticky", + top: "-1em", + backgroundColor: "var(--modal-background)" + }); + // Trap internal links. + troubleShootEl.querySelectorAll("a.internal-link").forEach((anchorEl) => { + anchorEl.addEventListener("click", async (evt) => { + const uri = anchorEl.getAttr("data-href"); + if (!uri) return; + if (uri.startsWith("#")) { + evt.preventDefault(); + const elements = Array.from(troubleShootEl.querySelectorAll("[data-heading]")) + const p = elements.find(e => e.getAttr("data-heading")?.toLowerCase().split(" ").join("-") == uri.substring(1).toLowerCase()); + if (p) { + p.setCssStyles({ scrollMargin: "3em" }); + p.scrollIntoView({ behavior: "instant", block: "start" }); + } + } else { + evt.preventDefault(); + await loadMarkdownPage(uri, basePath); + troubleShootEl.setCssStyles({ scrollMargin: "1em" }); + troubleShootEl.scrollIntoView({ behavior: "instant", block: "start" }); + } + }) + }) + troubleShootEl.style.minHeight = ""; + } + loadMarkdownPage(topPath); + }); + }); + addPane(containerEl, "General Settings", "⚙️", 20, false).then((paneEl) => { + addPanel(paneEl, "Appearance").then((paneEl) => { + const languages = Object.fromEntries([["", "Default"], ...SUPPORTED_I18N_LANGS.map(e => [e, $t(`lang-${e}`)])]) as Record; + new Setting(paneEl).autoWireDropDown( + "displayLanguage", + { + options: languages + } + ) + this.addOnSaved("displayLanguage", () => this.display()); + new Setting(paneEl).autoWireToggle("showStatusOnEditor"); + new Setting(paneEl).autoWireToggle("showOnlyIconsOnEditor", + { onUpdate: visibleOnly(() => this.isConfiguredAs("showStatusOnEditor", true)) } + ); + new Setting(paneEl).autoWireToggle("showStatusOnStatusbar"); + }); + addPanel(paneEl, "Logging").then((paneEl) => { + + new Setting(paneEl).autoWireToggle("lessInformationInLog"); + + new Setting(paneEl) + .autoWireToggle("showVerboseLog", { onUpdate: visibleOnly(() => this.isConfiguredAs("lessInformationInLog", false)) }); + }); + + }) + addPane(containerEl, "Remote Configuration", "🛰️", 0, false).then((paneEl) => { + addPanel(paneEl, "Remote Server").then((paneEl) => { + // const containerRemoteDatabaseEl = containerEl.createDiv(); + new Setting(paneEl) + .autoWireDropDown("remoteType", { + holdValue: true, options: { + [REMOTE_COUCHDB]: "CouchDB", [REMOTE_MINIO]: "Minio,S3,R2", + }, onUpdate: enableOnlySyncDisabled + }) + + + + addPanel(paneEl, "Minio,S3,R2", undefined, onlyOnMinIO).then(paneEl => { + + const syncWarnMinio = this.createEl(paneEl, "div", { + text: "" + }); + const ObjectStorageMessage = `Kindly notice: this is a pretty experimental feature, hence we have some limitations. +- Append only architecture. It will not shrink used storage if we do not perform a rebuild. +- A bit fragile. +- During the first synchronization, the entire history to date will be transferred. For this reason, it is preferable to do this while connected to a Wi-Fi network. +- From the second, we always transfer only differences. + +However, your report is needed to stabilise this. I appreciate you for your great dedication. +`; + + MarkdownRenderer.render(this.plugin.app, ObjectStorageMessage, syncWarnMinio, "/", this.plugin); + syncWarnMinio.addClass("op-warn-info"); + + new Setting(paneEl).autoWireText("endpoint", { holdValue: true }) + new Setting(paneEl).autoWireText("accessKey", { holdValue: true }); + + new Setting(paneEl).autoWireText("secretKey", { holdValue: true, isPassword: true }); + + new Setting(paneEl).autoWireText("region", { holdValue: true }); + + new Setting(paneEl).autoWireText("bucket", { holdValue: true }); + + new Setting(paneEl).autoWireToggle("useCustomRequestHandler", { holdValue: true }); + new Setting(paneEl) + .setName("Test Connection") + .addButton((button) => + button + .setButtonText("Test") + .setDisabled(false) + .onClick(async () => { + await this.testConnection(this.editingSettings); + }) + ); + new Setting(paneEl) + .setName("Apply Settings") + .setClass("wizardHidden") + .addApplyButton(["remoteType", "endpoint", "region", "accessKey", "secretKey", "bucket", "useCustomRequestHandler"]) + .addOnUpdate(onlyOnMinIO) + + }); + + + addPanel(paneEl, "CouchDB", undefined, onlyOnCouchDB).then(paneEl => { + if (this.plugin.isMobile) { + this.createEl(paneEl, "div", { + text: `Configured as using non-HTTPS. We cannot connect to the remote. Please set up the credentials and use HTTPS for the remote URI.`, + }, undefined, visibleOnly(() => !this.editingSettings.couchDB_URI.startsWith("https://"))) + .addClass("op-warn"); + } else { + this.createEl(paneEl, "div", { + text: `Configured as using non-HTTPS. We might fail on mobile devices.` + }, undefined, visibleOnly(() => !this.editingSettings.couchDB_URI.startsWith("https://"))) + .addClass("op-warn-info"); + } + + this.createEl(paneEl, "div", { text: `These settings are kept locked while any synchronization options are enabled. Disable these options in the "Sync Settings" tab to unlock.` }, + undefined, visibleOnly(() => isAnySyncEnabled()) + ).addClass("sls-setting-hidden"); + + new Setting(paneEl).autoWireText("couchDB_URI", { holdValue: true, onUpdate: enableOnlySyncDisabled }); + new Setting(paneEl).autoWireText("couchDB_USER", { holdValue: true, onUpdate: enableOnlySyncDisabled }); + new Setting(paneEl).autoWireText("couchDB_PASSWORD", { holdValue: true, isPassword: true, onUpdate: enableOnlySyncDisabled }); + new Setting(paneEl).autoWireText("couchDB_DBNAME", { holdValue: true, onUpdate: enableOnlySyncDisabled }); + + + new Setting(paneEl) + .setName("Test Database Connection") + .setClass("wizardHidden") + .setDesc("Open database connection. If the remote database is not found and you have the privilege to create a database, the database will be created.") + .addButton((button) => + button + .setButtonText("Test") + .setDisabled(false) + .onClick(async () => { + await this.testConnection(); + }) + ); + + new Setting(paneEl) + .setName("Check and fix database configuration") + .setDesc("Check the database configuration, and fix if there are any problems.") + .addButton((button) => + button + .setButtonText("Check") + .setDisabled(false) + .onClick(async () => { + const checkConfig = async () => { + Logger(`Checking database configuration`, LOG_LEVEL_INFO); + + const emptyDiv = createDiv(); + emptyDiv.innerHTML = ""; + checkResultDiv.replaceChildren(...[emptyDiv]); + const addResult = (msg: string, classes?: string[]) => { + const tmpDiv = createDiv(); + tmpDiv.addClass("ob-btn-config-fix"); + if (classes) { + tmpDiv.addClasses(classes); + } + tmpDiv.innerHTML = `${msg}`; + checkResultDiv.appendChild(tmpDiv); + }; + try { + + if (isCloudantURI(this.editingSettings.couchDB_URI)) { + Logger("This feature cannot be used with IBM Cloudant.", LOG_LEVEL_NOTICE); + return; + } + const r = await requestToCouchDB(this.editingSettings.couchDB_URI, this.editingSettings.couchDB_USER, this.editingSettings.couchDB_PASSWORD, window.origin); + const responseConfig = r.json; + + const addConfigFixButton = (title: string, key: string, value: string) => { + const tmpDiv = createDiv(); + tmpDiv.addClass("ob-btn-config-fix"); + tmpDiv.innerHTML = ``; + const x = checkResultDiv.appendChild(tmpDiv); + x.querySelector("button")?.addEventListener("click", async () => { + Logger(`CouchDB Configuration: ${title} -> Set ${key} to ${value}`) + const res = await requestToCouchDB(this.editingSettings.couchDB_URI, this.editingSettings.couchDB_USER, this.editingSettings.couchDB_PASSWORD, undefined, key, value); + if (res.status == 200) { + Logger(`CouchDB Configuration: ${title} successfully updated`, LOG_LEVEL_NOTICE); + checkResultDiv.removeChild(x); + checkConfig(); + } else { + Logger(`CouchDB Configuration: ${title} failed`, LOG_LEVEL_NOTICE); + Logger(res.text, LOG_LEVEL_VERBOSE); + } + }); + }; + addResult("---Notice---", ["ob-btn-config-head"]); + addResult( + "If the server configuration is not persistent (e.g., running on docker), the values set from here will also be volatile. Once you are able to connect, please reflect the settings in the server's local.ini.", + ["ob-btn-config-info"] + ); + + addResult("--Config check--", ["ob-btn-config-head"]); + + // Admin check + // for database creation and deletion + if (!(this.editingSettings.couchDB_USER in responseConfig.admins)) { + addResult(`⚠ You do not have administrative privileges.`); + } else { + addResult("✔ You have administrative privileges."); + } + // HTTP user-authorization check + if (responseConfig?.chttpd?.require_valid_user != "true") { + addResult("❗ chttpd.require_valid_user is wrong."); + addConfigFixButton("Set chttpd.require_valid_user = true", "chttpd/require_valid_user", "true"); + } else { + addResult("✔ chttpd.require_valid_user is ok."); + } + if (responseConfig?.chttpd_auth?.require_valid_user != "true") { + addResult("❗ chttpd_auth.require_valid_user is wrong."); + addConfigFixButton("Set chttpd_auth.require_valid_user = true", "chttpd_auth/require_valid_user", "true"); + } else { + addResult("✔ chttpd_auth.require_valid_user is ok."); + } + // HTTPD check + // Check Authentication header + if (!responseConfig?.httpd["WWW-Authenticate"]) { + addResult("❗ httpd.WWW-Authenticate is missing"); + addConfigFixButton("Set httpd.WWW-Authenticate", "httpd/WWW-Authenticate", 'Basic realm="couchdb"'); + } else { + addResult("✔ httpd.WWW-Authenticate is ok."); + } + if (responseConfig?.httpd?.enable_cors != "true") { + addResult("❗ httpd.enable_cors is wrong"); + addConfigFixButton("Set httpd.enable_cors", "httpd/enable_cors", "true"); + } else { + addResult("✔ httpd.enable_cors is ok."); + } + // If the server is not cloudant, configure request size + if (!isCloudantURI(this.editingSettings.couchDB_URI)) { + // REQUEST SIZE + if (Number(responseConfig?.chttpd?.max_http_request_size ?? 0) < 4294967296) { + addResult("❗ chttpd.max_http_request_size is low)"); + addConfigFixButton("Set chttpd.max_http_request_size", "chttpd/max_http_request_size", "4294967296"); + } else { + addResult("✔ chttpd.max_http_request_size is ok."); + } + if (Number(responseConfig?.couchdb?.max_document_size ?? 0) < 50000000) { + addResult("❗ couchdb.max_document_size is low)"); + addConfigFixButton("Set couchdb.max_document_size", "couchdb/max_document_size", "50000000"); + } else { + addResult("✔ couchdb.max_document_size is ok."); + } + } + // CORS check + // checking connectivity for mobile + if (responseConfig?.cors?.credentials != "true") { + addResult("❗ cors.credentials is wrong"); + addConfigFixButton("Set cors.credentials", "cors/credentials", "true"); + } else { + addResult("✔ cors.credentials is ok."); + } + const ConfiguredOrigins = ((responseConfig?.cors?.origins ?? "") + "").split(","); + if ( + responseConfig?.cors?.origins == "*" || + (ConfiguredOrigins.indexOf("app://obsidian.md") !== -1 && ConfiguredOrigins.indexOf("capacitor://localhost") !== -1 && ConfiguredOrigins.indexOf("http://localhost") !== -1) + ) { + addResult("✔ cors.origins is ok."); + } else { + addResult("❗ cors.origins is wrong"); + addConfigFixButton("Set cors.origins", "cors/origins", "app://obsidian.md,capacitor://localhost,http://localhost"); + } + addResult("--Connection check--", ["ob-btn-config-head"]); + addResult(`Current origin:${window.location.origin}`); + + // Request header check + const origins = ["app://obsidian.md", "capacitor://localhost", "http://localhost"]; + for (const org of origins) { + const rr = await requestToCouchDB(this.editingSettings.couchDB_URI, this.editingSettings.couchDB_USER, this.editingSettings.couchDB_PASSWORD, org); + const responseHeaders = Object.fromEntries(Object.entries(rr.headers) + .map((e) => { + e[0] = `${e[0]}`.toLowerCase(); + return e; + })); + addResult(`Origin check:${org}`); + if (responseHeaders["access-control-allow-credentials"] != "true") { + addResult("❗ CORS is not allowing credentials"); + } else { + addResult("✔ CORS credentials OK"); + } + if (responseHeaders["access-control-allow-origin"] != org) { + addResult(`❗ CORS Origin is unmatched:${origin}->${responseHeaders["access-control-allow-origin"]}`); + } else { + addResult("✔ CORS origin OK"); + } + } + addResult("--Done--", ["ob-btn-config-head"]); + addResult("If you have some trouble with Connection-check even though all Config-check has been passed, please check your reverse proxy's configuration.", ["ob-btn-config-info"]); + Logger(`Checking configuration done`, LOG_LEVEL_INFO); + } catch (ex: any) { + if (ex?.status == 401) { + addResult(`❗ Access forbidden.`); + addResult(`We could not continue the test.`); + Logger(`Checking configuration done`, LOG_LEVEL_INFO); + } else { + Logger(`Checking configuration failed`, LOG_LEVEL_NOTICE); + Logger(ex); + } + } + }; + await checkConfig(); + }) + ); + const checkResultDiv = this.createEl(paneEl, "div", { + text: "", + }); + + new Setting(paneEl) + .setName("Apply Settings") + .setClass("wizardHidden") + .addApplyButton(["remoteType", "couchDB_URI", "couchDB_USER", "couchDB_PASSWORD", "couchDB_DBNAME"]) + .addOnUpdate(onlyOnCouchDB) + }); + }); + addPanel(paneEl, "Notification").then((paneEl) => { + paneEl.addClass("wizardHidden") + new Setting(paneEl).autoWireNumeric("notifyThresholdOfRemoteStorageSize", {}).setClass("wizardHidden"); + }); + + addPanel(paneEl, "Confidentiality").then((paneEl) => { + + new Setting(paneEl) + .autoWireToggle("encrypt", { holdValue: true }) + + const isEncryptEnabled = visibleOnly(() => this.isConfiguredAs("encrypt", true)) + + new Setting(paneEl) + .autoWireText("passphrase", { holdValue: true, isPassword: true, onUpdate: isEncryptEnabled }) + + new Setting(paneEl) + .autoWireToggle("usePathObfuscation", { holdValue: true, onUpdate: isEncryptEnabled }) + new Setting(paneEl) + .autoWireToggle("useDynamicIterationCount", { holdValue: true, onUpdate: isEncryptEnabled }).setClass("wizardHidden"); + + }); + new Setting(paneEl) + .setClass("wizardOnly") + .addButton((button) => + button + .setButtonText("Next") + .setCta() + .setDisabled(false) + .onClick(() => { + if (!this.editingSettings.encrypt) { + this.editingSettings.passphrase = ""; + } + if (isCloudantURI(this.editingSettings.couchDB_URI)) { + this.editingSettings = { ...this.editingSettings, ...PREFERRED_SETTING_CLOUDANT }; + } else if (this.editingSettings.remoteType == REMOTE_MINIO) { + this.editingSettings = { ...this.editingSettings, ...PREFERRED_JOURNAL_SYNC }; + } else { + this.editingSettings = { ...this.editingSettings, ...PREFERRED_SETTING_SELF_HOSTED }; + } + changeDisplay("30") + }) + ) + ; + }); + addPane(containerEl, "Sync Settings", "🔄", 30, false).then((paneEl) => { + if (this.editingSettings.versionUpFlash != "") { + const c = this.createEl(paneEl, "div", { + text: this.editingSettings.versionUpFlash, + cls: "op-warn sls-setting-hidden" + }, el => { + this.createEl(el, "button", { text: "I got it and updated." }, (e) => { + e.addClass("mod-cta"); + e.addEventListener("click", async () => { + this.editingSettings.versionUpFlash = ""; + await this.saveAllDirtySettings(); + c.remove(); + }); + }) + }, visibleOnly(() => !this.isConfiguredAs("versionUpFlash", ""))); + } + + this.createEl(paneEl, "div", + { + text: `Please select any preset to complete the wizard.`, + cls: "wizardOnly" + } + ).addClasses(["op-warn-info"]); + + + addPanel(paneEl, "Synchronization Preset").then((paneEl) => { + + const options: Record = this.editingSettings.remoteType == REMOTE_COUCHDB ? { + NONE: "", + LIVESYNC: "LiveSync", + PERIODIC: "Periodic w/ batch", + DISABLE: "Disable all automatic" + } : { NONE: "", PERIODIC: "Periodic w/ batch", DISABLE: "Disable all automatic" }; + + new Setting(paneEl) + .autoWireDropDown("preset", { + options: options, holdValue: true, + }).addButton(button => { + button.setButtonText("Apply"); + button.onClick(async () => { + await this.saveSettings(["preset"]); + }) + }) + + this.addOnSaved("preset", async (currentPreset) => { + if (currentPreset == "") { + Logger("Select any preset.", LOG_LEVEL_NOTICE); + return; + } + const presetAllDisabled = { + batchSave: false, + liveSync: false, + periodicReplication: false, + syncOnSave: false, + syncOnEditorSave: false, + syncOnStart: false, + syncOnFileOpen: false, + syncAfterMerge: false, + } as Partial; + const presetLiveSync = { + ...presetAllDisabled, + liveSync: true + } as Partial; + const presetPeriodic = { + ...presetAllDisabled, + batchSave: true, + periodicReplication: true, + syncOnSave: false, + syncOnEditorSave: false, + syncOnStart: true, + syncOnFileOpen: true, + syncAfterMerge: true, + } as Partial; + + if (currentPreset == "LIVESYNC") { + this.editingSettings = { + ...this.editingSettings, + ...presetLiveSync } - const pluginConfig = JSON.parse(JSON.stringify(this.editingSettings)) as ObsidianLiveSyncSettings; - pluginConfig.couchDB_DBNAME = REDACTED; - pluginConfig.couchDB_PASSWORD = REDACTED; - const scheme = pluginConfig.couchDB_URI.startsWith("http:") ? "(HTTP)" : (pluginConfig.couchDB_URI.startsWith("https:")) ? "(HTTPS)" : "" - pluginConfig.couchDB_URI = isCloudantURI(pluginConfig.couchDB_URI) ? "cloudant" : `self-hosted${scheme}`; - pluginConfig.couchDB_USER = REDACTED; - pluginConfig.passphrase = REDACTED; - pluginConfig.encryptedPassphrase = REDACTED; - pluginConfig.encryptedCouchDBConnection = REDACTED; - pluginConfig.accessKey = REDACTED; - pluginConfig.secretKey = REDACTED; - pluginConfig.region = `${REDACTED}(${pluginConfig.region.length} letters)`; - pluginConfig.bucket = `${REDACTED}(${pluginConfig.bucket.length} letters)`; - pluginConfig.pluginSyncExtendedSetting = {}; - const endpoint = pluginConfig.endpoint; - if (endpoint == "") { - pluginConfig.endpoint = "Not configured or AWS"; + Logger("Synchronization setting configured as LiveSync.", LOG_LEVEL_NOTICE); + } else if (currentPreset == "PERIODIC") { + this.editingSettings = { + ...this.editingSettings, + ...presetPeriodic + } + Logger("Synchronization setting configured as Periodic sync with batch database update.", LOG_LEVEL_NOTICE); + } else { + Logger("All synchronizations disabled.", LOG_LEVEL_NOTICE); + this.editingSettings = { + ...this.editingSettings, + ...presetAllDisabled + } + } + + if (this.inWizard) { + this.closeSetting(); + this.inWizard = false; + if (!this.editingSettings.isConfigured) { + this.editingSettings.isConfigured = true; + await this.saveAllDirtySettings(); + await this.plugin.realizeSettingSyncMode(); + await rebuildDB("localOnly"); + // this.resetEditingSettings(); + Logger("All done! Please set up subsequent devices with 'Copy current settings as a new setup URI' and 'Use the copied setup URI'.", LOG_LEVEL_NOTICE); + await this.plugin.addOnSetup.command_copySetupURI(); } else { - const endpointScheme = pluginConfig.endpoint.startsWith("http:") ? "(HTTP)" : (pluginConfig.endpoint.startsWith("https:")) ? "(HTTPS)" : ""; - pluginConfig.endpoint = `${endpoint.indexOf(".r2.cloudflarestorage.") !== -1 ? "R2" : "self-hosted?"}(${endpointScheme})`; + if (isNeedRebuildLocal() || isNeedRebuildRemote()) { + await confirmRebuild(); + } else { + await this.saveAllDirtySettings(); + await this.plugin.realizeSettingSyncMode(); + this.plugin.askReload(); + } } - const obsidianInfo = `Navigator: ${navigator.userAgent} + } else { + await this.saveAllDirtySettings(); + await this.plugin.realizeSettingSyncMode(); + } + }) + + }); + addPanel(paneEl, "Synchronization Methods").then((paneEl) => { + paneEl.addClass("wizardHidden"); + + // const onlyOnLiveSync = visibleOnly(() => this.isConfiguredAs("syncMode", "LIVESYNC")); + const onlyOnNonLiveSync = visibleOnly(() => !this.isConfiguredAs("syncMode", "LIVESYNC")); + const onlyOnPeriodic = visibleOnly(() => this.isConfiguredAs("syncMode", "PERIODIC")); + + const optionsSyncMode = this.editingSettings.remoteType == REMOTE_COUCHDB ? { + "ONEVENTS": "On events", + PERIODIC: "Periodic and On events", + "LIVESYNC": "LiveSync" + } : { "ONEVENTS": "On events", PERIODIC: "Periodic and On events" } + + + new Setting(paneEl) + .autoWireDropDown("syncMode", { + //@ts-ignore + options: optionsSyncMode + }) + .setClass("wizardHidden") + this.addOnSaved("syncMode", async (value) => { + this.editingSettings.liveSync = false; + this.editingSettings.periodicReplication = false; + if (value == "LIVESYNC") { + this.editingSettings.liveSync = true; + } else if (value == "PERIODIC") { + this.editingSettings.periodicReplication = true; + } + await this.saveSettings(["liveSync", "periodicReplication"]); + + await this.plugin.realizeSettingSyncMode(); + }) + + + new Setting(paneEl) + .autoWireNumeric("periodicReplicationInterval", + { clampMax: 5000, onUpdate: onlyOnPeriodic } + ).setClass("wizardHidden") + + + new Setting(paneEl) + .setClass("wizardHidden") + .autoWireToggle("syncOnSave", { onUpdate: onlyOnNonLiveSync }) + new Setting(paneEl) + .setClass("wizardHidden") + .autoWireToggle("syncOnEditorSave", { onUpdate: onlyOnNonLiveSync }) + new Setting(paneEl) + .setClass("wizardHidden") + .autoWireToggle("syncOnFileOpen", { onUpdate: onlyOnNonLiveSync }) + new Setting(paneEl) + .setClass("wizardHidden") + .autoWireToggle("syncOnStart", { onUpdate: onlyOnNonLiveSync }) + new Setting(paneEl) + .setClass("wizardHidden") + .autoWireToggle("syncAfterMerge", { onUpdate: onlyOnNonLiveSync }) + + }); + + addPanel(paneEl, "Update thinning").then((paneEl) => { + paneEl.addClass("wizardHidden"); + new Setting(paneEl) + .setClass("wizardHidden") + .autoWireToggle("batchSave") + new Setting(paneEl) + .setClass("wizardHidden") + .autoWireNumeric("batchSaveMinimumDelay", + { + acceptZero: true, + onUpdate: visibleOnly(() => this.isConfiguredAs("batchSave", true)) + } + ) + new Setting(paneEl) + .setClass("wizardHidden") + .autoWireNumeric("batchSaveMaximumDelay", + { + acceptZero: true, + onUpdate: visibleOnly(() => this.isConfiguredAs("batchSave", true)) + } + ) + }); + + addPanel(paneEl, "Deletion Propagation", undefined, undefined, LEVEL_ADVANCED).then((paneEl) => { + paneEl.addClass("wizardHidden"); + new Setting(paneEl) + .setClass("wizardHidden") + .autoWireToggle("trashInsteadDelete") + + new Setting(paneEl) + .setClass("wizardHidden") + .autoWireToggle("doNotDeleteFolder") + }); + addPanel(paneEl, "Conflict resolution", undefined, undefined, LEVEL_ADVANCED).then((paneEl) => { + + paneEl.addClass("wizardHidden"); + + new Setting(paneEl) + .setClass("wizardHidden") + .autoWireToggle("resolveConflictsByNewerFile") + + new Setting(paneEl) + .setClass("wizardHidden") + .autoWireToggle("checkConflictOnlyOnOpen") + + new Setting(paneEl) + .setClass("wizardHidden") + .autoWireToggle("showMergeDialogOnlyOnActive") + }); + + addPanel(paneEl, "Sync settings via markdown", undefined, undefined, LEVEL_ADVANCED).then((paneEl) => { + new Setting(paneEl) + .autoWireText("settingSyncFile", { holdValue: true }) + .addApplyButton(["settingSyncFile"]) + + new Setting(paneEl) + .autoWireToggle("writeCredentialsForSettingSync"); + + new Setting(paneEl) + .autoWireToggle("notifyAllSettingSyncFile") + }); + + addPanel(paneEl, "Hidden files", undefined, undefined, LEVEL_ADVANCED).then((paneEl) => { + paneEl.addClass("wizardHidden"); + + + const LABEL_ENABLED = "🔁 : Enabled"; + const LABEL_DISABLED = "⏹️ : Disabled" + + const hiddenFileSyncSetting = new Setting(paneEl) + .setName("Hidden file synchronization").setClass("wizardHidden") + const hiddenFileSyncSettingEl = hiddenFileSyncSetting.settingEl + const hiddenFileSyncSettingDiv = hiddenFileSyncSettingEl.createDiv(""); + hiddenFileSyncSettingDiv.innerText = this.editingSettings.syncInternalFiles ? LABEL_ENABLED : LABEL_DISABLED; + + if (this.editingSettings.syncInternalFiles) { + new Setting(paneEl) + .setName("Disable Hidden files sync") + .setClass("wizardHidden") + .addButton((button) => { + button.setButtonText("Disable") + .onClick(async () => { + this.editingSettings.syncInternalFiles = false; + await this.saveAllDirtySettings(); + this.display(); + }) + }) + } else { + + new Setting(paneEl) + .setName("Enable Hidden files sync") + .setClass("wizardHidden") + .addButton((button) => { + button.setButtonText("Merge") + .onClick(async () => { + this.closeSetting() + // this.resetEditingSettings(); + await this.plugin.addOnSetup.configureHiddenFileSync("MERGE"); + }) + }) + .addButton((button) => { + button.setButtonText("Fetch") + .onClick(async () => { + this.closeSetting() + // this.resetEditingSettings(); + await this.plugin.addOnSetup.configureHiddenFileSync("FETCH"); + }) + }) + .addButton((button) => { + button.setButtonText("Overwrite") + .onClick(async () => { + this.closeSetting() + // this.resetEditingSettings(); + await this.plugin.addOnSetup.configureHiddenFileSync("OVERWRITE"); + }) + }); + } + + new Setting(paneEl) + .setClass("wizardHidden") + .autoWireToggle("syncInternalFilesBeforeReplication", + { onUpdate: visibleOnly(() => this.isConfiguredAs("watchInternalFileChanges", false)) } + ) + + new Setting(paneEl) + .setClass("wizardHidden") + .autoWireNumeric("syncInternalFilesInterval", { clampMin: 10, acceptZero: true }) + + }); + + }); + addPane(containerEl, "Selector", "🚦", 33, false, LEVEL_ADVANCED).then((paneEl) => { + addPanel(paneEl, "Normal Files").then((paneEl) => { + paneEl.addClass("wizardHidden"); + + const syncFilesSetting = new Setting(paneEl) + .setName("Synchronising files") + .setDesc("(RegExp) Empty to sync all files. Set filter as a regular expression to limit synchronising files.") + .setClass("wizardHidden") + new MultipleRegExpControl( + { + target: syncFilesSetting.controlEl, + props: { + patterns: this.editingSettings.syncOnlyRegEx.split("|[]|"), + originals: [...this.editingSettings.syncOnlyRegEx.split("|[]|")], + apply: async (newPatterns: string[]) => { + this.editingSettings.syncOnlyRegEx = newPatterns.map((e: string) => e.trim()).filter(e => e != "").join("|[]|"); + await this.saveAllDirtySettings(); + this.display(); + } + } + } + ) + + const nonSyncFilesSetting = new Setting(paneEl) + .setName("Non-Synchronising files") + .setDesc("(RegExp) If this is set, any changes to local and remote files that match this will be skipped.") + .setClass("wizardHidden"); + + new MultipleRegExpControl( + { + target: nonSyncFilesSetting.controlEl, + props: { + patterns: this.editingSettings.syncIgnoreRegEx.split("|[]|"), + originals: [...this.editingSettings.syncIgnoreRegEx.split("|[]|")], + apply: async (newPatterns: string[]) => { + this.editingSettings.syncIgnoreRegEx = newPatterns.map(e => e.trim()).filter(e => e != "").join("|[]|"); + await this.saveAllDirtySettings(); + this.display(); + } + } + } + ) + new Setting(paneEl) + .setClass("wizardHidden") + .autoWireNumeric("syncMaxSizeInMB", { clampMin: 0 }) + + new Setting(paneEl) + .setClass("wizardHidden") + .autoWireToggle("useIgnoreFiles") + new Setting(paneEl) + .setClass("wizardHidden") + .autoWireTextArea("ignoreFiles", { onUpdate: visibleOnly(() => this.isConfiguredAs("useIgnoreFiles", true)) }); + }); + addPanel(paneEl, "Hidden Files", undefined, undefined, LEVEL_ADVANCED).then((paneEl) => { + + const defaultSkipPattern = "\\/node_modules\\/, \\/\\.git\\/, ^\\.git\\/, \\/obsidian-livesync\\/"; + const defaultSkipPatternXPlat = defaultSkipPattern + ",\\/workspace$ ,\\/workspace.json$,\\/workspace-mobile.json$"; + + const pat = this.editingSettings.syncInternalFilesIgnorePatterns.split(",").map(x => x.trim()).filter(x => x != ""); + const patSetting = new Setting(paneEl) + .setName("Ignore patterns") + .setClass("wizardHidden") + .setDesc(""); + + new MultipleRegExpControl( + { + target: patSetting.controlEl, + props: { + patterns: pat, originals: [...pat], apply: async (newPatterns: string[]) => { + this.editingSettings.syncInternalFilesIgnorePatterns = newPatterns.map(e => e.trim()).filter(e => e != "").join(", "); + await this.saveAllDirtySettings(); + this.display(); + } + } + } + ) + + const addDefaultPatterns = async (patterns: string) => { + const oldList = this.editingSettings.syncInternalFilesIgnorePatterns.split(",").map(x => x.trim()).filter(x => x != ""); + const newList = patterns.split(",").map(x => x.trim()).filter(x => x != ""); + const allSet = new Set([...oldList, ...newList]); + this.editingSettings.syncInternalFilesIgnorePatterns = [...allSet].join(", "); + await this.saveAllDirtySettings(); + this.display(); + } + + new Setting(paneEl) + .setName("Add default patterns") + .setClass("wizardHidden") + .addButton((button) => { + button.setButtonText("Default") + .onClick(async () => { + await addDefaultPatterns(defaultSkipPattern); + }) + }).addButton((button) => { + button.setButtonText("Cross-platform") + .onClick(async () => { + await addDefaultPatterns(defaultSkipPatternXPlat); + }) + }) + }); + }); + + addPane(containerEl, "Customization sync", "🔌", 60, false, LEVEL_ADVANCED).then((paneEl) => { + // With great respect, thank you TfTHacker! + // Refer: https://github.com/TfTHacker/obsidian42-brat/blob/main/src/features/BetaPlugins.ts + addPanel(paneEl, "Customization Sync").then((paneEl) => { + const enableOnlyOnPluginSyncIsNotEnabled = enableOnly(() => this.isConfiguredAs("usePluginSync", false)); + const visibleOnlyOnPluginSyncEnabled = visibleOnly(() => this.isConfiguredAs("usePluginSync", true)); + + new Setting(paneEl) + .autoWireText("deviceAndVaultName", { + placeHolder: "desktop", + onUpdate: enableOnlyOnPluginSyncIsNotEnabled + }); + + new Setting(paneEl) + .autoWireToggle("usePluginSyncV2") + + new Setting(paneEl) + .autoWireToggle("usePluginSync", { + onUpdate: enableOnly(() => !this.isConfiguredAs("deviceAndVaultName", "")) + }); + + new Setting(paneEl) + .autoWireToggle("autoSweepPlugins", { + onUpdate: visibleOnlyOnPluginSyncEnabled + }) + + new Setting(paneEl) + .autoWireToggle("autoSweepPluginsPeriodic", { + onUpdate: visibleOnly(() => this.isConfiguredAs("usePluginSync", true) && this.isConfiguredAs("autoSweepPlugins", true)) + }) + new Setting(paneEl) + .autoWireToggle("notifyPluginOrSettingUpdated", { + onUpdate: visibleOnlyOnPluginSyncEnabled + }) + + new Setting(paneEl) + .setName("Open") + .setDesc("Open the dialog") + .addButton((button) => { + button + .setButtonText("Open") + .setDisabled(false) + .onClick(() => { + this.plugin.addOnConfigSync.showPluginSyncModal(); + }); + }) + .addOnUpdate(visibleOnlyOnPluginSyncEnabled); + }); + }); + + + + addPane(containerEl, "Hatch", "🧰", 50, false).then((paneEl) => { + // const hatchWarn = this.createEl(paneEl, "div", { text: `To stop the boot up sequence for fixing problems on databases, you can put redflag.md on top of your vault (Rebooting obsidian is required).` }); + // hatchWarn.addClass("op-warn-info"); + addPanel(paneEl, "Reporting Issue").then((paneEl) => { + new Setting(paneEl) + .setName("Make report to inform the issue") + .addButton((button) => + button + .setButtonText("Make report") + .setCta() + .setDisabled(false) + .onClick(async () => { + let responseConfig: any = {}; + const REDACTED = "𝑅𝐸𝐷𝐴𝐶𝑇𝐸𝐷"; + if (this.editingSettings.remoteType == REMOTE_COUCHDB) { + try { + const r = await requestToCouchDB(this.editingSettings.couchDB_URI, this.editingSettings.couchDB_USER, this.editingSettings.couchDB_PASSWORD, window.origin); + + Logger(JSON.stringify(r.json, null, 2)); + + responseConfig = r.json; + responseConfig["couch_httpd_auth"].secret = REDACTED; + responseConfig["couch_httpd_auth"].authentication_db = REDACTED; + responseConfig["couch_httpd_auth"].authentication_redirect = REDACTED; + responseConfig["couchdb"].uuid = REDACTED; + responseConfig["admins"] = REDACTED; + + } catch (ex) { + responseConfig = "Requesting information from the remote CouchDB has failed. If you are using IBM Cloudant, this is normal behaviour." + } + } else if (this.editingSettings.remoteType == REMOTE_MINIO) { + responseConfig = "Object Storage Synchronisation"; + // + } + const pluginConfig = JSON.parse(JSON.stringify(this.editingSettings)) as ObsidianLiveSyncSettings; + pluginConfig.couchDB_DBNAME = REDACTED; + pluginConfig.couchDB_PASSWORD = REDACTED; + const scheme = pluginConfig.couchDB_URI.startsWith("http:") ? "(HTTP)" : (pluginConfig.couchDB_URI.startsWith("https:")) ? "(HTTPS)" : "" + pluginConfig.couchDB_URI = isCloudantURI(pluginConfig.couchDB_URI) ? "cloudant" : `self-hosted${scheme}`; + pluginConfig.couchDB_USER = REDACTED; + pluginConfig.passphrase = REDACTED; + pluginConfig.encryptedPassphrase = REDACTED; + pluginConfig.encryptedCouchDBConnection = REDACTED; + pluginConfig.accessKey = REDACTED; + pluginConfig.secretKey = REDACTED; + pluginConfig.region = `${REDACTED}(${pluginConfig.region.length} letters)`; + pluginConfig.bucket = `${REDACTED}(${pluginConfig.bucket.length} letters)`; + pluginConfig.pluginSyncExtendedSetting = {}; + const endpoint = pluginConfig.endpoint; + if (endpoint == "") { + pluginConfig.endpoint = "Not configured or AWS"; + } else { + const endpointScheme = pluginConfig.endpoint.startsWith("http:") ? "(HTTP)" : (pluginConfig.endpoint.startsWith("https:")) ? "(HTTPS)" : ""; + pluginConfig.endpoint = `${endpoint.indexOf(".r2.cloudflarestorage.") !== -1 ? "R2" : "self-hosted?"}(${endpointScheme})`; + } + const obsidianInfo = `Navigator: ${navigator.userAgent} FileSystem: ${this.plugin.vaultAccess.isStorageInsensitive() ? "insensitive" : "sensitive"}`; - const msgConfig = `---- Obsidian info ---- + const msgConfig = `---- Obsidian info ---- ${obsidianInfo} ---- remote config ---- ${stringifyYaml(responseConfig)} ---- Plug-in config --- version:${manifestVersion} ${stringifyYaml(pluginConfig)}`; - console.log(msgConfig); - await navigator.clipboard.writeText(msgConfig); - Logger(`Information has been copied to clipboard`, LOG_LEVEL_NOTICE); - }) - ); - - if (this.plugin?.replicator?.remoteLockedAndDeviceNotAccepted) { - const c = this.createEl(containerHatchEl, "div", { - text: "To prevent unwanted vault corruption, the remote database has been locked for synchronization, and this device was not marked as 'resolved'. It caused by some operations like this. Re-initialized. Local database initialization should be required. Please back your vault up, reset the local database, and press 'Mark this device as resolved'. ", + console.log(msgConfig); + await navigator.clipboard.writeText(msgConfig); + Logger(`Information has been copied to clipboard`, LOG_LEVEL_NOTICE); + }) + ); + new Setting(paneEl) + .autoWireToggle("writeLogToTheFile") }); - this.createEl(c, "button", { text: "I'm ready, mark this device 'resolved'" }, (e) => { - e.addClass("mod-warning"); - e.addEventListener("click", async () => { - await this.plugin.markRemoteResolved(); - c.remove(); - }); + + addPanel(paneEl, "Scram Switches").then((paneEl) => { + new Setting(paneEl) + .autoWireToggle("suspendFileWatching") + this.addOnSaved("suspendFileWatching", () => this.plugin.askReload()); + + new Setting(paneEl) + .autoWireToggle("suspendParseReplicationResult") + this.addOnSaved("suspendParseReplicationResult", () => this.plugin.askReload()); }); - c.addClass("op-warn"); - } else { - if (this.plugin?.replicator?.remoteLocked) { - const c = this.createEl(containerHatchEl, "div", { - text: "To prevent unwanted vault corruption, the remote database has been locked for synchronization. (This device is marked 'resolved') When all your devices are marked 'resolved', unlock the database.", - }); - this.createEl(c, "button", { text: "I'm ready, unlock the database" }, (e) => { - e.addClass("mod-warning"); - e.addEventListener("click", async () => { - await this.plugin.markRemoteUnlocked(); - c.remove(); - }); - }); - c.addClass("op-warn"); - } - } - new Setting(containerHatchEl) - .setName("Back to non-configured") - .addButton((button) => - button - .setButtonText("Back") - .setDisabled(false) - .onClick(async () => { - this.editingSettings.isConfigured = false; - await this.saveAllDirtySettings(); - this.plugin.askReload(); - })); - const hatchWarn = this.createEl(containerHatchEl, "div", { text: `To stop the boot up sequence for fixing problems on databases, you can put redflag.md on top of your vault (Rebooting obsidian is required).` }); - hatchWarn.addClass("op-warn-info"); + addPanel(paneEl, "Recovery and Repair").then((paneEl) => { - - const addResult = (path: string, file: TFile | false, fileOnDB: LoadedEntry | false) => { - resultArea.appendChild(this.createEl(resultArea, "div", {}, el => { - el.appendChild(this.createEl(el, "h6", { text: path })); - el.appendChild(this.createEl(el, "div", {}, infoGroupEl => { - infoGroupEl.appendChild(this.createEl(infoGroupEl, "div", { text: `Storage : Modified: ${!file ? `Missing:` : `${new Date(file.stat.mtime).toLocaleString()}, Size:${file.stat.size}`}` })) - infoGroupEl.appendChild(this.createEl(infoGroupEl, "div", { text: `Database: Modified: ${!fileOnDB ? `Missing:` : `${new Date(fileOnDB.mtime).toLocaleString()}, Size:${fileOnDB.size}`}` })) - })); - if (fileOnDB && file) { - el.appendChild(this.createEl(el, "button", { text: "Show history" }, buttonEl => { - buttonEl.onClickEvent(() => { - this.plugin.showHistory(file, fileOnDB._id); - }) - })) - } - if (file) { - el.appendChild(this.createEl(el, "button", { text: "Storage -> Database" }, buttonEl => { - buttonEl.onClickEvent(() => { - this.plugin.updateIntoDB(file, undefined, true); - el.remove(); - }) - })) - } - if (fileOnDB) { - el.appendChild(this.createEl(el, "button", { text: "Database -> Storage" }, buttonEl => { - buttonEl.onClickEvent(() => { - this.plugin.pullFile(this.plugin.getPath(fileOnDB), undefined, true, undefined, false); - el.remove(); - }) - })) - } - return el; - })) - } - - const checkBetweenStorageAndDatabase = async (file: TFile, fileOnDB: LoadedEntry) => { - const dataContent = readAsBlob(fileOnDB); - const content = createBlob(await this.plugin.vaultAccess.vaultReadAuto(file)) - if (await isDocContentSame(content, dataContent)) { - Logger(`Compare: SAME: ${file.path}`) - } else { - Logger(`Compare: CONTENT IS NOT MATCHED! ${file.path}`, LOG_LEVEL_NOTICE); - addResult(file.path, file, fileOnDB) - } - } - new Setting(containerHatchEl) - .setName("Recreate missing chunks for all files") - .setDesc("This will recreate chunks for all files. If there were missing chunks, this may fix the errors.") - .addButton((button) => - button. - setButtonText("Recreate all") - .setCta() - .onClick(async () => { - await this.plugin.createAllChunks(true); - }) - ) - - new Setting(containerHatchEl) - .setName("Verify and repair all files") - .setDesc("Compare the content of files between on local database and storage. If not matched, you will be asked which one you want to keep.") - .addButton((button) => - button - .setButtonText("Verify all") - .setDisabled(false) - .setCta() - .onClick(async () => { - Logger("Start verifying all files", LOG_LEVEL_NOTICE, "verify"); - const files = this.app.vault.getFiles(); - const documents = [] as FilePathWithPrefix[]; - - const adn = this.plugin.localDatabase.findAllNormalDocs() - for await (const i of adn) documents.push(this.plugin.getPath(i)); - const allPaths = [...new Set([...documents, ...files.map(e => e.path as FilePathWithPrefix)])]; - let i = 0; - const incProc = () => { - i++; - if (i % 25 == 0) Logger(`Checking ${i}/${files.length} files \n`, LOG_LEVEL_NOTICE, "verify-processed"); + const addResult = (path: string, file: TFile | false, fileOnDB: LoadedEntry | false) => { + resultArea.appendChild(this.createEl(resultArea, "div", {}, el => { + el.appendChild(this.createEl(el, "h6", { text: path })); + el.appendChild(this.createEl(el, "div", {}, infoGroupEl => { + infoGroupEl.appendChild(this.createEl(infoGroupEl, "div", { text: `Storage : Modified: ${!file ? `Missing:` : `${new Date(file.stat.mtime).toLocaleString()}, Size:${file.stat.size}`}` })) + infoGroupEl.appendChild(this.createEl(infoGroupEl, "div", { text: `Database: Modified: ${!fileOnDB ? `Missing:` : `${new Date(fileOnDB.mtime).toLocaleString()}, Size:${fileOnDB.size}`}` })) + })); + if (fileOnDB && file) { + el.appendChild(this.createEl(el, "button", { text: "Show history" }, buttonEl => { + buttonEl.onClickEvent(() => { + this.plugin.showHistory(file, fileOnDB._id); + }) + })) } - const semaphore = Semaphore(10); - const processes = allPaths.map(async path => { - try { - if (shouldBeIgnored(path)) { - return incProc(); - } - const abstractFile = this.plugin.vaultAccess.getAbstractFileByPath(path); - const fileOnStorage = abstractFile instanceof TFile ? abstractFile : false; - if (!await this.plugin.isTargetFile(path)) return incProc(); - const releaser = await semaphore.acquire(1) - if (fileOnStorage && this.plugin.isFileSizeExceeded(fileOnStorage.stat.size)) return incProc(); - try { - const fileOnDB = await this.plugin.localDatabase.getDBEntry(path); - if (fileOnDB && this.plugin.isFileSizeExceeded(fileOnDB.size)) return incProc(); + if (file) { + el.appendChild(this.createEl(el, "button", { text: "Storage -> Database" }, buttonEl => { + buttonEl.onClickEvent(() => { + this.plugin.updateIntoDB(file, undefined, true); + el.remove(); + }) + })) + } + if (fileOnDB) { + el.appendChild(this.createEl(el, "button", { text: "Database -> Storage" }, buttonEl => { + buttonEl.onClickEvent(() => { + this.plugin.pullFile(this.plugin.getPath(fileOnDB), undefined, true, undefined, false); + el.remove(); + }) + })) + } + return el; + })) + } - if (!fileOnDB && fileOnStorage) { - Logger(`Compare: Not found on the local database: ${path}`, LOG_LEVEL_NOTICE); - addResult(path, fileOnStorage, false) - return incProc(); - } - if (fileOnDB && !fileOnStorage) { - Logger(`Compare: Not found on the storage: ${path}`, LOG_LEVEL_NOTICE); - addResult(path, false, fileOnDB) - return incProc(); - } - if (fileOnStorage && fileOnDB) { - await checkBetweenStorageAndDatabase(fileOnStorage, fileOnDB) - } - } catch (ex) { - Logger(`Error while processing ${path}`, LOG_LEVEL_NOTICE); - Logger(ex, LOG_LEVEL_VERBOSE); - } finally { - releaser(); - incProc(); + const checkBetweenStorageAndDatabase = async (file: TFile, fileOnDB: LoadedEntry) => { + const dataContent = readAsBlob(fileOnDB); + const content = createBlob(await this.plugin.vaultAccess.vaultReadAuto(file)) + if (await isDocContentSame(content, dataContent)) { + Logger(`Compare: SAME: ${file.path}`) + } else { + Logger(`Compare: CONTENT IS NOT MATCHED! ${file.path}`, LOG_LEVEL_NOTICE); + addResult(file.path, file, fileOnDB) + } + } + new Setting(paneEl) + .setName("Recreate missing chunks for all files") + .setDesc("This will recreate chunks for all files. If there were missing chunks, this may fix the errors.") + .addButton((button) => + button. + setButtonText("Recreate all") + .setCta() + .onClick(async () => { + await this.plugin.createAllChunks(true); + }) + ) + + new Setting(paneEl) + .setName("Verify and repair all files") + .setDesc("Compare the content of files between on local database and storage. If not matched, you will be asked which one you want to keep.") + .addButton((button) => + button + .setButtonText("Verify all") + .setDisabled(false) + .setCta() + .onClick(async () => { + this.plugin.localDatabase.hashCaches.clear(); + Logger("Start verifying all files", LOG_LEVEL_NOTICE, "verify"); + const files = this.app.vault.getFiles(); + const documents = [] as FilePathWithPrefix[]; + + const adn = this.plugin.localDatabase.findAllNormalDocs() + for await (const i of adn) documents.push(this.plugin.getPath(i)); + const allPaths = [...new Set([...documents, ...files.map(e => e.path as FilePathWithPrefix)])]; + let i = 0; + const incProc = () => { + i++; + if (i % 25 == 0) Logger(`Checking ${i}/${files.length} files \n`, LOG_LEVEL_NOTICE, "verify-processed"); } - } catch (ex) { - Logger(`Error while processing without semaphore ${path}`, LOG_LEVEL_NOTICE); - Logger(ex, LOG_LEVEL_VERBOSE); - } - }); - await Promise.all(processes); - Logger("done", LOG_LEVEL_NOTICE, "verify"); - // Logger(`${i}/${files.length}\n`, LOG_LEVEL_NOTICE, "verify-processed"); - }) - ); - const resultArea = containerHatchEl.createDiv({ text: "" }); - new Setting(containerHatchEl) - .setName("Check and convert non-path-obfuscated files") - .setDesc("") - .addButton((button) => - button - .setButtonText("Perform") - .setDisabled(false) - .setWarning() - .onClick(async () => { - for await (const docName of this.plugin.localDatabase.findAllDocNames()) { - if (!docName.startsWith("f:")) { - const idEncoded = await this.plugin.path2id(docName as FilePathWithPrefix); - const doc = await this.plugin.localDatabase.getRaw(docName as DocumentID); - if (!doc) continue; - if (doc.type != "newnote" && doc.type != "plain") { - continue; - } - if (doc?.deleted ?? false) continue; - const newDoc = { ...doc }; - //Prepare converted data - newDoc._id = idEncoded; - newDoc.path = docName as FilePathWithPrefix; - // @ts-ignore - delete newDoc._rev; - try { - const obfuscatedDoc = await this.plugin.localDatabase.getRaw(idEncoded, { revs_info: true }); - // Unfortunately we have to delete one of them. - // Just now, save it as a conflicted document. - obfuscatedDoc._revs_info?.shift(); // Drop latest revision. - const previousRev = obfuscatedDoc._revs_info?.shift(); // Use second revision. - if (previousRev) { - newDoc._rev = previousRev.rev; - } else { - //If there are no revisions, set the possibly unique one - newDoc._rev = "1-" + (`00000000000000000000000000000000${~~(Math.random() * 1e9)}${~~(Math.random() * 1e9)}${~~(Math.random() * 1e9)}${~~(Math.random() * 1e9)}`.slice(-32)); - } - const ret = await this.plugin.localDatabase.putRaw(newDoc, { force: true }); - if (ret.ok) { - Logger(`${docName} has been converted as conflicted document`, LOG_LEVEL_NOTICE); - doc._deleted = true; - if ((await this.plugin.localDatabase.putRaw(doc)).ok) { - Logger(`Old ${docName} has been deleted`, LOG_LEVEL_NOTICE); + const semaphore = Semaphore(10); + const processes = allPaths.map(async path => { + try { + if (shouldBeIgnored(path)) { + return incProc(); } - await this.plugin.queueConflictCheck(docName as FilePathWithPrefix); - } else { - Logger(`Converting ${docName} Failed!`, LOG_LEVEL_NOTICE); - Logger(ret, LOG_LEVEL_VERBOSE); + const abstractFile = this.plugin.vaultAccess.getAbstractFileByPath(path); + const fileOnStorage = abstractFile instanceof TFile ? abstractFile : false; + if (!await this.plugin.isTargetFile(path)) return incProc(); + const releaser = await semaphore.acquire(1) + if (fileOnStorage && this.plugin.isFileSizeExceeded(fileOnStorage.stat.size)) return incProc(); + try { + const fileOnDB = await this.plugin.localDatabase.getDBEntry(path); + if (fileOnDB && this.plugin.isFileSizeExceeded(fileOnDB.size)) return incProc(); + + if (!fileOnDB && fileOnStorage) { + Logger(`Compare: Not found on the local database: ${path}`, LOG_LEVEL_NOTICE); + addResult(path, fileOnStorage, false) + return incProc(); + } + if (fileOnDB && !fileOnStorage) { + Logger(`Compare: Not found on the storage: ${path}`, LOG_LEVEL_NOTICE); + addResult(path, false, fileOnDB) + return incProc(); + } + if (fileOnStorage && fileOnDB) { + await checkBetweenStorageAndDatabase(fileOnStorage, fileOnDB) + } + } catch (ex) { + Logger(`Error while processing ${path}`, LOG_LEVEL_NOTICE); + Logger(ex, LOG_LEVEL_VERBOSE); + } finally { + releaser(); + incProc(); + } + } catch (ex) { + Logger(`Error while processing without semaphore ${path}`, LOG_LEVEL_NOTICE); + Logger(ex, LOG_LEVEL_VERBOSE); } - } catch (ex: any) { - if (ex?.status == 404) { - // We can perform this safely - if ((await this.plugin.localDatabase.putRaw(newDoc)).ok) { - Logger(`${docName} has been converted`, LOG_LEVEL_NOTICE); - doc._deleted = true; - if ((await this.plugin.localDatabase.putRaw(doc)).ok) { - Logger(`Old ${docName} has been deleted`, LOG_LEVEL_NOTICE); + }); + await Promise.all(processes); + Logger("done", LOG_LEVEL_NOTICE, "verify"); + // Logger(`${i}/${files.length}\n`, LOG_LEVEL_NOTICE, "verify-processed"); + }) + ); + const resultArea = paneEl.createDiv({ text: "" }); + new Setting(paneEl) + .setName("Check and convert non-path-obfuscated files") + .setDesc("") + .addButton((button) => + button + .setButtonText("Perform") + .setDisabled(false) + .setWarning() + .onClick(async () => { + for await (const docName of this.plugin.localDatabase.findAllDocNames()) { + if (!docName.startsWith("f:")) { + const idEncoded = await this.plugin.path2id(docName as FilePathWithPrefix); + const doc = await this.plugin.localDatabase.getRaw(docName as DocumentID); + if (!doc) continue; + if (doc.type != "newnote" && doc.type != "plain") { + continue; + } + if (doc?.deleted ?? false) continue; + const newDoc = { ...doc }; + //Prepare converted data + newDoc._id = idEncoded; + newDoc.path = docName as FilePathWithPrefix; + // @ts-ignore + delete newDoc._rev; + try { + const obfuscatedDoc = await this.plugin.localDatabase.getRaw(idEncoded, { revs_info: true }); + // Unfortunately we have to delete one of them. + // Just now, save it as a conflicted document. + obfuscatedDoc._revs_info?.shift(); // Drop latest revision. + const previousRev = obfuscatedDoc._revs_info?.shift(); // Use second revision. + if (previousRev) { + newDoc._rev = previousRev.rev; + } else { + //If there are no revisions, set the possibly unique one + newDoc._rev = "1-" + (`00000000000000000000000000000000${~~(Math.random() * 1e9)}${~~(Math.random() * 1e9)}${~~(Math.random() * 1e9)}${~~(Math.random() * 1e9)}`.slice(-32)); + } + const ret = await this.plugin.localDatabase.putRaw(newDoc, { force: true }); + if (ret.ok) { + Logger(`${docName} has been converted as conflicted document`, LOG_LEVEL_NOTICE); + doc._deleted = true; + if ((await this.plugin.localDatabase.putRaw(doc)).ok) { + Logger(`Old ${docName} has been deleted`, LOG_LEVEL_NOTICE); + } + await this.plugin.queueConflictCheck(docName as FilePathWithPrefix); + } else { + Logger(`Converting ${docName} Failed!`, LOG_LEVEL_NOTICE); + Logger(ret, LOG_LEVEL_VERBOSE); + } + } catch (ex: any) { + if (ex?.status == 404) { + // We can perform this safely + if ((await this.plugin.localDatabase.putRaw(newDoc)).ok) { + Logger(`${docName} has been converted`, LOG_LEVEL_NOTICE); + doc._deleted = true; + if ((await this.plugin.localDatabase.putRaw(doc)).ok) { + Logger(`Old ${docName} has been deleted`, LOG_LEVEL_NOTICE); + } + } + } else { + Logger(`Something went wrong while converting ${docName}`, LOG_LEVEL_NOTICE); + Logger(ex, LOG_LEVEL_VERBOSE); + // Something wrong. } } - } else { - Logger(`Something went wrong while converting ${docName}`, LOG_LEVEL_NOTICE); - Logger(ex, LOG_LEVEL_VERBOSE); - // Something wrong. } } - } - } - Logger(`Converting finished`, LOG_LEVEL_NOTICE); - })); + Logger(`Converting finished`, LOG_LEVEL_NOTICE); + })); + }); + addPanel(paneEl, "Reset").then((paneEl) => { + new Setting(paneEl) + .setName("Back to non-configured") + .addButton((button) => + button + .setButtonText("Back") + .setDisabled(false) + .onClick(async () => { + this.editingSettings.isConfigured = false; + await this.saveAllDirtySettings(); + this.plugin.askReload(); + })); - new Setting(containerHatchEl) - .setName("Delete all customization sync data") - .addButton((button) => - button - .setButtonText("Delete") - .setDisabled(false) - .setWarning() - .onClick(async () => { - Logger(`Deleting customization sync data`, LOG_LEVEL_NOTICE); - const entriesToDelete = (await this.plugin.localDatabase.allDocsRaw({ - startkey: "ix:", - endkey: "ix:\u{10ffff}", - include_docs: true - })); - const newData = entriesToDelete.rows.map(e => ({ ...e.doc, _deleted: true })); - const r = await this.plugin.localDatabase.bulkDocsRaw(newData as any[]); - // Do not care about the result. - Logger(`${r.length} items have been removed, to confirm how many items are left, please perform it again.`, LOG_LEVEL_NOTICE); - })) + new Setting(paneEl) + .setName("Delete all customization sync data") + .addButton((button) => + button + .setButtonText("Delete") + .setDisabled(false) + .setWarning() + .onClick(async () => { + Logger(`Deleting customization sync data`, LOG_LEVEL_NOTICE); + const entriesToDelete = (await this.plugin.localDatabase.allDocsRaw({ + startkey: "ix:", + endkey: "ix:\u{10ffff}", + include_docs: true + })); + const newData = entriesToDelete.rows.map(e => ({ ...e.doc, _deleted: true })); + const r = await this.plugin.localDatabase.bulkDocsRaw(newData as any[]); + // Do not care about the result. + Logger(`${r.length} items have been removed, to confirm how many items are left, please perform it again.`, LOG_LEVEL_NOTICE); + })) + }); + }); + addPane(containerEl, "Advanced", "🔧", 46, false, LEVEL_ADVANCED).then((paneEl) => { + addPanel(paneEl, "Memory cache").then((paneEl) => { + new Setting(paneEl) + .autoWireNumeric("hashCacheMaxCount", { clampMin: 10 }); + new Setting(paneEl) + .autoWireNumeric("hashCacheMaxAmount", { clampMin: 1 }); + }); + addPanel(paneEl, "Local Database Tweak").then((paneEl) => { + paneEl.addClass("wizardHidden"); - new Setting(containerHatchEl) - .autoWireToggle("suspendFileWatching") - this.addOnSaved("suspendFileWatching", () => this.plugin.askReload()); + new Setting(paneEl) + .setClass("wizardHidden") + .autoWireNumeric("customChunkSize", { clampMin: 0 }) - new Setting(containerHatchEl) - .autoWireToggle("suspendParseReplicationResult") - this.addOnSaved("suspendParseReplicationResult", () => this.plugin.askReload()); + new Setting(paneEl) + .setClass("wizardHidden") + .autoWireToggle("enableChunkSplitterV2", { onUpdate: enableOnly(() => this.isConfiguredAs("useSegmenter", false)) }) + new Setting(paneEl) + .setClass("wizardHidden") + .autoWireToggle("useSegmenter", { onUpdate: enableOnly(() => this.isConfiguredAs("enableChunkSplitterV2", false)) }) + }); - new Setting(containerHatchEl) - .autoWireToggle("writeLogToTheFile") + addPanel(paneEl, "Transfer Tweak").then((paneEl) => { + new Setting(paneEl) + .setClass("wizardHidden") + .autoWireToggle("readChunksOnline", { onUpdate: onlyOnCouchDB }) - this.createEl(containerHatchEl, "h4", { - text: sanitizeHTMLToDom(`Compatibility`), - cls: "wizardHidden" + new Setting(paneEl) + .setClass("wizardHidden") + .autoWireNumeric("concurrencyOfReadChunksOnline", { clampMin: 10, onUpdate: onlyOnCouchDB }) + + new Setting(paneEl) + .setClass("wizardHidden") + .autoWireNumeric("minimumIntervalOfReadChunksOnline", { clampMin: 10, onUpdate: onlyOnCouchDB }) + new Setting(paneEl) + .setClass("wizardHidden") + .autoWireToggle("sendChunksBulk", { onUpdate: onlyOnCouchDB }) + new Setting(paneEl) + .setClass("wizardHidden") + .autoWireNumeric("sendChunksBulkMaxSize", { clampMax: 100, clampMin: 1, onUpdate: onlyOnCouchDB }) + + }); }); - new Setting(containerHatchEl) - .setClass("wizardHidden") - .autoWireToggle("deleteMetadataOfDeletedFiles") - - new Setting(containerHatchEl) - .setClass("wizardHidden") - .autoWireNumeric("automaticallyDeleteMetadataOfDeletedFiles", { onUpdate: visibleOnly(() => this.isConfiguredAs("deleteMetadataOfDeletedFiles", true)) }) + addPane(containerEl, "Power users", "💪", 47, true, LEVEL_POWER_USER).then((paneEl) => { - new Setting(containerHatchEl) - .autoWireToggle("useIndexedDBAdapter", { invert: true }) - new Setting(containerHatchEl) - .autoWireToggle("doNotUseFixedRevisionForChunks", { holdValue: true }) - .setClass("wizardHidden") - new Setting(containerHatchEl) - .autoWireToggle("handleFilenameCaseSensitive", { holdValue: true }) - .setClass("wizardHidden") + addPanel(paneEl, "Remote Database Tweak").then((paneEl) => { - new Setting(containerHatchEl) - .setName("Apply") - .setDesc("These configurations require a database rebuild.") - .setClass("wizardHidden") - .addButton((button) => - button - .setButtonText("Apply and rebuild") - .setWarning() - .setDisabled(false) - .onClick(async () => { - await this.saveAllDirtySettings(); - // await this.applySetting(["doNotUseFixedRevisionForChunks", "handleFilenameCaseSensitive"]); - // await this.saveSettings(["doNotUseFixedRevisionForChunks", "handleFilenameCaseSensitive"]); - // debugger; - await rebuildDB("rebuildBothByThisDevice"); + new Setting(paneEl).autoWireToggle("useEden").setClass("wizardHidden"); + const onlyUsingEden = visibleOnly(() => this.isConfiguredAs("useEden", true)); + new Setting(paneEl).autoWireNumeric("maxChunksInEden", { onUpdate: onlyUsingEden }).setClass("wizardHidden"); + new Setting(paneEl).autoWireNumeric("maxTotalLengthInEden", { onUpdate: onlyUsingEden }).setClass("wizardHidden"); + new Setting(paneEl).autoWireNumeric("maxAgeInEden", { onUpdate: onlyUsingEden }).setClass("wizardHidden"); + + new Setting(paneEl).autoWireToggle("enableCompression").setClass("wizardHidden"); + }); + + + + addPanel(paneEl, "CouchDB Connection Tweak", undefined, onlyOnCouchDB).then((paneEl) => { + paneEl.addClass("wizardHidden"); + + this.createEl(paneEl, "div", { + text: `If you reached the payload size limit when using IBM Cloudant, please decrease batch size and batch limit to a lower value.`, + }, undefined, onlyOnCouchDB).addClass("wizardHidden"); + + new Setting(paneEl) + .setClass("wizardHidden") + .autoWireNumeric("batch_size", { clampMin: 2, onUpdate: onlyOnCouchDB }) + new Setting(paneEl) + .setClass("wizardHidden") + .autoWireNumeric("batches_limit", { clampMin: 2, onUpdate: onlyOnCouchDB }) + new Setting(paneEl) + .setClass("wizardHidden") + .autoWireToggle("useTimeouts", { onUpdate: onlyOnCouchDB }); + }); + addPanel(paneEl, "Configuration Encryption").then((paneEl) => { + const passphrase_options: Record = { + "": "Default", + LOCALSTORAGE: "Use a custom passphrase", + ASK_AT_LAUNCH: "Ask an passphrase at every launch", + } + + new Setting(paneEl) + .setName("Encrypting sensitive configuration items") + .autoWireDropDown("configPassphraseStore", { options: passphrase_options, holdValue: true }) + .setClass("wizardHidden"); + + new Setting(paneEl) + .autoWireText("configPassphrase", { isPassword: true, holdValue: true }) + .setClass("wizardHidden") + .addOnUpdate(() => ({ + disabled: !this.isConfiguredAs("configPassphraseStore", "LOCALSTORAGE") + })) + new Setting(paneEl) + .addApplyButton(["configPassphrase", "configPassphraseStore"]) + .setClass("wizardHidden") + }); + }); + + addPane(containerEl, "Patches", "🩹", 51, false, LEVEL_EDGE_CASE).then((paneEl) => { + + + addPanel(paneEl, "Compatibility (Metadata)").then((paneEl) => { + + new Setting(paneEl) + .setClass("wizardHidden") + .autoWireToggle("deleteMetadataOfDeletedFiles") + + new Setting(paneEl) + .setClass("wizardHidden") + .autoWireNumeric("automaticallyDeleteMetadataOfDeletedFiles", { onUpdate: visibleOnly(() => this.isConfiguredAs("deleteMetadataOfDeletedFiles", true)) }) + + }); + + + addPanel(paneEl, "Compatibility (Conflict Behaviour)").then((paneEl) => { + paneEl.addClass("wizardHidden"); + new Setting(paneEl) + .setClass("wizardHidden") + .autoWireToggle("disableMarkdownAutoMerge") + new Setting(paneEl) + .setClass("wizardHidden") + .autoWireToggle("writeDocumentsIfConflicted") + }); + + addPanel(paneEl, "Compatibility (Database structure)").then((paneEl) => { + + new Setting(paneEl) + .autoWireToggle("useIndexedDBAdapter", { invert: true, holdValue: true }) + + new Setting(paneEl) + .autoWireToggle("doNotUseFixedRevisionForChunks", { holdValue: true }) + .setClass("wizardHidden") + new Setting(paneEl) + .autoWireToggle("handleFilenameCaseSensitive", { holdValue: true }) + .setClass("wizardHidden") + + this.addOnSaved("useIndexedDBAdapter", async () => { + await this.saveAllDirtySettings(); + await rebuildDB("localOnly"); + }) + }); + + addPanel(paneEl, "Compatibility (Internal API Usage)").then((paneEl) => { + new Setting(paneEl) + .autoWireToggle("watchInternalFileChanges", { invert: true }) + + }); + + + + + addPanel(paneEl, "Edge case addressing (Database)").then((paneEl) => { + new Setting(paneEl) + .autoWireText("additionalSuffixOfDatabaseName", { holdValue: true }) + .addApplyButton(["additionalSuffixOfDatabaseName"]); + + this.addOnSaved("additionalSuffixOfDatabaseName", async (key) => { + Logger("Suffix has been changed. Reopening database...", LOG_LEVEL_NOTICE); + await this.plugin.initializeDatabase(); + }) + + new Setting(paneEl) + .autoWireDropDown("hashAlg", { + options: { + "": "Old Algorithm", + "xxhash32": "xxhash32 (Fast)", + "xxhash64": "xxhash64 (Fastest)", + "sha1": "Fallback (Without WebAssembly)" + } as Record }) - ) - .addOnUpdate(() => ({ - isCta: this.isSomeDirty(["doNotUseFixedRevisionForChunks", "handleFilenameCaseSensitive"]), - disabled: !this.isSomeDirty(["doNotUseFixedRevisionForChunks", "handleFilenameCaseSensitive"]), - })) - this.addOnSaved("useIndexedDBAdapter", async () => { - await this.saveAllDirtySettings(); - await rebuildDB("localOnly"); - }) - - new Setting(containerHatchEl) - .autoWireToggle("watchInternalFileChanges", { invert: true }) - - new Setting(containerHatchEl) - .autoWireText("additionalSuffixOfDatabaseName", { holdValue: true }) - .addApplyButton(["additionalSuffixOfDatabaseName"]); - - this.addOnSaved("additionalSuffixOfDatabaseName", async (key) => { - Logger("Suffix has been changed. Reopening database...", LOG_LEVEL_NOTICE); - await this.plugin.initializeDatabase(); - }) - - new Setting(containerHatchEl) - .autoWireDropDown("hashAlg", { - options: { - "": "Old Algorithm", - "xxhash32": "xxhash32 (Fast)", - "xxhash64": "xxhash64 (Fastest)", - "sha1": "Fallback (Without WebAssembly)" - } as Record - }) - this.addOnSaved("hashAlg", async () => { - await this.plugin.localDatabase.prepareHashFunctions(); - }) - - - new Setting(containerHatchEl) - .autoWireToggle("doNotSuspendOnFetching") - new Setting(containerHatchEl) - .autoWireToggle("disableCheckingConfigMismatch") - - new Setting(containerHatchEl) - .autoWireToggle("disableWorkerForGeneratingChunks") - - new Setting(containerHatchEl) - .autoWireToggle("processSmallFilesInUIThread", { - onUpdate: visibleOnly(() => this.isConfiguredAs("disableWorkerForGeneratingChunks", false)) - }) - - addScreenElement("50", containerHatchEl); - - - // With great respect, thank you TfTHacker! - // Refer: https://github.com/TfTHacker/obsidian42-brat/blob/main/src/features/BetaPlugins.ts - const containerPluginSettings = containerEl.createDiv(); - this.createEl(containerPluginSettings, "h3", { text: "Customization sync (beta 3)" }); - - const enableOnlyOnPluginSyncIsNotEnabled = enableOnly(() => this.isConfiguredAs("usePluginSync", false)); - const visibleOnlyOnPluginSyncEnabled = visibleOnly(() => this.isConfiguredAs("usePluginSync", true)); - - new Setting(containerPluginSettings) - .autoWireText("deviceAndVaultName", { - placeHolder: "desktop", - onUpdate: enableOnlyOnPluginSyncIsNotEnabled + this.addOnSaved("hashAlg", async () => { + await this.plugin.localDatabase.prepareHashFunctions(); + }) + }); + addPanel(paneEl, "Edge case addressing (Behaviour)").then((paneEl) => { + new Setting(paneEl) + .autoWireToggle("doNotSuspendOnFetching") + new Setting(paneEl) + .setClass("wizardHidden") + .autoWireToggle("doNotDeleteFolder") }); - new Setting(containerPluginSettings) - .autoWireToggle("usePluginSyncV2") + addPanel(paneEl, "Edge case addressing (Processing)").then((paneEl) => { + new Setting(paneEl) + .autoWireToggle("disableWorkerForGeneratingChunks") - new Setting(containerPluginSettings) - .autoWireToggle("usePluginSync", { - onUpdate: enableOnly(() => !this.isConfiguredAs("deviceAndVaultName", "")) + new Setting(paneEl) + .autoWireToggle("processSmallFilesInUIThread", { + onUpdate: visibleOnly(() => this.isConfiguredAs("disableWorkerForGeneratingChunks", false)) + }) }); - new Setting(containerPluginSettings) - .autoWireToggle("autoSweepPlugins", { - onUpdate: visibleOnlyOnPluginSyncEnabled - }) + addPanel(paneEl, "Compatibility (Trouble addressed)").then((paneEl) => { + new Setting(paneEl) + .autoWireToggle("disableCheckingConfigMismatch") + }); + }); - new Setting(containerPluginSettings) - .autoWireToggle("autoSweepPluginsPeriodic", { - onUpdate: visibleOnly(() => this.isConfiguredAs("usePluginSync", true) && this.isConfiguredAs("autoSweepPlugins", true)) - }) - new Setting(containerPluginSettings) - .autoWireToggle("notifyPluginOrSettingUpdated", { - onUpdate: visibleOnlyOnPluginSyncEnabled - }) - new Setting(containerPluginSettings) - .setName("Open") - .setDesc("Open the dialog") - .addButton((button) => { - button - .setButtonText("Open") - .setDisabled(false) - .onClick(() => { - this.plugin.addOnConfigSync.showPluginSyncModal(); + + addPane(containerEl, "Maintenance", "🎛️", 70, true).then((paneEl) => { + + const isRemoteLockedAndDeviceNotAccepted = () => this.plugin?.replicator?.remoteLockedAndDeviceNotAccepted; + const isRemoteLocked = () => this.plugin?.replicator?.remoteLocked; + // if (this.plugin?.replicator?.remoteLockedAndDeviceNotAccepted) { + this.createEl(paneEl, "div", { + text: "To prevent unwanted vault corruption, the remote database has been locked for synchronization, and this device was not marked as 'resolved'. It caused by some operations like this. Re-initialized. Local database initialization should be required. Please back your vault up, reset the local database, and press 'Mark this device as resolved'. ", + cls: "op-warn" + }, c => { + this.createEl(c, "button", { + text: "I'm ready, mark this device 'resolved'", + cls: "mod-warning" + }, (e) => { + e.addEventListener("click", async () => { + await this.plugin.markRemoteResolved(); + this.display(); }); - }) - .addOnUpdate(visibleOnlyOnPluginSyncEnabled); + }) + }, visibleOnly(isRemoteLockedAndDeviceNotAccepted)); + this.createEl(paneEl, "div", { + text: "To prevent unwanted vault corruption, the remote database has been locked for synchronization. (This device is marked 'resolved') When all your devices are marked 'resolved', unlock the database.", + cls: "op-warn" + }, c => + this.createEl(c, "button", { text: "I'm ready, unlock the database", cls: "mod-warning" }, (e) => { + e.addEventListener("click", async () => { + await this.plugin.markRemoteUnlocked(); + this.display(); + }); + }), visibleOnly(isRemoteLocked)); - addScreenElement("60", containerPluginSettings); + addPanel(paneEl, "Scram!").then((paneEl) => { + new Setting(paneEl) + .setName("Lock remote") + .setDesc("Lock remote to prevent synchronization with other devices.") + .addButton((button) => + button + .setButtonText("Lock") + .setDisabled(false) + .setWarning() + .onClick(async () => { + await this.plugin.markRemoteLocked(); + }) + ); - const containerMaintenanceEl = containerEl.createDiv(); + new Setting(paneEl) + .setName("Emergency restart") + .setDesc("place the flag file to prevent all operation and restart.") + .addButton((button) => + button + .setButtonText("Flag and restart") + .setDisabled(false) + .setWarning() + .onClick(async () => { + await this.plugin.vaultAccess.vaultCreate(FLAGMD_REDFLAG, ""); + this.plugin.performAppReload(); + }) + ); - this.createEl(containerMaintenanceEl, "h3", { text: "Maintenance" }); + }); - this.createEl(containerMaintenanceEl, "h4", { text: "Remote" }); + addPanel(paneEl, "Data-complementary Operations").then((paneEl) => { + new Setting(paneEl) + .setName("Resend") + .setDesc("Resend all chunks to the remote.") + .addButton((button) => + button + .setButtonText("Send chunks") + .setWarning() + .setDisabled(false) + .onClick(async () => { + if (this.plugin.replicator instanceof LiveSyncCouchDBReplicator) { + await this.plugin.replicator.sendChunks(this.plugin.settings, undefined, true, 0); + } + })) + .addOnUpdate(onlyOnCouchDB); + new Setting(paneEl) + .setName("Reset journal received history") + .setDesc("Initialise journal received history. On the next sync, every item except this device sent will be downloaded again.") + .addButton((button) => + button + .setButtonText("Reset received") + .setWarning() + .setDisabled(false) + .onClick(async () => { + await this.plugin.getMinioJournalSyncClient().updateCheckPointInfo((info) => ({ + ...info, + receivedFiles: new Set(), + knownIDs: new Set() + })); + Logger(`Journal received history has been cleared.`, LOG_LEVEL_NOTICE); + }) + ).addOnUpdate(onlyOnMinIO); - new Setting(containerMaintenanceEl) - .setName("Perform compaction") - .setDesc("Compaction discards all of Eden in the non-latest revisions, reducing the storage usage. However, this operation requires the same free space on the remote as the current database.") - .addButton((button) => - button - .setButtonText("Perform") - .setDisabled(false) - .onClick(async () => { - const replicator = this.plugin.replicator as LiveSyncCouchDBReplicator; - Logger(`Compaction has been began`, LOG_LEVEL_NOTICE, "compaction") - if (await replicator.compactRemote(this.editingSettings)) { - Logger(`Compaction has been completed!`, LOG_LEVEL_NOTICE, "compaction"); - } else { - Logger(`Compaction has been failed!`, LOG_LEVEL_NOTICE, "compaction"); - } - }) - ).addOnUpdate(onlyOnCouchDB); + new Setting(paneEl) + .setName("Reset journal sent history") + .setDesc("Initialise journal sent history. On the next sync, every item except this device received will be sent again.") + .addButton((button) => + button + .setButtonText("Reset sent history") + .setWarning() + .setDisabled(false) + .onClick(async () => { + await this.plugin.getMinioJournalSyncClient().updateCheckPointInfo((info) => ({ + ...info, + lastLocalSeq: 0, + sentIDs: new Set(), + sentFiles: new Set() + })); + Logger(`Journal sent history has been cleared.`, LOG_LEVEL_NOTICE); + }) + ).addOnUpdate(onlyOnMinIO); - new Setting(containerMaintenanceEl) - .setName("Lock remote") - .setDesc("Lock remote to prevent synchronization with other devices.") - .addButton((button) => - button - .setButtonText("Lock") - .setDisabled(false) - .setWarning() - .onClick(async () => { - await this.plugin.markRemoteLocked(); - }) - ); + }); - new Setting(containerMaintenanceEl) - .setName("Overwrite remote") - .setDesc("Overwrite remote with local DB and passphrase.") - .addButton((button) => - button - .setButtonText("Send") - .setWarning() - .setDisabled(false) - .onClick(async () => { - await rebuildDB("remoteOnly"); - }) - ) - .addButton((button) => - button - .setButtonText("Send chunks") - .setWarning() - .setDisabled(false) - .onClick(async () => { - if (this.plugin.replicator instanceof LiveSyncCouchDBReplicator) { - await this.plugin.replicator.sendChunks(this.plugin.settings, undefined, true, 0); - } - }) - ) - new Setting(containerMaintenanceEl) - .setName("Reset journal received history") - .setDesc("Initialise journal received history. On the next sync, every item except this device sent will be downloaded again.") - .addButton((button) => - button - .setButtonText("Reset received") - .setWarning() - .setDisabled(false) - .onClick(async () => { - await this.plugin.getMinioJournalSyncClient().updateCheckPointInfo((info) => ({ - ...info, - receivedFiles: new Set(), - knownIDs: new Set() - })); - Logger(`Journal received history has been cleared.`, LOG_LEVEL_NOTICE); - }) - ).addOnUpdate(onlyOnMinIO); + addPanel(paneEl, "Rebuilding Operations (Local)").then((paneEl) => { - new Setting(containerMaintenanceEl) - .setName("Reset journal sent history") - .setDesc("Initialise journal sent history. On the next sync, every item except this device received will be sent again.") - .addButton((button) => - button - .setButtonText("Reset sent history") - .setWarning() - .setDisabled(false) - .onClick(async () => { - await this.plugin.getMinioJournalSyncClient().updateCheckPointInfo((info) => ({ - ...info, - lastLocalSeq: 0, - sentIDs: new Set(), - sentFiles: new Set() - })); - Logger(`Journal sent history has been cleared.`, LOG_LEVEL_NOTICE); - }) - ).addOnUpdate(onlyOnMinIO); + new Setting(paneEl) + .setName("Fetch from remote") + .setDesc("Restore or reconstruct local database from remote.") + .addButton((button) => + button + .setButtonText("Fetch") + .setWarning() + .setDisabled(false) + .onClick(async () => { + await this.plugin.vaultAccess.vaultCreate(FLAGMD_REDFLAG3_HR, ""); + this.plugin.performAppReload(); + }) + ).addButton((button) => + button + .setButtonText("Fetch w/o restarting") + .setWarning() + .setDisabled(false) + .onClick(async () => { + await rebuildDB("localOnly"); + }) + ) - new Setting(containerMaintenanceEl) - .setName("Reset all journal counter") - .setDesc("Initialise all journal history, On the next sync, every item will be received and sent.") - .addButton((button) => - button - .setButtonText("Reset all") - .setWarning() - .setDisabled(false) - .onClick(async () => { - await this.plugin.getMinioJournalSyncClient().resetCheckpointInfo(); - Logger(`Journal exchange history has been cleared.`, LOG_LEVEL_NOTICE); - }) - ).addOnUpdate(onlyOnMinIO); + new Setting(paneEl) + .setName("Fetch rebuilt DB (Save local documents before)") + .setDesc("Restore or reconstruct local database from remote database but use local chunks.") + .addButton((button) => + button + .setButtonText("Save and Fetch") + .setWarning() + .setDisabled(false) + .onClick(async () => { + await rebuildDB("localOnlyWithChunks"); + }) + ).addOnUpdate(onlyOnCouchDB); - new Setting(containerMaintenanceEl) - .setName("Purge all journal counter") - .setDesc("Purge all sending and downloading cache.") - .addButton((button) => - button - .setButtonText("Reset all") - .setWarning() - .setDisabled(false) - .onClick(async () => { - await this.plugin.getMinioJournalSyncClient().resetAllCaches(); - Logger(`Journal sending and downloading cache has been cleared.`, LOG_LEVEL_NOTICE); - }) - ).addOnUpdate(onlyOnMinIO); + }); - new Setting(containerMaintenanceEl) - .setName("Make empty the bucket") - .setDesc("Delete all data on the remote.") - .addButton((button) => - button - .setButtonText("Delete") - .setWarning() - .setDisabled(false) - .onClick(async () => { - await this.plugin.getMinioJournalSyncClient().updateCheckPointInfo((info) => ({ - ...info, - receivedFiles: new Set(), - knownIDs: new Set(), - lastLocalSeq: 0, - sentIDs: new Set(), - sentFiles: new Set() - })); - await this.plugin.resetRemoteBucket(); - Logger(`the bucket has been cleared.`, LOG_LEVEL_NOTICE); - }) - ).addOnUpdate(onlyOnMinIO); + addPanel(paneEl, "Total Overhaul").then((paneEl) => { + new Setting(paneEl) + .setName("Rebuild everything") + .setDesc("Rebuild local and remote database with local files.") + .addButton((button) => + button + .setButtonText("Rebuild") + .setWarning() + .setDisabled(false) + .onClick(async () => { + await this.plugin.vaultAccess.vaultCreate(FLAGMD_REDFLAG2_HR, ""); + this.plugin.performAppReload(); + }) + ) + .addButton((button) => + button + .setButtonText("Rebuild w/o restarting") + .setWarning() + .setDisabled(false) + .onClick(async () => { + await rebuildDB("rebuildBothByThisDevice"); + }) + ) + }); + addPanel(paneEl, "Rebuilding Operations (Remote Only)").then((paneEl) => { - this.createEl(containerMaintenanceEl, "h4", { text: "Local database" }); + new Setting(paneEl) + .setName("Perform compaction") + .setDesc("Compaction discards all of Eden in the non-latest revisions, reducing the storage usage. However, this operation requires the same free space on the remote as the current database.") + .addButton((button) => + button + .setButtonText("Perform") + .setDisabled(false) + .onClick(async () => { + const replicator = this.plugin.replicator as LiveSyncCouchDBReplicator; + Logger(`Compaction has been began`, LOG_LEVEL_NOTICE, "compaction") + if (await replicator.compactRemote(this.editingSettings)) { + Logger(`Compaction has been completed!`, LOG_LEVEL_NOTICE, "compaction"); + } else { + Logger(`Compaction has been failed!`, LOG_LEVEL_NOTICE, "compaction"); + } + }) + ).addOnUpdate(onlyOnCouchDB); - new Setting(containerMaintenanceEl) - .setName("Fetch from remote") - .setDesc("Restore or reconstruct local database from remote.") - .addButton((button) => - button - .setButtonText("Fetch") - .setWarning() - .setDisabled(false) - .onClick(async () => { - await this.plugin.vaultAccess.vaultCreate(FLAGMD_REDFLAG3_HR, ""); - this.plugin.performAppReload(); - }) - ).addButton((button) => - button - .setButtonText("Fetch w/o restarting") - .setWarning() - .setDisabled(false) - .onClick(async () => { - await rebuildDB("localOnly"); - }) - ) - new Setting(containerMaintenanceEl) - .setName("Fetch rebuilt DB (Save local documents before)") - .setDesc("Restore or reconstruct local database from remote database but use local chunks.") - .addButton((button) => - button - .setButtonText("Save and Fetch") - .setWarning() - .setDisabled(false) - .onClick(async () => { - await rebuildDB("localOnlyWithChunks"); - }) - ).addOnUpdate(onlyOnCouchDB); + new Setting(paneEl) + .setName("Overwrite remote") + .setDesc("Overwrite remote with local DB and passphrase.") + .addButton((button) => + button + .setButtonText("Send") + .setWarning() + .setDisabled(false) + .onClick(async () => { + await rebuildDB("remoteOnly"); + }) + ) - new Setting(containerMaintenanceEl) - .setName("Discard local database to reset or uninstall Self-hosted LiveSync") - .addButton((button) => - button - .setButtonText("Discard") - .setWarning() - .setDisabled(false) - .onClick(async () => { - await this.plugin.resetLocalDatabase(); - await this.plugin.initializeDatabase(); - }) - ); + new Setting(paneEl) + .setName("Reset all journal counter") + .setDesc("Initialise all journal history, On the next sync, every item will be received and sent.") + .addButton((button) => + button + .setButtonText("Reset all") + .setWarning() + .setDisabled(false) + .onClick(async () => { + await this.plugin.getMinioJournalSyncClient().resetCheckpointInfo(); + Logger(`Journal exchange history has been cleared.`, LOG_LEVEL_NOTICE); + }) + ).addOnUpdate(onlyOnMinIO); - this.createEl(containerMaintenanceEl, "h4", { text: "Both databases" }); + new Setting(paneEl) + .setName("Purge all journal counter") + .setDesc("Purge all sending and downloading cache.") + .addButton((button) => + button + .setButtonText("Reset all") + .setWarning() + .setDisabled(false) + .onClick(async () => { + await this.plugin.getMinioJournalSyncClient().resetAllCaches(); + Logger(`Journal sending and downloading cache has been cleared.`, LOG_LEVEL_NOTICE); + }) + ).addOnUpdate(onlyOnMinIO); - new Setting(containerMaintenanceEl) - .setName("(Beta2) Clean up databases") - .setDesc("Delete unused chunks to shrink the database. This feature requires disabling 'Use an old adapter for compatibility'") - .addButton((button) => - button.setButtonText("DryRun") - .setDisabled(false) - .onClick(async () => { - await this.plugin.dryRunGC(); - }) - ).addButton((button) => - button.setButtonText("Perform cleaning") - .setDisabled(false) - .setWarning() - .onClick(async () => { - this.closeSetting() - await this.plugin.dbGC(); - }) - ).addOnUpdate(onlyOnCouchDB); + new Setting(paneEl) + .setName("Make empty the bucket") + .setDesc("Delete all data on the remote.") + .addButton((button) => + button + .setButtonText("Delete") + .setWarning() + .setDisabled(false) + .onClick(async () => { + await this.plugin.getMinioJournalSyncClient().updateCheckPointInfo((info) => ({ + ...info, + receivedFiles: new Set(), + knownIDs: new Set(), + lastLocalSeq: 0, + sentIDs: new Set(), + sentFiles: new Set() + })); + await this.plugin.resetRemoteBucket(); + Logger(`the bucket has been cleared.`, LOG_LEVEL_NOTICE); + }) + ).addOnUpdate(onlyOnMinIO); + }); - new Setting(containerMaintenanceEl) - .setName("Rebuild everything") - .setDesc("Rebuild local and remote database with local files.") - .addButton((button) => - button - .setButtonText("Rebuild") - .setWarning() - .setDisabled(false) - .onClick(async () => { - await this.plugin.vaultAccess.vaultCreate(FLAGMD_REDFLAG2_HR, ""); - this.plugin.performAppReload(); - }) - ) - .addButton((button) => - button - .setButtonText("Rebuild w/o restarting") - .setWarning() - .setDisabled(false) - .onClick(async () => { - await rebuildDB("rebuildBothByThisDevice"); - }) - ) + addPanel(paneEl, "Niches").then((paneEl) => { + new Setting(paneEl) + .setClass("sls-setting-obsolete") + .setName("(Obsolete) Clean up databases") + .setDesc("Delete unused chunks to shrink the database. However, this feature could be not effective in some cases. Please use rebuild everything instead.") + .addButton((button) => + button.setButtonText("DryRun") + .setDisabled(false) + .onClick(async () => { + await this.plugin.dryRunGC(); + }) + ).addButton((button) => + button.setButtonText("Perform cleaning") + .setDisabled(false) + .setWarning() + .onClick(async () => { + this.closeSetting() + await this.plugin.dbGC(); + }) + ).addOnUpdate(onlyOnCouchDB); + }); + addPanel(paneEl, "Reset").then((paneEl) => { + new Setting(paneEl) + .setName("Discard local database to reset or uninstall Self-hosted LiveSync") + .addButton((button) => + button + .setButtonText("Discard") + .setWarning() + .setDisabled(false) + .onClick(async () => { + await this.plugin.resetLocalDatabase(); + await this.plugin.initializeDatabase(); + }) + ); + }); - addScreenElement("70", containerMaintenanceEl); - if (this.selectedScreen == "") { - if (lastVersion != this.editingSettings.lastReadUpdates) { - if (this.editingSettings.isConfigured) { - changeDisplay("100"); + }); + yieldNextAnimationFrame().then(() => { + if (this.selectedScreen == "") { + if (lastVersion != this.editingSettings.lastReadUpdates) { + if (this.editingSettings.isConfigured) { + changeDisplay("100"); + } else { + changeDisplay("110") + } } else { - changeDisplay("110") + if (isAnySyncEnabled()) { + changeDisplay("20"); + } else { + changeDisplay("110") + } } } else { - if (isAnySyncEnabled()) { - changeDisplay("20"); - } else { - changeDisplay("110") - } + changeDisplay(this.selectedScreen); } - } else { - changeDisplay(this.selectedScreen); - } - this.requestUpdate(); + this.requestUpdate(); + }); } } diff --git a/src/ui/components/LiveSyncSetting.ts b/src/ui/components/LiveSyncSetting.ts new file mode 100644 index 0000000..7d945f4 --- /dev/null +++ b/src/ui/components/LiveSyncSetting.ts @@ -0,0 +1,339 @@ +import { Setting, TextComponent, type ToggleComponent, type DropdownComponent, ButtonComponent, type TextAreaComponent, type ValueComponent } from "obsidian"; +import { unique } from "octagonal-wheels/collection"; +import { LEVEL_ADVANCED, LEVEL_POWER_USER, statusDisplay, type ConfigurationItem } from "../../lib/src/common/types"; +import { type ObsidianLiveSyncSettingTab, type AutoWireOption, wrapMemo, type OnUpdateResult, createStub, findAttrFromParent } from "../ObsidianLiveSyncSettingTab"; +import { type AllSettingItemKey, getConfig, type AllSettings, type AllStringItemKey, type AllNumericItemKey, type AllBooleanItemKey } from "../settingConstants"; + + +export class LiveSyncSetting extends Setting { + autoWiredComponent?: TextComponent | ToggleComponent | DropdownComponent | ButtonComponent | TextAreaComponent; + applyButtonComponent?: ButtonComponent; + selfKey?: AllSettingItemKey; + watchDirtyKeys = [] as AllSettingItemKey[]; + holdValue: boolean = false; + static env: ObsidianLiveSyncSettingTab; + + descBuf: string | DocumentFragment = ""; + nameBuf: string | DocumentFragment = ""; + placeHolderBuf: string = ""; + hasPassword: boolean = false; + + invalidateValue?: () => void; + setValue?: (value: any) => void; + constructor(containerEl: HTMLElement) { + super(containerEl); + LiveSyncSetting.env.settingComponents.push(this); + } + + _createDocStub(key: string, value: string | DocumentFragment) { + DEV: { + const paneName = findAttrFromParent(this.settingEl, "data-pane"); + const panelName = findAttrFromParent(this.settingEl, "data-panel"); + const itemName = typeof this.nameBuf == "string" ? this.nameBuf : this.nameBuf.textContent?.toString() ?? ""; + const strValue = typeof value == "string" ? value : value.textContent?.toString() ?? ""; + + createStub(itemName, key, strValue, panelName, paneName); + } + } + + setDesc(desc: string | DocumentFragment): this { + this.descBuf = desc; + DEV: { + this._createDocStub("desc", desc); + } + super.setDesc(desc); + return this; + } + setName(name: string | DocumentFragment): this { + this.nameBuf = name; + DEV: { + this._createDocStub("name", name); + } + super.setName(name); + return this; + } + setAuto(key: AllSettingItemKey, opt?: AutoWireOption) { + this.autoWireSetting(key, opt); + return this; + } + autoWireSetting(key: AllSettingItemKey, opt?: AutoWireOption) { + const conf = getConfig(key); + if (!conf) { + // throw new Error(`No such setting item :${key}`) + return; + } + const name = `${conf.name}${statusDisplay(conf.status)}`; + this.setName(name); + if (conf.desc) { + this.setDesc(conf.desc); + } + DEV: { + this._createDocStub("key", key); + if (conf.obsolete) this._createDocStub("is_obsolete", "true"); + if (conf.level) this._createDocStub("level", conf.level); + } + + this.holdValue = opt?.holdValue || this.holdValue; + this.selfKey = key; + if (conf.obsolete || opt?.obsolete) { + this.settingEl.toggleClass("sls-setting-obsolete", true); + } + if (opt?.onUpdate) this.addOnUpdate(opt.onUpdate); + const stat = this._getComputedStatus(); + if (stat.visibility === false) { + this.settingEl.toggleClass("sls-setting-hidden", !stat.visibility); + } + return conf; + } + autoWireComponent(component: ValueComponent, conf?: ConfigurationItem, opt?: AutoWireOption) { + this.placeHolderBuf = conf?.placeHolder || opt?.placeHolder || ""; + if (conf?.level == LEVEL_ADVANCED) { + this.settingEl.toggleClass("sls-setting-advanced", true); + } else if (conf?.level == LEVEL_POWER_USER) { + this.settingEl.toggleClass("sls-setting-poweruser", true); + } + if (this.placeHolderBuf && component instanceof TextComponent) { + component.setPlaceholder(this.placeHolderBuf); + } + if (opt?.onUpdate) this.addOnUpdate(opt.onUpdate); + } + async commitValue(value: AllSettings[T]) { + const key = this.selfKey as T; + if (key !== undefined) { + if (value != LiveSyncSetting.env.editingSettings[key]) { + LiveSyncSetting.env.editingSettings[key] = value; + if (!this.holdValue) { + await LiveSyncSetting.env.saveSettings([key]); + } + } + } + LiveSyncSetting.env.requestUpdate(); + } + autoWireText(key: AllStringItemKey, opt?: AutoWireOption) { + const conf = this.autoWireSetting(key, opt); + this.addText(text => { + this.autoWiredComponent = text; + const setValue = wrapMemo((value: string) => text.setValue(value)); + this.invalidateValue = () => setValue(`${LiveSyncSetting.env.editingSettings[key]}`); + this.invalidateValue(); + text.onChange(async (value) => { + await this.commitValue(value); + }); + if (opt?.isPassword) { + text.inputEl.setAttribute("type", "password"); + this.hasPassword = true; + } + this.autoWireComponent(this.autoWiredComponent, conf, opt); + }); + return this; + } + autoWireTextArea(key: AllStringItemKey, opt?: AutoWireOption) { + const conf = this.autoWireSetting(key, opt); + this.addTextArea(text => { + this.autoWiredComponent = text; + const setValue = wrapMemo((value: string) => text.setValue(value)); + this.invalidateValue = () => setValue(`${LiveSyncSetting.env.editingSettings[key]}`); + this.invalidateValue(); + text.onChange(async (value) => { + await this.commitValue(value); + }); + if (opt?.isPassword) { + text.inputEl.setAttribute("type", "password"); + this.hasPassword = true; + } + this.autoWireComponent(this.autoWiredComponent, conf, opt); + }); + return this; + } + autoWireNumeric(key: AllNumericItemKey, opt: AutoWireOption & { clampMin?: number; clampMax?: number; acceptZero?: boolean; }) { + const conf = this.autoWireSetting(key, opt); + this.addText(text => { + this.autoWiredComponent = text; + if (opt.clampMin) { + text.inputEl.setAttribute("min", `${opt.clampMin}`); + } + if (opt.clampMax) { + text.inputEl.setAttribute("max", `${opt.clampMax}`); + } + let lastError = false; + const setValue = wrapMemo((value: string) => text.setValue(value)); + this.invalidateValue = () => { + if (!lastError) setValue(`${LiveSyncSetting.env.editingSettings[key]}`); + }; + this.invalidateValue(); + text.onChange(async (TextValue) => { + const parsedValue = Number(TextValue); + const value = parsedValue; + let hasError = false; + if (isNaN(value)) hasError = true; + if (opt.clampMax && opt.clampMax < value) hasError = true; + if (opt.clampMin && opt.clampMin > value) { + if (opt.acceptZero && value == 0) { + // This is ok. + } else { + hasError = true; + } + } + if (!hasError) { + lastError = false; + this.setTooltip(``); + text.inputEl.toggleClass("sls-item-invalid-value", false); + await this.commitValue(value); + } else { + this.setTooltip(`The value should ${opt.clampMin || "~"} < value < ${opt.clampMax || "~"}`); + text.inputEl.toggleClass("sls-item-invalid-value", true); + lastError = true; + return false; + } + }); + text.inputEl.setAttr("type", "number"); + this.autoWireComponent(this.autoWiredComponent, conf, opt); + }); + return this; + } + autoWireToggle(key: AllBooleanItemKey, opt?: AutoWireOption) { + const conf = this.autoWireSetting(key, opt); + this.addToggle(toggle => { + this.autoWiredComponent = toggle; + const setValue = wrapMemo((value: boolean) => toggle.setValue(opt?.invert ? !value : value)); + this.invalidateValue = () => setValue(LiveSyncSetting.env.editingSettings[key] ?? false); + this.invalidateValue(); + + toggle.onChange(async (value) => { + await this.commitValue(opt?.invert ? !value : value); + }); + + this.autoWireComponent(this.autoWiredComponent, conf, opt); + }); + return this; + } + autoWireDropDown(key: AllStringItemKey, opt: AutoWireOption & { options: Record; }) { + const conf = this.autoWireSetting(key, opt); + this.addDropdown(dropdown => { + this.autoWiredComponent = dropdown; + const setValue = wrapMemo((value: string) => { + dropdown.setValue(value); + }); + + dropdown + .addOptions(opt.options); + + this.invalidateValue = () => setValue(LiveSyncSetting.env.editingSettings[key] || ""); + this.invalidateValue(); + dropdown.onChange(async (value) => { + await this.commitValue(value); + }); + this.autoWireComponent(this.autoWiredComponent, conf, opt); + }); + return this; + } + addApplyButton(keys: AllSettingItemKey[], text?: string) { + this.addButton((button) => { + this.applyButtonComponent = button; + this.watchDirtyKeys = unique([...keys, ...this.watchDirtyKeys]); + button.setButtonText(text ?? "Apply"); + button.onClick(async () => { + await LiveSyncSetting.env.saveSettings(keys); + LiveSyncSetting.env.reloadAllSettings(); + }); + LiveSyncSetting.env.requestUpdate(); + }); + return this; + } + addOnUpdate(func: () => OnUpdateResult) { + this.updateHandlers.add(func); + // this._applyOnUpdateHandlers(); + return this; + } + updateHandlers = new Set<() => OnUpdateResult>(); + + prevStatus: OnUpdateResult = {}; + + _getComputedStatus() { + let newConf = {} as OnUpdateResult; + for (const handler of this.updateHandlers) { + newConf = { + ...newConf, + ...handler(), + }; + } + return newConf; + } + _applyOnUpdateHandlers() { + if (this.updateHandlers.size > 0) { + const newConf = this._getComputedStatus(); + const keys = Object.keys(newConf) as [keyof OnUpdateResult]; + for (const k of keys) { + + if (k in this.prevStatus && this.prevStatus[k] == newConf[k]) { + continue; + } + // const newValue = newConf[k]; + switch (k) { + case "visibility": + this.settingEl.toggleClass("sls-setting-hidden", !(newConf[k] || false)); + this.prevStatus[k] = newConf[k]; + break; + case "classes": + break; + case "disabled": + this.setDisabled((newConf[k] || false)); + this.settingEl.toggleClass("sls-setting-disabled", (newConf[k] || false)); + this.prevStatus[k] = newConf[k]; + break; + case "isCta": + { + const component = this.autoWiredComponent; + if (component instanceof ButtonComponent) { + if (newConf[k]) { + component.setCta(); + } else { + component.removeCta(); + } + } + this.prevStatus[k] = newConf[k]; + } + break; + case "isWarning": + { + const component = this.autoWiredComponent; + if (component instanceof ButtonComponent) { + if (newConf[k]) { + component.setWarning(); + } else { + //TODO:IMPLEMENT + // component.removeCta(); + } + } + this.prevStatus[k] = newConf[k]; + } + break; + } + } + } + } + _onUpdate() { + if (this.applyButtonComponent) { + const isDirty = LiveSyncSetting.env.isSomeDirty(this.watchDirtyKeys); + this.applyButtonComponent.setDisabled(!isDirty); + if (isDirty) { + this.applyButtonComponent.setCta(); + } else { + this.applyButtonComponent.removeCta(); + } + } + if (this.selfKey && !LiveSyncSetting.env.isDirty(this.selfKey) && this.invalidateValue) { + this.invalidateValue(); + } + if (this.holdValue && this.selfKey) { + const isDirty = LiveSyncSetting.env.isDirty(this.selfKey); + const alt = isDirty ? `Original: ${LiveSyncSetting.env.initialSettings![this.selfKey]}` : ""; + this.controlEl.toggleClass("sls-item-dirty", isDirty); + if (!this.hasPassword) { + this.nameEl.toggleClass("sls-item-dirty-help", isDirty); + this.setTooltip(alt, { delay: 10, placement: "right" }); + } + } + this._applyOnUpdateHandlers(); + } +} diff --git a/src/ui/settingConstants.ts b/src/ui/settingConstants.ts index 0f6237d..8914f5a 100644 --- a/src/ui/settingConstants.ts +++ b/src/ui/settingConstants.ts @@ -198,8 +198,9 @@ export const SettingInformation: Partial.setting-item-control>input { -webkit-text-security: disc; } @@ -338,4 +403,27 @@ span.ls-mark-cr::after { background: var(--background-modifier-error); -webkit-filter: unset; filter: unset; +} + +.menu-setting-poweruser-disabled .sls-setting-poweruser { + display: none; +} + +.menu-setting-advanced-disabled .sls-setting-advanced { + display: none; +} + +.menu-setting-edgecase-disabled .sls-setting-edgecase { + display: none; +} + +.sls-setting-panel-title { + position: sticky; +} + +.sls-setting-panel-title { + top: 2em; + background-color: rgba(var(--background-primary), 0.3); + backdrop-filter: blur(4px); + border-radius: 30%; } \ No newline at end of file diff --git a/terser.config.mjs b/terser.config.mjs new file mode 100644 index 0000000..7554bfe --- /dev/null +++ b/terser.config.mjs @@ -0,0 +1,61 @@ +const banner = `/* +THIS IS A GENERATED/BUNDLED FILE BY ESBUILD AND TERSER +if you want to view the source, please visit the github repository of this plugin +*/ +`; + +const prod = process.argv[2] === "production"; +const terserOption = { + sourceMap: !prod + ? { + url: "inline", + } + : {}, + format: { + indent_level: 2, + beautify: true, + comments: "some", + ecma: 2018, + preamble: banner, + webkit: true, + }, + parse: { + // parse options + }, + compress: { + // compress options + defaults: false, + evaluate: true, + dead_code: true, + // directives: true, + // conditionals: true, + inline: 3, + join_vars: true, + loops: true, + passes: 4, + reduce_vars: true, + reduce_funcs: true, + arrows: true, + collapse_vars: true, + comparisons: true, + lhs_constants: true, + hoist_props: true, + side_effects: true, + ecma: 2018, + if_return: true, + unused: true, + }, + mangle: false, + + ecma: 2018, // specify one of: 5, 2015, 2016, etc. + enclose: false, // or specify true, or "args:values" + keep_classnames: true, + keep_fnames: true, + ie8: false, + module: false, + // nameCache: null, // or specify a name cache object + safari10: false, + toplevel: false, +}; + +export { terserOption }; diff --git a/tsconfig.json b/tsconfig.json index 94e5e92..19442a2 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -25,6 +25,7 @@ "ES7", "es2019.array", "ES2020.BigInt", + "ESNext.Intl" ] }, "include": [ From 48315d657d37c131a408ac940d59f186d50c9945 Mon Sep 17 00:00:00 2001 From: vorotamoroz Date: Tue, 24 Sep 2024 14:02:53 +0100 Subject: [PATCH 11/12] bump --- manifest.json | 2 +- package-lock.json | 4 ++-- package.json | 2 +- updates.md | 55 +++++++++++++++++++++-------------------------- updates_old.md | 31 ++++++++++++++++++++++++++ 5 files changed, 59 insertions(+), 35 deletions(-) diff --git a/manifest.json b/manifest.json index 62875c6..a87a752 100644 --- a/manifest.json +++ b/manifest.json @@ -1,7 +1,7 @@ { "id": "obsidian-livesync", "name": "Self-hosted LiveSync", - "version": "0.23.22", + "version": "0.23.23", "minAppVersion": "0.9.12", "description": "Community implementation of self-hosted livesync. Reflect your vault changes to some other devices immediately. Please make sure to disable other synchronize solutions to avoid content corruption or duplication.", "author": "vorotamoroz", diff --git a/package-lock.json b/package-lock.json index 11113e1..919fc63 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "obsidian-livesync", - "version": "0.23.22", + "version": "0.23.23", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "obsidian-livesync", - "version": "0.23.22", + "version": "0.23.23", "license": "MIT", "dependencies": { "@aws-sdk/client-s3": "^3.645.0", diff --git a/package.json b/package.json index 3a7d0e2..257daad 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "obsidian-livesync", - "version": "0.23.22", + "version": "0.23.23", "description": "Reflect your vault changes to some other devices immediately. Please make sure to disable other synchronize solutions to avoid content corruption or duplication.", "main": "main.js", "type": "module", diff --git a/updates.md b/updates.md index e4567cb..b0bd4b5 100644 --- a/updates.md +++ b/updates.md @@ -18,6 +18,30 @@ I have a lot of respect for that plugin, even though it is sometimes treated as Hooray for open source, and generous licences, and the sharing of knowledge by experts. #### Version history +- 0.23.23: + - Refined: + - Setting dialogue very slightly refined. + - The hodgepodge inside the `Hatch` pane has been sorted into more explicit categorised panes. + - Now we have new panes for: + - `Selector` + - `Advanced` + - `Power users` + - `Patches (Edge case)` + - Applying the settings will now be more informative. + - The header bar will be shown for applying the settings which needs a database rebuild. + - Applying methods are now more clearly navigated. + - Definitely, drastic change. I hope this will be more user-friendly. However, if you notice any issues, please let me know. I hope that nothing missed. + - New features: + - Word-segmented chunk building on users language + - Chunks can now be built with word-segmented data, enhancing efficiency for markdown files which contains the multiple sentences in a single line. + - This feature is enabled by default through `Use Segmented-splitter`. + - (Default: Disabled, Please be relived, I have learnt). + - Fixed: + - Sending chunks on `Send chunk in bulk` are now buffered to avoid the out-of-memory error. + - `Send chunk in bulk` is back to default disabled. (Sorry, not applied to the migrated users; I did not think we should deepen the wound any further "automatically"). + - Merging conflicts of JSON files are now works fine even if it contains `null`. + - Development: + - Implemented the logic for automatically generating the stub of document for the setting dialogue. - 0.23.22: - Fixed: - Case-insensitive file handling @@ -69,36 +93,5 @@ Hooray for open source, and generous licences, and the sharing of knowledge by e - Remote Storage Limit Notification dialogue has been fixed, now the chosen value is saved. - Improved: - The Enlarging button on the enlarging threshold dialogue now displays the new value. -- 0.23.19: - - Not released. -- 0.23.18: - - New feature: - - Per-file-saved customization sync has been shipped. - - We can synchronise plug-igs etc., more smoothly. - - Default: disabled. We need a small migration when enabling this. And all devices should be updated to v0.23.18. Once we enabled this, we lost compatibility with old versions. - - Customisation sync has got beta3. - - We can set `Flag` to each item to select the newest, automatically. - - This configuration is per device. - - Improved: - - Start-up speed has been improved. - - Fixed: - - On the customisation sync dialogue, buttons are kept within the screen. - - No more unnecessary entries on `data.json` for customisation sync. - - Selections are no longer lost while updating customisation items. - - Tidied on source codes: - - Many typos have been fixed. - - Some unnecessary type casting removed. -- 0.23.17: - - Improved: - - Overall performance has been improved by using PouchDB 9.0.0. - - Configuration mismatch detection is refined. We can resolve mismatches more smoothly and naturally. - More detail is on `troubleshooting.md` on the repository. - - Fixed: - - Customisation Sync will be disabled when a corrupted configuration is detected. - Therefore, the Device Name can be changed even in the event of a configuration mismatch. - - New feature: - - We can get a notification about the storage usage of the remote database. - - Default: We will be asked. - - If the remote storage usage approaches the configured value, we will be asked whether we want to Rebuild or increase the limit. Older notes is in [updates_old.md](https://github.com/vrtmrz/obsidian-livesync/blob/main/updates_old.md). \ No newline at end of file diff --git a/updates_old.md b/updates_old.md index 5f634e5..91e9ea0 100644 --- a/updates_old.md +++ b/updates_old.md @@ -18,6 +18,37 @@ I have a lot of respect for that plugin, even though it is sometimes treated as Hooray for open source, and generous licences, and the sharing of knowledge by experts. #### Version history +- 0.23.19: + - Not released. +- 0.23.18: + - New feature: + - Per-file-saved customization sync has been shipped. + - We can synchronise plug-igs etc., more smoothly. + - Default: disabled. We need a small migration when enabling this. And all devices should be updated to v0.23.18. Once we enabled this, we lost compatibility with old versions. + - Customisation sync has got beta3. + - We can set `Flag` to each item to select the newest, automatically. + - This configuration is per device. + - Improved: + - Start-up speed has been improved. + - Fixed: + - On the customisation sync dialogue, buttons are kept within the screen. + - No more unnecessary entries on `data.json` for customisation sync. + - Selections are no longer lost while updating customisation items. + - Tidied on source codes: + - Many typos have been fixed. + - Some unnecessary type casting removed. +- 0.23.17: + - Improved: + - Overall performance has been improved by using PouchDB 9.0.0. + - Configuration mismatch detection is refined. We can resolve mismatches more smoothly and naturally. + More detail is on `troubleshooting.md` on the repository. + - Fixed: + - Customisation Sync will be disabled when a corrupted configuration is detected. + Therefore, the Device Name can be changed even in the event of a configuration mismatch. + - New feature: + - We can get a notification about the storage usage of the remote database. + - Default: We will be asked. + - If the remote storage usage approaches the configured value, we will be asked whether we want to Rebuild or increase the limit. - 0.23.16: - Maintenance Update: - Library refining (Phase 1 - step 2). There are no significant changes on the user side. From cf3b9e55226450153863a62e4cca198d4c96995d Mon Sep 17 00:00:00 2001 From: vorotamoroz Date: Thu, 17 Oct 2024 10:26:19 +0100 Subject: [PATCH 12/12] Add manifest beta --- manifest-beta.json | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 manifest-beta.json diff --git a/manifest-beta.json b/manifest-beta.json new file mode 100644 index 0000000..08ef881 --- /dev/null +++ b/manifest-beta.json @@ -0,0 +1,10 @@ +{ + "id": "obsidian-livesync", + "name": "Self-hosted LiveSync", + "version": "0.24.0.dev-rc3", + "minAppVersion": "0.9.12", + "description": "Community implementation of self-hosted livesync. Reflect your vault changes to some other devices immediately. Please make sure to disable other synchronize solutions to avoid content corruption or duplication.", + "author": "vorotamoroz", + "authorUrl": "https://github.com/vrtmrz", + "isDesktopOnly": false +} \ No newline at end of file