Merge main for current release and TypeScript compatibility

This commit is contained in:
vorotamoroz
2026-09-08 17:26:23 +00:00
27 changed files with 451 additions and 584 deletions
+4 -4
View File
@@ -84,10 +84,10 @@ jobs:
task: ${{ fromJson(needs.prepare.outputs.task_matrix) }} task: ${{ fromJson(needs.prepare.outputs.task_matrix) }}
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v5
- name: Setup Node.js - name: Setup Node.js
uses: actions/setup-node@v4 uses: actions/setup-node@v5
with: with:
node-version: '24.x' node-version: '24.x'
cache: 'npm' cache: 'npm'
@@ -99,7 +99,7 @@ jobs:
deno-version: v2.x deno-version: v2.x
- name: Cache Deno dependencies - name: Cache Deno dependencies
uses: actions/cache@v4 uses: actions/cache@v5
with: with:
path: ~/.cache/deno path: ~/.cache/deno
key: ${{ runner.os }}-deno-${{ hashFiles('src/apps/cli/testdeno/deno.lock', 'src/apps/cli/testdeno/deno.json') }} key: ${{ runner.os }}-deno-${{ hashFiles('src/apps/cli/testdeno/deno.lock', 'src/apps/cli/testdeno/deno.json') }}
@@ -151,7 +151,7 @@ jobs:
timeout-minutes: 45 timeout-minutes: 45
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v5
- name: Show Docker versions - name: Show Docker versions
run: | run: |
+6 -6
View File
@@ -46,7 +46,7 @@ jobs:
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v5
- name: Derive image tag - name: Derive image tag
id: meta id: meta
@@ -86,22 +86,22 @@ jobs:
echo "push=${PUSH}" >> $GITHUB_OUTPUT echo "push=${PUSH}" >> $GITHUB_OUTPUT
- name: Log in to GitHub Container Registry - name: Log in to GitHub Container Registry
uses: docker/login-action@v3 uses: docker/login-action@v4
with: with:
registry: ghcr.io registry: ghcr.io
username: ${{ github.actor }} username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }} password: ${{ secrets.GITHUB_TOKEN }}
- name: Set up QEMU - name: Set up QEMU
uses: docker/setup-qemu-action@v3 uses: docker/setup-qemu-action@v4
with: with:
platforms: arm64 platforms: arm64
- name: Set up Docker Buildx - name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3 uses: docker/setup-buildx-action@v4
- name: Setup Node.js - name: Setup Node.js
uses: actions/setup-node@v4 uses: actions/setup-node@v5
with: with:
node-version: "24.x" node-version: "24.x"
cache: "npm" cache: "npm"
@@ -128,7 +128,7 @@ jobs:
- name: Build and push - name: Build and push
if: ${{ steps.e2e.outcome == 'success' || (github.event_name == 'workflow_dispatch' && inputs.force) }} if: ${{ steps.e2e.outcome == 'success' || (github.event_name == 'workflow_dispatch' && inputs.force) }}
uses: docker/build-push-action@v6 uses: docker/build-push-action@v7
with: with:
context: . context: .
file: src/apps/cli/Dockerfile file: src/apps/cli/Dockerfile
+2 -2
View File
@@ -22,10 +22,10 @@ jobs:
timeout-minutes: 45 timeout-minutes: 45
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v5
- name: Setup Node.js - name: Setup Node.js
uses: actions/setup-node@v4 uses: actions/setup-node@v5
with: with:
node-version: '24.x' node-version: '24.x'
cache: 'npm' cache: 'npm'
+2 -2
View File
@@ -38,7 +38,7 @@ jobs:
timeout-minutes: 45 timeout-minutes: 45
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v5
- name: Show Docker versions - name: Show Docker versions
run: | run: |
@@ -83,7 +83,7 @@ jobs:
- name: Upload benchmark results - name: Upload benchmark results
if: always() if: always()
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v6
with: with:
name: cli-p2p-compose-smoke-results name: cli-p2p-compose-smoke-results
path: test/bench-network/bench-results/** path: test/bench-network/bench-results/**
+5 -5
View File
@@ -36,10 +36,10 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v5
- name: Setup Node.js - name: Setup Node.js
uses: actions/setup-node@v4 uses: actions/setup-node@v5
with: with:
node-version: '24.x' node-version: '24.x'
cache: 'npm' cache: 'npm'
@@ -85,10 +85,10 @@ jobs:
run: npm run test:browser-apps:pages run: npm run test:browser-apps:pages
- name: Configure GitHub Pages - name: Configure GitHub Pages
uses: actions/configure-pages@v5 uses: actions/configure-pages@v6
- name: Upload GitHub Pages artifact - name: Upload GitHub Pages artifact
uses: actions/upload-pages-artifact@v3 uses: actions/upload-pages-artifact@v5
with: with:
path: _site path: _site
@@ -103,4 +103,4 @@ jobs:
steps: steps:
- name: Deploy GitHub Pages - name: Deploy GitHub Pages
id: deployment id: deployment
uses: actions/deploy-pages@v4 uses: actions/deploy-pages@v5
+2 -2
View File
@@ -47,13 +47,13 @@ jobs:
fi fi
echo "name=${BRANCH}" >> "$GITHUB_OUTPUT" echo "name=${BRANCH}" >> "$GITHUB_OUTPUT"
- uses: actions/checkout@v4 - uses: actions/checkout@v5
with: with:
ref: ${{ steps.branch.outputs.name }} ref: ${{ steps.branch.outputs.name }}
fetch-depth: 0 fetch-depth: 0
- name: Use Node.js - name: Use Node.js
uses: actions/setup-node@v4 uses: actions/setup-node@v5
with: with:
node-version: "24.x" node-version: "24.x"
+2 -2
View File
@@ -33,13 +33,13 @@ jobs:
contents: write contents: write
pull-requests: write pull-requests: write
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v5
with: with:
ref: ${{ inputs.base_branch }} ref: ${{ inputs.base_branch }}
fetch-depth: 0 fetch-depth: 0
- name: Use Node.js - name: Use Node.js
uses: actions/setup-node@v4 uses: actions/setup-node@v5
with: with:
node-version: "24.x" node-version: "24.x"
cache: npm cache: npm
+3 -3
View File
@@ -26,12 +26,12 @@ jobs:
id-token: write id-token: write
attestations: write attestations: write
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v5
with: with:
fetch-depth: 0 fetch-depth: 0
ref: ${{ inputs.tag }} ref: ${{ inputs.tag }}
- name: Use Node.js - name: Use Node.js
uses: actions/setup-node@v4 uses: actions/setup-node@v5
with: with:
node-version: '24.x' node-version: '24.x'
- name: Validate release - name: Validate release
@@ -61,7 +61,7 @@ jobs:
manifest.json manifest.json
styles.css styles.css
- name: Create Release and Upload Assets - name: Create Release and Upload Assets
uses: softprops/action-gh-release@v2 uses: softprops/action-gh-release@v3
with: with:
files: | files: |
main.js main.js
+10 -7
View File
@@ -65,10 +65,10 @@ jobs:
timeout-minutes: 15 timeout-minutes: 15
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v5
- name: Setup Node.js - name: Setup Node.js
uses: actions/setup-node@v4 uses: actions/setup-node@v5
with: with:
node-version: '24.x' node-version: '24.x'
cache: 'npm' cache: 'npm'
@@ -113,10 +113,10 @@ jobs:
timeout-minutes: 30 timeout-minutes: 30
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v5
- name: Setup Node.js - name: Setup Node.js
uses: actions/setup-node@v4 uses: actions/setup-node@v5
with: with:
node-version: '24.x' node-version: '24.x'
cache: 'npm' cache: 'npm'
@@ -127,6 +127,9 @@ jobs:
- name: Run source checks - name: Run source checks
run: npm run check run: npm run check
- name: Run release process tests
run: npm run test:release-process
- name: Run unit tests suite with coverage - name: Run unit tests suite with coverage
run: npm run test:unit:coverage run: npm run test:unit:coverage
@@ -135,7 +138,7 @@ jobs:
- name: Upload coverage report - name: Upload coverage report
if: always() if: always()
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v6
with: with:
name: unit-coverage-report name: unit-coverage-report
path: coverage/** path: coverage/**
@@ -146,7 +149,7 @@ jobs:
timeout-minutes: 30 timeout-minutes: 30
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v5
- name: Detect LiveSync-owned integration tests - name: Detect LiveSync-owned integration tests
id: integration_tests id: integration_tests
@@ -165,7 +168,7 @@ jobs:
- name: Setup Node.js - name: Setup Node.js
if: ${{ steps.integration_tests.outputs.present == 'true' }} if: ${{ steps.integration_tests.outputs.present == 'true' }}
uses: actions/setup-node@v4 uses: actions/setup-node@v5
with: with:
node-version: '24.x' node-version: '24.x'
cache: 'npm' cache: 'npm'
+2 -2
View File
@@ -1,14 +1,14 @@
--- ---
date: 2026-09-08 date: 2026-09-08
commonlib-version: "0.1.24" commonlib-version: "0.1.24"
self-hosted-livesync-version: "1.0.26" self-hosted-livesync-version: "1.0.27"
status: unreleased status: unreleased
--- ---
# Tweak compatibility and recovery # Tweak compatibility and recovery
This document describes the integration of Commonlib 0.1.24 with This document describes the integration of Commonlib 0.1.24 with
Self-hosted LiveSync 1.0.26. Commonlib owns the interpretation of synchronisation Self-hosted LiveSync 1.0.27. Commonlib owns the interpretation of synchronisation
settings; LiveSync owns the dialogues and operations which consume that result. settings; LiveSync owns the dialogues and operations which consume that result.
## Shared assessment ## Shared assessment
+82
View File
@@ -2,6 +2,88 @@
This document contains earlier published releases from the 1.0 line of the [current Self-hosted LiveSync release history](../../updates.md). Beta and release-candidate builds published before 1.0.0 are recorded in the [1.0 preview history](1.0-previews.md). Earlier release lines continue in the [0.25 history](0.25.md) and the [legacy history](legacy.md). This document contains earlier published releases from the 1.0 line of the [current Self-hosted LiveSync release history](../../updates.md). Beta and release-candidate builds published before 1.0.0 are recorded in the [1.0 preview history](1.0-previews.md). Earlier release lines continue in the [0.25 history](0.25.md) and the [legacy history](legacy.md).
## 1.0.21
26th August, 2026
It is becoming more 'ordinary' with each release, but please let me know if anything has become less convenient.
### Interface and translation
#### Fixed
- Remote Configuration section headings no longer overlap their contents when scrolling on mobile. Action buttons in Remote Configuration, Maintenance, and Patches now remain inside the settings pane on narrow screens.
## 1.0.20
~~1.0.19~~ was cancelled because prerelease validation exposed an incorrect warning at start-up.
25th August, 2026
I know this is the second time I have said it, but I had grown quite fond of the settings screen. It seems, however, that a simpler, healthier life is called for.
### Interface and translation
#### Fixed
- Compatibility pause warnings now direct you to the dedicated compatibility review instead of the Change Log.
- The Obsidian 1.13 settings page now waits for saved settings before choosing its initial layout. This prevents a spurious missing-replicator warning at start-up, keeps configured devices on the Synchronisation-first layout even when automatic synchronisation triggers are disabled, and keeps Quick Setup first on unconfigured devices.
#### Improved
- Settings page names, controls in General Settings, Quick Setup actions, and Advanced controls now use Obsidian 1.13's native settings interface and global search, while retaining their familiar icons. The landing page keeps Remote Configuration and Sync Settings together, places Appearance, Logging, and Extra menus under General Settings, and groups maintenance, optional features, advanced settings, and help by purpose. Earlier supported Obsidian versions continue to use the pane-based interface.
- Settings changes which require database initialisation now use a focused Setup Manager dialogue to choose between existing synchronisation data and the files in the current Vault. The selected reset or rebuild is reserved before the settings are saved, while cancelling offers a separate, explicit settings-only fallback.
## 1.0.18
24th August, 2026
### Synchronisation and storage
#### Fixed
- Reset and rebuild workflows now use the local database selected by their updated settings, preventing stale data from reopening after a **Database Suffix** change. If database initialisation does not complete, the workflow remains paused instead of continuing with incomplete state.
#### Improved
- Rebuilds now recheck restored file events against the current Vault, use current file contents, and finish processing them before the plug-in reports readiness.
## 1.0.17
23rd August, 2026
### Interface and translation
#### Fixed
- Settings generated from the settings manifest, Setup Wizard configuration summaries, and warnings about externally changed settings now honour **Display language** when a translation is available, instead of remaining in English (PR #1123). Thank you to @nimula for the contribution!
### Peer-to-peer synchronisation
#### Improved
- P2P connection profiles now provide four **P2P message size** presets and a **Connection path** choice between **Automatic** and **TURN relay only**. Smaller messages can improve compatibility on paths which fragment or drop larger WebRTC messages, while relay-only routing requires a configured TURN server. P2P connection strings and encrypted Setup URIs preserve both choices.
- Thank you to @andrewschreiber for the detailed fragmentation diagnosis and working 800-byte threshold in vrtmrz/livesync-commonlib#97, which informed this compatibility design.
- An optional self-hosted Coturn Compose starter is now available for P2P deployments that need a TURN relay. It uses a pinned upstream image and documents its network, credential, security, and verification boundaries.
## 1.0.16
19th August, 2026
### Conflict handling and recovery
#### Fixed
- **Back to this revision** in Document History now restores the selected content as a new non-deleted successor revision before reflecting it to the Vault. A readable revision restored after a logical deletion therefore remains restored through later synchronisation instead of being overwritten by the deletion.
- If the file changes while restoration is in progress, the operation stops instead of extending a stale revision. Existing conflicts remain available through **Inspect conflicts and file/database differences**.
### Synchronisation and storage
#### Improved
- One-shot CouchDB synchronisation now releases stalled web-compatible connection checks before replication starts, so a later synchronisation can make a fresh attempt (Commonlib 0.1.16).
- The 60-second safeguard applies only to pre-replication checks. It does not limit ordinary synchronisation, and the **Use Internal API** path is unchanged.
## 1.0.15 ## 1.0.15
15th August, 2026 15th August, 2026
+1 -1
View File
@@ -1,7 +1,7 @@
{ {
"id": "obsidian-livesync", "id": "obsidian-livesync",
"name": "Self-hosted LiveSync", "name": "Self-hosted LiveSync",
"version": "1.0.26", "version": "1.0.27",
"minAppVersion": "1.7.2", "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.", "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", "author": "vorotamoroz",
+163 -435
View File
@@ -1,12 +1,12 @@
{ {
"name": "obsidian-livesync", "name": "obsidian-livesync",
"version": "1.0.26", "version": "1.0.27",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "obsidian-livesync", "name": "obsidian-livesync",
"version": "1.0.26", "version": "1.0.27",
"license": "MIT", "license": "MIT",
"workspaces": [ "workspaces": [
"src/apps/cli", "src/apps/cli",
@@ -27,7 +27,6 @@
"@vrtmrz/obsidian-plugin-kit": "0.1.4", "@vrtmrz/obsidian-plugin-kit": "0.1.4",
"@vrtmrz/ui-interactions": "0.1.2", "@vrtmrz/ui-interactions": "0.1.2",
"diff-match-patch": "^1.0.5", "diff-match-patch": "^1.0.5",
"fflate": "^0.8.2",
"idb": "^8.0.3", "idb": "^8.0.3",
"markdown-it": "^14.2.0", "markdown-it": "^14.2.0",
"minimatch": "^10.2.5", "minimatch": "^10.2.5",
@@ -56,7 +55,7 @@
"@types/pouchdb-mapreduce": "^6.1.10", "@types/pouchdb-mapreduce": "^6.1.10",
"@types/pouchdb-replication": "^6.4.7", "@types/pouchdb-replication": "^6.4.7",
"@types/transform-pouch": "^1.0.6", "@types/transform-pouch": "^1.0.6",
"@typescript-eslint/parser": "8.56.1", "@typescript-eslint/parser": "8.69.0",
"@vitest/coverage-v8": "^4.1.8", "@vitest/coverage-v8": "^4.1.8",
"@vrtmrz/obsidian-test-session": "0.2.6", "@vrtmrz/obsidian-test-session": "0.2.6",
"dotenv-cli": "^11.0.0", "dotenv-cli": "^11.0.0",
@@ -86,13 +85,13 @@
"svelte": "5.56.3", "svelte": "5.56.3",
"svelte-check": "^4.6.0", "svelte-check": "^4.6.0",
"svelte-eslint-parser": "^1.8.0", "svelte-eslint-parser": "^1.8.0",
"svelte-preprocess": "^6.0.3", "svelte-preprocess": "6.0.5",
"terser": "^5.39.0", "terser": "^5.39.0",
"tinyglobby": "^0.2.15", "tinyglobby": "^0.2.15",
"transform-pouch": "^2.0.0", "transform-pouch": "^2.0.0",
"tsx": "^4.21.0", "tsx": "^4.21.0",
"typescript": "5.9.3", "typescript": "6.0.3",
"typescript-eslint": "^8.61.0", "typescript-eslint": "8.69.0",
"vite": "^8.0.16", "vite": "^8.0.16",
"vitest": "^4.1.8", "vitest": "^4.1.8",
"yaml": "^2.8.2" "yaml": "^2.8.2"
@@ -2054,29 +2053,43 @@
} }
}, },
"node_modules/@humanfs/core": { "node_modules/@humanfs/core": {
"version": "0.19.1", "version": "0.19.2",
"resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.2.tgz",
"integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", "integrity": "sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==",
"dev": true, "dev": true,
"license": "Apache-2.0", "license": "Apache-2.0",
"dependencies": {
"@humanfs/types": "^0.15.0"
},
"engines": { "engines": {
"node": ">=18.18.0" "node": ">=18.18.0"
} }
}, },
"node_modules/@humanfs/node": { "node_modules/@humanfs/node": {
"version": "0.16.7", "version": "0.16.8",
"resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz", "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.8.tgz",
"integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==", "integrity": "sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==",
"dev": true, "dev": true,
"license": "Apache-2.0", "license": "Apache-2.0",
"dependencies": { "dependencies": {
"@humanfs/core": "^0.19.1", "@humanfs/core": "^0.19.2",
"@humanfs/types": "^0.15.0",
"@humanwhocodes/retry": "^0.4.0" "@humanwhocodes/retry": "^0.4.0"
}, },
"engines": { "engines": {
"node": ">=18.18.0" "node": ">=18.18.0"
} }
}, },
"node_modules/@humanfs/types": {
"version": "0.15.0",
"resolved": "https://registry.npmjs.org/@humanfs/types/-/types-0.15.0.tgz",
"integrity": "sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==",
"dev": true,
"license": "Apache-2.0",
"engines": {
"node": ">=18.18.0"
}
},
"node_modules/@humanwhocodes/module-importer": { "node_modules/@humanwhocodes/module-importer": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz",
@@ -4157,17 +4170,56 @@
"devOptional": true, "devOptional": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/@typescript-eslint/parser": { "node_modules/@typescript-eslint/eslint-plugin": {
"version": "8.56.1", "version": "8.69.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.56.1.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.69.0.tgz",
"integrity": "sha512-klQbnPAAiGYFyI02+znpBRLyjL4/BrBd0nyWkdC0s/6xFLkXYQ8OoRrSkqacS1ddVxf/LDyODIKbQ5TgKAf/Fg==", "integrity": "sha512-t5jQTKPIgVW1PE6dR6H6Qz5gm8zjMlX5/2gRaOGd9eO6V7J+tQc6iWKukEe7dY8u9HyYasQ0yfF0/FSSTEO2gA==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@typescript-eslint/scope-manager": "8.56.1", "@eslint-community/regexpp": "^4.12.2",
"@typescript-eslint/types": "8.56.1", "@typescript-eslint/scope-manager": "8.69.0",
"@typescript-eslint/typescript-estree": "8.56.1", "@typescript-eslint/type-utils": "8.69.0",
"@typescript-eslint/visitor-keys": "8.56.1", "@typescript-eslint/utils": "8.69.0",
"@typescript-eslint/visitor-keys": "8.69.0",
"ignore": "^7.0.5",
"natural-compare": "^1.4.0",
"ts-api-utils": "^2.5.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
"@typescript-eslint/parser": "^8.69.0",
"eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
"typescript": ">=4.8.4 <6.1.0"
}
},
"node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": {
"version": "7.0.8",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.8.tgz",
"integrity": "sha512-YYNsSlXBjMk92SKnkwvB5LOVSa6OznlFUGcsvrFgNJbJCd0M1XKeFVRc8ZByeCqz32FivYNHJVooLmdqrmvp/Q==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 4"
}
},
"node_modules/@typescript-eslint/parser": {
"version": "8.69.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.69.0.tgz",
"integrity": "sha512-l4b0DhWioGg6Gt2ebGlvfkFMOjRsauxtsnDRwUSRX1qHq3HdTfQHV8wW9zEXeciai6HfeaKOedQn2Zoofx3WBw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/scope-manager": "8.69.0",
"@typescript-eslint/types": "8.69.0",
"@typescript-eslint/typescript-estree": "8.69.0",
"@typescript-eslint/visitor-keys": "8.69.0",
"debug": "^4.4.3" "debug": "^4.4.3"
}, },
"engines": { "engines": {
@@ -4179,18 +4231,18 @@
}, },
"peerDependencies": { "peerDependencies": {
"eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
"typescript": ">=4.8.4 <6.0.0" "typescript": ">=4.8.4 <6.1.0"
} }
}, },
"node_modules/@typescript-eslint/project-service": { "node_modules/@typescript-eslint/project-service": {
"version": "8.56.1", "version": "8.69.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.56.1.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.69.0.tgz",
"integrity": "sha512-TAdqQTzHNNvlVFfR+hu2PDJrURiwKsUvxFn1M0h95BB8ah5jejas08jUWG4dBA68jDMI988IvtfdAI53JzEHOQ==", "integrity": "sha512-yi4obFrHMmnsesWehHbkg9zMA7Jt8cXT+mKM08G999pH1yT6nqgsHx7MYm0uY1wAj8CqiBXYRJ7WAT0QdQHQXg==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@typescript-eslint/tsconfig-utils": "^8.56.1", "@typescript-eslint/tsconfig-utils": "^8.69.0",
"@typescript-eslint/types": "^8.56.1", "@typescript-eslint/types": "^8.69.0",
"debug": "^4.4.3" "debug": "^4.4.3"
}, },
"engines": { "engines": {
@@ -4201,18 +4253,18 @@
"url": "https://opencollective.com/typescript-eslint" "url": "https://opencollective.com/typescript-eslint"
}, },
"peerDependencies": { "peerDependencies": {
"typescript": ">=4.8.4 <6.0.0" "typescript": ">=4.8.4 <6.1.0"
} }
}, },
"node_modules/@typescript-eslint/scope-manager": { "node_modules/@typescript-eslint/scope-manager": {
"version": "8.56.1", "version": "8.69.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.56.1.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.69.0.tgz",
"integrity": "sha512-YAi4VDKcIZp0O4tz/haYKhmIDZFEUPOreKbfdAN3SzUDMcPhJ8QI99xQXqX+HoUVq8cs85eRKnD+rne2UAnj2w==", "integrity": "sha512-ewfspqWvSxKSOaplqAUNbaSFO0eB6w1EtQ+esfYFRm3614Ty4uNtExkcbgd6nWsXphbqKyf9ZYdbZdv2xEoWEQ==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@typescript-eslint/types": "8.56.1", "@typescript-eslint/types": "8.69.0",
"@typescript-eslint/visitor-keys": "8.56.1" "@typescript-eslint/visitor-keys": "8.69.0"
}, },
"engines": { "engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0" "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -4223,9 +4275,9 @@
} }
}, },
"node_modules/@typescript-eslint/tsconfig-utils": { "node_modules/@typescript-eslint/tsconfig-utils": {
"version": "8.56.1", "version": "8.69.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.56.1.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.69.0.tgz",
"integrity": "sha512-qOtCYzKEeyr3aR9f28mPJqBty7+DBqsdd63eO0yyDwc6vgThj2UjWfJIcsFeSucYydqcuudMOprZ+x1SpF3ZuQ==", "integrity": "sha512-xNqK7YTDZsLniQMV/4rpFR8Z5JlqeRvVjuG1YgF/mdPVH84HSD19L8CczMA0qg2RfwEV231GHH3VnToJDo4MfQ==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"engines": { "engines": {
@@ -4236,13 +4288,38 @@
"url": "https://opencollective.com/typescript-eslint" "url": "https://opencollective.com/typescript-eslint"
}, },
"peerDependencies": { "peerDependencies": {
"typescript": ">=4.8.4 <6.0.0" "typescript": ">=4.8.4 <6.1.0"
}
},
"node_modules/@typescript-eslint/type-utils": {
"version": "8.69.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.69.0.tgz",
"integrity": "sha512-ZfoJAVg3JZndQEpEl9petVlxau3lRuElc4HRMuAlLCf8to04/iHz692RUSNmXKDjEuJmIL+KZ2/BsOcBc16dsA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/types": "8.69.0",
"@typescript-eslint/typescript-estree": "8.69.0",
"@typescript-eslint/utils": "8.69.0",
"debug": "^4.4.3",
"ts-api-utils": "^2.5.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
"eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
"typescript": ">=4.8.4 <6.1.0"
} }
}, },
"node_modules/@typescript-eslint/types": { "node_modules/@typescript-eslint/types": {
"version": "8.56.1", "version": "8.69.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.56.1.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.69.0.tgz",
"integrity": "sha512-dbMkdIUkIkchgGDIv7KLUpa0Mda4IYjo4IAMJUZ+3xNoUXxMsk9YtKpTHSChRS85o+H9ftm51gsK1dZReY9CVw==", "integrity": "sha512-K3VrubUPhlo9VDBS6QdI8YB5j7ClpqLRdefcz6PFrhnwicehBweqQ9Evhl4l+FYz0HdDmMqIiSX0aldGRYtDCA==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"engines": { "engines": {
@@ -4254,139 +4331,16 @@
} }
}, },
"node_modules/@typescript-eslint/typescript-estree": { "node_modules/@typescript-eslint/typescript-estree": {
"version": "8.56.1", "version": "8.69.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.56.1.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.69.0.tgz",
"integrity": "sha512-qzUL1qgalIvKWAf9C1HpvBjif+Vm6rcT5wZd4VoMb9+Km3iS3Cv9DY6dMRMDtPnwRAFyAi7YXJpTIEXLvdfPxg==", "integrity": "sha512-AdFkgqck3Vudb/kWnxlyafU/4aBhHrbQ9locP2N4psXTy5mOBg0SHJumnLvx7r6g1gV4DKvUFwV2nJZBoqOD8w==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@typescript-eslint/project-service": "8.56.1", "@typescript-eslint/project-service": "8.69.0",
"@typescript-eslint/tsconfig-utils": "8.56.1", "@typescript-eslint/tsconfig-utils": "8.69.0",
"@typescript-eslint/types": "8.56.1", "@typescript-eslint/types": "8.69.0",
"@typescript-eslint/visitor-keys": "8.56.1", "@typescript-eslint/visitor-keys": "8.69.0",
"debug": "^4.4.3",
"minimatch": "^10.2.2",
"semver": "^7.7.3",
"tinyglobby": "^0.2.15",
"ts-api-utils": "^2.4.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
"typescript": ">=4.8.4 <6.0.0"
}
},
"node_modules/@typescript-eslint/utils": {
"version": "8.61.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.61.1.tgz",
"integrity": "sha512-1+P/3Dj6jvtybE1q0HQ6yBt/gq+oKJyLdEv4HdnqasaEXRSYCAsD59mXEVQnM/ULNdQxbX77tdG4jPRjIS6knA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@eslint-community/eslint-utils": "^4.9.1",
"@typescript-eslint/scope-manager": "8.61.1",
"@typescript-eslint/types": "8.61.1",
"@typescript-eslint/typescript-estree": "8.61.1"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
"eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
"typescript": ">=4.8.4 <6.1.0"
}
},
"node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/project-service": {
"version": "8.61.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.61.1.tgz",
"integrity": "sha512-PrC4JYGmR241lYnfhmKGTXkFqv8+ymbTFgSAY0fVXpY82/QkMw5TZPl+vGzuDDU2QYJk9fIDOBTntF+yDv9LEA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/tsconfig-utils": "^8.61.1",
"@typescript-eslint/types": "^8.61.1",
"debug": "^4.4.3"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
"typescript": ">=4.8.4 <6.1.0"
}
},
"node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/scope-manager": {
"version": "8.61.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.61.1.tgz",
"integrity": "sha512-L2bdIeoQS8FlKAvONAr20w6OcLXeB+qiDKbAooS9A0Ben+iSIkBef0FxqwKWYqt5sa0i4KJtxVyVmhMylKzF5w==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/types": "8.61.1",
"@typescript-eslint/visitor-keys": "8.61.1"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
}
},
"node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/tsconfig-utils": {
"version": "8.61.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.61.1.tgz",
"integrity": "sha512-UN/H4di+OO7EWx2ovME+8t31YO+KVnK0RRKEHR3kOt21/Ay8BOq3M1OMvWs5vNiqcFCYGYoxK3MXPZzmMUE+yg==",
"dev": true,
"license": "MIT",
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
"typescript": ">=4.8.4 <6.1.0"
}
},
"node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/types": {
"version": "8.61.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.61.1.tgz",
"integrity": "sha512-G+CRlPqLv7Bz1IZVs03x5K59F1veqL0EJUROAdGhKsEq8qOiRiZbI+HUojPq5l0fEGOKModD9br6lObhB8zkoA==",
"dev": true,
"license": "MIT",
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
}
},
"node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/typescript-estree": {
"version": "8.61.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.61.1.tgz",
"integrity": "sha512-u+oQD3BqYWPc8YV9Zab4vaJElJuwOLPRc10Jm1o/qS+6Qwen14HCWwx0Seo4LnSn2wxea2Ik8DxPt2/FHmuhrg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/project-service": "8.61.1",
"@typescript-eslint/tsconfig-utils": "8.61.1",
"@typescript-eslint/types": "8.61.1",
"@typescript-eslint/visitor-keys": "8.61.1",
"debug": "^4.4.3", "debug": "^4.4.3",
"minimatch": "^10.2.2", "minimatch": "^10.2.2",
"semver": "^7.7.3", "semver": "^7.7.3",
@@ -4404,15 +4358,17 @@
"typescript": ">=4.8.4 <6.1.0" "typescript": ">=4.8.4 <6.1.0"
} }
}, },
"node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/visitor-keys": { "node_modules/@typescript-eslint/utils": {
"version": "8.61.1", "version": "8.69.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.61.1.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.69.0.tgz",
"integrity": "sha512-6fJ9MHWtK14C1DSkiMlHUSOmrVebL7150xZJBlJiL62jjhIA4JmOq6flwBgDxIdBKKdoiZRel+dfPD5MLfny3w==", "integrity": "sha512-tUbx60BBqQa31kXF5MCsOOLL5E/WzUuxIn7YpAvq+eaUlqvk8/NXnXMBNAdLCr0icjkzem7iUA5QqWHe/hJ1aw==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@typescript-eslint/types": "8.61.1", "@eslint-community/eslint-utils": "^4.9.1",
"eslint-visitor-keys": "^5.0.0" "@typescript-eslint/scope-manager": "8.69.0",
"@typescript-eslint/types": "8.69.0",
"@typescript-eslint/typescript-estree": "8.69.0"
}, },
"engines": { "engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0" "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -4420,29 +4376,20 @@
"funding": { "funding": {
"type": "opencollective", "type": "opencollective",
"url": "https://opencollective.com/typescript-eslint" "url": "https://opencollective.com/typescript-eslint"
}
},
"node_modules/@typescript-eslint/utils/node_modules/eslint-visitor-keys": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz",
"integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==",
"dev": true,
"license": "Apache-2.0",
"engines": {
"node": "^20.19.0 || ^22.13.0 || >=24"
}, },
"funding": { "peerDependencies": {
"url": "https://opencollective.com/eslint" "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
"typescript": ">=4.8.4 <6.1.0"
} }
}, },
"node_modules/@typescript-eslint/visitor-keys": { "node_modules/@typescript-eslint/visitor-keys": {
"version": "8.56.1", "version": "8.69.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.56.1.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.69.0.tgz",
"integrity": "sha512-KiROIzYdEV85YygXw6BI/Dx4fnBlFQu6Mq4QE4MOH9fFnhohw6wX/OAvDY2/C+ut0I3RSPKenvZJIVYqJNkhEw==", "integrity": "sha512-+rmdgPA+EXkNgKYvHvFfhrs35utXbwaC5PGpDquSXcoXQDKUA5UjV0LmTucG/4JXkM31BTu4TilHtrN8IVBe8w==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@typescript-eslint/types": "8.56.1", "@typescript-eslint/types": "8.69.0",
"eslint-visitor-keys": "^5.0.0" "eslint-visitor-keys": "^5.0.0"
}, },
"engines": { "engines": {
@@ -7214,9 +7161,9 @@
} }
}, },
"node_modules/fflate": { "node_modules/fflate": {
"version": "0.8.2", "version": "0.8.3",
"resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz", "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.3.tgz",
"integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==", "integrity": "sha512-tbZNuJrLwGUp3zshBtdy4W+ORxZuIh8a5ilyIEQDC5rY1f3U20JMry0Ll3WBzU58EZKsEuJFXhb5gwv8CsPvgA==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/file-entry-cache": { "node_modules/file-entry-cache": {
@@ -11521,9 +11468,9 @@
} }
}, },
"node_modules/svelte-preprocess": { "node_modules/svelte-preprocess": {
"version": "6.0.3", "version": "6.0.5",
"resolved": "https://registry.npmjs.org/svelte-preprocess/-/svelte-preprocess-6.0.3.tgz", "resolved": "https://registry.npmjs.org/svelte-preprocess/-/svelte-preprocess-6.0.5.tgz",
"integrity": "sha512-PLG2k05qHdhmRG7zR/dyo5qKvakhm8IJ+hD2eFRQmMLHp7X3eJnjeupUtvuRpbNiF31RjVw45W+abDwHEmP5OA==", "integrity": "sha512-sgwew5yV/2eMeQobIWgAxCNarKwiTUDIc3siAUbq3sp0G6ONtzk0W+wJihMdqjbYb3iGU3ubpGv0usnnuXT3qg==",
"dev": true, "dev": true,
"hasInstallScript": true, "hasInstallScript": true,
"license": "MIT", "license": "MIT",
@@ -11541,7 +11488,7 @@
"stylus": ">=0.55", "stylus": ">=0.55",
"sugarss": "^2.0.0 || ^3.0.0 || ^4.0.0", "sugarss": "^2.0.0 || ^3.0.0 || ^4.0.0",
"svelte": "^4.0.0 || ^5.0.0-next.100 || ^5.0.0", "svelte": "^4.0.0 || ^5.0.0-next.100 || ^5.0.0",
"typescript": "^5.0.0" "typescript": "^5.0.0 || ^6.0.0"
}, },
"peerDependenciesMeta": { "peerDependenciesMeta": {
"@babel/core": { "@babel/core": {
@@ -11993,9 +11940,9 @@
} }
}, },
"node_modules/typescript": { "node_modules/typescript": {
"version": "5.9.3", "version": "6.0.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz",
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==",
"dev": true, "dev": true,
"license": "Apache-2.0", "license": "Apache-2.0",
"bin": { "bin": {
@@ -12007,16 +11954,16 @@
} }
}, },
"node_modules/typescript-eslint": { "node_modules/typescript-eslint": {
"version": "8.61.1", "version": "8.69.0",
"resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.61.1.tgz", "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.69.0.tgz",
"integrity": "sha512-V7PayAfJokV3pEHgN7/v03D1SpujhRfQtYLbLIiBfDDncdg4PAiRBfoS4cnCANK4jmAPncczi59QO3afiXUlNw==", "integrity": "sha512-B3MltX0VqjUBNEe3b3sSuiRbfa6XrfHFtBiPamjT5AsW/dfq+y+bc0wyuS9DxAS1LyzCxRp2+rxzpLUvqM2BvA==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@typescript-eslint/eslint-plugin": "8.61.1", "@typescript-eslint/eslint-plugin": "8.69.0",
"@typescript-eslint/parser": "8.61.1", "@typescript-eslint/parser": "8.69.0",
"@typescript-eslint/typescript-estree": "8.61.1", "@typescript-eslint/typescript-estree": "8.69.0",
"@typescript-eslint/utils": "8.61.1" "@typescript-eslint/utils": "8.69.0"
}, },
"engines": { "engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0" "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -12030,225 +11977,6 @@
"typescript": ">=4.8.4 <6.1.0" "typescript": ">=4.8.4 <6.1.0"
} }
}, },
"node_modules/typescript-eslint/node_modules/@typescript-eslint/eslint-plugin": {
"version": "8.61.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.61.1.tgz",
"integrity": "sha512-ZPlVl3PB3et/59Ne0fv/sci6ZXz4T4Hp4nTJ56i/Y0gR89ARb+KphojTq6j+56E5PIezmOIOOWyY+aWQFd+IkQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@eslint-community/regexpp": "^4.12.2",
"@typescript-eslint/scope-manager": "8.61.1",
"@typescript-eslint/type-utils": "8.61.1",
"@typescript-eslint/utils": "8.61.1",
"@typescript-eslint/visitor-keys": "8.61.1",
"ignore": "^7.0.5",
"natural-compare": "^1.4.0",
"ts-api-utils": "^2.5.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
"@typescript-eslint/parser": "^8.61.1",
"eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
"typescript": ">=4.8.4 <6.1.0"
}
},
"node_modules/typescript-eslint/node_modules/@typescript-eslint/parser": {
"version": "8.61.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.61.1.tgz",
"integrity": "sha512-PJ5vePq5/ognBbrIcoC5+SHO5dfpeLPzP9FpLkzWrguoYQEeeSjlJpVwOpo1JRSTEi7dRcwNy4h4dzV70PqHcg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/scope-manager": "8.61.1",
"@typescript-eslint/types": "8.61.1",
"@typescript-eslint/typescript-estree": "8.61.1",
"@typescript-eslint/visitor-keys": "8.61.1",
"debug": "^4.4.3"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
"eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
"typescript": ">=4.8.4 <6.1.0"
}
},
"node_modules/typescript-eslint/node_modules/@typescript-eslint/project-service": {
"version": "8.61.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.61.1.tgz",
"integrity": "sha512-PrC4JYGmR241lYnfhmKGTXkFqv8+ymbTFgSAY0fVXpY82/QkMw5TZPl+vGzuDDU2QYJk9fIDOBTntF+yDv9LEA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/tsconfig-utils": "^8.61.1",
"@typescript-eslint/types": "^8.61.1",
"debug": "^4.4.3"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
"typescript": ">=4.8.4 <6.1.0"
}
},
"node_modules/typescript-eslint/node_modules/@typescript-eslint/scope-manager": {
"version": "8.61.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.61.1.tgz",
"integrity": "sha512-L2bdIeoQS8FlKAvONAr20w6OcLXeB+qiDKbAooS9A0Ben+iSIkBef0FxqwKWYqt5sa0i4KJtxVyVmhMylKzF5w==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/types": "8.61.1",
"@typescript-eslint/visitor-keys": "8.61.1"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
}
},
"node_modules/typescript-eslint/node_modules/@typescript-eslint/tsconfig-utils": {
"version": "8.61.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.61.1.tgz",
"integrity": "sha512-UN/H4di+OO7EWx2ovME+8t31YO+KVnK0RRKEHR3kOt21/Ay8BOq3M1OMvWs5vNiqcFCYGYoxK3MXPZzmMUE+yg==",
"dev": true,
"license": "MIT",
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
"typescript": ">=4.8.4 <6.1.0"
}
},
"node_modules/typescript-eslint/node_modules/@typescript-eslint/type-utils": {
"version": "8.61.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.61.1.tgz",
"integrity": "sha512-GYRicKmVK0C4fsKgaACaknOUAq9Oa2kwsjnpFhFcS/5p4Ht5IP9OVLbgIgcK4SRk92nVHFluurg1lumD9dBcLw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/types": "8.61.1",
"@typescript-eslint/typescript-estree": "8.61.1",
"@typescript-eslint/utils": "8.61.1",
"debug": "^4.4.3",
"ts-api-utils": "^2.5.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
"eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
"typescript": ">=4.8.4 <6.1.0"
}
},
"node_modules/typescript-eslint/node_modules/@typescript-eslint/types": {
"version": "8.61.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.61.1.tgz",
"integrity": "sha512-G+CRlPqLv7Bz1IZVs03x5K59F1veqL0EJUROAdGhKsEq8qOiRiZbI+HUojPq5l0fEGOKModD9br6lObhB8zkoA==",
"dev": true,
"license": "MIT",
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
}
},
"node_modules/typescript-eslint/node_modules/@typescript-eslint/typescript-estree": {
"version": "8.61.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.61.1.tgz",
"integrity": "sha512-u+oQD3BqYWPc8YV9Zab4vaJElJuwOLPRc10Jm1o/qS+6Qwen14HCWwx0Seo4LnSn2wxea2Ik8DxPt2/FHmuhrg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/project-service": "8.61.1",
"@typescript-eslint/tsconfig-utils": "8.61.1",
"@typescript-eslint/types": "8.61.1",
"@typescript-eslint/visitor-keys": "8.61.1",
"debug": "^4.4.3",
"minimatch": "^10.2.2",
"semver": "^7.7.3",
"tinyglobby": "^0.2.15",
"ts-api-utils": "^2.5.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
"typescript": ">=4.8.4 <6.1.0"
}
},
"node_modules/typescript-eslint/node_modules/@typescript-eslint/visitor-keys": {
"version": "8.61.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.61.1.tgz",
"integrity": "sha512-6fJ9MHWtK14C1DSkiMlHUSOmrVebL7150xZJBlJiL62jjhIA4JmOq6flwBgDxIdBKKdoiZRel+dfPD5MLfny3w==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/types": "8.61.1",
"eslint-visitor-keys": "^5.0.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
}
},
"node_modules/typescript-eslint/node_modules/eslint-visitor-keys": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz",
"integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==",
"dev": true,
"license": "Apache-2.0",
"engines": {
"node": "^20.19.0 || ^22.13.0 || >=24"
},
"funding": {
"url": "https://opencollective.com/eslint"
}
},
"node_modules/typescript-eslint/node_modules/ignore": {
"version": "7.0.5",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz",
"integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 4"
}
},
"node_modules/uc.micro": { "node_modules/uc.micro": {
"version": "2.1.0", "version": "2.1.0",
"resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz", "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz",
@@ -12937,7 +12665,7 @@
}, },
"src/apps/cli": { "src/apps/cli": {
"name": "self-hosted-livesync-cli", "name": "self-hosted-livesync-cli",
"version": "1.0.26-cli", "version": "1.0.27-cli",
"dependencies": { "dependencies": {
"chokidar": "^4.0.0", "chokidar": "^4.0.0",
"minimatch": "^10.2.5", "minimatch": "^10.2.5",
@@ -12955,26 +12683,26 @@
"werift": "^0.24.4" "werift": "^0.24.4"
}, },
"devDependencies": { "devDependencies": {
"typescript": "5.9.3", "typescript": "6.0.3",
"vite": "^8.0.16", "vite": "^8.0.16",
"vitest": "^4.1.8" "vitest": "^4.1.8"
} }
}, },
"src/apps/webapp": { "src/apps/webapp": {
"name": "livesync-webapp", "name": "livesync-webapp",
"version": "1.0.26-webapp", "version": "1.0.27-webapp",
"dependencies": { "dependencies": {
"octagonal-wheels": "^0.1.54" "octagonal-wheels": "^0.1.54"
}, },
"devDependencies": { "devDependencies": {
"@sveltejs/vite-plugin-svelte": "^7.1.2", "@sveltejs/vite-plugin-svelte": "^7.1.2",
"svelte": "5.56.3", "svelte": "5.56.3",
"typescript": "5.9.3", "typescript": "6.0.3",
"vite": "^8.0.16" "vite": "^8.0.16"
} }
}, },
"src/apps/webpeer": { "src/apps/webpeer": {
"version": "1.0.26-webpeer", "version": "1.0.27-webpeer",
"dependencies": { "dependencies": {
"octagonal-wheels": "^0.1.54" "octagonal-wheels": "^0.1.54"
}, },
@@ -12984,7 +12712,7 @@
"eslint-plugin-svelte": "^3.19.0", "eslint-plugin-svelte": "^3.19.0",
"svelte": "5.56.3", "svelte": "5.56.3",
"svelte-check": "^4.6.0", "svelte-check": "^4.6.0",
"typescript": "5.9.3", "typescript": "6.0.3",
"vite": "^8.0.16" "vite": "^8.0.16"
} }
} }
+15 -8
View File
@@ -1,6 +1,6 @@
{ {
"name": "obsidian-livesync", "name": "obsidian-livesync",
"version": "1.0.26", "version": "1.0.27",
"description": "Reflect your vault changes to some other devices immediately. Please make sure to disable other synchronize solutions to avoid content corruption or duplication.", "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", "main": "main.js",
"type": "module", "type": "module",
@@ -30,6 +30,7 @@
"i18n:json2yaml": "tsx _tools/json2yaml.ts", "i18n:json2yaml": "tsx _tools/json2yaml.ts",
"i18n:yaml2json": "tsx _tools/yaml2json.ts", "i18n:yaml2json": "tsx _tools/yaml2json.ts",
"test:unit": "vitest run --config vitest.config.unit.ts", "test:unit": "vitest run --config vitest.config.unit.ts",
"test:release-process": "vitest run --config vitest.config.unit.ts utils/release-process.unit.spec.ts",
"build:browser-apps": "npm run build --workspace livesync-webapp --workspace webpeer", "build:browser-apps": "npm run build --workspace livesync-webapp --workspace webpeer",
"test:browser-apps": "npm run test:browser --workspace livesync-webapp && npm run test:browser --workspace webpeer", "test:browser-apps": "npm run test:browser --workspace livesync-webapp && npm run test:browser --workspace webpeer",
"test:browser-apps:pages": "deno test -A --no-check --frozen --config test/browser-apps/deno.json --lock test/browser-apps/deno.lock test/browser-apps/pages/browser-smoke.test.ts", "test:browser-apps:pages": "deno test -A --no-check --frozen --config test/browser-apps/deno.json --lock test/browser-apps/deno.lock test/browser-apps/pages/browser-smoke.test.ts",
@@ -46,7 +47,7 @@
"test:e2e:cli:p2p": "npm run test:e2e:p2p --workspace self-hosted-livesync-cli", "test:e2e:cli:p2p": "npm run test:e2e:p2p --workspace self-hosted-livesync-cli",
"test:e2e:cli:all": "npm run test:e2e:all --workspace self-hosted-livesync-cli", "test:e2e:cli:all": "npm run test:e2e:all --workspace self-hosted-livesync-cli",
"test:integration": "npx dotenv-cli -e .env -e .test.env -- vitest run --config vitest.config.integration.ts", "test:integration": "npx dotenv-cli -e .env -e .test.env -- vitest run --config vitest.config.integration.ts",
"test:unit:coverage": "vitest run --config vitest.config.unit.ts --coverage", "test:unit:coverage": "vitest run --config vitest.config.unit.ts --coverage --exclude utils/release-process.unit.spec.ts",
"test:e2e:obsidian:install-appimage": "tsx test/e2e-obsidian/scripts/install-appimage.ts", "test:e2e:obsidian:install-appimage": "tsx test/e2e-obsidian/scripts/install-appimage.ts",
"test:e2e:obsidian:runner": "vitest run --config vitest.config.e2e-runner.ts", "test:e2e:obsidian:runner": "vitest run --config vitest.config.e2e-runner.ts",
"test:e2e:obsidian:discover": "tsx test/e2e-obsidian/scripts/discover.ts", "test:e2e:obsidian:discover": "tsx test/e2e-obsidian/scripts/discover.ts",
@@ -70,6 +71,7 @@
"test:e2e:obsidian:cli-to-obsidian-sync": "tsx test/e2e-obsidian/scripts/cli-to-obsidian-sync.ts", "test:e2e:obsidian:cli-to-obsidian-sync": "tsx test/e2e-obsidian/scripts/cli-to-obsidian-sync.ts",
"test:e2e:obsidian:minio-upload": "tsx test/e2e-obsidian/scripts/minio-upload.ts", "test:e2e:obsidian:minio-upload": "tsx test/e2e-obsidian/scripts/minio-upload.ts",
"test:e2e:obsidian:object-storage-setup-uri-workflow": "tsx test/e2e-obsidian/scripts/object-storage-setup-uri-workflow.ts", "test:e2e:obsidian:object-storage-setup-uri-workflow": "tsx test/e2e-obsidian/scripts/object-storage-setup-uri-workflow.ts",
"test:e2e:obsidian:object-storage-custom-http-handler-setup-uri-workflow": "tsx test/e2e-obsidian/scripts/object-storage-setup-uri-workflow.ts --custom-http-handler",
"test:e2e:obsidian:p2p-setup-uri-workflow": "tsx test/e2e-obsidian/scripts/p2p-setup-uri-workflow.ts", "test:e2e:obsidian:p2p-setup-uri-workflow": "tsx test/e2e-obsidian/scripts/p2p-setup-uri-workflow.ts",
"pretest:e2e:obsidian:p2p-connection-check": "npm run build && npm run build --workspace webpeer", "pretest:e2e:obsidian:p2p-connection-check": "npm run build && npm run build --workspace webpeer",
"test:e2e:obsidian:p2p-connection-check": "tsx test/e2e-obsidian/scripts/p2p-connection-check.ts", "test:e2e:obsidian:p2p-connection-check": "tsx test/e2e-obsidian/scripts/p2p-connection-check.ts",
@@ -128,7 +130,7 @@
"@types/pouchdb-mapreduce": "^6.1.10", "@types/pouchdb-mapreduce": "^6.1.10",
"@types/pouchdb-replication": "^6.4.7", "@types/pouchdb-replication": "^6.4.7",
"@types/transform-pouch": "^1.0.6", "@types/transform-pouch": "^1.0.6",
"@typescript-eslint/parser": "8.56.1", "@typescript-eslint/parser": "8.69.0",
"@vitest/coverage-v8": "^4.1.8", "@vitest/coverage-v8": "^4.1.8",
"@vrtmrz/obsidian-test-session": "0.2.6", "@vrtmrz/obsidian-test-session": "0.2.6",
"dotenv-cli": "^11.0.0", "dotenv-cli": "^11.0.0",
@@ -158,13 +160,13 @@
"svelte": "5.56.3", "svelte": "5.56.3",
"svelte-check": "^4.6.0", "svelte-check": "^4.6.0",
"svelte-eslint-parser": "^1.8.0", "svelte-eslint-parser": "^1.8.0",
"svelte-preprocess": "^6.0.3", "svelte-preprocess": "6.0.5",
"terser": "^5.39.0", "terser": "^5.39.0",
"tinyglobby": "^0.2.15", "tinyglobby": "^0.2.15",
"transform-pouch": "^2.0.0", "transform-pouch": "^2.0.0",
"tsx": "^4.21.0", "tsx": "^4.21.0",
"typescript": "5.9.3", "typescript": "6.0.3",
"typescript-eslint": "^8.61.0", "typescript-eslint": "8.69.0",
"vite": "^8.0.16", "vite": "^8.0.16",
"vitest": "^4.1.8", "vitest": "^4.1.8",
"yaml": "^2.8.2" "yaml": "^2.8.2"
@@ -183,7 +185,6 @@
"@vrtmrz/obsidian-plugin-kit": "0.1.4", "@vrtmrz/obsidian-plugin-kit": "0.1.4",
"@vrtmrz/ui-interactions": "0.1.2", "@vrtmrz/ui-interactions": "0.1.2",
"diff-match-patch": "^1.0.5", "diff-match-patch": "^1.0.5",
"fflate": "^0.8.2",
"idb": "^8.0.3", "idb": "^8.0.3",
"markdown-it": "^14.2.0", "markdown-it": "^14.2.0",
"minimatch": "^10.2.5", "minimatch": "^10.2.5",
@@ -204,5 +205,11 @@
"src/apps/cli", "src/apps/cli",
"src/apps/webpeer", "src/apps/webpeer",
"src/apps/webapp" "src/apps/webapp"
] ],
"allowScripts": {
"esbuild@0.28.1": true,
"leveldown@5.6.0": true,
"leveldown@6.1.1": true,
"svelte-preprocess": false
}
} }
+2 -2
View File
@@ -1,7 +1,7 @@
{ {
"name": "self-hosted-livesync-cli", "name": "self-hosted-livesync-cli",
"private": true, "private": true,
"version": "1.0.26-cli", "version": "1.0.27-cli",
"main": "dist/index.cjs", "main": "dist/index.cjs",
"type": "module", "type": "module",
"scripts": { "scripts": {
@@ -51,7 +51,7 @@
"werift": "^0.24.4" "werift": "^0.24.4"
}, },
"devDependencies": { "devDependencies": {
"typescript": "5.9.3", "typescript": "6.0.3",
"vite": "^8.0.16", "vite": "^8.0.16",
"vitest": "^4.1.8" "vitest": "^4.1.8"
} }
+2 -2
View File
@@ -1,7 +1,7 @@
{ {
"name": "livesync-webapp", "name": "livesync-webapp",
"private": true, "private": true,
"version": "1.0.26-webapp", "version": "1.0.27-webapp",
"type": "module", "type": "module",
"description": "Browser-based Self-hosted LiveSync using FileSystem API", "description": "Browser-based Self-hosted LiveSync using FileSystem API",
"scripts": { "scripts": {
@@ -20,7 +20,7 @@
"devDependencies": { "devDependencies": {
"@sveltejs/vite-plugin-svelte": "^7.1.2", "@sveltejs/vite-plugin-svelte": "^7.1.2",
"svelte": "5.56.3", "svelte": "5.56.3",
"typescript": "5.9.3", "typescript": "6.0.3",
"vite": "^8.0.16" "vite": "^8.0.16"
} }
} }
+2 -2
View File
@@ -1,7 +1,7 @@
{ {
"name": "webpeer", "name": "webpeer",
"private": true, "private": true,
"version": "1.0.26-webpeer", "version": "1.0.27-webpeer",
"type": "module", "type": "module",
"scripts": { "scripts": {
"dev": "vite", "dev": "vite",
@@ -23,7 +23,7 @@
"@tsconfig/svelte": "^5.0.8", "@tsconfig/svelte": "^5.0.8",
"svelte": "5.56.3", "svelte": "5.56.3",
"svelte-check": "^4.6.0", "svelte-check": "^4.6.0",
"typescript": "5.9.3", "typescript": "6.0.3",
"vite": "^8.0.16" "vite": "^8.0.16"
} }
} }
@@ -1,7 +1,7 @@
// This file is based on a file that was published by the @remotely-save, under the Apache 2 License. // This file was originally based on code published by @remotely-save under the Apache License 2.0.
// I would love to express my deepest gratitude to the original authors for their hard work and dedication. Without their contributions, this project would not have been possible. // I would like to express my gratitude to the original authors for their work.
// //
// Original Implementation is here: https://github.com/remotely-save/remotely-save/blob/28b99557a864ef59c19d2ad96101196e401718f0/src/remoteForS3.ts // Original implementation: https://github.com/remotely-save/remotely-save/blob/28b99557a864ef59c19d2ad96101196e401718f0/src/remoteForS3.ts
import { FetchHttpHandler, type FetchHttpHandlerOptions } from "@smithy/fetch-http-handler"; import { FetchHttpHandler, type FetchHttpHandlerOptions } from "@smithy/fetch-http-handler";
import { HttpRequest, HttpResponse } from "@smithy/protocol-http"; import { HttpRequest, HttpResponse } from "@smithy/protocol-http";
@@ -102,6 +102,7 @@ export class ObsHttpHandler extends FetchHttpHandler {
method: method, method: method,
url: url, url: url,
contentType: contentType, contentType: contentType,
throw: false,
}; };
const raceOfPromises = [ const raceOfPromises = [
@@ -1,9 +1,10 @@
import { GetObjectCommand, S3Client } from "@aws-sdk/client-s3";
import { HttpRequest } from "@smithy/protocol-http"; import { HttpRequest } from "@smithy/protocol-http";
import { beforeEach, describe, expect, it, vi } from "vitest"; import { beforeEach, describe, expect, it, vi } from "vitest";
const requestUrlMock = vi.hoisted(() => const requestUrlMock = vi.hoisted(() =>
vi.fn< vi.fn<
(param: { body?: string | ArrayBuffer }) => Promise<{ (param: { body?: string | ArrayBuffer; throw?: boolean }) => Promise<{
headers: Record<string, string>; headers: Record<string, string>;
status: number; status: number;
arrayBuffer: ArrayBuffer; arrayBuffer: ArrayBuffer;
@@ -28,6 +29,42 @@ function requestWithBody(body: unknown) {
}); });
} }
function mockS3ErrorResponse(status: number, code?: string) {
requestUrlMock.mockImplementation(async (param) => {
if (param.throw !== false) {
throw new Error(`Request failed, status ${status}`);
}
return {
headers: { "content-type": "application/xml" },
status,
arrayBuffer: new TextEncoder().encode(code ? `<Error><Code>${code}</Code></Error>` : "").buffer,
};
});
}
function createS3Client() {
return new S3Client({
region: "us-east-1",
credentials: {
accessKeyId: "access-key",
secretAccessKey: "secret-key",
},
endpoint: "https://objects.example.com",
forcePathStyle: true,
maxAttempts: 1,
requestHandler: new ObsHttpHandler(),
});
}
function getMissingObject(client: S3Client) {
return client.send(
new GetObjectCommand({
Bucket: "bucket",
Key: "missing.json",
})
);
}
describe("ObsHttpHandler request bodies", () => { describe("ObsHttpHandler request bodies", () => {
beforeEach(() => { beforeEach(() => {
requestUrlMock.mockReset(); requestUrlMock.mockReset();
@@ -58,3 +95,56 @@ describe("ObsHttpHandler request bodies", () => {
expect(requestUrlMock).not.toHaveBeenCalled(); expect(requestUrlMock).not.toHaveBeenCalled();
}); });
}); });
describe("ObsHttpHandler response handling", () => {
beforeEach(() => {
requestUrlMock.mockReset();
});
it("returns an HTTP error response to the Smithy client", async () => {
mockS3ErrorResponse(404, "NoSuchKey");
const request = new HttpRequest({
protocol: "https:",
hostname: "objects.example.com",
method: "GET",
path: "/bucket/missing.json",
headers: {},
});
const result = await new ObsHttpHandler().handle(request);
expect(requestUrlMock).toHaveBeenCalledWith(expect.objectContaining({ throw: false }));
expect(result.response.statusCode).toBe(404);
});
it.each([
{ code: "NoSuchKey", name: "NoSuchKey" },
{ code: undefined, name: "NotFound" },
])("lets the S3 client classify a missing object as $name", async ({ code, name }) => {
mockS3ErrorResponse(404, code);
await expect(getMissingObject(createS3Client())).rejects.toMatchObject({
name,
$metadata: { httpStatusCode: 404 },
});
});
it.each([
{ status: 403, code: "AccessDenied" },
{ status: 500, code: "InternalError" },
])("keeps an S3 $status response distinct from a missing object", async ({ status, code }) => {
mockS3ErrorResponse(status, code);
await expect(getMissingObject(createS3Client())).rejects.toMatchObject({
name: code,
$metadata: { httpStatusCode: status },
});
});
it("preserves a transport failure", async () => {
const failure = new Error("network failed");
requestUrlMock.mockRejectedValue(failure);
await expect(new ObsHttpHandler().handle(requestWithBody(new ArrayBuffer(0)))).rejects.toBe(failure);
});
});
+2
View File
@@ -19,6 +19,7 @@ export type SetupState = {
endpoint: string; endpoint: string;
bucket: string; bucket: string;
bucketPrefix: string; bucketPrefix: string;
useCustomRequestHandler: boolean;
p2pEnabled: boolean; p2pEnabled: boolean;
p2pRelays: string; p2pRelays: string;
p2pRoomId: string; p2pRoomId: string;
@@ -354,6 +355,7 @@ export async function readSetupState(cliBinary: string, environment: NodeJS.Proc
"endpoint:settings.endpoint||'',", "endpoint:settings.endpoint||'',",
"bucket:settings.bucket||'',", "bucket:settings.bucket||'',",
"bucketPrefix:settings.bucketPrefix||'',", "bucketPrefix:settings.bucketPrefix||'',",
"useCustomRequestHandler:settings.useCustomRequestHandler===true,",
"p2pEnabled:settings.P2P_Enabled===true,", "p2pEnabled:settings.P2P_Enabled===true,",
"p2pRelays:settings.P2P_relays||'',", "p2pRelays:settings.P2P_relays||'',",
"p2pRoomId:settings.P2P_roomID||'',", "p2pRoomId:settings.P2P_roomID||'',",
+4
View File
@@ -35,6 +35,10 @@ const testSteps: Step[] = [
name: "Object Storage Setup URI workflow", name: "Object Storage Setup URI workflow",
args: ["run", "test:e2e:obsidian:object-storage-setup-uri-workflow"], args: ["run", "test:e2e:obsidian:object-storage-setup-uri-workflow"],
}, },
{
name: "Object Storage Custom HTTP Handler Setup URI workflow",
args: ["run", "test:e2e:obsidian:object-storage-custom-http-handler-setup-uri-workflow"],
},
{ name: "P2P Setup URI workflow", args: ["run", "test:e2e:obsidian:p2p-setup-uri-workflow"] }, { name: "P2P Setup URI workflow", args: ["run", "test:e2e:obsidian:p2p-setup-uri-workflow"] },
{ name: "startup scan", args: ["run", "test:e2e:obsidian:startup-scan"] }, { name: "startup scan", args: ["run", "test:e2e:obsidian:startup-scan"] },
{ name: "provisioned Setup URI workflow", args: ["run", "test:e2e:obsidian:setup-uri-workflow"] }, { name: "provisioned Setup URI workflow", args: ["run", "test:e2e:obsidian:setup-uri-workflow"] },
@@ -45,7 +45,10 @@ import { createTemporaryVault, type TemporaryVault } from "../runner/vault.ts";
process.env.E2E_OBSIDIAN_CLI_TIMEOUT_MS ??= "90000"; process.env.E2E_OBSIDIAN_CLI_TIMEOUT_MS ??= "90000";
const execFileAsync = promisify(execFile); const execFileAsync = promisify(execFile);
const captures: SetupCaptureNames = { scenario: "object-storage-setup-uri", guide: "object-storage-setup" }; const useCustomRequestHandler = process.argv.includes("--custom-http-handler");
const captures: SetupCaptureNames = useCustomRequestHandler
? { scenario: "object-storage-custom-http-handler-setup-uri", guide: "object-storage-custom-http-handler-setup" }
: { scenario: "object-storage-setup-uri", guide: "object-storage-setup" };
const noteFromFirst = "E2E/object-storage/from-first.md"; const noteFromFirst = "E2E/object-storage/from-first.md";
const noteFromSecond = "E2E/object-storage/from-second.md"; const noteFromSecond = "E2E/object-storage/from-second.md";
const firstContent = const firstContent =
@@ -94,7 +97,8 @@ async function runDeno(script: string, environment: NodeJS.ProcessEnv): Promise<
async function generateBootstrapSetupURI( async function generateBootstrapSetupURI(
objectStorage: ObjectStorageConfig, objectStorage: ObjectStorageConfig,
bucketPrefix: string bucketPrefix: string,
useCustomRequestHandler: boolean
): Promise<SetupArtifact> { ): Promise<SetupArtifact> {
const setupPassphrase = randomBytes(24).toString("base64url"); const setupPassphrase = randomBytes(24).toString("base64url");
const output = await runDeno("utils/setup/generate_setup_uri.ts", { const output = await runDeno("utils/setup/generate_setup_uri.ts", {
@@ -107,6 +111,7 @@ async function generateBootstrapSetupURI(
region: objectStorage.region, region: objectStorage.region,
force_path_style: String(objectStorage.forcePathStyle), force_path_style: String(objectStorage.forcePathStyle),
bucket_prefix: bucketPrefix, bucket_prefix: bucketPrefix,
...(useCustomRequestHandler ? { use_custom_request_handler: "true" } : {}),
passphrase: randomBytes(24).toString("base64url"), passphrase: randomBytes(24).toString("base64url"),
uri_passphrase: setupPassphrase, uri_passphrase: setupPassphrase,
}); });
@@ -251,7 +256,7 @@ async function main(): Promise<void> {
const objectStorage = await loadObjectStorageConfig(); const objectStorage = await loadObjectStorageConfig();
const bucketPrefix = makeUniqueBucketPrefix("setup-uri-workflow"); const bucketPrefix = makeUniqueBucketPrefix("setup-uri-workflow");
const bootstrapArtifact = await generateBootstrapSetupURI(objectStorage, bucketPrefix); const bootstrapArtifact = await generateBootstrapSetupURI(objectStorage, bucketPrefix, useCustomRequestHandler);
const vaultA = await createTemporaryVault(); const vaultA = await createTemporaryVault();
const vaultB = await createTemporaryVault(); const vaultB = await createTemporaryVault();
const [portA, portB] = sessionPorts(); const [portA, portB] = sessionPorts();
@@ -285,6 +290,11 @@ async function main(): Promise<void> {
bucketPrefix, bucketPrefix,
"The first device did not activate the unique bucket prefix." "The first device did not activate the unique bucket prefix."
); );
assertEqual(
firstState.useCustomRequestHandler,
useCustomRequestHandler,
"The first device did not preserve the expected Custom HTTP Handler setting."
);
await writeNote(context.cliBinary, sessionA.cliEnv, noteFromFirst, firstContent); await writeNote(context.cliBinary, sessionA.cliEnv, noteFromFirst, firstContent);
await pushLocalChanges(context.cliBinary, sessionA.cliEnv); await pushLocalChanges(context.cliBinary, sessionA.cliEnv);
@@ -330,6 +340,11 @@ async function main(): Promise<void> {
bucketPrefix, bucketPrefix,
"The second device did not import the unique bucket prefix." "The second device did not import the unique bucket prefix."
); );
assertEqual(
secondState.useCustomRequestHandler,
useCustomRequestHandler,
"The second device did not import the expected Custom HTTP Handler setting."
);
await pushLocalChanges(context.cliBinary, sessionB.cliEnv); await pushLocalChanges(context.cliBinary, sessionB.cliEnv);
await waitForPathContent(vaultB, noteFromFirst, firstContent); await waitForPathContent(vaultB, noteFromFirst, firstContent);
screenshots.push( screenshots.push(
@@ -337,7 +352,7 @@ async function main(): Promise<void> {
portB, portB,
noteFromFirst, noteFromFirst,
"Object Storage from the first device", "Object Storage from the first device",
"guide-object-storage-setup-first-to-second.png" `guide-${captures.guide}-first-to-second.png`
) )
); );
@@ -357,12 +372,14 @@ async function main(): Promise<void> {
portA, portA,
noteFromSecond, noteFromSecond,
"Object Storage from the second device", "Object Storage from the second device",
"guide-object-storage-setup-second-to-first.png" `guide-${captures.guide}-second-to-first.png`
) )
); );
console.log( console.log(
`Object Storage Setup URI and two-device roundtrip succeeded. Screenshots: ${screenshots.join(", ")}` `Object Storage Setup URI and two-device roundtrip succeeded with the ${
useCustomRequestHandler ? "Custom HTTP Handler" : "default HTTP handler"
}. Screenshots: ${screenshots.join(", ")}`
); );
} finally { } finally {
await stopSessions(context).catch((error: unknown) => { await stopSessions(context).catch((error: unknown) => {
+1
View File
@@ -21,6 +21,7 @@ const focusedScenarios = new Set([
"cli-to-obsidian-sync", "cli-to-obsidian-sync",
"minio-upload", "minio-upload",
"object-storage-setup-uri-workflow", "object-storage-setup-uri-workflow",
"object-storage-custom-http-handler-setup-uri-workflow",
"p2p-setup-uri-workflow", "p2p-setup-uri-workflow",
"partial-startup-file-failure", "partial-startup-file-failure",
"startup-scan", "startup-scan",
+12 -82
View File
@@ -19,6 +19,18 @@ Earlier releases remain available in the 1.0 release history, the 1.0 preview hi
- A missing legacy file-name case setting no longer makes the configuration mismatch dialogue require a database rebuild when this device already uses case-insensitive handling. Case-sensitive handling now correctly requires a compatibility decision when the remote omits that setting. - A missing legacy file-name case setting no longer makes the configuration mismatch dialogue require a database rebuild when this device already uses case-insensitive handling. Case-sensitive handling now correctly requires a compatibility decision when the remote omits that setting.
- Configuration review now compares the selected remote profile's trial settings, and discards a pending decision if its settings or active connection change before it can be applied. - Configuration review now compares the selected remote profile's trial settings, and discards a pending decision if its settings or active connection change before it can be applied.
## 1.0.27
7th September, 2026
For now, I am addressing the issues I can resolve first. I hope this helps.
### Synchronisation and storage
#### Fixed
- First-time Object Storage setup now completes when **Use Custom HTTP Handler** is enabled for an empty remote, including a new Cloudflare R2 bucket. LiveSync can now create the remote state required to begin synchronisation. (#1166)
## 1.0.26 ## 1.0.26
~~1.0.25~~ was cancelled because pre-release validation found that LiveSync could appear to finish synchronising even though Android had not written a received file to the Vault; the warning appeared only after restart. ~~1.0.25~~ was cancelled because pre-release validation found that LiveSync could appear to finish synchronising even though Android had not written a received file to the Vault; the warning appeared only after restart.
@@ -119,85 +131,3 @@ I am sorry to make this release while several pull requests are still awaiting m
#### Fixed #### Fixed
- `mark-resolved`, `lock-remote`, and `unlock-remote` now return a non-zero exit code when the selected provider cannot verify the requested remote state. Use `--compat-remote-admin-exit-zero` to retain the former exit code for returned verification failures; unknown remote IDs and mutation errors still fail. - `mark-resolved`, `lock-remote`, and `unlock-remote` now return a non-zero exit code when the selected provider cannot verify the requested remote state. Use `--compat-remote-admin-exit-zero` to retain the former exit code for returned verification failures; unknown remote IDs and mutation errors still fail.
## 1.0.21
26th August, 2026
It is becoming more 'ordinary' with each release, but please let me know if anything has become less convenient.
### Interface and translation
#### Fixed
- Remote Configuration section headings no longer overlap their contents when scrolling on mobile. Action buttons in Remote Configuration, Maintenance, and Patches now remain inside the settings pane on narrow screens.
## 1.0.20
~~1.0.19~~ was cancelled because prerelease validation exposed an incorrect warning at start-up.
25th August, 2026
I know this is the second time I have said it, but I had grown quite fond of the settings screen. It seems, however, that a simpler, healthier life is called for.
### Interface and translation
#### Fixed
- Compatibility pause warnings now direct you to the dedicated compatibility review instead of the Change Log.
- The Obsidian 1.13 settings page now waits for saved settings before choosing its initial layout. This prevents a spurious missing-replicator warning at start-up, keeps configured devices on the Synchronisation-first layout even when automatic synchronisation triggers are disabled, and keeps Quick Setup first on unconfigured devices.
#### Improved
- Settings page names, controls in General Settings, Quick Setup actions, and Advanced controls now use Obsidian 1.13's native settings interface and global search, while retaining their familiar icons. The landing page keeps Remote Configuration and Sync Settings together, places Appearance, Logging, and Extra menus under General Settings, and groups maintenance, optional features, advanced settings, and help by purpose. Earlier supported Obsidian versions continue to use the pane-based interface.
- Settings changes which require database initialisation now use a focused Setup Manager dialogue to choose between existing synchronisation data and the files in the current Vault. The selected reset or rebuild is reserved before the settings are saved, while cancelling offers a separate, explicit settings-only fallback.
## 1.0.18
24th August, 2026
### Synchronisation and storage
#### Fixed
- Reset and rebuild workflows now use the local database selected by their updated settings, preventing stale data from reopening after a **Database Suffix** change. If database initialisation does not complete, the workflow remains paused instead of continuing with incomplete state.
#### Improved
- Rebuilds now recheck restored file events against the current Vault, use current file contents, and finish processing them before the plug-in reports readiness.
## 1.0.17
23rd August, 2026
### Interface and translation
#### Fixed
- Settings generated from the settings manifest, Setup Wizard configuration summaries, and warnings about externally changed settings now honour **Display language** when a translation is available, instead of remaining in English (PR #1123). Thank you to @nimula for the contribution!
### Peer-to-peer synchronisation
#### Improved
- P2P connection profiles now provide four **P2P message size** presets and a **Connection path** choice between **Automatic** and **TURN relay only**. Smaller messages can improve compatibility on paths which fragment or drop larger WebRTC messages, while relay-only routing requires a configured TURN server. P2P connection strings and encrypted Setup URIs preserve both choices.
- Thank you to @andrewschreiber for the detailed fragmentation diagnosis and working 800-byte threshold in vrtmrz/livesync-commonlib#97, which informed this compatibility design.
- An optional self-hosted Coturn Compose starter is now available for P2P deployments that need a TURN relay. It uses a pinned upstream image and documents its network, credential, security, and verification boundaries.
## 1.0.16
19th August, 2026
### Conflict handling and recovery
#### Fixed
- **Back to this revision** in Document History now restores the selected content as a new non-deleted successor revision before reflecting it to the Vault. A readable revision restored after a logical deletion therefore remains restored through later synchronisation instead of being overwritten by the deletion.
- If the file changes while restoration is in progress, the operation stops instead of extending a stale revision. Existing conflicts remain available through **Inspect conflicts and file/database differences**.
### Synchronisation and storage
#### Improved
- One-shot CouchDB synchronisation now releases stalled web-compatible connection checks before replication starts, so a later synchronisation can make a fresh attempt (Commonlib 0.1.16).
- The 60-second safeguard applies only to pre-replication checks. It does not limit ordinary synchronisation, and the **Use Internal API** path is unchanged.
+2 -1
View File
@@ -37,5 +37,6 @@
"1.0.22": "1.7.2", "1.0.22": "1.7.2",
"1.0.23": "1.7.2", "1.0.23": "1.7.2",
"1.0.24": "1.7.2", "1.0.24": "1.7.2",
"1.0.26": "1.7.2" "1.0.26": "1.7.2",
"1.0.27": "1.7.2"
} }
+2 -6
View File
@@ -89,6 +89,7 @@ export default defineConfig({
treeShaking: true, treeShaking: true,
}), }),
svelte({ svelte({
configFile: false,
preprocess: sveltePreprocess(), preprocess: sveltePreprocess(),
compilerOptions: { css: "injected", preserveComments: false }, compilerOptions: { css: "injected", preserveComments: false },
}), }),
@@ -99,12 +100,7 @@ export default defineConfig({
src: path.resolve(__dirname, "./src"), src: path.resolve(__dirname, "./src"),
}, },
}, },
esbuild: { define,
define: define,
target: "es2018",
platform: "browser",
},
// define,
server: { server: {
headers: { headers: {
"Service-Worker-Allowed": "/", "Service-Worker-Allowed": "/",
+5
View File
@@ -7,6 +7,10 @@ import { defineConfig, mergeConfig } from "vitest/config";
import viteConfig from "./vitest.config.common"; import viteConfig from "./vitest.config.common";
const importOnlyFiles = ["**/encryption/encryptHKDF.ts"]; const importOnlyFiles = ["**/encryption/encryptHKDF.ts"];
const obsidianRuntimeOnlyFiles = [
"src/deps.ts",
"src/serviceModules/FileSystemAdapters/ObsidianTypeGuardAdapter.ts",
];
export default mergeConfig( export default mergeConfig(
viteConfig, viteConfig,
defineConfig({ defineConfig({
@@ -32,6 +36,7 @@ export default mergeConfig(
"src/apps/**/testdeno/**", "src/apps/**/testdeno/**",
"**/*_obsolete.ts", "**/*_obsolete.ts",
...importOnlyFiles, ...importOnlyFiles,
...obsidianRuntimeOnlyFiles,
], ],
provider: "v8", provider: "v8",
reporter: ["text", "json", "html", ["text", { file: "coverage-text.txt" }]], reporter: ["text", "json", "html", ["text", { file: "coverage-text.txt" }]],