Merge 6ea7d53a49 into 9a75569e83
This commit is contained in:
commit
afc96005ea
91 changed files with 6230 additions and 9161 deletions
11
.github/workflows/check.yml
vendored
11
.github/workflows/check.yml
vendored
|
|
@ -23,14 +23,19 @@ jobs:
|
||||||
- name: Setup Node.js environment
|
- name: Setup Node.js environment
|
||||||
uses: actions/setup-node@v4.2.0
|
uses: actions/setup-node@v4.2.0
|
||||||
with:
|
with:
|
||||||
node-version: "22.x"
|
node-version: "25.x"
|
||||||
check-latest: true
|
check-latest: true
|
||||||
|
|
||||||
- name: Setup Rust toolchain
|
- name: Setup Rust toolchain
|
||||||
uses: dtolnay/rust-toolchain@stable
|
uses: dtolnay/rust-toolchain@stable
|
||||||
with:
|
with:
|
||||||
toolchain: "1.89.0"
|
toolchain: "1.92.0"
|
||||||
components: clippy, rustfmt
|
components: clippy, rustfmt
|
||||||
|
|
||||||
|
- name: Install Task
|
||||||
|
uses: arduino/setup-task@v2
|
||||||
|
with:
|
||||||
|
version: 3.x
|
||||||
|
|
||||||
- name: Lint & test
|
- name: Lint & test
|
||||||
run: scripts/check.sh
|
run: task check
|
||||||
|
|
|
||||||
20
.github/workflows/deploy-docs.yml
vendored
20
.github/workflows/deploy-docs.yml
vendored
|
|
@ -5,8 +5,8 @@ on:
|
||||||
branches:
|
branches:
|
||||||
- main
|
- main
|
||||||
paths:
|
paths:
|
||||||
- 'docs/**'
|
- "docs/**"
|
||||||
- '.github/workflows/deploy-docs.yml'
|
- ".github/workflows/deploy-docs.yml"
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
|
|
@ -28,18 +28,22 @@ jobs:
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
- name: Setup Node
|
- name: Setup Node.js environment
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v4.2.0
|
||||||
with:
|
with:
|
||||||
node-version: 22
|
node-version: "25.x"
|
||||||
cache: npm
|
check-latest: true
|
||||||
cache-dependency-path: docs/package-lock.json
|
|
||||||
|
- name: Install Task
|
||||||
|
uses: arduino/setup-task@v2
|
||||||
|
with:
|
||||||
|
version: 3.x
|
||||||
|
|
||||||
- name: Setup Pages
|
- name: Setup Pages
|
||||||
uses: actions/configure-pages@v4
|
uses: actions/configure-pages@v4
|
||||||
|
|
||||||
- name: Build docs
|
- name: Build docs
|
||||||
run: scripts/build-docs.sh
|
run: task docs:check
|
||||||
|
|
||||||
- name: Upload artifact
|
- name: Upload artifact
|
||||||
uses: actions/upload-pages-artifact@v3
|
uses: actions/upload-pages-artifact@v3
|
||||||
|
|
|
||||||
23
.github/workflows/e2e.yml
vendored
23
.github/workflows/e2e.yml
vendored
|
|
@ -6,7 +6,7 @@ on:
|
||||||
pull_request:
|
pull_request:
|
||||||
branches: ["main"]
|
branches: ["main"]
|
||||||
schedule:
|
schedule:
|
||||||
- cron: '0 * * * *'
|
- cron: "0 * * * *"
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
concurrency:
|
concurrency:
|
||||||
|
|
@ -28,21 +28,22 @@ jobs:
|
||||||
- name: Setup Node.js environment
|
- name: Setup Node.js environment
|
||||||
uses: actions/setup-node@v4.2.0
|
uses: actions/setup-node@v4.2.0
|
||||||
with:
|
with:
|
||||||
node-version: "22.x"
|
node-version: "25.x"
|
||||||
check-latest: true
|
check-latest: true
|
||||||
|
|
||||||
- name: Setup Rust toolchain
|
- name: Setup Rust toolchain
|
||||||
uses: dtolnay/rust-toolchain@stable
|
uses: dtolnay/rust-toolchain@stable
|
||||||
with:
|
with:
|
||||||
toolchain: "1.89.0"
|
toolchain: "1.92.0"
|
||||||
components: clippy, rustfmt
|
components: clippy, rustfmt
|
||||||
|
|
||||||
- name: Setup rust
|
- name: Install Task
|
||||||
run: |
|
uses: arduino/setup-task@v2
|
||||||
which sqlx || cargo install sqlx-cli
|
with:
|
||||||
cd sync-server
|
version: 3.x
|
||||||
sqlx database create --database-url sqlite://db.sqlite3
|
|
||||||
sqlx migrate run --source src/app_state/database/migrations --database-url sqlite://db.sqlite3
|
- name: Setup database
|
||||||
|
run: task db:setup
|
||||||
|
|
||||||
- name: E2E tests
|
- name: E2E tests
|
||||||
run: |
|
run: |
|
||||||
|
|
@ -51,7 +52,7 @@ jobs:
|
||||||
SERVER_PID=$!
|
SERVER_PID=$!
|
||||||
cd ..
|
cd ..
|
||||||
|
|
||||||
scripts/e2e.sh 8
|
task e2e -- 8
|
||||||
EXIT_CODE=$?
|
EXIT_CODE=$?
|
||||||
|
|
||||||
kill $SERVER_PID 2>/dev/null || true
|
kill $SERVER_PID 2>/dev/null || true
|
||||||
|
|
@ -69,4 +70,4 @@ jobs:
|
||||||
|
|
||||||
- name: Cleanup
|
- name: Cleanup
|
||||||
if: always()
|
if: always()
|
||||||
run: scripts/clean-up.sh
|
run: task clean
|
||||||
|
|
|
||||||
22
.github/workflows/publish-plugin.yml
vendored
22
.github/workflows/publish-plugin.yml
vendored
|
|
@ -19,28 +19,30 @@ jobs:
|
||||||
- name: Setup Node.js environment
|
- name: Setup Node.js environment
|
||||||
uses: actions/setup-node@v4.2.0
|
uses: actions/setup-node@v4.2.0
|
||||||
with:
|
with:
|
||||||
node-version: "22.x"
|
node-version: "25.x"
|
||||||
check-latest: true
|
check-latest: true
|
||||||
|
|
||||||
- name: Build plugin
|
|
||||||
run: |
|
|
||||||
cd frontend
|
|
||||||
npm ci
|
|
||||||
npm run build
|
|
||||||
|
|
||||||
- name: Setup Rust toolchain
|
- name: Setup Rust toolchain
|
||||||
uses: dtolnay/rust-toolchain@stable
|
uses: dtolnay/rust-toolchain@stable
|
||||||
with:
|
with:
|
||||||
toolchain: "1.89.0"
|
toolchain: "1.92.0"
|
||||||
components: clippy, rustfmt
|
components: clippy, rustfmt
|
||||||
|
|
||||||
|
- name: Install Task
|
||||||
|
uses: arduino/setup-task@v2
|
||||||
|
with:
|
||||||
|
version: 3.x
|
||||||
|
|
||||||
- name: Install cross-compilation tools
|
- name: Install cross-compilation tools
|
||||||
run: |
|
run: |
|
||||||
apt update
|
apt update
|
||||||
apt install -y gcc-aarch64-linux-gnu musl-tools gcc-mingw-w64-x86-64
|
apt install -y gcc-aarch64-linux-gnu musl-tools gcc-mingw-w64-x86-64
|
||||||
|
|
||||||
- name: Build Linux and Windows binaries
|
- name: Build frontend
|
||||||
run: ./scripts/build-sync-server-binaries.sh
|
run: task frontend:build
|
||||||
|
|
||||||
|
- name: Build binaries
|
||||||
|
run: task release:build-binaries
|
||||||
|
|
||||||
- name: Create release
|
- name: Create release
|
||||||
env:
|
env:
|
||||||
|
|
|
||||||
4
.vscode/settings.json
vendored
4
.vscode/settings.json
vendored
|
|
@ -5,6 +5,6 @@
|
||||||
"**/dist": true,
|
"**/dist": true,
|
||||||
"**/node_modules": true,
|
"**/node_modules": true,
|
||||||
"**/.sqlx": true,
|
"**/.sqlx": true,
|
||||||
"**/target": true,
|
"**/target": true
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
240
CLAUDE.md
240
CLAUDE.md
|
|
@ -4,7 +4,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
|
||||||
|
|
||||||
## Project Overview
|
## Project Overview
|
||||||
|
|
||||||
VaultLink is a self-hosted Obsidian plugin for real-time collaborative file syncing. The project consists of a Rust-based sync server and a TypeScript frontend with three main components: an Obsidian plugin, a sync client library, and a test client.
|
VaultLink is a self-hosted Obsidian plugin for real-time collaborative file syncing. The project consists of a Rust-based sync server and a TypeScript frontend with four main components: an Obsidian plugin, a sync client library, a test client, and a standalone CLI client.
|
||||||
|
|
||||||
## Architecture
|
## Architecture
|
||||||
|
|
||||||
|
|
@ -13,98 +13,234 @@ VaultLink is a self-hosted Obsidian plugin for real-time collaborative file sync
|
||||||
- **sync-server/**: Rust-based WebSocket server with SQLite database for document versioning and real-time synchronization
|
- **sync-server/**: Rust-based WebSocket server with SQLite database for document versioning and real-time synchronization
|
||||||
- **frontend/sync-client/**: TypeScript library providing core sync functionality, WebSocket management, and file operations
|
- **frontend/sync-client/**: TypeScript library providing core sync functionality, WebSocket management, and file operations
|
||||||
- **frontend/obsidian-plugin/**: Obsidian plugin that integrates the sync client with Obsidian's API
|
- **frontend/obsidian-plugin/**: Obsidian plugin that integrates the sync client with Obsidian's API
|
||||||
- **frontend/test-client/**: CLI testing tool for the sync functionality
|
- **frontend/test-client/**: CLI testing tool for simulating multiple concurrent users
|
||||||
|
- **frontend/local-client-cli/**: Standalone CLI for VaultLink sync client
|
||||||
|
|
||||||
### Key Technologies
|
### Key Technologies
|
||||||
|
|
||||||
- **Backend**: Rust with Axum framework, SQLite with SQLx, WebSockets for real-time sync
|
- **Backend**: Rust with Axum framework, SQLite with SQLx, WebSockets for real-time sync
|
||||||
- **Frontend**: TypeScript, Webpack for bundling, Jest for testing
|
- **Frontend**: TypeScript, Webpack for bundling, Node.js native test runner
|
||||||
- **Sync Algorithm**: Uses reconcile-text library for operational transformation
|
- **Sync Algorithm**: Uses reconcile-text library for operational transformation
|
||||||
|
|
||||||
|
### Architectural Patterns
|
||||||
|
|
||||||
|
**Server Architecture:**
|
||||||
|
|
||||||
|
- `AppState`: Central state container holding `Database`, `Cursors`, and `Broadcasts`
|
||||||
|
- `Database`: SQLite-backed document versioning with SQLx for compile-time query verification
|
||||||
|
- `Broadcasts`: WebSocket broadcast system for real-time updates to connected clients
|
||||||
|
- `Cursors`: Tracks user cursor positions across documents with background cleanup task
|
||||||
|
|
||||||
|
**Client Architecture:**
|
||||||
|
|
||||||
|
- `SyncClient`: Main entry point, orchestrates all sync operations
|
||||||
|
- `SyncService`: HTTP API client for CRUD operations on documents
|
||||||
|
- `WebSocketManager`: Manages WebSocket connection and real-time updates
|
||||||
|
- `Syncer`: Coordinates file synchronization between local filesystem and server
|
||||||
|
- `CursorTracker`: Manages local and remote cursor positions
|
||||||
|
- `Database`: Client-side document metadata cache
|
||||||
|
- `FileOperations`: Abstraction layer for filesystem operations
|
||||||
|
|
||||||
|
**Dual-Bundle Strategy:**
|
||||||
|
The sync-client builds two separate bundles:
|
||||||
|
|
||||||
|
- `sync-client.web.js`: Browser-compatible UMD bundle (excludes `ws` package)
|
||||||
|
- `sync-client.node.js`: Node.js CommonJS bundle with WebSocket support
|
||||||
|
|
||||||
## Development Commands
|
## Development Commands
|
||||||
|
|
||||||
### Server Development
|
This project uses [Taskfile](https://taskfile.dev/) for task automation. Run `task --list` to see all available tasks.
|
||||||
|
|
||||||
|
### Initial Setup
|
||||||
|
|
||||||
|
**Taskfile:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Install Task (https://taskfile.dev/installation/)
|
||||||
|
# macOS
|
||||||
|
brew install go-task
|
||||||
|
|
||||||
|
# Linux
|
||||||
|
sh -c "$(curl --location https://taskfile.dev/install.sh)" -- -d -b ~/.local/bin
|
||||||
|
|
||||||
|
# Or via npm
|
||||||
|
npm install -g @go-task/cli
|
||||||
|
```
|
||||||
|
|
||||||
|
**Node.js (requires version 25):**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash
|
||||||
|
nvm install 25
|
||||||
|
nvm use 25
|
||||||
|
nvm alias default 25 # Optional: set as system default
|
||||||
|
```
|
||||||
|
|
||||||
|
**Rust:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
|
||||||
|
curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh
|
||||||
|
cargo install sqlx-cli cargo-machete cargo-edit cargo-insta
|
||||||
|
```
|
||||||
|
|
||||||
|
**Frontend:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
task frontend:install
|
||||||
|
```
|
||||||
|
|
||||||
|
### Common Tasks (Taskfile)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
task check # Full CI check (lint, test, format). Run before pushing.
|
||||||
|
task check:fix # Same as above but auto-fixes issues
|
||||||
|
task e2e -- 8 # E2E tests with 8 concurrent clients
|
||||||
|
task clean # Clean logs and database files
|
||||||
|
task update-api-types # Update TypeScript bindings from Rust types
|
||||||
|
task release:bump -- patch # Bump version (patch|minor|major)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Server Tasks
|
||||||
|
|
||||||
|
```bash
|
||||||
|
task rust:run # Start development server
|
||||||
|
task rust:test # Run all Rust tests
|
||||||
|
task rust:clippy # Lint Rust code
|
||||||
|
task rust:clippy-fix # Auto-fix clippy warnings
|
||||||
|
task rust:fmt # Format Rust code
|
||||||
|
task rust:fmt-check # Check Rust formatting
|
||||||
|
task rust:machete # Detect unused dependencies
|
||||||
|
```
|
||||||
|
|
||||||
|
### Frontend Tasks
|
||||||
|
|
||||||
|
```bash
|
||||||
|
task frontend:dev # Start development mode
|
||||||
|
task frontend:build # Build all workspaces
|
||||||
|
task frontend:test # Run all frontend tests
|
||||||
|
task frontend:lint # Lint and format TypeScript
|
||||||
|
```
|
||||||
|
|
||||||
|
### Database Tasks
|
||||||
|
|
||||||
|
```bash
|
||||||
|
task db:setup # Create and migrate database
|
||||||
|
task db:reset # Reset database (delete and recreate)
|
||||||
|
task db:prepare # Prepare SQLx offline data
|
||||||
|
task db:add-migration NAME=<migration_name> # Add new migration
|
||||||
|
```
|
||||||
|
|
||||||
|
### Documentation Tasks
|
||||||
|
|
||||||
|
```bash
|
||||||
|
task docs:check # Build and check documentation
|
||||||
|
task docs:dev # Start documentation dev server
|
||||||
|
```
|
||||||
|
|
||||||
|
### Direct Commands (Alternative)
|
||||||
|
|
||||||
|
If you prefer not to use Taskfile, these commands work directly:
|
||||||
|
|
||||||
|
**Server:**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd sync-server
|
cd sync-server
|
||||||
cargo run config-e2e.yml # Start development server
|
cargo run config-e2e.yml # Start development server
|
||||||
cargo test --verbose # Run Rust tests
|
cargo test --verbose # Run all Rust tests
|
||||||
cargo clippy --all-targets --all-features # Lint Rust code
|
cargo clippy --all-targets --all-features # Lint
|
||||||
cargo clippy --all-targets --all-features --fix --allow-dirty --allow-staged # Auto-fix clippy warnings
|
cargo fmt --all # Format
|
||||||
cargo fmt --all -- --check # Check Rust formatting
|
|
||||||
cargo fmt --all # Auto-format Rust code
|
|
||||||
cargo machete --with-metadata # Detect unused dependencies
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Frontend Development
|
**Frontend:**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd frontend
|
cd frontend
|
||||||
npm run dev # Start development mode (watches sync-client and obsidian-plugin)
|
npm run dev # Development mode
|
||||||
npm run build # Build all workspaces
|
npm run build # Build all workspaces
|
||||||
npm run test # Run all tests
|
npm run test # Run tests
|
||||||
npm run lint # Lint and format TypeScript code
|
npm run lint # Lint and format
|
||||||
```
|
```
|
||||||
|
|
||||||
### Database Setup (Development)
|
**Database:**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd sync-server
|
cd sync-server
|
||||||
sqlx database create --database-url sqlite://db.sqlite3
|
sqlx database create --database-url sqlite://db.sqlite3
|
||||||
sqlx migrate run --source src/app_state/database/migrations --database-url sqlite://db.sqlite3
|
sqlx migrate run --source src/app_state/database/migrations --database-url sqlite://db.sqlite3
|
||||||
cargo sqlx prepare --workspace
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Initial Setup
|
|
||||||
```bash
|
|
||||||
# Install required cargo tools
|
|
||||||
cargo install sqlx-cli cargo-machete cargo-edit
|
|
||||||
```
|
|
||||||
|
|
||||||
### Scripts
|
|
||||||
- `scripts/check.sh`: Full CI check (builds, lints, tests both server and frontend)
|
|
||||||
- `scripts/check.sh --fix`: Same as above but auto-fixes linting and formatting issues
|
|
||||||
- `scripts/e2e.sh`: End-to-end testing
|
|
||||||
- `scripts/clean-up.sh`: Clean logs and database files
|
|
||||||
- `scripts/bump-version.sh patch`: Publish new version
|
|
||||||
- `scripts/update-api-types.sh`: Update TypeScript bindings from Rust types
|
|
||||||
|
|
||||||
## Code Structure
|
## Code Structure
|
||||||
|
|
||||||
### Workspace Configuration
|
### Workspace Configuration
|
||||||
|
|
||||||
The frontend uses npm workspaces with four packages:
|
The frontend uses npm workspaces with four packages:
|
||||||
- `sync-client`: Core synchronization logic
|
|
||||||
|
- `sync-client`: Core synchronization logic (builds dual bundles for web and Node.js)
|
||||||
- `obsidian-plugin`: Obsidian-specific integration
|
- `obsidian-plugin`: Obsidian-specific integration
|
||||||
- `test-client`: Testing utilities
|
- `test-client`: Testing utilities for E2E tests
|
||||||
- `local-client-cli`: Standalone CLI for VaultLink sync client
|
- `local-client-cli`: Standalone CLI for VaultLink sync client
|
||||||
|
|
||||||
### Type Generation
|
### Type Generation and API Updates
|
||||||
Rust structs generate TypeScript types via ts-rs crate, stored in `sync-server/bindings/` and used by frontend packages.
|
|
||||||
|
|
||||||
### Key Files
|
Rust structs generate TypeScript types via ts-rs crate:
|
||||||
- `sync-server/src/`: Rust server implementation with WebSocket handlers
|
|
||||||
- `frontend/sync-client/src/sync-client.ts`: Main sync client entry point
|
1. Rust structs annotated with `#[derive(TS)]` export to `sync-server/bindings/`
|
||||||
- `frontend/obsidian-plugin/src/vault-link-plugin.ts`: Main Obsidian plugin class
|
2. Run `task update-api-types` to copy bindings to `frontend/sync-client/src/services/types/`
|
||||||
- `frontend/sync-client/src/services/sync-service.ts`: Core synchronization logic
|
3. Frontend imports these types for type-safe API communication
|
||||||
|
|
||||||
|
### Important Implementation Details
|
||||||
|
|
||||||
|
**SQLx Compile-Time Verification:**
|
||||||
|
|
||||||
|
- SQLx verifies SQL queries at compile time against the database schema
|
||||||
|
- Run `cargo sqlx prepare --workspace` after schema changes to update `.sqlx/` directory
|
||||||
|
- CI builds require prepared query metadata to avoid needing a live database
|
||||||
|
|
||||||
## Testing
|
## Testing
|
||||||
|
|
||||||
### Running Tests
|
### Running Tests
|
||||||
- Server: `cargo test --verbose`
|
|
||||||
- Frontend: `npm run test` (runs Jest across all workspaces)
|
```bash
|
||||||
- E2E: `scripts/e2e.sh`
|
task rust:test # All Rust tests
|
||||||
|
task frontend:test # All frontend tests
|
||||||
|
task e2e -- 8 # E2E with 8 concurrent clients
|
||||||
|
task clean # Clean up after tests
|
||||||
|
```
|
||||||
|
|
||||||
|
Or use direct commands:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd sync-server && cargo test --verbose # Rust tests
|
||||||
|
cd frontend && npm run test # Frontend tests
|
||||||
|
```
|
||||||
|
|
||||||
### Test Structure
|
### Test Structure
|
||||||
- Rust: Unit tests alongside source files
|
|
||||||
- TypeScript: `.test.ts` files using Jest
|
|
||||||
- E2E: Uses test-client to simulate multiple concurrent users
|
|
||||||
|
|
||||||
## Code Style
|
- **Rust**: Unit tests alongside source files, uses `cargo-insta` for snapshot testing
|
||||||
|
- **TypeScript**: `.test.ts` files using Node.js native test runner (not Jest)
|
||||||
|
- **E2E**: Uses `test-client` to simulate multiple concurrent users with random operations
|
||||||
|
|
||||||
|
## Code Style and Formatting
|
||||||
|
|
||||||
### Rust
|
### Rust
|
||||||
- Uses extensive Clippy lints (see Cargo.toml)
|
|
||||||
- Follows pedantic linting rules
|
- Extensive Clippy lints (see `Cargo.toml`)
|
||||||
|
- Pedantic linting rules enabled
|
||||||
- Forbids unsafe code
|
- Forbids unsafe code
|
||||||
- Uses cargo fmt with default settings
|
- Uses `rustfmt.toml` for formatting configuration (4 spaces, Unix line endings)
|
||||||
|
- Run `cargo fmt --all` to format
|
||||||
|
|
||||||
### TypeScript
|
### TypeScript
|
||||||
- Prettier configuration: 4-space tabs, trailing commas removed, LF line endings
|
|
||||||
- ESLint with unused imports plugin
|
- **Prettier**: 4-space indentation, no trailing commas, LF line endings
|
||||||
- Consistent across all three frontend packages
|
- **YAML/Markdown override**: 2-space indentation (via prettier config)
|
||||||
|
- **ESLint**: Strict rules with unused imports detection
|
||||||
|
- Configuration in `frontend/package.json`
|
||||||
|
- Run `npm run lint` to format and fix issues
|
||||||
|
|
||||||
|
### EditorConfig
|
||||||
|
|
||||||
|
- `.editorconfig` at project root defines baseline formatting rules
|
||||||
|
- `rustfmt.toml` and Prettier config explicitly mirror these settings
|
||||||
|
- Both formatters enforce: 4-space indent (2 for YAML/MD), LF endings, final newline, trim trailing whitespace
|
||||||
|
|
|
||||||
15
README.md
15
README.md
|
|
@ -8,12 +8,12 @@
|
||||||
|
|
||||||
## Develop
|
## Develop
|
||||||
|
|
||||||
### Install [nvm](https://github.com/nvm-sh/nvm)
|
### Set up Node.JS 25 with [nvm](https://github.com/nvm-sh/nvm)
|
||||||
|
|
||||||
- `curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash`
|
- `curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash`
|
||||||
- `nvm install 22`
|
- `nvm install 25`
|
||||||
- `nvm use 22`
|
- `nvm use 25`
|
||||||
- Optionally set the system-wide default: `nvm alias default 22`
|
- Optionally, set the system-wide default: `nvm alias default 25`
|
||||||
|
|
||||||
### Set up Rust
|
### Set up Rust
|
||||||
|
|
||||||
|
|
@ -77,3 +77,10 @@ And to clean up the logs & database files, run `scripts/clean-up.sh`
|
||||||
## Projects
|
## Projects
|
||||||
|
|
||||||
- [Sync server](./sync-server/README.md)
|
- [Sync server](./sync-server/README.md)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
a create that has been processed by the server but got lost on the way back will create a 2nd doc if it gets edited
|
||||||
|
|
|
||||||
96
Taskfile.yml
Normal file
96
Taskfile.yml
Normal file
|
|
@ -0,0 +1,96 @@
|
||||||
|
version: "3"
|
||||||
|
|
||||||
|
vars:
|
||||||
|
NODE_VERSION: "25"
|
||||||
|
|
||||||
|
includes:
|
||||||
|
rust:
|
||||||
|
taskfile: ./taskfiles/rust.yml
|
||||||
|
dir: ./sync-server
|
||||||
|
frontend:
|
||||||
|
taskfile: ./taskfiles/frontend.yml
|
||||||
|
dir: ./frontend
|
||||||
|
db:
|
||||||
|
taskfile: ./taskfiles/database.yml
|
||||||
|
dir: ./sync-server
|
||||||
|
e2e:
|
||||||
|
taskfile: ./taskfiles/e2e.yml
|
||||||
|
docs:
|
||||||
|
taskfile: ./taskfiles/docs.yml
|
||||||
|
dir: ./docs
|
||||||
|
release:
|
||||||
|
taskfile: ./taskfiles/release.yml
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
check:
|
||||||
|
desc: Run all checks (lint, test, format)
|
||||||
|
cmds:
|
||||||
|
- task: check-node
|
||||||
|
- task: db:setup
|
||||||
|
- task: rust:test
|
||||||
|
- task: rust:lint
|
||||||
|
- task: update-api-types
|
||||||
|
- task: frontend:install
|
||||||
|
- task: frontend:build
|
||||||
|
- task: frontend:test
|
||||||
|
- task: frontend:lint
|
||||||
|
- task: format
|
||||||
|
- task: check-clean
|
||||||
|
|
||||||
|
check:fix:
|
||||||
|
desc: Run all checks with auto-fix enabled
|
||||||
|
cmds:
|
||||||
|
- task: check-node
|
||||||
|
- task: db:setup
|
||||||
|
- task: rust:test
|
||||||
|
- task: rust:lint-fix
|
||||||
|
- task: update-api-types
|
||||||
|
- task: frontend:install
|
||||||
|
- task: frontend:build
|
||||||
|
- task: frontend:test
|
||||||
|
- task: frontend:lint
|
||||||
|
- task: format
|
||||||
|
|
||||||
|
check-node:
|
||||||
|
internal: true
|
||||||
|
silent: true
|
||||||
|
preconditions:
|
||||||
|
- sh: node -v | grep -q "^v{{.NODE_VERSION}}"
|
||||||
|
msg: "Node.js {{.NODE_VERSION}} required (found: $(node -v))"
|
||||||
|
cmds:
|
||||||
|
- echo "Node.js {{.NODE_VERSION}} confirmed"
|
||||||
|
|
||||||
|
check-clean:
|
||||||
|
internal: true
|
||||||
|
preconditions:
|
||||||
|
- sh: test -z "$(git status --porcelain)"
|
||||||
|
msg: |
|
||||||
|
Working directory not clean after linting:
|
||||||
|
$(git status --porcelain)
|
||||||
|
|
||||||
|
format:
|
||||||
|
desc: Format all files with Prettier
|
||||||
|
dir: "{{.ROOT_DIR}}"
|
||||||
|
cmds:
|
||||||
|
- npx -C frontend prettier --write "**/*.{ts,js,json,md,yml,yaml}"
|
||||||
|
|
||||||
|
update-api-types:
|
||||||
|
desc: Update TypeScript bindings from Rust types
|
||||||
|
cmds:
|
||||||
|
- rm -rf sync-server/bindings
|
||||||
|
- task: rust:export-bindings
|
||||||
|
- cp -r sync-server/bindings/* frontend/sync-client/src/services/types/
|
||||||
|
- cd frontend && npm run lint
|
||||||
|
- task: format
|
||||||
|
|
||||||
|
clean:
|
||||||
|
desc: Clean up logs and databases
|
||||||
|
cmds:
|
||||||
|
- rm -rf sync-server/databases logs
|
||||||
|
|
||||||
|
e2e:
|
||||||
|
desc: Run E2E tests (usage - task e2e -- 8)
|
||||||
|
cmds:
|
||||||
|
- task: e2e:run
|
||||||
|
vars:
|
||||||
|
PROCESS_COUNT: "{{.CLI_ARGS}}"
|
||||||
|
|
@ -2,12 +2,7 @@
|
||||||
"version": "0.2",
|
"version": "0.2",
|
||||||
"language": "en-GB",
|
"language": "en-GB",
|
||||||
"dictionaries": ["en-gb"],
|
"dictionaries": ["en-gb"],
|
||||||
"ignorePaths": [
|
"ignorePaths": ["node_modules", ".vitepress/dist", ".vitepress/cache", "package-lock.json"],
|
||||||
"node_modules",
|
|
||||||
".vitepress/dist",
|
|
||||||
".vitepress/cache",
|
|
||||||
"package-lock.json"
|
|
||||||
],
|
|
||||||
"words": [
|
"words": [
|
||||||
"VaultLink",
|
"VaultLink",
|
||||||
"Obsidian",
|
"Obsidian",
|
||||||
|
|
|
||||||
|
|
@ -361,11 +361,11 @@ VALUES (?, ?, ?);
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"type": "upload_file",
|
"type": "upload_file",
|
||||||
"path": "notes/example.md",
|
"path": "notes/example.md",
|
||||||
"content": "File content here...",
|
"content": "File content here...",
|
||||||
"base_version": 10,
|
"base_version": 10,
|
||||||
"timestamp": "2024-01-01T12:00:00Z"
|
"timestamp": "2024-01-01T12:00:00Z"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -373,8 +373,8 @@ VALUES (?, ?, ?);
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"type": "download_file",
|
"type": "download_file",
|
||||||
"path": "notes/example.md"
|
"path": "notes/example.md"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -382,8 +382,8 @@ VALUES (?, ?, ?);
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"type": "delete_file",
|
"type": "delete_file",
|
||||||
"path": "notes/old.md"
|
"path": "notes/old.md"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -391,8 +391,8 @@ VALUES (?, ?, ?);
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"type": "list_files",
|
"type": "list_files",
|
||||||
"since_version": 0
|
"since_version": 0
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -402,11 +402,11 @@ VALUES (?, ?, ?);
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"type": "file_updated",
|
"type": "file_updated",
|
||||||
"path": "notes/example.md",
|
"path": "notes/example.md",
|
||||||
"version": 11,
|
"version": 11,
|
||||||
"size": 1024,
|
"size": 1024,
|
||||||
"hash": "abc123..."
|
"hash": "abc123..."
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -414,10 +414,10 @@ VALUES (?, ?, ?);
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"type": "file_content",
|
"type": "file_content",
|
||||||
"path": "notes/example.md",
|
"path": "notes/example.md",
|
||||||
"content": "Updated content...",
|
"content": "Updated content...",
|
||||||
"version": 11
|
"version": 11
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -425,9 +425,9 @@ VALUES (?, ?, ?);
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"type": "file_deleted",
|
"type": "file_deleted",
|
||||||
"path": "notes/old.md",
|
"path": "notes/old.md",
|
||||||
"version": 12
|
"version": 12
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -435,9 +435,9 @@ VALUES (?, ?, ?);
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"type": "sync_complete",
|
"type": "sync_complete",
|
||||||
"total_files": 150,
|
"total_files": 150,
|
||||||
"current_version": 200
|
"current_version": 200
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -445,9 +445,9 @@ VALUES (?, ?, ?);
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"type": "error",
|
"type": "error",
|
||||||
"message": "File too large",
|
"message": "File too large",
|
||||||
"code": "FILE_TOO_LARGE"
|
"code": "FILE_TOO_LARGE"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -53,7 +53,7 @@ Central authority for synchronisation. Rust + Axum framework.
|
||||||
|
|
||||||
**Technology**:
|
**Technology**:
|
||||||
|
|
||||||
- **Language**: Rust 1.89+
|
- **Language**: Rust 1.92+
|
||||||
- **Framework**: Axum (async web framework)
|
- **Framework**: Axum (async web framework)
|
||||||
- **Database**: SQLite with SQLx
|
- **Database**: SQLite with SQLx
|
||||||
- **Protocol**: WebSockets for real-time communication
|
- **Protocol**: WebSockets for real-time communication
|
||||||
|
|
|
||||||
|
|
@ -243,9 +243,9 @@ users:
|
||||||
2. Client sends authentication message:
|
2. Client sends authentication message:
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"type": "auth",
|
"type": "auth",
|
||||||
"token": "user-token",
|
"token": "user-token",
|
||||||
"vault": "vault-name"
|
"vault": "vault-name"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
3. Server validates:
|
3. Server validates:
|
||||||
|
|
|
||||||
|
|
@ -75,7 +75,7 @@ chmod +x sync_server-linux-x86_64
|
||||||
|
|
||||||
### Build from Source
|
### Build from Source
|
||||||
|
|
||||||
Requirements: Rust 1.89.0+, SQLite development headers, SQLx CLI
|
Requirements: Rust 1.92.0+, SQLite development headers, SQLx CLI
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Clone the repository
|
# Clone the repository
|
||||||
|
|
|
||||||
5960
docs/package-lock.json
generated
5960
docs/package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
|
@ -1,4 +1,4 @@
|
||||||
FROM node:22-slim AS builder
|
FROM node:25-slim AS builder
|
||||||
|
|
||||||
WORKDIR /build
|
WORKDIR /build
|
||||||
|
|
||||||
|
|
@ -7,7 +7,7 @@ COPY . .
|
||||||
RUN npm ci
|
RUN npm ci
|
||||||
RUN npm run build
|
RUN npm run build
|
||||||
|
|
||||||
FROM node:22-alpine
|
FROM node:25-alpine
|
||||||
|
|
||||||
LABEL org.opencontainers.image.title="VaultLink Local CLI"
|
LABEL org.opencontainers.image.title="VaultLink Local CLI"
|
||||||
LABEL org.opencontainers.image.description="Standalone CLI for VaultLink sync client"
|
LABEL org.opencontainers.image.description="Standalone CLI for VaultLink sync client"
|
||||||
|
|
|
||||||
|
|
@ -47,24 +47,24 @@ vaultlink \
|
||||||
|
|
||||||
### Required
|
### Required
|
||||||
|
|
||||||
| Option | Description |
|
| Option | Description |
|
||||||
|--------|-------------|
|
| ------------------------- | --------------------------------------------- |
|
||||||
| `-l, --local-path <path>` | Local directory to sync |
|
| `-l, --local-path <path>` | Local directory to sync |
|
||||||
| `-r, --remote-uri <uri>` | Remote server WebSocket URI (ws:// or wss://) |
|
| `-r, --remote-uri <uri>` | Remote server WebSocket URI (ws:// or wss://) |
|
||||||
| `-t, --token <token>` | Authentication token |
|
| `-t, --token <token>` | Authentication token |
|
||||||
| `-v, --vault-name <name>` | Vault name on server |
|
| `-v, --vault-name <name>` | Vault name on server |
|
||||||
|
|
||||||
### Optional
|
### Optional
|
||||||
|
|
||||||
| Option | Default | Description |
|
| Option | Default | Description |
|
||||||
|--------|---------|-------------|
|
| ------------------------------------ | ------- | -------------------------------------- |
|
||||||
| `--sync-concurrency <number>` | `1` | Concurrent sync operations |
|
| `--sync-concurrency <number>` | `1` | Concurrent sync operations |
|
||||||
| `--max-file-size-mb <number>` | `10` | Maximum file size in MB |
|
| `--max-file-size-mb <number>` | `10` | Maximum file size in MB |
|
||||||
| `--ignore-pattern <pattern>` | - | Glob pattern to ignore (repeatable) |
|
| `--ignore-pattern <pattern>` | - | Glob pattern to ignore (repeatable) |
|
||||||
| `--websocket-retry-interval-ms <ms>` | `3500` | WebSocket reconnection interval |
|
| `--websocket-retry-interval-ms <ms>` | `3500` | WebSocket reconnection interval |
|
||||||
| `--log-level <level>` | `INFO` | Log level: DEBUG, INFO, WARNING, ERROR |
|
| `--log-level <level>` | `INFO` | Log level: DEBUG, INFO, WARNING, ERROR |
|
||||||
| `-h, --help` | - | Show help |
|
| `-h, --help` | - | Show help |
|
||||||
| `-V, --version` | - | Show version |
|
| `-V, --version` | - | Show version |
|
||||||
|
|
||||||
### Auto-Ignored Patterns
|
### Auto-Ignored Patterns
|
||||||
|
|
||||||
|
|
@ -74,11 +74,13 @@ vaultlink \
|
||||||
### Examples
|
### Examples
|
||||||
|
|
||||||
Basic usage:
|
Basic usage:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
vaultlink -l ./vault -r wss://sync.example.com -t token123 -v default
|
vaultlink -l ./vault -r wss://sync.example.com -t token123 -v default
|
||||||
```
|
```
|
||||||
|
|
||||||
With ignore patterns:
|
With ignore patterns:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
vaultlink -l ./vault -r wss://sync.example.com -t token123 -v default \
|
vaultlink -l ./vault -r wss://sync.example.com -t token123 -v default \
|
||||||
--ignore-pattern "*.tmp" \
|
--ignore-pattern "*.tmp" \
|
||||||
|
|
@ -87,6 +89,7 @@ vaultlink -l ./vault -r wss://sync.example.com -t token123 -v default \
|
||||||
```
|
```
|
||||||
|
|
||||||
With debug logging:
|
With debug logging:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
vaultlink -l ./vault -r wss://sync.example.com -t token123 -v default \
|
vaultlink -l ./vault -r wss://sync.example.com -t token123 -v default \
|
||||||
--log-level DEBUG
|
--log-level DEBUG
|
||||||
|
|
@ -176,6 +179,7 @@ services:
|
||||||
## Development
|
## Development
|
||||||
|
|
||||||
Build:
|
Build:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
npm run build
|
npm run build
|
||||||
# or from the parent folder, run
|
# or from the parent folder, run
|
||||||
|
|
@ -183,11 +187,13 @@ docker build -f local-client-cli/Dockerfile .
|
||||||
```
|
```
|
||||||
|
|
||||||
Test:
|
Test:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
npm test
|
npm test
|
||||||
```
|
```
|
||||||
|
|
||||||
Docker build:
|
Docker build:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd frontend
|
cd frontend
|
||||||
docker build -f local-client-cli/Dockerfile -t vault-link-cli:test .
|
docker build -f local-client-cli/Dockerfile -t vault-link-cli:test .
|
||||||
|
|
|
||||||
|
|
@ -11,18 +11,16 @@
|
||||||
"build": "webpack --mode production",
|
"build": "webpack --mode production",
|
||||||
"test": "tsx --test 'src/**/*.test.ts'"
|
"test": "tsx --test 'src/**/*.test.ts'"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
|
||||||
"commander": "^14.0.2",
|
|
||||||
"watcher": "^2.3.1"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^24.8.1",
|
"commander": "^14.0.2",
|
||||||
|
"watcher": "^2.3.1",
|
||||||
|
"@types/node": "^25.0.2",
|
||||||
"sync-client": "file:../sync-client",
|
"sync-client": "file:../sync-client",
|
||||||
"ts-loader": "^9.5.2",
|
"ts-loader": "^9.5.4",
|
||||||
"tslib": "2.8.1",
|
"tslib": "2.8.1",
|
||||||
"tsx": "^4.20.6",
|
"tsx": "^4.21.0",
|
||||||
"typescript": "5.8.3",
|
"typescript": "5.9.3",
|
||||||
"webpack": "^5.99.9",
|
"webpack": "^5.103.0",
|
||||||
"webpack-cli": "^6.0.1"
|
"webpack-cli": "^6.0.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -106,8 +106,8 @@ export class FileWatcher {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert a native platform path to forward slashes
|
* Convert a native platform path to forward slashes
|
||||||
*/
|
*/
|
||||||
private toUnixPath(nativePath: string): string {
|
private toUnixPath(nativePath: string): string {
|
||||||
if (path.sep === "\\") {
|
if (path.sep === "\\") {
|
||||||
return nativePath.replace(/\\/g, "/");
|
return nativePath.replace(/\\/g, "/");
|
||||||
|
|
|
||||||
|
|
@ -185,8 +185,8 @@ export class NodeFileSystemOperations implements FileSystemOperations {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert a forward-slash path to native platform path separators
|
* Convert a forward-slash path to native platform path separators
|
||||||
*/
|
*/
|
||||||
private toNativePath(relativePath: string): string {
|
private toNativePath(relativePath: string): string {
|
||||||
if (path.sep === "\\") {
|
if (path.sep === "\\") {
|
||||||
return relativePath.replace(/\//g, "\\");
|
return relativePath.replace(/\//g, "\\");
|
||||||
|
|
@ -195,8 +195,8 @@ export class NodeFileSystemOperations implements FileSystemOperations {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert a native platform path to forward slashes
|
* Convert a native platform path to forward slashes
|
||||||
*/
|
*/
|
||||||
private toUnixPath(nativePath: string): string {
|
private toUnixPath(nativePath: string): string {
|
||||||
if (path.sep === "\\") {
|
if (path.sep === "\\") {
|
||||||
return nativePath.replace(/\\/g, "/");
|
return nativePath.replace(/\\/g, "/");
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,5 @@
|
||||||
"declarationMap": true,
|
"declarationMap": true,
|
||||||
"sourceMap": true
|
"sourceMap": true
|
||||||
},
|
},
|
||||||
"exclude": [
|
"exclude": ["dist"]
|
||||||
"dist"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue