mirror of
https://github.com/vrtmrz/obsidian-livesync.git
synced 2026-06-05 01:53:04 +03:00
Compare commits
28 Commits
0.25.70-pa
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
31050c9cb8 | ||
|
|
1b44ab9f2b | ||
|
|
3a78c70539 | ||
|
|
0f08ad435f | ||
|
|
96abd930b3 | ||
|
|
4581db45c4 | ||
|
|
05cab8ec66 | ||
|
|
b005625ef3 | ||
|
|
e5408b4dd7 | ||
|
|
95a9b1b41c | ||
|
|
2aa8bc1165 | ||
|
|
f8998d5441 | ||
|
|
26f5f54f24 | ||
|
|
99f3aca024 | ||
|
|
194397dd94 | ||
|
|
afa79d78dc | ||
|
|
9a9440a768 | ||
|
|
e375860af8 | ||
|
|
b57f34c15b | ||
|
|
b82fd9f04b | ||
|
|
bb77426b7b | ||
|
|
1bef5fbef3 | ||
|
|
7d2ba1b0b9 | ||
|
|
ac6b9a4dad | ||
|
|
225e2c5096 | ||
|
|
674d68b7d9 | ||
|
|
0e6dd300ef | ||
|
|
5a280c7919 |
65
AGENTS.md
Normal file
65
AGENTS.md
Normal file
@@ -0,0 +1,65 @@
|
||||
# AI Coding Assistant Instructions (AGENTS.md)
|
||||
|
||||
When working on this repository (writing code, comments, documentation, or commits), you MUST follow these guidelines to maintain consistency.
|
||||
|
||||
## Required Reference Files
|
||||
|
||||
Before making changes to documentation, user-facing text, or settings:
|
||||
1. Read [docs/terms.md](file:///Users/vorotamoroz/dev/js/obsidian-livesync/docs/terms.md) for terminology, vocabulary conventions, and technical definitions.
|
||||
2. Read [docs/settings.md](file:///Users/vorotamoroz/dev/js/obsidian-livesync/docs/settings.md) (and [docs/settings_ja.md](file:///Users/vorotamoroz/dev/js/obsidian-livesync/docs/settings_ja.md)) for UI settings and setting key mappings.
|
||||
3. Read [docs/troubleshooting.md](file:///Users/vorotamoroz/dev/js/obsidian-livesync/docs/troubleshooting.md) for troubleshooting guidelines and common recovery steps (such as flag files and SCRAM state).
|
||||
4. Read [devs.md](file:///Users/vorotamoroz/dev/js/obsidian-livesync/devs.md) for development workflows, module architecture, and testing infrastructure.
|
||||
|
||||
---
|
||||
|
||||
## Documentation and User-Facing Text Rules
|
||||
|
||||
Always adhere to the following stylistic and spelling rules:
|
||||
|
||||
1. **British English Spelling**:
|
||||
- Write all documentation and user-facing messages in British English. If in doubt, the BBC News Styleguide may be useful as a reference.
|
||||
- **Traditional Spelling (Trad-spelling)**: Use `-ise` and `-isation` suffixes instead of `-ize` and `-ization` (for example: 'initialisation', 'synchronisation', and 'organisation').
|
||||
- **Oxford Comma**: Use the serial (Oxford) comma to separate items in lists of three or more (for example: 'settings, snippets, and themes' instead of 'settings, snippets and themes').
|
||||
- **Logical Punctuation**: Place punctuation marks (such as commas and full stops) outside quotation marks unless they are part of the quoted text itself (for example: write 'dialogue', not 'dialogue,').
|
||||
|
||||
2. **No Contractions**:
|
||||
- Do not use contractions in general text or documentation (for example: write "do not" instead of "don't", "cannot" instead of "can't", and "is not" instead of "isn't").
|
||||
|
||||
3. **Quotation Style**:
|
||||
- Prefer single quotation marks (`'`) over double quotation marks (`"`) in general documentation text, unless the context requires double quotes (for example, inside JSON code blocks).
|
||||
|
||||
4. **Specific Terminology and Spelling**:
|
||||
- Use **'dialogue'** in documentation, user-facing messages, and general text. Use **'dialog'** only inside source code (e.g. class names, methods).
|
||||
- Use the hyphenated form **'plug-in'** in user-facing text. Use **'plugin'** only in codebase files, configuration settings, or technical contexts.
|
||||
|
||||
---
|
||||
|
||||
## Technical & Architecture Rules
|
||||
|
||||
1. **Database Structure**:
|
||||
- Remember that Self-hosted LiveSync splits files into **Metadata** (file properties, size, paths) and **Chunks** (actual content). Do not store raw content in the metadata document directly.
|
||||
2. **Setup and Recovery**:
|
||||
- **Fast Setup (Simple Fetch)** is the preferred flow for initial replication on secondary devices. It utilises stream-based replication for high speed and delays local file reflection to suppress temporary synchronisation warnings.
|
||||
- **Flag files** (such as `redflag.md`, `redflag2.md`, and `redflag3.md`) at the root of the vault control the boot-up sequence and trigger automated fetch/rebuild tasks.
|
||||
3. **Subrepositories**:
|
||||
- The directory [src/lib](file:///Users/vorotamoroz/dev/js/obsidian-livesync/src/lib) is a subrepository (Git submodule) pointing to the shared library `livesync-commonlib`. Do not make modifications inside this directory without careful consideration, as changes affect the shared library.
|
||||
4. **Application Directories**:
|
||||
- The directory [src/apps](file:///Users/vorotamoroz/dev/js/obsidian-livesync/src/apps) contains independent application modules:
|
||||
- `cli`: A Command Line Interface application. Tests specifically for the CLI (both unit and End-to-End tests) are located and executed within [src/apps/cli](file:///Users/vorotamoroz/dev/js/obsidian-livesync/src/apps/cli) using its local `package.json` scripts.
|
||||
- `webapp`: A Web-based application.
|
||||
- `webpeer`: A Web-based peer utility.
|
||||
|
||||
---
|
||||
|
||||
## Development & Verification Commands
|
||||
|
||||
Before submitting code, you should run verification scripts locally to ensure correct syntax and function.
|
||||
|
||||
1. **Lint and Type Checking**:
|
||||
- Run `npm run check` to perform code verification. This runs type-checking (`tsc-check`), ESLint (`lint`), and Svelte checks (`svelte-check`).
|
||||
2. **Unit Tests**:
|
||||
- Run `npm run test:unit` to execute fast local unit tests.
|
||||
- Run `npm run test` or `npm run test:full` for full testing suites (including dockerised services).
|
||||
3. **Build**:
|
||||
- Run `npm run build` to compile the production bundle (`main.js`).
|
||||
- Run `npm run dev` for the development watch/build task.
|
||||
49
README.md
49
README.md
@@ -4,7 +4,7 @@
|
||||
|
||||
Self-hosted LiveSync is a community-developed synchronisation plug-in available on all Obsidian-compatible platforms. It leverages robust server solutions such as CouchDB or object storage systems (e.g., MinIO, S3, R2, etc.) to ensure reliable data synchronisation.
|
||||
|
||||
Additionally, it supports peer-to-peer synchronisation using WebRTC, enabling you to synchronise your notes directly between devices without relying on a server. Documentations is available for [Peer-to-Peer Synchronisation](./docs/p2p_sync_updates_2026.md).
|
||||
Additionally, it supports peer-to-peer synchronisation using WebRTC, enabling you to synchronise your notes directly between devices without relying on a server. Documentation is available for [Peer-to-Peer Synchronisation](./docs/p2p_sync_updates_2026.md).
|
||||
|
||||

|
||||
|
||||
@@ -25,17 +25,17 @@ Additionally, it supports peer-to-peer synchronisation using WebRTC, enabling yo
|
||||
- Instead of keeping your device online as a stable peer, you can use two pseudo-peers:
|
||||
- [livesync-serverpeer](https://github.com/vrtmrz/livesync-serverpeer): A pseudo-client running on the server for receiving and sending data between devices.
|
||||
- [webpeer](https://github.com/vrtmrz/obsidian-livesync/tree/main/src/apps/webpeer): A pseudo-client for receiving and sending data between devices.
|
||||
- A pre-built instance is available at [fancy-syncing.vrtmrz.net/webpeer](https://fancy-syncing.vrtmrz.net/webpeer/) (hosted on the vrtmrz blog site). This is also peer-to-peer. Feel free to use it.
|
||||
- A pre-built instance is available at [fancy-syncing.vrtmrz.net/webpeer](https://fancy-syncing.vrtmrz.net/webpeer/) (hosted on the vrtmrz's blog site). This is also peer-to-peer. Feel free to use it.
|
||||
- For more information, refer to the [English explanatory article](https://fancy-syncing.vrtmrz.net/blog/0034-p2p-sync-en.html) or the [Japanese explanatory article](https://fancy-syncing.vrtmrz.net/blog/0034-p2p-sync).
|
||||
|
||||
This plug-in may be particularly useful for researchers, engineers, and developers who need to keep their notes fully self-hosted for security reasons. It is also suitable for anyone seeking the peace of mind that comes with knowing their notes remain entirely private.
|
||||
|
||||
>[!IMPORTANT]
|
||||
> - Before installing or upgrading this plug-in, please back up your vault.
|
||||
> - Do not enable this plug-in alongside another synchronisation solution at the same time (including iCloud and Obsidian Sync).
|
||||
> - Do not enable this plug-in alongside another synchronisation solution (including iCloud and Obsidian Sync).
|
||||
> - For backups, we also provide a plug-in called [Differential ZIP Backup](https://github.com/vrtmrz/diffzip).
|
||||
|
||||
## How to use
|
||||
## How to Use
|
||||
|
||||
### 3-minute setup - CouchDB on fly.io
|
||||
|
||||
@@ -43,54 +43,55 @@ This plug-in may be particularly useful for researchers, engineers, and develope
|
||||
|
||||
[](https://www.youtube.com/watch?v=7sa_I1832Xc)
|
||||
|
||||
1. [Setup CouchDB on fly.io](docs/setup_flyio.md)
|
||||
1. [Set up CouchDB on fly.io](docs/setup_flyio.md)
|
||||
2. Configure plug-in in [Quick Setup](docs/quick_setup.md)
|
||||
|
||||
### Manually Setup
|
||||
### Manual Setup
|
||||
|
||||
1. Setup the server
|
||||
1. [Setup CouchDB on fly.io](docs/setup_flyio.md)
|
||||
2. [Setup your CouchDB](docs/setup_own_server.md)
|
||||
1. Set up the server
|
||||
1. [Set up CouchDB on fly.io](docs/setup_flyio.md)
|
||||
2. [Set up your CouchDB](docs/setup_own_server.md)
|
||||
2. Configure plug-in in [Quick Setup](docs/quick_setup.md)
|
||||
> [!TIP]
|
||||
> Fly.io is no longer free. Fortunately, despite some issues, we can still use IBM Cloudant. Refer to [Setup IBM Cloudant](docs/setup_cloudant.md).
|
||||
> And also, we can use peer-to-peer synchronisation without a server. Or very cheap Object Storage -- Cloudflare R2 can be used for free.
|
||||
> HOWEVER, most importantly, we can use the server that we trust. Therefore, please set up your own server.
|
||||
> CouchDB can be run on a Raspberry Pi. (But please be careful about the security of your server).
|
||||
> Fly.io is no longer free. Fortunately, we can still use IBM Cloudant despite some limitations. Refer to [Set up IBM Cloudant](docs/setup_cloudant.md).
|
||||
> We can also use peer-to-peer synchronisation without a server. Alternatively, cheap object storage like Cloudflare R2 can be used for free.
|
||||
> However, most importantly, we can use a server that we trust. Therefore, please set up your own server.
|
||||
> CouchDB can also be run on a Raspberry Pi (please be mindful of your server's security).
|
||||
|
||||
|
||||
## Information in StatusBar
|
||||
## Information in the Status Bar
|
||||
|
||||
Synchronization status is shown in the status bar with the following icons.
|
||||
Synchronisation status is shown in the status bar with the following icons.
|
||||
|
||||
- Activity Indicator
|
||||
- 📲 Network request
|
||||
- Status
|
||||
- ⏹️ Stopped
|
||||
- 💤 LiveSync enabled. Waiting for changes
|
||||
- ⚡️ Synchronization in progress
|
||||
- ⚡️ Synchronisation in progress
|
||||
- ⚠ An error occurred
|
||||
- Statistical indicator
|
||||
- Statistical Indicators
|
||||
- ↑ Uploaded chunks and metadata
|
||||
- ↓ Downloaded chunks and metadata
|
||||
- Progress indicator
|
||||
- Progress Indicators
|
||||
- 📥 Unprocessed transferred items
|
||||
- 📄 Working database operation
|
||||
- 💾 Working write storage processes
|
||||
- ⏳ Working read storage processes
|
||||
- 🛫 Pending read storage processes
|
||||
- 📬 Batched read storage processes
|
||||
- ⚙️ Working or pending storage processes of hidden files
|
||||
- ⚙️ Working or pending storage processes for hidden files
|
||||
- 🧩 Waiting chunks
|
||||
- 🔌 Working Customisation items (Configuration, snippets, and plug-ins)
|
||||
- 🔌 Working customisation items (configuration, snippets, and plug-ins)
|
||||
|
||||
To prevent file and database corruption, please wait to stop Obsidian until all progress indicators have disappeared as possible (The plugin will also try to resume, though). Especially in case of if you have deleted or renamed files.
|
||||
To prevent file and database corruption, please avoid closing Obsidian until all progress indicators have disappeared as much as possible (although the plug-in will attempt to resume if interrupted). This is especially important if you have deleted or renamed files.
|
||||
|
||||
## Tips and Troubleshooting
|
||||
If you are having problems getting the plugin working see: [Tips and Troubleshooting](docs/troubleshooting.md).
|
||||
- If you want a faster and simpler initial replication when setting up subsequent devices, see the [Fast Setup Guide](docs/tips/fast-setup.md).
|
||||
- If you are having problems getting the plug-in working, see [Tips and Troubleshooting](docs/troubleshooting.md).
|
||||
|
||||
## Acknowledgements
|
||||
The project has been in continual progress and harmony thanks to:
|
||||
The project has been in continual progress and harmony thanks to the following:
|
||||
- Many [Contributors](https://github.com/vrtmrz/obsidian-livesync/graphs/contributors).
|
||||
- Many [GitHub Sponsors](https://github.com/sponsors/vrtmrz#sponsors).
|
||||
- JetBrains Community Programs / Support for Open-Source Projects. <img src="https://resources.jetbrains.com/storage/products/company/brand/logos/jetbrains.png" alt="JetBrains logo" height="24">
|
||||
@@ -98,7 +99,7 @@ The project has been in continual progress and harmony thanks to:
|
||||
May those who have contributed be honoured and remembered for their kindness and generosity.
|
||||
|
||||
## Development Guide
|
||||
Please refer to [Development Guide](devs.md) for development setup, testing infrastructure, code conventions, and more.
|
||||
Please refer to the [Development Guide](devs.md) for development setup, testing infrastructure, code conventions, and more.
|
||||
|
||||
## License
|
||||
|
||||
|
||||
@@ -78,7 +78,8 @@ NDAや類似の契約や義務、倫理を守る必要のある、研究者、
|
||||
|
||||
|
||||
## Tips and Troubleshooting
|
||||
何かこまったら、[Tips and Troubleshooting](docs/troubleshooting.md)をご参照ください。
|
||||
- 2台目以降のセットアップ時に、初期同期をより迅速かつ簡単に行うには、[ファストセットアップガイド](docs/tips/fast-setup_ja.md)をご参照ください。
|
||||
- 何かこまったら、[Tips and Troubleshooting](docs/troubleshooting.md)をご参照ください。
|
||||
|
||||
## License
|
||||
|
||||
|
||||
122
devs.md
122
devs.md
@@ -3,6 +3,76 @@
|
||||
|
||||
Self-hosted LiveSync is an Obsidian plugin for synchronising vaults across devices using CouchDB, MinIO/S3, or peer-to-peer WebRTC. The codebase uses a modular architecture with TypeScript, Svelte, and PouchDB.
|
||||
|
||||
## Build & Development Workflow
|
||||
|
||||
### Environment Setup
|
||||
|
||||
#### First-time Setup
|
||||
|
||||
This repository uses submodules by convention. Therefore, you must use the `--recursive` flag when cloning it.
|
||||
```bash
|
||||
git clone --recursive https://github.com/vrtmrz/obsidian-livesync
|
||||
npm ci
|
||||
npm run build
|
||||
```
|
||||
|
||||
Note: if you already cloned without submodules, run: `git submodule update --init --recursive`
|
||||
|
||||
#### Branch switching
|
||||
When switching branches, please make sure to update submodules as well, since they may be updated in the new branch.
|
||||
```bash
|
||||
git checkout --recurse-submodules 0.25.70-patch1 # tag or branch name
|
||||
npm ci
|
||||
npm run build
|
||||
```
|
||||
|
||||
### Commands
|
||||
|
||||
```bash
|
||||
npm run test:unit # Run unit tests with vitest (or `npm run test:unit:coverage` for coverage)
|
||||
npm run check # TypeScript and svelte type checking
|
||||
npm run dev # Development build with auto-rebuild (uses .env for test vault paths)
|
||||
npm run build # Production build
|
||||
npm run buildDev # Development build (one-time)
|
||||
npm run bakei18n # Pre-build step: compile i18n resources (YAML → JSON → TS)
|
||||
npm run test:unit # Run unit tests only (no Docker services required)
|
||||
npm test # Run Harness based vitest tests (requires Docker services), not recommended, unstable.
|
||||
```
|
||||
|
||||
### Tips
|
||||
|
||||
We can use CLI's E2E test command instead of `npm test`.
|
||||
|
||||
### Auto-copy to test vaults
|
||||
|
||||
To facilitate development and testing, the build process can automatically copy the built plugin to specified test vault
|
||||
|
||||
- Create `.env` file with `PATHS_TEST_INSTALL` pointing to test vault plug-in directories (`:` separated on Unix, `;` on Windows)
|
||||
- Development builds auto-copy to these paths on build whilst `npm run dev` is running (watch mode)
|
||||
|
||||
### Testing Infrastructure
|
||||
|
||||
- ~~**Deno Tests**: Unit tests for platform-independent code (e.g., `HashManager.test.ts`)~~
|
||||
- This is now obsolete, migrated to vitest.
|
||||
- **Vitest** (`vitest.config.ts`): E2E test by Browser-based-harness using Playwright, unit tests.
|
||||
- Unit tests should be `*.unit.spec.ts` and placed alongside the implementation file (e.g., `ChunkFetcher.unit.spec.ts`).
|
||||
|
||||
- **Docker Services**: Tests require CouchDB, MinIO (S3), and P2P services:
|
||||
```bash
|
||||
npm run test:docker-all:start # Start all test services
|
||||
npm run test:full # Run tests with coverage
|
||||
npm run test:docker-all:stop # Stop services
|
||||
```
|
||||
If some services are not needed, start only required ones (e.g., `test:docker-couchdb:start`)
|
||||
Note that if services are already running, starting script will fail. Please stop them first.
|
||||
|
||||
- **Test Structure**:
|
||||
- `test/suite/` - Integration tests for sync operations
|
||||
- `test/unit/` - Unit tests (via vitest, as harness is browser-based)
|
||||
- `test/harness/` - Mock implementations (e.g., `obsidian-mock.ts`)
|
||||
|
||||
|
||||
|
||||
## Architecture
|
||||
|
||||
### Module System
|
||||
@@ -47,48 +117,6 @@ Hence, the new feature should be implemented as follows:
|
||||
- **Development code**: Use `.dev.ts` suffix (replaced with `.prod.ts` in production)
|
||||
- **Path aliases**: `@/*` maps to `src/*`, `@lib/*` maps to `src/lib/src/*`
|
||||
|
||||
## Build & Development Workflow
|
||||
|
||||
### Commands
|
||||
|
||||
```bash
|
||||
npm run test:unit # Run unit tests with vitest (or `npm run test:unit:coverage` for coverage)
|
||||
npm run check # TypeScript and svelte type checking
|
||||
npm run dev # Development build with auto-rebuild (uses .env for test vault paths)
|
||||
npm run build # Production build
|
||||
npm run buildDev # Development build (one-time)
|
||||
npm run bakei18n # Pre-build step: compile i18n resources (YAML → JSON → TS)
|
||||
npm test # Run vitest tests (requires Docker services)
|
||||
```
|
||||
|
||||
### Environment Setup
|
||||
|
||||
- Clone with submodules: `git clone --recurse-submodules <repository-url>`
|
||||
- If you already cloned without them, run: `git submodule update --init --recursive`
|
||||
- The shared common library is provided by the `src/lib` submodule, and builds will fail if it is missing
|
||||
- Create `.env` file with `PATHS_TEST_INSTALL` pointing to test vault plug-in directories (`:` separated on Unix, `;` on Windows)
|
||||
- Development builds auto-copy to these paths on build
|
||||
|
||||
### Testing Infrastructure
|
||||
|
||||
- ~~**Deno Tests**: Unit tests for platform-independent code (e.g., `HashManager.test.ts`)~~
|
||||
- This is now obsolete, migrated to vitest.
|
||||
- **Vitest** (`vitest.config.ts`): E2E test by Browser-based-harness using Playwright, unit tests.
|
||||
- Unit tests should be `*.unit.spec.ts` and placed alongside the implementation file (e.g., `ChunkFetcher.unit.spec.ts`).
|
||||
|
||||
- **Docker Services**: Tests require CouchDB, MinIO (S3), and P2P services:
|
||||
```bash
|
||||
npm run test:docker-all:start # Start all test services
|
||||
npm run test:full # Run tests with coverage
|
||||
npm run test:docker-all:stop # Stop services
|
||||
```
|
||||
If some services are not needed, start only required ones (e.g., `test:docker-couchdb:start`)
|
||||
Note that if services are already running, starting script will fail. Please stop them first.
|
||||
- **Test Structure**:
|
||||
- `test/suite/` - Integration tests for sync operations
|
||||
- `test/unit/` - Unit tests (via vitest, as harness is browser-based)
|
||||
- `test/harness/` - Mock implementations (e.g., `obsidian-mock.ts`)
|
||||
|
||||
## Code Conventions
|
||||
|
||||
### Internationalisation (i18n)
|
||||
@@ -156,17 +184,17 @@ export class ModuleExample extends AbstractObsidianModule {
|
||||
|
||||
## Beta Policy
|
||||
|
||||
- Beta versions are denoted by appending `+patchedN` to the base version number.
|
||||
- Beta versions are denoted by appending `-patchedN` to the base version number.
|
||||
- `The base version` mostly corresponds to the stable release version.
|
||||
- e.g., v0.25.41+patched1 is equivalent to v0.25.42-beta1.
|
||||
- e.g., v0.25.41-patched1 is equivalent to v0.25.42-beta1.
|
||||
- This notation is due to SemVer incompatibility of Obsidian's plugin system.
|
||||
- Hence, this release is `0.25.41+patched1`.
|
||||
- Hence, this release is `0.25.41-patched1`.
|
||||
- Each beta version may include larger changes, but bug fixes will often not be included.
|
||||
- I think that in most cases, bug fixes will cause the stable releases.
|
||||
- They will not be released per branch or backported; they will simply be released.
|
||||
- Bug fixes for previous versions will be applied to the latest beta version.
|
||||
This means, if xx.yy.02+patched1 exists and there is a defect in xx.yy.01, a fix is applied to xx.yy.02+patched1 and yields xx.yy.02+patched2.
|
||||
If the fix is required immediately, it is released as xx.yy.02 (with xx.yy.01+patched1).
|
||||
This means, if xx.yy.02-patched1 exists and there is a defect in xx.yy.01, a fix is applied to xx.yy.02-patched1 and yields xx.yy.02-patched2.
|
||||
If the fix is required immediately, it is released as xx.yy.02 (with xx.yy.01-patched1).
|
||||
- This procedure remains unchanged from the current one.
|
||||
- At the very least, I am using the latest beta.
|
||||
- However, I will not be using a beta continuously for a week after it has been released. It is probably closer to an RC in nature.
|
||||
|
||||
@@ -53,7 +53,7 @@ This command synchronises with every peer whose **SYNC** toggle is enabled in th
|
||||
*Tip: Pair this command with a hotkey for a quick, keyboard-driven sync workflow.*
|
||||
|
||||
## 6. Technical Improvements in 2026
|
||||
- **Decoupled Architecture:** The UI is now strictly separated from the core logic, making the plugin more stable across different platforms (Mobile, Desktop, and Web).
|
||||
- **Decoupled Architecture:** The UI is now strictly separated from the core logic, making the plug-in more stable across different platforms (Mobile, Desktop, and Web).
|
||||
- **Svelte 5 UI:** The interface has been rebuilt for better responsiveness and clearer status indicators.
|
||||
- **Security:** All data remains end-to-end encrypted. Even the signalling relay never sees your actual notes.
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
[Japanese docs](./quick_setup_ja.md) - [Chinese docs](./quick_setup_cn.md).
|
||||
|
||||
The plugin has so many configuration options to deal with different circumstances. However, only a few settings are required in the normal cases. Therefore, `The Setup wizard` has been implemented to simplify the setup.
|
||||
The plug-in has so many configuration options to deal with different circumstances. However, only a few settings are required in the normal cases. Therefore, `The Setup wizard` has been implemented to simplify the setup.
|
||||
|
||||

|
||||
|
||||
@@ -10,7 +10,7 @@ There are three methods to set up Self-hosted LiveSync.
|
||||
|
||||
1. [Using setup URIs](#1-using-setup-uris) *(Recommended)*
|
||||
2. [Minimal setup](#2-minimal-setup)
|
||||
3. [Full manually setup the and Enable on this dialogue](#3-manually-setup)
|
||||
3. [Fully manual setup and enabling on this dialogue](#3-manually-setup)
|
||||
|
||||
## At the first device
|
||||
|
||||
@@ -24,7 +24,7 @@ There are three methods to set up Self-hosted LiveSync.
|
||||
|
||||
In this procedure, [this video](https://youtu.be/7sa_I1832Xc?t=146) may help us.
|
||||
|
||||
1. Click `Use` button (Or launch `Use the copied setup URI` from Command palette).
|
||||
1. Click the `Use` button (or launch the `Use the copied setup URI (Formerly Open setup URI)` command from the command palette).
|
||||
2. Paste the Setup URI into the dialogue
|
||||
3. Type the passphrase of the Setup URI
|
||||
4. Answer `yes` for `Importing LiveSync's conf, OK?`.
|
||||
@@ -107,23 +107,27 @@ Note: If you are going to use Object Storage, you cannot select `LiveSync`.
|
||||
|
||||
Select any synchronisation methods we want to use and `Apply`. If database initialisation is required, it will be performed at this time. When `All done!` is displayed, we are ready to synchronise.
|
||||
|
||||
The dialogue of `Copy settings as a new setup URI` will be open automatically. Please input a passphrase to encrypt the new `Setup URI`. (This passphrase is to encrypt the setup URI, not the vault).
|
||||
The dialogue of `Copy current settings as a new setup URI` will open automatically. Please input a passphrase to encrypt the new `Setup URI`. (This passphrase is to encrypt the setup URI, not the vault).
|
||||
|
||||

|
||||
|
||||
The Setup URI will be copied to the clipboard, please make a note(Not in Obsidian) of this.
|
||||
|
||||
>[!TIP]
|
||||
We can copy this in any time by `Copy current settings as a new setup URI`.
|
||||
We can copy this at any time by running the "Copy settings as a new setup URI" command from the command palette (or clicking the "Copy the current settings to a Setup URI" button in the settings UI).
|
||||
|
||||
### 3. Manually setup
|
||||
|
||||
It is strongly recommended to perform a "minimal set-up" first and set up the other contents after making sure has been synchronised.
|
||||
|
||||
However, if you have some specific reasons to configure it manually, please click the `Enable` button of `Enable LiveSync on this device as the set-up was completed manually`.
|
||||
And, please copy the setup URI by `Copy current settings as a new setup URI` and make a note(Not in Obsidian) of this.
|
||||
And, please copy the setup URI by running the "Copy settings as a new setup URI" command (or using the "Copy the current settings to a Setup URI" button) and make a note(Not in Obsidian) of this.
|
||||
|
||||
## At the subsequent device
|
||||
After installing Self-hosted LiveSync on the first device, we should have a setup URI. **The first choice is to use it**. Please share it with the device you want to setup.
|
||||
|
||||
It is completely same as [Using setup URIs on the first device](#1-using-setup-uris). Please refer it.
|
||||
|
||||
> [!TIP]
|
||||
> **Fast Setup (Simple Fetch)**
|
||||
> In recent versions, when you import a Setup URI or trigger a Fetch All, the plug-in boots in scheduled fetch mode and runs a simplified **Fast Setup** process. This allows you to choose your sync strategy with a single dialogue and performs initial synchronisation in one step. Refer to the [Fast Setup Guide](./tips/fast-setup.md) for more details.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Quick setup
|
||||
このプラグインには、いろいろな状況に対応するための非常に多くの設定オプションがあります。しかし、実際に使用する設定項目はそれほど多くはありません。そこで、初期設定を簡略化するために、「セットアップウィザード」を実装しています。
|
||||
※なお、次のデバイスからは、`Copy setup URI`と`Open setup URI`を使ってセットアップしてください。
|
||||
※なお、次のデバイスからは、`現在の設定をセットアップURIにコピー`と`セットアップURIで接続`を使ってセットアップしてください。
|
||||
|
||||
|
||||
## Wizardの使い方
|
||||
@@ -71,7 +71,8 @@ Fixボタンがなくなり、すべてチェックマークになれば完了
|
||||

|
||||
|
||||
Presetsから、いずれかの同期方法を選び`Apply`を行うと、必要に応じてローカル・リモートのデータベースを初期化・構築します。
|
||||
All done! と表示されれば完了です。自動的に、`Copy setup URI`が開き、`Setup URI`を暗号化するパスフレーズを聞かれます。
|
||||
「All done!」(日本語環境では「完了!」)と表示されれば完了です。自動的に、「現在の設定をセットアップURIにコピー」のダイアログが開き、Setup URIを暗号化するためのパスフレーズを求められます(このパスフレーズはSetup URIを暗号化するためのもので、Vault自体の暗号化キーではありません)。
|
||||
パスフレーズを入力すると、クリップボードにSetup URIが保存されますので、これを2台目以降のデバイスに何らかの方法で転送してください。
|
||||
|
||||

|
||||
|
||||
@@ -79,10 +80,14 @@ All done! と表示されれば完了です。自動的に、`Copy setup URI`が
|
||||
クリップボードにSetup URIが保存されますので、これを2台目以降のデバイスに何らかの方法で転送してください。
|
||||
|
||||
# 2台目以降の設定方法
|
||||
2台目の端末にSelf-hosted LiveSyncをインストールしたあと、コマンドパレットから`Open setup URI`を選択し、転送したsetup URIを入力します。その後、パスフレーズを入力するとセットアップ用のウィザードが開きます。
|
||||
2台目の端末にSelf-hosted LiveSyncをインストールしたあと、コマンドパレットから`Use the copied setup URI (Formerly Open setup URI)`を選択し、転送したsetup URIを入力します。その後、パスフレーズを入力するとセットアップ用のウィザードが開きます。
|
||||
下記のように答えてください。
|
||||
|
||||
- `Importing LiveSync's conf, OK?` に `Yes`
|
||||
- `How would you like to set it up?` に `Set it up as secondary or subsequent device`
|
||||
|
||||
これで設定が反映され、レプリケーションが開始されます。
|
||||
これで設定が反映され、レプリケーションが開始されます。
|
||||
|
||||
> [!TIP]
|
||||
> **ファストセットアップ (Fast Setup)**
|
||||
> 近年のバージョンでは、セットアップURIの読み込みやデータの全取得(Fetch All)を実行した際、より簡単に同期戦略を選択して即座に初期同期を完了できる **ファストセットアップ (Simple Fetch)** フローが利用できます。詳細は [ファストセットアップガイド](./tips/fast-setup_ja.md) をご参照ください。
|
||||
296
docs/settings.md
296
docs/settings.md
@@ -12,7 +12,7 @@ There are many settings in Self-hosted LiveSync. This document describes each se
|
||||
| 🛰️ | [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) |
|
||||
| 🔌 | [6. Customisation sync (Advanced)](#6-customisation-sync-advanced) |
|
||||
| 🧰 | [7. Hatch](#7-hatch) |
|
||||
| 🔧 | [8. Advanced (Advanced)](#8-advanced-advanced) |
|
||||
| 💪 | [9. Power users (Power User)](#9-power-users-power-user) |
|
||||
@@ -68,7 +68,7 @@ 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) |
|
||||
| 🔌 | [6. Customisation sync (Advanced)](#6-customisation-sync-advanced) |
|
||||
| 🔧 | [8. Advanced (Advanced)](#8-advanced-advanced) |
|
||||
|
||||
#### Enable poweruser features
|
||||
@@ -120,6 +120,18 @@ Setting key: showStatusOnStatusbar
|
||||
|
||||
We can show the status of synchronisation on the status bar. (Default: On)
|
||||
|
||||
#### Show status icon instead of file warnings banner
|
||||
|
||||
Setting key: hideFileWarningNotice
|
||||
|
||||
If enabled, the ⛔ icon will be shown inside the status instead of the file warnings banner. No details will be shown.
|
||||
|
||||
#### Network warning style
|
||||
|
||||
Setting key: networkWarningStyle
|
||||
|
||||
How to display network errors when the sync server is unreachable.
|
||||
|
||||
### 2. Logging
|
||||
|
||||
#### Show only notifications
|
||||
@@ -138,11 +150,19 @@ Show verbose log. Please enable when you report the logs
|
||||
|
||||
### 1. Remote Server
|
||||
|
||||
Self-hosted LiveSync supports multiple remote connection profiles under **Remote Server** -> **Remote Databases**. This allows you to save and switch between multiple databases or bucket configurations in a single vault.
|
||||
|
||||
- **➕ Add new connection**: Create a new connection profile by launching the setup dialogue.
|
||||
- **📥 Import connection**: Paste a connection string (e.g., `sls+https://...`, `sls+s3://...`, `sls+p2p://...`) to import a remote configuration profile.
|
||||
- **🔧 Configure**: Open the setup dialogue to edit settings for the selected connection profile.
|
||||
- **✅ Activate**: Select and activate this profile as the current active remote.
|
||||
- **🗑️ Delete**: Remove this connection profile from the list.
|
||||
|
||||
#### Remote Type
|
||||
|
||||
Setting key: remoteType
|
||||
|
||||
Remote server type
|
||||
The active remote server type. This is automatically projected to the legacy configuration when you activate a connection profile.
|
||||
|
||||
### 2. Notification
|
||||
|
||||
@@ -172,6 +192,14 @@ Setting key: usePathObfuscation
|
||||
|
||||
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.
|
||||
|
||||
#### Encryption Algorithm
|
||||
|
||||
Setting key: E2EEAlgorithm
|
||||
|
||||
The encryption algorithm version used for end-to-end encryption.
|
||||
- `v2` (V2: AES-256-GCM With HKDF): Recommended and default version.
|
||||
- `forceV1` or `""` (V1: Legacy): Older legacy encryption. Only use this if you have an existing vault encrypted in the legacy format.
|
||||
|
||||
#### Use dynamic iteration count (Experimental)
|
||||
|
||||
Setting key: useDynamicIterationCount
|
||||
@@ -192,30 +220,62 @@ Fetch necessary settings from already configured remote server.
|
||||
|
||||
### 5. Minio,S3,R2
|
||||
|
||||
These settings are configured within the S3/MinIO/R2 Setup dialogue when adding (`➕`) or editing (`🔧`) an Object Storage connection profile.
|
||||
|
||||
#### Endpoint URL
|
||||
|
||||
Setting key: endpoint
|
||||
|
||||
The URL of the remote storage endpoint.
|
||||
Note: Only Secure (HTTPS) connections can be used on Obsidian Mobile.
|
||||
|
||||
#### Access Key
|
||||
|
||||
Setting key: accessKey
|
||||
|
||||
The Access Key ID used for authentication.
|
||||
|
||||
#### Secret Key
|
||||
|
||||
Setting key: secretKey
|
||||
|
||||
The Secret Access Key used for authentication.
|
||||
|
||||
#### Region
|
||||
|
||||
Setting key: region
|
||||
|
||||
The storage region (e.g., `us-east-1`, or `auto` for Cloudflare R2).
|
||||
|
||||
#### Bucket Name
|
||||
|
||||
Setting key: bucket
|
||||
|
||||
The name of the bucket to store synchronised files.
|
||||
|
||||
#### Use Custom HTTP Handler
|
||||
|
||||
Setting key: useCustomRequestHandler
|
||||
Enable this if your Object Storage doesn't support CORS
|
||||
|
||||
This option is labeled **Use internal API** in the setup dialogue. Enable this if your Object Storage does not support CORS. It uses Obsidian's internal API to communicate with the S3 server, which is not compliant with web standards but can bypass CORS restrictions. Note that this might break in future Obsidian versions.
|
||||
|
||||
#### File prefix on the bucket
|
||||
|
||||
Setting key: bucketPrefix
|
||||
|
||||
This option is labeled **Folder Prefix** in the setup dialogue. Effectively a directory. Should end with `/`. e.g., `vault-name/`. Leave blank to store data at the root of the bucket.
|
||||
|
||||
#### Enable forcePathStyle
|
||||
|
||||
Setting key: forcePathStyle
|
||||
|
||||
This option is labeled **Use Path-Style Access** in the setup dialogue. If enabled, the forcePathStyle option will be used for bucket operations.
|
||||
|
||||
#### Custom Headers
|
||||
|
||||
Setting key: bucketCustomHeaders
|
||||
|
||||
Custom HTTP headers to include in every request sent to the Object Storage bucket. Specify them in the format `Header-Name: Value`, with each header on a new line.
|
||||
|
||||
#### Test Connection
|
||||
|
||||
@@ -223,24 +283,82 @@ Enable this if your Object Storage doesn't support CORS
|
||||
|
||||
### 6. CouchDB
|
||||
|
||||
These settings are configured within the CouchDB Setup dialogue when adding (`➕`) or editing (`🔧`) a CouchDB connection profile.
|
||||
|
||||
#### Server URI
|
||||
|
||||
Setting key: couchDB_URI
|
||||
|
||||
The URI of the CouchDB server.
|
||||
Note: Only Secure (HTTPS) connections can be used on Obsidian Mobile. The URI must not end with a trailing slash.
|
||||
|
||||
#### Username
|
||||
|
||||
Setting key: couchDB_USER
|
||||
username
|
||||
|
||||
The username used to authenticate with CouchDB.
|
||||
|
||||
#### Password
|
||||
|
||||
Setting key: couchDB_PASSWORD
|
||||
password
|
||||
|
||||
The password used to authenticate with CouchDB.
|
||||
|
||||
#### Database Name
|
||||
|
||||
Setting key: couchDB_DBNAME
|
||||
|
||||
The name of the database.
|
||||
Note: The database name cannot contain capital letters, spaces, or special characters other than `_$()+/-`, and cannot start with an underscore (`_`).
|
||||
|
||||
#### Use Request API to avoid inevitable CORS problem
|
||||
|
||||
Setting key: useRequestAPI
|
||||
|
||||
This option is labeled **Use Internal API** in the setup dialogue. If enabled, Obsidian's internal request API will be used to bypass CORS restrictions. This is a workaround that may not be compliant with web standards and is less secure. Note that this might break in future Obsidian versions.
|
||||
|
||||
#### Custom Headers
|
||||
|
||||
Setting key: couchDB_CustomHeaders
|
||||
|
||||
Custom HTTP headers to include in every request sent to the CouchDB server. Specify them in the format `Header-Name: Value`, with each header on a new line.
|
||||
|
||||
#### Use JWT Authentication
|
||||
|
||||
Setting key: useJWT
|
||||
|
||||
Enable JSON Web Token (JWT) authentication for CouchDB. This is an experimental feature and has not been thoroughly verified.
|
||||
|
||||
#### JWT Algorithm
|
||||
|
||||
Setting key: jwtAlgorithm
|
||||
|
||||
The algorithm used to sign the JWT. Supported algorithms: `HS256`, `HS512`, `ES256`, `ES512`.
|
||||
|
||||
#### JWT Expiration Duration (minutes)
|
||||
|
||||
Setting key: jwtExpDuration
|
||||
|
||||
Token expiration duration in minutes. Set to 0 to disable expiration.
|
||||
|
||||
#### JWT Key
|
||||
|
||||
Setting key: jwtKey
|
||||
|
||||
The secret key (for HS256/HS512) or the PKCS#8 PEM-formatted private key (for ES256/ES512) used to sign the JWT.
|
||||
|
||||
#### JWT Key ID (kid)
|
||||
|
||||
Setting key: jwtKid
|
||||
|
||||
The Key ID (`kid`) header parameter included in the JWT.
|
||||
|
||||
#### JWT Subject (sub)
|
||||
|
||||
Setting key: jwtSub
|
||||
|
||||
The subject (`sub`) claim of the JWT, which should match your CouchDB username.
|
||||
|
||||
#### Test Database Connection
|
||||
|
||||
Open database connection. If the remote database is not found and you have permission to create a database, the database will be created.
|
||||
@@ -251,26 +369,100 @@ Checks and fixes any potential issues with the database config.
|
||||
|
||||
#### Apply Settings
|
||||
|
||||
### 7. Peer-to-Peer (P2P) Synchronisation
|
||||
|
||||
#### Enable P2P Synchronisation
|
||||
|
||||
Setting key: P2P_Enabled
|
||||
|
||||
Enable direct peer-to-peer synchronisation via WebRTC.
|
||||
|
||||
#### Relay URL
|
||||
|
||||
Setting key: P2P_relays
|
||||
|
||||
The WebSocket relay server URL(s) used for coordinating P2P connections via WebRTC. Multiple URLs can be separated by commas.
|
||||
|
||||
#### Group ID
|
||||
|
||||
Setting key: P2P_roomID
|
||||
|
||||
The room ID or Group ID used to identify your group of synchronising devices. All devices you wish to synchronise must use the same Group ID. You can enter any custom string or generate a random Group ID.
|
||||
|
||||
#### Passphrase
|
||||
|
||||
Setting key: P2P_passphrase
|
||||
|
||||
The password or passphrase used to authenticate and encrypt P2P communication. All devices must use the same passphrase.
|
||||
|
||||
#### Device Peer ID
|
||||
|
||||
Setting key: P2P_DevicePeerName
|
||||
|
||||
The peer name or identifier of this device in the P2P network. This should be unique within your group of devices.
|
||||
|
||||
#### Automatically start P2P connection on launch
|
||||
|
||||
Setting key: P2P_AutoStart
|
||||
|
||||
This option is labeled **Auto Start P2P Connection** in the setup dialogue. If enabled, the P2P connection will start automatically when the plug-in launches.
|
||||
|
||||
#### Automatically broadcast changes to connected peers
|
||||
|
||||
Setting key: P2P_AutoBroadcast
|
||||
|
||||
This option is labeled **Auto Broadcast Changes** in the setup dialogue. If enabled, changes will be automatically broadcasted to connected peers, requesting them to fetch the changes.
|
||||
|
||||
#### TURN Server URLs (comma-separated)
|
||||
|
||||
Setting key: P2P_turnServers
|
||||
|
||||
A comma-separated list of TURN/STUN server URLs. Used to relay P2P connections when direct WebRTC connection fails due to strict NAT or firewalls. In most cases, these can be left blank.
|
||||
|
||||
#### TURN Username
|
||||
|
||||
Setting key: P2P_turnUsername
|
||||
|
||||
The username for authentication with the TURN server.
|
||||
|
||||
#### TURN Credential
|
||||
|
||||
Setting key: P2P_turnCredential
|
||||
|
||||
The password or credential for authentication with the TURN server.
|
||||
|
||||
## 4. Sync Settings
|
||||
|
||||
### 1. Synchronization Preset
|
||||
### 1. Synchronisation Preset
|
||||
|
||||
#### Presets
|
||||
|
||||
Setting key: preset
|
||||
Apply preset configuration
|
||||
|
||||
### 2. Synchronization Method
|
||||
### 2. Synchronisation Method
|
||||
|
||||
#### Sync Mode
|
||||
|
||||
Setting key: syncMode
|
||||
|
||||
The trigger mechanism for synchronisation.
|
||||
- **LiveSync** (`LIVESYNC`): Real-time, continuous, bidirectional synchronisation.
|
||||
Note: This requires a CouchDB or WebRTC P2P remote server. It is not supported for S3-compatible Object Storage.
|
||||
- **Periodic Sync** (`PERIODIC`): Synchronisation is performed at regular intervals specified by the **Periodic Sync interval** setting.
|
||||
- **On Events** (`ONEVENTS`): Synchronisation is triggered by specific events (such as save, file open, or startup) configured via the toggles below.
|
||||
|
||||
#### Periodic Sync interval
|
||||
|
||||
Setting key: periodicReplicationInterval
|
||||
Interval (sec)
|
||||
|
||||
#### Minimum interval for syncing
|
||||
|
||||
Setting key: syncMinimumInterval
|
||||
|
||||
The minimum interval for automatic synchronisation on event.
|
||||
|
||||
#### Sync on Save
|
||||
|
||||
Setting key: syncOnSave
|
||||
@@ -323,7 +515,7 @@ Move remotely deleted files to the trash, instead of deleting.
|
||||
#### Keep empty folder
|
||||
|
||||
Setting key: doNotDeleteFolder
|
||||
Should we keep folders that don't have any files inside?
|
||||
Should we keep folders that do not have any files inside?
|
||||
|
||||
### 5. Conflict resolution (Advanced)
|
||||
|
||||
@@ -360,7 +552,7 @@ Setting key: notifyAllSettingSyncFile
|
||||
|
||||
### 7. Hidden Files (Advanced)
|
||||
|
||||
#### Hidden file synchronization
|
||||
#### Hidden file synchronisation
|
||||
|
||||
#### Enable Hidden files sync
|
||||
|
||||
@@ -373,6 +565,12 @@ Setting key: syncInternalFilesBeforeReplication
|
||||
Setting key: syncInternalFilesInterval
|
||||
Seconds, 0 to disable
|
||||
|
||||
#### Suppress notification of hidden files change
|
||||
|
||||
Setting key: suppressNotifyHiddenFilesChange
|
||||
|
||||
If enabled, the notification of hidden files change will be suppressed.
|
||||
|
||||
## 5. Selector (Advanced)
|
||||
|
||||
### 1. Normal Files
|
||||
@@ -406,42 +604,42 @@ Comma separated `.gitignore, .dockerignore`
|
||||
|
||||
#### Add default patterns
|
||||
|
||||
## 6. Customization sync (Advanced)
|
||||
## 6. Customisation sync (Advanced)
|
||||
|
||||
### 1. Customization Sync
|
||||
### 1. Customisation Sync
|
||||
|
||||
#### Device name
|
||||
|
||||
Setting key: deviceAndVaultName
|
||||
Unique name between all synchronized devices. To edit this setting, please disable customization sync once.
|
||||
Unique name between all synchronised devices. To edit this setting, please disable customisation sync once.
|
||||
|
||||
#### Per-file-saved customization sync
|
||||
#### Per-file-saved customisation 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.
|
||||
If enabled, per-file efficient customisation sync will be used. We need a small migration when enabling this. And all devices should be updated to v0.23.18. Once we enable this, we lose compatibility with old versions.
|
||||
|
||||
#### Enable customization sync
|
||||
#### Enable customisation sync
|
||||
|
||||
Setting key: usePluginSync
|
||||
|
||||
#### Scan customization automatically
|
||||
#### Scan customisation automatically
|
||||
|
||||
Setting key: autoSweepPlugins
|
||||
Scan customization before replicating.
|
||||
Scan customisation before replicating.
|
||||
|
||||
#### Scan customization periodically
|
||||
#### Scan customisation periodically
|
||||
|
||||
Setting key: autoSweepPluginsPeriodic
|
||||
Scan customization every 1 minute.
|
||||
Scan customisation every 1 minute.
|
||||
|
||||
#### Notify customized
|
||||
#### Notify customised
|
||||
|
||||
Setting key: notifyPluginOrSettingUpdated
|
||||
Notify when other device has newly customized.
|
||||
Notify when another device has newly customised.
|
||||
|
||||
#### Open
|
||||
|
||||
Open the dialog
|
||||
Open the dialogue
|
||||
|
||||
## 7. Hatch
|
||||
|
||||
@@ -456,14 +654,18 @@ Warning! This will have a serious impact on performance. And the logs will not b
|
||||
|
||||
### 2. Scram Switches
|
||||
|
||||
Emergency controls to suspend synchronisation processes in order to prevent database corruption. If a critical mismatch or sync error occurs, the plug-in may automatically enter a Scram state and suspend operations.
|
||||
|
||||
#### Suspend file watching
|
||||
|
||||
Setting key: suspendFileWatching
|
||||
Stop watching for file changes.
|
||||
|
||||
Stop watching for local file changes.
|
||||
|
||||
#### Suspend database reflecting
|
||||
|
||||
Setting key: suspendParseReplicationResult
|
||||
|
||||
Stop reflecting database changes to storage files.
|
||||
|
||||
### 3. Recovery and Repair
|
||||
@@ -486,7 +688,7 @@ Compare the content of files between on local database and storage. If not match
|
||||
|
||||
#### Back to non-configured
|
||||
|
||||
#### Delete all customization sync data
|
||||
#### Delete all customisation sync data
|
||||
|
||||
## 8. Advanced (Advanced)
|
||||
|
||||
@@ -507,6 +709,12 @@ Setting key: hashCacheMaxAmount
|
||||
|
||||
Setting key: customChunkSize
|
||||
|
||||
#### Chunk Splitter
|
||||
|
||||
Setting key: chunkSplitterVersion
|
||||
|
||||
Select the chunk splitter version; V3 is the most efficient. If you experience issues, please choose Default or Legacy.
|
||||
|
||||
#### Use splitting-limit-capped chunk splitter
|
||||
|
||||
Setting key: enableChunkSplitterV2
|
||||
@@ -532,6 +740,12 @@ Setting key: concurrencyOfReadChunksOnline
|
||||
|
||||
Setting key: minimumIntervalOfReadChunksOnline
|
||||
|
||||
#### Maximum size of chunks to send in one request
|
||||
|
||||
Setting key: sendChunksBulkMaxSize
|
||||
|
||||
Limit the maximum size of chunks to send in a single bulk request (MB).
|
||||
|
||||
## 9. Power users (Power User)
|
||||
|
||||
### 1. Remote Database Tweak
|
||||
@@ -639,7 +853,7 @@ If this enabled, All files are handled as case-Sensitive (Previous behaviour).
|
||||
|
||||
### 4. Compatibility (Internal API Usage)
|
||||
|
||||
#### Scan changes on customization sync
|
||||
#### Scan changes on customisation sync
|
||||
|
||||
Setting key: watchInternalFileChanges
|
||||
Do not use internal API
|
||||
@@ -664,7 +878,13 @@ Setting key: doNotSuspendOnFetching
|
||||
#### Keep empty folder
|
||||
|
||||
Setting key: doNotDeleteFolder
|
||||
Should we keep folders that don't have any files inside?
|
||||
Should we keep folders that do not have any files inside?
|
||||
|
||||
#### Process files even if seems to be corrupted
|
||||
|
||||
Setting key: processSizeMismatchedFiles
|
||||
|
||||
Enable this setting to process files with size mismatches, which can sometimes be created by certain external APIs or integrations.
|
||||
|
||||
### 7. Edge case addressing (Processing)
|
||||
|
||||
@@ -684,17 +904,25 @@ If enabled, the file under 1kb will be processed in the UI thread.
|
||||
|
||||
Setting key: disableCheckingConfigMismatch
|
||||
|
||||
### 9. Remediation
|
||||
|
||||
#### Maximum file modification time for reflected file events
|
||||
|
||||
Setting key: maxMTimeForReflectEvents
|
||||
|
||||
Files with modification times greater than this value (in seconds since the Unix epoch) will not have their events reflected. Set to 0 to disable this limit.
|
||||
|
||||
## 11. Maintenance
|
||||
|
||||
### 1. Scram!
|
||||
|
||||
#### Lock Server
|
||||
|
||||
Lock the remote server to prevent synchronization with other devices.
|
||||
Lock the remote server to prevent synchronisation with other devices.
|
||||
|
||||
#### Emergency restart
|
||||
|
||||
Disables all synchronization and restart.
|
||||
Disables all synchronisation and restart.
|
||||
|
||||
### 2. Syncing
|
||||
|
||||
@@ -712,17 +940,13 @@ Initialise journal sent history. On the next sync, every item except this device
|
||||
|
||||
### 3. Rebuilding Operations (Local)
|
||||
|
||||
#### Fetch from remote
|
||||
#### Reset Synchronisation on This Device
|
||||
|
||||
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
|
||||
#### Overwrite Server Data with This Device's Files
|
||||
|
||||
Rebuild local and remote database with local files.
|
||||
|
||||
@@ -752,7 +976,7 @@ Delete all data on the remote server.
|
||||
|
||||
#### Run database cleanup
|
||||
|
||||
Attempt to shrink the database by deleting unused chunks. This may not work consistently. Use the 'Rebuild everything' under Total Overhaul.
|
||||
Attempt to shrink the database by deleting unused chunks. This may not work consistently. Use the 'Overwrite Server Data with This Device's Files' under Reset Synchronisation information.
|
||||
|
||||
### 7. Reset
|
||||
|
||||
|
||||
@@ -3,23 +3,133 @@
|
||||
# このプラグインの設定項目
|
||||
|
||||
## Remote Database Configurations
|
||||
同期先のデータベース設定を行います。何らかの同期が有効になっている場合は編集できないため、同期を解除してから行ってください。
|
||||
同期先のデータベース設定(Remote Server)を行います。
|
||||
|
||||
### URI
|
||||
CouchDBのURIを入力します。Cloudantの場合は「External Endpoint(preferred)」になります。
|
||||
**スラッシュで終わってはいけません。**
|
||||
こちらにデータベース名を含めてもかまいません。
|
||||
現在のバージョンでは、複数のリモート接続設定(接続プロファイル)を登録・管理し、切り替えて使用することが可能です(「Remote Databases」リスト)。
|
||||
|
||||
### Username
|
||||
ユーザー名を入力します。このユーザーは管理者権限があることが望ましいです。
|
||||
- **➕ 新規接続を追加 (Add new connection)**: 新しい接続設定を作成し、各セットアップダイアログを起動します。
|
||||
- **📥 接続をインポート (Import connection)**: 接続文字列(`sls+https://...`、`sls+s3://...`、`sls+p2p://...`など)を貼り付けてインポートします。
|
||||
- **🔧 設定 (Configure)**: セットアップダイアログを開き、選択した接続プロファイルの設定を編集します。
|
||||
- **✅ 有効化 (Activate)**: 選択したプロファイルをアクティブな同期先として有効化します。
|
||||
- **🗑️ 削除 (Delete)**: 接続プロファイルを一覧から削除します。
|
||||
|
||||
### Password
|
||||
パスワードを入力します。
|
||||
これらの接続プロファイルを追加・編集する際、選択したデータベースの種類(CouchDB、S3互換オブジェクトストレージ、P2Pなど)に応じたセットアップダイアログが開きます。
|
||||
|
||||
### Database Name
|
||||
同期するデータベース名を入力します。
|
||||
⚠️存在しない場合は、テストや接続を行った際、自動的に作成されます[^1]。
|
||||
[^1]:権限がない場合は自動作成には失敗します。
|
||||
何らかの同期が有効になっている場合は編集できないため、同期を解除してから行ってください。
|
||||
|
||||
### CouchDB の設定
|
||||
CouchDBの各設定項目は、接続プロファイルを追加 (➕) または設定 (🔧) する際に開く **CouchDB セットアップダイアログ** 内で設定します。
|
||||
|
||||
#### URI
|
||||
設定キー: couchDB_URI
|
||||
|
||||
CouchDBの接続先URIです。ダイアログ内では **URL** と表記されます。Cloudantの場合は「External Endpoint (preferred)」になります。
|
||||
注意: Obsidian Mobileではセキュア接続 (HTTPS) のみが使用可能です。また、末尾にスラッシュ(`/`)を付けてはいけません。
|
||||
|
||||
#### Username
|
||||
設定キー: couchDB_USER
|
||||
|
||||
CouchDBのログインユーザー名です。ダイアログ内では **Username** と表記されます。このユーザーには管理者権限があることが望ましいです。
|
||||
|
||||
#### Password
|
||||
設定キー: couchDB_PASSWORD
|
||||
|
||||
CouchDBのログインパスワードです。ダイアログ内では **Password** と表記されます。
|
||||
|
||||
#### Database Name
|
||||
設定キー: couchDB_DBNAME
|
||||
|
||||
同期先のデータベース名です。ダイアログ内では **Database Name** と表記されます。
|
||||
注意: データベース名には大文字、スペース、および一部の特殊文字(`_$()+/-` 以外)は使用できません。また、アンダースコア(`_`)から始めることはできません。存在しない場合は、接続テスト時または設定適用時に自動作成されます(作成権限が必要です)。
|
||||
|
||||
#### CORS回避のためにRequest APIを使用する
|
||||
設定キー: useRequestAPI
|
||||
|
||||
この項目はセットアップダイアログ内では **Use Internal API** と表記されます。有効な場合、不可避なCORS問題を回避するためにObsidianの内部Request APIを使用します。これはWeb標準に準拠していない回避策であり、すべての環境での動作を保証するものではありません。安全性が低下する可能性がある点にご注意ください。将来のObsidianのアップデートによって動作しなくなる可能性があります。
|
||||
|
||||
#### カスタムヘッダー
|
||||
設定キー: couchDB_CustomHeaders
|
||||
|
||||
CouchDBサーバーに送信するすべてのリクエストに含めるカスタムHTTPヘッダーを設定します。ダイアログ内では **Custom Headers** と表記されます。`ヘッダー名: 値` の形式で、1行に1つずつ入力してください。
|
||||
|
||||
#### JWT認証の使用 (実験的機能)
|
||||
設定キー: useJWT
|
||||
|
||||
CouchDBでのJSON Web Token (JWT) 認証を有効にします。ダイアログ内では **Use JWT Authentication** と表記されます。十分に検証されていない実験的機能であるため、ご注意ください。
|
||||
|
||||
#### JWTアルゴリズム
|
||||
設定キー: jwtAlgorithm
|
||||
|
||||
JWTの署名に使用するアルゴリズムを選択します。ダイアログ内では **JWT Algorithm** と表記されます。対応アルゴリズム: `HS256`, `HS512`, `ES256`, `ES512`
|
||||
|
||||
#### JWT有効期限 (分)
|
||||
設定キー: jwtExpDuration
|
||||
|
||||
トークンの有効期限を分単位で指定します。ダイアログ内では **JWT Expiration Duration (minutes)** と表記されます。`0` を指定すると有効期限は無効になります。
|
||||
|
||||
#### JWTキー
|
||||
設定キー: jwtKey
|
||||
|
||||
JWTの署名に使用する秘密鍵またはプライベートキーを指定します。ダイアログ内では **JWT Key** と表記されます。`HS256/HS512` の場合は共通鍵を、`ES256/ES512` の場合は pkcs8 PEM形式の秘密鍵を入力してください。
|
||||
|
||||
#### JWTキーID (kid)
|
||||
設定キー: jwtKid
|
||||
|
||||
JWTヘッダーに含めるキーIDを指定します。ダイアログ内では **JWT Key ID (kid)** と表記されます。
|
||||
|
||||
#### JWTサブジェクト (sub)
|
||||
設定キー: jwtSub
|
||||
|
||||
JWTのサブジェクト (CouchDBユーザー名) を指定します。ダイアログ内では **JWT Subject (sub)** と表記されます。
|
||||
|
||||
### Object Storage (Minio, S3, R2) の設定
|
||||
Object Storageの各設定項目は、接続プロファイルを追加 (➕) または設定 (🔧) する際に開く **S3/MinIO/R2 セットアップダイアログ** 内で設定します。
|
||||
|
||||
#### エンドポイントURL
|
||||
設定キー: endpoint
|
||||
|
||||
S3互換ストレージのエンドポイントURLです。ダイアログ内では **Endpoint URL** と表記されます。
|
||||
注意: Obsidian Mobileではセキュア接続 (HTTPS) のみが使用可能です。
|
||||
|
||||
#### アクセスキー ID
|
||||
設定キー: accessKey
|
||||
|
||||
認証に使用するアクセスキーIDです。ダイアログ内では **Access Key ID** と表記されます。
|
||||
|
||||
#### シークレットアクセスキー
|
||||
設定キー: secretKey
|
||||
|
||||
認証に使用するシークレットアクセスキーです。ダイアログ内では **Secret Access Key** と表記されます。
|
||||
|
||||
#### リージョン
|
||||
設定キー: region
|
||||
|
||||
ストレージのリージョンを指定します(例: `us-east-1`、Cloudflare R2の場合は `auto`)。ダイアログ内では **Region** と表記されます。
|
||||
|
||||
#### バケット名
|
||||
設定キー: bucket
|
||||
|
||||
同期データを保存するバケット名です。ダイアログ内では **Bucket Name** と表記されます。
|
||||
|
||||
#### カスタムHTTPハンドラーを使用する
|
||||
設定キー: useCustomRequestHandler
|
||||
|
||||
この項目はセットアップダイアログ内では **Use internal API** と表記されます。オブジェクトストレージがCORSをサポートしていない場合に有効にします。Obsidianの内部APIを使用してS3サーバーと通信することでCORS制約を回避します。Web標準には準拠していないため、将来のObsidianのアップデートによって動作しなくなる可能性があります。
|
||||
|
||||
#### バケット内のファイルプレフィックス
|
||||
設定キー: bucketPrefix
|
||||
|
||||
この項目はセットアップダイアログ内では **Folder Prefix** と表記されます。実質的なディレクトリ指定です。末尾は `/` である必要があります(例:`vault-name/`)。バケットのルートに保存する場合は空欄のままにしてください。
|
||||
|
||||
#### forcePathStyleを有効にする
|
||||
設定キー: forcePathStyle
|
||||
|
||||
この項目はセットアップダイアログ内では **Use Path-Style Access** と表記されます。有効な場合、バケット操作でforcePathStyleオプションを使用します。
|
||||
|
||||
#### カスタムヘッダー
|
||||
設定キー: bucketCustomHeaders
|
||||
|
||||
オブジェクトストレージバケットに送信するすべてのリクエストに含めるカスタムHTTPヘッダーを設定します。ダイアログ内では **Custom Headers** と表記されます。`ヘッダー名: 値` の形式で、1行に1つずつ入力してください。
|
||||
|
||||
|
||||
|
||||
@@ -30,6 +140,18 @@ CouchDBのURIを入力します。Cloudantの場合は「External Endpoint(prefe
|
||||
### Passphrase
|
||||
暗号化を行う際に使用するパスフレーズです。充分に長いものを使用してください。
|
||||
|
||||
### パスの難読化
|
||||
設定キー: usePathObfuscation
|
||||
|
||||
ダイアログ内では **Obfuscate Properties** と表記されます。有効な場合、リモートサーバー上でのファイルパスやフォルダ名を難読化(暗号化)します。これによりプライバシーが向上しますが、パフォーマンスがわずかに低下する可能性があります。
|
||||
|
||||
### 暗号化アルゴリズム
|
||||
設定キー: E2EEAlgorithm
|
||||
|
||||
ダイアログ内では **Encryption Algorithm** と表記されます。エンドツーエンド暗号化に使用する暗号化アルゴリズムのバージョンを選択します。
|
||||
- `v2` (V2: AES-256-GCM With HKDF): 推奨されるデフォルトのバージョンです。
|
||||
- `forceV1` または `""` (V1: Legacy): レガシーな暗号化バージョンです。古いバージョンで暗号化された既存の保管庫(Vault)を同期する場合にのみ使用してください。
|
||||
|
||||
### Apply
|
||||
End to End 暗号化を行うに当たって、異なるパスフレーズで暗号化された同一の内容を入手されることは避けるべきです。また、Self-hosted LiveSyncはコンテンツのcrc32を重複回避に使用しているため、その点でも攻撃が有効になってしまいます。
|
||||
|
||||
@@ -53,12 +175,66 @@ End to End 暗号化を行うに当たって、異なるパスフレーズで暗
|
||||
どちらのオペレーションも、実行するとすべての同期設定が無効化されます。
|
||||
|
||||
|
||||
|
||||
|
||||
### Test Database connection
|
||||
上記の設定でデータベースに接続できるか確認します。
|
||||
|
||||
### Check database configuration
|
||||
ここから直接CouchDBの設定を確認・変更できます。
|
||||
|
||||
### Peer-to-Peer (P2P) 同期の設定
|
||||
|
||||
#### P2P同期を有効にする
|
||||
設定キー: P2P_Enabled
|
||||
|
||||
WebRTCを介したデバイス間での直接的なP2P同期を有効にします。ダイアログ内では **Enabled** と表記されます。
|
||||
|
||||
#### リレーサーバーのURL
|
||||
設定キー: P2P_relays
|
||||
|
||||
WebRTCによるP2P接続を仲介・調整するためのWebSocketリレーサーバーのURLを指定します。ダイアログ内では **Relay URL** と表記されます。複数のURLを指定する場合はカンマで区切ります。ダイアログ内のボタンをクリックすると、デフォルトのリレーサーバーを設定できます。
|
||||
|
||||
#### グループID
|
||||
設定キー: P2P_roomID
|
||||
|
||||
同期するデバイス群を識別するためのルームIDまたはグループIDを指定します。ダイアログ内では **Group ID** と表記されます。同期させたいすべてのデバイスで同じグループIDを指定する必要があります。任意のカスタム文字列を入力するか、ランダム生成ボタンで生成できます。
|
||||
|
||||
#### パスフレーズ
|
||||
設定キー: P2P_passphrase
|
||||
|
||||
P2P通信の認証および暗号化に使用するパスワード(パスフレーズ)を指定します。ダイアログ内では **Passphrase** と表記されます。同期するすべてのデバイスで同じパスフレーズを指定する必要があります。
|
||||
|
||||
#### デバイス名
|
||||
設定キー: P2P_DevicePeerName
|
||||
|
||||
P2Pネットワーク上でこのデバイスを識別するための名前を指定します。ダイアログ内では **Device Peer ID** と表記されます。グループ内のデバイス間で重複しない一意の値を設定してください。
|
||||
|
||||
#### 起動時のP2P自動接続開始
|
||||
設定キー: P2P_AutoStart
|
||||
|
||||
有効な場合、プラグインの起動時に自動的にP2P接続を開始します。ダイアログ内では **Auto Start P2P Connection** と表記されます。
|
||||
|
||||
#### 接続済みピアへの変更の自動ブロードキャスト
|
||||
設定キー: P2P_AutoBroadcast
|
||||
|
||||
有効な場合、ローカルでの変更が接続済みのピアに自動的にブロードキャストされます。ダイアログ内では **Auto Broadcast Changes** と表記されます。通知されたピアは変更の取得を開始します。
|
||||
|
||||
#### TURNサーバーのURL (カンマ区切り)
|
||||
設定キー: P2P_turnServers
|
||||
|
||||
ダイアログ内では **TURN Server URLs (comma-separated)** と表記されます。厳しいNATやファイアウォールがある環境で、WebRTCの直接接続が確立できない場合にP2P接続を中継するためのTURN/STUNサーバーのURLをカンマ区切りで指定します。通常は空欄のままで問題ありません。
|
||||
|
||||
#### TURNユーザー名
|
||||
設定キー: P2P_turnUsername
|
||||
|
||||
TURNサーバーでの認証に使用するユーザー名を設定します。ダイアログ内では **TURN Username** と表記されます。
|
||||
|
||||
#### TURNパスワード
|
||||
設定キー: P2P_turnCredential
|
||||
|
||||
TURNサーバーでの認証に使用するパスワード(クレデンシャル)を設定します。ダイアログ内では **TURN Credential** と表記されます。
|
||||
|
||||
## Local Database Configurations
|
||||
端末内に作成されるデータベースの設定です。
|
||||
|
||||
@@ -71,7 +247,8 @@ End to End 暗号化を行うに当たって、異なるパスフレーズで暗
|
||||
このオプションはLiveSyncと同時には使用できません。
|
||||
|
||||
### minimum chunk size と LongLine threshold
|
||||
チャンクの分割についての設定です。
|
||||
チャンクの分割についての設定です。※現在これらの項目はUIから直接設定することはできません(デフォルト値で自動処理されます)。
|
||||
|
||||
Self-hosted LiveSyncは一つのチャンクのサイズを最低minimum chunk size文字確保した上で、できるだけ効率的に同期できるよう、ノートを分割してチャンクを作成します。
|
||||
これは、同期を行う際に、一定の文字数で分割した場合、先頭の方を編集すると、その後の分割位置がすべてずれ、結果としてほぼまるごとのファイルのファイル送受信を行うことになっていた問題を避けるために実装されました。
|
||||
具体的には、先頭から順に直近の下記の箇所を検索し、一番長く切れたものを一つのチャンクとします。
|
||||
@@ -88,6 +265,11 @@ Self-hosted LiveSyncは一つのチャンクのサイズを最低minimum chunk s
|
||||
改行文字と#を除き、すべて●に置換しても、アルゴリズムは有効に働きます。
|
||||
デフォルトは20文字と、250文字です。
|
||||
|
||||
### チャンクスプリッター
|
||||
設定キー: chunkSplitterVersion
|
||||
|
||||
チャンク分割アルゴリズムを選択します。V3が最も効率的です。問題が発生した場合はDefaultまたはLegacyに設定してください。
|
||||
|
||||
## General Settings
|
||||
一般的な設定です。
|
||||
|
||||
@@ -97,18 +279,35 @@ Self-hosted LiveSyncは一つのチャンクのサイズを最低minimum chunk s
|
||||
### Vervose log
|
||||
詳細なログをログに出力します。
|
||||
|
||||
### ファイル警告バナーの代わりにステータスアイコンを表示
|
||||
設定キー: hideFileWarningNotice
|
||||
|
||||
有効な場合、ファイル警告バナーの代わりにステータス表示内に ⛔ アイコンが表示されます(詳細情報は非表示になります)。
|
||||
|
||||
### ネットワーク警告のスタイル
|
||||
設定キー: networkWarningStyle
|
||||
|
||||
同期サーバーに接続できない場合のネットワークエラーの表示方法。
|
||||
|
||||
## Sync setting
|
||||
同期に関する設定です。
|
||||
|
||||
### LiveSync
|
||||
LiveSyncを行います。
|
||||
他の同期方法では、同期の順序が「バージョン確認を行い、ロックが行われていないか確認した後、リモートの変更を受信した後、デバイスの変更を送信する」という挙動になります。
|
||||
### 同期モード (Sync Mode)
|
||||
設定キー: syncMode
|
||||
|
||||
### Periodic Sync
|
||||
定期的に同期を行います。
|
||||
同期処理を実行するトリガーとなる条件を設定します。
|
||||
- **LiveSync** (`LIVESYNC`): リアルタイムかつ継続的な双方向同期を行います。
|
||||
注意: このモードには CouchDB または WebRTC P2P リモートサーバーが必要です。S3互換オブジェクトストレージではサポートされていません。
|
||||
- **Periodic Sync** (`PERIODIC`): **Periodic Sync Interval** で指定した一定の間隔ごとに同期処理を実行します。
|
||||
- **On Events** (`ONEVENTS`): ファイルの保存、ファイルを開く、起動時など、特定のイベントが発生した際に同期をトリガーします(詳細は下部の設定スイッチで制御します)。
|
||||
|
||||
### Periodic Sync Interval
|
||||
定期的に同期を行う場合の間隔です。
|
||||
定期的に同期を行う場合の間隔(秒単位)です。
|
||||
|
||||
### 同期の最小間隔
|
||||
設定キー: syncMinimumInterval
|
||||
|
||||
イベント時の自動同期の最小間隔(ミリ秒)。
|
||||
|
||||
### Sync on Save
|
||||
ファイルが保存されたときに同期を行います。
|
||||
@@ -146,6 +345,11 @@ Self-hosted LiveSyncは通常、フォルダ内のファイルがすべて削除
|
||||
- Scan hidden files periodicaly.
|
||||
このオプションを有効にすると、n秒おきに隠しファイルをスキャンします。
|
||||
|
||||
#### 非表示ファイルの変更通知を抑制
|
||||
設定キー: suppressNotifyHiddenFilesChange
|
||||
|
||||
有効な場合、非表示ファイルの変更に関する通知を抑制します。
|
||||
|
||||
隠しファイルは能動的に検出されないため、スキャンが必要です。
|
||||
スキャンでは、ファイルと共にファイルの変更時刻を保存します。もしファイルが消された場合は、その事実も保存します。このファイルを記録したエントリーがレプリケーションされた際、ストレージよりも新しい場合はストレージに反映されます。
|
||||
|
||||
@@ -176,6 +380,45 @@ Self-hosted LiveSyncはPouchDBを使用し、リモートと[このプロトコ
|
||||
### Batch limit
|
||||
一度に処理するBatchの数です。デフォルトは40です。
|
||||
|
||||
### 1回のリクエストで送信するチャンクの最大サイズ
|
||||
設定キー: sendChunksBulkMaxSize
|
||||
|
||||
メガバイト(MB)単位で指定します。
|
||||
|
||||
## Customisation Sync (カスタマイズ同期)
|
||||
プラグイン、ホットキー、テーマ、スニペットなどのObsidianのカスタマイズ設定を同期する機能です(以前は **Plugin Sync** と呼ばれていました)。
|
||||
|
||||
### デバイス名 (Device name)
|
||||
設定キー: deviceAndVaultName
|
||||
|
||||
同期するすべてのデバイス間で一意となるデバイス名です。この設定を編集するには、一度カスタマイズ同期を無効にする必要があります。
|
||||
|
||||
### ファイル保存ごとのカスタマイズ同期 (Per-file-saved customisation sync)
|
||||
設定キー: usePluginSyncV2
|
||||
|
||||
有効な場合、ファイルごとの効率的なカスタマイズ同期が使用されます。有効にする際には簡単な移行作業が必要であり、すべてのデバイスを v0.23.18 以降にアップデートする必要があります。この機能を有効にすると、古いバージョンとの互換性が失われます。
|
||||
|
||||
### カスタマイズ同期を有効にする (Enable customisation sync)
|
||||
設定キー: usePluginSync
|
||||
|
||||
テーマ、スニペット、ホットキー、プラグイン設定などの同期を有効にします。
|
||||
注意: 安全上の理由から、この機能を使用するにはエンドツーエンド暗号化(End-to-End Encryption)が有効になっている必要があります。
|
||||
|
||||
### カスタマイズの自動スキャン (Scan customisation automatically)
|
||||
設定キー: autoSweepPlugins
|
||||
|
||||
レプリケーション(同期処理)を実行する前に、カスタマイズ設定の変更をスキャンします。
|
||||
|
||||
### 定期的なカスタマイズのスキャン (Scan customisation periodically)
|
||||
設定キー: autoSweepPluginsPeriodic
|
||||
|
||||
1分ごとにカスタマイズ設定の変更を定期的にスキャンします。
|
||||
|
||||
### カスタマイズ更新の通知 (Notify customised)
|
||||
設定キー: notifyPluginOrSettingUpdated
|
||||
|
||||
他のデバイスで新しくカスタマイズ設定が更新されたときに通知を表示します。
|
||||
|
||||
## Miscellaneous
|
||||
その他の設定です
|
||||
### Show status inside editor
|
||||
@@ -195,8 +438,8 @@ Self-hosted LiveSyncはPouchDBを使用し、リモートと[このプロトコ
|
||||

|
||||
データベースがロックされていて、端末が「解決済み」とマークされていない場合、警告が表示されます。
|
||||
他のデバイスで、End to End暗号化を有効にしたか、Drop Historyを行った等、他の端末がそのまま同期を行ってはいない状態に陥った場合表示されます。
|
||||
暗号化を有効化した場合は、パスフレーズを設定してApply and recieve、Drop Historyを行った場合は、Drop and recieveを行うと自動的に解除されます。
|
||||
手動でこのロックを解除する場合は「mark this device as resolved」をクリックしてください。
|
||||
暗号化を有効化した場合は、パスフレーズを設定して「このデバイスの同期状態をリセット」、または「このデバイスのファイルでサーバーデータを上書き」を行うと自動的に解除されます。
|
||||
手動でこのロックを解除する場合は「I've made a backup, mark this device 'resolved'」をクリックしてください。
|
||||
|
||||
- パターン2
|
||||

|
||||
@@ -207,18 +450,52 @@ Self-hosted LiveSyncはPouchDBを使用し、リモートと[このプロトコ
|
||||
### Verify and repair all files
|
||||
Vault内のファイルを全て読み込み直し、もし差分があったり、データベースから正常に読み込めなかったものに関して、データベースに反映します。
|
||||
|
||||
- Drop and send
|
||||
デバイスとリモートのデータベースを破棄し、ロックしてからデバイスのファイルでデータベースを構築後、リモートに上書きします。
|
||||
- Drop and receive
|
||||
デバイスのデータベースを破棄した後、リモートから、操作しているデバイスに関してロックを解除し、データを受信して再構築します。
|
||||
- このデバイスの同期状態をリセット (Reset Synchronisation on This Device)
|
||||
ローカルのデータベースを破棄し、リモートのデータから再構築します。
|
||||
- このデバイスのファイルでサーバーデータを上書き (Overwrite Server Data with This Device's Files)
|
||||
ローカルおよびリモートのデータベースをこのデバイス上のファイルで再構築(上書き)します。
|
||||
|
||||
### Lock remote database
|
||||
リモートのデータベースをロックし、他の端末で同期を行おうとしてもエラーとともに同期がキャンセルされるように設定します。これは、データベースの再構築を行った場合、自動的に設定されるものと同じものです。
|
||||
|
||||
万が一同期に不具合が発生していて、使用しているデバイスのデータ+サーバーのデータを保護する場合などに、緊急避難的に使用してください。
|
||||
|
||||
### Suspend file watching
|
||||
ファイルの更新の監視を止めます。
|
||||
### Scram スイッチ (Scram Switches)
|
||||
データベースの破損や予期しないデータ喪失を防ぐために、同期処理を緊急停止するためのスイッチです。重大な設定不一致や同期エラーが発生した場合、プラグインは自動的に Scram 状態に移行し、同期動作を一時停止することがあります。
|
||||
|
||||
#### ファイルの更新監視を一時停止 (Suspend file watching)
|
||||
設定キー: suspendFileWatching
|
||||
|
||||
ローカルファイル変更の監視と検知を停止します。
|
||||
|
||||
#### データベース反映を一時停止 (Suspend database reflecting)
|
||||
設定キー: suspendParseReplicationResult
|
||||
|
||||
データベースでの変更をストレージファイル(Vault内のファイル)へ書き戻す処理を停止します。
|
||||
|
||||
### 互換性(メタデータ)(Compatibility (Metadata))
|
||||
|
||||
#### 削除済みファイルのメタデータを保持しない (Do not keep metadata of deleted files.)
|
||||
設定キー: deleteMetadataOfDeletedFiles
|
||||
|
||||
ファイルを削除した際に、そのファイルの同期履歴メタデータも即座にデータベースから削除し、保持しないようにします。
|
||||
|
||||
#### 削除済みデータのメタデータをクリーンナップする (Delete old metadata of deleted files on start-up)
|
||||
設定キー: automaticallyDeleteMetadataOfDeletedFiles
|
||||
|
||||
ファイルを削除した際のメタデータを保持する期間(日数)を設定します。指定した日数を経過した古い削除済みファイルのメタデータは、プラグイン起動時にデータベースから自動的に削除(クリーンナップ)されます。`0` を指定すると自動削除は無効になります。
|
||||
|
||||
### 破損している可能性があるファイルも処理する
|
||||
設定キー: processSizeMismatchedFiles
|
||||
|
||||
サイズ不一致のあるファイルを処理します。特定のAPIや外部連携によって作成されたファイルを同期する際に役立ちます。
|
||||
|
||||
### Remediation
|
||||
|
||||
#### イベント反映時の最大ファイル更新日時
|
||||
設定キー: maxMTimeForReflectEvents
|
||||
|
||||
この値(Unixエポックからの秒数)より新しい更新日時を持つファイルについては、イベントの反映を無視します。0を指定すると制限が無効になります。
|
||||
|
||||
### Corrupted data
|
||||

|
||||
|
||||
@@ -13,7 +13,7 @@ In these instructions, create IBM Cloudant Instance for trial.
|
||||
1. You can choose "Lite plan" for free.
|
||||

|
||||
|
||||
1. Select Multitenant(it's the default) and the region as you like.
|
||||
1. Select Multitenant (it is the default) and the region as you like.
|
||||

|
||||
|
||||
1. Be sure to select "IAM and Legacy credentials" for "Authentication Method".
|
||||
@@ -28,20 +28,20 @@ In these instructions, create IBM Cloudant Instance for trial.
|
||||
1. When all of the above steps have been done, open "Resource list" on the left pane. you can see the Cloudant instance in the "Service and software". Click it.
|
||||

|
||||
|
||||
1. In resource details, there's information to connect from Self-hosted LiveSync.
|
||||
Copy the "External Endpoint(preferred)" address. <sup>(\*1)</sup>. We use this address later, with the database name.
|
||||
1. In resource details, there is information to connect from Self-hosted LiveSync.
|
||||
Copy the "External Endpoint (preferred)" address. <sup>(\*1)</sup>. We use this address later, with the database name.
|
||||

|
||||
|
||||
## Database setup
|
||||
|
||||
1. Hit the "Launch Dashboard" button, Cloudant dashboard will be shown.
|
||||
Yes, it's almost CouchDB's fauxton.
|
||||
Yes, it is almost CouchDB's fauxton.
|
||||

|
||||
|
||||
1. First, you have to enable the CORS option.
|
||||
Hit the Account menu and open the "CORS" tab.
|
||||
Initially, "Origin Domains" is set to "Restrict to specific domains"., so set to "All domains(\*)"
|
||||
_NOTE: of course We want to set "app://obsidian.md" but it's not acceptable on Cloudant._
|
||||
_NOTE: of course We want to set "app://obsidian.md" but it is not acceptable on Cloudant._
|
||||

|
||||
|
||||
1. Next, Open the "Databases" tab and hit the "Create Database" button.
|
||||
@@ -55,10 +55,10 @@ In these instructions, create IBM Cloudant Instance for trial.
|
||||
|
||||
### Credentials Setup
|
||||
|
||||
1. Back into IBM Cloud, Open the "Service credentials". You'll get an empty list, hit the "New credential" button.
|
||||
1. Back into IBM Cloud, Open the "Service credentials". You will get an empty list, hit the "New credential" button.
|
||||

|
||||
|
||||
1. The dialog to create a credential will be shown.
|
||||
1. The dialogue to create a credential will be shown.
|
||||
type any name or leave it default, hit the "Add" button.
|
||||

|
||||
_NOTE: This "name" is not related to your username that uses in Self-hosted LiveSync._
|
||||
@@ -68,14 +68,14 @@ In these instructions, create IBM Cloudant Instance for trial.
|
||||

|
||||
The username and password pair is inside this JSON.
|
||||
"username" and "password" are so.
|
||||
follow the figure, it's
|
||||
follow the figure, it is
|
||||
"apikey-v2-2unu15184f7o8emr90xlqgkm2ncwhbltml6tgnjl9sd5"<sup>(\*3)</sup> and "c2c11651d75497fa3d3c486e4c8bdf27"<sup>(\*4)</sup>
|
||||
|
||||
## Self-hosted LiveSync settings
|
||||
|
||||

|
||||
|
||||
The Setting should be as below:
|
||||
The settings should be as follows:
|
||||
|
||||
| Items | Value | example |
|
||||
| ------------- | ----- | ----------------------------------------------------------------- |
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
# Designed architecture
|
||||
|
||||
## How does this plugin synchronize.
|
||||
## How does this plug-in synchronise.
|
||||
|
||||

|
||||

|
||||
|
||||
1. When notes are created or modified, Obsidian raises some events. Self-hosted LiveSync catches these events and reflects changes into Local PouchDB.
|
||||
2. PouchDB automatically or manually replicates changes to remote CouchDB.
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
## 这个插件是怎么实现同步的.
|
||||
|
||||

|
||||

|
||||
|
||||
1. 当笔记创建或修改时,Obsidian会触发事件。Self-hosted LiveSync捕获这些事件,并将变更同步至本地PouchDB
|
||||
2. PouchDB通过自动或手动方式将变更同步至远程CouchDB
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
## 同期
|
||||
|
||||

|
||||

|
||||
|
||||
1. ノートが更新された際、Obsidianがイベントを発報します。Obsidian-LiveSyncはそれをハンドリングして、ローカルのPouchDBに変更を反映します。
|
||||
2. PouchDBは、リモートのCouchDBに差分をレプリケーションします。
|
||||
|
||||
@@ -2,23 +2,102 @@
|
||||
|
||||
## Spelling and Vocabulary conventions
|
||||
|
||||
1. Almost all of the english words are written in British English. For example, "organisation" instead of "organization", "synchronisation" instead of "synchronization", etc. This convention originated from the author's personal preference but is now maintained for consistency.
|
||||
All guidelines and conventions listed below are disclosed and maintained solely for the sake of documentation `consistency`.
|
||||
|
||||
2. Idiomatic terms, such as used in HTML, CSS, and JavaScript, are usually be aligned with the language used in the technology. For example, "color" instead of "colour", "program" instead of "programme", etc. Especially, terms which are used for attributes, properties, and methods are notable.
|
||||
1. Almost all of the English words are written in British English. This convention originated from the author's personal preference.
|
||||
- **Traditional Spelling (Trad-spelling)**: We prefer traditional British English spellings. In particular, we use `-ise` and `-isation` suffixes rather than the Oxford spelling `-ize` and `-ization` (for example, 'initialisation', 'synchronisation', and 'organisation').
|
||||
- **Oxford Comma**: We use the serial (Oxford) comma to separate items in lists of three or more (for example, 'settings, snippets, and themes' instead of 'settings, snippets and themes').
|
||||
- **Logical Punctuation**: We place punctuation marks (such as commas and full stops) outside quotation marks, unless the punctuation mark is part of the quoted text itself. For example, we write 'dialogue', not 'dialogue,'.
|
||||
- **BBC News Styleguide**: If in wonder, the BBC News Styleguide may be useful as a reference.
|
||||
|
||||
2. Idiomatic terms, such as those used in HTML, CSS, and JavaScript, are usually aligned with the language used in the technology. For example, "color" instead of "colour", "program" instead of "programme", etc. Especially, terms which are used for attributes, properties, and methods are notable.
|
||||
|
||||
3. We use `dialogue` in documentation for consistency. While `dialog` may appear in source code, particularly in class names, method names, and attributes (following technical conventions in No. 2), we consistently use `dialogue` for user-facing messages and general documentation text. This approach balances No. 1 with No. 2.
|
||||
|
||||
4. Contractions are not used. For example, "do not" instead of "don't", "cannot" instead of "can't", etc. especially `'d`.
|
||||
4. Contractions are not used. For example, "do not" instead of "don't", "cannot" instead of "can't", etc., especially `'d`.
|
||||
- We may encounter difficulties with tenses.
|
||||
|
||||
5. However, try using affirmative forms, `Discard` instead of `Do not keep`, `Continue` instead of `Do not stop`, etc.
|
||||
- Some languages, such as Japanese, have a different meaning for `yes` and `no` between affirmative and negative questions.
|
||||
|
||||
## Terminology
|
||||
6. Single quotation marks (`'`) are preferred over double quotation marks (`"`) in general documentation text, unless the context requires double quotes (for example, inside JSON code blocks).
|
||||
|
||||
- Self-hosted LiveSync
|
||||
- This plug-in name. `Self-hosted` is one word.
|
||||
### Terminology
|
||||
|
||||
- Boot-up sequence (boot-sequence)
|
||||
- The initialisation process of the plug-in when Obsidian starts. It starts with the loading of the plug-in, setting up core services, loading saved settings, and opening the local database. Once the layout is ready, the plug-in checks for the presence of flag files, runs configuration diagnostics, connects to the remote database, and begins file watching. The sequence finishes once the plug-in is fully ready and operational.
|
||||
- Broken files (Size mismatch)
|
||||
- A state where a file's metadata and the actual content stored in its chunks do not match, causing file retrieval or synchronisation failures. These mismatches can be detected and resolved by running validation tools such as `Verify and repair all files` on the Hatch pane.
|
||||
- Chunk / Chunks
|
||||
- Divided units of data stored in the database or object storage to facilitate efficient synchronisation.
|
||||
- Compaction
|
||||
- A database maintenance procedure that discards old historical document revisions to shrink the remote database size.
|
||||
- Custom HTTP Handler / Use Internal API (CORS Bypass Settings)
|
||||
- Settings used to bypass CORS restrictions by routing requests through Obsidian's native request APIs. There are two distinct settings under the hood depending on the remote server type:
|
||||
- **For S3-compatible Object Storage (useCustomRequestHandler)**: Labeled as **"Use Custom HTTP Handler"** in the standard settings tab, **"Use internal API"** in the Svelte-based Setup Wizard dialogue, and represented as `useProxy` in the Setup URI's query parameters due to an unfortunate misunderstanding during development.
|
||||
- **For CouchDB (useRequestAPI)**: Labeled as **"Use Request API to avoid `inevitable` CORS problem"** in the standard settings tab, **"Use Internal API"** in the Svelte-based Setup Wizard dialogue, and represented as `useRequestAPI` in the Setup URI's query parameters.
|
||||
- Customisation Sync
|
||||
- The feature that synchronises settings, snippets, themes, and plug-ins. Write with an "s" in documentation (`Customisation`), though technical configurations and links may use `customization`.
|
||||
- Database Adapter (IDB vs. IndexedDB)
|
||||
- The local database storage interface used by PouchDB. The `IDB` adapter is recommended since the older `IndexedDB` adapter is obsolete and known to cause memory leaks in `LiveSync` mode. Users can switch between these adapters without a full database rebuild, although a local data migration and an Obsidian restart are required.
|
||||
- Database Suffix (additionalSuffixOfDatabaseName)
|
||||
- A unique suffix appended to the database name to allow synchronising multiple vaults with the same name on the same remote server.
|
||||
- E2EE Algorithm
|
||||
- The cryptographic algorithm version used for end-to-end encryption. All devices in the synchronisation group must be configured with a compatible version (such as `V2` or `V1`).
|
||||
- Eden (Eden Chunks)
|
||||
- A performance optimisation where newly created chunks are held within the document until they stabilise, before graduating to independent chunks.
|
||||
- Fast Setup (Simple Fetch)
|
||||
- A simplified, automated initial synchronisation flow triggered when setting up subsequent devices or recovering a database. It bypasses the detailed step-by-step setup wizard dialogues, prompting the user with high-level data processing decisions and completing the initial download and local file scan in one continuous process.
|
||||
- Flag files (redflag.md, redflag2.md, redflag3.md)
|
||||
- Special Markdown files (or directories) placed at the root of the vault to stop the boot-up sequence or trigger recovery tasks. For instance, `redflag.md` suspends all processes, while `redflag2.md` (`flag_rebuild.md`) triggers a full database rebuild and `redflag3.md` (`flag_fetch.md`) discards the local database to fetch it again from the remote.
|
||||
- Garbage Collection (GC)
|
||||
- The process of identifying and purging unreferenced chunks (unused data) from local and remote databases to reclaim storage space.
|
||||
- Hatch (Hatch pane)
|
||||
- A dedicated troubleshooting and maintenance section in the plug-in settings, typically hidden behind a warning-labeled collapsible panel to prevent accidental misconfiguration. It contains diagnostic utilities, database reset controls, status reports, and advanced edge-case patches.
|
||||
- Hidden File Sync
|
||||
- The feature that synchronises files located in hidden directories (like `.obsidian`).
|
||||
- JWT Authentication
|
||||
- An experimental authentication option for CouchDB allowing secure token-based authentication instead of standard credentials. It requires a configured private key/secret, algorithm, expiration duration, subject, and key ID.
|
||||
- LiveSync
|
||||
- Very confusing term.
|
||||
- As shorten-form of `Self-hosted LiveSync`.
|
||||
- As a name of synchronisation mode. This should be changed to `Continuos`, in contrast to `Periodic`.
|
||||
- A very confusing term.
|
||||
- As a shortened form of `Self-hosted LiveSync`.
|
||||
- As the name of a synchronisation mode. This should be changed to `Continuous`, in contrast to `Periodic`.
|
||||
- livesync-serverpeer / webpeer
|
||||
- Pseudo-clients that assist in WebRTC peer-to-peer communication.
|
||||
- Metadata (File metadata)
|
||||
- A database document that stores properties of a file, including its filename, path, size, modification time, conflict history, and references (hashes) of the chunks that comprise the file's content. In Self-hosted LiveSync, metadata is stored separately from the actual file content to enable efficient synchronisation and versioning.
|
||||
- OneShot Sync
|
||||
- A single, immediate bidirectional synchronisation (pull then push) triggered on demand or on specific events, as opposed to continuous (live) replication.
|
||||
- Overwrite Server Data with This Device's Files
|
||||
- A maintenance operation (formerly known as `Rebuild everything`) that discards the remote database and reconstructs it by uploading all current local files as a fresh database, overwriting any remote changes.
|
||||
- Path Obfuscation
|
||||
- A privacy option that encrypts file paths and folder names on the remote server.
|
||||
- plug-in
|
||||
- We use the hyphenated form `plug-in` in user-facing messages and general documentation, while `plugin` may appear in codebase files, configuration settings, or technical contexts.
|
||||
- Relay Server (P2P relays)
|
||||
- A WebSocket-based coordination server used to establish direct WebRTC peer-to-peer connections. The default relay is provided by the plug-in author.
|
||||
- Remediation (maxMTimeForReflectEvents)
|
||||
- A recovery setting that restricts the propagation of changes from the database to local storage, ignoring any file events (such as accidental mass deletions) that occurred after a specified date and time.
|
||||
- Reset Synchronisation on This Device
|
||||
- A maintenance operation (formerly known as `Fetch everything`) that discards the local database and reconstructs it by downloading all data from the remote server.
|
||||
- Scram (Scram Switches)
|
||||
- Emergency controls in the settings that allow users to suspend file watching or database writes to prevent corruption.
|
||||
- Segmenter (Segmented-splitter)
|
||||
- A chunking method that divides files on semantic boundaries (such as paragraphs or sections) rather than arbitrary byte boundaries.
|
||||
- Self-hosted LiveSync
|
||||
- The name of this plug-in. `Self-hosted` is one word.
|
||||
- Setting Doctor (Config Doctor)
|
||||
- A diagnostic utility that checks for mismatches or suboptimal configurations, presenting users with ideal values and recommendation reasons to easily resolve issues during migration, configuration import, or general troubleshooting.
|
||||
- Setup URI
|
||||
- An encrypted representation of the plug-in's settings containing server configuration, which allows users to clone their configuration across devices securely using a passphrase.
|
||||
- Streaming replication (Stream-based replication)
|
||||
- A data transfer method that downloads database documents as a continuous stream of events. It is significantly faster than traditional chunk-by-chunk HTTP requests and is used during Fast Setup to retrieve remote metadata quickly.
|
||||
- Sync Mode
|
||||
- The replication trigger mechanism. Users can select from `On Events` (synchronising on local file changes), `Periodic and Events` (synchronising at fixed intervals as well as on events), or `LiveSync` (continuous, real-time synchronisation).
|
||||
- TURN Server (WebRTC P2P)
|
||||
- A server type (Traversal Using Relays around NAT) used as a fallback to relay traffic when direct WebRTC peer-to-peer connection is blocked by strict NAT or firewalls.
|
||||
- Update Thinning (Batch database update)
|
||||
- An optimisation that groups multiple local file edits together over a short delay before committing them to the local database, reducing the number of database write operations.
|
||||
- WebRTC P2P (Peer-to-Peer)
|
||||
- A synchronisation method enabling direct communication between devices without a central server database.
|
||||
|
||||
|
||||
65
docs/tips/fast-setup.md
Normal file
65
docs/tips/fast-setup.md
Normal file
@@ -0,0 +1,65 @@
|
||||
# Fast Setup (Simple Fetch)
|
||||
|
||||
Fast Setup is a streamlined, user-friendly data retrieval and initialisation flow designed to simplify setting up secondary devices or recovering databases.
|
||||
|
||||
Instead of guiding the user through the detailed multi-step setup wizard dialogues, Fast Setup prompts the user with high-level sync decisions and automates database download and local storage scanning in one continuous process.
|
||||
|
||||
---
|
||||
|
||||
## How It Works
|
||||
|
||||
When you import a **Setup URI** on a secondary device, or when a **Fetch All** operation is triggered (such as by placing a `redflag3.md` / `flag_fetch.md` flag file at the root of the vault), the plug-in schedules remote data retrieval.
|
||||
|
||||
On the next startup, the plug-in boots in scheduled fetch mode and opens a simplified dialogue: **"Data retrieval scheduled"**.
|
||||
|
||||
---
|
||||
|
||||
## Technical Characteristics
|
||||
|
||||
Fast Setup leverages several backend optimisations to make the retrieval fast, safe, and clean:
|
||||
|
||||
1. **Stream-based Replication for Speed**
|
||||
- It fetches all remote metadata via stream reception, which is significantly faster than traditional chunk-by-chunk retrieval.
|
||||
2. **Delayed File Reflection to Prevent Corrupted Warnings**
|
||||
- By suspending file reflection during the download phase, it prevents the plug-in from raising temporary or false "corrupted data synchronisation" or "size mismatch" warnings that can occur during the chunk download process.
|
||||
3. **Time-Based Comparison is Generally Sufficient**
|
||||
- Since the vault is entering a fresh synchronisation or recovery state, comparing files based on their modification timestamps (newer-wins) is highly reliable and sufficient to reconcile files without needing complex manual conflict resolution.
|
||||
|
||||
---
|
||||
|
||||
## Step-by-Step Guide
|
||||
|
||||
### Step 1: Choose Data Processing Method
|
||||
You will be prompted to choose how the retrieved remote data will interact with your existing local files:
|
||||
|
||||
1. **Compare time and take newer (newer-wins)**
|
||||
- Compares the modified time of files and accepts the newer version.
|
||||
- **Recommended if:** You have been using Self-hosted LiveSync and have made changes on multiple devices that you want to merge.
|
||||
2. **Overwrite all with remote files (remote-wins)**
|
||||
- Remote data is treated as the source of truth.
|
||||
- **Recommended if:** You are setting up a brand new device with an empty or clean vault.
|
||||
- *Warning: This will overwrite local files with remote files. Please ensure you have a backup of your local vault before proceeding.*
|
||||
3. **Use the detailed flow (legacy)**
|
||||
- Switches back to the detailed, traditional setup wizard dialogues.
|
||||
- **Recommended if:** You want full control over the step-by-step database setup options.
|
||||
|
||||
### Step 2: Configure Conflict & Deletion Rules
|
||||
Depending on your choice in Step 1, you will configure how to handle mismatches:
|
||||
|
||||
#### If you chose "Compare time and take newer":
|
||||
- **Delete local files if they were deleted on remote**
|
||||
- Keeps your local vault clean by removing files that have already been deleted on other devices.
|
||||
- **Recreate remote files even if they were deleted on remote**
|
||||
- Preserves local files and uploads them back to the remote database, even if they were deleted on other devices.
|
||||
|
||||
#### If you chose "Overwrite all with remote files":
|
||||
- **Delete local files if not on remote**
|
||||
- Removes local-only files so that your local vault matches the remote database exactly.
|
||||
- **Keep local files even if not on remote**
|
||||
- Retains all existing local-only files, although this may result in duplicates that you will need to clean up manually after synchronisation.
|
||||
|
||||
### Step 3: Automated Synchronisation
|
||||
Once you confirm your choices:
|
||||
1. The plug-in performs a fast download of the remote database (`fetchLocalDBFast`).
|
||||
2. It automatically runs a full scan (`synchroniseAllFilesBetweenDBandStorage`) in the foreground to reflect database changes in your local vault files immediately.
|
||||
3. The plug-in finalises the process and resumes normal operational status.
|
||||
66
docs/tips/fast-setup_ja.md
Normal file
66
docs/tips/fast-setup_ja.md
Normal file
@@ -0,0 +1,66 @@
|
||||
# ファストセットアップ (Fast Setup / Simple Fetch)
|
||||
|
||||
ファストセットアップは、2台目以降のデバイスのセットアップやデータベース再構築時のデータ取得・初期化処理を、直感的かつ迅速に行うための簡略化された同期フローです。
|
||||
|
||||
従来のセットアップウィザードにおける複数の詳細なステップを踏むことなく、同期の基本方針を選択するだけで、データベースのダウンロードとローカルファイルのスキャン・反映を一連のプロセスとして自動的に実行します。
|
||||
|
||||
---
|
||||
|
||||
## 仕組み
|
||||
|
||||
2台目以降のデバイスで **セットアップURI** をインポートした場合や、手動でデータの再フェッチ(Vaultルートに `redflag3.md` / `flag_fetch.md` を配置する等)が予約された場合、プラグインはリモートデータベースからのデータ取得スケジュールを設定します。
|
||||
|
||||
その後の起動時、プラグインはデータフェッチ予約モードで立ち上がり、**「Data retrieval scheduled(データ取得のスケジュール)」** という簡略化されたダイアログを表示します。
|
||||
|
||||
---
|
||||
|
||||
## 技術的な特徴
|
||||
|
||||
ファストセットアップは、高速かつ安全でクリーンな処理を実現するために、以下の最適化を行っています。
|
||||
|
||||
1. **高速なストリーム受信**
|
||||
- 全データを取得しますが、ストリーム受信によるレプリケーションを使用するため、処理が非常に高速です。
|
||||
2. **ストレージ反映の遅延による不要な警告の抑制**
|
||||
- データのダウンロード中にローカルファイル(ストレージ)への書き出し(反映)処理をあえて遅延させることで、同期途中で発生しがちな「破損データ同期」や「サイズ不一致」などの一時的なエラー警告を抑え込みます。
|
||||
- すべてのデータダウンロードが完了した後に一括してストレージへ書き出すため、不必要な警告画面でユーザーを混乱させません。
|
||||
3. **時刻ベース比較の妥当性**
|
||||
- 初期セットアップやリカバリーの段階(この状態に移行した直後)においては、概ねファイル更新時刻(タイムスタンプ)ベースでの単純比較を行うことで、十分かつ妥当な同期結果を得ることができます。これにより複雑な競合解決の手間を省きます。
|
||||
|
||||
---
|
||||
|
||||
## 設定手順
|
||||
|
||||
### ステップ 1: データの反映方法の選択
|
||||
取得したリモートデータを、既存のローカルファイルとどのように統合するかを選択します。
|
||||
|
||||
1. **Compare time and take newer (newer-wins)**
|
||||
- ファイルの更新日時を比較し、より新しい方を採用します。
|
||||
- **推奨されるケース:** すでに Self-hosted LiveSync を使用しており、複数のデバイスで編集した変更内容をタイムスタンプに基づいて統合したい場合。
|
||||
2. **Overwrite all with remote files (remote-wins)**
|
||||
- リモートデータベースの内容を正(Source of Truth)として扱います。
|
||||
- **推奨されるケース:** まったく新しいデバイスをセットアップする場合(空のVaultなど)。
|
||||
- *警告: ローカルにあるすべてのファイルがリモートの内容で上書きされます。重要なデータがある場合は、事前にバックアップを取得してください。*
|
||||
3. **Use the detailed flow (legacy)**
|
||||
- 従来の詳細なセットアップウィザードダイアログに戻ります。
|
||||
- **推奨されるケース:** データベースの構成オプションをステップバイステップで細かく制御・確認したい場合。
|
||||
|
||||
### ステップ 2: 競合および削除ルールの構成
|
||||
ステップ 1 での選択内容に応じて、ローカルとリモートの不一致をどう処理するかを設定します。
|
||||
|
||||
#### 「Compare time and take newer」を選択した場合:
|
||||
- **Delete local files if they were deleted on remote**
|
||||
- 他のデバイスで削除済みのファイルをローカルからも削除し、Vaultを同期・クリーンな状態に保ちます。
|
||||
- **Recreate remote files even if they were deleted on remote**
|
||||
- 他のデバイスで削除されたファイルであっても、ローカルファイルを維持し、リモートデータベースに再度アップロードします。
|
||||
|
||||
#### 「Overwrite all with remote files」を選択した場合:
|
||||
- **Delete local files if not on remote**
|
||||
- リモートに存在しないローカル専用ファイルを削除し、ローカルのVaultをリモートデータベースと完全に一致させます。
|
||||
- **Keep local files even if not on remote**
|
||||
- リモートに存在しないローカルファイルをそのまま残します。ただし、同期後に重複ファイルが発生する可能性があるため、その場合は手動でクリーンアップを行ってください。
|
||||
|
||||
### ステップ 3: 自動同期の実行
|
||||
選択を確定すると、以下の処理が順次実行されます。
|
||||
1. リモートデータベースの高速ダウンロードを実行します (`fetchLocalDBFast`)。
|
||||
2. ローカルファイルへの変更反映のため、フォアグラウンドでフルスキャン (`synchroniseAllFilesBetweenDBandStorage`) を自動的に実行します。
|
||||
3. 処理が完了すると、プラグインは通常の動作状態へ復帰します。
|
||||
@@ -224,7 +224,7 @@ There are many cases where this is really unclear. One possibility is that the c
|
||||
- If you know when the files were deleted, set the time a bit before that.
|
||||
- If not, bisecting may help us.
|
||||
6. Delete `redflag.md`.
|
||||
7. Perform `Reset synchronisation on This Device` on the `🎛️ Maintenance` pane.
|
||||
7. Perform `Reset Synchronisation on This Device` on the `🎛️ Maintenance` pane.
|
||||
|
||||
This mode is very fragile. Please be careful.
|
||||
|
||||
@@ -236,16 +236,16 @@ not been stable. The new adapter has better performance and has a new feature
|
||||
like purging. Therefore, we should use new adapters and current default is so.
|
||||
|
||||
However, when switching from an old adapter to a new adapter, some converting or
|
||||
local database rebuilding is required, and it takes a few time. It was a long
|
||||
local database rebuilding is required, and it takes some time. It was a long
|
||||
time ago now, but we once inconvenienced everyone in a hurry when we changed the
|
||||
format of our database. For these reasons, this toggle is automatically on if we
|
||||
have upgraded from vault which using an old adapter.
|
||||
|
||||
When you rebuild everything or fetch from the remote again, you will be asked to
|
||||
When you overwrite server data with this device's files or reset synchronisation on this device again, you will be asked to
|
||||
switch this.
|
||||
|
||||
Therefore, experienced users (especially those stable enough not to have to
|
||||
rebuild the database) may have this toggle enabled in their Vault. Please
|
||||
overwrite server data) may have this toggle enabled in their Vault. Please
|
||||
disable it when you have enough time.
|
||||
|
||||
### ZIP (or any extensions) files were not synchronised. Why?
|
||||
@@ -303,9 +303,9 @@ happened on other devices. This means that conflicts will happen in the past,
|
||||
after the time we have synchronised. Hence we cannot collect and delete the
|
||||
unused chunks even though if we are not currently referenced.
|
||||
|
||||
To shrink the database size, `Rebuild everything` only reliably and effectively.
|
||||
To shrink the database size, `Overwrite Server Data with This Device's Files` is the only reliable and effective way.
|
||||
But do not worry, if we have synchronised well. We have the actual and real
|
||||
files. Only it takes a bit of time and traffics.
|
||||
files. Only it takes a bit of time and traffic.
|
||||
|
||||
### How to launch the DevTools
|
||||
|
||||
@@ -373,47 +373,64 @@ without Obsidian.
|
||||
For example, if there is `redflag.md`, Self-hosted LiveSync suspends all database and storage
|
||||
processes.
|
||||
|
||||
### Flag Files
|
||||
### Scram State and Flag Files (SCRAM Warning Loop)
|
||||
|
||||
The flag file is a simple Markdown file designed to prevent storage events and database events in self-hosted LiveSync.
|
||||
Its very existence is significant; it may be left blank, or it may contain text; either is acceptable.
|
||||
The plug-in uses a **Scram state** (emergency suspension of all synchronisation processes) to prevent database corruption when severe errors or conflicts are detected. This state is often triggered or persisted by **flag files** placed at the root of the vault.
|
||||
|
||||
This file is in Markdown format so that it can be placed in the Vault externally, even if Obsidian fails to launch.
|
||||
If you encounter a warning saying **"Scram detected, all sync operations are suspended per SCRAM"** or get caught in an infinite loop where the warning persists even after clicking "Resume", it is likely due to a flag file in your vault.
|
||||
|
||||
There are some options to use `redflag.md`.
|
||||
#### Flag Files
|
||||
|
||||
| Filename | Human-Friendly Name | Description |
|
||||
| ------------- | ------------------- | ------------------------------------------------------------------------------------ |
|
||||
| `redflag.md` | - | Suspends all processes. |
|
||||
| `redflag2.md` | `flag_rebuild.md` | Suspends all processes, and rebuild both local and remote databases by local files. |
|
||||
| `redflag3.md` | `flag_fetch.md` | Suspends all processes, discard the local database, and fetch from the remote again. |
|
||||
A flag file is a simple Markdown file located at the root of your vault. Its very existence is significant; it may be left blank or contain any text. These files are used so they can be easily placed or deleted from outside Obsidian (e.g., when Obsidian fails to launch).
|
||||
|
||||
When fetching everything remotely or performing a rebuild, restarting Obsidian
|
||||
is performed once for safety reasons. At that time, Self-hosted LiveSync uses
|
||||
these files to determine whether the process should be carried out. (The use of
|
||||
normal markdown files is a trick to externally force cancellation in the event
|
||||
of faults in the rebuild or fetch function itself, especially on mobile
|
||||
devices). This mechanism is also used for set-up. And just for information,
|
||||
these files are also not subject to synchronisation.
|
||||
| Filename | Human-Friendly Name | Description |
|
||||
| ------------- | ------------------- | --------------------------------------------------------------------------------------- |
|
||||
| `redflag.md` | - | Suspends all processes (activates Scram). |
|
||||
| `redflag2.md` | `flag_rebuild.md` | Suspends all processes, and overwrites server data with this device's files. |
|
||||
| `redflag3.md` | `flag_fetch.md` | Suspends all processes, discards the local database, and resets synchronisation on this device. |
|
||||
|
||||
However, occasionally the deletion of files may fail. This should generally work
|
||||
normally after restarting Obsidian. (As far as I can observe).
|
||||
When resetting synchronisation on this device or overwriting server data, restarting Obsidian is performed once for safety reasons. At that time, Self-hosted LiveSync uses these files to determine whether the process should be carried out. (This mechanism is especially useful on mobile devices to force cancellation if the database rebuilding fails). These files are not subject to synchronisation.
|
||||
|
||||
#### How to Resolve the Scram Loop
|
||||
|
||||
If you cannot disable Scram, please follow these steps:
|
||||
1. Close Obsidian completely.
|
||||
2. Open your system's file manager and check the root directory of your vault.
|
||||
3. Locate and delete any flag files (such as `redflag.md`, `redflag2.md`, or `redflag3.md`).
|
||||
4. Launch Obsidian.
|
||||
5. Go to the settings dialogue -> **Hatch** -> **Scram Switches**, and manually toggle **Suspend file watching** and **Suspend database reflecting** to `false` (disabled) if they have not been reset automatically.
|
||||
|
||||
> [!TIP]
|
||||
> This is the reason why flag files are standard `.md` files: it allows you to manage them externally. On mobile devices, you can use system file manager applications (such as the native **Files** app on iOS/iPadOS or **Files by Google** on Android) to find and delete these files to resolve a lock, or conversely, create/place a new `redflag.md` file (or directory) at the root of your vault to force-suspend synchronisation and stop Obsidian's boot-up sequence if you need to fix a database issue.
|
||||
|
||||
### JWT Authentication Errors
|
||||
|
||||
#### DataError when configuring JWT authentication
|
||||
|
||||
If you encounter a `DataError:` with no additional information in the logs when configuring JWT authentication, this usually indicates a private key formatting issue.
|
||||
|
||||
Self-hosted LiveSync requires the private key (for ES256/ES512 algorithms) to be in the **PKCS#8 PEM** format. Standard SEC1 EC private keys (which begin with `-----BEGIN EC PRIVATE KEY-----`) will trigger this error.
|
||||
|
||||
To resolve this, convert your private key to PKCS#8 format using the following `openssl` command:
|
||||
```bash
|
||||
openssl pkcs8 -topk8 -inform PEM -nocrypt -in private.key -out pkcs8.key
|
||||
```
|
||||
Then paste the contents of `pkcs8.key` (which begins with `-----BEGIN PRIVATE KEY-----`) into the JWT Key field.
|
||||
|
||||
### Old tips
|
||||
|
||||
- Rarely, a file in the database could be corrupted. The plugin will not write
|
||||
- Rarely, a file in the database could be corrupted. The plug-in will not write
|
||||
to local storage when a file looks corrupted. If a local version of the file
|
||||
is on your device, the corruption could be fixed by editing the local file and
|
||||
synchronizing it. But if the file does not exist on any of your devices, then
|
||||
synchronising it. But if the file does not exist on any of your devices, then
|
||||
it can not be rescued. In this case, you can delete these items from the
|
||||
settings dialog.
|
||||
settings dialogue.
|
||||
- To stop the boot-up sequence (eg. for fixing problems on databases), you can
|
||||
put a `redflag.md` file (or directory) at the root of your vault. Tip for iOS:
|
||||
a redflag directory can be created at the root of the vault using the File
|
||||
application.
|
||||
- Also, with `redflag2.md` placed, we can automatically rebuild both the local
|
||||
and the remote databases during the boot-up sequence. With `redflag3.md`, we
|
||||
can discard only the local database and fetch from the remote again.
|
||||
- Also, with `redflag2.md` placed, we can automatically overwrite server data with this device's files during the boot-up sequence. With `redflag3.md`, we
|
||||
can discard only the local database and reset synchronisation on this device.
|
||||
- Q: The database is growing, how can I shrink it down? A: each of the docs is
|
||||
saved with their past 100 revisions for detecting and resolving conflicts.
|
||||
Picturing that one device has been offline for a while, and comes online
|
||||
@@ -425,7 +442,7 @@ normally after restarting Obsidian. (As far as I can observe).
|
||||
So, We have to make the database again like an enlarged git repo if you want
|
||||
to solve the root of the problem.
|
||||
- And more technical Information is in the [Technical Information](tech_info.md)
|
||||
- If you want to synchronize files without obsidian, you can use
|
||||
- If you want to synchronise files without obsidian, you can use
|
||||
[filesystem-livesync](https://github.com/vrtmrz/filesystem-livesync).
|
||||
- WebClipper is also available on Chrome Web
|
||||
Store:[obsidian-livesync-webclip](https://chrome.google.com/webstore/detail/obsidian-livesync-webclip/jfpaflmpckblieefkegjncjoceapakdf)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"id": "obsidian-livesync",
|
||||
"name": "Self-hosted LiveSync",
|
||||
"version": "0.25.70-patch1",
|
||||
"version": "0.25.73",
|
||||
"minAppVersion": "1.7.2",
|
||||
"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",
|
||||
|
||||
321
package-lock.json
generated
321
package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "obsidian-livesync",
|
||||
"version": "0.25.70-patch1",
|
||||
"version": "0.25.73",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "obsidian-livesync",
|
||||
"version": "0.25.70-patch1",
|
||||
"version": "0.25.73",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-s3": "^3.808.0",
|
||||
@@ -52,9 +52,9 @@
|
||||
"@types/transform-pouch": "^1.0.6",
|
||||
"@typescript-eslint/eslint-plugin": "8.56.1",
|
||||
"@typescript-eslint/parser": "8.56.1",
|
||||
"@vitest/browser": "^4.1.1",
|
||||
"@vitest/browser-playwright": "^4.1.1",
|
||||
"@vitest/coverage-v8": "^4.1.1",
|
||||
"@vitest/browser": "^4.1.8",
|
||||
"@vitest/browser-playwright": "^4.1.8",
|
||||
"@vitest/coverage-v8": "^4.1.8",
|
||||
"dotenv-cli": "^11.0.0",
|
||||
"esbuild": "0.25.0",
|
||||
"esbuild-plugin-inline-worker": "^0.1.1",
|
||||
@@ -91,7 +91,7 @@
|
||||
"typescript": "5.9.3",
|
||||
"vite": "^7.3.1",
|
||||
"vite-plugin-istanbul": "^8.0.0",
|
||||
"vitest": "^4.1.1",
|
||||
"vitest": "^4.1.8",
|
||||
"webdriverio": "^9.27.0",
|
||||
"yaml": "^2.8.2"
|
||||
}
|
||||
@@ -1852,9 +1852,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@eslint/config-array/node_modules/brace-expansion": {
|
||||
"version": "1.1.14",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz",
|
||||
"integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==",
|
||||
"version": "1.1.15",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.15.tgz",
|
||||
"integrity": "sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -1933,9 +1933,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@eslint/eslintrc/node_modules/brace-expansion": {
|
||||
"version": "1.1.14",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz",
|
||||
"integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==",
|
||||
"version": "1.1.15",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.15.tgz",
|
||||
"integrity": "sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -3547,13 +3547,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@smithy/core": {
|
||||
"version": "3.24.5",
|
||||
"resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.24.5.tgz",
|
||||
"integrity": "sha512-Kt8phUg45M15EjhYAbZ+fFikYneijLu9Liugz8ZsYz2i8j0hzGv27LWKpEHYRfvj+LyCOSijpcR/2i8RouV+cA==",
|
||||
"version": "3.24.6",
|
||||
"resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.24.6.tgz",
|
||||
"integrity": "sha512-wBXDRup6UU97VKyaiRo8AssnfStPtG0oAAfpq/bC0a1YYau8pM86YB4kM6ccoVi1mS8l/UHbn9oDM+7uozr/ug==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@aws-crypto/crc32": "5.2.0",
|
||||
"@smithy/types": "^4.14.2",
|
||||
"@smithy/types": "^4.14.3",
|
||||
"tslib": "^2.6.2"
|
||||
},
|
||||
"engines": {
|
||||
@@ -3720,12 +3720,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@smithy/is-array-buffer": {
|
||||
"version": "4.3.3",
|
||||
"resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-4.3.3.tgz",
|
||||
"integrity": "sha512-RRxYqjUa/n8dRVkbhyuiRarppLzt4H/AtMUEFmiHlDy8o4wrgqAdzxsk9naemzu6iX67ZV375fNmX7Q8dynGKw==",
|
||||
"version": "4.3.6",
|
||||
"resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-4.3.6.tgz",
|
||||
"integrity": "sha512-/cSYHP8jPffkhBClQzH9fAJujIh8dwMwg2swrVF4stXQsUWO5Oi2bwyaMUcBPIyulUI5IxaJFxd9C8UQX+YZsQ==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@smithy/core": "^3.24.3",
|
||||
"@smithy/core": "^3.24.6",
|
||||
"tslib": "^2.6.2"
|
||||
},
|
||||
"engines": {
|
||||
@@ -3989,9 +3989,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@smithy/types": {
|
||||
"version": "4.14.2",
|
||||
"resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.14.2.tgz",
|
||||
"integrity": "sha512-P+otAxbV4CqBybp7EkcJCrig63yE2E7PuNVOmilVMRcx/O+QDzGULTrKsq4DV13gSfak9ObPrWaHl/9bL5YcWw==",
|
||||
"version": "4.14.3",
|
||||
"resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.14.3.tgz",
|
||||
"integrity": "sha512-YupL0ZWmFtJexUN2cHzkvvF/b9pKrtAIfT1o7/oY/Ppu8IYeZ+lDPM5vZdQJaSeA132dJCqojjGC9NhXeF71VQ==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"tslib": "^2.6.2"
|
||||
@@ -4053,12 +4053,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@smithy/util-buffer-from": {
|
||||
"version": "4.3.3",
|
||||
"resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-4.3.3.tgz",
|
||||
"integrity": "sha512-5xlgilVaX96HdVlLZymKUa7vOTZtisOTxBJloM2J4PeRqyAWBeFIq0DnIxQISvwxT4rgJAvk7rHhB+GlCCKe8g==",
|
||||
"version": "4.3.6",
|
||||
"resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-4.3.6.tgz",
|
||||
"integrity": "sha512-sms/ty2CJwHOiGzEaAVWizTVK5KusXpAYqCUeXIa+hWtNKLwjimH4z11mc07d0Fe3DT3lmZJIZWOMcVQ/N4hBQ==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@smithy/core": "^3.24.3",
|
||||
"@smithy/core": "^3.24.6",
|
||||
"tslib": "^2.6.2"
|
||||
},
|
||||
"engines": {
|
||||
@@ -4194,12 +4194,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@smithy/util-utf8": {
|
||||
"version": "4.3.3",
|
||||
"resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-4.3.3.tgz",
|
||||
"integrity": "sha512-c1QpRBn3aMsoqE64dd4Imgjy8Pynfw+eR7GkjElquxUFSnezwYVaOFm8JcYa+Bo/5ssbEyPKcT3+4bmrWYh6eQ==",
|
||||
"version": "4.3.6",
|
||||
"resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-4.3.6.tgz",
|
||||
"integrity": "sha512-tAa4sePYB7mlJzdYbdBqdv37KwFKWixmM/r3ihcI0HFOVjf+a5oGvtcLXcGm4S1bY4DFsLAIOHgjubtp+oRufw==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@smithy/core": "^3.24.3",
|
||||
"@smithy/core": "^3.24.6",
|
||||
"tslib": "^2.6.2"
|
||||
},
|
||||
"engines": {
|
||||
@@ -4994,47 +4994,46 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@vitest/browser": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/browser/-/browser-4.1.1.tgz",
|
||||
"integrity": "sha512-gjjrFC4+kPVK/fN9URDJWrssU5Gqh8Az8pKG/NSfQ2V+ky8b/y1BgBg0Ug13+hOGp5pzInonmGRPn7vOgSLgzA==",
|
||||
"version": "4.1.8",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/browser/-/browser-4.1.8.tgz",
|
||||
"integrity": "sha512-u21VzX07HzlJYpFgkxmjEXar/tG2UqWGgyGG/46SrrPc7rSdCTPw5vuowopO9CIqF8UCUQzDFdbVnNpw6N0BfQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@blazediff/core": "1.9.1",
|
||||
"@vitest/mocker": "4.1.1",
|
||||
"@vitest/utils": "4.1.1",
|
||||
"@vitest/mocker": "4.1.8",
|
||||
"@vitest/utils": "4.1.8",
|
||||
"magic-string": "^0.30.21",
|
||||
"pngjs": "^7.0.0",
|
||||
"sirv": "^3.0.2",
|
||||
"tinyrainbow": "^3.0.3",
|
||||
"tinyrainbow": "^3.1.0",
|
||||
"ws": "^8.19.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/vitest"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"vitest": "4.1.1"
|
||||
"vitest": "4.1.8"
|
||||
}
|
||||
},
|
||||
"node_modules/@vitest/browser-playwright": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/browser-playwright/-/browser-playwright-4.1.1.tgz",
|
||||
"integrity": "sha512-dtVSBZZha2k/7P7EAXXrEAoxuIKl8Yv9f2Dk4GN/DGfmhf4DQvkvu+57okR2wq/gan1xppKjL/aBxK/kbYrbGw==",
|
||||
"version": "4.1.8",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/browser-playwright/-/browser-playwright-4.1.8.tgz",
|
||||
"integrity": "sha512-SR7FqgegaexEg73xvf3ArtygXegagMdXnL0EZMpxrWvvhQxvicD/E8p0ib0J91riPRtQUViyh67Xjw3NqvyhVg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@vitest/browser": "4.1.1",
|
||||
"@vitest/mocker": "4.1.1",
|
||||
"tinyrainbow": "^3.0.3"
|
||||
"@vitest/browser": "4.1.8",
|
||||
"@vitest/mocker": "4.1.8",
|
||||
"tinyrainbow": "^3.1.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/vitest"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"playwright": "*",
|
||||
"vitest": "4.1.1"
|
||||
"vitest": "4.1.8"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"playwright": {
|
||||
@@ -5043,14 +5042,15 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@vitest/coverage-v8": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-4.1.1.tgz",
|
||||
"integrity": "sha512-nZ4RWwGCoGOQRMmU/Q9wlUY540RVRxJZ9lxFsFfy0QV7Zmo5VVBhB6Sl9Xa0KIp2iIs3zWfPlo9LcY1iqbpzCw==",
|
||||
"version": "4.1.8",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-4.1.8.tgz",
|
||||
"integrity": "sha512-lt3kovsyHwYe00wq4D1ti0Z974fWj4NLp6siqiyEufUpyFwK9Yhi7rBhac9JL5aA0zoMrJqc4vYPZRUnI7l7nw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@bcoe/v8-coverage": "^1.0.2",
|
||||
"@vitest/utils": "4.1.1",
|
||||
"@vitest/utils": "4.1.8",
|
||||
"ast-v8-to-istanbul": "^1.0.0",
|
||||
"istanbul-lib-coverage": "^3.2.2",
|
||||
"istanbul-lib-report": "^3.0.1",
|
||||
@@ -5058,14 +5058,14 @@
|
||||
"magicast": "^0.5.2",
|
||||
"obug": "^2.1.1",
|
||||
"std-env": "^4.0.0-rc.1",
|
||||
"tinyrainbow": "^3.0.3"
|
||||
"tinyrainbow": "^3.1.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/vitest"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@vitest/browser": "4.1.1",
|
||||
"vitest": "4.1.1"
|
||||
"@vitest/browser": "4.1.8",
|
||||
"vitest": "4.1.8"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@vitest/browser": {
|
||||
@@ -5074,41 +5074,31 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@vitest/expect": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.1.tgz",
|
||||
"integrity": "sha512-xAV0fqBTk44Rn6SjJReEQkHP3RrqbJo6JQ4zZ7/uVOiJZRarBtblzrOfFIZeYUrukp2YD6snZG6IBqhOoHTm+A==",
|
||||
"version": "4.1.8",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.8.tgz",
|
||||
"integrity": "sha512-h3nDO677RDLEGlBxyQ5CW8RlMThSKSRLUePLOx09gNIWRL40edgA1GCZSZgf1W55MFAG6/Sw14KeaAnqv0NKdQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@standard-schema/spec": "^1.1.0",
|
||||
"@types/chai": "^5.2.2",
|
||||
"@vitest/spy": "4.1.1",
|
||||
"@vitest/utils": "4.1.1",
|
||||
"@vitest/spy": "4.1.8",
|
||||
"@vitest/utils": "4.1.8",
|
||||
"chai": "^6.2.2",
|
||||
"tinyrainbow": "^3.0.3"
|
||||
"tinyrainbow": "^3.1.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/vitest"
|
||||
}
|
||||
},
|
||||
"node_modules/@vitest/expect/node_modules/chai": {
|
||||
"version": "6.2.2",
|
||||
"resolved": "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz",
|
||||
"integrity": "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@vitest/mocker": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.1.tgz",
|
||||
"integrity": "sha512-h3BOylsfsCLPeceuCPAAJ+BvNwSENgJa4hXoXu4im0bs9Lyp4URc4JYK4pWLZ4pG/UQn7AT92K6IByi6rE6g3A==",
|
||||
"version": "4.1.8",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.8.tgz",
|
||||
"integrity": "sha512-LEiN/xe4OSIbKe9HQIp5OC24agGD9J5CnmMgsLohVVoOPWL9a2sBoR6VBx43jQZb7Kr1l4RCuyCJzcAa0+dojw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@vitest/spy": "4.1.1",
|
||||
"@vitest/spy": "4.1.8",
|
||||
"estree-walker": "^3.0.3",
|
||||
"magic-string": "^0.30.21"
|
||||
},
|
||||
@@ -5129,26 +5119,26 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@vitest/pretty-format": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.1.tgz",
|
||||
"integrity": "sha512-GM+TEQN5WhOygr1lp7skeVjdLPqqWMHsfzXrcHAqZJi/lIVh63H0kaRCY8MDhNWikx19zBUK8ceaLB7X5AH9NQ==",
|
||||
"version": "4.1.8",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.8.tgz",
|
||||
"integrity": "sha512-9GasEBxpZ1VYIpqHf/0+YGg121uSNwCKOJqIrTwWP/TB7DmFCiaBpNl3aPZzoLWfWkuqhbH8vJIVobZkvdo2cA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"tinyrainbow": "^3.0.3"
|
||||
"tinyrainbow": "^3.1.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/vitest"
|
||||
}
|
||||
},
|
||||
"node_modules/@vitest/runner": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.1.tgz",
|
||||
"integrity": "sha512-f7+FPy75vN91QGWsITueq0gedwUZy1fLtHOCMeQpjs8jTekAHeKP80zfDEnhrleviLHzVSDXIWuCIOFn3D3f8A==",
|
||||
"version": "4.1.8",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.8.tgz",
|
||||
"integrity": "sha512-EmVxeBAfMJvycdjd6Hm+RbFBbA9fKvo0Kx37hNpBYoYeavH3RNsBXWDooR1mgD52dCrxIIuP7UotpfiwOikvcg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@vitest/utils": "4.1.1",
|
||||
"@vitest/utils": "4.1.8",
|
||||
"pathe": "^2.0.3"
|
||||
},
|
||||
"funding": {
|
||||
@@ -5156,14 +5146,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@vitest/snapshot": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.1.tgz",
|
||||
"integrity": "sha512-kMVSgcegWV2FibXEx9p9WIKgje58lcTbXgnJixfcg15iK8nzCXhmalL0ZLtTWLW9PH1+1NEDShiFFedB3tEgWg==",
|
||||
"version": "4.1.8",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.8.tgz",
|
||||
"integrity": "sha512-acfZboRmAIf05DEKcBQy33VXojFJjtUdLyo7oOmV9kebb2xdU01UknNiPuPZoJZQyO7DF0gZdTGTpeAzET9QPQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@vitest/pretty-format": "4.1.1",
|
||||
"@vitest/utils": "4.1.1",
|
||||
"@vitest/pretty-format": "4.1.8",
|
||||
"@vitest/utils": "4.1.8",
|
||||
"magic-string": "^0.30.21",
|
||||
"pathe": "^2.0.3"
|
||||
},
|
||||
@@ -5172,9 +5162,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@vitest/spy": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.1.tgz",
|
||||
"integrity": "sha512-6Ti/KT5OVaiupdIZEuZN7l3CZcR0cxnxt70Z0//3CtwgObwA6jZhmVBA3yrXSVN3gmwjgd7oDNLlsXz526gpRA==",
|
||||
"version": "4.1.8",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.8.tgz",
|
||||
"integrity": "sha512-6EevtBp6OZOPF7bmz36HrGMeP3txgVSrgebWxHOafDXGkhIzfXK14f8KF6MuFfgXXUeHxmpD3BQxkV00/3s5mA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
@@ -5182,15 +5172,15 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@vitest/utils": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.1.tgz",
|
||||
"integrity": "sha512-cNxAlaB3sHoCdL6pj6yyUXv9Gry1NHNg0kFTXdvSIZXLHsqKH7chiWOkwJ5s5+d/oMwcoG9T0bKU38JZWKusrQ==",
|
||||
"version": "4.1.8",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.8.tgz",
|
||||
"integrity": "sha512-uOJamYALNhfJ6iolExyQM40yIQwDqYnkKtQ5VCiSe17E33H0aQ/u+1GlRuz4LZBk6Mm3sg90G9hEbmEt37C1Zg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@vitest/pretty-format": "4.1.1",
|
||||
"@vitest/pretty-format": "4.1.8",
|
||||
"convert-source-map": "^2.0.0",
|
||||
"tinyrainbow": "^3.0.3"
|
||||
"tinyrainbow": "^3.1.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/vitest"
|
||||
@@ -5223,9 +5213,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@wdio/config/node_modules/brace-expansion": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.0.tgz",
|
||||
"integrity": "sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w==",
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.1.tgz",
|
||||
"integrity": "sha512-WR1cURNjuvBLMZBMbqM0UoE+WAfdUcEV1ccD8PVBVOI+Z3ND4+SZbN8RsfT2bMuG1qwz5RFvPukSZm5fF2D5eA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -5591,9 +5581,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/archiver-utils/node_modules/brace-expansion": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.0.tgz",
|
||||
"integrity": "sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w==",
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.1.tgz",
|
||||
"integrity": "sha512-WR1cURNjuvBLMZBMbqM0UoE+WAfdUcEV1ccD8PVBVOI+Z3ND4+SZbN8RsfT2bMuG1qwz5RFvPukSZm5fF2D5eA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -6400,6 +6390,16 @@
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/chai": {
|
||||
"version": "6.2.2",
|
||||
"resolved": "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz",
|
||||
"integrity": "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/chalk": {
|
||||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
|
||||
@@ -7882,9 +7882,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/eslint-plugin-import/node_modules/brace-expansion": {
|
||||
"version": "1.1.14",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz",
|
||||
"integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==",
|
||||
"version": "1.1.15",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.15.tgz",
|
||||
"integrity": "sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -7979,9 +7979,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/eslint-plugin-json-schema-validator/node_modules/brace-expansion": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.0.tgz",
|
||||
"integrity": "sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w==",
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.1.tgz",
|
||||
"integrity": "sha512-WR1cURNjuvBLMZBMbqM0UoE+WAfdUcEV1ccD8PVBVOI+Z3ND4+SZbN8RsfT2bMuG1qwz5RFvPukSZm5fF2D5eA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -8045,9 +8045,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/eslint-plugin-n/node_modules/brace-expansion": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.0.tgz",
|
||||
"integrity": "sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w==",
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.1.tgz",
|
||||
"integrity": "sha512-WR1cURNjuvBLMZBMbqM0UoE+WAfdUcEV1ccD8PVBVOI+Z3ND4+SZbN8RsfT2bMuG1qwz5RFvPukSZm5fF2D5eA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -8206,9 +8206,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/eslint-plugin-react/node_modules/brace-expansion": {
|
||||
"version": "1.1.14",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz",
|
||||
"integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==",
|
||||
"version": "1.1.15",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.15.tgz",
|
||||
"integrity": "sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -8408,9 +8408,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/eslint/node_modules/brace-expansion": {
|
||||
"version": "1.1.14",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz",
|
||||
"integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==",
|
||||
"version": "1.1.15",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.15.tgz",
|
||||
"integrity": "sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -9244,9 +9244,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/globby/node_modules/brace-expansion": {
|
||||
"version": "1.1.14",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz",
|
||||
"integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==",
|
||||
"version": "1.1.15",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.15.tgz",
|
||||
"integrity": "sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -10257,10 +10257,20 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/js-yaml": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz",
|
||||
"integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==",
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.2.0.tgz",
|
||||
"integrity": "sha512-ePWsvanv0DWuDRsW8dnt+R4jQ31SCRCQ7hhNcPXZPsoBZiemuZNYGf7adZdqX2D86j6rvKp3RpCxVTSb8WQlOw==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/puzrin"
|
||||
},
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/nodeca"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"argparse": "^2.0.1"
|
||||
@@ -10928,9 +10938,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lru-cache": {
|
||||
"version": "11.4.0",
|
||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.4.0.tgz",
|
||||
"integrity": "sha512-W+R+kFL4HgVxONq2bhXPi3bGpzGe/yEhVOp233qw9wCRtgncJ15P3bC+e4zZMu4Cq7d+WAJjXGW0uUkifhcatA==",
|
||||
"version": "11.5.1",
|
||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.1.tgz",
|
||||
"integrity": "sha512-RPimw/7aMdv2oqRrxKwvZXcPfwBrn/JZ2xYcY9Hus/6LaS3VOAKVWKWgNLCFSiOm1ESXinjsDlidVU7JlnCN2A==",
|
||||
"dev": true,
|
||||
"license": "BlueOak-1.0.0",
|
||||
"engines": {
|
||||
@@ -12650,9 +12660,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/readdir-glob/node_modules/brace-expansion": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.0.tgz",
|
||||
"integrity": "sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w==",
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.1.tgz",
|
||||
"integrity": "sha512-WR1cURNjuvBLMZBMbqM0UoE+WAfdUcEV1ccD8PVBVOI+Z3ND4+SZbN8RsfT2bMuG1qwz5RFvPukSZm5fF2D5eA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -13075,9 +13085,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/semver": {
|
||||
"version": "7.8.0",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.8.0.tgz",
|
||||
"integrity": "sha512-AcM7dV/5ul4EekoQ29Agm5vri8JNqRyj39o0qpX6vDF2GZrtutZl5RwgD1XnZjiTAfncsJhMI48QQH3sN87YNA==",
|
||||
"version": "7.8.1",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.8.1.tgz",
|
||||
"integrity": "sha512-rkVq3IXh+4FDGch+KwzX3aV9W3kO54GyEgpvBzSyctDA6Xtd7RJQV1xmXbeQp5v7+VzLOfVqiutSE6GICgPFvg==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"bin": {
|
||||
@@ -15249,9 +15259,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/undici": {
|
||||
"version": "7.25.0",
|
||||
"resolved": "https://registry.npmjs.org/undici/-/undici-7.25.0.tgz",
|
||||
"integrity": "sha512-xXnp4kTyor2Zq+J1FfPI6Eq3ew5h6Vl0F/8d9XU5zZQf1tX9s2Su1/3PiMmUANFULpmksxkClamIZcaUqryHsQ==",
|
||||
"version": "7.27.0",
|
||||
"resolved": "https://registry.npmjs.org/undici/-/undici-7.27.0.tgz",
|
||||
"integrity": "sha512-+t2Z/GwkZQDtu00813aP66ygViGtPHKhhoFZpQKpKrE+9jIgES+Zw+mFNaDWOVRKiuJjuqKHzD3B1sfGg8+ZOQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@@ -16030,20 +16040,19 @@
|
||||
}
|
||||
},
|
||||
"node_modules/vitest": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.1.tgz",
|
||||
"integrity": "sha512-yF+o4POL41rpAzj5KVILUxm1GCjKnELvaqmU9TLLUbMfDzuN0UpUR9uaDs+mCtjPe+uYPksXDRLQGGPvj1cTmA==",
|
||||
"version": "4.1.8",
|
||||
"resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.8.tgz",
|
||||
"integrity": "sha512-flY6ScbCIt9HThs+C5HS7jvGOB560DJtk/Z15IQROTA6zEy49Nh8T/dofWTQL+n3vswqn87sbJNiuqw1SDp5Ig==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@vitest/expect": "4.1.1",
|
||||
"@vitest/mocker": "4.1.1",
|
||||
"@vitest/pretty-format": "4.1.1",
|
||||
"@vitest/runner": "4.1.1",
|
||||
"@vitest/snapshot": "4.1.1",
|
||||
"@vitest/spy": "4.1.1",
|
||||
"@vitest/utils": "4.1.1",
|
||||
"@vitest/expect": "4.1.8",
|
||||
"@vitest/mocker": "4.1.8",
|
||||
"@vitest/pretty-format": "4.1.8",
|
||||
"@vitest/runner": "4.1.8",
|
||||
"@vitest/snapshot": "4.1.8",
|
||||
"@vitest/spy": "4.1.8",
|
||||
"@vitest/utils": "4.1.8",
|
||||
"es-module-lexer": "^2.0.0",
|
||||
"expect-type": "^1.3.0",
|
||||
"magic-string": "^0.30.21",
|
||||
@@ -16054,7 +16063,7 @@
|
||||
"tinybench": "^2.9.0",
|
||||
"tinyexec": "^1.0.2",
|
||||
"tinyglobby": "^0.2.15",
|
||||
"tinyrainbow": "^3.0.3",
|
||||
"tinyrainbow": "^3.1.0",
|
||||
"vite": "^6.0.0 || ^7.0.0 || ^8.0.0",
|
||||
"why-is-node-running": "^2.3.0"
|
||||
},
|
||||
@@ -16071,10 +16080,12 @@
|
||||
"@edge-runtime/vm": "*",
|
||||
"@opentelemetry/api": "^1.9.0",
|
||||
"@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0",
|
||||
"@vitest/browser-playwright": "4.1.1",
|
||||
"@vitest/browser-preview": "4.1.1",
|
||||
"@vitest/browser-webdriverio": "4.1.1",
|
||||
"@vitest/ui": "4.1.1",
|
||||
"@vitest/browser-playwright": "4.1.8",
|
||||
"@vitest/browser-preview": "4.1.8",
|
||||
"@vitest/browser-webdriverio": "4.1.8",
|
||||
"@vitest/coverage-istanbul": "4.1.8",
|
||||
"@vitest/coverage-v8": "4.1.8",
|
||||
"@vitest/ui": "4.1.8",
|
||||
"happy-dom": "*",
|
||||
"jsdom": "*",
|
||||
"vite": "^6.0.0 || ^7.0.0 || ^8.0.0"
|
||||
@@ -16098,6 +16109,12 @@
|
||||
"@vitest/browser-webdriverio": {
|
||||
"optional": true
|
||||
},
|
||||
"@vitest/coverage-istanbul": {
|
||||
"optional": true
|
||||
},
|
||||
"@vitest/coverage-v8": {
|
||||
"optional": true
|
||||
},
|
||||
"@vitest/ui": {
|
||||
"optional": true
|
||||
},
|
||||
@@ -16206,9 +16223,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/webdriver/node_modules/undici": {
|
||||
"version": "6.25.0",
|
||||
"resolved": "https://registry.npmjs.org/undici/-/undici-6.25.0.tgz",
|
||||
"integrity": "sha512-ZgpWDC5gmNiuY9CnLVXEH8rl50xhRCuLNA97fAUnKi8RRuV4E6KG31pDTsLVUKnohJE0I3XDrTeEydAXRw47xg==",
|
||||
"version": "6.26.0",
|
||||
"resolved": "https://registry.npmjs.org/undici/-/undici-6.26.0.tgz",
|
||||
"integrity": "sha512-4yqz8a3n5HmGTlsbADNtr/dJlhkh/55Rq798G6ibiULcXbDtaLpTl1pvdqcbFfeoj3iSi52lePFM7h9H21cw/A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
|
||||
10
package.json
10
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "obsidian-livesync",
|
||||
"version": "0.25.70-patch1",
|
||||
"version": "0.25.73",
|
||||
"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",
|
||||
@@ -80,9 +80,9 @@
|
||||
"@types/transform-pouch": "^1.0.6",
|
||||
"@typescript-eslint/eslint-plugin": "8.56.1",
|
||||
"@typescript-eslint/parser": "8.56.1",
|
||||
"@vitest/browser": "^4.1.1",
|
||||
"@vitest/browser-playwright": "^4.1.1",
|
||||
"@vitest/coverage-v8": "^4.1.1",
|
||||
"@vitest/browser": "^4.1.8",
|
||||
"@vitest/browser-playwright": "^4.1.8",
|
||||
"@vitest/coverage-v8": "^4.1.8",
|
||||
"dotenv-cli": "^11.0.0",
|
||||
"esbuild": "0.25.0",
|
||||
"esbuild-plugin-inline-worker": "^0.1.1",
|
||||
@@ -119,7 +119,7 @@
|
||||
"typescript": "5.9.3",
|
||||
"vite": "^7.3.1",
|
||||
"vite-plugin-istanbul": "^8.0.0",
|
||||
"vitest": "^4.1.1",
|
||||
"vitest": "^4.1.8",
|
||||
"webdriverio": "^9.27.0",
|
||||
"yaml": "^2.8.2"
|
||||
},
|
||||
|
||||
@@ -105,7 +105,7 @@ class CLIWatchAdapter implements IStorageEventWatchAdapter {
|
||||
|
||||
private _toNodeFile(filePath: string, stats: Stats | undefined): NodeFile {
|
||||
return {
|
||||
path: path.relative(this.basePath, filePath) as FilePath,
|
||||
path: path.relative(this.basePath, filePath).replace(/\\/g, "/") as FilePath,
|
||||
stat: {
|
||||
ctime: stats?.ctimeMs ?? Date.now(),
|
||||
mtime: stats?.mtimeMs ?? Date.now(),
|
||||
@@ -117,7 +117,7 @@ class CLIWatchAdapter implements IStorageEventWatchAdapter {
|
||||
|
||||
private _toNodeFolder(dirPath: string): NodeFolder {
|
||||
return {
|
||||
path: path.relative(this.basePath, dirPath) as FilePath,
|
||||
path: path.relative(this.basePath, dirPath).replace(/\\/g, "/") as FilePath,
|
||||
isFolder: true,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ import { defineConfig } from "vite";
|
||||
import { svelte } from "@sveltejs/vite-plugin-svelte";
|
||||
import path from "node:path";
|
||||
import { readFileSync } from "node:fs";
|
||||
const resolve = (...args: string[]) => path.resolve(...args).replace(/\\/g, "/");
|
||||
const packageJson = JSON.parse(readFileSync("../../../package.json", "utf-8"));
|
||||
const manifestJson = JSON.parse(readFileSync("../../../manifest.json", "utf-8"));
|
||||
// https://vite.dev/config/
|
||||
@@ -63,17 +64,14 @@ export default defineConfig({
|
||||
resolve: {
|
||||
alias: {
|
||||
"@lib/worker/bgWorker.ts": "../../lib/src/worker/bgWorker.mock.ts",
|
||||
"@lib/pouchdb/pouchdb-browser.ts": path.resolve(__dirname, "lib/pouchdb-node.ts"),
|
||||
"@lib/pouchdb/pouchdb-browser.ts": resolve(__dirname, "lib/pouchdb-node.ts"),
|
||||
// The CLI runs on Node.js; force AWS XML builder to its CJS Node entry
|
||||
// so Vite does not resolve the browser DOMParser-based XML parser.
|
||||
"@aws-sdk/xml-builder": path.resolve(
|
||||
__dirname,
|
||||
"../../../node_modules/@aws-sdk/xml-builder/dist-cjs/index.js"
|
||||
),
|
||||
"@aws-sdk/xml-builder": resolve(__dirname, "../../../node_modules/@aws-sdk/xml-builder/dist-cjs/index.js"),
|
||||
// Force fflate to the Node CJS entry; browser entry expects Web Worker globals.
|
||||
fflate: path.resolve(__dirname, "../../../node_modules/fflate/lib/node.cjs"),
|
||||
"@": path.resolve(__dirname, "../../"),
|
||||
"@lib": path.resolve(__dirname, "../../lib/src"),
|
||||
fflate: resolve(__dirname, "../../../node_modules/fflate/lib/node.cjs"),
|
||||
"@": resolve(__dirname, "../../"),
|
||||
"@lib": resolve(__dirname, "../../lib/src"),
|
||||
"../../src/worker/bgWorker.ts": "../../src/worker/bgWorker.mock.ts",
|
||||
},
|
||||
},
|
||||
@@ -85,7 +83,7 @@ export default defineConfig({
|
||||
minify: false,
|
||||
rollupOptions: {
|
||||
input: {
|
||||
index: path.resolve(__dirname, "entrypoint.ts"),
|
||||
index: resolve(__dirname, "entrypoint.ts"),
|
||||
},
|
||||
external: (id) => {
|
||||
if (defaultExternal.includes(id)) return true;
|
||||
@@ -101,7 +99,7 @@ export default defineConfig({
|
||||
},
|
||||
},
|
||||
lib: {
|
||||
entry: path.resolve(__dirname, "entrypoint.ts"),
|
||||
entry: resolve(__dirname, "entrypoint.ts"),
|
||||
formats: ["cjs"],
|
||||
fileName: "index",
|
||||
},
|
||||
|
||||
2
src/lib
2
src/lib
Submodule src/lib updated: 6f977537f4...76d91674c2
@@ -8,7 +8,6 @@ import { TestPaneView, VIEW_TYPE_TEST } from "./devUtil/TestPaneView.ts";
|
||||
import { writable } from "svelte/store";
|
||||
import type { FilePathWithPrefix } from "../../lib/src/common/types.ts";
|
||||
import type { LiveSyncCore } from "../../main.ts";
|
||||
|
||||
export class ModuleDev extends AbstractObsidianModule {
|
||||
_everyOnloadStart(): Promise<boolean> {
|
||||
__onMissingTranslation(() => {});
|
||||
@@ -98,6 +97,7 @@ export class ModuleDev extends AbstractObsidianModule {
|
||||
});
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
|
||||
async _everyOnLayoutReady(): Promise<boolean> {
|
||||
if (!this.settings.enableDebugTools) return Promise.resolve(true);
|
||||
// if (await this.core.storageAccess.isExistsIncludeHidden("_SHOWDIALOGAUTO.md")) {
|
||||
|
||||
@@ -262,7 +262,7 @@ export class ModuleLog extends AbstractObsidianModule {
|
||||
this.statusDiv.remove();
|
||||
// this.statusDiv.pa();
|
||||
const container = mdv.view.containerEl;
|
||||
container.insertBefore(this.statusDiv, container.lastChild);
|
||||
container.appendChild(this.statusDiv);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -466,12 +466,14 @@ ${stringifyYaml(info)}
|
||||
|
||||
this.observeForLogs();
|
||||
|
||||
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" });
|
||||
this.statusDiv.style.display = this.settings?.showStatusOnEditor ? "" : "none";
|
||||
if (this.settings.showStatusOnEditor) {
|
||||
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" });
|
||||
this.statusDiv.style.display = this.settings?.showStatusOnEditor ? "" : "none";
|
||||
}
|
||||
eventHub.onEvent(EVENT_LAYOUT_READY, () => this.adjustStatusDivPosition());
|
||||
if (this.settings?.showStatusOnStatusbar) {
|
||||
this.statusBar = this.services.API.addStatusBarItem();
|
||||
|
||||
@@ -180,7 +180,7 @@
|
||||
autocapitalize="off"
|
||||
spellcheck="false"
|
||||
required
|
||||
pattern="^[a-z0-9][a-z0-9_]*$"
|
||||
pattern="^[a-z][a-z0-9_$()+/-]*$"
|
||||
bind:value={syncSetting.couchDB_DBNAME}
|
||||
/>
|
||||
</InputRow>
|
||||
|
||||
197
src/serviceFeatures/redFlag.simpleFetch.ts
Normal file
197
src/serviceFeatures/redFlag.simpleFetch.ts
Normal file
@@ -0,0 +1,197 @@
|
||||
import { LOG_LEVEL_NOTICE } from "octagonal-wheels/common/logger";
|
||||
import type { NecessaryServices } from "@lib/interfaces/ServiceModule";
|
||||
import { type LogFunction } from "@lib/services/lib/logUtils";
|
||||
import { UnresolvedErrorManager } from "@lib/services/base/UnresolvedErrorManager";
|
||||
import {
|
||||
ExtraOnLocal,
|
||||
ExtraOnRemote,
|
||||
FullScanModes,
|
||||
normaliseFullScanOptions,
|
||||
synchroniseAllFilesBetweenDBandStorage,
|
||||
type FullScanOptions,
|
||||
} from "@lib/serviceFeatures/offlineScanner";
|
||||
import { adjustSettingToRemoteIfNeeded, processVaultInitialisation } from "./redFlag";
|
||||
|
||||
export const SIMPLE_FETCH_STAGE1_REMOTE_WINS = "Overwrite all with remote files";
|
||||
export const SIMPLE_FETCH_STAGE1_NEWER_WINS = "Compare time and take newer";
|
||||
export const SIMPLE_FETCH_STAGE1_LEGACY = "Use the detailed flow";
|
||||
export const SIMPLE_FETCH_STAGE1_CANCEL = "Cancel";
|
||||
|
||||
export const SIMPLE_FETCH_STAGE2_REMOTE_DELETE_NONE = "Keep local files even if not on remote";
|
||||
export const SIMPLE_FETCH_STAGE2_REMOTE_DELETE_ALL = "Delete local files if not on remote";
|
||||
|
||||
export const SIMPLE_FETCH_STAGE2_NEWER_CLEANUP = "Delete local files if deleted on remote";
|
||||
export const SIMPLE_FETCH_STAGE2_NEWER_SYNC_ALL = "Keep local files even if deleted on remote";
|
||||
export const STAGE2_ABORT = "Cancel all and reboot";
|
||||
|
||||
export async function askSimpleFetchMode(
|
||||
host: NecessaryServices<"UI" | "vault", "storageAccess">
|
||||
): Promise<{ mode: string; options: Partial<FullScanOptions> } | "cancelled" | "aborted"> {
|
||||
const msg = `We are about to retrieve the remote data.
|
||||
|
||||
Firstly, how shall we handle the data retrieved from this remote server?
|
||||
|
||||
- **${SIMPLE_FETCH_STAGE1_NEWER_WINS}**: Compares the modified time of files and takes the newer one.
|
||||
If you have been using Self-hosted LiveSync and have made changes on multiple devices, this option may be suitable for you as it tries to merge changes based on modified time.
|
||||
- **${SIMPLE_FETCH_STAGE1_REMOTE_WINS}**: Remote data is the source of truth.
|
||||
If you are new to using Self-hosted LiveSync. This option may be easiest to understand and get started with.
|
||||
It will overwrite all your local files with the remote data, so please make sure you have a backup if there is any important data in your vault.
|
||||
- **${SIMPLE_FETCH_STAGE1_LEGACY}**: Opens the detailed setup wizard.
|
||||
If you want to have more control over the synchronisation process, or want to review the changes before applying, you can choose this option to use the detailed flow.
|
||||
`;
|
||||
const stage1 = await host.services.UI.confirm.confirmWithMessage(
|
||||
"Data retrieval scheduled",
|
||||
msg,
|
||||
[
|
||||
SIMPLE_FETCH_STAGE1_NEWER_WINS,
|
||||
SIMPLE_FETCH_STAGE1_REMOTE_WINS,
|
||||
SIMPLE_FETCH_STAGE1_LEGACY,
|
||||
SIMPLE_FETCH_STAGE1_CANCEL,
|
||||
],
|
||||
SIMPLE_FETCH_STAGE1_NEWER_WINS,
|
||||
0
|
||||
);
|
||||
|
||||
if (!stage1 || stage1 === SIMPLE_FETCH_STAGE1_CANCEL) return "cancelled";
|
||||
|
||||
if (stage1 === SIMPLE_FETCH_STAGE1_LEGACY) {
|
||||
return { mode: "legacy", options: {} };
|
||||
}
|
||||
|
||||
if (stage1 === SIMPLE_FETCH_STAGE1_REMOTE_WINS) {
|
||||
const msg = `Since you have chosen to overwrite all local files with remote data, **how would you like to handle local files that are not present in the remote database?**
|
||||
|
||||
- **${SIMPLE_FETCH_STAGE2_REMOTE_DELETE_ALL}**: Local-only files and remote-deleted files will be removed.
|
||||
This option will make your local vault exactly the same as the remote database, but please make sure you have a backup if there is any important data in your vault.
|
||||
- **${SIMPLE_FETCH_STAGE2_REMOTE_DELETE_NONE}**: All existing local files will be preserved.
|
||||
This option will keep all your local files, but it may cause duplicates if there are files that exist on local but not on remote. You can clean up these duplicates manually after the synchronisation.`;
|
||||
|
||||
const stage2 = await host.services.UI.confirm.confirmWithMessage(
|
||||
"How to handle extra existing local files?",
|
||||
msg,
|
||||
[SIMPLE_FETCH_STAGE2_REMOTE_DELETE_ALL, SIMPLE_FETCH_STAGE2_REMOTE_DELETE_NONE, STAGE2_ABORT],
|
||||
SIMPLE_FETCH_STAGE2_REMOTE_DELETE_NONE,
|
||||
0
|
||||
);
|
||||
if (!stage2) return "cancelled";
|
||||
if (stage2 === STAGE2_ABORT) {
|
||||
return "aborted";
|
||||
}
|
||||
return {
|
||||
mode: "remote-only",
|
||||
options: {
|
||||
mode: FullScanModes.DB_APPLY,
|
||||
extraOnRemote:
|
||||
stage2 === SIMPLE_FETCH_STAGE2_REMOTE_DELETE_ALL ? ExtraOnRemote.DELETE_LOCAL_MISSING : undefined,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if (stage1 === SIMPLE_FETCH_STAGE1_NEWER_WINS) {
|
||||
const msg = `How should files that were deleted on other devices be handled?
|
||||
|
||||
- **${SIMPLE_FETCH_STAGE2_NEWER_CLEANUP}**: Delete local files if they were deleted on remote.
|
||||
This is useful if you want to keep your vault clean and consistent across devices, but please make sure you have a backup if there is already any important data in your vault.
|
||||
- **${SIMPLE_FETCH_STAGE2_NEWER_SYNC_ALL}**: Recreate remote files even if they were deleted on remote.
|
||||
This option will keep all your local files, but it may cause duplicates if there are files that exist on local but not on remote. You can clean up these duplicates manually after the synchronisation.
|
||||
`;
|
||||
|
||||
const stage2 = await host.services.UI.confirm.confirmWithMessage(
|
||||
"Conflict & Deletion Options",
|
||||
msg,
|
||||
[SIMPLE_FETCH_STAGE2_NEWER_CLEANUP, SIMPLE_FETCH_STAGE2_NEWER_SYNC_ALL, STAGE2_ABORT],
|
||||
SIMPLE_FETCH_STAGE2_NEWER_SYNC_ALL,
|
||||
0
|
||||
);
|
||||
if (!stage2) return "cancelled";
|
||||
if (stage2 === STAGE2_ABORT) {
|
||||
return "aborted";
|
||||
}
|
||||
return {
|
||||
mode: "newer-wins",
|
||||
options: {
|
||||
mode: FullScanModes.NEWER_WINS,
|
||||
extraOnLocal:
|
||||
stage2 === SIMPLE_FETCH_STAGE2_NEWER_CLEANUP
|
||||
? ExtraOnLocal.DELETE_DB_DELETED
|
||||
: ExtraOnLocal.APPEND_STORAGE_ONLY,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
return "cancelled";
|
||||
}
|
||||
|
||||
const RERUN_PROCESS = "Reboot to re-run the process";
|
||||
const RELEASE_FLAG_PROCESS = "Finalise the process and resume normal operation";
|
||||
export async function askAndPerformFastSetupOnScheduledFetchAll(
|
||||
host: NecessaryServices<
|
||||
| "vault"
|
||||
| "fileProcessing"
|
||||
| "tweakValue"
|
||||
| "UI"
|
||||
| "setting"
|
||||
| "appLifecycle"
|
||||
| "path"
|
||||
| "keyValueDB"
|
||||
| "database",
|
||||
"storageAccess" | "rebuilder" | "fileHandler"
|
||||
>,
|
||||
log: LogFunction,
|
||||
cleanupFlag: () => Promise<void>
|
||||
): Promise<boolean | undefined> {
|
||||
const result = await askSimpleFetchMode(host);
|
||||
if (result === "cancelled") {
|
||||
log("Fetch cancelled by user.", LOG_LEVEL_NOTICE);
|
||||
await cleanupFlag();
|
||||
host.services.appLifecycle.performRestart();
|
||||
return false;
|
||||
}
|
||||
if (result === "aborted") {
|
||||
log("Fetch exited by user.", LOG_LEVEL_NOTICE);
|
||||
host.services.appLifecycle.performRestart();
|
||||
return false;
|
||||
}
|
||||
if (result.mode === "legacy") {
|
||||
return undefined; // Let the legacy flow handle it.
|
||||
}
|
||||
|
||||
return await processVaultInitialisation(host, log, async () => {
|
||||
const settings = host.services.setting.currentSettings();
|
||||
await adjustSettingToRemoteIfNeeded(host, log, { preventFetchingConfig: false }, settings);
|
||||
// 1. Perform fast DB fetch (download remote DB content to local DB)
|
||||
await host.serviceModules.rebuilder.$fetchLocalDBFast(false);
|
||||
|
||||
// 2. Call the extended synchroniseAllFilesBetweenDBandStorage to reflect changes in storage
|
||||
const errorManager = new UnresolvedErrorManager(host.services.appLifecycle);
|
||||
const syncResult = await synchroniseAllFilesBetweenDBandStorage(
|
||||
host,
|
||||
log,
|
||||
errorManager,
|
||||
normaliseFullScanOptions({
|
||||
...result.options,
|
||||
showingNotice: true,
|
||||
omitEvents: true,
|
||||
ignoreSuspending: true,
|
||||
})
|
||||
);
|
||||
if (!syncResult) {
|
||||
const canRelease = await host.services.UI.confirm.askSelectStringDialogue(
|
||||
"Some files failed to synchronise. What would you like to do?",
|
||||
[RERUN_PROCESS, RELEASE_FLAG_PROCESS],
|
||||
{ defaultAction: RELEASE_FLAG_PROCESS, title: "Synchronisation Issues Detected" }
|
||||
);
|
||||
if (canRelease === RERUN_PROCESS) {
|
||||
log("User chose to reboot and re-run the process.", LOG_LEVEL_NOTICE);
|
||||
// Prevent to delete the flag, so that the process will be re-run after reboot.
|
||||
// await cleanupFlag();
|
||||
host.services.appLifecycle.performRestart();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
await host.serviceModules.rebuilder.finishRebuild();
|
||||
await cleanupFlag();
|
||||
log("Simple fetch and scan operation completed.", LOG_LEVEL_NOTICE);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
@@ -12,6 +12,9 @@ import type {
|
||||
FetchEverythingResult,
|
||||
RebuildEverythingResult,
|
||||
} from "@/modules/features/SetupWizard/dialogs/setupDialogTypes";
|
||||
import { askAndPerformFastSetupOnScheduledFetchAll } from "./redFlag.simpleFetch";
|
||||
import { ConnectionStringParser } from "@lib/common/ConnectionString";
|
||||
import { activateRemoteConfiguration } from "@lib/serviceFeatures/remoteConfig";
|
||||
|
||||
/**
|
||||
* Flag file handler interface, similar to target filter pattern.
|
||||
@@ -45,14 +48,79 @@ export async function deleteFlagFile(host: NecessaryServices<never, "storageAcce
|
||||
log(ex, LOG_LEVEL_VERBOSE);
|
||||
}
|
||||
}
|
||||
const REMOTE_KEEP_CURRENT = "Use active remote";
|
||||
const REMOTE_CANCEL = "Cancel";
|
||||
async function askAndActivateRemoteDatabase(host: NecessaryServices<"UI" | "setting", any>, log: LogFunction) {
|
||||
const settings = host.services.setting.currentSettings();
|
||||
if (settings.remoteConfigurations && Object.keys(settings.remoteConfigurations).length > 1) {
|
||||
const message =
|
||||
"Multiple remote configurations detected. Please select the remote configuration you want to fetch from.";
|
||||
const options = Object.entries(settings.remoteConfigurations).map(([id, config]) => {
|
||||
const parsed = ConnectionStringParser.parse(config.uri);
|
||||
const displayURI = (config.uri.split("@").pop() || "").substring(0, 20) + "..."; // Show only the last part of URI for better readability and privacy.
|
||||
return {
|
||||
name: `${config.name} - ${parsed.type} (${displayURI})`,
|
||||
id: id,
|
||||
};
|
||||
});
|
||||
options.push({
|
||||
name: REMOTE_KEEP_CURRENT,
|
||||
id: "keep_current",
|
||||
});
|
||||
options.push({
|
||||
name: REMOTE_CANCEL,
|
||||
id: "cancel",
|
||||
});
|
||||
|
||||
const selections = options.map((option) => option.name);
|
||||
// const defaultAction =
|
||||
// options.find((option) => option.id === settings.activeConfigurationId)?.name || selections[0];
|
||||
const selectedId = await host.services.UI.confirm.askSelectStringDialogue(message, selections, {
|
||||
title: "Select Remote Configuration",
|
||||
defaultAction: REMOTE_KEEP_CURRENT,
|
||||
});
|
||||
const selectedConfig = options.find((option) => option.name === selectedId);
|
||||
if (selectedConfig) {
|
||||
if (selectedConfig.id === "keep_current") {
|
||||
log(`Keeping current remote configuration.`, LOG_LEVEL_INFO);
|
||||
return true;
|
||||
}
|
||||
if (selectedConfig.id === "cancel") {
|
||||
log(`Remote configuration selection cancelled.`, LOG_LEVEL_NOTICE);
|
||||
return false;
|
||||
}
|
||||
const activated = activateRemoteConfiguration(settings, selectedConfig.id);
|
||||
if (activated) {
|
||||
await host.services.setting.applyPartial(activated);
|
||||
log(`Activated remote configuration: ${selectedConfig.name}`, LOG_LEVEL_INFO);
|
||||
return true;
|
||||
} else {
|
||||
log(`Failed to activate remote configuration: ${selectedConfig.name}`, LOG_LEVEL_NOTICE);
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
log(`No remote configuration selected.`, LOG_LEVEL_NOTICE);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true; // If there is only one or no remote configuration, proceed without asking.
|
||||
}
|
||||
/**
|
||||
* Factory function to create a fetch all flag handler.
|
||||
* All logic related to fetch all flag is encapsulated here.
|
||||
*/
|
||||
export function createFetchAllFlagHandler(
|
||||
host: NecessaryServices<
|
||||
"vault" | "fileProcessing" | "tweakValue" | "UI" | "setting" | "appLifecycle",
|
||||
"storageAccess" | "rebuilder"
|
||||
| "vault"
|
||||
| "fileProcessing"
|
||||
| "tweakValue"
|
||||
| "UI"
|
||||
| "setting"
|
||||
| "appLifecycle"
|
||||
| "path"
|
||||
| "keyValueDB"
|
||||
| "database",
|
||||
"storageAccess" | "rebuilder" | "fileHandler"
|
||||
>,
|
||||
log: LogFunction
|
||||
): FlagFileHandler {
|
||||
@@ -69,6 +137,19 @@ export function createFetchAllFlagHandler(
|
||||
|
||||
// Handle the fetch all scheduled operation
|
||||
const onScheduled = async () => {
|
||||
// Select the remote database if there are multiple remotes configured.
|
||||
const isRemoteActivated = await askAndActivateRemoteDatabase(host, log);
|
||||
if (!isRemoteActivated) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Ask user for use Fast Setup
|
||||
const useFastSetup = await askAndPerformFastSetupOnScheduledFetchAll(host, log, cleanupFlag);
|
||||
if (useFastSetup !== undefined) {
|
||||
return useFastSetup;
|
||||
}
|
||||
// if useFastSetup is undefined, it means user choose to proceed with normal fetch process, so continue to ask for fetch method.
|
||||
|
||||
const method =
|
||||
await host.services.UI.dialogManager.openWithExplicitCancel<FetchEverythingResult>(FetchEverything);
|
||||
if (method === "cancelled") {
|
||||
@@ -380,8 +461,17 @@ export function flagHandlerToEventHandler(flagHandler: FlagFileHandler) {
|
||||
|
||||
export function useRedFlagFeatures(
|
||||
host: NecessaryServices<
|
||||
"API" | "appLifecycle" | "UI" | "setting" | "tweakValue" | "fileProcessing" | "vault",
|
||||
"storageAccess" | "rebuilder"
|
||||
| "API"
|
||||
| "appLifecycle"
|
||||
| "UI"
|
||||
| "setting"
|
||||
| "tweakValue"
|
||||
| "fileProcessing"
|
||||
| "vault"
|
||||
| "path"
|
||||
| "keyValueDB"
|
||||
| "database",
|
||||
"storageAccess" | "rebuilder" | "fileHandler"
|
||||
>
|
||||
) {
|
||||
const log = createInstanceLogFunction("SF:RedFlag", host.services.API);
|
||||
|
||||
@@ -19,6 +19,45 @@ import {
|
||||
TweakValuesShouldMatchedTemplate,
|
||||
TweakValuesTemplate,
|
||||
} from "@/lib/src/common/types";
|
||||
import {
|
||||
ExtraOnLocal,
|
||||
FullScanModes,
|
||||
synchroniseAllFilesBetweenDBandStorage,
|
||||
} from "@/lib/src/serviceFeatures/offlineScanner";
|
||||
import {
|
||||
SIMPLE_FETCH_STAGE1_LEGACY,
|
||||
SIMPLE_FETCH_STAGE1_NEWER_WINS,
|
||||
SIMPLE_FETCH_STAGE1_REMOTE_WINS,
|
||||
SIMPLE_FETCH_STAGE2_NEWER_CLEANUP,
|
||||
SIMPLE_FETCH_STAGE2_NEWER_SYNC_ALL,
|
||||
SIMPLE_FETCH_STAGE2_REMOTE_DELETE_NONE,
|
||||
SIMPLE_FETCH_STAGE2_REMOTE_DELETE_ALL,
|
||||
STAGE2_ABORT,
|
||||
askAndPerformFastSetupOnScheduledFetchAll,
|
||||
askSimpleFetchMode,
|
||||
} from "./redFlag.simpleFetch";
|
||||
import { activateRemoteConfiguration } from "@lib/serviceFeatures/remoteConfig";
|
||||
//Mock synchroniseAllFilesBetweenDBandStorage
|
||||
vi.mock("@/lib/src/serviceFeatures/offlineScanner", async (importOriginal) => {
|
||||
const originalModule = (await importOriginal()) as any;
|
||||
return {
|
||||
...originalModule,
|
||||
synchroniseAllFilesBetweenDBandStorage: vi.fn(() => Promise.resolve(true)),
|
||||
};
|
||||
});
|
||||
|
||||
vi.mock("@lib/serviceFeatures/remoteConfig", () => {
|
||||
return {
|
||||
activateRemoteConfiguration: vi.fn((settings: any, configurationId: string) => {
|
||||
if (!settings?.remoteConfigurations?.[configurationId]) return false;
|
||||
return {
|
||||
activeConfigurationId: configurationId,
|
||||
remoteType: settings.remoteConfigurations[configurationId].remoteType ?? settings.remoteType,
|
||||
remoteURI: settings.remoteConfigurations[configurationId].uri,
|
||||
};
|
||||
}),
|
||||
};
|
||||
});
|
||||
|
||||
// Mock types and functions
|
||||
const createLoggerMock = (): LogFunction => {
|
||||
@@ -68,6 +107,9 @@ const createAppLifecycleMock = () => {
|
||||
onLayoutReady: {
|
||||
addHandler: vi.fn(),
|
||||
},
|
||||
getUnresolvedMessages: {
|
||||
addHandler: vi.fn(),
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
@@ -79,6 +121,7 @@ const createUIServiceMock = () => {
|
||||
confirm: {
|
||||
askSelectStringDialogue: vi.fn(),
|
||||
askYesNoDialog: vi.fn(),
|
||||
confirmWithMessage: vi.fn(),
|
||||
},
|
||||
};
|
||||
};
|
||||
@@ -86,7 +129,9 @@ const createUIServiceMock = () => {
|
||||
const createRebuilderMock = () => {
|
||||
return {
|
||||
$fetchLocal: vi.fn(async () => {}),
|
||||
$fetchLocalDBFast: vi.fn(async () => {}),
|
||||
$rebuildEverything: vi.fn(async () => {}),
|
||||
finishRebuild: vi.fn(async () => {}),
|
||||
};
|
||||
};
|
||||
|
||||
@@ -389,6 +434,394 @@ describe("Red Flag Feature", () => {
|
||||
const handler = createFetchAllFlagHandler(host as any, log);
|
||||
expect(handler.priority).toBe(10);
|
||||
});
|
||||
|
||||
it("should use simplified remote-only mode and call performFullScan", async () => {
|
||||
const host = createHostMock();
|
||||
const log = createLoggerMock();
|
||||
|
||||
host.mocks.storageAccess.files.add(FlagFilesOriginal.FETCH_ALL);
|
||||
// Stage 1: Overwrite all with remote files
|
||||
// Stage 2: Delete local files if not on remote (Clean overwrite)
|
||||
host.mocks.ui.confirm.confirmWithMessage
|
||||
.mockResolvedValueOnce(SIMPLE_FETCH_STAGE1_REMOTE_WINS)
|
||||
.mockResolvedValueOnce(SIMPLE_FETCH_STAGE2_REMOTE_DELETE_ALL);
|
||||
|
||||
host.mocks.tweakValue.fetchRemotePreferred.mockResolvedValueOnce({
|
||||
batchSave: false,
|
||||
} as any);
|
||||
|
||||
const handler = createFetchAllFlagHandler(host as any, log);
|
||||
const result = await handler.handle();
|
||||
|
||||
expect(result).toBe(true);
|
||||
expect(host.mocks.rebuilder.$fetchLocalDBFast).toHaveBeenCalled();
|
||||
expect(synchroniseAllFilesBetweenDBandStorage).toHaveBeenCalled();
|
||||
// We can't easily check performFullScan call here because it's imported,
|
||||
// but we can verify rebuilder was called.
|
||||
});
|
||||
|
||||
it("should restore legacy fetch flow when requested", async () => {
|
||||
const host = createHostMock();
|
||||
const log = createLoggerMock();
|
||||
|
||||
host.mocks.storageAccess.files.add(FlagFilesOriginal.FETCH_ALL);
|
||||
host.mocks.ui.confirm.confirmWithMessage.mockResolvedValueOnce(SIMPLE_FETCH_STAGE1_LEGACY);
|
||||
host.mocks.ui.dialogManager.openWithExplicitCancel.mockResolvedValueOnce({
|
||||
vault: "identical",
|
||||
backup: "backup_skipped",
|
||||
extra: { preventFetchingConfig: false },
|
||||
});
|
||||
host.mocks.tweakValue.fetchRemotePreferred.mockResolvedValueOnce({
|
||||
batchSave: false,
|
||||
} as any);
|
||||
const handler = createFetchAllFlagHandler(host as any, log);
|
||||
const result = await handler.handle();
|
||||
|
||||
expect(result).toBe(true);
|
||||
expect(host.mocks.ui.dialogManager.openWithExplicitCancel).toHaveBeenCalled();
|
||||
expect(host.mocks.rebuilder.$fetchLocal).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("should cancel fetch flow when first quick step is cancelled", async () => {
|
||||
const host = createHostMock();
|
||||
const log = createLoggerMock();
|
||||
|
||||
host.mocks.storageAccess.files.add(FlagFilesOriginal.FETCH_ALL);
|
||||
host.mocks.ui.confirm.confirmWithMessage.mockResolvedValueOnce(false);
|
||||
|
||||
const handler = createFetchAllFlagHandler(host as any, log);
|
||||
host.mocks.tweakValue.fetchRemotePreferred.mockResolvedValueOnce({
|
||||
batchSave: false,
|
||||
} as any);
|
||||
const result = await handler.handle();
|
||||
|
||||
expect(result).toBe(false);
|
||||
expect(host.mocks.rebuilder.$fetchLocal).not.toHaveBeenCalled();
|
||||
expect(host.mocks.appLifecycle.performRestart).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("should use remote-authoritative quick mode for empty vault", async () => {
|
||||
const host = createHostMock();
|
||||
const log = createLoggerMock();
|
||||
|
||||
host.mocks.storageAccess.files.add(FlagFilesOriginal.FETCH_ALL);
|
||||
host.mocks.ui.confirm.confirmWithMessage
|
||||
.mockResolvedValueOnce(SIMPLE_FETCH_STAGE1_REMOTE_WINS)
|
||||
.mockResolvedValueOnce(SIMPLE_FETCH_STAGE2_REMOTE_DELETE_ALL);
|
||||
|
||||
host.mocks.tweakValue.fetchRemotePreferred.mockResolvedValueOnce({
|
||||
batchSave: false,
|
||||
} as any);
|
||||
const handler = createFetchAllFlagHandler(host as any, log);
|
||||
const result = await handler.handle();
|
||||
|
||||
expect(result).toBe(true);
|
||||
expect(host.mocks.rebuilder.$fetchLocalDBFast).toHaveBeenCalled();
|
||||
expect(host.mocks.rebuilder.$fetchLocal).not.toHaveBeenCalledWith(false, true);
|
||||
});
|
||||
|
||||
it("should keep current remote configuration when selected", async () => {
|
||||
const host = createHostMock();
|
||||
const log = createLoggerMock();
|
||||
|
||||
host.mocks.storageAccess.files.add(FlagFilesOriginal.FETCH_ALL);
|
||||
Object.assign(host.mocks.setting.settings, {
|
||||
remoteConfigurations: {
|
||||
alpha: { name: "Alpha", uri: "sls+https://user:pass@example.com/db1" },
|
||||
beta: { name: "Beta", uri: "sls+https://user:pass@example.com/db2" },
|
||||
},
|
||||
});
|
||||
host.mocks.ui.confirm.askSelectStringDialogue.mockResolvedValueOnce("Use active remote");
|
||||
host.mocks.ui.confirm.confirmWithMessage.mockResolvedValueOnce(false);
|
||||
|
||||
const handler = createFetchAllFlagHandler(host as any, log);
|
||||
const result = await handler.handle();
|
||||
|
||||
expect(result).toBe(false);
|
||||
expect(host.mocks.setting.applyPartial).not.toHaveBeenCalledWith(
|
||||
expect.objectContaining({ activeConfigurationId: expect.any(String) })
|
||||
);
|
||||
});
|
||||
|
||||
it("should stop when remote selection is cancelled", async () => {
|
||||
const host = createHostMock();
|
||||
const log = createLoggerMock();
|
||||
|
||||
host.mocks.storageAccess.files.add(FlagFilesOriginal.FETCH_ALL);
|
||||
Object.assign(host.mocks.setting.settings, {
|
||||
remoteConfigurations: {
|
||||
alpha: { name: "Alpha", uri: "sls+https://user:pass@example.com/db1" },
|
||||
beta: { name: "Beta", uri: "sls+https://user:pass@example.com/db2" },
|
||||
},
|
||||
});
|
||||
host.mocks.ui.confirm.askSelectStringDialogue.mockResolvedValueOnce("Cancel");
|
||||
|
||||
const handler = createFetchAllFlagHandler(host as any, log);
|
||||
const result = await handler.handle();
|
||||
|
||||
expect(result).toBe(false);
|
||||
expect(host.mocks.ui.confirm.confirmWithMessage).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("should activate selected remote configuration", async () => {
|
||||
const host = createHostMock();
|
||||
const log = createLoggerMock();
|
||||
|
||||
host.mocks.storageAccess.files.add(FlagFilesOriginal.FETCH_ALL);
|
||||
Object.assign(host.mocks.setting.settings, {
|
||||
remoteConfigurations: {
|
||||
alpha: {
|
||||
name: "Alpha",
|
||||
uri: "sls+https://user:pass@example.com/db1",
|
||||
remoteType: "CouchDB",
|
||||
},
|
||||
beta: {
|
||||
name: "Beta",
|
||||
uri: "sls+https://user:pass@example.com/db2",
|
||||
remoteType: "CouchDB",
|
||||
},
|
||||
},
|
||||
});
|
||||
host.mocks.ui.confirm.askSelectStringDialogue.mockImplementationOnce(
|
||||
async (_message: string, selections: string[]) => selections.find((e) => e.startsWith("Beta -"))
|
||||
);
|
||||
host.mocks.ui.confirm.confirmWithMessage.mockResolvedValueOnce(false);
|
||||
|
||||
const handler = createFetchAllFlagHandler(host as any, log);
|
||||
const result = await handler.handle();
|
||||
|
||||
expect(result).toBe(false);
|
||||
expect(activateRemoteConfiguration).toHaveBeenCalledWith(host.mocks.setting.settings, "beta");
|
||||
expect(host.mocks.setting.applyPartial).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ activeConfigurationId: "beta" })
|
||||
);
|
||||
});
|
||||
|
||||
it("should stop when selected remote name is unknown", async () => {
|
||||
const host = createHostMock();
|
||||
const log = createLoggerMock();
|
||||
|
||||
host.mocks.storageAccess.files.add(FlagFilesOriginal.FETCH_ALL);
|
||||
Object.assign(host.mocks.setting.settings, {
|
||||
remoteConfigurations: {
|
||||
alpha: { name: "Alpha", uri: "sls+https://user:pass@example.com/db1" },
|
||||
beta: { name: "Beta", uri: "sls+https://user:pass@example.com/db2" },
|
||||
},
|
||||
});
|
||||
host.mocks.ui.confirm.askSelectStringDialogue.mockResolvedValueOnce("Unknown option");
|
||||
|
||||
const handler = createFetchAllFlagHandler(host as any, log);
|
||||
const result = await handler.handle();
|
||||
|
||||
expect(result).toBe(false);
|
||||
expect(host.mocks.ui.confirm.confirmWithMessage).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("should stop when remote activation fails", async () => {
|
||||
const host = createHostMock();
|
||||
const log = createLoggerMock();
|
||||
|
||||
host.mocks.storageAccess.files.add(FlagFilesOriginal.FETCH_ALL);
|
||||
Object.assign(host.mocks.setting.settings, {
|
||||
remoteConfigurations: {
|
||||
alpha: { name: "Alpha", uri: "sls+https://user:pass@example.com/db1" },
|
||||
beta: { name: "Beta", uri: "sls+https://user:pass@example.com/db2" },
|
||||
},
|
||||
});
|
||||
host.mocks.ui.confirm.askSelectStringDialogue.mockImplementationOnce(
|
||||
async (_message: string, selections: string[]) => selections.find((e) => e.startsWith("Beta -"))
|
||||
);
|
||||
(activateRemoteConfiguration as any).mockReturnValueOnce(false);
|
||||
|
||||
const handler = createFetchAllFlagHandler(host as any, log);
|
||||
const result = await handler.handle();
|
||||
|
||||
expect(result).toBe(false);
|
||||
expect(host.mocks.ui.confirm.confirmWithMessage).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe("askSimpleFetchMode", () => {
|
||||
it("should return cancelled when stage1 is cancelled", async () => {
|
||||
const host = createHostMock();
|
||||
host.mocks.ui.confirm.confirmWithMessage.mockResolvedValueOnce(false);
|
||||
|
||||
await expect(askSimpleFetchMode(host as any)).resolves.toBe("cancelled");
|
||||
});
|
||||
|
||||
it("should return legacy mode when selected", async () => {
|
||||
const host = createHostMock();
|
||||
host.mocks.ui.confirm.confirmWithMessage.mockResolvedValueOnce(SIMPLE_FETCH_STAGE1_LEGACY);
|
||||
|
||||
await expect(askSimpleFetchMode(host as any)).resolves.toEqual({ mode: "legacy", options: {} });
|
||||
});
|
||||
|
||||
it("should return remote-only with keep-local option", async () => {
|
||||
const host = createHostMock();
|
||||
host.mocks.ui.confirm.confirmWithMessage
|
||||
.mockResolvedValueOnce(SIMPLE_FETCH_STAGE1_REMOTE_WINS)
|
||||
.mockResolvedValueOnce(SIMPLE_FETCH_STAGE2_REMOTE_DELETE_NONE);
|
||||
|
||||
await expect(askSimpleFetchMode(host as any)).resolves.toEqual({
|
||||
mode: "remote-only",
|
||||
options: {
|
||||
mode: FullScanModes.DB_APPLY,
|
||||
extraOnRemote: undefined,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it("should return cancelled when remote-only stage2 is cancelled", async () => {
|
||||
const host = createHostMock();
|
||||
host.mocks.ui.confirm.confirmWithMessage
|
||||
.mockResolvedValueOnce(SIMPLE_FETCH_STAGE1_REMOTE_WINS)
|
||||
.mockResolvedValueOnce(false);
|
||||
|
||||
await expect(askSimpleFetchMode(host as any)).resolves.toBe("cancelled");
|
||||
});
|
||||
|
||||
it("should return aborted when remote-only stage2 aborts", async () => {
|
||||
const host = createHostMock();
|
||||
host.mocks.ui.confirm.confirmWithMessage
|
||||
.mockResolvedValueOnce(SIMPLE_FETCH_STAGE1_REMOTE_WINS)
|
||||
.mockResolvedValueOnce(STAGE2_ABORT);
|
||||
|
||||
await expect(askSimpleFetchMode(host as any)).resolves.toBe("aborted");
|
||||
});
|
||||
|
||||
it("should return newer-wins cleanup option", async () => {
|
||||
const host = createHostMock();
|
||||
host.mocks.ui.confirm.confirmWithMessage
|
||||
.mockResolvedValueOnce(SIMPLE_FETCH_STAGE1_NEWER_WINS)
|
||||
.mockResolvedValueOnce(SIMPLE_FETCH_STAGE2_NEWER_CLEANUP);
|
||||
|
||||
await expect(askSimpleFetchMode(host as any)).resolves.toEqual({
|
||||
mode: "newer-wins",
|
||||
options: {
|
||||
mode: FullScanModes.NEWER_WINS,
|
||||
extraOnLocal: ExtraOnLocal.DELETE_DB_DELETED,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it("should return newer-wins keep-all option", async () => {
|
||||
const host = createHostMock();
|
||||
host.mocks.ui.confirm.confirmWithMessage
|
||||
.mockResolvedValueOnce(SIMPLE_FETCH_STAGE1_NEWER_WINS)
|
||||
.mockResolvedValueOnce(SIMPLE_FETCH_STAGE2_NEWER_SYNC_ALL);
|
||||
|
||||
await expect(askSimpleFetchMode(host as any)).resolves.toEqual({
|
||||
mode: "newer-wins",
|
||||
options: {
|
||||
mode: FullScanModes.NEWER_WINS,
|
||||
extraOnLocal: ExtraOnLocal.APPEND_STORAGE_ONLY,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it("should return cancelled when newer-wins stage2 is cancelled", async () => {
|
||||
const host = createHostMock();
|
||||
host.mocks.ui.confirm.confirmWithMessage
|
||||
.mockResolvedValueOnce(SIMPLE_FETCH_STAGE1_NEWER_WINS)
|
||||
.mockResolvedValueOnce(false);
|
||||
|
||||
await expect(askSimpleFetchMode(host as any)).resolves.toBe("cancelled");
|
||||
});
|
||||
|
||||
it("should return aborted when newer-wins stage2 aborts", async () => {
|
||||
const host = createHostMock();
|
||||
host.mocks.ui.confirm.confirmWithMessage
|
||||
.mockResolvedValueOnce(SIMPLE_FETCH_STAGE1_NEWER_WINS)
|
||||
.mockResolvedValueOnce(STAGE2_ABORT);
|
||||
|
||||
await expect(askSimpleFetchMode(host as any)).resolves.toBe("aborted");
|
||||
});
|
||||
});
|
||||
|
||||
describe("askAndPerformFastSetupOnScheduledFetchAll", () => {
|
||||
it("should return false and cleanup when quick flow is cancelled", async () => {
|
||||
const host = createHostMock();
|
||||
const log = createLoggerMock();
|
||||
const cleanupFlag = vi.fn().mockResolvedValue(undefined);
|
||||
|
||||
host.mocks.ui.confirm.confirmWithMessage.mockResolvedValueOnce(false);
|
||||
|
||||
const result = await askAndPerformFastSetupOnScheduledFetchAll(host as any, log, cleanupFlag);
|
||||
|
||||
expect(result).toBe(false);
|
||||
expect(cleanupFlag).toHaveBeenCalled();
|
||||
expect(host.mocks.appLifecycle.performRestart).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("should return false without cleanup when quick flow is aborted", async () => {
|
||||
const host = createHostMock();
|
||||
const log = createLoggerMock();
|
||||
const cleanupFlag = vi.fn().mockResolvedValue(undefined);
|
||||
|
||||
host.mocks.ui.confirm.confirmWithMessage
|
||||
.mockResolvedValueOnce(SIMPLE_FETCH_STAGE1_REMOTE_WINS)
|
||||
.mockResolvedValueOnce(STAGE2_ABORT);
|
||||
|
||||
const result = await askAndPerformFastSetupOnScheduledFetchAll(host as any, log, cleanupFlag);
|
||||
|
||||
expect(result).toBe(false);
|
||||
expect(cleanupFlag).not.toHaveBeenCalled();
|
||||
expect(host.mocks.appLifecycle.performRestart).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("should return undefined when legacy mode is selected", async () => {
|
||||
const host = createHostMock();
|
||||
const log = createLoggerMock();
|
||||
const cleanupFlag = vi.fn().mockResolvedValue(undefined);
|
||||
|
||||
host.mocks.ui.confirm.confirmWithMessage.mockResolvedValueOnce(SIMPLE_FETCH_STAGE1_LEGACY);
|
||||
|
||||
const result = await askAndPerformFastSetupOnScheduledFetchAll(host as any, log, cleanupFlag);
|
||||
|
||||
expect(result).toBeUndefined();
|
||||
expect(host.mocks.rebuilder.$fetchLocalDBFast).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("should reboot and return false when sync has failures and user chooses rerun", async () => {
|
||||
const host = createHostMock();
|
||||
const log = createLoggerMock();
|
||||
const cleanupFlag = vi.fn().mockResolvedValue(undefined);
|
||||
|
||||
host.mocks.ui.confirm.confirmWithMessage
|
||||
.mockResolvedValueOnce(SIMPLE_FETCH_STAGE1_REMOTE_WINS)
|
||||
.mockResolvedValueOnce(SIMPLE_FETCH_STAGE2_REMOTE_DELETE_ALL);
|
||||
host.mocks.tweakValue.fetchRemotePreferred.mockResolvedValueOnce({ batchSave: false } as any);
|
||||
(synchroniseAllFilesBetweenDBandStorage as any).mockResolvedValueOnce(false);
|
||||
host.mocks.ui.confirm.askSelectStringDialogue.mockResolvedValueOnce("Reboot to re-run the process");
|
||||
|
||||
const result = await askAndPerformFastSetupOnScheduledFetchAll(host as any, log, cleanupFlag);
|
||||
|
||||
expect(result).toBe(false);
|
||||
expect(host.mocks.appLifecycle.performRestart).toHaveBeenCalled();
|
||||
expect(cleanupFlag).not.toHaveBeenCalled();
|
||||
expect(host.mocks.rebuilder.finishRebuild).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("should continue and finalise when sync has failures but user releases flag", async () => {
|
||||
const host = createHostMock();
|
||||
const log = createLoggerMock();
|
||||
const cleanupFlag = vi.fn().mockResolvedValue(undefined);
|
||||
|
||||
host.mocks.ui.confirm.confirmWithMessage
|
||||
.mockResolvedValueOnce(SIMPLE_FETCH_STAGE1_REMOTE_WINS)
|
||||
.mockResolvedValueOnce(SIMPLE_FETCH_STAGE2_REMOTE_DELETE_ALL);
|
||||
host.mocks.tweakValue.fetchRemotePreferred.mockResolvedValueOnce({ batchSave: false } as any);
|
||||
(synchroniseAllFilesBetweenDBandStorage as any).mockResolvedValueOnce(false);
|
||||
host.mocks.ui.confirm.askSelectStringDialogue.mockResolvedValueOnce(
|
||||
"Finalise the process and resume normal operation"
|
||||
);
|
||||
|
||||
const result = await askAndPerformFastSetupOnScheduledFetchAll(host as any, log, cleanupFlag);
|
||||
|
||||
expect(result).toBe(true);
|
||||
expect(host.mocks.rebuilder.finishRebuild).toHaveBeenCalled();
|
||||
expect(cleanupFlag).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe("Rebuild All Flag Handler", () => {
|
||||
@@ -980,6 +1413,8 @@ describe("Red Flag Feature", () => {
|
||||
const log = createLoggerMock();
|
||||
|
||||
host.mocks.storageAccess.files.add(FlagFilesOriginal.FETCH_ALL);
|
||||
host.mocks.tweakValue.fetchRemotePreferred.mockResolvedValueOnce({});
|
||||
host.mocks.ui.confirm.confirmWithMessage.mockResolvedValueOnce(SIMPLE_FETCH_STAGE1_LEGACY);
|
||||
host.mocks.ui.dialogManager.openWithExplicitCancel.mockResolvedValueOnce("cancelled");
|
||||
|
||||
const handler = createFetchAllFlagHandler(host as any, log);
|
||||
@@ -1056,11 +1491,12 @@ describe("Red Flag Feature", () => {
|
||||
it("should handle fetchAll flag with flagHandlerToEventHandler identical", async () => {
|
||||
const host = createHostMock();
|
||||
const log = createLoggerMock();
|
||||
host.mocks.tweakValue.fetchRemotePreferred.mockResolvedValueOnce({
|
||||
host.mocks.tweakValue.fetchRemotePreferred.mockResolvedValue({
|
||||
customChunkSize: 1,
|
||||
} as any);
|
||||
|
||||
host.mocks.storageAccess.files.add(FlagFilesOriginal.FETCH_ALL);
|
||||
host.mocks.ui.confirm.confirmWithMessage.mockResolvedValueOnce(SIMPLE_FETCH_STAGE1_LEGACY);
|
||||
host.mocks.ui.dialogManager.openWithExplicitCancel.mockResolvedValueOnce({ vault: "identical", extra: {} });
|
||||
host.mocks.rebuilder.$fetchLocal.mockResolvedValueOnce();
|
||||
const handler = createFetchAllFlagHandler(host as any, log);
|
||||
|
||||
240
updates.md
240
updates.md
@@ -3,20 +3,38 @@ Since 19th July, 2025 (beta1 in 0.25.0-beta1, 13th July, 2025)
|
||||
|
||||
The head note of 0.25 is now in [updates_old.md](https://github.com/vrtmrz/obsidian-livesync/blob/main/updates_old.md). Because 0.25 got a lot of updates, thankfully, compatibility is kept and we do not need breaking changes! In other words, when get enough stabled. The next version will be v1.0.0. Even though it my hope.
|
||||
|
||||
## 0.25.70-patch1
|
||||
## 0.25.73
|
||||
|
||||
1st June, 2026
|
||||
4th June, 2026
|
||||
|
||||
This release does not include any changes to behaviour (if everything is as intended).
|
||||
However, this release had addressed a large number of errors and potential issues caused by the switch to a modern ESLint configuration, as well as unintended log output.
|
||||
I have also separated out some parts where the type definitions were a bit loose.
|
||||
### Fixed
|
||||
|
||||
As the diff has become too large, I am releasing it as a beta.
|
||||
To anyone who has submitted a pull request, please bear with me for a little while.
|
||||
- Adjust CouchDB's database name checking to its specification (#926).
|
||||
- `Reset Syncronisation on This Device` for minio and P2P is now working properly.
|
||||
|
||||
## ~~0.25.71~~ 0.25.72
|
||||
|
||||
0.25.71 was cancelled due to the fixes needed (Object Storage related)
|
||||
|
||||
3rd June, 2026
|
||||
|
||||
### Improved
|
||||
|
||||
- Database fetching (a.k.a. Reset Synchronisation on This Device) on the initialisation now supports streaming and is faster (CouchDB only)
|
||||
- The database fetching process has been streamlined, and database operations are now suspended until it has been completed
|
||||
- The initial synchronisation process has been simplified, making it easier to synchronise files with the remote server
|
||||
- We can select the remote database to fetch from during the initialisation, when there are multiple remote databases configured (e.g. multiple CouchDBs or S3 remotes)
|
||||
- Hebrew (he) Translation has been added (Thank you so much, @MusiCode1)!
|
||||
- Translation loading time has been reduced (Thank you so much, @bmcyver)!
|
||||
|
||||
### Fixed
|
||||
|
||||
- No longer does the status element break other plugins' interaction (#930).
|
||||
- No longer does file events occured during initial database fetching using Object Storage.
|
||||
|
||||
### Refactored
|
||||
|
||||
- Many
|
||||
To support the new Community automated tests, we fixed numerous lint warnings. This may have also resolved potential issues.
|
||||
|
||||
## 0.25.70
|
||||
|
||||
@@ -87,211 +105,5 @@ Thank you so much to @SeleiXi for implementing these features!
|
||||
- Added a `Generate full report for opening the issue with debug info` command to the command palette, which generates a report without opening the settings dialogue.
|
||||
|
||||
|
||||
## 0.25.64
|
||||
|
||||
17th May, 2026
|
||||
|
||||
### P2P Status Pane
|
||||
|
||||
- Added active P2P remote selector (combo box) and `+` action to create/select a P2P remote from the P2P setup dialogue.
|
||||
- Added per-peer immediate replication action on accepted peers.
|
||||
- Updated status control icons for clarity:
|
||||
- Replicate now: `🔄` (`⏳` while running)
|
||||
- Watch: `🔔` / `🔕`
|
||||
- Sync target: `🔗` / `⛓️💥`
|
||||
- Added warning state when no active P2P remote is selected.
|
||||
|
||||
### P2P Status Card
|
||||
|
||||
- Added stable Room ID suffix display and placed it above Peer ID for better identification.
|
||||
|
||||
### Non behavioural internal changes
|
||||
|
||||
#### P2P
|
||||
|
||||
- Added `P2P_ActiveRemoteConfigurationId` as a dedicated active remote selection for P2P features, separate from the normal active remote.
|
||||
- Added activation logic for P2P dedicated remote configuration that reflects P2P settings while keeping `remoteType` unchanged.
|
||||
- Added migration support to carry over P2P active remote selection when appropriate.
|
||||
- Added shared Room ID utility functions and applied them across P2P setup and P2P panes.
|
||||
|
||||
#### Tests
|
||||
|
||||
- Added/updated unit test coverage around settings load behaviour for P2P active remote application.
|
||||
|
||||
## 0.25.63
|
||||
|
||||
17th May, 2026
|
||||
|
||||
### Fixed
|
||||
- The issue which cannot synchronise in Only-P2P mode has been fixed.
|
||||
- Fixed an issue where "Failed to connect to the remote server" was shown during the redFlag rebuild flow when P2P was the primary remote type. Remote configuration fetch is now skipped for P2P.
|
||||
|
||||
### P2P Replication UI Improvements
|
||||
- Brand-new P2P Server Status pane has been added to provide real-time visibility into your connection status and peer network.
|
||||
- For detailed instructions on using the new P2P features, please refer to the updated [User Guide: Peer-to-Peer Synchronisation (2026 Edition)](./docs/p2p_sync_updates_2026.md).
|
||||
- Now `Replicate` button or ribbon icon opens a redesigned interactive replication dialogue that performs smart bidirectional sync with a single click.
|
||||
- The vault rebuild flow (`replicateAllFromServer`) now opens the redesigned P2P Replication modal instead of a plain text selection dialogue, providing a consistent UI experience.
|
||||
|
||||
## 0.25.62
|
||||
|
||||
14th May, 2026
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed an issue where a connection could not be established when attempting to connect to a brand-new remote database without going through the set-up wizard or configuration checking (#660).
|
||||
|
||||
## 0.25.61
|
||||
|
||||
13th May, 2026
|
||||
|
||||
Reviews have started on the Obsidian Community, haven't they? It was quite a struggle, what with having to fix the outdated ESLint.
|
||||
I am a bit nervous, but it is far better than just plodding along aimlessly, so let us get on with it. If you spot any issues, please let me know straight away.
|
||||
|
||||
From now on, I am avoiding committing directly to the main branch. This is because you lots have all been sending so much PRs. I wanted to keep things harmonious.
|
||||
That said, I am still not used to rebasing, so there are some parts where the commit history is a right mess. I will work on improving that.
|
||||
|
||||
### Improved
|
||||
|
||||
- P2P synchronisation has been made more robust
|
||||
Now the foundation for P2P synchronisation has been rewritten, and the unit tests have been added. The foundation has been separated into the transport layer, signalling-and-connection layer, and, an RPC layers. And each layer has been unit-tested. As the result, the P2P synchronisation now uses the robust shim that uses RPC-ed PouchDB synchronisation in contrast to previous implementation.
|
||||
This P2P synchronisation is not compatible with previous versions in terms of connectivity. All devices must be updated.
|
||||
|
||||
### Fixed
|
||||
|
||||
- No longer baffling errors occur when setting-update is triggered during the early stage of initialisation.
|
||||
- Network error notice pop-ups are now suppressed when 'NetworkWarningStyle' is set to 'Hidden'. (Thank you so much @SeleiXi!)
|
||||
|
||||
### New features
|
||||
|
||||
- Diff navigation buttons have been added to the diff view, making it easier to move between differences. (Thank you so much @SeleiXi! #871)
|
||||
|
||||
### Translations
|
||||
|
||||
- Chinese (Simplified) translations for settings and the Setup Wizard have been added. (Thank you so much @zombiek731!)
|
||||
- Common UI controls and signal words are now localised into Chinese (Simplified). (Thank you so much @zombiek731!)
|
||||
- i18n runtime behaviour and locale coverage have been improved. (Thank you so much @52sanmao!)
|
||||
|
||||
### CLI
|
||||
|
||||
#### New features
|
||||
|
||||
- Daemon synchronisation is now supported. (Thank you so much @andrewleech! #843)
|
||||
- `HeadlessConfirm` has been implemented with sensible defaults, enabling unattended operation in headless environments. (Thank you so much @andrewleech!)
|
||||
- The CLI onboarding experience has been improved. (Thank you so much @OriBoharon! #872)
|
||||
|
||||
#### Fixed
|
||||
|
||||
- Sub-millisecond CLI mtimes are now truncated to prevent mobile crash. (Thank you so much @brian-spackman! #893)
|
||||
|
||||
## 0.25.60
|
||||
|
||||
29th April, 2026
|
||||
|
||||
### Fixed
|
||||
|
||||
- Now larger settings can be exported and imported via QR code without issues. (#595)
|
||||
- When the settings data exceeds the QR code capacity, it is now split into multiple QR codes.
|
||||
- These QR codes are reassembled by the aggregator page, which collects the split data and reconstructs the original settings.
|
||||
- Aggregator page is available at `https://vrtmrz.github.io/obsidian-livesync/aggregator.html`, and this file is also included in the repository.
|
||||
- We will not send the settings data to any server. The QR code data is generated and processed entirely on the client side, ensuring that your settings remain private and secure. HOWEVER, please be careful your network environment.
|
||||
- Fixed some errors during serialisation and deserialisation of the settings, which caused issues in some cases when importing/exporting settings via QR code.
|
||||
|
||||
### Fixed (CLI)
|
||||
|
||||
- `ls` and `mirror` commands now provide informative feedback when no documents are found or filters skip all files, resolving the issue where they would exit silently (#860).
|
||||
- Improved the clarity of CLI command logs by including the total count of processed items.
|
||||
- The command-line argument `vault` has been renamed to a more appropriate name, `databaseDir`.
|
||||
- The `mirror` command now accepts a `vault` directory, which specifies the location where the actual files are stored. For compatibility reasons, the previous behaviour is still supported.
|
||||
|
||||
## 0.25.59
|
||||
|
||||
### Fixed
|
||||
|
||||
- No longer Setup-wizard drops username and password silently. (#865)
|
||||
- Thank you so much for @koteitan !
|
||||
- Setup URI is now correctly imported (#859).
|
||||
- Also thank you so much for @koteitan !
|
||||
|
||||
### Improved
|
||||
|
||||
- now French translation is added by @foXaCe ! Thank you so much!
|
||||
|
||||
## 0.25.58
|
||||
|
||||
### Fixed
|
||||
|
||||
- No longer credentials are broken during object storage configuration (related: #852).
|
||||
- Fixed a worker-side recursion issue that could raise `Maximum call stack size exceeded` during chunk splitting (related: #855).
|
||||
- Improved background worker crash cleanup so pending split/encryption tasks are released cleanly instead of being left in a waiting state (related: #855).
|
||||
- On start-up, the selected remote configuration is now applied to runtime connection fields as well, reducing intermittent authentication failures caused by stale runtime settings (related: #855).
|
||||
- Issue report generation now redacts `remoteConfigurations` connection strings and keeps only the scheme (e.g. `sls+https://`), so credentials are not exposed in reports.
|
||||
- Hidden file JSON conflicts no longer keep re-opening and dismissing the merge dialogue before we can act, which fixes persistent unresolvable `data.json` conflicts in plug-in settings sync (related: #850).
|
||||
|
||||
## 0.25.57
|
||||
|
||||
9th April, 2026
|
||||
|
||||
- Packing a batch during the journal sync now continues even if the batch contains no items to upload.
|
||||
- No unexpected error (about a replicator) during the early stage of initialisation.
|
||||
- Now error messages are kept hidden if the show status inside the editor is disabled (related: #829).
|
||||
- Fixed an issue where devices could no longer upload after another device performed 'Fresh Start Wipe' and 'Overwrite remote' in Object Storage mode (#848).
|
||||
- Each device's local deduplication caches (`knownIDs`, `sentIDs`, `receivedFiles`, `sentFiles`) now track the remote journal epoch (derived from the encryption parameters stored on the remote).
|
||||
- When the epoch changes, the plugin verifies whether the device's last uploaded file still exists on the remote. If the file is gone, it confirms a remote wipe and automatically clears the stale caches. If the file is still present (e.g. a protocol upgrade without a wipe), the caches are preserved, and only the epoch is updated. This means normal upgrades never cause unnecessary re-processing.
|
||||
|
||||
### Translations
|
||||
|
||||
- Russian translation has been added! Thank you so much for the contribution, @vipka1n! (#845)
|
||||
|
||||
### New features
|
||||
|
||||
- Now we can configure multiple Remote Databases of the same type, e.g, multiple CouchDBs or S3 remotes.
|
||||
- A user interface for managing multiple remote databases has been added to the settings dialogue. I think no explanation is needed for the UI, but please let me know if you have any questions.
|
||||
- We can switch between multiple Remote Databases in the settings dialogue.
|
||||
|
||||
### CLI
|
||||
|
||||
#### Fixed
|
||||
|
||||
- Replication progress is now correctly saved and restored in the CLI (related: #846).
|
||||
|
||||
## ~~0.25.55~~ 0.25.56
|
||||
|
||||
30th March, 2026
|
||||
|
||||
### Fixed
|
||||
|
||||
- No longer `Peer-to-Peer Sync is not enabled. We cannot open a new connection.` error occurs when we have not enabled P2P sync and are not expected to use it (#830).
|
||||
|
||||
### CLI
|
||||
|
||||
- Fixed incomplete localStorage support in the CLI (#831). Thank you so much @rewse !
|
||||
- Fixed the issue where the CLI could not be connected to the remote which had been locked once (#833), also thanks to @rewse !
|
||||
|
||||
## 0.25.54
|
||||
|
||||
18th March, 2026
|
||||
|
||||
### Fixed
|
||||
|
||||
- Remote storage size check now works correctly again (#818).
|
||||
- Some buttons on the settings dialogue now respond correctly again (#827).
|
||||
|
||||
### Refactored
|
||||
|
||||
- P2P replicator has been refactored to be a little more robust and easier to understand.
|
||||
- Delete items which are no longer used that might cause potential problems
|
||||
|
||||
### CLI
|
||||
|
||||
- Fixed the corrupted display of the help message.
|
||||
- Remove some unnecessary code.
|
||||
|
||||
### WebApp
|
||||
|
||||
- Fixed the issue where the detail level was not being applied in the log pane.
|
||||
- Pop-ups are now shown.
|
||||
- Add coverage for the test.
|
||||
- Pop-ups are now shown in the web app as well.
|
||||
|
||||
Full notes are in
|
||||
[updates_old.md](https://github.com/vrtmrz/obsidian-livesync/blob/main/updates_old.md).
|
||||
|
||||
100
updates_old.md
100
updates_old.md
@@ -3,6 +3,106 @@ Since 19th July, 2025 (beta1 in 0.25.0-beta1, 13th July, 2025)
|
||||
|
||||
The head note of 0.25 is now in [updates_old.md](https://github.com/vrtmrz/obsidian-livesync/blob/main/updates_old.md). Because 0.25 got a lot of updates, thankfully, compatibility is kept and we do not need breaking changes! In other words, when get enough stabled. The next version will be v1.0.0. Even though it my hope.
|
||||
|
||||
## 0.25.73
|
||||
|
||||
4th June, 2026
|
||||
|
||||
### Fixed
|
||||
|
||||
- Adjust CouchDB's database name checking to its specification (#926).
|
||||
- `Reset Syncronisation on This Device` for minio and P2P is now working properly.
|
||||
|
||||
## ~~0.25.71~~ 0.25.72
|
||||
|
||||
0.25.71 was cancelled due to the fixes needed (Object Storage related)
|
||||
|
||||
3rd June, 2026
|
||||
|
||||
### Improved
|
||||
|
||||
- Database fetching (a.k.a. Reset Synchronisation on This Device) on the initialisation now supports streaming and is faster (CouchDB only)
|
||||
- The database fetching process has been streamlined, and database operations are now suspended until it has been completed
|
||||
- The initial synchronisation process has been simplified, making it easier to synchronise files with the remote server
|
||||
- We can select the remote database to fetch from during the initialisation, when there are multiple remote databases configured (e.g. multiple CouchDBs or S3 remotes)
|
||||
- Hebrew (he) Translation has been added (Thank you so much, @MusiCode1)!
|
||||
- Translation loading time has been reduced (Thank you so much, @bmcyver)!
|
||||
|
||||
### Fixed
|
||||
|
||||
- No longer does the status element break other plugins' interaction (#930).
|
||||
- No longer does file events occured during initial database fetching using Object Storage.
|
||||
|
||||
### Refactored
|
||||
|
||||
To support the new Community automated tests, we fixed numerous lint warnings. This may have also resolved potential issues.
|
||||
|
||||
## 0.25.70
|
||||
|
||||
25th May, 2026
|
||||
|
||||
### New features
|
||||
- Diff dialogue now has great tools to navigate and understand the differences, including:
|
||||
- A checkbox to toggle the visibility of collapsed identical sections, making it easier to focus on the actual differences (PR #889).
|
||||
- A search feature to find specific text in past revisions, and navigate revisions with search results highlighted in the dialogue (PR #890).
|
||||
|
||||
- Conflict resolution dialogue now has a navigation feature to jump between conflicts (PR #891).
|
||||
|
||||
Thank you so much to @SeleiXi for implementing these features!
|
||||
|
||||
### Improved
|
||||
|
||||
- More diagnostic information for P2P connections is now shown, including why a connection failure occurred and the current connection status.
|
||||
|
||||
## 0.25.69
|
||||
|
||||
22nd May, 2026
|
||||
|
||||
### Fixed
|
||||
- No longer does the P2P passphrase mismatch cause a server shutdown.
|
||||
- Settings related to P2P synchronisation are now correctly applied on start-up and no longer reverted.
|
||||
|
||||
### New features
|
||||
- Diagnostic P2P connection stats are now available.
|
||||
- These stats indicate the number of connection trials, successes, and failures.
|
||||
|
||||
## 0.25.68
|
||||
|
||||
22nd May, 2026
|
||||
|
||||
### Improved
|
||||
|
||||
- P2P connections have improved slightly
|
||||
- Upgrade to `trystero` v0.24.0, and fixes event handler assignment. This should fix some edge cases where P2P connections fail to establish or messages are not properly handled.
|
||||
- Weaken terser options to avoid potential issues with minification that could cause runtime errors in some environments.
|
||||
|
||||
## ~~0.25.66~~ 0.25.67
|
||||
|
||||
20th May, 2026
|
||||
|
||||
0.25.66 had a bug that the auto-accept logic for compatible but lossy mismatches was not working as intended.
|
||||
|
||||
### New features
|
||||
- Implement an auto-accept compatible tweak setting and enhance the mismatch resolution logic.
|
||||
|
||||
### Improved
|
||||
- Many messages related to tweak mismatch resolution have been updated for clarity.
|
||||
|
||||
## 0.25.65
|
||||
|
||||
19th May, 2026
|
||||
|
||||
### Fixed
|
||||
- Fix an issue about resuming from background on iOS (#888).
|
||||
- Now Chunk Splitter: `V3: Fine Deduplication` is working fine again (#866).
|
||||
- It has some drawbacks, such as fewer chunks are generated. However, it makes less transfer and storage when the files are modified but not completely changed.
|
||||
- Unsynchronised local changes (which means changes that have not been sent) are now correctly preserved as a conflict (Thank you so much for @SeleiXi!).
|
||||
- Avoid creating a new revision when the current and conflicted revisions have identical content (Thank you so much for @daichi-629).
|
||||
|
||||
### Improved
|
||||
- Improved the error verbosity on concurrent processing during the start-up process.
|
||||
- Now the `report` includes recent logs (of verbosity `verbose` even settings is not set to `verbose`).
|
||||
- Updating logs is now debounced to avoid excessive updates during rapid log generation.
|
||||
- Added a `Generate full report for opening the issue with debug info` command to the command palette, which generates a report without opening the settings dialogue.
|
||||
|
||||
## 0.25.64
|
||||
|
||||
|
||||
@@ -11,17 +11,24 @@ export default mergeConfig(
|
||||
},
|
||||
},
|
||||
test: {
|
||||
logHeapUsage: true,
|
||||
// maxConcurrency: 2,
|
||||
name: "unit-tests",
|
||||
include: ["**/*unit.test.ts", "**/*.unit.spec.ts"],
|
||||
exclude: ["test/**"],
|
||||
exclude: ["test/**", "src/apps/**/testdeno/**"],
|
||||
coverage: {
|
||||
include: ["src/**/*.ts"],
|
||||
exclude: [
|
||||
"**/*.test.ts",
|
||||
"**/*unit.test.ts",
|
||||
"**/*.unit.spec.ts",
|
||||
"test/**",
|
||||
"src/lib/**/*.test.ts",
|
||||
"**/_*",
|
||||
"src/lib/apps",
|
||||
"src/lib/src/cli",
|
||||
"src/apps/**/testdeno/**",
|
||||
// "src/apps/**",
|
||||
// "src/cli/**",
|
||||
"src/lib/src/cli/**",
|
||||
"**/*_obsolete.ts",
|
||||
...importOnlyFiles,
|
||||
],
|
||||
|
||||
Reference in New Issue
Block a user