mirror of
https://github.com/vrtmrz/obsidian-livesync.git
synced 2026-06-13 18:00:14 +00:00
Compare commits
13 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9d86c2828b | |||
| 3b6d3beaa7 | |||
| bb75b6ead8 | |||
| fccb2304f6 | |||
| f00ef5eaae | |||
| 4e7ee760de | |||
| c4faade30c | |||
| 295dc1392a | |||
| 445a8c747c | |||
| 292a6b9e1e | |||
| 0e04e7d31d | |||
| 4cf4acf7e9 | |||
| c78e583399 |
@@ -8,6 +8,8 @@ name: Build and Push CLI Docker Image
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
tags:
|
||||
- "*.*.*-cli"
|
||||
workflow_dispatch:
|
||||
@@ -41,14 +43,32 @@ jobs:
|
||||
id: meta
|
||||
run: |
|
||||
VERSION=$(jq -r '.version' manifest.json)
|
||||
EPOCH=$(date +%s)
|
||||
TAG="${VERSION}-${EPOCH}-cli"
|
||||
SHORT_SHA=$(git rev-parse --short HEAD)
|
||||
IMAGE="ghcr.io/${{ github.repository_owner }}/livesync-cli"
|
||||
echo "tag=${TAG}" >> $GITHUB_OUTPUT
|
||||
echo "image=${IMAGE}" >> $GITHUB_OUTPUT
|
||||
echo "full=${IMAGE}:${TAG}" >> $GITHUB_OUTPUT
|
||||
echo "version=${IMAGE}:${VERSION}-cli" >> $GITHUB_OUTPUT
|
||||
echo "latest=${IMAGE}:latest" >> $GITHUB_OUTPUT
|
||||
|
||||
# Build tag list based on the event and git ref
|
||||
TAGS=""
|
||||
if [[ "${{ github.ref }}" == refs/tags/* ]]; then
|
||||
# Stable release builds
|
||||
TAGS="${IMAGE}:${VERSION}-cli,${IMAGE}:latest,${IMAGE}:${VERSION}-sha-${SHORT_SHA}-cli"
|
||||
elif [[ "${{ github.ref }}" == refs/heads/main ]]; then
|
||||
# Bleeding-edge / nightly builds
|
||||
TAGS="${IMAGE}:edge,${IMAGE}:${VERSION}-dev-sha-${SHORT_SHA}-cli"
|
||||
else
|
||||
# Other branches / manual run fallback
|
||||
TAGS="${IMAGE}:${VERSION}-dev-sha-${SHORT_SHA}-cli"
|
||||
fi
|
||||
|
||||
# Determine if the image should be pushed
|
||||
PUSH="true"
|
||||
if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then
|
||||
if [[ "${{ inputs.dry_run }}" == "true" ]]; then
|
||||
PUSH="false"
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "tags=${TAGS}" >> $GITHUB_OUTPUT
|
||||
echo "push=${PUSH}" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Log in to GitHub Container Registry
|
||||
uses: docker/login-action@v3
|
||||
@@ -92,10 +112,7 @@ jobs:
|
||||
with:
|
||||
context: .
|
||||
file: src/apps/cli/Dockerfile
|
||||
push: ${{ !(github.event_name == 'workflow_dispatch' && inputs.dry_run) }}
|
||||
tags: |
|
||||
${{ steps.meta.outputs.full }}
|
||||
${{ steps.meta.outputs.version }}
|
||||
${{ steps.meta.outputs.latest }}
|
||||
push: ${{ steps.meta.outputs.push }}
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
|
||||
+111
-68
@@ -1,68 +1,111 @@
|
||||
# Run Unit test without Harnesses
|
||||
name: unit-ci
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- beta
|
||||
paths:
|
||||
- 'src/**'
|
||||
- 'test/**'
|
||||
- 'package.json'
|
||||
- 'package-lock.json'
|
||||
- 'tsconfig.json'
|
||||
- 'vite.config.ts'
|
||||
- 'vitest.config*.ts'
|
||||
- 'esbuild.config.mjs'
|
||||
- 'eslint.config.mjs'
|
||||
- '.github/workflows/unit-ci.yml'
|
||||
pull_request:
|
||||
paths:
|
||||
- 'src/**'
|
||||
- 'test/**'
|
||||
- 'package.json'
|
||||
- 'package-lock.json'
|
||||
- 'tsconfig.json'
|
||||
- 'vite.config.ts'
|
||||
- 'vitest.config*.ts'
|
||||
- 'esbuild.config.mjs'
|
||||
- 'eslint.config.mjs'
|
||||
- '.github/workflows/unit-ci.yml'
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '24.x'
|
||||
cache: 'npm'
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
|
||||
# unit tests do not require Playwright, so we can skip installing its dependencies to save time
|
||||
# - name: Install test dependencies (Playwright Chromium)
|
||||
# run: npm run test:install-dependencies
|
||||
|
||||
- name: Run unit tests suite with coverage
|
||||
run: npm run test:unit:coverage
|
||||
|
||||
- name: Upload coverage report
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: coverage-report
|
||||
path: coverage/**
|
||||
# Run Unit test without Harnesses
|
||||
name: unit-ci
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- beta
|
||||
paths:
|
||||
- 'src/**'
|
||||
- 'test/**'
|
||||
- 'package.json'
|
||||
- 'package-lock.json'
|
||||
- 'tsconfig.json'
|
||||
- 'vite.config.ts'
|
||||
- 'vitest.config*.ts'
|
||||
- 'esbuild.config.mjs'
|
||||
- 'eslint.config.mjs'
|
||||
- '.github/workflows/unit-ci.yml'
|
||||
pull_request:
|
||||
paths:
|
||||
- 'src/**'
|
||||
- 'test/**'
|
||||
- 'package.json'
|
||||
- 'package-lock.json'
|
||||
- 'tsconfig.json'
|
||||
- 'vite.config.ts'
|
||||
- 'vitest.config*.ts'
|
||||
- 'esbuild.config.mjs'
|
||||
- 'eslint.config.mjs'
|
||||
- '.github/workflows/unit-ci.yml'
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
unit-test:
|
||||
name: Unit Tests
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '24.x'
|
||||
cache: 'npm'
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
|
||||
- name: Run unit tests suite with coverage
|
||||
run: npm run test:unit:coverage
|
||||
|
||||
- name: Upload coverage report
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: unit-coverage-report
|
||||
path: coverage/**
|
||||
|
||||
integration-test:
|
||||
name: Integration Tests
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '24.x'
|
||||
cache: 'npm'
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
|
||||
- name: Create environment configuration files
|
||||
run: |
|
||||
cat <<EOF > .env
|
||||
BUILD_MODE=dev
|
||||
PATHS_TEST_INSTALL=
|
||||
EOF
|
||||
cat <<EOF > .test.env
|
||||
hostname=http://127.0.0.1:5989/
|
||||
dbname=livesync-test-db2
|
||||
username=admin
|
||||
password=testpassword
|
||||
minioEndpoint=http://127.0.0.1:9000
|
||||
accessKey=minioadmin
|
||||
secretKey=minioadmin
|
||||
bucketName=livesync-test-bucket
|
||||
EOF
|
||||
|
||||
- name: Start CouchDB container
|
||||
run: npm run test:docker-couchdb:start
|
||||
|
||||
- name: Run integration tests
|
||||
run: npm run test:integration
|
||||
|
||||
- name: Stop CouchDB container
|
||||
if: always()
|
||||
run: npm run test:docker-couchdb:stop || true
|
||||
@@ -54,8 +54,13 @@ To facilitate development and testing, the build process can automatically copy
|
||||
|
||||
- ~~**Deno Tests**: Unit tests for platform-independent code (e.g., `HashManager.test.ts`)~~
|
||||
- This is now obsolete, migrated to vitest.
|
||||
- **Vitest** (`vitest.config.ts`): E2E test by Browser-based-harness using Playwright, unit tests.
|
||||
- Unit tests should be `*.unit.spec.ts` and placed alongside the implementation file (e.g., `ChunkFetcher.unit.spec.ts`).
|
||||
- **Vitest**:
|
||||
- **Unit Tests** (`vitest.config.unit.ts`): Unit tests run in Node.js (excluding harnesses and integration tests). Unit tests should be `*.unit.spec.ts` and placed alongside the implementation file (e.g., `ChunkFetcher.unit.spec.ts`). Executed via `npm run test:unit`.
|
||||
- **Integration Tests** (`vitest.config.integration.ts`): Tests run in Node.js against a real CouchDB instance. Integration tests should be `*.integration.spec.ts` or `*.integration.test.ts` and placed alongside the implementation file (e.g., `StreamingFetch.integration.spec.ts`). Executed via `npm run test:integration`.
|
||||
- If you add a feature that interacts with the remote database (e.g., replication changes, custom changes feed parameters, or custom HTTP queries), you strongly expected to write an integration test to verify the behaviour against a real CouchDB server.
|
||||
- **E2E Tests** (`vitest.config.ts`): End-to-end tests run in a browser-based harness using Playwright/Chromium to test full synchronisation scenarios. Executed via `npm run test`.
|
||||
- **P2P Tests** (`vitest.config.p2p.ts`): Browser-based Peer-to-Peer replication tests. Executed via `npm run test:p2p`.
|
||||
- **RPC Unit Tests** (`vitest.config.rpc-unit.ts`): RPC-specific unit tests with coverage thresholds.
|
||||
|
||||
- **Docker Services**: Tests require CouchDB, MinIO (S3), and P2P services:
|
||||
```bash
|
||||
@@ -63,11 +68,11 @@ To facilitate development and testing, the build process can automatically copy
|
||||
npm run test:full # Run tests with coverage
|
||||
npm run test:docker-all:stop # Stop services
|
||||
```
|
||||
If some services are not needed, start only required ones (e.g., `test:docker-couchdb:start`)
|
||||
If some services are not needed, start only required ones (e.g., `test:docker-couchdb:start`).
|
||||
Note that if services are already running, starting script will fail. Please stop them first.
|
||||
|
||||
- **Test Structure**:
|
||||
- `test/suite/` - Integration tests for sync operations
|
||||
- `test/suite/` - E2E tests for sync operations (running in browser)
|
||||
- `test/unit/` - Unit tests (via vitest, as harness is browser-based)
|
||||
- `test/harness/` - Mock implementations (e.g., `obsidian-mock.ts`)
|
||||
|
||||
@@ -151,7 +156,7 @@ Hence, the new feature should be implemented as follows:
|
||||
|
||||
## Common Patterns
|
||||
|
||||
### Module Implementation
|
||||
### Module Implementation (Now not recommended for new features, use services instead)
|
||||
|
||||
```typescript
|
||||
export class ModuleExample extends AbstractObsidianModule {
|
||||
@@ -204,6 +209,7 @@ In short, the situation remains unchanged for me, but it means you all become a
|
||||
## Contribution Guidelines
|
||||
|
||||
- Follow existing code style and conventions
|
||||
- Write integration tests (`*.integration.spec.ts` or `*.integration.test.ts`) when adding or modifying features that interact with the remote database, and ensure that they pass in the CI workflow.
|
||||
- Please bump dependencies with care, check artifacts after updates, with diff-tools and only expected changes in the build output (to avoid unexpected vulnerabilities).
|
||||
- When adding new features, please consider it has an OSS implementation, and avoid using proprietary services or APIs that may limit usage.
|
||||
- For example, any functionality to connect to a new type of server is expected to either have an OSS implementation available for that server, or to be managed under some responsibilities and/or limitations without disrupting existing functionality, and scope for surveillance reduced by some means (e.g., by client-side encryption, auditing the server ourselves).
|
||||
|
||||
@@ -488,6 +488,11 @@ Automatically Sync all files when opening Obsidian.
|
||||
Setting key: syncAfterMerge
|
||||
Sync automatically after merging files
|
||||
|
||||
#### Keep replication active in the background
|
||||
|
||||
Setting key: keepReplicationActiveInBackground
|
||||
Desktop only; uses more battery and network.
|
||||
|
||||
### 3. Update thinning
|
||||
|
||||
#### Batch database update
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"id": "obsidian-livesync",
|
||||
"name": "Self-hosted LiveSync",
|
||||
"version": "0.25.74",
|
||||
"version": "0.25.75",
|
||||
"minAppVersion": "1.7.2",
|
||||
"description": "Community implementation of self-hosted livesync. Reflect your vault changes to some other devices immediately. Please make sure to disable other synchronize solutions to avoid content corruption or duplication.",
|
||||
"author": "vorotamoroz",
|
||||
|
||||
Generated
+168
-151
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "obsidian-livesync",
|
||||
"version": "0.25.74",
|
||||
"version": "0.25.75",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "obsidian-livesync",
|
||||
"version": "0.25.74",
|
||||
"version": "0.25.75",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-s3": "^3.808.0",
|
||||
@@ -56,7 +56,7 @@
|
||||
"@vitest/browser-playwright": "^4.1.8",
|
||||
"@vitest/coverage-v8": "^4.1.8",
|
||||
"dotenv-cli": "^11.0.0",
|
||||
"esbuild": "0.25.0",
|
||||
"esbuild": "0.28.1",
|
||||
"esbuild-plugin-inline-worker": "^0.1.1",
|
||||
"esbuild-svelte": "^0.9.4",
|
||||
"eslint": "^9.39.3",
|
||||
@@ -1359,9 +1359,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/aix-ppc64": {
|
||||
"version": "0.25.0",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.0.tgz",
|
||||
"integrity": "sha512-O7vun9Sf8DFjH2UtqK8Ku3LkquL9SZL8OLY1T5NZkA34+wG3OQF7cl4Ql8vdNzM6fzBbYfLaiRLIOZ+2FOCgBQ==",
|
||||
"version": "0.28.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.1.tgz",
|
||||
"integrity": "sha512-Svl7tq8k/08+p6CXPpRjQ1fKX+1odH/BQbb48fV6fj3CWHhsoIOoY87w1oHXm0qEpkIK3ZfVgp0hed3XBXzXMQ==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
@@ -1376,9 +1376,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/android-arm": {
|
||||
"version": "0.25.0",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.0.tgz",
|
||||
"integrity": "sha512-PTyWCYYiU0+1eJKmw21lWtC+d08JDZPQ5g+kFyxP0V+es6VPPSUhM6zk8iImp2jbV6GwjX4pap0JFbUQN65X1g==",
|
||||
"version": "0.28.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.1.tgz",
|
||||
"integrity": "sha512-0k2F129Xdio1TdJfzJ8sy1Q47vUD2NnwdhiAf7drUN1EBTfPf4hsFCtmMgu/6m8JSzsBrlmVjudMBQqOfG8usQ==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
@@ -1393,9 +1393,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/android-arm64": {
|
||||
"version": "0.25.0",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.0.tgz",
|
||||
"integrity": "sha512-grvv8WncGjDSyUBjN9yHXNt+cq0snxXbDxy5pJtzMKGmmpPxeAmAhWxXI+01lU5rwZomDgD3kJwulEnhTRUd6g==",
|
||||
"version": "0.28.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.1.tgz",
|
||||
"integrity": "sha512-34EGEbCIAgosYz6goLcopX6Mo7NyGv9tfwEM2/7Ce2VcVRk568iSvniGWcUXIy7wEDR1wzolcxcriFVrWYcwBg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -1410,9 +1410,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/android-x64": {
|
||||
"version": "0.25.0",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.0.tgz",
|
||||
"integrity": "sha512-m/ix7SfKG5buCnxasr52+LI78SQ+wgdENi9CqyCXwjVR2X4Jkz+BpC3le3AoBPYTC9NHklwngVXvbJ9/Akhrfg==",
|
||||
"version": "0.28.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.1.tgz",
|
||||
"integrity": "sha512-dbwY7ltSMDWsRatcRpCnES4F+im88OCUgGZjy52shC7GqHRE/cYlxNbB4Z4UpJswpcc4Qxd2oE/ufM0p61IKng==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -1427,9 +1427,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/darwin-arm64": {
|
||||
"version": "0.25.0",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.0.tgz",
|
||||
"integrity": "sha512-mVwdUb5SRkPayVadIOI78K7aAnPamoeFR2bT5nszFUZ9P8UpK4ratOdYbZZXYSqPKMHfS1wdHCJk1P1EZpRdvw==",
|
||||
"version": "0.28.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.1.tgz",
|
||||
"integrity": "sha512-TZbWkQY7kvTAXbXUT7uVACR5cMHsDiSz9z7ZKAX/RTq/WJEk3QyRr0wZpNhBDX+/0CtdqUIJlOiodQcta6tY3Q==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -1444,9 +1444,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/darwin-x64": {
|
||||
"version": "0.25.0",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.0.tgz",
|
||||
"integrity": "sha512-DgDaYsPWFTS4S3nWpFcMn/33ZZwAAeAFKNHNa1QN0rI4pUjgqf0f7ONmXf6d22tqTY+H9FNdgeaAa+YIFUn2Rg==",
|
||||
"version": "0.28.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.1.tgz",
|
||||
"integrity": "sha512-zfdzgK9ACBNZLI/CyHTOx81SyNbM6YXn7rxSgX97VjyiPl9W1i4Ka4fgKECEoFCKGpvBj5qArWIGgQjOwkgskQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -1461,9 +1461,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/freebsd-arm64": {
|
||||
"version": "0.25.0",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.0.tgz",
|
||||
"integrity": "sha512-VN4ocxy6dxefN1MepBx/iD1dH5K8qNtNe227I0mnTRjry8tj5MRk4zprLEdG8WPyAPb93/e4pSgi1SoHdgOa4w==",
|
||||
"version": "0.28.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.1.tgz",
|
||||
"integrity": "sha512-wG2EA8ENdEI0qhkSZMjfqrdY+ziCYCPMmtZjjIwOmXFjmyzEHn+UUxk5of+SYsjtfs3VpnlC7QLzSI5hY/rOAw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -1478,9 +1478,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/freebsd-x64": {
|
||||
"version": "0.25.0",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.0.tgz",
|
||||
"integrity": "sha512-mrSgt7lCh07FY+hDD1TxiTyIHyttn6vnjesnPoVDNmDfOmggTLXRv8Id5fNZey1gl/V2dyVK1VXXqVsQIiAk+A==",
|
||||
"version": "0.28.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.1.tgz",
|
||||
"integrity": "sha512-i7dZ9vQgnvSCzi/rYCXNgtF/U+eKZNJBzu3eTQbRgHnM7tNSizLOkRFAl3qzVc/Op/u5YkHHa4pf/3DOYHthLQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -1495,9 +1495,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-arm": {
|
||||
"version": "0.25.0",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.0.tgz",
|
||||
"integrity": "sha512-vkB3IYj2IDo3g9xX7HqhPYxVkNQe8qTK55fraQyTzTX/fxaDtXiEnavv9geOsonh2Fd2RMB+i5cbhu2zMNWJwg==",
|
||||
"version": "0.28.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.1.tgz",
|
||||
"integrity": "sha512-qVXBOHQS+d5Y722GwJzJUtOLlX7km3CraOaGormF1pDtPd2C/l1SHRPgjLunLGe51Sh5YYWKMFDyV4SxgMQYTQ==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
@@ -1512,9 +1512,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-arm64": {
|
||||
"version": "0.25.0",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.0.tgz",
|
||||
"integrity": "sha512-9QAQjTWNDM/Vk2bgBl17yWuZxZNQIF0OUUuPZRKoDtqF2k4EtYbpyiG5/Dk7nqeK6kIJWPYldkOcBqjXjrUlmg==",
|
||||
"version": "0.28.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.1.tgz",
|
||||
"integrity": "sha512-yHs+0uc8+nvEAfAfxrWQKK5peSNzBc4PegcMO0EJ2hT71uA7vB8Ihg2e77R2P7SG5uYjPbHlLLmve4LLLRCf0g==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -1529,9 +1529,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-ia32": {
|
||||
"version": "0.25.0",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.0.tgz",
|
||||
"integrity": "sha512-43ET5bHbphBegyeqLb7I1eYn2P/JYGNmzzdidq/w0T8E2SsYL1U6un2NFROFRg1JZLTzdCoRomg8Rvf9M6W6Gg==",
|
||||
"version": "0.28.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.1.tgz",
|
||||
"integrity": "sha512-d1z4ZuP0ajrfz/FhGT4vv278rX8KnPPJx8i5+AtK7TYbx9Le9F1hyzurZpkEyjkGa9dUGhQow4C1NmeGvqxN2w==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
@@ -1546,9 +1546,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-loong64": {
|
||||
"version": "0.25.0",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.0.tgz",
|
||||
"integrity": "sha512-fC95c/xyNFueMhClxJmeRIj2yrSMdDfmqJnyOY4ZqsALkDrrKJfIg5NTMSzVBr5YW1jf+l7/cndBfP3MSDpoHw==",
|
||||
"version": "0.28.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.1.tgz",
|
||||
"integrity": "sha512-M5sRjUVZrkm1OAPR3dlOYzNmN+loZKGVi1VUQGrwuqLcbR6qeAz+famMhjASeH3YVKvZz+zT1jlh/keC3Rj/lg==",
|
||||
"cpu": [
|
||||
"loong64"
|
||||
],
|
||||
@@ -1563,9 +1563,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-mips64el": {
|
||||
"version": "0.25.0",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.0.tgz",
|
||||
"integrity": "sha512-nkAMFju7KDW73T1DdH7glcyIptm95a7Le8irTQNO/qtkoyypZAnjchQgooFUDQhNAy4iu08N79W4T4pMBwhPwQ==",
|
||||
"version": "0.28.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.1.tgz",
|
||||
"integrity": "sha512-mRObBZeHh2OxcBFPWE/FjylkRgZdYuiTR3vaTozquCGOH14iP9oN4x4Ge81CoIDYQrXmIxpFumJBu5MtZpnQJQ==",
|
||||
"cpu": [
|
||||
"mips64el"
|
||||
],
|
||||
@@ -1580,9 +1580,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-ppc64": {
|
||||
"version": "0.25.0",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.0.tgz",
|
||||
"integrity": "sha512-NhyOejdhRGS8Iwv+KKR2zTq2PpysF9XqY+Zk77vQHqNbo/PwZCzB5/h7VGuREZm1fixhs4Q/qWRSi5zmAiO4Fw==",
|
||||
"version": "0.28.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.1.tgz",
|
||||
"integrity": "sha512-slScBsMAb3GFDcdrCgLwZtPYRoH2H/youv10QiZyRjmsP48fznoveWytSgCI/R0ZcUgpc0ZhIUEx6LHts8yrfQ==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
@@ -1597,9 +1597,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-riscv64": {
|
||||
"version": "0.25.0",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.0.tgz",
|
||||
"integrity": "sha512-5S/rbP5OY+GHLC5qXp1y/Mx//e92L1YDqkiBbO9TQOvuFXM+iDqUNG5XopAnXoRH3FjIUDkeGcY1cgNvnXp/kA==",
|
||||
"version": "0.28.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.1.tgz",
|
||||
"integrity": "sha512-kw0owk1o0GFETUJyW0jc0G4Yzs0BHZn0JDZ8JRT088vjJYX777BAs1fDGxAC+q831qOs2DTC96mNsG2opdfyyQ==",
|
||||
"cpu": [
|
||||
"riscv64"
|
||||
],
|
||||
@@ -1614,9 +1614,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-s390x": {
|
||||
"version": "0.25.0",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.0.tgz",
|
||||
"integrity": "sha512-XM2BFsEBz0Fw37V0zU4CXfcfuACMrppsMFKdYY2WuTS3yi8O1nFOhil/xhKTmE1nPmVyvQJjJivgDT+xh8pXJA==",
|
||||
"version": "0.28.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.1.tgz",
|
||||
"integrity": "sha512-/lAIjX8aYFRByhh6L5rYtPEDRqa9de/4V/juOXcta5frjvzXO4/sqEtyytse0g3zZFuWu5cDN0MkLz2qRDD2Ag==",
|
||||
"cpu": [
|
||||
"s390x"
|
||||
],
|
||||
@@ -1631,9 +1631,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-x64": {
|
||||
"version": "0.25.0",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.0.tgz",
|
||||
"integrity": "sha512-9yl91rHw/cpwMCNytUDxwj2XjFpxML0y9HAOH9pNVQDpQrBxHy01Dx+vaMu0N1CKa/RzBD2hB4u//nfc+Sd3Cw==",
|
||||
"version": "0.28.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.1.tgz",
|
||||
"integrity": "sha512-u/anNYF2mmVOEDwLtnQ1wOr3EZ9sTNGLWrsYGYwHWzGA3Si84IOkHXlbWTD1NB+9/1lcnweYKO54uhxZydNzfA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -1648,9 +1648,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/netbsd-arm64": {
|
||||
"version": "0.25.0",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.0.tgz",
|
||||
"integrity": "sha512-RuG4PSMPFfrkH6UwCAqBzauBWTygTvb1nxWasEJooGSJ/NwRw7b2HOwyRTQIU97Hq37l3npXoZGYMy3b3xYvPw==",
|
||||
"version": "0.28.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.1.tgz",
|
||||
"integrity": "sha512-oks0DYbLwWMmaakTsCb+zL4E+aHRVLom9IJZOAthMQEPiQmydXHkziYEsGYRx0uNV/IjEKGAV941JzH02pflqw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -1665,9 +1665,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/netbsd-x64": {
|
||||
"version": "0.25.0",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.0.tgz",
|
||||
"integrity": "sha512-jl+qisSB5jk01N5f7sPCsBENCOlPiS/xptD5yxOx2oqQfyourJwIKLRA2yqWdifj3owQZCL2sn6o08dBzZGQzA==",
|
||||
"version": "0.28.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.1.tgz",
|
||||
"integrity": "sha512-aeL6lAnN89Hz43Mlh1G8ARasbuoYvSITDEx0tHh5b7jJnHcssqgjy9Yx430GDpmCa6OyrKoS0aNRjKundRizGg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -1682,9 +1682,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/openbsd-arm64": {
|
||||
"version": "0.25.0",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.0.tgz",
|
||||
"integrity": "sha512-21sUNbq2r84YE+SJDfaQRvdgznTD8Xc0oc3p3iW/a1EVWeNj/SdUCbm5U0itZPQYRuRTW20fPMWMpcrciH2EJw==",
|
||||
"version": "0.28.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.1.tgz",
|
||||
"integrity": "sha512-MEFJe5C3R8pwXdZ5Y21oo6m7ePiS0d9pWucn99O/wvyJZChoIQKrQDxKrGeW8F5+T0okTHesAmDeiHDTIq0V/Q==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -1699,9 +1699,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/openbsd-x64": {
|
||||
"version": "0.25.0",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.0.tgz",
|
||||
"integrity": "sha512-2gwwriSMPcCFRlPlKx3zLQhfN/2WjJ2NSlg5TKLQOJdV0mSxIcYNTMhk3H3ulL/cak+Xj0lY1Ym9ysDV1igceg==",
|
||||
"version": "0.28.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.1.tgz",
|
||||
"integrity": "sha512-i/ZLIOafE0Z8cI/XANJAixoJL/uRAoS2xOA3rb0xN+KK0K177cMAsQYkzHtBrtMXAKuAc7HGgcWiZ/sRC1Nxgw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -1733,9 +1733,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/sunos-x64": {
|
||||
"version": "0.25.0",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.0.tgz",
|
||||
"integrity": "sha512-bxI7ThgLzPrPz484/S9jLlvUAHYMzy6I0XiU1ZMeAEOBcS0VePBFxh1JjTQt3Xiat5b6Oh4x7UC7IwKQKIJRIg==",
|
||||
"version": "0.28.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.1.tgz",
|
||||
"integrity": "sha512-BEjgtECkL3vY+SaSQ6nzVfiALUeFxpawyp8Jmf5PtYhf1Ug40N1h/hxlhts+f1FvSvarEigdxS3BlSMI2PJLcQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -1750,9 +1750,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/win32-arm64": {
|
||||
"version": "0.25.0",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.0.tgz",
|
||||
"integrity": "sha512-ZUAc2YK6JW89xTbXvftxdnYy3m4iHIkDtK3CLce8wg8M2L+YZhIvO1DKpxrd0Yr59AeNNkTiic9YLf6FTtXWMw==",
|
||||
"version": "0.28.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.1.tgz",
|
||||
"integrity": "sha512-lCv9eK/H6ZJWbE7bh2nw54CZ9M2nupBxJcTsdk/QQnWkdSjKGuxmmH8/GWrlT1eMmZfn4dGcCjRte397WqfQXA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -1767,9 +1767,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/win32-ia32": {
|
||||
"version": "0.25.0",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.0.tgz",
|
||||
"integrity": "sha512-eSNxISBu8XweVEWG31/JzjkIGbGIJN/TrRoiSVZwZ6pkC6VX4Im/WV2cz559/TXLcYbcrDN8JtKgd9DJVIo8GA==",
|
||||
"version": "0.28.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.1.tgz",
|
||||
"integrity": "sha512-zvb/mB2bSCoJOpoCBgYKKpX6YM6mJBlBUVUtVj41DlZJVEB6/0CKlRYxP5wWl1C1ILiCoAU5wZZ4q1P3qeS6Eg==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
@@ -1784,9 +1784,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/win32-x64": {
|
||||
"version": "0.25.0",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.0.tgz",
|
||||
"integrity": "sha512-ZENoHJBxA20C2zFzh6AI4fT6RraMzjYw4xKWemRTRmRVtN9c5DcH9r/f2ihEkMjOW5eGgrwCslG/+Y/3bL+DHQ==",
|
||||
"version": "0.28.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.1.tgz",
|
||||
"integrity": "sha512-bm4Mowrv+GXMlpWX++EcXw/iLyd1o3+bJkC2DkWXYVvgZCqD/bSj9ctZeAMC3cIxgjRVR2Dufaiu4YPxr5gW1A==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -3547,13 +3547,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@smithy/core": {
|
||||
"version": "3.24.6",
|
||||
"resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.24.6.tgz",
|
||||
"integrity": "sha512-wBXDRup6UU97VKyaiRo8AssnfStPtG0oAAfpq/bC0a1YYau8pM86YB4kM6ccoVi1mS8l/UHbn9oDM+7uozr/ug==",
|
||||
"version": "3.24.7",
|
||||
"resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.24.7.tgz",
|
||||
"integrity": "sha512-KoUi4M1f3BG6kzN1FnCwL7oyFptTbyBJKjR6yhSib+JHRdUmM1o+VwsFtJ66NZCkCzVfJMWRHJNo0R0jznp0Pg==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@aws-crypto/crc32": "5.2.0",
|
||||
"@smithy/types": "^4.14.3",
|
||||
"@smithy/types": "^4.14.4",
|
||||
"tslib": "^2.6.2"
|
||||
},
|
||||
"engines": {
|
||||
@@ -3720,12 +3720,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@smithy/is-array-buffer": {
|
||||
"version": "4.3.6",
|
||||
"resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-4.3.6.tgz",
|
||||
"integrity": "sha512-/cSYHP8jPffkhBClQzH9fAJujIh8dwMwg2swrVF4stXQsUWO5Oi2bwyaMUcBPIyulUI5IxaJFxd9C8UQX+YZsQ==",
|
||||
"version": "4.3.7",
|
||||
"resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-4.3.7.tgz",
|
||||
"integrity": "sha512-AusFkhQbCFK8ucF0RH9ojrWTvE26FgBzJvobi3dY7D/cIw6Mzdz8NwNg/gtQSzSo6JQ5tCaRJyWxM8AL0zm6fQ==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@smithy/core": "^3.24.6",
|
||||
"@smithy/core": "^3.24.7",
|
||||
"tslib": "^2.6.2"
|
||||
},
|
||||
"engines": {
|
||||
@@ -3989,9 +3989,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@smithy/types": {
|
||||
"version": "4.14.3",
|
||||
"resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.14.3.tgz",
|
||||
"integrity": "sha512-YupL0ZWmFtJexUN2cHzkvvF/b9pKrtAIfT1o7/oY/Ppu8IYeZ+lDPM5vZdQJaSeA132dJCqojjGC9NhXeF71VQ==",
|
||||
"version": "4.14.4",
|
||||
"resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.14.4.tgz",
|
||||
"integrity": "sha512-B2S9+UGm1+/pHkcx3ZoLVX1a+pmSk8rqxRR+ZsNqZaJ5q9FWX9AFGQVM4qG5+OBeQUZVy99HY8HqW8gK/wgXzQ==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"tslib": "^2.6.2"
|
||||
@@ -4053,12 +4053,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@smithy/util-buffer-from": {
|
||||
"version": "4.3.6",
|
||||
"resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-4.3.6.tgz",
|
||||
"integrity": "sha512-sms/ty2CJwHOiGzEaAVWizTVK5KusXpAYqCUeXIa+hWtNKLwjimH4z11mc07d0Fe3DT3lmZJIZWOMcVQ/N4hBQ==",
|
||||
"version": "4.3.7",
|
||||
"resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-4.3.7.tgz",
|
||||
"integrity": "sha512-Ur4edaceoTQZSCi7cLcJfRpw1Kh28pcSVFuOLSrrjppbO3xIMo9Zi0Q0ZfQ0qflMZzB0f5NIBHswBLiFSwzVVg==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@smithy/core": "^3.24.6",
|
||||
"@smithy/core": "^3.24.7",
|
||||
"tslib": "^2.6.2"
|
||||
},
|
||||
"engines": {
|
||||
@@ -4194,12 +4194,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@smithy/util-utf8": {
|
||||
"version": "4.3.6",
|
||||
"resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-4.3.6.tgz",
|
||||
"integrity": "sha512-tAa4sePYB7mlJzdYbdBqdv37KwFKWixmM/r3ihcI0HFOVjf+a5oGvtcLXcGm4S1bY4DFsLAIOHgjubtp+oRufw==",
|
||||
"version": "4.3.7",
|
||||
"resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-4.3.7.tgz",
|
||||
"integrity": "sha512-U2XPgO73I8ef8FP3jRQO4Iiol7chWuv4TD6LxNLvRo/pYwBvbelxLcODkpAA9ek+k23NMgc9NoiVzp8INwogMA==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@smithy/core": "^3.24.6",
|
||||
"@smithy/core": "^3.24.7",
|
||||
"tslib": "^2.6.2"
|
||||
},
|
||||
"engines": {
|
||||
@@ -4489,13 +4489,13 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "24.12.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.12.4.tgz",
|
||||
"integrity": "sha512-GUUEShf+PBCGW2KaXwcIt3Yk+e3pkKwWKb9GSyM9WQVE+ep2jzmHdGsHzu4wgcZy5fN9FBdVzjpBQsYlpfpgLA==",
|
||||
"version": "24.13.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.13.2.tgz",
|
||||
"integrity": "sha512-fRa09kZTgu8o71KFcDjUFuc7F+dEbZYZmkI0mg5YBTRs0yMKjYHsq/c0urDKeDb+D5qVgXOdFcuu+DZPKOITwA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"undici-types": "~7.16.0"
|
||||
"undici-types": "~7.18.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/pouchdb": {
|
||||
@@ -5335,9 +5335,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@wdio/repl/node_modules/@types/node": {
|
||||
"version": "20.19.41",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.41.tgz",
|
||||
"integrity": "sha512-ECymXOukMnOoVkC2bb1Vc/w/836DXncOg5m8Xj1RH7xSHZJWNYY6Zh7EH477vcnD5egKNNfy2RpNOmuChhFPgQ==",
|
||||
"version": "20.19.43",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.43.tgz",
|
||||
"integrity": "sha512-6oYBAi5ikg4Pl+kGsoYtawUMBT2zZMCvPNF7pVLnHZfd1zf38DRiWn/gT01RYCdUqkv7Fhr+C9ot4/tb+2sVvA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -5365,9 +5365,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@wdio/types/node_modules/@types/node": {
|
||||
"version": "20.19.41",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.41.tgz",
|
||||
"integrity": "sha512-ECymXOukMnOoVkC2bb1Vc/w/836DXncOg5m8Xj1RH7xSHZJWNYY6Zh7EH477vcnD5egKNNfy2RpNOmuChhFPgQ==",
|
||||
"version": "20.19.43",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.43.tgz",
|
||||
"integrity": "sha512-6oYBAi5ikg4Pl+kGsoYtawUMBT2zZMCvPNF7pVLnHZfd1zf38DRiWn/gT01RYCdUqkv7Fhr+C9ot4/tb+2sVvA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -7565,13 +7565,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/esbuild": {
|
||||
"version": "0.25.0",
|
||||
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.0.tgz",
|
||||
"integrity": "sha512-BXq5mqc8ltbaN34cDqWuYKyNhX8D/Z0J1xdtdQ8UcIIIyJyz+ZMKUt58tF3SrZ85jcfN/PZYhjR5uDQAYNVbuw==",
|
||||
"version": "0.28.1",
|
||||
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.1.tgz",
|
||||
"integrity": "sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"esbuild": "bin/esbuild"
|
||||
},
|
||||
@@ -7579,31 +7578,32 @@
|
||||
"node": ">=18"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@esbuild/aix-ppc64": "0.25.0",
|
||||
"@esbuild/android-arm": "0.25.0",
|
||||
"@esbuild/android-arm64": "0.25.0",
|
||||
"@esbuild/android-x64": "0.25.0",
|
||||
"@esbuild/darwin-arm64": "0.25.0",
|
||||
"@esbuild/darwin-x64": "0.25.0",
|
||||
"@esbuild/freebsd-arm64": "0.25.0",
|
||||
"@esbuild/freebsd-x64": "0.25.0",
|
||||
"@esbuild/linux-arm": "0.25.0",
|
||||
"@esbuild/linux-arm64": "0.25.0",
|
||||
"@esbuild/linux-ia32": "0.25.0",
|
||||
"@esbuild/linux-loong64": "0.25.0",
|
||||
"@esbuild/linux-mips64el": "0.25.0",
|
||||
"@esbuild/linux-ppc64": "0.25.0",
|
||||
"@esbuild/linux-riscv64": "0.25.0",
|
||||
"@esbuild/linux-s390x": "0.25.0",
|
||||
"@esbuild/linux-x64": "0.25.0",
|
||||
"@esbuild/netbsd-arm64": "0.25.0",
|
||||
"@esbuild/netbsd-x64": "0.25.0",
|
||||
"@esbuild/openbsd-arm64": "0.25.0",
|
||||
"@esbuild/openbsd-x64": "0.25.0",
|
||||
"@esbuild/sunos-x64": "0.25.0",
|
||||
"@esbuild/win32-arm64": "0.25.0",
|
||||
"@esbuild/win32-ia32": "0.25.0",
|
||||
"@esbuild/win32-x64": "0.25.0"
|
||||
"@esbuild/aix-ppc64": "0.28.1",
|
||||
"@esbuild/android-arm": "0.28.1",
|
||||
"@esbuild/android-arm64": "0.28.1",
|
||||
"@esbuild/android-x64": "0.28.1",
|
||||
"@esbuild/darwin-arm64": "0.28.1",
|
||||
"@esbuild/darwin-x64": "0.28.1",
|
||||
"@esbuild/freebsd-arm64": "0.28.1",
|
||||
"@esbuild/freebsd-x64": "0.28.1",
|
||||
"@esbuild/linux-arm": "0.28.1",
|
||||
"@esbuild/linux-arm64": "0.28.1",
|
||||
"@esbuild/linux-ia32": "0.28.1",
|
||||
"@esbuild/linux-loong64": "0.28.1",
|
||||
"@esbuild/linux-mips64el": "0.28.1",
|
||||
"@esbuild/linux-ppc64": "0.28.1",
|
||||
"@esbuild/linux-riscv64": "0.28.1",
|
||||
"@esbuild/linux-s390x": "0.28.1",
|
||||
"@esbuild/linux-x64": "0.28.1",
|
||||
"@esbuild/netbsd-arm64": "0.28.1",
|
||||
"@esbuild/netbsd-x64": "0.28.1",
|
||||
"@esbuild/openbsd-arm64": "0.28.1",
|
||||
"@esbuild/openbsd-x64": "0.28.1",
|
||||
"@esbuild/openharmony-arm64": "0.28.1",
|
||||
"@esbuild/sunos-x64": "0.28.1",
|
||||
"@esbuild/win32-arm64": "0.28.1",
|
||||
"@esbuild/win32-ia32": "0.28.1",
|
||||
"@esbuild/win32-x64": "0.28.1"
|
||||
}
|
||||
},
|
||||
"node_modules/esbuild-plugin-inline-worker": {
|
||||
@@ -7634,6 +7634,23 @@
|
||||
"svelte": ">=4.2.1 <6"
|
||||
}
|
||||
},
|
||||
"node_modules/esbuild/node_modules/@esbuild/openharmony-arm64": {
|
||||
"version": "0.28.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.1.tgz",
|
||||
"integrity": "sha512-ge+Z7EXFNt2BO1oAMsVpiQ8EwndV9i1xXerAeTIK7AtPs3bKFXQM7nlRxDSIUIMeueR1CNXxqztLzdNeReKBJg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"openharmony"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/escalade": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
|
||||
@@ -13085,9 +13102,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/semver": {
|
||||
"version": "7.8.1",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.8.1.tgz",
|
||||
"integrity": "sha512-rkVq3IXh+4FDGch+KwzX3aV9W3kO54GyEgpvBzSyctDA6Xtd7RJQV1xmXbeQp5v7+VzLOfVqiutSE6GICgPFvg==",
|
||||
"version": "7.8.4",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.8.4.tgz",
|
||||
"integrity": "sha512-rUCObTnP32Q08R2uuIrt7r9PlEonuTmtuXYcW6s5kjdlj3xbnwe+21yXptAUYcMAABLkYYTtnmzb3w3EDZfueA==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"bin": {
|
||||
@@ -15259,9 +15276,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/undici": {
|
||||
"version": "7.27.0",
|
||||
"resolved": "https://registry.npmjs.org/undici/-/undici-7.27.0.tgz",
|
||||
"integrity": "sha512-+t2Z/GwkZQDtu00813aP66ygViGtPHKhhoFZpQKpKrE+9jIgES+Zw+mFNaDWOVRKiuJjuqKHzD3B1sfGg8+ZOQ==",
|
||||
"version": "7.27.2",
|
||||
"resolved": "https://registry.npmjs.org/undici/-/undici-7.27.2.tgz",
|
||||
"integrity": "sha512-uZsKNuzQxDMUY6M3pIMvy5tvlGmtq8XJ2oLAkfRKGNu+1VQAIvLy2xIVG5ATZl5wDXl/tddByAWCizRbOme+TA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@@ -15269,9 +15286,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/undici-types": {
|
||||
"version": "7.16.0",
|
||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz",
|
||||
"integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==",
|
||||
"version": "7.18.2",
|
||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz",
|
||||
"integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
@@ -16213,9 +16230,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/webdriver/node_modules/@types/node": {
|
||||
"version": "20.19.41",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.41.tgz",
|
||||
"integrity": "sha512-ECymXOukMnOoVkC2bb1Vc/w/836DXncOg5m8Xj1RH7xSHZJWNYY6Zh7EH477vcnD5egKNNfy2RpNOmuChhFPgQ==",
|
||||
"version": "20.19.43",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.43.tgz",
|
||||
"integrity": "sha512-6oYBAi5ikg4Pl+kGsoYtawUMBT2zZMCvPNF7pVLnHZfd1zf38DRiWn/gT01RYCdUqkv7Fhr+C9ot4/tb+2sVvA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -16285,9 +16302,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/webdriverio/node_modules/@types/node": {
|
||||
"version": "20.19.41",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.41.tgz",
|
||||
"integrity": "sha512-ECymXOukMnOoVkC2bb1Vc/w/836DXncOg5m8Xj1RH7xSHZJWNYY6Zh7EH477vcnD5egKNNfy2RpNOmuChhFPgQ==",
|
||||
"version": "20.19.43",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.43.tgz",
|
||||
"integrity": "sha512-6oYBAi5ikg4Pl+kGsoYtawUMBT2zZMCvPNF7pVLnHZfd1zf38DRiWn/gT01RYCdUqkv7Fhr+C9ot4/tb+2sVvA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
|
||||
+5
-3
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "obsidian-livesync",
|
||||
"version": "0.25.74",
|
||||
"version": "0.25.75",
|
||||
"description": "Reflect your vault changes to some other devices immediately. Please make sure to disable other synchronize solutions to avoid content corruption or duplication.",
|
||||
"main": "main.js",
|
||||
"type": "module",
|
||||
@@ -25,10 +25,12 @@
|
||||
"pretty": "npm run prettyNoWrite -- --write --log-level error",
|
||||
"prettyCheck": "npm run prettyNoWrite -- --check",
|
||||
"prettyNoWrite": "prettier --config ./.prettierrc.mjs \"**/*.js\" \"**/*.ts\" \"**/*.json\" ",
|
||||
"check": "npm run tsc-check && npm run lint && npm run svelte-check",
|
||||
"check:compatibility": "node utils/check-compatibility.js --file main.js --ios 15",
|
||||
"check": "npm run tsc-check && npm run lint && npm run svelte-check && npm run check:compatibility",
|
||||
"unittest": "deno test -A --no-check --coverage=cov_profile --v8-flags=--expose-gc --trace-leaks ./src/",
|
||||
"test": "vitest run",
|
||||
"test:unit": "vitest run --config vitest.config.unit.ts",
|
||||
"test:integration": "vitest run --config vitest.config.integration.ts",
|
||||
"test:unit:coverage": "vitest run --config vitest.config.unit.ts --coverage",
|
||||
"test:install-playwright": "npx playwright install chromium",
|
||||
"test:install-dependencies": "npm run test:install-playwright",
|
||||
@@ -84,7 +86,7 @@
|
||||
"@vitest/browser-playwright": "^4.1.8",
|
||||
"@vitest/coverage-v8": "^4.1.8",
|
||||
"dotenv-cli": "^11.0.0",
|
||||
"esbuild": "0.25.0",
|
||||
"esbuild": "0.28.1",
|
||||
"esbuild-plugin-inline-worker": "^0.1.1",
|
||||
"esbuild-svelte": "^0.9.4",
|
||||
"eslint": "^9.39.3",
|
||||
|
||||
+1
-1
Submodule src/lib updated: 53804cbaec...5a552b3ec4
@@ -2,7 +2,7 @@ import { AbstractObsidianModule } from "../AbstractObsidianModule.ts";
|
||||
import { EVENT_FILE_RENAMED, EVENT_LEAF_ACTIVE_CHANGED, eventHub } from "../../common/events.js";
|
||||
import { LOG_LEVEL_NOTICE, LOG_LEVEL_VERBOSE } from "octagonal-wheels/common/logger";
|
||||
import { scheduleTask } from "octagonal-wheels/concurrency/task";
|
||||
import { type TFile } from "../../deps.ts";
|
||||
import type { TFile } from "../../deps.ts";
|
||||
import { fireAndForget } from "octagonal-wheels/promises";
|
||||
import { type FilePathWithPrefix } from "../../lib/src/common/types.ts";
|
||||
import { reactive, reactiveSource, type ReactiveSource } from "octagonal-wheels/dataobject/reactive";
|
||||
@@ -138,12 +138,38 @@ export class ModuleObsidianEvents extends AbstractObsidianModule {
|
||||
|
||||
await this.services.fileProcessing.commitPendingFileEvents();
|
||||
|
||||
// Desktop opt-in (LiveSync/Periodic only): keep the background channel running while the
|
||||
// window is hidden, instead of suspending on hide. On hide we skip the suspend for both
|
||||
// modes (LiveSync's continuous replication and Periodic's timer both stall otherwise);
|
||||
// becoming visible reopens normally, and for LiveSync additionally forces a teardown first
|
||||
// (see the resume branch) so a stalled continuous channel is always replaced.
|
||||
const keepActiveInBackground =
|
||||
this.settings.keepReplicationActiveInBackground &&
|
||||
(this.settings.liveSync || this.settings.periodicReplication) &&
|
||||
!this.services.API.isMobile();
|
||||
|
||||
if (isHidden) {
|
||||
await this.services.appLifecycle.onSuspending();
|
||||
if (!keepActiveInBackground) await this.services.appLifecycle.onSuspending();
|
||||
} else {
|
||||
// suspend all temporary.
|
||||
if (this.services.appLifecycle.isSuspended()) return;
|
||||
// Do not block resume by focus state here; visibility recovery should be enough.
|
||||
// Only the continuous (LiveSync) channel can go stalled-but-not-terminated: PouchDB
|
||||
// emits paused/retry while the replicator keeps its AbortController set, so the reopen
|
||||
// below would no-op on exactly the channel that needs replacing. Force a teardown first
|
||||
// so becoming visible always re-establishes a fresh channel (restoring the default's
|
||||
// reset-on-visibility). Periodic mode has no such channel — its timer just resumes via
|
||||
// the normal path below — so this teardown is gated on liveSync to avoid needlessly
|
||||
// bouncing it. The teardown's closeReplication() aborts synchronously while the reopen is
|
||||
// deferred (fireAndForget + awaited isReplicationReady/initializeDatabaseForReplication),
|
||||
// so the aborted continuousReplication run (and its shareRunningResult lock) unwinds in
|
||||
// microtasks before the reopen runs: it neither double-opens nor gets swallowed by the
|
||||
// still-registered shared run.
|
||||
if (keepActiveInBackground && this.settings.liveSync) {
|
||||
await this.services.appLifecycle.onSuspending();
|
||||
}
|
||||
// Resume is not gated on focus in this branch, but note the top-of-handler check
|
||||
// (isLastHidden && !hasFocus) still defers the whole handler when the window becomes
|
||||
// visible again while unfocused; in that case recovery happens on the next focus.
|
||||
await this.services.appLifecycle.onResuming();
|
||||
await this.services.appLifecycle.onResumed();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,166 @@
|
||||
import { describe, it, expect, vi, afterEach } from "vitest";
|
||||
|
||||
import { ModuleObsidianEvents } from "./ModuleObsidianEvents";
|
||||
import { DEFAULT_SETTINGS, REMOTE_COUCHDB } from "@lib/common/types";
|
||||
|
||||
type SetupOptions = {
|
||||
settings?: Partial<typeof DEFAULT_SETTINGS>;
|
||||
hidden: boolean;
|
||||
isLastHidden?: boolean;
|
||||
hasFocus?: boolean;
|
||||
isSuspended?: boolean;
|
||||
// Platform is read via services.API.isMobile(); default desktop (false) so the feature applies.
|
||||
isMobile?: boolean;
|
||||
};
|
||||
|
||||
function setup(opts: SetupOptions) {
|
||||
const appLifecycle = {
|
||||
isReady: vi.fn(() => true),
|
||||
isSuspended: vi.fn(() => opts.isSuspended ?? false),
|
||||
onSuspending: vi.fn(async () => true),
|
||||
onResuming: vi.fn(async () => true),
|
||||
onResumed: vi.fn(async () => true),
|
||||
};
|
||||
const fileProcessing = { commitPendingFileEvents: vi.fn(async () => true) };
|
||||
|
||||
const core = {
|
||||
_services: {
|
||||
API: {
|
||||
addLog: vi.fn(),
|
||||
addCommand: vi.fn(),
|
||||
registerWindow: vi.fn(),
|
||||
addRibbonIcon: vi.fn(),
|
||||
registerProtocolHandler: vi.fn(),
|
||||
isMobile: vi.fn(() => opts.isMobile ?? false),
|
||||
},
|
||||
setting: { saveSettingData: vi.fn(async () => undefined) },
|
||||
appLifecycle,
|
||||
fileProcessing,
|
||||
},
|
||||
settings: {
|
||||
...DEFAULT_SETTINGS,
|
||||
remoteType: REMOTE_COUCHDB,
|
||||
isConfigured: true,
|
||||
...opts.settings,
|
||||
},
|
||||
} as any;
|
||||
Object.defineProperty(core, "services", { get: () => core._services });
|
||||
|
||||
const module = new ModuleObsidianEvents({} as any, core);
|
||||
module.isLastHidden = opts.isLastHidden ?? false;
|
||||
module.hasFocus = opts.hasFocus ?? true;
|
||||
|
||||
// The handler reads `activeWindow.document.hidden`.
|
||||
(globalThis as any).activeWindow = { document: { hidden: opts.hidden } };
|
||||
|
||||
return { module, appLifecycle, fileProcessing };
|
||||
}
|
||||
|
||||
describe("watchWindowVisibilityAsync — keepReplicationActiveInBackground", () => {
|
||||
afterEach(() => {
|
||||
// The handler reads a global `activeWindow`; clear it so it doesn't leak into sibling spec
|
||||
// files running in the same worker.
|
||||
delete (globalThis as any).activeWindow;
|
||||
});
|
||||
|
||||
it("does NOT suspend on hide when enabled in LiveSync mode on the desktop app", async () => {
|
||||
const { module, appLifecycle } = setup({
|
||||
settings: { keepReplicationActiveInBackground: true, liveSync: true },
|
||||
hidden: true,
|
||||
});
|
||||
await module.watchWindowVisibilityAsync();
|
||||
expect(appLifecycle.onSuspending).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("suspends on hide by default (setting off)", async () => {
|
||||
const { module, appLifecycle } = setup({
|
||||
settings: { keepReplicationActiveInBackground: false, liveSync: true },
|
||||
hidden: true,
|
||||
});
|
||||
await module.watchWindowVisibilityAsync();
|
||||
expect(appLifecycle.onSuspending).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("forces onSuspending before the resume on becoming visible when enabled (LiveSync teardown)", async () => {
|
||||
const { module, appLifecycle } = setup({
|
||||
settings: { keepReplicationActiveInBackground: true, liveSync: true },
|
||||
hidden: false,
|
||||
isLastHidden: true, // hidden -> visible transition
|
||||
});
|
||||
await module.watchWindowVisibilityAsync();
|
||||
// Decision-logic only: on visible + enabled + LiveSync the handler calls onSuspending (the
|
||||
// forced teardown) before onResuming. The actual stalled-channel replacement is exercised by
|
||||
// the manual integration test, not here.
|
||||
expect(appLifecycle.onSuspending).toHaveBeenCalledTimes(1);
|
||||
expect(appLifecycle.onResuming).toHaveBeenCalledTimes(1);
|
||||
expect(appLifecycle.onResumed).toHaveBeenCalledTimes(1);
|
||||
expect(appLifecycle.onSuspending.mock.invocationCallOrder[0]).toBeLessThan(
|
||||
appLifecycle.onResuming.mock.invocationCallOrder[0]
|
||||
);
|
||||
});
|
||||
|
||||
it("does not force a teardown on becoming visible by default (setting off)", async () => {
|
||||
const { module, appLifecycle } = setup({
|
||||
settings: { keepReplicationActiveInBackground: false, liveSync: true },
|
||||
hidden: false,
|
||||
isLastHidden: true,
|
||||
});
|
||||
await module.watchWindowVisibilityAsync();
|
||||
expect(appLifecycle.onSuspending).not.toHaveBeenCalled();
|
||||
expect(appLifecycle.onResumed).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("does not apply in On-Events mode even if the flag is set (no scope leak)", async () => {
|
||||
const { module, appLifecycle } = setup({
|
||||
settings: {
|
||||
keepReplicationActiveInBackground: true,
|
||||
liveSync: false,
|
||||
periodicReplication: false,
|
||||
},
|
||||
hidden: true,
|
||||
});
|
||||
await module.watchWindowVisibilityAsync();
|
||||
expect(appLifecycle.onSuspending).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("does NOT suspend on hide when enabled in Periodic mode (the periodic timer also stalls otherwise)", async () => {
|
||||
const { module, appLifecycle } = setup({
|
||||
settings: {
|
||||
keepReplicationActiveInBackground: true,
|
||||
liveSync: false,
|
||||
periodicReplication: true,
|
||||
},
|
||||
hidden: true,
|
||||
});
|
||||
await module.watchWindowVisibilityAsync();
|
||||
expect(appLifecycle.onSuspending).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("does NOT force a teardown on becoming visible in Periodic mode (only the continuous channel can stall)", async () => {
|
||||
const { module, appLifecycle } = setup({
|
||||
settings: {
|
||||
keepReplicationActiveInBackground: true,
|
||||
liveSync: false,
|
||||
periodicReplication: true,
|
||||
},
|
||||
hidden: false,
|
||||
isLastHidden: true,
|
||||
});
|
||||
await module.watchWindowVisibilityAsync();
|
||||
// The teardown is gated on liveSync: a periodic timer doesn't go half-open, so bouncing it
|
||||
// on every restore would be needless churn. Resume still runs normally.
|
||||
expect(appLifecycle.onSuspending).not.toHaveBeenCalled();
|
||||
expect(appLifecycle.onResuming).toHaveBeenCalledTimes(1);
|
||||
expect(appLifecycle.onResumed).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("does not apply on mobile even if the flag is set", async () => {
|
||||
const { module, appLifecycle } = setup({
|
||||
settings: { keepReplicationActiveInBackground: true, liveSync: true },
|
||||
hidden: true,
|
||||
isMobile: true,
|
||||
});
|
||||
await module.watchWindowVisibilityAsync();
|
||||
expect(appLifecycle.onSuspending).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
@@ -189,6 +189,16 @@ export function paneSyncSettings(
|
||||
new Setting(paneEl).setClass("wizardHidden").autoWireToggle("syncOnFileOpen", { onUpdate: onlyOnNonLiveSync });
|
||||
new Setting(paneEl).setClass("wizardHidden").autoWireToggle("syncOnStart", { onUpdate: onlyOnNonLiveSync });
|
||||
new Setting(paneEl).setClass("wizardHidden").autoWireToggle("syncAfterMerge", { onUpdate: onlyOnNonLiveSync });
|
||||
// Desktop app only, and only for the sync modes that keep a background replication channel
|
||||
// (LiveSync and Periodic). Ignored on mobile, where suspending preserves battery. The
|
||||
// visibility predicate mirrors the runtime guard in ModuleObsidianEvents.
|
||||
if (!this.services.API.isMobile()) {
|
||||
new Setting(paneEl).setClass("wizardHidden").autoWireToggle("keepReplicationActiveInBackground", {
|
||||
onUpdate: visibleOnly(
|
||||
() => this.isConfiguredAs("syncMode", "LIVESYNC") || this.isConfiguredAs("syncMode", "PERIODIC")
|
||||
),
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
void addPanel(
|
||||
|
||||
+14
-1
@@ -3,7 +3,16 @@ Since 19th July, 2025 (beta1 in 0.25.0-beta1, 13th July, 2025)
|
||||
|
||||
The head note of 0.25 is now in [updates_old.md](https://github.com/vrtmrz/obsidian-livesync/blob/main/updates_old.md). Because 0.25 got a lot of updates, thankfully, compatibility is kept and we do not need breaking changes! In other words, when get enough stabled. The next version will be v1.0.0. Even though it my hope.
|
||||
|
||||
## unreleased
|
||||
## 0.25.75
|
||||
|
||||
13th June, 2026
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed an issue where using fast synchronisation caused a TypeError in some environments (#953).
|
||||
|
||||
### New features
|
||||
- Now we can configure to keep replication active in the background on desktop platforms (#939, PR #949). Thank you so much for @migsferro!
|
||||
|
||||
### Fixed (CLI, automated)
|
||||
|
||||
@@ -13,6 +22,10 @@ The head note of 0.25 is now in [updates_old.md](https://github.com/vrtmrz/obsid
|
||||
|
||||
- (CLI) Ported the remaining bash regression tests (`test-daemon-linux.sh`, `test-decoupled-vault-linux.sh`, and `test-remote-commands-linux.sh`) to Deno for cross-platform validation.
|
||||
|
||||
### Miscellaneous
|
||||
- Some dependencies have been updated.
|
||||
- Now we check the compatibility with iOS 15 in the CI tests to ensure the plugin continues to work on older iOS versions even after we upgrade some dependencies.
|
||||
|
||||
## 0.25.74
|
||||
|
||||
8th June, 2026
|
||||
|
||||
@@ -0,0 +1,363 @@
|
||||
import * as acorn from 'acorn';
|
||||
import fs from 'fs';
|
||||
|
||||
// Parse command line arguments
|
||||
const args = process.argv.slice(2);
|
||||
let file = 'main.js';
|
||||
let target = 2018;
|
||||
let ios = null;
|
||||
|
||||
// Help menu
|
||||
if (args.includes('--help') || args.includes('-h')) {
|
||||
console.log(`Usage: node utils/check-compatibility.js [options]
|
||||
Options:
|
||||
--file <path> Path to the bundle file to check (default: main.js)
|
||||
--target <year> Target ECMAScript version (default: 2018)
|
||||
--ios <version> Target iOS version (e.g. 14, 15, 16.4). Sets defaults automatically.
|
||||
--[no-]allow-dynamic-import Allow dynamic import() expressions
|
||||
--[no-]allow-bigint Allow BigInt literals
|
||||
--[no-]allow-numeric-separator Allow numeric separators (e.g. 1_000)
|
||||
--[no-]allow-class-fields Allow public/private/static class fields
|
||||
--[no-]allow-class-static-blocks Allow class static initialization blocks
|
||||
--[no-]allow-regexp-lookbehind Allow RegExp lookbehind assertions ((?<=...) / (?<!...))
|
||||
--[no-]allow-regexp-indices Allow RegExp 'd' (indices) flag
|
||||
--[no-]allow-regexp-v-flag Allow RegExp 'v' (Unicode properties) flag
|
||||
`);
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
for (let i = 0; i < args.length; i++) {
|
||||
if (args[i] === '--file' && args[i + 1]) {
|
||||
file = args[i + 1];
|
||||
i++;
|
||||
} else if (args[i] === '--target' && args[i + 1]) {
|
||||
target = parseInt(args[i + 1], 10);
|
||||
i++;
|
||||
} else if (args[i] === '--ios' && args[i + 1]) {
|
||||
ios = parseFloat(args[i + 1]);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
// Default feature flags based on target ECMA version
|
||||
let allowDynamicImport = target >= 2020;
|
||||
let allowBigInt = target >= 2020;
|
||||
let allowNumericSeparator = target >= 2021;
|
||||
let allowClassFields = target >= 2022;
|
||||
let allowClassStaticBlocks = target >= 2022;
|
||||
let allowRegexpLookbehind = target >= 2023;
|
||||
let allowRegexpIndices = target >= 2022;
|
||||
let allowRegexpVFlag = target >= 2024;
|
||||
|
||||
// Override feature flags if target iOS version is specified
|
||||
if (ios !== null) {
|
||||
// Determine a general baseline ECMA version for parser reports
|
||||
if (ios >= 16.4) target = 2022;
|
||||
else if (ios >= 15.0) target = 2021;
|
||||
else if (ios >= 14.0) target = 2020;
|
||||
else target = 2018;
|
||||
|
||||
allowDynamicImport = ios >= 11.3;
|
||||
allowBigInt = ios >= 14.0;
|
||||
allowNumericSeparator = ios >= 14.0;
|
||||
allowClassFields = ios >= 14.5;
|
||||
allowRegexpIndices = ios >= 15.0;
|
||||
allowClassStaticBlocks = ios >= 16.4;
|
||||
allowRegexpLookbehind = ios >= 16.4;
|
||||
allowRegexpVFlag = ios >= 17.0;
|
||||
}
|
||||
|
||||
// Override defaults with explicit command line options if specified
|
||||
for (let i = 0; i < args.length; i++) {
|
||||
if (args[i] === '--allow-dynamic-import') allowDynamicImport = true;
|
||||
else if (args[i] === '--no-allow-dynamic-import') allowDynamicImport = false;
|
||||
else if (args[i] === '--allow-bigint') allowBigInt = true;
|
||||
else if (args[i] === '--no-allow-bigint') allowBigInt = false;
|
||||
else if (args[i] === '--allow-numeric-separator') allowNumericSeparator = true;
|
||||
else if (args[i] === '--no-allow-numeric-separator') allowNumericSeparator = false;
|
||||
else if (args[i] === '--allow-class-fields') allowClassFields = true;
|
||||
else if (args[i] === '--no-allow-class-fields') allowClassFields = false;
|
||||
else if (args[i] === '--allow-class-static-blocks') allowClassStaticBlocks = true;
|
||||
else if (args[i] === '--no-allow-class-static-blocks') allowClassStaticBlocks = false;
|
||||
else if (args[i] === '--allow-regexp-lookbehind') allowRegexpLookbehind = true;
|
||||
else if (args[i] === '--no-allow-regexp-lookbehind') allowRegexpLookbehind = false;
|
||||
else if (args[i] === '--allow-regexp-indices') allowRegexpIndices = true;
|
||||
else if (args[i] === '--no-allow-regexp-indices') allowRegexpIndices = false;
|
||||
else if (args[i] === '--allow-regexp-v-flag') allowRegexpVFlag = true;
|
||||
else if (args[i] === '--no-allow-regexp-v-flag') allowRegexpVFlag = false;
|
||||
}
|
||||
|
||||
if (!fs.existsSync(file)) {
|
||||
console.error(`Error: File '${file}' does not exist.`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const code = fs.readFileSync(file, 'utf8');
|
||||
let ast;
|
||||
|
||||
const targetInfo = ios !== null ? `iOS ${ios}` : `ES${target}`;
|
||||
console.log(`Parsing '${file}' to inspect compatibility (target ${targetInfo})...`);
|
||||
console.log(`Rules:
|
||||
Dynamic Import: ${allowDynamicImport ? 'Allowed' : 'Prohibited'}
|
||||
BigInt: ${allowBigInt ? 'Allowed' : 'Prohibited'}
|
||||
Numeric Separators: ${allowNumericSeparator ? 'Allowed' : 'Prohibited'}
|
||||
Class Fields: ${allowClassFields ? 'Allowed' : 'Prohibited'}
|
||||
Class Static Block: ${allowClassStaticBlocks ? 'Allowed' : 'Prohibited'}
|
||||
RegExp Lookbehind: ${allowRegexpLookbehind ? 'Allowed' : 'Prohibited'}
|
||||
RegExp Indices (d): ${allowRegexpIndices ? 'Allowed' : 'Prohibited'}
|
||||
RegExp Unicode (v): ${allowRegexpVFlag ? 'Allowed' : 'Prohibited'}
|
||||
`);
|
||||
|
||||
try {
|
||||
ast = acorn.parse(code, { ecmaVersion: 'latest', sourceType: 'script' });
|
||||
} catch (err) {
|
||||
console.error(`Syntax Error: Failed to parse '${file}' due to a syntax issue:`);
|
||||
console.error(err.message);
|
||||
if (err.pos !== undefined) {
|
||||
const line = code.substring(0, err.pos).split('\n').length;
|
||||
console.error(`Location: line ${line}, character ${err.pos}`);
|
||||
const start = Math.max(0, err.pos - 50);
|
||||
const end = Math.min(code.length, err.pos + 50);
|
||||
console.error('Context around error:');
|
||||
console.error(code.substring(start, end));
|
||||
console.error(' '.repeat(err.pos - start) + '^');
|
||||
}
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Violations list
|
||||
const violations = [];
|
||||
|
||||
function hasLookbehind(pattern) {
|
||||
let index = 0;
|
||||
while (true) {
|
||||
const match = pattern.indexOf('(?<=', index);
|
||||
const match2 = pattern.indexOf('(?<!', index);
|
||||
const pos = (match !== -1 && match2 !== -1) ? Math.min(match, match2) : (match !== -1 ? match : match2);
|
||||
if (pos === -1) break;
|
||||
|
||||
let backslashes = 0;
|
||||
for (let i = pos - 1; i >= 0; i--) {
|
||||
if (pattern[i] === '\\') backslashes++;
|
||||
else break;
|
||||
}
|
||||
if (backslashes % 2 === 0) {
|
||||
return true;
|
||||
}
|
||||
index = pos + 4;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function checkNode(node) {
|
||||
if (!node || typeof node !== 'object') return;
|
||||
|
||||
if (node.type) {
|
||||
// 1. Optional catch binding (ES2019 / iOS 11.3+)
|
||||
if (node.type === 'CatchClause' && !node.param) {
|
||||
if (target < 2019 && ios === null) {
|
||||
violations.push({
|
||||
feature: 'Optional catch binding (ES2019 / iOS 11.3+)',
|
||||
pos: node.start,
|
||||
node
|
||||
});
|
||||
} else if (ios !== null && ios < 11.3) {
|
||||
violations.push({
|
||||
feature: 'Optional catch binding (iOS 11.3+)',
|
||||
pos: node.start,
|
||||
node
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Dynamic import (ES2020 / iOS 11.3+)
|
||||
if (node.type === 'ImportExpression') {
|
||||
if (!allowDynamicImport) {
|
||||
violations.push({
|
||||
feature: 'Dynamic import (ES2020 / iOS 11.3+)',
|
||||
pos: node.start,
|
||||
node
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 3. import.meta (ES2020 / iOS 11.3+)
|
||||
if (node.type === 'MetaProperty' && node.meta && node.meta.name === 'import') {
|
||||
if (!allowDynamicImport) {
|
||||
violations.push({
|
||||
feature: 'import.meta (ES2020 / iOS 11.3+)',
|
||||
pos: node.start,
|
||||
node
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 4. Optional chaining (ES2020 / iOS 13.4+)
|
||||
if (node.type === 'ChainExpression') {
|
||||
const isProhibited = ios !== null ? ios < 13.4 : target < 2020;
|
||||
if (isProhibited) {
|
||||
violations.push({
|
||||
feature: 'Optional chaining (ES2020 / iOS 13.4+)',
|
||||
pos: node.start,
|
||||
node
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 5. Nullish coalescing (ES2020 / iOS 13.4+)
|
||||
if (node.type === 'LogicalExpression' && node.operator === '??') {
|
||||
const isProhibited = ios !== null ? ios < 13.4 : target < 2020;
|
||||
if (isProhibited) {
|
||||
violations.push({
|
||||
feature: 'Nullish coalescing (ES2020 / iOS 13.4+)',
|
||||
pos: node.start,
|
||||
node
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 6. BigInt literal (ES2020 / iOS 14.0+)
|
||||
if (node.type === 'Literal' && node.bigint !== undefined) {
|
||||
if (!allowBigInt) {
|
||||
violations.push({
|
||||
feature: 'BigInt literal (ES2020 / iOS 14.0+)',
|
||||
pos: node.start,
|
||||
node
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 7. Logical assignment (ES2021 / iOS 14.0+)
|
||||
if (node.type === 'AssignmentExpression' && ['||=', '&&=', '??='].includes(node.operator)) {
|
||||
const isProhibited = ios !== null ? ios < 14.0 : target < 2021;
|
||||
if (isProhibited) {
|
||||
violations.push({
|
||||
feature: `Logical assignment operator '${node.operator}' (ES2021 / iOS 14.0+)`,
|
||||
pos: node.start,
|
||||
node
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 8. Numeric separators (ES2021 / iOS 14.0+)
|
||||
if (node.type === 'Literal' && typeof node.value === 'number' && node.raw && node.raw.includes('_')) {
|
||||
if (!allowNumericSeparator) {
|
||||
violations.push({
|
||||
feature: 'Numeric separator (ES2021 / iOS 14.0+)',
|
||||
pos: node.start,
|
||||
node
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 9. Class Fields (ES2022 / iOS 14.0+ public, iOS 14.5+ private/static)
|
||||
if (node.type === 'PropertyDefinition') {
|
||||
if (!allowClassFields) {
|
||||
const requiredVersion = node.key.type === 'PrivateIdentifier' || node.static ? 'iOS 14.5+' : 'iOS 14.0+';
|
||||
violations.push({
|
||||
feature: `Class field definition '${node.key.name || node.key.value || '#private'}' (ES2022 / ${requiredVersion})`,
|
||||
pos: node.start,
|
||||
node
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 10. Class Static Initialization Blocks (ES2022 / iOS 16.4+)
|
||||
if (node.type === 'StaticBlock') {
|
||||
if (!allowClassStaticBlocks) {
|
||||
violations.push({
|
||||
feature: 'Class static initialization block (ES2022 / iOS 16.4+)',
|
||||
pos: node.start,
|
||||
node
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 11. RegExp lookbehind assertions (ES2018 / iOS 16.4+)
|
||||
if (node.type === 'Literal' && node.regex) {
|
||||
if (!allowRegexpLookbehind && hasLookbehind(node.regex.pattern)) {
|
||||
violations.push({
|
||||
feature: 'RegExp Lookbehind assertion (iOS 16.4+)',
|
||||
pos: node.start,
|
||||
node
|
||||
});
|
||||
}
|
||||
if (!allowRegexpIndices && node.regex.flags.includes('d')) {
|
||||
violations.push({
|
||||
feature: "RegExp 'd' (indices) flag (ES2022 / iOS 15.0+)",
|
||||
pos: node.start,
|
||||
node
|
||||
});
|
||||
}
|
||||
if (!allowRegexpVFlag && node.regex.flags.includes('v')) {
|
||||
violations.push({
|
||||
feature: "RegExp 'v' (Unicode properties) flag (ES2024 / iOS 17.0+)",
|
||||
pos: node.start,
|
||||
node
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if ((node.type === 'NewExpression' || node.type === 'CallExpression') && node.callee && node.callee.name === 'RegExp') {
|
||||
if (!allowRegexpLookbehind && node.arguments[0] && node.arguments[0].type === 'Literal' && typeof node.arguments[0].value === 'string') {
|
||||
if (hasLookbehind(node.arguments[0].value)) {
|
||||
violations.push({
|
||||
feature: 'RegExp Lookbehind assertion (iOS 16.4+)',
|
||||
pos: node.start,
|
||||
node
|
||||
});
|
||||
}
|
||||
}
|
||||
if (node.arguments[1] && node.arguments[1].type === 'Literal' && typeof node.arguments[1].value === 'string') {
|
||||
const flags = node.arguments[1].value;
|
||||
if (!allowRegexpIndices && flags.includes('d')) {
|
||||
violations.push({
|
||||
feature: "RegExp 'd' (indices) flag (ES2022 / iOS 15.0+)",
|
||||
pos: node.start,
|
||||
node
|
||||
});
|
||||
}
|
||||
if (!allowRegexpVFlag && flags.includes('v')) {
|
||||
violations.push({
|
||||
feature: "RegExp 'v' (Unicode properties) flag (ES2024 / iOS 17.0+)",
|
||||
pos: node.start,
|
||||
node
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const key in node) {
|
||||
if (key === 'loc' || key === 'start' || key === 'end') continue;
|
||||
const val = node[key];
|
||||
if (Array.isArray(val)) {
|
||||
for (const child of val) {
|
||||
checkNode(child);
|
||||
}
|
||||
} else if (val && typeof val === 'object') {
|
||||
checkNode(val);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Run compatibility checks on the AST
|
||||
checkNode(ast);
|
||||
|
||||
if (violations.length > 0) {
|
||||
console.error(`\nCompatibility Check Failed: Found ${violations.length} prohibited features.`);
|
||||
violations.forEach((v, index) => {
|
||||
const line = code.substring(0, v.pos).split('\n').length;
|
||||
console.error(`\n[${index + 1}] Prohibited feature: ${v.feature}`);
|
||||
console.error(`Location: line ${line}, character ${v.pos}`);
|
||||
const start = Math.max(0, v.pos - 50);
|
||||
const end = Math.min(code.length, v.pos + 50);
|
||||
console.error('Context around feature:');
|
||||
console.error(code.substring(start, end));
|
||||
console.error(' '.repeat(v.pos - start) + '^');
|
||||
});
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
console.log(`\nCompatibility Check Passed: '${file}' matches the compatibility rules.`);
|
||||
process.exit(0);
|
||||
@@ -1,3 +1,9 @@
|
||||
/**
|
||||
* @file vitest.config.common.ts
|
||||
* @description Shared base configuration for all Vitest test environments in the project,
|
||||
* defining common resolve aliases, build defines, and plugins (svelte, inlineWorker).
|
||||
* This configuration is not executed directly, but is imported and merged by other specific configuration files.
|
||||
*/
|
||||
import { defineConfig } from "vitest/config";
|
||||
import { svelte } from "@sveltejs/vite-plugin-svelte";
|
||||
import { sveltePreprocess } from "svelte-preprocess";
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
/**
|
||||
* @file vitest.config.integration.ts
|
||||
* @description Configuration for running database integration tests in Node.js against a real CouchDB instance
|
||||
* (e.g. testing streaming changes, database connectivity, and replication status checks).
|
||||
* This is executed via `npm run test:integration` during development and is run in the GitHub Actions `unit-ci` workflow.
|
||||
*/
|
||||
import { defineConfig, mergeConfig } from "vitest/config";
|
||||
import viteConfig from "./vitest.config.common";
|
||||
|
||||
export default mergeConfig(
|
||||
viteConfig,
|
||||
defineConfig({
|
||||
resolve: {
|
||||
alias: {
|
||||
obsidian: "", // prevent accidental imports of obsidian types in integration tests
|
||||
},
|
||||
},
|
||||
test: {
|
||||
logHeapUsage: true,
|
||||
name: "integration-tests",
|
||||
include: ["**/*.integration.spec.ts", "**/*.integration.test.ts"],
|
||||
exclude: ["test/**", "src/apps/**/testdeno/**"],
|
||||
},
|
||||
})
|
||||
);
|
||||
@@ -1,3 +1,9 @@
|
||||
/**
|
||||
* @file vitest.config.p2p.ts
|
||||
* @description Configuration for running browser-based Peer-to-Peer (P2P) replication tests
|
||||
* in Playwright (Chromium) using Trystero and Nostr relays.
|
||||
* This is executed via the `npm run test:p2p` command (which runs `test/suitep2p/run-p2p-tests.sh` internally).
|
||||
*/
|
||||
import { defineConfig, mergeConfig } from "vitest/config";
|
||||
import { playwright } from "@vitest/browser-playwright";
|
||||
import viteConfig from "./vitest.config.common";
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
/**
|
||||
* @file vitest.config.rpc-unit.ts
|
||||
* @description Configuration for running RPC-specific unit tests (such as RpcRoom and transport layers) in Node.js,
|
||||
* enforcing coverage thresholds on the RPC sub-module.
|
||||
* This can be run manually to verify RPC-specific coverage, or is matched by the glob patterns in `npm run test:unit`.
|
||||
*/
|
||||
import { defineConfig, mergeConfig } from "vitest/config";
|
||||
import viteConfig from "./vitest.config.common";
|
||||
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
/**
|
||||
* @file vitest.config.ts
|
||||
* @description Configuration for running browser-based end-to-end (E2E) integration tests
|
||||
* using Playwright (Chromium) to test replication and synchronisation scenarios.
|
||||
* This is executed when running the full test suite via `npm run test` or `npm run test:full`.
|
||||
*/
|
||||
import { defineConfig, mergeConfig } from "vitest/config";
|
||||
import { playwright } from "@vitest/browser-playwright";
|
||||
import viteConfig from "./vitest.config.common";
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
/**
|
||||
* @file vitest.config.unit.ts
|
||||
* @description Configuration for running unit tests in Node.js (excluding browser harnesses, E2E, and database integration tests).
|
||||
* This is executed during local development via `npm run test:unit` (or with coverage via `npm run test:unit:coverage`), and automatically in the GitHub Actions `unit-ci` workflow.
|
||||
*/
|
||||
import { defineConfig, mergeConfig } from "vitest/config";
|
||||
import viteConfig from "./vitest.config.common";
|
||||
|
||||
|
||||
Reference in New Issue
Block a user