diff --git a/.claude/launch.json b/.claude/launch.json new file mode 100644 index 0000000..a699d6e --- /dev/null +++ b/.claude/launch.json @@ -0,0 +1,11 @@ +{ + "version": "0.0.1", + "configurations": [ + { + "name": "web", + "runtimeExecutable": "pnpm", + "runtimeArgs": ["dev"], + "port": 1420 + } + ] +} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..e3f302b --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,126 @@ +# Builds and tests the app on every desktop platform it ships to. Only Linux is used day to day, +# so this is where macOS and Windows get compiled and tested at all. +name: ci + +on: + push: + branches: [develop, main] + pull_request: + workflow_dispatch: + +permissions: + contents: read + +# a new push to a pull request makes its running check pointless; every commit on +# develop and main keeps its own run +concurrency: + group: ci-${{ github.ref }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + +jobs: + build: + name: build (${{ matrix.os }}) + strategy: + # one platform failing shouldn't hide whether the others pass + fail-fast: false + matrix: + os: [ubuntu-latest, macos-latest, windows-latest] + runs-on: ${{ matrix.os }} + timeout-minutes: 45 + + steps: + # git on the Windows runner would check out CRLF line endings + - name: Keep LF line endings + if: runner.os == 'Windows' + run: git config --global core.autocrlf false + + - uses: actions/checkout@v7 + + - name: Install the Tauri system packages + if: runner.os == 'Linux' + run: | + sudo apt-get update + sudo apt-get install -y libwebkit2gtk-4.1-dev build-essential curl wget file libxdo-dev libssl-dev libayatana-appindicator3-dev librsvg2-dev + + # Vite 8 needs Node 20.19 or 22.12 and newer + - uses: actions/setup-node@v7 + with: + node-version: 24 + + # the version comes from packageManager in package.json + - uses: pnpm/action-setup@v6 + with: + cache: true + + - uses: dtolnay/rust-toolchain@stable + with: + components: clippy + + - uses: Swatinem/rust-cache@v2 + with: + workspaces: src-tauri + + - run: pnpm install --frozen-lockfile + + - run: pnpm test:frontend --run + + # also creates dist/, which the Tauri crate embeds + - run: pnpm build + + - run: cargo test + working-directory: src-tauri + + # the 2 module_inception warnings are known, so warnings don't fail the job + - run: cargo clippy --all-targets + working-directory: src-tauri + + # the #[ignore] tests against a real MongoDB; service containers exist only on Linux runners + integration: + runs-on: ubuntu-latest + timeout-minutes: 30 + services: + mongo: + # 8.0 and 8.3 refuse to start on Linux 6.19 and newer (SERVER-121912), 8.2 runs + image: mongo:8.2 + env: + MONGO_INITDB_ROOT_USERNAME: root + MONGO_INITDB_ROOT_PASSWORD: ci-root-password + ports: + - 27017:27017 + options: >- + --health-cmd "mongosh --quiet --eval 'db.runCommand({ ping: 1 }).ok'" + --health-interval 5s + --health-timeout 5s + --health-retries 20 + env: + MONGODB_TEST_HOST: 127.0.0.1 + MONGODB_TEST_PORT: 27017 + MONGODB_TEST_USER: root + MONGODB_TEST_PASSWORD: ci-root-password + + steps: + - uses: actions/checkout@v7 + + - name: Install the Tauri system packages + run: | + sudo apt-get update + sudo apt-get install -y libwebkit2gtk-4.1-dev build-essential curl wget file libxdo-dev libssl-dev libayatana-appindicator3-dev librsvg2-dev + + - uses: dtolnay/rust-toolchain@stable + + - uses: Swatinem/rust-cache@v2 + with: + workspaces: src-tauri + + # what the tests expect, see the integration module in src-tauri/src/drivers/mongo_official_driver.rs + - name: Create the test user and data + run: >- + docker exec ${{ job.services.mongo.id }} mongosh --quiet + -u root -p ci-root-password --authenticationDatabase admin --eval ' + const shop = db.getSiblingDB("shop"); + shop.createUser({ user: "reader", pwd: "readerpw", roles: [{ role: "read", db: "shop" }] }); + shop.users.insertMany([{ name: "Ada", age: 36 }, { name: "Bob", age: 19 }]);' + + # a dev build needs no dist/, so there is no frontend build here + - run: cargo test -- --ignored + working-directory: src-tauri diff --git a/CLAUDE.md b/CLAUDE.md index edf1068..35d95c4 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -6,15 +6,16 @@ Read `docs/README.md` first. `docs/architecture/overview.md` has every IPC comma ## Repository -Single package, always use pnpm (lockfile v9). The Rust crate lives in `src-tauri`. +Single package, always use pnpm. It is pinned in `package.json` (`packageManager` and `devEngines.packageManager`, 12.17.0); a plain `pnpm` switches to that version. Build scripts of dependencies only run when `pnpm-workspace.yaml` lists them under `allowBuilds`, else `pnpm install` fails. The Rust crate lives in `src-tauri`. | Path | Content | |---|---| -| `src-tauri/src` | Rust core: `connector` (saved connections, keyring), `database` (test, connect, list, find commands), `task_manager` (cancellable tasks), `connection_handler` (driver pool), `drivers` (`DatabaseDriver` trait, MongoDB driver) | +| `src-tauri/src` | Rust core: `connector` (saved connections, keyring), `database` (test, connect, disconnect, list, find commands), `task_manager` (cancellable tasks), `connection_handler` (driver pool), `drivers` (`DatabaseDriver` trait, MongoDB driver) | | `src` | Vue 3 + Pinia + Tailwind 4 UI: `domains` (models, services, provider registry), `stores`, `components`, `composables`, `utils`, `plugins` | | `src/components/ui` | shadcn-vue components (`components.json`), generated by its CLI, then owned by the repo | | `src/utils/queryParser` | The shell-syntax query parser and its vitest tests | | `docs` | How the app is built and why (English): `architecture/overview.md`, `architecture/decisions.md` (decision log), one file per feature in `features/`. `docs/README.md` has the template of a feature file and the open issues by area | +| `.github/workflows` | `ci.yml`: tests and builds on Linux, macOS and Windows, integration tests on Linux | | `docs/plans` | Researched plans as written before the work, not updated afterwards. Open plans in `docs/plans/`, built ones in `docs/plans/implemented/` with a status note at the top; `docs/plans/README.md` lists every plan with its status. See "Keep in sync" | GitHub: `github.com/WebXIO/monolens` (public), issues there are the backlog. Branches: `develop` is current, `main` is far behind (last merged 2026-02-22). @@ -29,11 +30,12 @@ pnpm build # vue-tsc --noEmit (includes the tests), then vi pnpm tauri build # installers; runs pnpm build first cd src-tauri && cargo check -cd src-tauri && cargo test # no Rust tests exist yet +cd src-tauri && cargo test # unit tests +cd src-tauri && MONGODB_TEST_HOST=127.0.0.1 MONGODB_TEST_PORT=27017 MONGODB_TEST_USER=root MONGODB_TEST_PASSWORD=… cargo test -- --ignored # integration tests against a real MongoDB cd src-tauri && cargo clippy ``` -Needs Rust and the Tauri prerequisites (`README.md`). For a manual check run a local MongoDB, e.g. `docker run --rm -p 27017:27017 mongo:8`, and connect to `localhost:27017` without authentication. +Needs Rust and the Tauri prerequisites (`README.md`); on Ubuntu 26.04 `sudo apt install libwebkit2gtk-4.1-dev build-essential curl wget file libxdo-dev libssl-dev libayatana-appindicator3-dev librsvg2-dev`. Without them no Rust code compiles, but an old `target/` can make `cargo check` look fine. For a manual check run a local MongoDB, e.g. `docker run --rm -p 27017:27017 mongo:8.2`, and connect to `localhost:27017` without authentication. ## Architecture @@ -42,18 +44,22 @@ Needs Rust and the Tauri prerequisites (`README.md`). For a manual check run a l - `lib.rs` creates the managed state in `setup`: `AppState` (a `FileRepository` on `/connections.json` with a `CredentialService` for the keyring service `monolens-connections`), `ConnectionHandler` and `TaskManager`. Every command is listed in `generate_handler!` there. - One command per file in `/commands/.rs`, re-exported by its `mod.rs`. - Errors cross IPC as `{ kind, message, user_message }`: `DriverError` (drivers), `CommandError` (connector), `TaskError` (tasks). Each implements `Serialize` by hand. A new error variant needs a `user_message`, and the UI shows only that. +- The connection test stages exist only in `TEST_STAGES` of `mongo_official_driver.rs`: the driver reports all of them as pending first, the UI builds its list from the `test-connection-progress` events that carry its `testId`. A stage is `pending`, `passed`, `failed` or `skipped` (it could not run, e.g. without a privilege), with an optional `detail`. - Slow work is a task: a `start_*` command passes its future to `TaskManager.spawn_task` and returns the id; the UI waits with `await_task_result` and cancels with `cancel_task`. The future returns `Result`, with errors serialized through `serde_json::to_value(&e)`. Follow `start_connect.rs`. -- Credentials: `get_connections`/`get_connection` never return a password. A command that talks to MongoDB loads it with `hydrate_connection_with_credentials` right before use. The only command that hands a password to the UI is `get_connection_password` (edit dialog). -- `ConnectionHandler.get_or_connect` keeps one driver per connection id for the life of the process. Nothing removes it yet (see Gotchas). +- Credentials: `get_connections` never returns a password. A command that talks to MongoDB loads it with `ConnectorRepository::hydrate` right before use. The only command that hands a password to the UI is `get_connection_password` (edit dialog). Passwords go through the `CredentialStore` trait: `CredentialService` is the OS keyring, `MemoryCredentialStore` (tests only) keeps them in memory and can be told to fail. +- Commands that talk to MongoDB take a `connection_id`; `ConnectionHandler` loads the saved connection and its password from the repository and keeps one driver per id, shared without a lock (the mongodb client is thread-safe); the pool is not locked while a driver connects. `open` (connect and list the databases) drops a new driver again when that first command fails; `disconnect`, `update_connection` and `delete_connection` drop it too, so the next command uses the saved settings. Tests swap the driver through `ConnectionHandler::with_factory`. - `DatabaseDriver` is the seam for database work. Add a method to the trait, implement it in `MongoDbOfficialDriver`, then call it from a task command. -- `connections.json` is `{ "version": 1, "connections": { "": … } }`. A change of the stored shape needs a new `STORAGE_VERSION` and a migration in `FileRepository.load_storage`; today a version mismatch is an error. +- `connections.json` is `{ "version": 2, "connections": { "": … } }`. A connection has a `target` (`{ "kind": "Host", host, port }` or `{ "kind": "Uri", uri }`) and `options` (TLS, CA file, invalid certificates, replica set, server selection timeout; host targets only). A change of the stored shape needs a new `STORAGE_VERSION` and a migration in `repositories/migrations.rs`, called from `FileRepository.load_storage`, which keeps the old file as `connections.v.json`. +- Before storing, `models/normalize.rs` checks a connection and moves credentials out of a connection string (`models/uri.rs`); a refusal is `RepositoryError::Invalid`, whose text the UI shows. `drivers/client_options.rs` builds the driver's `ClientOptions`: the builder for a host, `ClientOptions::parse` for a string, with the user info put back in just before parsing. ### Frontend (`src`) -- `main.ts` calls `registerDomains` (creates `CommandRegistry`, `ConnectionServiceIPC`, `ConnectionRepositoryIPC` once and provides them) and then `registerPlugins` (Pinia, shortcuts). Read a service with `useDomain(Class)` in components, or `useDomain(Class, app)` in plugins. -- IPC goes through `ConnectionServiceIPC`: CRUD returns `Result` via `tryCatch`, slow commands use `abortableInvoke` with an `AbortSignal`. `TabViewQuery` and `AppSidebar` still call `invoke` directly; new code uses the service. -- Stores are Pinia setup stores (`defineStore('x', () => {...})`): `connectionStore` (connections, active connection, databases, test state), `tabsStore` (tabs, active tab, history). +- `main.ts` calls `registerDomains` (creates `CommandRegistry` and `ConnectionServiceIPC` once and provides them) and then `registerPlugins` (Pinia, shortcuts). Read a service with `useDomain(Class)` in components, or `useDomain(Class, app)` in plugins. +- IPC goes through `ConnectionServiceIPC`: CRUD returns `Result` via `tryCatch`, slow commands use `abortableInvoke` with an `AbortSignal`. Components don't call `invoke` themselves. +- Stores are Pinia setup stores (`defineStore('x', () => {...})`). `connectionStore` keeps the saved connections and one session per open connection (`sessions`: status `connecting`/`connected`, databases, cached collections); several connections can be open at once, and the grid gives way once one is connected. `disconnect(id)` closes that connection's tabs. `tabsStore` keeps the tabs (`{ connectionId, database, collection }` as context), the active tab and the history. - Errors reach the user as `toast.error(extractErrorMessage(e))` (`vue-sonner`). Don't build error boxes. +- Ask before destructive actions with `await useConfirm().confirm({ title, description, confirmLabel, destructive })`; `ConfirmDialog` in `App.vue` shows it. Never the browser's `confirm()`. +- Forms use TanStack Form (`@tanstack/vue-form`) with a zod 4 schema as validator (Standard Schema, no adapter) and shadcn-vue's `Field` components (`FieldLabel`, `FieldError`). Bind inputs through `update:model-value`. `ConnectionDialog.vue` is the example; its values, schema and conversion live in `connectionForm.ts`, tested without mounting. - Log with `useLogger('')`, never `console.*`. Never log credentials, not even at `trace`. - Models live in `src/domains//models` with an `index.ts` barrel. TypeScript models mirror the Rust structs: camelCase fields (`#[serde(rename_all = "camelCase")]`) and enum values equal to the Rust variant names. Change both sides together. - Shortcuts: add a command id in `domains/shortcuts/models/commands.ts`, register it in `config/commands.ts`, bind it in `config/default-binding.ts`. @@ -69,41 +75,42 @@ Needs Rust and the Tauri prerequisites (`README.md`). For a manual check run a l ## Testing - Every piece of logic gets unit tests, for the regular cases and every edge case you can think of: empty and missing input, boundaries, wrong kinds of values, failing collaborators (MongoDB, keyring, file system). -- Frontend: vitest, files `*.test.ts` in a `test/` folder next to the code (as `src/utils/queryParser/test/`). Pull logic out of components into composables or `utils` so it can be tested without mounting. -- Rust: `#[cfg(test)] mod tests` in the file, `cargo test`. Abstract MongoDB and the keyring behind traits where a test needs to replace them. -- Untested today, worth covering when touched: `formatCellValue`, `mergeColumnOrder` and the column sizing, `eventToKeyString` and `CommandRegistry`, `tabsStore`, `extractErrorMessage`, `abortableInvoke`; in Rust `TaskManager`, `DriverError::user_message`, `build_connection_string`, `FileRepository`. +- Frontend: vitest in happy-dom (the `test` block of `vite.config.ts`), files `*.test.ts` in a `test/` folder next to the code (as `src/utils/queryParser/test/`, `src/composables/table/test/`). Pull logic out of components into composables or `utils` so it can be tested without mounting. `src/test/setup.ts` mocks Tauri's IPC for every test (`mockIPC` answering `undefined`); a test that needs real answers calls `mockIPC` with its own handler. +- Rust: `#[cfg(test)] mod tests` in the file, `cargo test`, async tests with `#[tokio::test]`. Abstract MongoDB and the keyring behind traits where a test needs to replace them. `file_repository.rs` shows the pattern: a `TempDir` per test and a `MemoryCredentialStore`. +- Untested today, worth covering when touched: `formatCellValue`, `useColumnDragDrop`, `extractErrorMessage`, `abortableInvoke`; in Rust `TaskManager`. - `vue-tsc` type-checks the test files with `noUnusedLocals`, so an unused import in a test breaks `pnpm build`. -- End-to-end checks are manual: `pnpm tauri dev` against a local MongoDB. +- End-to-end checks are manual: `pnpm tauri dev` against a local MongoDB. For the UI alone, `.claude/launch.json` starts Vite for a browser preview; there every IPC call fails unless the page gets a stand-in on `window.__TAURI_INTERNALS__` (`invoke`, `transformCallback`) answering the commands with test data, after which the stores can be driven through `document.querySelector('#app').__vue_app__.config.globalProperties.$pinia`. ## Keep in sync - Feature change: update its file in `docs/features` (add a building step with the commit, bring "How it works" and "Limits and open points" up to date). A decision also goes into `docs/architecture/decisions.md`. - New, changed or removed Tauri command: the command table in `docs/architecture/overview.md`, `generate_handler!` in `lib.rs`, and the wrapper in `ConnectionServiceIPC`. - Rust model change: the TypeScript model in `src/domains/*/models`, and `STORAGE_VERSION` with a migration if `connections.json` changes. -- Connection test stages: the list in `mongo_official_driver.rs` and `_createPendingStages` in `connectionStore.ts`. - Plan written, its decisions agreed, or built: update its row in `docs/plans/README.md`. A built plan gets a status note at the top (date, commits, deviations, what is still open) and moves to `docs/plans/implemented/` with `git mv`; fix the links to it. - Setup or build change: update `README.md`. +- Docs changed: run `/graphify . --update` in a session and commit the new files in `graphify-out/cache/semantic/`. ## Git and GitHub - Feature branches come off `develop` and are merged through GitHub pull requests into `develop`. Names so far: `feat/` and, more recently, `feat_`, `refactor_`. `develop` goes to `main` by a pull request from time to time. - Before any push, run the `/code-review` skill on the branch and fix or settle its findings. -- Commit messages are conventional and lowercase: `feat: …`, `fix: …`, `refactor: …`, `chore: …`, `docs: …`, `test: …`. The body explains why and ends with `refers to #` where an issue exists. Do not add Claude co-author trailers. -- There is no CI. Run `pnpm test:frontend --run`, `pnpm build` and `cargo check` locally before pushing. +- Commit messages are conventional and lowercase: `feat: …`, `fix: …`, `refactor: …`, `chore: …`, `docs: …`, `test: …`. The body explains why and ends with `refers to #` where an issue exists. Commit messages name no people. Do not add Claude co-author trailers. +- CI (`.github/workflows/ci.yml`) runs `pnpm test:frontend --run`, `pnpm build`, `cargo test` and `cargo clippy` on Linux, macOS and Windows for pushes to `develop` and `main` and for every pull request, and the integration tests on Linux against a `mongo:8.2` service container. Run them locally before pushing anyway, the integration tests when the driver changed. A new integration test that needs data or users adds them to the job's "Create the test user and data" step. ## Gotchas -- `pnpm build` (and `pnpm tauri build`) fails on `develop` as of 2026-11-03: `'T' is declared but its value is never read` in `src/utils/queryParser/test/tokens.test.ts`. -- The driver pool never drops a driver. After a failed connect or an edited connection, the old MongoDB client stays until restart. Disconnect in the UI only resets frontend state. -- `TabsContainer` keys each tab's view by the *active* tab id, so views multiply and run duplicate queries. `Ctrl+Enter` runs in every mounted view (issue #42), and `CommandRegistry.unsubscribe` uses `findIndex(subscriber)`, which calls the subscriber. Details in `docs/features/tabs.md` and `shortcuts.md`. -- `TabViewQuery` queries `connectionStore.activeConnection`, not the tab's own connection, and tabs survive a switch of connection. -- The connection field `uri` holds a host. The driver always builds `mongodb://:/?authSource=`; SRV and full connection strings don't work. -- TypeScript `ConnectionType.ShardedCluster` is `"SharedCluster"`, Rust expects `ShardedCluster`. Harmless only because the UI always sends `Standalone`. -- `ConnectionServiceIPC.saveConnection`/`saveConnections` call commands that don't exist. `ConnectionRepositoryIPC` is registered and unused. -- Commands reach the keyring by downcasting the repository to `FileRepository`. Another repository type fails with "Unsupported repository type". -- The log level is `Debug` in release builds too. `ConnectionDialog` logs form values, password included, at `trace`. -- Query filters cross IPC as extended JSON. bson turns `{ $oid }`, `{ $date: "" }` and the number wrappers into BSON types; anything it can't read stays a plain object and silently matches nothing. -- On Linux the keyring needs a Secret Service (GNOME Keyring, KWallet). `keyring` is built with `vendored`, which compiles OpenSSL. +- TypeScript stays on 6.x: `vue-tsc` can't run on TypeScript 7, which no longer ships `lib/tsc`. TypeScript 6 deprecates `baseUrl`, so the tsconfig has `paths` only. +- Run tools with `pnpm exec`, not `npx`: npm refuses to run in this repo, because `devEngines` names pnpm. +- TanStack Table 9 only has the APIs of registered features. The table's features are `documentTableFeatures` in `useDocumentTable.ts`; `row.getVisibleCells()`, for example, needs `columnVisibilityFeature`. +- vue-tsc 3 breaks on a `//` inside a static attribute value (a placeholder like `mongodb+srv://...` made it report errors further down the template). +- Every tab has one view instance, kept mounted while hidden (`v-show`) and given its `tabId`; a view reads its own tab, never the active one. Subscribers of `event.execute` (`mod+enter`) must check that their tab is active. +- The log level is `Debug` in development builds and `Info` in release builds (`lib.rs`). +- Query filter, sort and projection cross IPC as extended JSON (`FindQuery`); `drivers/documents.rs` turns them into BSON (anything bson can't read stays a plain object, so the parser checks its shell functions) and turns found documents into relaxed extended JSON, with Int64 beyond ±2^53 as `{ "$numberLong": … }`. Find errors carry the server's message. +- On Linux the keyring needs a Secret Service (GNOME Keyring, KWallet). keyring 4 talks to it through `zbus`; there is no keyutils cache any more. +- `cargo clippy` reports 2 warnings, `module_inception` for `connection_handler/connection_handler.rs` and `task_manager/task_manager.rs`. Keep it at that. +- Change the pinned pnpm with `pnpm self-update ` inside the repo, never by hand, or `--frozen-lockfile` fails (pnpm records its version in the lockfile). +- The app is only run on Linux. macOS and Windows are compiled and tested in CI, but nobody has started the app there; keep platform code (`cfg!(target_os = …)`, `isMacPlatform`) small and say in the docs when something is unchecked on a platform. +- MongoDB 8.0 and 8.3 (`mongo:8` is 8.3 now) refuse to start on Linux 6.19 and newer (SERVER-121912), which includes this machine and the GitHub runners; `mongo:8.2` runs. - `gh` is not installed on this machine. The repo is public: read issues and pull requests through `https://api.github.com/repos/WebXIO/monolens/issues` and `/pulls`. ## graphify diff --git a/README.md b/README.md index 0db1b73..d72e9c3 100644 --- a/README.md +++ b/README.md @@ -18,9 +18,13 @@ Licensed under the [Apache-2.0 License](https://www.apache.org/licenses/LICENSE- Currently supported features include: -- **Database Connections** — create, save, edit, and delete MongoDB connections -- **Credential Handling** — secure storage of credentials using the OS-native keyring -- **Extensible Architecture** — designed to easily add new features, such as data querying, visualization, and advanced operations +- **Database Connections** — create, save, edit, and delete MongoDB connections, by host and port (with TLS, replica set and timeout options) or by `mongodb://` / `mongodb+srv://` connection string +- **Credential Handling** — passwords in the OS-native keyring, never in the connection file +- **Connection Test** — a staged check of reachability, authentication and server version +- **Several Connections at Once** — browse databases and collections of every open connection side by side +- **Querying** — filter, sort and projection in MongoDB shell syntax with autocomplete, paging with the total count +- **Table and JSON Views** — a virtualized table with sortable, movable, resizable columns, or formatted JSON +- **Extensible Architecture** — designed to easily add new features, such as editing data, aggregation, and import/export But there are many more to come! diff --git a/docs/README.md b/docs/README.md index 1e9c8b0..0edc39c 100644 --- a/docs/README.md +++ b/docs/README.md @@ -2,7 +2,7 @@ How to install and run Monolens is in the root [README.md](../README.md). This folder explains how it is built and why it is built that way. -The documentation was written on 2026-11-03 from `develop` (`859c252`), the commit history, the pull requests and the issues on GitHub, when the project was picked up again after a break since March 2026. +The documentation was written on 2026-11-03 from `develop` (`859c252`), the commit history, the pull requests and the issues on GitHub, when the project was picked up again after a break since March 2026. The same day the working-state round ([plans/implemented/working-state.md](plans/implemented/working-state.md)) fixed the bugs found and completed querying; the files describe the state after it. | Where | What you find there | |---|---| @@ -16,14 +16,14 @@ The documentation was written on 2026-11-03 from `develop` (`859c252`), the comm | File | Feature | |---|---| -| [connection-manager.md](features/connection-manager.md) | Saved connections: create, edit, delete, the start screen, the header dropdown, `connections.json` | +| [connection-manager.md](features/connection-manager.md) | Saved connections by host and port with options or by connection string: create, edit, delete, `connections.json` and its migration | | [credential-storage.md](features/credential-storage.md) | Passwords in the OS keyring, loaded in Rust only when connecting | -| [connection-test.md](features/connection-test.md) | The staged connection test with live progress | -| [connect-and-explorer.md](features/connect-and-explorer.md) | Connecting, the driver pool, the sidebar with databases and collections | +| [connection-test.md](features/connection-test.md) | The staged connection test with live progress, skipped stages and the server version | +| [connect-and-explorer.md](features/connect-and-explorer.md) | Several connections at once, the driver pool, the sidebar with databases and collections | | [background-tasks.md](features/background-tasks.md) | Cancellable tasks in Rust and `abortableInvoke` in the UI | | [error-handling.md](features/error-handling.md) | `kind`, `message`, `user_message`, and the toasts | -| [tabs.md](features/tabs.md) | One tab per collection, tab history, kept-alive views | -| [query-tab.md](features/query-tab.md) | Shell-style filter with autocomplete, the query parser, find and count in Rust | +| [tabs.md](features/tabs.md) | One tab per collection and connection, tab history, one kept view per tab | +| [query-tab.md](features/query-tab.md) | Filter, sort and projection in shell syntax, paging with the total, the query parser, find and count in Rust | | [document-views.md](features/document-views.md) | The virtualized table and the JSON view of the results | | [shortcuts.md](features/shortcuts.md) | The command registry and the key bindings | | [logging.md](features/logging.md) | One log for Rust and the web view | @@ -39,7 +39,6 @@ Monolens reads. Writing data, indexes, aggregation, import, export and statistic | Querying | #11 query builder, #41 queries by drag and drop, #13 export a query as code | | Connections | #40 quick connections, #45 stage types with a guard for production, #28 in-memory MongoDB | | Display | #39 table enhancements (nested objects, text size) | -| Bugs | #42 `Ctrl+Enter` runs in every tab | | Other | #14 database statistics, #18 log file and rotation, #31 i18n | The tab kinds `INDEXES`, `AGGREGATION`, `EXPORTER`, `IMPORTER` and `STATISTICS` already exist in the code as placeholders. diff --git a/docs/architecture/decisions.md b/docs/architecture/decisions.md index 7ad3e7a..ceb0833 100644 --- a/docs/architecture/decisions.md +++ b/docs/architecture/decisions.md @@ -8,6 +8,9 @@ This log was written on 2026-11-03 from the commit history, the pull requests an | Date | Decision | Why | Rejected | |---|---|---|---| +| 2026-11-03 | All packages to their latest release, except TypeScript 7 and Tauri 3 (`3493da8`, `955c08f`) | The project was picked up again; old versions would only make later updates bigger. vue-tsc can't run on TypeScript 7 (no `lib/tsc`), Tauri 3 is an alpha | Staying on the old versions; TypeScript 7 without type-checking the templates | +| 2026-11-03 | pnpm pinned to 12 in `packageManager` and `devEngines`; `pnpm-workspace.yaml` allows the build scripts of esbuild and vue-demi (`e19ec32`) | node_modules came from pnpm 10 while the machines run newer versions; the same setup as smtp-relay | No pin; pinning the installed 11.3 | +| 2026-11-03 | A `CredentialStore` trait and a driver factory in `ConnectionHandler` (`a7b4c1a`, `119bf4e`) | Repository and pool can be tested without the keyring and without MongoDB; the commands no longer downcast the repository | | | 2026-02-05 | Database access goes through a `DatabaseDriver` trait with a factory per `ConnectorKind` (`4100b21`) | Room for more drivers or a second MongoDB implementation. The commit only lists the parts | | | 2026-02-05 | Dependency injection on both sides: Tauri managed state in Rust, a provider registry (`src/domains`) with `useDomain` in Vue (`4100b21`, `aa891e0`) | Services are created once and can be swapped behind an interface (`ConnectionService`, `ConnectionRepository`, `LogStrategy`). The history records no further reason | | | 2026-02-02 | Tauri 2 with a Rust core and a Vue 3 web view | The README calls Rust and Tauri a "high-performance backend with secure system-level access", Vue and Tailwind "modern, responsive, and maintainable". The goal is a free, European alternative to proprietary tools like Studio 3T | | @@ -16,11 +19,18 @@ This log was written on 2026-11-03 from the commit history, the pull requests an | Date | Decision | Why | Rejected | |---|---|---|---| +| 2026-11-03 | Commands that talk to MongoDB take a connection id; Rust loads the saved connection and its password (`119bf4e`) | The UI's copy can be stale after an edit, and the password no longer passes through the web view for these calls | Sending the connection object as before | +| 2026-11-03 | A driver leaves the pool on disconnect, edit, delete and when its first command fails (`119bf4e`) | A driver is created before the server is contacted, so a wrong password or an edited host stuck until restart | Keeping drivers for the life of the process | +| 2026-11-03 | Connection model version 2: `target` (host and port, or a connection string) and `options` for host targets; `connectionType` dropped; v1 files migrated and kept as `connections.v1.json` (`5f6efaa`) | Connection strings and options (issue #4) were out of reach with a host field and a URL built by hand; `connectionType` was never read | Keeping `connectionType`; options for both modes, overriding the string | +| 2026-11-03 | A connection string keeps all its options; credentials in it move out on save, the password to the keyring, and go back in only in memory before the driver parses the string (`5f6efaa`) | One place per setting and no password in `connections.json`; the driver checks `authMechanism` against the user info | Storing the string as typed | +| 2026-11-03 | The driver gets `ClientOptions` instead of a URL string (`5f6efaa`) | No hand-made percent-encoding, and srv strings work | Fixing the URL building | +| 2026-11-03 | Host options: TLS, CA file, invalid certificates, replica set, server selection timeout (`5f6efaa`, `29aceb2`) | What issue #4 asked for, without client certificates | More fields (read preference, …); those go into a connection string | | 2026-02-22 | No credentials in logs. The connection URI is logged only in debug builds, with the password masked (`8d6bcac`, PR #32) | The URI carries the password | | | 2026-02-20 | The edit dialog shows the stored password; it is fetched from the keyring only when the dialog opens (`dc020e7`, issue #29). Partly reverses "credentials never reach the frontend" of PR #23 | Users wanted to see and fix the password they saved | Keeping the password write-only | | 2026-02-20 | Testing a connection is optional; a connection can be saved untested (`963da88`, issue #26) | The issue gives no reason | Requiring a successful test before saving | | 2026-02-18 | Credentials stay in Rust. `get_connections` and `get_connection` never return a password, the commands that connect load it from the keyring themselves (`00b3284`, `84839a6`, PR #23, issue #24) | Passwords should not be exposed to the web view, and the grid shouldn't hit the keyring for every connection | Hydrating passwords when listing (the first version) | | 2026-02-18 | Connections are stored as a versioned JSON object keyed by id: `{ "version": 1, "connections": { … } }` (`84839a6`, issue #24) | O(1) access by id, and a version to migrate the file when its shape changes | A plain array of connections (the first version) | +| 2026-11-03 | keyring 4 with its default `v1` mode: Keychain, Credential Manager, Secret Service. Reverses the keyutils cache of `aad2e90` | All packages were updated. keyring 4 moved its stores into separate crates; `v1` keeps the API. Passwords saved with keyring 3 stay readable (checked) | `keyring-core` with chosen store crates (keyutils plus Secret Service as before), staying on keyring 3.6 | | 2026-02-18 | On Linux the keyring uses `linux-native-sync-persistent` with `vendored` (`aad2e90`) | keyutils for fast kernel-level access during a session, Secret Service for persistence. `vendored` bundles OpenSSL, which the keyring needs on Linux | `sync-secret-service` alone (the first setting) | | 2026-02-17 | Passwords are stored in the OS keyring through the `keyring` crate (`eb8fdd9`, issue #15) | Uses the native store on Windows, macOS and Linux | Tauri Stronghold: added on 2026-02-15 (`8b54cd3`) and reverted on 2026-02-17 (`02a2c3a`). The revert says only that Stronghold "is not needed anymore" | | 2026-02-15 | The password is set on the URL with `Url::set_password`, which percent-encodes it (`8a9638a`, issue #21) | Passwords with special characters failed with "password must be URL encoded" | | @@ -30,7 +40,11 @@ This log was written on 2026-11-03 from the commit history, the pull requests an | Date | Decision | Why | Rejected | |---|---|---|---| +| 2026-11-03 | The connection test skips `serverStatus` when the user lacks the privilege, reads the version from `buildInfo`, marks a failing stage failed, and its events carry a test id from the UI (`2e7d3e1`) | A user who may only read one database failed the test although connecting worked; a failed stage stayed grey; two tests couldn't be told apart | Dropping the status stage | +| 2026-11-03 | `CredentialsUnavailable`, `ConnectionNotFound`, `StorageFailed`; a failed find carries the server's message (`119bf4e`, `5a30f74`) | A keyring failure read as a host problem; a broken query only said "check your permissions" | | +| 2026-11-03 | Logs at `Debug` in development builds, `Info` in release builds (`119bf4e`) | Release builds logged everything at debug | | | 2026-02-23 | Errors are shown as toasts (vue-sonner) instead of an error box per component (`37819e1`, issue #36) | Cleaner, less duplicated code and styling, fits the theme | The error boxes of before | +| 2026-11-03 | The connection test waits 10 s for a server unless the connection sets a timeout; connecting keeps the driver's 30 s; the waiting stage shows its seconds (`37f0aea`) | A wrong port sat at "Ping Database" for 30 s without a sign of progress | One shorter timeout for connecting too; a fixed timeout overriding the connection's | | 2026-02-21 | Testing, connecting and finding documents run as background tasks with an id. The UI waits with `await_task_result` and can `cancel_task` (`a14c8fc`, issue #27) | A slow connection froze the UI, and there was no way to give up | A timeout only | | 2026-02-21 | The connection test streams each stage as a Tauri event (`a14c8fc`) | Each stage shows when it really finishes, not all at once | | | 2026-02-20 | Every error carries `kind`, the technical `message` and a `user_message`; the UI shows the `user_message` (`b5a9455`) | Raw driver errors were long and unreadable (issue #16) | Showing the raw error | @@ -40,6 +54,12 @@ This log was written on 2026-11-03 from the commit history, the pull requests an | Date | Decision | Why | Rejected | |---|---|---|---| +| 2026-11-03 | Several connections open at once; disconnecting closes the connection's tabs; each tab belongs to its connection and shows connection and database (`afd84c7`) | Working with two servers is common; tabs used to survive a switch of connection and query the new server | Closing all tabs on switching; keeping tabs per connection while only one is open | +| 2026-11-03 | Every tab has exactly one view, keyed by its own id; `mod+enter` runs only in the active tab (`afd84c7`) | Views multiplied and ran duplicate queries, `Ctrl+Enter` ran everywhere (issue #42) | | +| 2026-11-03 | Paging, the total count, sort and projection in the query tab; paging repeats the executed query (`5a30f74`, `16b6cd7`) | Issue #12 listed them; typing must not change what the next page means | | +| 2026-11-03 | An empty filter is counted with `estimatedDocumentCount`, marked as estimate; a filter exactly (`5a30f74`) | `countDocuments` scans a big collection | Always counting exactly; no count | +| 2026-11-03 | Documents leave Rust as relaxed extended JSON, an `Int64` beyond ±2^53 as `{ "$numberLong" }` (`5a30f74`) | Readable dates in the JSON view, no silent change of large integers | Canonical extended JSON (every number wrapped) | +| 2026-11-03 | The query parser checks every shell function and produces only shapes bson reads as types; regexes as `$regularExpression` (`094ed26`) | Invalid or date-only values silently matched nothing | | | 2026-03-11 | The table renders only the visible rows (TanStack Virtual), caches formatted cells and sizes columns from a sample (`0aca486`, PR #44, issue #39) | Bigger result sets render fast | | | 2026-03-02 | Documents are shown in a table by default, with JSON as the second view. The table's column order is saved per connection, database and collection (`ead8967`) | The commit gives no reason for the default | | | 2026-03-01 | The query input is a single-line CodeMirror editor with MongoDB autocomplete (`9f677e1`) | The history gives no reason | | @@ -50,6 +70,10 @@ This log was written on 2026-11-03 from the commit history, the pull requests an | Date | Decision | Why | Rejected | |---|---|---|---| +| 2026-11-03 | Forms on TanStack Form with zod 4 and shadcn-vue's `Field` (`3c87afd`) | `@vee-validate/zod` only supports zod 3; vee-validate 5 has been a beta since 2025; shadcn-vue documents TanStack Form | vee-validate 5 beta; an adapter of our own for vee-validate 4; keeping zod 3 | +| 2026-11-03 | A `ConfirmDialog` with `useConfirm()` instead of the browser's `confirm()` (`29aceb2`) | It fits the app and will be needed for deleting data | | +| 2026-11-03 | Shortcuts on `mod` (Cmd on macOS, Ctrl elsewhere); on macOS the default menu without "Close Window" (`95a3e7b`) | Mac users expect Cmd; Cmd+W would otherwise close the app. Not checked on a Mac | | +| 2026-11-03 | The sidebar has one node per open connection with refresh, edit and disconnect; the grid gives way once a connection is connected (`afd84c7`) | Editing must work while connected; the view switches only on success (issue #27) | | | 2026-02-23 | Roboto is bundled with the app (`226eceb`, issue #30) | The font of the built app looked wrong | The system font | | 2026-02-18 | The default window is 1200 × 900 (`b35246a`) | The history gives no reason | | @@ -57,6 +81,8 @@ This log was written on 2026-11-03 from the commit history, the pull requests an | Date | Decision | Why | Rejected | |---|---|---|---| +| 2026-11-03 | CI on GitHub Actions: frontend tests, build, `cargo test` and `cargo clippy` on Linux, macOS and Windows, for pushes to `develop` and `main` and every pull request; the integration tests on Linux only, against a `mongo:8.2` service container (8.0 and 8.3 refuse to start on Linux 6.19 and newer, SERVER-121912); no installers (`b69ba0b`, `9e6cc5c`) | The app is only developed on Linux; nobody could build it on macOS or Windows. The integration tests only ran when someone remembered them, and service containers exist only on Linux runners | Building installers in CI (no signing yet); integration tests on every platform | +| 2026-11-03 | Tests: vitest in happy-dom with Tauri's `mockIPC`; Rust unit tests with an in-memory credential store and temp directories; integration tests against a real MongoDB marked `#[ignore]` | The rule "every piece of logic gets unit tests"; keyring and MongoDB can't be in the default run | Mounting components in tests (logic moved into modules instead) | | 2026-11-03 | `docs/` documents features, architecture, decisions and plans, the same way as in smtp-relay. `CLAUDE.md` holds the working rules: `/code-review` before every push, no Claude co-author trailers, unit tests for every piece of logic, lowercase conventional commits | The project is being revived. Its state had to be written down first, and work on it should follow the same rules as the other projects | | | 2026-11-03 | Claude Code gets a knowledge graph of the code and the docs through graphify. Only the stored doc extraction (`graphify-out/cache/semantic/`) is committed, each clone builds the graph itself | Same setup as smtp-relay: the cache saves the model cost of reading the docs again, and `graph.json` is rewritten whole on every rebuild | Committing `graph.json`, ignoring `graphify-out/` completely | | 2026-02-05 | Feature branches off `develop`, merged through GitHub pull requests; `develop` goes to `main` from time to time | The history records no reason | | diff --git a/docs/architecture/overview.md b/docs/architecture/overview.md index c0dd7f4..7e9f9b7 100644 --- a/docs/architecture/overview.md +++ b/docs/architecture/overview.md @@ -1,8 +1,8 @@ # Architecture overview -Monolens is an open-source MongoDB GUI for the desktop (homepage: monolens.org, Apache-2.0). It is a Tauri 2 app: a Rust core talks to MongoDB, and a Vue 3 web view shows the UI. A user saves connections, connects to one, browses its databases and collections, and opens a collection in a tab. There it filters documents with a shell-style query and sees them as a table or as JSON. The app only reads data so far. +Monolens is an open-source MongoDB GUI for the desktop (homepage: monolens.org, Apache-2.0). It is a Tauri 2 app: a Rust core talks to MongoDB, and a Vue 3 web view shows the UI. A user saves connections (host and port with options, or a connection string), opens one or several, browses their databases and collections, and opens a collection in a tab. There the user filters, sorts, projects and pages through documents and sees them as a table or as JSON. The app only reads data so far. -This file describes `develop` as of 2026-11-03 (`859c252`). `main` is at `efee3a2` (2026-02-22) and has none of the query, tab view or document display work. +This file describes the state after the working-state round of 2026-11-03 ([plans/implemented/working-state.md](../plans/implemented/working-state.md)). ## The parts @@ -10,68 +10,63 @@ This file describes `develop` as of 2026-11-03 (`859c252`). `main` is at `efee3a +------------------------------------------------------------+ | Web view (Vue 3, Pinia, Tailwind 4, shadcn-vue/reka-ui) | | ConnectionGrid / Dialog AppSidebar TabsContainer | - | connectionStore tabsStore TabViewQuery | + | connectionStore (sessions) tabsStore TabViewQuery | | CommandRegistry (shortcuts) Logger -> tauri-plugin-log | +------------------------------------------------------------+ | invoke("", args) ^ event v | test-connection-progress +------------------------------------------------------------+ | Rust core (Tauri 2, tokio) | - | connector/ CRUD of saved connections | - | database/ test, connect, list, find | + | connector/ saved connections, keyring, migration | + | database/ test, connect, disconnect, list, find | | task_manager/ cancellable background tasks | | connection_handler/ one driver per connection id | - | drivers/ DatabaseDriver trait, MongoDB driver | + | drivers/ DatabaseDriver, MongoDB, ClientOptions | +------------------------------------------------------------+ | | | v v v - connections.json OS keyring MongoDB server - (app config dir) (passwords) (mongodb crate 3.5) + connections.json OS keyring MongoDB servers + (app config dir) (passwords) (mongodb crate 3.9) ``` | Part | Technology | Path | |---|---|---| -| Rust core | Tauri 2.10, tokio, `mongodb` 3.5 (bson 2.15), `keyring` 3.6, `thiserror`, `tauri-plugin-log`, `tauri-plugin-opener` | `src-tauri/src` | -| UI | Vue 3.5, Pinia 3, Tailwind 4 with shadcn-vue components on reka-ui, vee-validate + zod, vue-sonner, lucide icons | `src` | +| Rust core | Tauri 2.12, tokio, `mongodb` 3.9 (bson 2.15), `keyring` 4.2, `percent-encoding`, `thiserror`, `tauri-plugin-log`, `tauri-plugin-opener` | `src-tauri/src` | +| UI | Vue 3.5, Pinia 4, Tailwind 4 with shadcn-vue components on reka-ui, TanStack Form + zod 4, vue-sonner, lucide icons (`@lucide/vue`) | `src` | | Query editor and JSON view | CodeMirror 6 (`@codemirror/lang-json`, `theme-one-dark`, `autocomplete`) | `src/components/query`, `src/components/documents/json` | -| Table view | TanStack Table 8 and TanStack Virtual 3 | `src/components/documents/table`, `src/composables/table` | -| Frontend tests | vitest 4 | `src/utils/queryParser/test` | +| Table view | TanStack Table 9 and TanStack Virtual 3 | `src/components/documents/table`, `src/composables/table` | +| Tests | vitest 5 in happy-dom with Tauri's IPC mock; Rust unit tests and integration tests against a real MongoDB | `test/` folders next to the code, `#[cfg(test)]` modules | | Generated UI kit | shadcn-vue (`components.json`, style `new-york`) | `src/components/ui` | +| Tooling | pnpm 12 (pinned), TypeScript 6, Vite 8, graphify | `package.json`, `.claude/` | The app is one window of 1200 × 900 (`src-tauri/tauri.conf.json`), identifier `org.monolens`. In development Vite serves the UI on port 1420 and `pnpm tauri dev` loads it. ## The Rust core -`lib.rs` builds the app. In `setup` it creates three pieces of managed state: +`lib.rs` builds the app. On macOS it replaces Tauri's default menu with one without "Close Window" (`menu.rs`). In `setup` it creates three pieces of managed state: -- `AppState` with the connection repository: a `FileRepository` on `/connections.json`, with a `CredentialService` for the service name `monolens-connections`. +- `AppState` with the connection repository: a `FileRepository` on `/connections.json`, with a `CredentialService` (the keyring, service `monolens-connections`) behind the `CredentialStore` trait. - `ConnectionHandler`: a map from connection id to a shared `DatabaseDriver`. - `TaskManager`: background tasks by uuid. -The log plugin is set to `Debug` for the whole app. +The log plugin writes at `Debug` in development builds and `Info` in release builds. ### Commands -Every call from the UI is a Tauri command. These are all of them: - -| Command | Module | What it does | Used by the UI | -|---|---|---|---| -| `get_connections` | connector | List saved connections, without passwords | yes | -| `get_connection` | connector | One saved connection | wrapped in `ConnectionServiceIPC`, never called | -| `create_connection` | connector | Save a new connection, password into the keyring | yes | -| `update_connection` | connector | Replace a connection, update or delete its keyring entry | yes | -| `delete_connection` | connector | Remove a connection and its keyring entry | yes | -| `get_connection_password` | connector | Read the password from the keyring | edit dialog | -| `start_test_connection` | database | Test a connection as a task, streams `test-connection-progress` events | yes | -| `start_connect` | database | Connect as a task and list the databases | yes | -| `list_collections` | database | Collection names of one database | sidebar | -| `start_find_documents` | database | Find with filter, skip and limit as a task, plus a count | query tab | -| `cancel_task` | task_manager | Abort a task | yes | -| `await_task_result` | task_manager | Wait for a task and return its result | yes | -| `test_connection` | database | Synchronous test, the version before tasks existed | no | -| `get_databases` | database | Synchronous connect and list, the version before tasks existed | no | - -`ConnectionServiceIPC` also wraps `save_connection` and `save_connections`. Those commands do not exist in Rust any more (removed in `8a9638a`), so the wrappers would fail. Nothing calls them. +| Command | Module | What it does | +|---|---|---| +| `get_connections` | connector | List saved connections, without passwords | +| `create_connection` | connector | Check and save a new connection; password and credentials of a connection string into the keyring | +| `update_connection` | connector | Check and replace a connection, update or delete its password, drop its driver | +| `delete_connection` | connector | Drop its driver, remove the connection and its password | +| `get_connection_password` | connector | Read the password from the keyring (edit dialog) | +| `start_test_connection(connection, testId)` | database | Test form data as a task, streams `test-connection-progress` events with the test id | +| `start_connect(connectionId)` | database | Build the driver from the saved connection and list the databases, as a task | +| `disconnect(connectionId)` | database | Drop the driver | +| `list_collections(connectionId, databaseName)` | database | Collection names of one database | +| `start_find_documents(connectionId, query)` | database | Find with filter, sort, projection, skip and limit, plus the count, as a task | +| `cancel_task` | task_manager | Abort a task | +| `await_task_result` | task_manager | Wait for a task and return its result | ### Long-running work @@ -79,40 +74,40 @@ Every call from the UI is a Tauri command. These are all of them: ### Drivers and the connection pool -`DatabaseDriver` is the trait every database driver implements: `connect`, `disconnect`, `test_connection`, `list_databases`, `list_collections`, `find_documents`. `DatabaseDriverFactory` picks the implementation by `ConnectorKind`. There is one kind, `MongoDb`, and one driver, `MongoDbOfficialDriver`. +`DatabaseDriver` is the trait every database driver implements: `connect`, `disconnect`, `test_connection`, `list_databases`, `list_collections`, `find_documents`. `DatabaseDriverFactory` picks the implementation by `ConnectorKind`; there is one, `MongoDbOfficialDriver`. It builds its client from `ClientOptions` (`drivers/client_options.rs`) and converts between JSON and BSON in `drivers/documents.rs`. -`ConnectionHandler.get_or_connect` returns the driver for a connection id, creating and connecting it on first use. The driver stays in the map for the rest of the process: `ConnectionHandler.disconnect` exists, but no command calls it. See [connect-and-explorer.md](../features/connect-and-explorer.md#limits-and-open-points). +`ConnectionHandler.get_or_connect(repository, id)` returns the driver of a connection, loading the saved connection and its password and connecting it on first use. A driver leaves the pool on `disconnect`, on update or delete of its connection, and when its first command fails. See [connect-and-explorer.md](../features/connect-and-explorer.md). ## The UI - `main.ts` registers the domains (`src/domains/index.ts`), then the plugins (Pinia, then the shortcut plugin), and mounts `App.vue`. -- `src/domains` is a small provider registry. `registerDomains` creates `CommandRegistry`, `ConnectionServiceIPC` and `ConnectionRepositoryIPC` once and provides each under `$providers.`. `useDomain(Class)` reads one back through `inject`, or through `app.config.globalProperties` when given the app (plugins run outside components). The repository is registered but nothing uses it. +- `src/domains` is a small provider registry. `registerDomains` creates `CommandRegistry` and `ConnectionServiceIPC` once and provides each under `$providers.`. `useDomain(Class)` reads one back through `inject`, or through `app.config.globalProperties` when given the app (plugins run outside components). - The stores are Pinia setup stores: - - `connectionStore` holds the saved connections, the active connection, its database names, and the state of the connection test. - - `tabsStore` holds the open tabs and their history. -- `App.vue` shows `ConnectionGrid` while no connection is active. Once one is, it shows `AppSidebar` (the explorer) and `TabsContainer`. -- Errors reach the user as `vue-sonner` toasts (`` in `App.vue`). + - `connectionStore` holds the saved connections, one session per open connection (status, databases, cached collections) and the state of the connection test. + - `tabsStore` holds the open tabs, each with its connection, database and collection, and their history. +- `App.vue` shows `ConnectionGrid` while no connection is open. Once one is connected, it shows `AppSidebar` (one node per open connection) and `TabsContainer`. +- Errors reach the user as `vue-sonner` toasts; destructive actions ask through `ConfirmDialog` (`useConfirm`). ## How a query travels 1. The user clicks a collection in the sidebar. `tabsStore` finds the open query tab for that connection, database and collection, or adds one, and switches to it. -2. `TabViewQuery` mounts and runs `execute()` with the default filter `{ }` and limit 50. -3. `parseMongoDbQuery` turns the shell-style text into a plain object. Shell functions become extended-JSON objects, `ObjectId("…")` becomes `{ $oid: "…" }`. A parse error is a toast and nothing is sent. -4. The view calls `start_find_documents` with the active connection, the tab's database and collection, the filter, `skip: 0` and the limit, then waits on `await_task_result`. -5. In Rust the task loads the password from the keyring if the connection uses authentication, gets the pooled driver, and runs `find` with `skip`, `limit` and `allowDiskUse`. bson's serializer turns the extended-JSON objects of the filter back into BSON types. A second call, `count_documents`, counts all matches. -6. The result is `{ documents, totalCount }`. Each document is turned into JSON by serde (`ObjectId` as `{ "$oid": … }`, dates as `{ "$date": { "$numberLong": … } }`). -7. The view shows the documents in the table or the JSON view. The field names of the first document feed the autocomplete of the query input. +2. The tab's `TabViewQuery` mounts and runs its query: filter `{ }`, no sort or projection, skip 0, limit 50. +3. `parseQueryTexts` parses filter, sort and projection with the shell-syntax parser. Shell functions become extended-JSON objects, `ObjectId("…")` becomes `{ $oid: "…" }`; a parse error names the input and nothing is sent. +4. The view calls `ConnectionServiceIPC.findDocuments(connectionId, query)`, which starts `start_find_documents` and waits on `await_task_result`; a new query or Cancel aborts it. +5. In Rust the task gets the connection's driver from the pool (building it with the password if needed). The driver turns filter, sort and projection into BSON, runs `find` with skip, limit and `allowDiskUse`, and at the same time the count: estimated for an empty filter, exact otherwise. +6. The result is `{ documents, totalCount, totalCountEstimated }`. Documents come as relaxed extended JSON (dates as ISO strings, a large `Int64` as `{ "$numberLong" }`). +7. The view shows the documents in the table or the JSON view and "1–50 of 137". Paging repeats the executed query with another skip. ## Data the app keeps | What | Where | Written by | |---|---|---| -| Saved connections, without passwords | `/connections.json`, on Linux `~/.config/org.monolens/connections.json`: `{ "version": 1, "connections": { "": {…} } }` | `FileRepository` | +| Saved connections, without passwords | `/connections.json`, on Linux `~/.config/org.monolens/connections.json`: `{ "version": 2, "connections": { "": {…} } }`. A migrated version 1 file stays next to it as `connections.v1.json` | `FileRepository` | | Passwords of connections with authentication | OS keyring, service `monolens-connections`, user `connection::basic_password` | `CredentialService` | | Column order of the table view | Web view `localStorage`, key `monolens:columnOrder:::` | `useDocumentTable` | -| Logs | stdout and the log directory of `tauri-plugin-log` (its default targets), on Linux under `~/.local/share/org.monolens/logs` | Rust `log` macros and the UI's `Logger` | +| Logs | stdout and the log directory of `tauri-plugin-log`, on Linux under `~/.local/share/org.monolens/logs` | Rust `log` macros and the UI's `Logger` | -Open tabs, queries and results live only in memory. +Open connections, tabs, queries and results live only in memory. ## Errors @@ -120,7 +115,7 @@ Every error that crosses IPC has the same three fields: | Field | Meaning | |---|---| -| `kind` | The variant, e.g. `PingFailed`, `repository`, `Cancelled` | +| `kind` | The variant, e.g. `PingFailed`, `FindDocumentsFailed`, `invalid`, `Cancelled` | | `message` | The technical message | | `user_message` | A short text for the user, e.g. "Authentication failed. Check your username, password, and authentication database." | @@ -129,22 +124,27 @@ Every error that crosses IPC has the same three fields: ## Building and running ```bash -pnpm install +pnpm install # pnpm 12.17.0, pinned; a plain pnpm switches to it pnpm tauri dev # vite on :1420 plus the Rust app -pnpm test:frontend # vitest +pnpm test:frontend --run pnpm build # vue-tsc --noEmit, then vite build (also the beforeBuildCommand of `tauri build`) pnpm tauri build # installers for the current platform +cd src-tauri && cargo test # unit tests +cd src-tauri && MONGODB_TEST_HOST=… MONGODB_TEST_PORT=… MONGODB_TEST_USER=… MONGODB_TEST_PASSWORD=… cargo test -- --ignored ``` -Rust and the Tauri prerequisites must be installed (see the root README). `7ee4b70` (2026-02-15) notes that the build worked for deb, rpm and AppImage. +Rust and the Tauri prerequisites must be installed (see the root README). On Ubuntu 26.04: `sudo apt install libwebkit2gtk-4.1-dev build-essential curl wget file libxdo-dev libssl-dev libayatana-appindicator3-dev librsvg2-dev`. `7ee4b70` (2026-02-15) notes that the build worked for deb, rpm and AppImage. + +Checked on 2026-11-03 after the working-state round, Node 24.9, pnpm 12.17, Rust 1.93: -Checked on 2026-11-03 on `develop`, Node 24.9, pnpm 11.3, Rust 1.93: +- `pnpm test:frontend --run`: 141 tests pass. +- `pnpm build` passes. +- `cargo test`: 81 unit tests pass; the 11 integration tests pass against a `mongo:8` container (root, a user with `read` on one database). +- `cargo clippy`: 2 warnings (`module_inception`). +- Not checked: `pnpm tauri build`, the app itself in a Tauri window, `mongodb+srv://` (needs DNS records), TLS against a server, macOS and Windows. -- `pnpm test:frontend --run`: 3 files, 27 tests pass. -- `cargo check`: passes without warnings. -- `pnpm build` fails: `src/utils/queryParser/test/tokens.test.ts(3,1): error TS6133: 'T' is declared but its value is never read.` The tsconfig includes the test files and sets `noUnusedLocals`. `vite build` alone passes. So `pnpm tauri build` fails too until the import is removed. -- There are no Rust tests and no CI. +Before the round (`859c252`): 27 tests passed, `pnpm build` failed on an unused import in a test, and `cargo check` only seemed to pass because it reused build results from March: the Tauri prerequisites were not installed on the machine. ## Branches -The repository is `github.com/WebXIO/monolens`. Feature branches come off `develop` and are merged through GitHub pull requests. Branch names mix `feat/` and `feat_`. `develop` was merged into `main` once, in PR #35 on 2026-02-22. The 16 commits after it are only on `develop`. +The repository is `github.com/WebXIO/monolens`. Feature branches come off `develop` and are merged through GitHub pull requests. `develop` was merged into `main` once, in PR #35 on 2026-02-22; everything after it is only on `develop`. diff --git a/docs/features/app-shell.md b/docs/features/app-shell.md index 06d2793..f6a7de4 100644 --- a/docs/features/app-shell.md +++ b/docs/features/app-shell.md @@ -2,25 +2,29 @@ ## Purpose -The frame around the features: the window, the layout, the theme, the font and the icons. +The frame around the features: the window, the layout, the theme, the font, the icons and the packages it is built from. ## How it works -- **Window**: one window titled "Monolens", 1200 × 900, app identifier `org.monolens`, version 0.1.0. `tauri.conf.json` sets no CSP (`"csp": null`). Bundling covers all targets of the platform. +- **Window**: one window titled "Monolens", 1200 × 900, app identifier `org.monolens`, version 0.1.0. `tauri.conf.json` sets no CSP (`"csp": null`). Bundling covers all targets of the platform. On macOS the app builds its own menu, see [shortcuts.md](shortcuts.md). - **Layout** (`App.vue`): - - A header with the logo and, while connected, the connection dropdown. It stays at the top while the data scrolls. - - Below it either `ConnectionGrid` (no connection active) or `AppSidebar` next to `TabsContainer`, inside shadcn-vue's `SidebarProvider` and `SidebarInset`. -- **Theme**: dark only. The palette in `src/styles/default.css` is set on `:root` in OKLCH, with a green primary and dark greys. It also defines `success`, `warning` and `info`, which the default shadcn palette lacks. A `.dark` block exists but no element gets the class. Tailwind 4 through `@tailwindcss/vite`, animations from `tw-animate-css`. -- **Components**: shadcn-vue (`components.json`: style `new-york`, base colour `neutral`, CSS variables, lucide icons) in `src/components/ui/`, built on reka-ui. They are generated with the shadcn-vue CLI and then belong to the repo. + - A header with the logo and, while a connection is open, the "Connections" dropdown. It stays at the top while the data scrolls. + - Below it either `ConnectionGrid` (no connection open) or `AppSidebar` next to `TabsContainer`, inside shadcn-vue's `SidebarProvider` and `SidebarInset`. + - The toasts and one `ConfirmDialog` for the whole app. +- **Theme**: dark only. The palette in `src/styles/default.css` is set on `:root` in OKLCH, with a green primary and dark greys, plus `success`, `warning` and `info`. A `.dark` block exists but no element gets the class. Tailwind 4 through `@tailwindcss/vite`, animations from `tw-animate-css`. +- **Components**: shadcn-vue (`components.json`: style `new-york`, base colour `neutral`, CSS variables, lucide icons through `@lucide/vue`) in `src/components/ui/`, built on reka-ui; `field` comes from the `new-york-v4` registry. They are generated with the shadcn-vue CLI and then belong to the repo. - **Font**: Roboto 400, 500 and 700, bundled as TTF in `src/assets/fonts/roboto` (OFL licence next to it). -- **Icons**: generated with `pnpm tauri icon` from the Monolens logo (`monolens.png` in the root) for desktop, iOS and Android, later with a background. +- **Icons**: generated with `pnpm tauri icon` from the Monolens logo (`monolens.png` in the root) for desktop, iOS and Android. - **Toasts**: `vue-sonner`, dark, top centre, rich colours, see [error-handling.md](error-handling.md). +- **Packages** (2026-11-03): Tauri 2.12, Vue 3.5, Pinia 4, Vite 8, TypeScript 6 (vue-tsc can't run on 7), vitest 5 in happy-dom, TanStack Table 9 and Form 1, zod 4, pnpm 12 pinned in `package.json`. ## Decisions - **Bundled Roboto** (`226eceb`, issue #30): the built app looked wrong with the system font. - **1200 × 900** (`b35246a`): the history gives no reason. - **shadcn-vue on Tailwind 4**: the history gives no reason. The README names Vue 3 and Tailwind. +- **All packages to their latest release** (`3493da8`, `955c08f`), except TypeScript 7 (vue-tsc can't run on it) and Tauri 3 (an alpha). `lucide-vue-next` is deprecated and was replaced by `@lucide/vue`. +- **pnpm pinned** (`e19ec32`) in `packageManager` and `devEngines`, as in smtp-relay. pnpm 12 refuses build scripts that `pnpm-workspace.yaml` doesn't allow. ## Building steps @@ -34,23 +38,31 @@ The frame around the features: the window, the layout, the theme, the font and t | 2026-02-21 | New styling of the connection screens, stepper | `c780905`, `9229c45` | | 2026-02-23 | Toasts, Roboto | `37819e1`, `226eceb` | | 2026-03-02 | Sticky header | `0a88369` | +| 2026-11-03 | `pnpm build` works again (an unused import in a test broke it) | `c25ace4` | +| 2026-11-03 | pnpm 12 pinned; npm packages and Rust crates updated | `e19ec32`, `3493da8`, `955c08f` | +| 2026-11-03 | macOS menu without "Close Window" | `95a3e7b` | +| 2026-11-03 | Confirmation dialog | `29aceb2` | +| 2026-11-03 | Template leftovers removed: Vite favicon and SVGs, title "Monolens" | `d5ff9e3` | +| 2026-11-03 | Vite config on `import.meta.dirname` | `3c38991` | +| 2026-11-03 | CI: tests and build on Linux, macOS and Windows | `b69ba0b` | ## Limits and open points -- `pnpm build`, and with it `pnpm tauri build`, fails on `develop`: an unused import in `src/utils/queryParser/test/tokens.test.ts` (TS6133). See [overview.md](../architecture/overview.md#building-and-running). -- No CI, no release pipeline, no code signing, no auto-update. +- CI tests and builds on Linux, macOS and Windows, but the app has only been run on Linux. On macOS the Keychain may ask for access again after every rebuild of an unsigned app, and on Windows the web view may keep browser keys such as Ctrl+R (reload, which closes all tabs); both are unchecked. +- No release pipeline, no code signing, no auto-update. - No CSP. The web view only loads the bundled UI, but a CSP would limit the damage of injected content. - Dark theme only. No settings page. -- Leftovers of the template: `index.html` uses `/vite.svg` as favicon and the title `monolens`. `public/vite.svg`, `public/tauri.svg` and `src/assets/vue.svg` are unused. `tauri-plugin-opener` is registered but the UI doesn't use it. -- The root README lists only connections and credentials as features. +- `tauri-plugin-opener` is registered but the UI doesn't use it yet. - Open issues: #31 i18n. ## Where the code is | What | Path | |---|---| -| Window and bundle | `src-tauri/tauri.conf.json`, `src-tauri/capabilities/default.json` | -| Layout | `src/App.vue`, `src/components/Header.vue`, `src/components/Logo.vue` | +| Window, bundle, menu | `src-tauri/tauri.conf.json`, `src-tauri/capabilities/default.json`, `src-tauri/src/menu.rs` | +| CI | `.github/workflows/ci.yml` | +| Layout | `src/App.vue`, `src/components/Header.vue`, `src/components/Logo.vue`, `src/components/ConfirmDialog.vue` | | Theme and font | `src/styles/default.css`, `src/assets/fonts/roboto/` | | UI kit | `src/components/ui/`, `components.json`, `src/lib/utils.ts` | +| Packages | `package.json`, `pnpm-workspace.yaml`, `src-tauri/Cargo.toml` | | Icons | `src-tauri/icons/`, `monolens.png` | diff --git a/docs/features/background-tasks.md b/docs/features/background-tasks.md index c733080..0f49267 100644 --- a/docs/features/background-tasks.md +++ b/docs/features/background-tasks.md @@ -2,7 +2,7 @@ ## Purpose -Testing, connecting and finding documents can take long, up to the driver's server selection timeout. They run as background tasks in Rust, so the UI stays responsive and the user can cancel them. +Testing, connecting and finding documents can take long, up to the server selection timeout. They run as background tasks in Rust, so the UI stays responsive and the user can cancel them. ## How it works @@ -25,9 +25,9 @@ The commands that use it are `start_test_connection`, `start_connect` and `start - `abortableInvoke(startCommand, args, { signal })` invokes the start command, then `await_task_result`. If the `AbortSignal` fires in between, it calls `cancel_task`. - Errors are unwrapped: - an aborted signal or kind `Cancelled` throws `TaskCancelledError` - - kind `Failed` throws the original error, parsed back from `message`, so the caller gets the driver's `user_message` + - kind `Failed` throws the original error, parsed back from `message`, so the caller gets its `user_message` - anything else is rethrown as it is -- `ConnectionServiceIPC.testConnection`, `connect` and `findDocuments` use it. The store keeps one `AbortController` for the test and one for connecting; Cancel calls `abort()` on it. +- `ConnectionServiceIPC.testConnection`, `connect` and `findDocuments` use it. The store keeps one `AbortController` for the test and one per connecting connection; the query tab keeps one per tab, and a new query cancels the one still running. ## Decisions @@ -40,13 +40,13 @@ The commands that use it are `start_test_connection`, `start_connect` and `start |---|---|---| | 2026-02-21 | `TaskManager`, `cancel_task`, `await_task_result`, `abortableInvoke`; test and connect as tasks | `a14c8fc` | | 2026-02-23 | `start_find_documents` as a task | `cbc9b83` | +| 2026-11-03 | The query tab goes through `abortableInvoke` and can be cancelled | `16b6cd7` | ## Limits and open points -- **The query tab doesn't use it.** `TabViewQuery` invokes `start_find_documents` and `await_task_result` directly instead of `ConnectionServiceIPC.findDocuments`. A query can't be cancelled, and its errors aren't unwrapped (see [query-tab.md](query-tab.md#limits-and-open-points)). - A task that is never awaited stays in both maps. Today every start is awaited right after. - Aborting drops the future. A MongoDB operation that already reached the server is not killed on the server. -- No tests for the task manager. +- No tests for `TaskManager` and `abortableInvoke` yet (the IPC wrappers above them are tested). ## Where the code is @@ -56,4 +56,4 @@ The commands that use it are `start_test_connection`, `start_connect` and `start | Commands | `src-tauri/src/task_manager/commands.rs` | | Error | `src-tauri/src/task_manager/task_error.rs` | | UI helper | `src/composables/useAbortableCommand.ts` | -| Callers | `src/domains/connections/services/ConnectionServiceIPC.ts`, `src/stores/connectionStore.ts` | +| Callers | `src/domains/connections/services/ConnectionServiceIPC.ts`, `src/stores/connectionStore.ts`, `src/components/tabs/views/TabViewQuery.vue` | diff --git a/docs/features/connect-and-explorer.md b/docs/features/connect-and-explorer.md index 757f687..f8c36f3 100644 --- a/docs/features/connect-and-explorer.md +++ b/docs/features/connect-and-explorer.md @@ -2,36 +2,45 @@ ## Purpose -Connecting opens a saved connection. The sidebar then shows the server's databases and their collections, and a click on a collection opens it in a query tab. +Connecting opens a saved connection; several can be open at once. The sidebar shows each open connection with its databases and collections, and a click on a collection opens it in a query tab. ## How it works **Connecting** -1. The user clicks Connect on a card of the grid, or picks a connection in the header dropdown. -2. `connectionStore.connectTo` sets `isDatabasesLoading`, creates an `AbortController` and calls `ConnectionServiceIPC.connect`. That runs `start_connect` as a task, see [background-tasks.md](background-tasks.md). -3. The Rust task loads the password from the keyring and calls `ConnectionHandler.get_or_connect`: - - If the pool already has a driver for this connection id, it is returned as it is. - - Otherwise the factory creates one, `connect` builds the MongoDB `Client`, and the driver goes into the pool. -4. The task lists the database names. The `Client` connects lazily, so this is the first time the server is contacted. -5. On success the store sets `databases` and `activeConnectionId`, and `App.vue` switches from the grid to the sidebar and the tabs. A cancel is only logged. Any other error is a toast. +1. The user clicks Connect on a card of the grid, or picks a connection in the header dropdown "Connections". +2. `connectionStore.connectTo` adds a session for the connection (status `connecting`) and calls `ConnectionServiceIPC.connect(id)`, which runs `start_connect(connectionId)` as a task, see [background-tasks.md](background-tasks.md). +3. In Rust, `ConnectionHandler.open` loads the saved connection and its password from the repository, builds a driver from `ClientOptions` (see [connection-manager.md](connection-manager.md)), puts it in the pool and lists the databases. The client connects lazily, so listing is the first contact with the server. If it fails (wrong password, host down), the driver leaves the pool again. +4. On success the session holds the databases and turns `connected`. The grid gives way to the sidebar and the tabs once the first connection is connected, not while it connects (issue #27). A cancel removes the session; any other error removes it and shows a toast. -While connecting, the grid card shows Cancel and the dropdown shows the name being connected, with an X to cancel. All other connect buttons are disabled. +**Several connections** + +- `connectionStore.sessions` has one entry per open connection: status, databases, the collections of every database opened so far, and whether it is refreshing. Opening an open connection again does nothing. +- The header dropdown lists all saved connections with a dot for open ones and a spinner for connecting ones, and opens the others next to them. **The explorer** (`AppSidebar`) -- The header row "Explorer" has Refresh and Disconnect. -- Refresh runs `connectTo` again for the active connection. It gets the same pooled driver and lists the databases again. -- A click on a database expands or collapses it. The first expand calls `list_collections` and keeps the names for this session; collapsing and expanding again uses them. An error is only logged. -- A click on a collection opens its query tab, or switches to it if one is open for the same connection, database and collection. See [tabs.md](tabs.md). -- The collection of the active tab is highlighted. When the active tab changes, its database is expanded. -- Disconnect, the button or `Ctrl+Shift+D`, only resets the UI: `activeConnectionId` and `databases` are cleared and the grid shows again. The Rust driver stays in the pool, and the open tabs stay in `tabsStore`. +- One node per open connection, in the order they were opened, with its name and three buttons: refresh, edit, disconnect. While it connects, a spinner and a cancel button. +- Refresh lists the databases again and forgets the cached collection names. +- Edit opens the connection dialog. After saving, the Rust core has dropped the old driver and the connection reconnects with the new settings; its tabs stay. +- Disconnect, or `Mod+Shift+D` for the active tab's connection, closes the connection's tabs, removes its session and calls `disconnect(connectionId)`, which drops the driver. +- A click on a database expands it. The first expand calls `list_collections(connectionId, database)`, which builds the driver with the password if needed; errors are toasts. The names stay cached until refresh. +- A click on a collection opens its query tab, or switches to the one open for the same connection, database and collection. See [tabs.md](tabs.md). +- The collection of the active tab is highlighted; when the active tab changes, its connection and database open. + +**The driver pool** (`ConnectionHandler`) + +- One driver per connection id, shared without a lock: the mongodb client runs several commands of one connection at the same time, so a slow query in one tab doesn't hold up another tab or the sidebar. The pool is only locked to look up and insert, not while a driver loads its password and connects. +- A driver leaves the pool on `disconnect`, when its connection is edited or deleted, and when its first command fails. The next command builds it again from the saved settings. +- `with_factory` lets tests use a fake driver. ## Decisions -- **One driver per connection id for the whole process** (`1f358d4`). The MongoDB client keeps its own connection pool, so later commands reuse it. -- **Connecting is a cancellable task** (`a14c8fc`, `571e414`, issue #27). A slow or unreachable server froze the UI before. -- **Connect lists the databases at once**, so the sidebar can be drawn when the switch happens and nothing changes before the connection works (issue #27). +- **One driver per connection id** (`1f358d4`), reused across commands. **It is dropped when it may be wrong** (`119bf4e`): a driver is created before the server is contacted, so a wrong password or an edited host otherwise stuck until restart (issue #25 described the symptom). +- **Commands take the connection id** (`119bf4e`): Rust loads the saved connection and password itself. +- **Connecting is a cancellable task** (`a14c8fc`, `571e414`, issue #27), and the grid stays until the connection works. +- **Several connections at once** (`afd84c7`): working with two servers, e.g. staging and local, is common. +- **Disconnecting closes the connection's tabs** (`afd84c7`): a tab without its connection has nothing to show; before, tabs survived and queried whichever server was active. ## Building steps @@ -41,30 +50,26 @@ While connecting, the grid card shows Cancel and the dropdown shows the name bei | 2026-02-11 | Connect lists the databases, sidebar with Refresh | `01eb47b` | | 2026-02-11 | Collections of a database (`list_collections`) | `140963a` | | 2026-02-15 | Disconnect button | `09d7e1f` | -| 2026-02-17 | Disconnect shortcut `Ctrl+Shift+D` | `748c02a` | +| 2026-02-17 | Disconnect shortcut | `748c02a` | | 2026-02-18 | Password loaded only when connecting | `00b3284` | | 2026-02-21 | Connect as a cancellable task, cancel in the grid and in the dropdown | `a14c8fc`, `571e414` | | 2026-02-22 | Collection of the active tab highlighted, its database expanded | `fdf801a` | +| 2026-11-03 | Commands by connection id, the pool drops drivers, `disconnect` command, errors of `list_collections` shown | `119bf4e` | +| 2026-11-03 | Several connections at once, sidebar node per connection with refresh, edit and disconnect | `afd84c7` | ## Limits and open points -Read from the code on 2026-11-03, not reproduced in the running app: - -- **The pool never lets go of a driver.** `ConnectionHandler.disconnect` exists, but no command calls it. A driver is put in the pool before the server was ever contacted, and later calls get it back unchanged. So after a connect with a wrong password, or after editing host, port or password of a connection used in this session, the old client stays until the app restarts. Issue #25 (closed) described the same symptom: "broken forever". -- **Tabs survive Disconnect and a switch of connection.** `TabViewQuery` queries the *active* connection with the tab's database and collection. After connecting to another server, the old tabs show again and run against the new one. -- `list_collections` doesn't load the password. It only works because `start_connect` already pooled the driver. -- Refresh lists the databases again but keeps the cached collection names of expanded databases. -- Errors of `list_collections` are logged, not shown. -- No timeout of our own: an unreachable host takes the driver's default server selection timeout (30 s) unless the user cancels. -- Connecting needs the right to list databases. MongoDB returns only the databases a user may access when it lacks the `listDatabases` privilege. There is no field to type a database name directly. +- Connecting needs the right to list databases. Without the `listDatabases` privilege MongoDB returns only the databases the user may access; there is no field to name a database directly. +- A host that doesn't answer waits for the server selection timeout (the connection's option, else 30 s), unless the user cancels. +- Open connections and tabs are not restored after a restart. - Open issues: #14 database statistics (performance, connected users). ## Where the code is | What | Path | |---|---| -| Connect command | `src-tauri/src/database/commands/start_connect.rs` | +| Commands | `src-tauri/src/database/commands/start_connect.rs`, `list_collections.rs`, `disconnect.rs` | | Pool | `src-tauri/src/connection_handler/connection_handler.rs` | -| Listing | `list_databases`, `list_collections` in `src-tauri/src/drivers/mongo_official_driver.rs`, `src-tauri/src/database/commands/list_collections.rs` | -| Store | `connectTo`, `cancelConnect`, `disconnect` in `src/stores/connectionStore.ts` | +| Listing | `list_databases`, `list_collections` in `src-tauri/src/drivers/mongo_official_driver.rs` | +| Store | `connectTo`, `cancelConnect`, `refresh`, `disconnect`, `listCollections`, `sessions` in `src/stores/connectionStore.ts` | | UI | `src/components/AppSidebar.vue`, `src/components/connection/ConnectionGrid.vue`, `ConnectionSelector.vue`, `src/App.vue` | diff --git a/docs/features/connection-manager.md b/docs/features/connection-manager.md index 2a973fd..cb7b34c 100644 --- a/docs/features/connection-manager.md +++ b/docs/features/connection-manager.md @@ -2,53 +2,58 @@ ## Purpose -A user saves the MongoDB servers they work with as connections: a name, host, port and optional username and password. They can edit and delete them, and pick one to connect to. It is the first screen of the app. +A user saves the MongoDB servers they work with as connections: by host and port with options, or by a `mongodb://` / `mongodb+srv://` connection string, with an optional username and password. They can edit and delete them, and open one or several of them. It is the first screen of the app. ## How it works -**The connection** +**The connection** (`connections.json` version 2) | Field | Meaning | |---|---| | `id` | uuid v4, created in Rust when the connection is saved | -| `name` | Free text | -| `uri` | The host, e.g. `localhost`. Despite the name it is not a connection string, see the limits | -| `port` | 1 to 65535, default 27017 | +| `name` | Free text, required | | `connector` | `MongoDb`, the only kind | -| `connectionType` | Always `Standalone`. The enum also has replica set, sharded cluster and DNS seed list, but no UI offers them | -| `authentication` | `kind` `NONE` or `BASIC`, `username`, `password`, `database` (the authentication database) | +| `target` | `{ "kind": "Host", host, port }` or `{ "kind": "Uri", uri }`; a stored connection string never has user info | +| `authentication` | `kind` `NONE` or `BASIC`, `username`, `password` (never stored, see [credential-storage.md](credential-storage.md)), `database` (the authentication database) | +| `options` | For host targets only: `tls`, `tlsCaFile`, `tlsAllowInvalidCertificates`, `replicaSet`, `serverSelectionTimeoutSecs` (empty is the driver's default, 30 s; 10 s for the connection test). A connection string carries its own options | **Where the user manages them** -- `ConnectionGrid` is the start screen while no connection is active. It shows one card per connection with name and `host:port`. Edit and delete appear on hover, and the card has a Connect button, which turns into Cancel while that connection connects. The last card adds a connection. -- `ConnectionSelector` sits in the header while a connection is active. Its dropdown lists the connections with a check next to the active one, switches to another one and has "Add new connection…". While a connection connects, an X next to it cancels. +- `ConnectionGrid` is the start screen while no connection is open. It shows one card per connection with name and address (`host:port` or the string). Edit and delete appear on hover. Connect turns into Cancel while that connection connects; the grid gives way once it is connected. +- The header dropdown "Connections" lists all saved connections, marks the open and connecting ones, opens further connections and has "Add new connection…". See [connect-and-explorer.md](connect-and-explorer.md). +- The sidebar has an edit button on every open connection. - `ConnectionDialog` creates and edits: - - Fields: name, host, port, a "Use Authentication" checkbox, and with it username, password (with a show/hide toggle) and auth database (default `admin`). vee-validate with a zod schema checks name, host and port. - - Test Connection and Create/Save stay disabled until every field shown is filled, so with authentication the password is required. Save is also disabled while a test runs. - - Testing is optional, see [connection-test.md](connection-test.md). - - Closing the dialog resets the form and the test state. - - In edit mode the dialog loads the stored password from the keyring, see [credential-storage.md](credential-storage.md). + - Name, then a switch between "Host and port" and "Connection string". + - Host mode: host, port, and collapsible options (TLS with CA file and "allow invalid certificates", replica set, timeout in seconds). The options open by themselves when a connection has any. + - Connection string mode: one field. A hint says that a username and password in the string are moved out on save. + - "Use Authentication" with username, password (show/hide toggle) and auth database. Empty means `admin` for a host, and the string's `authSource` (else `admin`) for a connection string. + - TanStack Form validates the values with a zod schema (`connectionForm.ts`) when the user tests or saves, and shows what is wrong under each field: a missing name or host, a host that is really a connection string, a port outside 1–65535, a timeout outside 1–3600, a string without `mongodb://` or `mongodb+srv://`, missing username or password with authentication. + - Testing is optional, see [connection-test.md](connection-test.md). In edit mode the dialog loads the stored password. **Saving, editing, deleting** -- Create calls `create_connection` with the connection without id (`CreateConnection`). Rust creates the id, moves the password into the keyring and writes the connection without it to the file. The store adds the returned connection, then the grid or the selector reloads the whole list. -- Edit calls `update_connection(id, connection)`. With `BASIC` and a password, Rust writes the password to the keyring. With `NONE` it deletes the keyring entry. The password is stripped before the file is written. The store replaces the entry in its list with what the form sent. -- Delete asks with the browser's `confirm()`, then calls `delete_connection`. It removes the entry from the file and the keyring; keyring errors are ignored. If the deleted connection was active, the store clears the active connection and its databases. +- Before storing, and before a test, Rust checks the connection again (`models/normalize.rs`; it also refuses `host:port` in the host field) and moves credentials out of a connection string: the username into `authentication`, the password into the keyring (`models/uri.rs`). A refusal is `RepositoryError::Invalid`, and the UI shows its text, e.g. "Enter mongodb:// and mongodb+srv:// addresses as a connection string, not as a host." +- Create calls `create_connection` with the connection without id; Rust creates the id and returns the stored connection, which the store adds. +- Edit calls `update_connection(id, connection)`. With `BASIC` and a password, Rust writes the password; with `NONE` it deletes it; the file never gets one. The Rust core drops the connected driver of that connection; the store reloads the list and an open connection reconnects with the new settings. +- Delete asks with the app's confirmation dialog (`useConfirm`), disconnects an open connection (its tabs close), then calls `delete_connection`, which removes file entry, keyring entry and driver. **Storage** -- `FileRepository` keeps `/connections.json` (on Linux `~/.config/org.monolens/connections.json`) as `{ "version": 1, "connections": { "": { … } } }`. -- Every call reads the whole file, and every change writes the whole file (pretty JSON). A missing or empty file means no connections. -- A file with another `version` than `STORAGE_VERSION` (1) fails with `MigrationError`, and the list can't be loaded. There is no migration code yet. - -Errors come back as `CommandError` with a user message such as "Failed to load connections." and are shown as toasts. +- `FileRepository` keeps `/connections.json` (on Linux `~/.config/org.monolens/connections.json`) as `{ "version": 2, "connections": { "": { … } } }`. Every call reads the whole file, every change writes it. +- A version 1 file (until 2026-11-03: the host in `uri`, plus `port` and an unused `connectionType`) is migrated on the first load (`repositories/migrations.rs`) and kept next to it as `connections.v1.json`. A version 1 `uri` that is a connection string becomes a string target with its password moved to the keyring; a `host:port` loses the port after the host, the port field counted before too. +- Another version, or no version, is an error. ## Decisions -- **The id is created in Rust** (`8a9638a`). The UI sends `CreateConnection` without an id, so it can't invent or reuse one. -- **A versioned map instead of an array** (`84839a6`, issue #24): O(1) access by id, and the version allows migrating the file later. -- **Passwords are not in the file** (`eb8fdd9`, `84839a6`): they live in the OS keyring, see [credential-storage.md](credential-storage.md). +- **The id is created in Rust** (`8a9638a`). +- **A versioned map instead of an array** (`84839a6`, issue #24): O(1) access by id, and a version to migrate the file. Used for the first time with version 2 (`5f6efaa`). +- **Passwords are not in the file** (`eb8fdd9`, `84839a6`), see [credential-storage.md](credential-storage.md). - **Testing is optional** (`963da88`, issue #26). +- **Host and port with options, or a connection string** (`5f6efaa`, `29aceb2`, issue #4): a string carries all its options, the option fields apply to host targets only. `connectionType` was never read and is gone; the topology comes from the string or the replica set option. +- **Credentials leave a connection string on save** (`5f6efaa`): no password in `connections.json`, and the stored string can be shown. +- **The form validates instead of disabling buttons** (`29aceb2`): the user sees what is missing. +- **TanStack Form with zod 4** (`3c87afd`): `@vee-validate/zod` only supports zod 3, vee-validate 5 has been a beta since 2025. Rejected: vee-validate 5 beta, an adapter of our own for vee-validate 4. +- **A dialog of the app for deleting** (`29aceb2`) instead of the browser's `confirm()`. ## Building steps @@ -65,16 +70,19 @@ Errors come back as `CommandError` with a user message such as "Failed to load c | 2026-02-20 | Saving no longer requires a test | `963da88` | | 2026-02-21 | Cancel a connect from the dropdown, new dialog layout | `571e414`, `c780905` | | 2026-02-23 | Errors as toasts | `37819e1` | +| 2026-11-03 | The dialog on TanStack Form and zod 4 | `3c87afd` | +| 2026-11-03 | Model version 2 with targets and options, migration of version 1, validation and moving credentials out of strings in Rust | `5f6efaa` | +| 2026-11-03 | Dialog with both modes and options, form logic in `connectionForm.ts`, confirmation dialog for deleting | `29aceb2` | +| 2026-11-03 | Several connections at once: the dropdown opens further connections, edit from the sidebar | `afd84c7` | +| 2026-11-03 | Loaded once at start; reloaded from Rust after an edit | `d5ff9e3` | +| 2026-11-03 | Review fixes: the auth database stays empty by default (a filled `admin` overrode a string's `authSource`), the edit dialog fills on every opening, `host:port` refused, the test normalizes like saving | `8fe2ca0` | ## Limits and open points -- **Host and port only.** The driver always builds `mongodb://:/?authSource=`. The host field's placeholder says "localhost or mongodb+srv://...", but SRV and full connection strings don't work: the prefix is added again. TLS, replica set name, timeouts, read preference and other URI options can't be set. Issue #4 asked for a timeout setting and is closed, but no timeout exists. -- **Connection types are not wired up.** `connectionType` is always `Standalone` and nothing reads it. The TypeScript enum sends `"SharedCluster"` for `ShardedCluster`, which Rust's `ShardedCluster` would not accept. -- **No storage migration.** The version is checked, but a mismatch is an error. -- **Edits don't reach a pooled driver.** After a connection was used in this session, a changed host, port or password only takes effect after a restart, see [connect-and-explorer.md](connect-and-explorer.md#limits-and-open-points). -- Delete uses the browser's `confirm()`, not a dialog of the app. -- `App.vue` and `ConnectionGrid` both load the connections on mount, so they are loaded twice at start. -- Dead code: `ConnectionRepositoryIPC` is registered but unused. The `saveConnection`/`saveConnections` wrappers call commands that no longer exist. `get_connection` is wrapped but never called. The Rust commands `test_connection` and `get_databases` are not used by the UI. +- Authentication is username and password only (MongoDB's default SCRAM). No X.509, LDAP, Kerberos, AWS or OIDC, and no client certificates. +- The CA file is a typed path; there is no file picker. +- Options are fields for host targets only. For anything else (read preference, several hosts, …) use a connection string. +- `mongodb+srv://` resolves DNS records when connecting; it was not tested against a real srv cluster. - Open issues: #40 quick connections in the dropdown (at most 5, "Browse more", edit and delete there), #45 stage types (Develop, Demo, Production, with a confirmation before changing production data), #28 start an in-memory MongoDB. ## Where the code is @@ -82,8 +90,8 @@ Errors come back as `CommandError` with a user message such as "Failed to load c | What | Path | |---|---| | Commands | `src-tauri/src/connector/commands/` | -| Models | `src-tauri/src/connector/models/`, `src/domains/connections/models/` | -| Storage | `src-tauri/src/connector/repositories/file_repository.rs`, `storage_wrapper.rs` | +| Models, checks, connection strings | `src-tauri/src/connector/models/` (`connection.rs`, `options.rs`, `normalize.rs`, `uri.rs`), `src/domains/connections/models/` | +| Storage and migration | `src-tauri/src/connector/repositories/file_repository.rs`, `migrations.rs`, `storage_wrapper.rs` | | IPC wrapper | `src/domains/connections/services/ConnectionServiceIPC.ts` | | Store | `src/stores/connectionStore.ts` | -| UI | `src/components/connection/ConnectionGrid.vue`, `ConnectionDialog.vue`, `ConnectionSelector.vue` | +| UI | `src/components/connection/ConnectionGrid.vue`, `ConnectionDialog.vue`, `connectionForm.ts`, `ConnectionSelector.vue`, `src/components/ConfirmDialog.vue`, `src/composables/useConfirm.ts` | diff --git a/docs/features/connection-test.md b/docs/features/connection-test.md index 87196b7..d37bfae 100644 --- a/docs/features/connection-test.md +++ b/docs/features/connection-test.md @@ -7,28 +7,31 @@ Before or after saving a connection, the user can check that the server answers ## How it works 1. In `ConnectionDialog`, Test Connection validates the form and calls `connectionStore.testConnection` with the form values (including the typed password). -2. The store sets `testStages` to five pending stages, listens to the Tauri event `test-connection-progress`, and calls `ConnectionServiceIPC.testConnection`. That starts `start_test_connection` through `abortableInvoke`, with `id: ''` because the connection may not be saved yet. -3. In Rust the task creates its own driver through the factory, not the pooled one, builds the client, and runs `test_connection`. After each stage it emits `test-connection-progress` with `{ taskId: "", stageIndex, stage }`: - - | # | Stage | Check | - |---|---|---| - | 0 | Initialize | the client exists | - | 1 | Ping Database | `ping` on the authentication database (default `admin`) | - | 2 | Reading status | `serverStatus` | - | 3 | Detecting version | `true` if `serverStatus` has a `version` string, else `false` (the test goes on) | - | 4 | Connected | always `true` once reached | - -4. The store writes each event into `testStages[stageIndex]`. When the task finishes, the returned stages replace the list. -5. `TestStagesDisplay` draws the stages as a horizontal stepper: a green check for passed, a red cross for failed, a spinner on the stage that is running, and coloured separators. -6. A failing stage returns its error. The store shows the `user_message` as a toast ("Authentication failed. …", "Connection timed out. …") and stops the spinner. -7. While the test runs, Cancel Test replaces the button. It aborts the task (`cancel_task`) and shows "Test cancelled.". Closing the dialog clears the test state. +2. The store chooses a test id (`crypto.randomUUID()`), empties `testStages`, listens to the Tauri event `test-connection-progress`, and calls `ConnectionServiceIPC.testConnection`. That starts `start_test_connection(connection, testId)` through `abortableInvoke`, with `id: ''` because the connection may not be saved yet. +3. In Rust the task creates its own driver through the factory (`create_for_test`), not a pooled one, builds the client, and runs `test_connection`. Without a timeout of the connection (option field or `serverSelectionTimeoutMS` in the string), this driver waits 10 s for a server instead of the driver's 30 s. It first reports every stage of `TEST_STAGES` as `pending`, then each result, as events `{ testId, stageIndex, stage }`: + + | # | Stage | Check | Result | + |---|---|---|---| + | 0 | Initialize | the client exists | passed / failed | + | 1 | Ping Database | `ping` on the authentication database (default `admin`); the first command also authenticates | passed / failed, the test stops | + | 2 | Reading status | `serverStatus` | passed; *skipped* with "No permission to read the server status" when MongoDB answers unauthorized (code 13); failed otherwise, the test stops | + | 3 | Detecting version | `buildInfo`, which needs no privilege | passed with the detail "MongoDB 8.0.x"; failed without stopping the test | + | 4 | Connected | — | passed | + +4. The store takes only events with its own test id and builds the list from them; when the task finishes, the returned stages replace it. +5. `TestStagesDisplay` draws a horizontal stepper: a green check for passed, a red cross for failed, a yellow dash for skipped, a spinner on the stage that runs, from 2 s on its running time in seconds (`useElapsedSeconds`), the detail under the title, and "Starting the test…" before the first event. +6. A failing stage is reported as failed before the error returns. The store shows the error's `user_message` as a toast ("Authentication failed. …"). +7. While the test runs, Cancel Test replaces the button; it aborts the task and shows "Test cancelled.". Closing the dialog clears the test state. ## Decisions -- **Stages streamed as events** (`a14c8fc`): each stage shows when it really finishes, instead of all at the end. +- **Stages streamed as events** (`a14c8fc`), and since `2e7d3e1` with a test id, so a listener only takes its own test. - **A stepper** (`9229c45`) for readability. - **Optional** (`963da88`, issue #26): saving does not need a passed test. -- **A cancellable task** (`a14c8fc`, issue #27): an unreachable host could block the UI. +- **A cancellable task** (`a14c8fc`, issue #27). +- **A missing `serverStatus` privilege skips the stage** (`2e7d3e1`): a user who may only read one database can connect and read, so the test must not fail for them. The version comes from `buildInfo`, which every user may run. +- **The titles exist only in Rust** (`2e7d3e1`): the driver reports all stages as pending first. +- **The test gives up after 10 s, connecting keeps 30 s** (`37f0aea`): a wrong port sat at "Ping Database" for 30 s and looked like a hang. A connection's own timeout wins in both. ## Building steps @@ -42,21 +45,21 @@ Before or after saving a connection, the user can check that the server answers | 2026-02-21 | Test as a cancellable task with stage events | `a14c8fc` | | 2026-02-21 | Stages shown as a stepper | `9229c45` | | 2026-02-23 | Errors as toasts | `37819e1` | +| 2026-11-03 | Stage states with skipped, version from `buildInfo`, failed stages marked, test id, titles only in Rust; the synchronous `test_connection` command removed | `2e7d3e1`, `119bf4e` | +| 2026-11-03 | 10 s server selection timeout for the test, running time on the waiting stage | `37f0aea` | ## Limits and open points -- **A failing stage is not marked as failed.** The error returns before the stage's status is set or emitted, so the stepper stops at that stage and the reason only appears in the toast. The red cross only appears for "Detecting version". -- **`serverStatus` needs a privilege** (e.g. the `clusterMonitor` role). A user who may only read one database fails at "Reading status", although connecting and reading would work. -- The detected version is not shown anywhere. -- The event has an empty `taskId`, so a listener can't tell two tests apart. The UI only runs one at a time. -- The stage titles exist twice: in the driver and in `connectionStore._createPendingStages`. -- There is no timeout of our own. A host that doesn't answer runs into the driver's default server selection timeout (30 s), unless the user cancels. +- A host that doesn't answer fails after the connection's own timeout, else after 10 s, unless the user cancels. Connecting to it waits the driver's 30 s. +- The test checks the server, not the rights on a particular database. ## Where the code is | What | Path | |---|---| -| Stages | `test_connection` in `src-tauri/src/drivers/mongo_official_driver.rs`, `TestStage` in `src-tauri/src/drivers/driver.rs` | -| Command and event | `src-tauri/src/database/commands/start_test_connection.rs` | +| Stages | `test_connection` and `TEST_STAGES` in `src-tauri/src/drivers/mongo_official_driver.rs`, `TestStage`, `StageStatus` in `src-tauri/src/drivers/driver.rs` | +| Command and event | `src-tauri/src/database/commands/start_test_connection.rs`, `create_for_test` in `src-tauri/src/drivers/factory.rs` | +| Test timeout | `TEST_SERVER_SELECTION_TIMEOUT` in `mongo_official_driver.rs`, `fallback_timeout` of `client_options` in `src-tauri/src/drivers/client_options.rs` | | Store | `testConnection`, `cancelTest` in `src/stores/connectionStore.ts` | -| UI | `src/components/connection/ConnectionDialog.vue`, `TestStagesDisplay.vue` | +| UI | `src/components/connection/ConnectionDialog.vue`, `TestStagesDisplay.vue`, `src/composables/useElapsedSeconds.ts` | +| Tests | integration tests in `mongo_official_driver.rs`, `src/stores/test/connectionStore.test.ts` | diff --git a/docs/features/credential-storage.md b/docs/features/credential-storage.md index b5395f7..ac106bb 100644 --- a/docs/features/credential-storage.md +++ b/docs/features/credential-storage.md @@ -6,30 +6,30 @@ Passwords of saved connections must not sit in a plain file. Monolens keeps them ## How it works -- `CredentialService` wraps `keyring::Entry`. The service name is `monolens-connections` and the entry is `connection::basic_password`. Set, get and delete run in `spawn_blocking`, because the keyring calls block. -- The `keyring` crate uses the native store of each platform (features `apple-native`, `windows-native`, `linux-native-sync-persistent`, `vendored`). On Linux it combines keyutils, fast and kernel-level for the session, with the Secret Service for persistence. `vendored` bundles the OpenSSL that the Linux backend needs. +- Passwords go through the `CredentialStore` trait. `CredentialService` is the OS keyring through `keyring` 4 (its default `v1` mode: Keychain on macOS, Credential Manager on Windows, Secret Service on Linux through `zbus`). The service name is `monolens-connections`, the entry `connection::basic_password`. Keyring calls run in `spawn_blocking`. A missing entry is `CredentialError::NotFound`, anything else `CredentialError::Keyring`. +- Tests use `MemoryCredentialStore`, which keeps passwords in memory and can be told to fail. - Writing: - - `create_connection` takes the password out of the request, writes it to the keyring under the new id, and saves the connection without it. - - `update_connection` writes the password for `BASIC` when one is sent, and deletes the entry for `NONE`. The file never gets the password. + - `create_connection` takes the password out of the request, writes it under the new id, and saves the connection without it. + - A connection string with user info has it taken out first (`models/normalize.rs`): the username goes into `authentication`, the password to the keyring. Typed credentials win over the ones in the string. + - `update_connection` writes the password for `BASIC` when one is sent (without one the stored password stays), and deletes it for `NONE`. - `delete_connection` deletes the entry and ignores keyring errors. - Reading: - - `get_connections` and `get_connection` never return a password. - - `hydrate_connection_with_credentials` loads the password for a `BASIC` connection right before use. `start_connect`, `start_find_documents` and the unused `get_databases` call it. The commands reach the `CredentialService` by downcasting the repository to `FileRepository`. - - `list_collections` doesn't load the password. It relies on the driver that `start_connect` already put in the pool. - - `get_connection_password(id)` returns the password to the UI, or `null` on any keyring error. Only the edit dialog calls it, when it opens. - - A connection test from the dialog sends the password typed in the form, not the stored one. -- Building the connection string (`MongoDbOfficialDriver.build_connection_string`): - - `Url::set_username` and `set_password` percent-encode the credentials. The username falls back to `root` if it is missing, but the dialog requires one. - - `authSource` is the authentication database, else `admin`. It is also added to connections without authentication. -- Logging: only in debug builds the built URI is logged, with the password replaced by `***`. Release builds don't log it. + - `get_connections` never returns a password. + - `ConnectorRepository::hydrate` fills the password of a `BASIC` connection. `ConnectionHandler` calls it when it builds a driver, for every command that talks to MongoDB; those commands take a connection id, so the password never passes through the web view for them. + - A keyring that can't be read is `DriverError::CredentialsUnavailable`: "The password of this connection could not be read from the system keyring. Edit the connection and enter it again." + - `get_connection_password(id)` returns the password to the UI, or `null`. Only the edit dialog calls it, when it opens. + - A connection test from the dialog sends the password typed in the form, because the connection may not be saved yet. +- Building the client (`drivers/client_options.rs`): a host target gets a `Credential` with username, password and the authentication database (default `admin`). For a connection string the user info is put back into the string, percent-encoded, only in memory and right before the driver parses it, because the driver checks some options against it (an `authMechanism` needs a username). The authentication database is the form's, else the string's `authSource`, else `admin`. +- Logging: the driver logs hosts and settings of a new client, never the credential. The connection dialog logs no form values. ## Decisions - **OS keyring through `keyring`** (`eb8fdd9`, issue #15). Tauri Stronghold was added on 2026-02-15 (`8b54cd3`) and reverted two days later (`02a2c3a`). The revert says only that Stronghold "is not needed anymore". -- **Linux: keyutils plus Secret Service, OpenSSL vendored** (`aad2e90`). Fast access during a session, persistence across sessions, no system OpenSSL needed. -- **Passwords stay in Rust** (`00b3284`, `84839a6`, PR #23). Listing doesn't touch the keyring, and only the commands that connect load a password. -- **Exception: the edit dialog shows the password** (`dc020e7`, issue #29). Users wanted to see what they saved. It is fetched only when the dialog opens. -- **Percent-encoding through `Url`** (`8a9638a`, issue #21). Passwords with special characters broke the connection string. +- **Linux: keyutils plus Secret Service, OpenSSL vendored** (`aad2e90`), replaced on 2026-11-03 by keyring 4, which only uses the Secret Service. Passwords saved with keyring 3 are read by keyring 4 unchanged (checked with the three connections on the development machine). +- **Passwords stay in Rust** (`00b3284`, `84839a6`, PR #23), and since `119bf4e` the commands that connect load them by connection id. +- **Exception: the edit dialog shows the password** (`dc020e7`, issue #29). It is fetched only when the dialog opens. +- **A trait for the store** (`a7b4c1a`): the repository can be tested without the real keyring, and the commands no longer downcast the repository to reach it. +- **No credentials in a stored connection string** (`5f6efaa`): they are moved out on save and put back only in memory before parsing. ## Building steps @@ -43,22 +43,23 @@ Passwords of saved connections must not sit in a plain file. Monolens keeps them | 2026-02-18 | Password loaded only on connect, never returned when listing | `00b3284`, `84839a6` | | 2026-02-20 | The edit dialog shows the stored password | `dc020e7` | | 2026-02-22 | Credentials removed from logs, URI logged only in debug builds | `8d6bcac` | +| 2026-11-03 | keyring 4: the Secret Service alone on Linux, no keyutils cache, no OpenSSL | `955c08f` | +| 2026-11-03 | `CredentialStore` trait, `hydrate` and `get_password` on the repository, no downcasts | `a7b4c1a` | +| 2026-11-03 | Credentials moved out of connection strings, `ClientOptions` instead of a URL built by hand | `5f6efaa` | +| 2026-11-03 | Passwords loaded by connection id in Rust, `CredentialsUnavailable` | `119bf4e` | ## Limits and open points -- The password reaches the web view in two places: the edit dialog loads it, and a test sends the typed one. Everything else keeps it in Rust. -- A keyring failure while connecting becomes `ConnectionFailed("Could not fetch credentials")`, which the user sees as "Failed to connect. Verify your host, port, and that the server is running." That points to the wrong cause. The same happens when the file is copied to another machine without its keyring entries. +- The password reaches the web view in two places: the edit dialog loads it, and a test sends the typed one. - Only username and password (MongoDB's default SCRAM mechanism). No X.509, LDAP, Kerberos, AWS or OIDC. -- On Linux the persistent store is the Secret Service (e.g. GNOME Keyring or KWallet). How the app behaves without one was not tested. -- The commands downcast the repository to `FileRepository`. Another repository implementation would make connect and find fail with "Unsupported repository type". +- On Linux the store is the Secret Service (e.g. GNOME Keyring or KWallet). How the app behaves without one was not tested. ## Where the code is | What | Path | |---|---| -| Keyring access | `src-tauri/src/connector/repositories/credentials/credential_service.rs` | -| Loading the password before use | `src-tauri/src/connector/repositories/credentials/hydrate_connection_with_credentials.rs` | -| Writing and deleting | `src-tauri/src/connector/repositories/file_repository.rs` | +| Store trait and keyring | `src-tauri/src/connector/repositories/credentials/credential_store.rs`, `credential_service.rs` | +| Writing, reading, hydrating | `src-tauri/src/connector/repositories/file_repository.rs` | +| Credentials in connection strings | `src-tauri/src/connector/models/uri.rs`, `normalize.rs` | +| The driver's credential | `src-tauri/src/drivers/client_options.rs` | | Password for the edit dialog | `src-tauri/src/connector/commands/get_connection_password.rs`, `src/components/connection/ConnectionDialog.vue` | -| Connection string and log masking | `src-tauri/src/drivers/mongo_official_driver.rs` | -| Keyring features | `src-tauri/Cargo.toml` | diff --git a/docs/features/document-views.md b/docs/features/document-views.md index 3ed5b08..abe2b28 100644 --- a/docs/features/document-views.md +++ b/docs/features/document-views.md @@ -6,21 +6,22 @@ The result of a query is shown as a table, one row per document and one column p ## How it works -**Input.** Rust serializes each document with serde, so BSON types arrive as extended JSON: `ObjectId` as `{ "$oid": "…" }`, dates as `{ "$date": { "$numberLong": "" } }`, decimals as `{ "$numberDecimal": "…" }`. `Int32`, `Int64` and doubles arrive as plain JSON numbers. +**Input.** Rust turns each document into relaxed extended JSON (`drivers/documents.rs`): `ObjectId` as `{ "$oid": "…" }`, dates as `{ "$date": "2024-01-01T00:00:00Z" }`, decimals as `{ "$numberDecimal": "…" }`, binaries as `{ "$binary": { base64, subType } }`. `Int32`, doubles and `Int64` within ±2^53 arrive as plain JSON numbers; a larger `Int64` stays `{ "$numberLong": "…" }`, at any depth, because a JavaScript number would change it. **Table view** (`TableDocumentsView`, `composables/table/`) +- TanStack Table 9 with the features `documentTableFeatures` registers: sorting with the sorted row model and the sort functions `auto` picks from, column ordering, visibility, sizing and resizing. - Columns are the union of the top-level fields of all documents: `_id` first, the rest alphabetically. A missing field is an empty cell. - `formatCellValue` turns a value into text: - - `$oid` gives the hex string, `$date` gives the ISO date + - `$oid` gives the hex string, `$date` the ISO date (also the canonical `{ "$numberLong": ms }` form) - `$numberLong`, `$numberInt`, `$numberDecimal` and `$numberDouble` give the number - `$binary` gives `[Binary]`, `$regularExpression` gives `/pattern/options`, `$timestamp` gives its JSON - `null` gives `null`, other objects and arrays give JSON text `useFormattedValueCache` caches the text per document and field until the next result. - Cells longer than 120 characters are cut with `…` and show the full text in a tooltip. -- Rows are virtualized with TanStack Virtual: fixed row height 33 px, 20 rows of overscan. Only the visible rows are in the DOM. -- A click on a header sorts by that column, ascending, descending, off (TanStack Table, `sortingFn: 'auto'`). Sorting runs in the web view over the loaded documents only. +- Rows are virtualized with TanStack Virtual: fixed row height 33 px, 20 rows of overscan. +- A click on a header sorts by that column, ascending, descending, off (`sortFn: 'auto'`). Sorting runs in the web view over the loaded page only; the query tab's sort input sorts on the server. - Columns can be dragged by their grip to a new position. The order is saved in `localStorage` under `monolens:columnOrder:::` and merged with the current fields: saved ones keep their place, new ones are appended. - Columns can be resized by dragging their right edge, between 60 and 500 px. The start width comes from the 90th percentile of the text lengths in the first 500 documents, or the header if wider. Resized widths are not saved. - No documents: "No documents found for this query." @@ -36,6 +37,8 @@ The result of a query is shown as a table, one row per document and one column p - **Table as the default view** (`ead8967`). The commit gives no reason. - **Column order saved per connection, database and collection** (`ead8967`), so a reordered collection looks the same next time. - **Virtual rows, cached cell text, sampled column widths** (`0aca486`, PR #44, issue #39), so bigger results render fast. +- **Relaxed extended JSON with exact large integers** (`5a30f74`): readable dates, and no silent change of an `Int64` beyond 2^53. +- **TanStack Table 9 with explicit features** (`3493da8`): v9 only has the APIs of registered features. ## Building steps @@ -45,24 +48,24 @@ The result of a query is shown as a table, one row per document and one column p | 2026-02-26 | JSON view with one CodeMirror editor per document | `0c738f1` | | 2026-03-02 | Table view: sortable and movable columns, formatting of `_id` and other extended JSON | `ead8967` | | 2026-03-11 | Composables for drag and drop, sizing and the table; virtual rows; cached cell text; resizable columns | `0aca486` | +| 2026-11-03 | TanStack Table 9, tests for the table | `3493da8` | +| 2026-11-03 | Relaxed extended JSON from Rust, large `Int64` kept exact | `5a30f74` | +| 2026-11-03 | The JSON view's watcher guard that never returned removed | `d5ff9e3` | ## Limits and open points - **Read-only.** Documents can't be added, edited or deleted. Open issues: #5 CRUD on data, #6 adding, #7 updating, #9 deleting, #43 a command pattern so changes can be undone. -- **Nested values are flat text.** An embedded document or array is one JSON string in its cell. Issue #39 asks to open nested objects on double click and for bigger text; its column-width point is done. -- Sorting covers only the loaded documents, at most 200, not the collection. -- `Int64` values above 2^53 lose precision, because they arrive as JSON numbers. +- **Nested values are flat text.** An embedded document or array is one JSON string in its cell. Issue #39 asks to open nested objects on double click and for bigger text. - Column widths are not saved, only the order. - The JSON view shows the extended-JSON objects as they arrive (`{ "$oid": … }`), not shell notation. -- The JSON view's watcher opens every document on each new result. Its guard `if (!editors.keys())` is always false, an iterator is never falsy. -- No tests for `formatCellValue`, the column merge or the sizing. +- No tests for `formatCellValue` yet. ## Where the code is | What | Path | |---|---| | Table | `src/components/documents/table/TableDocumentsView.vue` | -| Table logic | `src/composables/table/useDocumentTable.ts`, `useColumnSizing.ts`, `useColumnDragDrop.ts`, `useFormattedValueCache.ts` | +| Table logic | `src/composables/table/useDocumentTable.ts`, `useColumnSizing.ts`, `useColumnDragDrop.ts`, `useFormattedValueCache.ts` (tests in `test/`) | | Cell text | `src/utils/formatCellValues.ts` | | JSON view | `src/components/documents/json/JSONDocumentsEditor.vue` | -| Shared types | `src/components/documents/models/types.ts` | +| Conversion in Rust | `src-tauri/src/drivers/documents.rs` | diff --git a/docs/features/error-handling.md b/docs/features/error-handling.md index f6781bb..2fd73bc 100644 --- a/docs/features/error-handling.md +++ b/docs/features/error-handling.md @@ -10,22 +10,24 @@ Driver errors are long and technical. The user gets a short sentence that says w | Type | Where | `kind` | `user_message` | |---|---|---|---| -| `DriverError` | `drivers/errors.rs`, every database command | The variant: `ConnectionFailed`, `ClientNotInitialized`, `PingFailed`, `ListDatabasesFailed`, `ListCollectionsFailed`, `ServerStatusFailed`, `UriMalformed`, `UnknownDriver`, `FindDocumentsFailed` | Chosen per variant. For connect, ping, list and server status it looks into the raw message: one containing "authentication", "unauthorized" or "auth error" gives "Authentication failed. Check your username, password, …". For ping, "timeout" or "timed out" gives "Connection timed out. Check your host, port, and firewall settings." | -| `CommandError` | `connector/commands/error.rs`, the connection CRUD commands | `repository`, `io`, `serialization`, `tauri` | One fixed sentence per command, e.g. "Failed to load connections." | +| `DriverError` | `drivers/errors.rs`, every database command | `ConnectionFailed`, `ClientNotInitialized`, `PingFailed`, `ListDatabasesFailed`, `ListCollectionsFailed`, `ServerStatusFailed`, `UriMalformed`, `UnknownDriver`, `FindDocumentsFailed`, `InvalidQuery`, `ConnectionNotFound`, `StorageFailed`, `CredentialsUnavailable` | Chosen per variant. Connect, ping, list and server status look into the raw message: "authentication", "unauthorized" or "auth error" give "Authentication failed. Check …"; a ping timeout gives "Connection timed out. …". A failed find shows the server's message ("The query failed: unknown top level operator: $foo"), or "Not allowed to read this collection" without the rights. `InvalidQuery` is already a sentence for the user ("The sort must be an object like { field: value }, not a number") | +| `CommandError` | `connector/commands/error.rs`, the connection CRUD commands | `repository`, `invalid`, `io`, `serialization`, `tauri` | One sentence per command, e.g. "Failed to load connections."; for `invalid` (`RepositoryError::Invalid`) the reason why a connection was refused | | `TaskError` | `task_manager/task_error.rs` | `NotFound`, `Cancelled`, `Failed` | Fixed. For `Failed` the `message` is the original error as JSON, which `abortableInvoke` unwraps | -Inside the connector `RepositoryError` (not found, io, serialization, credential, migration) and `CredentialError` (keyring, join) are used. The commands turn them into a `CommandError`, with their text as `message`. +Inside the connector `RepositoryError` (not found, invalid, io, serialization, credential, migration) and `CredentialError` (not found, keyring, join) are used. **In the UI** - `extractErrorMessage(e)` returns, in this order, `user_message`, `message`, `Error.message`, a string error itself, or "An unexpected error occurred.". -- `vue-sonner` shows it as a toast: `` in `App.vue`. `connectionStore` toasts every failed call. `TabViewQuery` toasts query parse errors. -- `Result` and `tryCatch` (`utils/result.ts`) turn a rejected `invoke` into `{ data: null, error }` for the CRUD calls. +- `vue-sonner` shows it as a toast: `` in `App.vue`. The connection store toasts every failed call; the query tab toasts parse errors (naming the input: "Sort: …") and failed queries; the sidebar toasts failed collection lists. +- `Result` and `tryCatch` (`utils/result.ts`) turn a rejected `invoke` into `{ data: null, error }` for the calls that aren't tasks. ## Decisions - **Three fields, the UI shows `user_message`** (`b5a9455`, issue #16). The raw messages overflowed the dialog and told the user little. -- **Toasts instead of error boxes** (`37819e1`, issue #36). The "Show details" toggle that `b5a9455` added to the test display went away with them, so the technical `message` is no longer shown in the UI. +- **Toasts instead of error boxes** (`37819e1`, issue #36). The "Show details" toggle of `b5a9455` went away with them. +- **Query errors keep the server's text** (`5a30f74`): it names the mistake in the query. +- **Credentials and storage get their own variants** (`119bf4e`): a keyring failure read as "verify your host" before. ## Building steps @@ -38,13 +40,15 @@ Inside the connector `RepositoryError` (not found, io, serialization, credential | 2026-02-20 | `user_message` on every error, short text and details in the dialog | `b5a9455` | | 2026-02-21 | `TaskError` | `a14c8fc` | | 2026-02-23 | Toasts, the error boxes and the details toggle removed | `37819e1` | +| 2026-11-03 | `CredentialError::NotFound` | `a7b4c1a` | +| 2026-11-03 | `RepositoryError::Invalid` with its reason for the user | `5f6efaa` | +| 2026-11-03 | `ConnectionNotFound`, `StorageFailed`, `CredentialsUnavailable`; errors of the collection list shown | `119bf4e` | +| 2026-11-03 | `FindDocumentsFailed` with the server's message, `InvalidQuery` | `5a30f74` | +| 2026-11-03 | Query errors shown as toasts | `16b6cd7` | ## Limits and open points -- Auth and timeout errors are recognised by substrings of the driver's English message. A changed driver text breaks the classification silently. -- `FindDocumentsFailed` drops the driver's message (`map_err(|_| …)`). Neither the user nor the log learns why a query failed, except for a filter that can't be turned into BSON, which is logged as a warning. -- Query errors from the server are not shown at all, see [query-tab.md](query-tab.md#limits-and-open-points). Errors of `list_collections` are only logged. -- A keyring failure while connecting reads as a host problem, see [credential-storage.md](credential-storage.md#limits-and-open-points). +- Auth and timeout errors are recognised by substrings of the driver's English message. A changed driver text breaks the classification silently (the unit tests in `errors.rs` cover today's texts). - All messages are English. Issue #31 asks for i18n (German and English first). ## Where the code is diff --git a/docs/features/logging.md b/docs/features/logging.md index 43e8eca..685f57a 100644 --- a/docs/features/logging.md +++ b/docs/features/logging.md @@ -6,20 +6,21 @@ Rust and the web view write to one log, so a problem can be followed across the ## How it works -- Rust logs with the `log` macros. `tauri-plugin-log` is set to level `Debug` in `lib.rs` and uses its default targets: stdout and a file in the app's log directory (on Linux under `~/.local/share/org.monolens/logs`). +- Rust logs with the `log` macros. `tauri-plugin-log` writes at `Debug` in development builds and `Info` in release builds (`lib.rs`), to its default targets: stdout and a file in the app's log directory (on Linux under `~/.local/share/org.monolens/logs`). - What Rust logs: - - the config path at start + - the config path at start, a migration of `connections.json` - tasks being spawned, cancelled, completed, failed or panicking - - a malformed URI, a filter that can't become BSON, a cursor that fails - - in debug builds only, the built connection URI with the password replaced by `***` + - a new client with its hosts, TLS, replica set and whether it authenticates, never the credential + - failed finds with the server's message, a failing `buildInfo` - The web view logs through `useLogger(context)`. It returns a `Logger` with the methods `trace` to `error` and a `LogStrategy`. The default `TauriLogStrategy` forwards to `@tauri-apps/plugin-log`, so UI messages land in the same log with the prefix `[WebView::]`. Objects are written as JSON, functions as `[Function: name]`. -- The capability `log:default` lets the web view use the plugin. +- The capability `log:default` lets the web view use the plugin. In tests the IPC is mocked, so logging code runs without Tauri. ## Decisions - **One log for both sides through `tauri-plugin-log`** (`80c3925`). - **A strategy behind the logger**, so the target can change without touching the callers (`80c3925`). -- **No credentials in logs** (`8d6bcac`, PR #32): the URI only in debug builds and masked. +- **No credentials in logs** (`8d6bcac`, PR #32); since `5f6efaa` no connection string at all, only hosts and settings, and since `3c87afd` the dialog logs no form values. +- **`Info` in release builds** (`119bf4e`): less noise for users; `Debug` while developing. ## Building steps @@ -27,11 +28,12 @@ Rust and the web view write to one log, so a problem can be followed across the |---|---|---| | 2026-02-11 | Log plugin, `Logger` with `TauriLogStrategy` | `80c3925` | | 2026-02-22 | Credentials removed from logs | `8d6bcac` | +| 2026-11-03 | The dialog logs no form values | `3c87afd` | +| 2026-11-03 | Client settings logged instead of the URI | `5f6efaa` | +| 2026-11-03 | `Info` in release builds | `119bf4e` | ## Limits and open points -- The level is `Debug` in release builds too. PR #32 ("remove log when in production") only took the URI out. -- `ConnectionDialog` logs the form values and the connection at `trace`, password included. `trace` is below the `Debug` filter today, so nothing is written. Lowering the level to `Trace` would put passwords in the log. - Rotation and size limits are the plugin's defaults, nothing is configured. Issue #18 asks for more logs, a log file and rotation. ## Where the code is @@ -40,4 +42,4 @@ Rust and the web view write to one log, so a problem can be followed across the |---|---| | Plugin setup | `src-tauri/src/lib.rs` | | UI logger | `src/domains/logger/Logger.ts`, `src/domains/logger/strategies/`, `src/composables/useLogger.ts` | -| Masked URI | `build_connection_string` in `src-tauri/src/drivers/mongo_official_driver.rs` | +| Client log line | `connect` in `src-tauri/src/drivers/mongo_official_driver.rs` | diff --git a/docs/features/query-tab.md b/docs/features/query-tab.md index 6923e78..3d4fe5a 100644 --- a/docs/features/query-tab.md +++ b/docs/features/query-tab.md @@ -2,59 +2,51 @@ ## Purpose -In a collection's tab the user writes a filter in MongoDB shell syntax, e.g. `{ age: { $gt: 18 }, _id: ObjectId("…") }`, runs it and sees the matching documents. This is how Monolens reads data today. +In a collection's tab the user writes a filter, a sort and a projection in MongoDB shell syntax, e.g. `{ age: { $gt: 18 }, _id: ObjectId("…") }`, runs it and pages through the matching documents. This is how Monolens reads data today. ## How it works **The view** (`TabViewQuery`) -- A query input, an Execute button, a limit dropdown (10, 25, 50, 100, 200; default 50), a Refresh button and the switch between table and JSON view. -- When the view mounts it runs the query once. Execute, Refresh and `Ctrl+Enter` run it again. -- `execute()`: - 1. An empty input becomes `{}`. - 2. `parseMongoDbQuery` parses the input. A parse error is a toast, and nothing is sent. - 3. It invokes `start_find_documents` with the active connection, the tab's database and collection, the filter, `skip: 0` and the limit, then `await_task_result`. - 4. It stores `{ documents, totalCount }`. - 5. If documents came back, it takes the field names of the first one for the autocomplete. +- Inputs for filter (default `{ }`), sort and projection, an Execute button that turns into Cancel while a query runs, a limit dropdown (10, 25, 50, 100, 200; default 50), Refresh (this page again), the switch between table and JSON view, and the page navigation: "51–100 of 1,234" with previous and next. A total with "~" is an estimate. +- When the view mounts it runs the query once. Execute, `Mod+Enter` (in the active tab only) and a new limit run what is typed, from the first page; a new limit only takes effect when the inputs parse. Previous, next and Refresh repeat the query that was run last, with another skip; what is typed since doesn't count until it is executed. +- `parseQueryTexts` parses the three inputs (`queryState.ts`). An empty input or `{ }` means none. A parse error names the input ("Sort: Unexpected token: EOF") and nothing is sent. +- The query goes through `ConnectionServiceIPC.findDocuments(connectionId, query)` as a cancellable task; a new query cancels one still running. A failed query is a toast with the server's message; the old results stay. +- The autocomplete knows the fields of all documents of the last result. - The documents go to the table or the JSON view, see [document-views.md](document-views.md). **The query input** (`QueryInput`) -- A CodeMirror 6 editor, kept to one line, with the One Dark theme. A clear button (X) appears when the query is not empty and resets it to `{ }`. -- Autocomplete (`useMongoAutoComplete` in `src/domains/mongo`): - - After a colon it offers the query operators (`$eq` … `$text`) and the shell functions (`ObjectId`, `ISODate`, `NumberLong`, `NumberDecimal`, `Timestamp`, `RegExp`), which insert their parentheses. - - After `{` or `,` it offers the field names of the last result, the top-level operators `$and`, `$or`, `$nor`, `$text`, and the operators. +- A CodeMirror 6 editor, kept to one line, with the One Dark theme and an optional placeholder. A clear button (X) appears when the input is not empty and resets it to `{ }`. +- Autocomplete (`useMongoAutoComplete` in `src/domains/mongo`): after a colon the query operators and the shell functions; after `{` or `,` the field names, the top-level operators `$and`, `$or`, `$nor`, `$text`, and the operators. **The parser** (`src/utils/queryParser`) -- `generateTokens` turns the text into tokens: braces, brackets, parentheses, colon, comma, strings in `"` or `'` with backslash escapes, numbers (digits, `.`, a leading `-`), and identifiers (`[a-zA-Z_$][a-zA-Z0-9_$]*`). -- `createParser().parseValue()` reads objects, arrays, strings, numbers, `true`, `false`, `null` and function calls. Keys may be identifiers or strings. A trailing comma is allowed. -- A function call becomes extended JSON (`transformFunction`): +- `generateTokens`: braces, brackets, parentheses, colon, comma, strings in `"` or `'` with backslash escapes, numbers with decimals and exponents, regex literals `/pattern/flags`, identifiers including dots (`user.address.city` as an unquoted key). An unterminated string or regex is an error. +- `createParser().parseQuery()` reads objects, arrays, strings, numbers, regexes, `true`, `false`, `null` and function calls, `new` in front of a function is ignored. Keys may be identifiers or strings, a trailing comma is allowed, a missing comma is an error, and nothing may follow the value. +- Shell functions become extended JSON that bson in Rust reads as BSON types; every function checks its arguments, so nothing silently becomes a plain object (`transformFunction`): - | Shell | Sent as | - |---|---| - | `ObjectId("x")` | `{ $oid: "x" }` | - | `ISODate("x")`, `Date("x")` | `{ $date: "x" }` | - | `NumberLong(n)` | `{ $numberLong: "n" }` | - | `NumberDecimal(n)` | `{ $numberDecimal: "n" }` | - | `Timestamp(t, i)` | `{ $timestamp: { t, i } }` | - | `BinData(type, "b64")` | `{ $binary: { base64, subType: type } }` | - | `RegExp("p", "o")` | `{ $regex: "p", $options: "o" }` | + | Shell | Sent as | Checked | + |---|---|---| + | `ObjectId("x")` | `{ $oid }`, lowercase | 24 hex characters | + | `ISODate("x")`, `Date("x")`, `new Date(…)` | `{ $date: "" }` | a date or date-time without zone is UTC, with `T` or a space between; milliseconds; no argument is now; invalid is an error | + | `NumberLong(n)`, `NumberInt(n)` | `{ $numberLong }`, `{ $numberInt }` | integers, NumberInt within 32 bits | + | `NumberDecimal(n)` | `{ $numberDecimal }` | a number | + | `Timestamp(t, i)` | `{ $timestamp: { t, i } }` | non-negative integers | + | `BinData(type, "b64")`, `UUID("…")` | `{ $binary: { base64, subType } }` (hex; UUID subtype `04`) | subtype 0–255, base64, 32 hex characters | + | `RegExp("p", "o")`, `/p/o` | `{ $regularExpression: { pattern, options } }` | options from `i m s x u`, sorted | - Any other function is an error. - -**In Rust** (`find_documents` of the MongoDB driver) - -- `bson::to_document` turns the JSON filter into BSON. bson's serializer reads objects that look like extended JSON as BSON types, so `{ $oid }`, `{ $numberLong }`, `{ $numberDecimal }`, `{ $timestamp }` and `{ $date }` with an RFC 3339 string become real types. -- `find` with `skip`, `limit` and `allowDiskUse(true)`. The cursor is read into a list of JSON values. -- `count_documents` with the same filter gives `totalCount`. +**In Rust**: `start_find_documents(connectionId, query)` runs find (filter, sort, projection, skip, limit 1–10000, `allowDiskUse`) and the count at the same time: `estimatedDocumentCount` for an empty filter (marked as estimate), `countDocuments` otherwise. Documents return as relaxed extended JSON, see [document-views.md](document-views.md). ## Decisions - **A parser of our own** (`0c738f1`) for the shell syntax: unquoted keys, single quotes and shell functions, which JSON doesn't allow. The history gives no reason for not using a library. -- **Extended JSON as the format between UI and Rust.** The filter crosses IPC as JSON, and bson turns the extended-JSON objects into types without code of our own. +- **Extended JSON between UI and Rust.** The query crosses IPC as JSON, and bson turns the extended-JSON objects into types. Since `094ed26` the parser checks every function, because what bson can't read silently matched nothing (e.g. `ISODate("2024-01-01")`, which bson only reads as a full RFC 3339 date). +- **A regex is a BSON regex** (`094ed26`), not the `$regex` operator, so it also works inside `$in`. - **CodeMirror with autocomplete** (`9f677e1`). - **Run on open**: a new tab shows the first documents at once. +- **Paging repeats the last executed query** (`16b6cd7`), so typing doesn't change what the next page means. +- **An empty filter is counted from metadata** (`5a30f74`): counting a big collection with `countDocuments` scans it. The UI marks the number as estimate. ## Building steps @@ -68,32 +60,24 @@ In a collection's tab the user writes a filter in MongoDB shell syntax, e.g. `{ | 2026-03-02 | Header and query controls stay in place while the data scrolls | `0a88369` | | 2026-03-02 | Clear button | `17ac29b` | | 2026-03-02 | Table view, limit dropdown, view switch | `ead8967` | +| 2026-11-03 | Parser: checked shell functions, UTC dates, regex literals, `NumberInt`, `UUID`, `new`, dotted keys, exponents, nothing after the query | `094ed26` | +| 2026-11-03 | `Mod+Enter` only in the active tab; each tab queries its own connection | `95a3e7b`, `afd84c7` | +| 2026-11-03 | Rust: one `FindQuery` with sort and projection, the count alongside, the server's message on errors | `5a30f74` | +| 2026-11-03 | Sort and projection inputs, paging with the total, cancel, error toasts, autocomplete from all documents | `16b6cd7` | ## Limits and open points -Read from the code on 2026-11-03, not reproduced in the running app: - -- **Only filter and limit.** `skip` is always 0, so there is no paging. Projection and sort are missing too, although issue #12 (closed) listed them. The Rust command already takes `skip`. -- **`totalCount` is computed but never shown.** Every query also counts all matches, a second full query on the server. -- **Server errors are swallowed.** `execute()` has `try`/`finally` without `catch`: a failed query shows no toast, the old results stay, and the rejection is unhandled. It also bypasses `ConnectionServiceIPC.findDocuments` and `abortableInvoke`, so it can't be cancelled and its error isn't unwrapped. -- **The autocomplete knows only the first document's fields.** `new Set(...arrays)` uses only its first argument. -- **Shell functions that don't become BSON types** stay plain objects and match nothing, without an error: - - `ISODate("2024-01-01")`: bson only reads a full RFC 3339 string like `2024-01-01T00:00:00Z` as a date - - `ObjectId` with a string that is not 24 hex characters - - `BinData(0, "…")`: bson expects the subtype as a hex string -- `RegExp` becomes the `$regex` operator, which works as the condition of a field but not inside `$in`. -- **Not supported**: dotted keys without quotes (`user.name: 1` fails; `"user.name": 1` works), `/regex/` literals, `new Date()`, `NumberInt`, `UUID`, exponents (`1e5`), comments. -- Text after the first complete value is ignored: `{ a: 1 } }` runs as `{ a: 1 }`. -- `ConnectionServiceIPC.findDocuments` exists and is unused. -- The test named "throws" in `query-parser.test.ts` checks nothing: it parses `{ a: 1, }`, which succeeds. -- Open issues: #11 a query builder, #41 building queries by drag and drop, #13 export a query as code, #42 `Ctrl+Enter` runs in every tab. +- Find only: no aggregation, no `explain`, no `$where` with JavaScript functions, no comments in the inputs, no `NumberDouble`, `MinKey` or `MaxKey`. +- The estimate of an unfiltered collection may be off, e.g. after an unclean shutdown or on a sharded cluster. +- Open issues: #11 a query builder, #41 building queries by drag and drop, #13 export a query as code. ## Where the code is | What | Path | |---|---| | View | `src/components/tabs/views/TabViewQuery.vue` | +| Parsing the inputs, paging | `src/components/query/queryState.ts` (tests in `src/components/query/test/`) | | Input and autocomplete | `src/components/query/QueryInput.vue`, `src/domains/mongo/index.ts` | | Parser and tests | `src/utils/queryParser/`, `src/utils/queryParser/test/` | -| Command | `src-tauri/src/database/commands/start_find_documents.rs` | -| Find and count | `find_documents` in `src-tauri/src/drivers/mongo_official_driver.rs`, `src-tauri/src/database/models/find_document.rs` | +| Command | `src-tauri/src/database/commands/start_find_documents.rs`, `FindQuery` in `src-tauri/src/database/models/find_document.rs` | +| Find, count, conversion | `find_documents` in `src-tauri/src/drivers/mongo_official_driver.rs`, `src-tauri/src/drivers/documents.rs` | diff --git a/docs/features/shortcuts.md b/docs/features/shortcuts.md index 109cfd8..503a275 100644 --- a/docs/features/shortcuts.md +++ b/docs/features/shortcuts.md @@ -7,19 +7,22 @@ Common actions have a key: close the tab, disconnect, run the query. ## How it works - `CommandRegistry` (in `src/domains/shortcuts`, created once by the domain registry) maps command ids to `{ id, name, execute }`. `executeCommand(id)` runs the command, then notifies every subscriber of that id. `subscribe` and `unsubscribe` manage the subscribers. `disableExecution` and `enableExecution` switch all commands off and on; nothing calls them yet. -- The `shortcuts` plugin (`src/plugins/shortcuts.ts`) registers the commands with the stores and adds one global `keydown` listener. `eventToKeyString` builds a key string like `ctrl+shift+d`: modifiers in the order ctrl, shift, alt, meta, then the lowercase key. On a match with a default binding the listener calls `preventDefault()` and runs the command. +- The `shortcuts` plugin (`src/plugins/shortcuts.ts`) registers the commands with the stores and adds one global `keydown` listener. `eventToKeyString` builds a key string like `mod+shift+d`: `mod` is the platform's command key (Cmd on macOS, Ctrl elsewhere), then `ctrl` (macOS) or `meta` (elsewhere), shift, alt, and the lowercase key. On a match with a default binding the listener calls `preventDefault()` and runs the command. - The bindings (`DefaultBindings`): | Key | Command | What it does | |---|---|---| - | `Ctrl+W` | `tab.close` | `tabsStore.closeActiveTab()` | - | `Ctrl+Shift+D` | `connection.disconnect` | `connectionStore.disconnect()` | - | `Ctrl+Enter` | `event.execute` | Nothing itself. `TabViewQuery` subscribes to it and runs its query | + | `Mod+W` | `tab.close` | `tabsStore.closeActiveTab()` | + | `Mod+Shift+D` | `connection.disconnect` | disconnects the connection of the active tab, closing its tabs | + | `Mod+Enter` | `event.execute` | Nothing itself. Every `TabViewQuery` subscribes and runs its query only if its tab is active | + +- On macOS, Tauri's default menu binds `Cmd+W` to "Close Window". `src-tauri/src/menu.rs` builds that menu without it (app menu, Edit for copy and paste, View, Window with minimize and maximize), and `lib.rs` uses it on macOS only. ## Decisions - **Commands decoupled from keys** (`748c02a`, issue #22). A command has an id and a name, and the bindings are a separate list, so they can later be configured or listed in a command palette. - **Events through the same registry.** `event.execute` is a command without its own action; components subscribe to it. +- **`mod` instead of `ctrl`** (`95a3e7b`): Mac users expect Cmd. Without the menu change `Cmd+W` would close the app. ## Building steps @@ -27,14 +30,14 @@ Common actions have a key: close the tab, disconnect, run the query. |---|---|---| | 2026-02-17 | Registry, plugin, the three bindings | `748c02a` | | 2026-02-24 | The query view subscribes to `Ctrl+Enter` | `e0256f3` | +| 2026-11-03 | `unsubscribe` with `indexOf` (it used `findIndex`, which called the subscriber and removed the first one); `mod` key; macOS menu without "Close Window"; tests | `95a3e7b` | +| 2026-11-03 | `Mod+Enter` only in the active tab (issue #42); `Mod+Shift+D` disconnects the active tab's connection | `afd84c7` | ## Limits and open points -- **`Ctrl+Enter` runs the query in every mounted query view**, not only in the active tab (issue #42). Every view instance subscribes when it mounts, and views in hidden tabs stay mounted, see [tabs.md](tabs.md#limits-and-open-points). -- **`unsubscribe` is broken.** It calls `subs.findIndex(subscriber)`, which uses the subscriber as the predicate. For the query view that calls `execute()`, whose promise is truthy, so the first subscriber is removed, whichever it is, and a query runs. It should be `indexOf`. -- The bindings are fixed. On macOS they need `Ctrl`, not `Cmd`, because `Cmd` reports as `meta`. +- The bindings are fixed. Nothing lists them in the UI. - The listener is global, so the keys also act while typing in an input. -- Nothing lists the shortcuts in the UI. +- The `Cmd` keys and the macOS menu were not checked on a Mac. ## Where the code is @@ -44,4 +47,5 @@ Common actions have a key: close the tab, disconnect, run the query. | Commands and bindings | `src/domains/shortcuts/config/commands.ts`, `default-binding.ts`, `src/domains/shortcuts/models/` | | Key string | `src/domains/shortcuts/utils/eventToKeyString.ts` | | Listener | `src/plugins/shortcuts.ts` | -| Subscriber | `src/components/tabs/views/TabViewQuery.vue` | +| macOS menu | `src-tauri/src/menu.rs`, `src-tauri/src/lib.rs` | +| Tests | `src/domains/shortcuts/test/` | diff --git a/docs/features/tabs.md b/docs/features/tabs.md index 3c5379a..dbf8cf7 100644 --- a/docs/features/tabs.md +++ b/docs/features/tabs.md @@ -2,25 +2,28 @@ ## Purpose -Several collections can be open at once, each in its own tab with its own query and results. Closing a tab returns to the one used before. +Several collections can be open at once, also from different connections, each in its own tab with its own query and results. Closing a tab returns to the one used before. ## How it works -- A tab is `{ id, context: { connection, database, collection }, type }`. `id` is a `crypto.randomUUID()`. `type` is a `TabKind`; only `QUERY` is ever created. +- A tab is `{ id, context: { connectionId, database, collection }, type }`. `id` is a `crypto.randomUUID()`. `type` is a `TabKind`; only `QUERY` is created. - `tabsStore` keeps the tabs in a `Map`, the `activeTab` id, and `tabHistory`, the ids in the order they were last active. - `addTab` creates a tab. - - `hasContext` finds an open tab of the same type for the same connection id, database and collection. The sidebar uses it so a collection is opened only once. + - `hasContext` finds an open tab of the same type for the same connection, database and collection. The sidebar uses it so a collection is opened only once. - `switchTab` moves the id to the end of the history and makes it active. - - `deleteTab` removes the tab and its history entry. If it was active, the last tab in the history becomes active. - - `closeActiveTab` deletes the active tab. `Ctrl+W` is bound to it. -- `TabsContainer` draws the tab strip: the collection name and a close icon per tab, the active one underlined. Below it each tab gets a container that is shown with `v-show` only while its tab is active. Inside it a `KeepAlive` holds the view for the tab kind, keyed by the active tab id. -- The view for every kind is `TabViewQuery`, see [query-tab.md](query-tab.md). It reads its context from `tabsStore.getActiveTab()`, not from a prop. + - `deleteTab` removes the tab and its history entry. If it was active, the last tab in the history becomes active, or none when no tab is left. + - `closeTabsOf(connectionId)` closes every tab of a connection; disconnecting calls it. + - `closeActiveTab` deletes the active tab. `Mod+W` is bound to it. +- `TabsContainer` draws the tab strip: the collection name, below it the connection's name and the database, and a close icon; the active one is underlined. Below the strip every tab has exactly one view, shown with `v-show` while its tab is active and kept mounted otherwise, so it keeps its query and results. +- The view gets its `tabId` and reads its own tab, never the active one. The view for every kind is `TabViewQuery`, see [query-tab.md](query-tab.md). ## Decisions -- **Tabs stay mounted** (`573dfc0`): `v-show` and `KeepAlive` instead of `v-if`, so a tab keeps its query and results. +- **Tabs stay mounted** (`573dfc0`): `v-show` instead of `v-if`, so a tab keeps its query and results. - **A history instead of "the tab to the left"** (`be509cf`): closing a tab goes back to the one used before. - **One tab per collection and kind**: the sidebar switches to an open tab instead of adding a second one. +- **One view per tab, keyed by its own id** (`afd84c7`). Before, every tab's container rendered a view keyed by the active tab, so views multiplied: each new copy ran its query, and `Ctrl+Enter` ran in all of them (issue #42). +- **A tab belongs to its connection** (`afd84c7`): closing the connection closes its tabs, and the label shows connection and database. ## Building steps @@ -33,24 +36,20 @@ Several collections can be open at once, each in its own tab with its own query | 2026-02-22 | Collection of the active tab highlighted in the sidebar | `fdf801a` | | 2026-02-24 | `TabViewQuery` as the view of a tab | `e0256f3` | | 2026-02-26 | Tabs stay mounted | `573dfc0` | +| 2026-11-03 | `Mod+W` (Cmd on macOS) | `95a3e7b` | +| 2026-11-03 | Context by connection id, one view per tab, connection and database in the label, `closeTabsOf`, no stale active tab | `afd84c7` | ## Limits and open points -Read from the code on 2026-11-03, not reproduced in the running app: - -- **The views multiply.** The view in every tab's container is keyed by the *active* tab id, not by its own tab. Each container therefore creates an instance for every tab that becomes active, and every new instance runs its query on mount. With n tabs open, opening or revisiting a tab can run up to n queries. The visible container keeps its state, the hidden copies only cost queries. -- **`Ctrl+Enter` runs the query in every instance** (issue #42), see [shortcuts.md](shortcuts.md#limits-and-open-points). -- **Closing a tab can run a query and unsubscribe the wrong view.** The unmounted views call `CommandRegistry.unsubscribe`, whose `findIndex(subscriber)` calls `execute` as a predicate, see [shortcuts.md](shortcuts.md#limits-and-open-points). -- **Tabs outlive their connection.** Disconnect and switching connections don't close them, and the view queries the active connection with the tab's database and collection names. -- The tab shows only the collection name. Collections with the same name in two databases look the same. - Tabs are not saved across restarts. - `INDEXES`, `AGGREGATION`, `EXPORTER`, `IMPORTER` and `STATISTICS` exist as kinds and are all mapped to `TabViewQuery`. Nothing creates them. +- Every open tab keeps its view and results in memory; many tabs with large results cost memory. ## Where the code is | What | Path | |---|---| -| Store | `src/stores/tabsStore.ts` | +| Store | `src/stores/tabsStore.ts` (tests in `src/stores/test/tabsStore.test.ts`) | | Models | `src/domains/tabs/models/`, `src/domains/context/models/context.ts` | -| Strip and containers | `src/components/tabs/TabsContainer.vue` | +| Strip and views | `src/components/tabs/TabsContainer.vue` | | Opening from the sidebar | `handleCollectionSelect` in `src/components/AppSidebar.vue` | diff --git a/docs/plans/README.md b/docs/plans/README.md index 0484776..2e541b4 100644 --- a/docs/plans/README.md +++ b/docs/plans/README.md @@ -13,3 +13,4 @@ A plan names the GitHub issues it covers. | Plan | Built | Now described in | |---|---|---| +| [working-state.md](implemented/working-state.md) | 2026-11-03: the bugs of the documentation fixed, several connections at once, connection strings with options, paging, count, sort and projection; plus a full package update | [connection-manager.md](../features/connection-manager.md), [connect-and-explorer.md](../features/connect-and-explorer.md), [query-tab.md](../features/query-tab.md) and the other feature files | diff --git a/docs/plans/implemented/.gitkeep b/docs/plans/implemented/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/docs/plans/implemented/working-state.md b/docs/plans/implemented/working-state.md new file mode 100644 index 0000000..a72d2e0 --- /dev/null +++ b/docs/plans/implemented/working-state.md @@ -0,0 +1,212 @@ +# Working state: bugs fixed, querying complete, several connections at once + +> **Status:** implemented on 2026-11-03 on `fix/working_state` (`520f438` to `d5ff9e3`, docs in the following commit). The feature files describe the result; the most changed are [connection-manager.md](../../features/connection-manager.md), [connect-and-explorer.md](../../features/connect-and-explorer.md) and [query-tab.md](../../features/query-tab.md). +> +> Deviations: +> - **A package update came in between**, at Kilian's request after step 1: pnpm 12 pinned (`e19ec32`), all npm packages (`3493da8`, with the TanStack Table 9 migration and `@lucide/vue` for the deprecated `lucide-vue-next`), forms moved from vee-validate to TanStack Form because `@vee-validate/zod` only supports zod 3 (`3c87afd`), all crates with keyring 4 (`955c08f`). TypeScript stays on 6, vue-tsc can't run on 7. +> - Steps 2 and 3 swapped places. Step 4 only brought the credential trait; model v2, migration and `ClientOptions` came together in step 5 with the TypeScript model, so every commit leaves a working app. Removing `get_connection` in step 6 also removed `ConnectionRepositoryIPC` and the `save_connection(s)` wrappers of step 12. +> - Not in the plan: the user info of a stored connection string goes back into it in memory before parsing (the driver checks `authMechanism` against it); the store reloads the list from Rust after an edit (the form's copy could hold credentials of a string); `update_connection` keeps the id of the path; closing the last tab leaves no stale active tab; the dialog validates instead of disabling its buttons. +> - The Tauri Linux prerequisites were not installed on the development machine, so the first `cargo check` only reused builds from March; they were installed during the round. +> +> Verified: 141 frontend tests, 81 Rust unit tests, 11 integration tests against a `mongo:8` container (root and a user with `read` on one database), the UI in a browser with a stand-in for the IPC. **Not verified**: the app in a Tauri window, `pnpm tauri build`, `mongodb+srv://`, TLS against a server, the migration of a real `connections.json` (unit-tested with its shape), macOS (`Cmd` and the menu) and Windows. + +## Context + +Monolens was picked up again on 2026-11-03 after a pause since March. The documentation written that day (`83ee6a3`) lists what `develop` does and what is broken. Kilian's goal for this round: a working state, with queries and everything around them, as the base for developing the tool further. Writing data comes after. + +What is broken or missing today, read from the code. The feature files have the details: + +- **Build**: `pnpm build` and `pnpm tauri build` fail on an unused import in `tokens.test.ts` ([app-shell.md](../../features/app-shell.md)). +- **Connections**: + - The Rust pool never drops a driver, so edits and a corrected password need a restart ([connect-and-explorer.md](../../features/connect-and-explorer.md)). + - The host field suggests `mongodb+srv://`, but only host and port work ([connection-manager.md](../../features/connection-manager.md)). + - `connectionType` is never read, and its TypeScript value `"SharedCluster"` doesn't match Rust's. + - A keyring failure reads as a host problem ([credential-storage.md](../../features/credential-storage.md)). +- **Connection test**: a failing stage isn't marked failed, and a user without the `serverStatus` privilege fails although connecting would work ([connection-test.md](../../features/connection-test.md)). +- **Tabs and shortcuts** ([tabs.md](../../features/tabs.md), [shortcuts.md](../../features/shortcuts.md)): + - Every tab's container keys the view by the active tab, so views multiply and run the query several times. + - `Ctrl+Enter` runs in every view (#42). + - `CommandRegistry.unsubscribe` calls the subscriber. + - Tabs survive a switch of connection and then query the new server. +- **Queries** ([query-tab.md](../../features/query-tab.md)): + - Errors from the server are swallowed. + - A query can't be cancelled. + - No paging, sort or projection; the total is counted but never shown. + - The autocomplete only knows the first document's fields. + - Several shell functions silently become filters that match nothing (`ISODate("2024-01-01")`, a bad `ObjectId`, `BinData`). +- **Errors and logs**: `FindDocumentsFailed` drops the server's message ([error-handling.md](../../features/error-handling.md)). The log level is `Debug` in release builds, and the dialog logs passwords at `trace` ([logging.md](../../features/logging.md)). +- **Display**: `Int64` above 2^53 loses precision ([document-views.md](../../features/document-views.md)). +- **Dead code** ([overview.md](../../architecture/overview.md#commands)): + - wrappers for Rust commands that no longer exist + - an unused repository layer + - two unused synchronous commands + - leftovers of the Vite template + +## Decisions + +| # | Decision | Why | Status | +|---|---|---|---| +| 1 | **Several connections at once.** Each open connection is a node in the sidebar with its databases and collections. Tabs of all open connections sit side by side, and each tab belongs to its connection | Kilian's choice. Working with two servers (e.g. staging and local) is common | agreed | +| 2 | **Querying gets paging, the total count, sort and projection** | Kilian's choice. Issue #12 listed them and was closed without them | agreed | +| 3 | **Connection strings plus options.** A connection is either host and port with option fields (TLS, replica set, timeout), or a `mongodb://` / `mongodb+srv://` connection string | Kilian's choice. Closes issue #4 properly | agreed | +| 4 | **Also in this round:** remove dead code; a connection test that goes on without the `serverStatus` privilege; a dialog of the app instead of `confirm()` for deleting; `Cmd` instead of `Ctrl` on macOS | Kilian's choice | agreed | +| 5 | Commands that talk to MongoDB take a **connection id**, not the connection object. Rust loads the saved connection and its password itself. The connection test keeps taking the form data, because it may not be saved | The UI's copy can be stale after an edit, and the password no longer passes through the web view for these calls. The pool is keyed by id anyway | agreed | +| 6 | **A driver leaves the pool** on disconnect, on update and delete of its connection, and when its first command after creation fails. The next command builds a new one | Fixes "edits need a restart" and "a wrong password sticks" | agreed | +| 7 | **Connection model v2** with `target` (`Host { host, port }` or `Uri { uri }`) and `options`. `connectionType` is dropped. `STORAGE_VERSION` becomes 2; a v1 file is migrated on load and written back. Keyring entries stay as they are (keyed by id) | The topology is expressed by the connection string or the replica set option. `connectionType` was never read | agreed | +| 8 | **URI mode keeps everything but the credentials in the URI.** On save, Rust moves credentials found in the URI out: the username to `authentication.username`, the password to the keyring. The stored URI has no user info. The option fields apply to host mode only | One place for each setting, and no password in `connections.json` | agreed | +| 9 | **The client is built from `ClientOptions`**, not from a URL string. Host mode uses the builder, URI mode `ClientOptions::parse`; then the credential is set | Replaces the string building with `url`, its percent-encoding, and the `mongodb://` prefix that broke SRV | agreed | +| 10 | **Host-mode options:** TLS on/off, CA file, allow invalid certificates, replica set name, server selection timeout in seconds. Empty means the driver's default (30 s) | What issue #4 and decision 3 ask for, without client certificates (X.509 is out of scope) | agreed | +| 11 | **Disconnect closes the tabs of that connection.** `Mod+Shift+D` disconnects the connection of the active tab. Deleting an open connection disconnects it first | With several connections, a tab without its connection has nothing to show | agreed | +| 12 | **Sidebar per connection:** refresh, edit and disconnect on each connection node; collections are cached per connection in the store. The header dropdown opens further connections and has "Add new connection…". Editing an open connection drops its driver and reloads its databases; its tabs stay | Editing must work while connected, and the grid only shows when nothing is open | agreed | +| 13 | **A tab shows the collection, with connection and database as second line.** The view is keyed by its own tab id, gets the tab id as a prop and reads its own context | Fixes the multiplied views and the wrong connection; the label tells two `users` collections apart | agreed | +| 14 | **The count runs next to the find:** `estimated_document_count` for an empty filter, `count_documents` otherwise | Counting a big collection with `countDocuments` scans it. The estimate reads metadata | agreed | +| 15 | **Documents leave Rust as relaxed extended JSON** (dates as ISO strings), except `Int64` beyond ±2^53, which stays `{ "$numberLong": "…" }` | Readable dates in the JSON view, and no silent precision loss | agreed | +| 16 | **Parser fixes and additions:** `ISODate`, `Date` and `new Date` with a date-only or zone-less string become UTC RFC 3339, an invalid date is a parse error; `ObjectId` must be 24 hex characters; `BinData` subtype as hex; `RegExp(…)` and `/re/flags` literals become `$regularExpression`, so they also work inside `$in`; `NumberInt`; `UUID(…)` as binary subtype 4; dotted keys without quotes; numbers with exponents; nothing may follow the value | Shell syntax users type every day, and every case that silently matched nothing becomes an error or works | agreed | +| 17 | **Query errors show the server's message**, e.g. "unknown operator: $foo". `FindDocumentsFailed` carries it | It names the mistake in the query | agreed | +| 18 | **Connection test stages:** Initialize, Ping (also authenticates), Reading status (`serverStatus`, *skipped* without the privilege, the test goes on), Detecting version (`buildInfo`, needs no privilege; the version goes into the title), Connected. A failing stage is marked failed. The driver first emits all stages as pending, so the titles exist only in Rust. Events carry a test id from the UI | Decision 4's lenient stage, plus the bugs of the stepper | agreed | +| 19 | **`DriverError::CredentialsUnavailable`** for keyring failures, with its own message | The message pointed at host and port | agreed | +| 20 | **Log level `Info` in release, `Debug` in debug builds.** The dialog no longer logs form values | Less noise, no password even at `trace` | agreed | +| 21 | **Bindings as `mod+…`:** `Ctrl` on Linux and Windows, `Cmd` on macOS. On macOS Tauri's default menu binds `Cmd+W` to "Close Window"; `lib.rs` builds the default menu without that item on macOS | Decision 4. Without the menu change `Cmd+W` closes the app instead of the tab | agreed | +| 22 | **A `ConfirmDialog` with a `useConfirm()` composable**, built from the existing shadcn `Dialog` | Decision 4; also needed later for deleting data | agreed | +| 23 | **Tests:** vitest with `happy-dom` and Tauri's `mockIPC` for stores and `abortableInvoke`; Rust unit tests with an in-memory credential store behind a `CredentialStore` trait and temp directories; Rust integration tests against a local MongoDB marked `#[ignore]`, run with `cargo test -- --ignored` and `MONGODB_TEST_URI` | The rule "every piece of logic gets unit tests". The keyring and MongoDB can't be in the default test run | agreed | + +## How it works afterwards + +**`connections.json`, version 2** + +```json +{ + "version": 2, + "connections": { + "": { + "id": "", + "name": "Local", + "connector": "MongoDb", + "target": { "kind": "Host", "host": "localhost", "port": 27017 }, + "authentication": { "kind": "BASIC", "username": "root", "password": null, "database": "admin" }, + "options": { + "tls": false, + "tlsCaFile": null, + "tlsAllowInvalidCertificates": false, + "replicaSet": null, + "serverSelectionTimeoutSecs": null + } + } + } +} +``` + +A connection string looks like `"target": { "kind": "Uri", "uri": "mongodb+srv://cluster0.example.net/?retryWrites=true" }`, and its `options` are ignored. The v1 → v2 migration turns `uri` and `port` into a `Host` target, drops `connectionType` and sets default options. + +**Commands** + +| Command | Change | +|---|---| +| `start_test_connection(connection, testId)` | Takes the form data as before, plus the test id the events echo | +| `start_connect(connectionId)` | By id; drops the driver again if listing the databases fails | +| `disconnect(connectionId)` | New: drops the pooled driver | +| `list_collections(connectionId, databaseName)` | By id, loads the password | +| `start_find_documents(connectionId, databaseName, collectionName, filter, sort, projection, skip, limit)` | Sort and projection added; the count runs alongside | +| `update_connection`, `delete_connection` | Also drop the pooled driver | +| `test_connection`, `get_databases`, `get_connection` | Removed (unused) | + +**UI** + +``` ++--------------------------------------------------------------+ +| [logo] | Connections v | ++------------------+-------------------------------------------+ +| EXPLORER | users orders users | +| v Local ⟳ ✎ ⏏ | Local·shop Local·shop Staging·crm | +| v shop +-------------------------------------------+ +| users | Filter [{ age: { $gt: 18 } } ] ▶ | +| orders | Sort [{ createdAt: -1 }] Projection [] | +| > Staging ⟳ ✎ ⏏ | 51–100 of 1,234 ‹ › Limit 50 ▦ {} | ++------------------+-------------------------------------------+ +``` + +The grid shows only while no connection is open. + +## Steps + +Every step is one commit with its tests. They run in this order, because later steps use the earlier ones. + +1. **Build**: remove the unused import in `tokens.test.ts`. The test named "throws" gets a name and an assertion. +2. **Shortcuts**: + - `unsubscribe` uses `indexOf`. + - `eventToKeyString` emits `mod`, and the bindings become `mod+w`, `mod+shift+d`, `mod+enter`. + - On macOS, `lib.rs` builds Tauri's default menu without "Close Window". + - Tests for the registry and the key strings. +3. **Query parser**: decision 16, with tests per case. +4. **Rust: credential store and model v2**: + - `CredentialStore` trait with the keyring implementation and an in-memory one for tests. + - `ConnectionTarget`, `ConnectionOptions`. + - `STORAGE_VERSION` 2 with the v1 migration in `FileRepository.load_storage`. + - Moving credentials out of URIs on create and update. + - Tests for migration, save, update, delete and credential moving, with temp directories. +5. **Rust: client options**: build `ClientOptions` from host mode or a URI, set the credential and TLS options (decisions 9 and 10). Unit tests for the options built. +6. **Rust: commands by id and the pool**: + - Decisions 5 and 6. + - `disconnect`. + - `hydrate` everywhere. + - `CredentialsUnavailable`. + - The log level. + - Removing `test_connection`, `get_databases` and `get_connection`. + - Unit tests for pool invalidation (with a fake driver from the factory) and `DriverError::user_message`. + - Integration tests marked `#[ignore]`. +7. **Connection test**: decision 18 in the driver and the command; the store builds its stages from the events; `TestStagesDisplay` shows skipped and failed stages. +8. **Rust: find**: sort, projection, the count strategy, relaxed extended JSON (decisions 14, 15, 17). Integration tests. +9. **UI: connections**: + - TypeScript models v2. + - `ConnectionDialog` with a mode switch (host and port with options, or connection string) and credentials in both modes. + - `ConfirmDialog` and `useConfirm` for deleting. + - The dialog's password logs removed. +10. **UI: several connections**: + - `connectionStore` with open sessions per connection (status, databases, collections, abort controller). + - The sidebar with one node per connection. + - The header dropdown. + - Tabs with `{ connectionId, database, collection }`, keyed by their own id, closed on disconnect. + - `mod+enter` only in the active tab. + - Store tests with `mockIPC`. +11. **UI: query tab**: + - Through `ConnectionServiceIPC.findDocuments` with cancel. + - Error toasts. + - Sort and projection inputs. + - Paging with "x–y of N". + - The page resets when filter, sort, projection or limit change. + - Autocomplete from all documents. +12. **Cleanup**: + - `ConnectionRepository(IPC)` and the `save_connection(s)` wrappers. + - The double load of the connections. + - The guard in the JSON view's watcher. + - Template leftovers: favicon, title, `vite.svg`, `tauri.svg`, `vue.svg`. +13. **Docs**: + - Update the feature files (building steps, "How it works", limits), `overview.md`, `decisions.md`, `CLAUDE.md` (gotchas that are gone) and the root README feature list. + - This plan gets its status note and moves to `implemented/`. + +## Verification + +- Automated, after every step: `pnpm test:frontend --run`, `pnpm build`, `cargo test`, `cargo clippy`. +- From step 6 on, also `cargo test -- --ignored` against `docker run --rm -p 27017:27017 mongo:8`. +- Manually in `pnpm tauri dev` against local containers: + - without authentication + - with authentication: root, plus a user with only `readWrite` on one database for the lenient stage + - a one-node replica set (`--replSet rs0`, `rs.initiate()`) in host mode with the replica set name + - TLS with a self-signed certificate, once with the CA file and once with "allow invalid certificates" + - two connections at once, tabs of both, disconnecting one + - paging, sort, projection, a broken query (toast), cancelling a slow query + - editing an open connection (new password takes effect without restart), deleting an open connection +- Migration: back up `~/.config/org.monolens/connections.json` (three v1 connections), start the new build, check the file is v2 and the connections still connect. +- Can't be verified here: + - `mongodb+srv://`, which needs DNS SRV records, e.g. an Atlas cluster, if Kilian has one. + - `Cmd` and the menu, which need a Mac. + +## Out of scope + +- Writing data (#5, #6, #7, #9, #43) and nested objects on double click (#39). +- Saving column widths. +- A CSP, i18n (#31), log file rotation (#18). +- Query builder (#11, #41), export as code (#13). +- Stage types (#45), quick connections (#40), database statistics (#14), in-memory MongoDB (#28). +- Authentication other than username and password, and client certificates. +- CI. diff --git a/graphify-out/cache/semantic/pd5fd89c46bb5/0c9982fd6b9ddcd4747eb4bc24b703594d6fa8daaf64d10c19e0b18102d37a03.json b/graphify-out/cache/semantic/pd5fd89c46bb5/0c9982fd6b9ddcd4747eb4bc24b703594d6fa8daaf64d10c19e0b18102d37a03.json new file mode 100644 index 0000000..30ba9ef --- /dev/null +++ b/graphify-out/cache/semantic/pd5fd89c46bb5/0c9982fd6b9ddcd4747eb4bc24b703594d6fa8daaf64d10c19e0b18102d37a03.json @@ -0,0 +1 @@ +{"nodes": [{"id": "github_workflows_ci", "label": "ci.yml (GitHub Actions workflow ci)", "file_type": "code", "source_file": ".github/workflows/ci.yml", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "Only Linux is used day to day, so this is where macOS and Windows get compiled and tested at all."}, {"id": "github_workflows_ci_triggers", "label": "CI triggers: push to develop and main, every pull request, workflow_dispatch; contents: read", "file_type": "code", "source_file": ".github/workflows/ci.yml", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "github_workflows_ci_concurrency", "label": "Concurrency group ci-; cancel-in-progress only for pull requests", "file_type": "code", "source_file": ".github/workflows/ci.yml", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "A new push to a pull request makes its running check pointless; every commit on develop and main keeps its own run."}, {"id": "github_workflows_ci_build_job", "label": "build job: matrix ubuntu-latest, macos-latest, windows-latest (fail-fast off, 45 min timeout)", "file_type": "code", "source_file": ".github/workflows/ci.yml", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "One platform failing should not hide whether the others pass."}, {"id": "github_workflows_ci_keep_lf_line_endings", "label": "Keep LF line endings on Windows (git core.autocrlf false)", "file_type": "code", "source_file": ".github/workflows/ci.yml", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "git on the Windows runner would check out CRLF line endings."}, {"id": "github_workflows_ci_tauri_system_packages", "label": "Install the Tauri system packages on Linux (libwebkit2gtk-4.1-dev, build-essential, libxdo-dev, libssl-dev, libayatana-appindicator3-dev, librsvg2-dev, ...)", "file_type": "code", "source_file": ".github/workflows/ci.yml", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "github_workflows_ci_setup_node", "label": "actions/setup-node with Node 24", "file_type": "code", "source_file": ".github/workflows/ci.yml", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "Vite 8 needs Node 20.19 or 22.12 and newer."}, {"id": "github_workflows_ci_pnpm_setup", "label": "pnpm/action-setup with cache (version from packageManager in package.json)", "file_type": "code", "source_file": ".github/workflows/ci.yml", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "github_workflows_ci_rust_toolchain", "label": "Rust stable toolchain (clippy component) with Swatinem/rust-cache on src-tauri", "file_type": "code", "source_file": ".github/workflows/ci.yml", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "github_workflows_ci_frozen_install", "label": "pnpm install --frozen-lockfile", "file_type": "code", "source_file": ".github/workflows/ci.yml", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "github_workflows_ci_frontend_tests", "label": "pnpm test:frontend --run (CI step)", "file_type": "code", "source_file": ".github/workflows/ci.yml", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "github_workflows_ci_pnpm_build", "label": "pnpm build (CI step; also creates dist/, which the Tauri crate embeds)", "file_type": "code", "source_file": ".github/workflows/ci.yml", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "github_workflows_ci_cargo_test", "label": "cargo test in src-tauri (CI step)", "file_type": "code", "source_file": ".github/workflows/ci.yml", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "github_workflows_ci_cargo_clippy", "label": "cargo clippy --all-targets (CI step; warnings do not fail the job)", "file_type": "code", "source_file": ".github/workflows/ci.yml", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "The 2 module_inception warnings are known, so warnings don't fail the job."}, {"id": "github_workflows_ci_integration_job", "label": "integration job: ubuntu-latest, 30 min timeout, MONGODB_TEST_HOST/PORT/USER/PASSWORD pointing at the service", "file_type": "code", "source_file": ".github/workflows/ci.yml", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "Runs the #[ignore] tests against a real MongoDB; service containers exist only on Linux runners; a dev build needs no dist/, so there is no frontend build."}, {"id": "github_workflows_ci_mongo_service", "label": "mongo:8.2 service container (root user, port 27017, mongosh ping health check)", "file_type": "code", "source_file": ".github/workflows/ci.yml", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "github_workflows_ci_seed_test_data", "label": "Create the test user and data: user reader (read on shop) and shop.users with Ada and Bob", "file_type": "code", "source_file": ".github/workflows/ci.yml", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "What the integration tests in mongo_official_driver.rs expect."}, {"id": "github_workflows_ci_ignored_tests", "label": "cargo test -- --ignored (integration tests, CI step)", "file_type": "code", "source_file": ".github/workflows/ci.yml", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}], "edges": [{"source": "github_workflows_ci", "target": "github_workflows_ci_triggers", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": ".github/workflows/ci.yml", "source_location": null, "weight": 1.0}, {"source": "github_workflows_ci", "target": "github_workflows_ci_concurrency", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": ".github/workflows/ci.yml", "source_location": null, "weight": 1.0}, {"source": "github_workflows_ci", "target": "github_workflows_ci_build_job", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": ".github/workflows/ci.yml", "source_location": null, "weight": 1.0}, {"source": "github_workflows_ci", "target": "github_workflows_ci_integration_job", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": ".github/workflows/ci.yml", "source_location": null, "weight": 1.0}, {"source": "github_workflows_ci_build_job", "target": "github_workflows_ci_keep_lf_line_endings", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": ".github/workflows/ci.yml", "source_location": null, "weight": 1.0}, {"source": "github_workflows_ci_build_job", "target": "github_workflows_ci_tauri_system_packages", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": ".github/workflows/ci.yml", "source_location": null, "weight": 1.0}, {"source": "github_workflows_ci_build_job", "target": "github_workflows_ci_setup_node", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": ".github/workflows/ci.yml", "source_location": null, "weight": 1.0}, {"source": "github_workflows_ci_build_job", "target": "github_workflows_ci_pnpm_setup", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": ".github/workflows/ci.yml", "source_location": null, "weight": 1.0}, {"source": "github_workflows_ci_build_job", "target": "github_workflows_ci_rust_toolchain", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": ".github/workflows/ci.yml", "source_location": null, "weight": 1.0}, {"source": "github_workflows_ci_build_job", "target": "github_workflows_ci_frozen_install", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": ".github/workflows/ci.yml", "source_location": null, "weight": 1.0}, {"source": "github_workflows_ci_build_job", "target": "github_workflows_ci_frontend_tests", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": ".github/workflows/ci.yml", "source_location": null, "weight": 1.0}, {"source": "github_workflows_ci_build_job", "target": "github_workflows_ci_pnpm_build", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": ".github/workflows/ci.yml", "source_location": null, "weight": 1.0}, {"source": "github_workflows_ci_build_job", "target": "github_workflows_ci_cargo_test", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": ".github/workflows/ci.yml", "source_location": null, "weight": 1.0}, {"source": "github_workflows_ci_build_job", "target": "github_workflows_ci_cargo_clippy", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": ".github/workflows/ci.yml", "source_location": null, "weight": 1.0}, {"source": "github_workflows_ci_integration_job", "target": "github_workflows_ci_mongo_service", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": ".github/workflows/ci.yml", "source_location": null, "weight": 1.0}, {"source": "github_workflows_ci_integration_job", "target": "github_workflows_ci_tauri_system_packages", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": ".github/workflows/ci.yml", "source_location": null, "weight": 1.0}, {"source": "github_workflows_ci_integration_job", "target": "github_workflows_ci_rust_toolchain", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": ".github/workflows/ci.yml", "source_location": null, "weight": 1.0}, {"source": "github_workflows_ci_integration_job", "target": "github_workflows_ci_seed_test_data", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": ".github/workflows/ci.yml", "source_location": null, "weight": 1.0}, {"source": "github_workflows_ci_integration_job", "target": "github_workflows_ci_ignored_tests", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": ".github/workflows/ci.yml", "source_location": null, "weight": 1.0}, {"source": "github_workflows_ci_seed_test_data", "target": "github_workflows_ci_mongo_service", "relation": "shares_data_with", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": ".github/workflows/ci.yml", "source_location": null, "weight": 1.0}, {"source": "github_workflows_ci_seed_test_data", "target": "src_tauri_src_drivers_mongo_official_driver", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": ".github/workflows/ci.yml", "source_location": null, "weight": 1.0}, {"source": "github_workflows_ci_seed_test_data", "target": "src_tauri_src_drivers_mongo_official_driver_a_user_without_the_server_status_privilege_skips_that_stage", "relation": "shares_data_with", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": ".github/workflows/ci.yml", "source_location": null, "weight": 1.0}, {"source": "github_workflows_ci_seed_test_data", "target": "src_tauri_src_drivers_mongo_official_driver_finds_with_sort_projection_and_paging_and_estimates_an_unfiltered_count", "relation": "shares_data_with", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": ".github/workflows/ci.yml", "source_location": null, "weight": 1.0}, {"source": "github_workflows_ci_ignored_tests", "target": "claude_integration_tests", "relation": "conceptually_related_to", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": ".github/workflows/ci.yml", "source_location": null, "weight": 1.0}, {"source": "github_workflows_ci_integration_job", "target": "docs_architecture_decisions_testing_strategy", "relation": "conceptually_related_to", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": ".github/workflows/ci.yml", "source_location": null, "weight": 1.0}, {"source": "github_workflows_ci_pnpm_setup", "target": "package_packagemanager", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": ".github/workflows/ci.yml", "source_location": null, "weight": 1.0}, {"source": "github_workflows_ci_frontend_tests", "target": "package_scripts_test_frontend", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": ".github/workflows/ci.yml", "source_location": null, "weight": 1.0}, {"source": "github_workflows_ci_pnpm_build", "target": "package_scripts_build", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": ".github/workflows/ci.yml", "source_location": null, "weight": 1.0}, {"source": "github_workflows_ci_pnpm_build", "target": "src_tauri_tauri_conf_build_frontenddist", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": ".github/workflows/ci.yml", "source_location": null, "weight": 1.0}, {"source": "github_workflows_ci_setup_node", "target": "package_devdependencies_vite", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": ".github/workflows/ci.yml", "source_location": null, "weight": 1.0}, {"source": "github_workflows_ci_cargo_clippy", "target": "claude_clippy_baseline", "relation": "conceptually_related_to", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": ".github/workflows/ci.yml", "source_location": null, "weight": 1.0}, {"source": "github_workflows_ci_frozen_install", "target": "claude_pnpm_pinned", "relation": "conceptually_related_to", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": ".github/workflows/ci.yml", "source_location": null, "weight": 1.0}, {"source": "github_workflows_ci_tauri_system_packages", "target": "claude_tauri_prerequisites", "relation": "conceptually_related_to", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": ".github/workflows/ci.yml", "source_location": null, "weight": 1.0}, {"source": "github_workflows_ci_cargo_test", "target": "docs_architecture_decisions_testing_strategy", "relation": "conceptually_related_to", "confidence": "INFERRED", "confidence_score": 0.75, "source_file": ".github/workflows/ci.yml", "source_location": null, "weight": 1.0}, {"source": "github_workflows_ci_triggers", "target": "docs_architecture_decisions_branching_model", "relation": "conceptually_related_to", "confidence": "INFERRED", "confidence_score": 0.75, "source_file": ".github/workflows/ci.yml", "source_location": null, "weight": 1.0}], "hyperedges": [{"id": "github_workflows_ci_check_steps", "label": "CI checks run on every platform (frontend tests, build, cargo test, clippy)", "nodes": ["github_workflows_ci_frontend_tests", "github_workflows_ci_pnpm_build", "github_workflows_ci_cargo_test", "github_workflows_ci_cargo_clippy"], "relation": "form", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": ".github/workflows/ci.yml"}, {"id": "github_workflows_ci_integration_run", "label": "CI integration run against a seeded MongoDB service", "nodes": ["github_workflows_ci_mongo_service", "github_workflows_ci_seed_test_data", "github_workflows_ci_ignored_tests", "src_tauri_src_drivers_mongo_official_driver_a_user_without_the_server_status_privilege_skips_that_stage", "src_tauri_src_drivers_mongo_official_driver_finds_with_sort_projection_and_paging_and_estimates_an_unfiltered_count"], "relation": "form", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": ".github/workflows/ci.yml"}]} \ No newline at end of file diff --git a/graphify-out/cache/semantic/pd5fd89c46bb5/292f367e2d860de099a2773fe761d3deddb3e7bc1d32caeea972dc36ebd7851a.json b/graphify-out/cache/semantic/pd5fd89c46bb5/292f367e2d860de099a2773fe761d3deddb3e7bc1d32caeea972dc36ebd7851a.json new file mode 100644 index 0000000..ce640c4 --- /dev/null +++ b/graphify-out/cache/semantic/pd5fd89c46bb5/292f367e2d860de099a2773fe761d3deddb3e7bc1d32caeea972dc36ebd7851a.json @@ -0,0 +1 @@ +{"nodes": [{"id": "docs_features_app_shell", "label": "App shell feature (app-shell.md)", "file_type": "document", "source_file": "docs/features/app-shell.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "docs_features_app_shell_window_config", "label": "Window config (title Monolens, 1200 x 900, identifier org.monolens, v0.1.0, all bundle targets)", "file_type": "concept", "source_file": "docs/features/app-shell.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "docs_features_app_shell_layout", "label": "App layout (sticky header with Connections dropdown; ConnectionGrid or AppSidebar + TabsContainer; toasts; one ConfirmDialog)", "file_type": "concept", "source_file": "docs/features/app-shell.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "docs_features_app_shell_dark_theme", "label": "Dark-only OKLCH theme (default.css on :root, green primary, success/warning/info; unused .dark block)", "file_type": "concept", "source_file": "docs/features/app-shell.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "docs_features_app_shell_shadcn_vue", "label": "shadcn-vue UI kit on reka-ui (new-york, neutral, @lucide/vue; generated by the CLI, then owned by the repo)", "file_type": "concept", "source_file": "docs/features/app-shell.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "The history gives no reason; the README names Vue 3 and Tailwind."}, {"id": "docs_features_app_shell_bundled_roboto", "label": "Bundled Roboto font (400/500/700 TTF, OFL)", "file_type": "concept", "source_file": "docs/features/app-shell.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "docs_features_app_shell_package_set", "label": "Package set 2026-11-03 (Tauri 2.12, Vue 3.5, Pinia 4, Vite 8, TypeScript 6, vitest 5, TanStack Table 9 / Form 1, zod 4, pnpm 12)", "file_type": "concept", "source_file": "docs/features/app-shell.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "docs_features_app_shell_no_csp", "label": "No Content Security Policy (csp: null)", "file_type": "concept", "source_file": "docs/features/app-shell.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "The web view only loads the bundled UI, but a CSP would limit the damage of injected content."}, {"id": "docs_features_app_shell_no_ci_release", "label": "No release pipeline, no code signing, no auto-update", "file_type": "concept", "source_file": "docs/features/app-shell.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "docs_features_app_shell_unused_opener", "label": "tauri-plugin-opener registered but unused by the UI", "file_type": "concept", "source_file": "docs/features/app-shell.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "docs_features_app_shell_app_icons", "label": "App icons generated with pnpm tauri icon from monolens.png", "file_type": "concept", "source_file": "docs/features/app-shell.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "docs_features_app_shell_ci", "label": "CI (.github/workflows/ci.yml): tests and build on Linux, macOS and Windows (b69ba0b)", "file_type": "concept", "source_file": "docs/features/app-shell.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "docs_features_app_shell_platform_limits", "label": "Only run on Linux: on macOS the Keychain may ask again after every rebuild of an unsigned app; on Windows the web view may keep browser keys such as Ctrl+R (reload closes all tabs); both unchecked", "file_type": "concept", "source_file": "docs/features/app-shell.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "docs_features_app_shell_vite_config_dirname", "label": "Building step: Vite config on import.meta.dirname (3c38991)", "file_type": "concept", "source_file": "docs/features/app-shell.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}], "edges": [{"source": "docs_features_app_shell", "target": "docs_features_app_shell_window_config", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/app-shell.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_app_shell", "target": "docs_features_app_shell_layout", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/app-shell.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_app_shell", "target": "docs_features_app_shell_dark_theme", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/app-shell.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_app_shell", "target": "docs_features_app_shell_shadcn_vue", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/app-shell.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_app_shell", "target": "docs_features_app_shell_bundled_roboto", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/app-shell.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_app_shell", "target": "docs_features_app_shell_package_set", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/app-shell.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_app_shell", "target": "docs_features_app_shell_no_csp", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/app-shell.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_app_shell", "target": "docs_features_app_shell_no_ci_release", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/app-shell.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_app_shell", "target": "docs_features_app_shell_unused_opener", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/app-shell.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_app_shell", "target": "docs_features_app_shell_app_icons", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/app-shell.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_app_shell", "target": "docs_features_shortcuts", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/app-shell.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_app_shell", "target": "docs_features_error_handling", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/app-shell.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_app_shell", "target": "pnpm_workspace", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/app-shell.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_app_shell", "target": "docs_architecture_decisions_latest_packages", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/features/app-shell.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_app_shell", "target": "docs_architecture_decisions_pnpm_pin", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/features/app-shell.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_app_shell_window_config", "target": "src_tauri_tauri_conf_app_windows", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/app-shell.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_app_shell_window_config", "target": "src_tauri_tauri_conf_identifier", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/app-shell.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_app_shell_no_csp", "target": "src_tauri_tauri_conf_security_csp", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/app-shell.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_app_shell_layout", "target": "src_app", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/app-shell.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_app_shell_layout", "target": "src_components_header", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/app-shell.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_app_shell_layout", "target": "src_components_connection_connectiongrid", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/app-shell.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_app_shell_layout", "target": "src_components_appsidebar", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/app-shell.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_app_shell_layout", "target": "src_components_tabs_tabscontainer", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/app-shell.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_app_shell_layout", "target": "src_components_confirmdialog", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/app-shell.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_app_shell_package_set", "target": "claude_typescript_6_pin", "relation": "conceptually_related_to", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/features/app-shell.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_app_shell_unused_opener", "target": "src_tauri_src_lib", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.75, "source_file": "docs/features/app-shell.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_app_shell", "target": "docs_features_app_shell_ci", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/app-shell.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_app_shell", "target": "docs_features_app_shell_platform_limits", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/app-shell.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_app_shell", "target": "docs_features_app_shell_vite_config_dirname", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/app-shell.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_app_shell", "target": "github_workflows_ci", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/app-shell.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_app_shell", "target": "src_tauri_src_menu", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/app-shell.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_app_shell_ci", "target": "github_workflows_ci", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/app-shell.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_app_shell_vite_config_dirname", "target": "vite_config", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/features/app-shell.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_app_shell_platform_limits", "target": "docs_features_app_shell_ci", "relation": "conceptually_related_to", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/app-shell.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_app_shell_platform_limits", "target": "claude_linux_only_runtime", "relation": "conceptually_related_to", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/features/app-shell.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_app_shell_platform_limits", "target": "docs_features_credential_storage_keyring_crate", "relation": "conceptually_related_to", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/features/app-shell.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_app_shell_platform_limits", "target": "docs_features_app_shell_no_ci_release", "relation": "conceptually_related_to", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/features/app-shell.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_app_shell_platform_limits", "target": "docs_architecture_decisions_mod_key_shortcuts", "relation": "conceptually_related_to", "confidence": "INFERRED", "confidence_score": 0.75, "source_file": "docs/features/app-shell.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_app_shell_platform_limits", "target": "docs_features_tabs_memory_cost", "relation": "conceptually_related_to", "confidence": "INFERRED", "confidence_score": 0.65, "source_file": "docs/features/app-shell.md", "source_location": null, "weight": 1.0}], "hyperedges": [{"id": "macos_windows_covered_only_by_ci", "label": "macOS and Windows covered only by CI, the app never run there", "nodes": ["claude_linux_only_runtime", "docs_features_app_shell_platform_limits", "docs_architecture_decisions_ci_github_actions", "github_workflows_ci_build_job"], "relation": "participate_in", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/features/app-shell.md"}]} \ No newline at end of file diff --git a/graphify-out/cache/semantic/pd5fd89c46bb5/4c1d74936d7cd616a2fc84a64ac048740a69f2cf630cf32183401320a15a3cc4.json b/graphify-out/cache/semantic/pd5fd89c46bb5/4c1d74936d7cd616a2fc84a64ac048740a69f2cf630cf32183401320a15a3cc4.json new file mode 100644 index 0000000..e7cef9e --- /dev/null +++ b/graphify-out/cache/semantic/pd5fd89c46bb5/4c1d74936d7cd616a2fc84a64ac048740a69f2cf630cf32183401320a15a3cc4.json @@ -0,0 +1 @@ +{"nodes": [{"id": "docs_features_connection_manager", "label": "Connection manager feature (connection-manager.md)", "file_type": "document", "source_file": "docs/features/connection-manager.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "docs_features_connection_manager_connection_model", "label": "Connection model v2 (id, name, connector MongoDb, target Host/Uri, authentication NONE/BASIC, options)", "file_type": "concept", "source_file": "docs/features/connection-manager.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "docs_features_connection_manager_host_options", "label": "Host-only options (tls, tlsCaFile, tlsAllowInvalidCertificates, replicaSet, serverSelectionTimeoutSecs)", "file_type": "concept", "source_file": "docs/features/connection-manager.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "A connection string carries its own options; anything else (read preference, several hosts) goes into a connection string."}, {"id": "docs_features_connection_manager_connections_json", "label": "connections.json version 2 ({ version: 2, connections: { : ... } }; whole file read per call, written per change)", "file_type": "concept", "source_file": "docs/features/connection-manager.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "docs_features_connection_manager_v1_migration", "label": "Version 1 file migrated on first load (repositories/migrations.rs) and kept as connections.v1.json", "file_type": "rationale", "source_file": "docs/features/connection-manager.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "Another version, or no version, is an error; a v1 uri that is a connection string becomes a string target with its password moved to the keyring."}, {"id": "docs_features_connection_manager_connection_ui", "label": "Connection management UI (ConnectionGrid start screen, header Connections dropdown, sidebar edit, ConnectionDialog with host and string modes)", "file_type": "concept", "source_file": "docs/features/connection-manager.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "docs_features_connection_manager_form_validation", "label": "Form validation with TanStack Form and a zod schema (connectionForm.ts) on test or save", "file_type": "rationale", "source_file": "docs/features/connection-manager.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "The form validates instead of disabling buttons, so the user sees what is missing."}, {"id": "docs_features_connection_manager_rust_normalize", "label": "Rust-side check before storing (models/normalize.rs) moving credentials out of connection strings (models/uri.rs); refusal is RepositoryError::Invalid", "file_type": "rationale", "source_file": "docs/features/connection-manager.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "No password in connections.json, and the stored string can be shown."}, {"id": "docs_features_connection_manager_rust_generated_id", "label": "Connection id (uuid v4) generated in Rust on create", "file_type": "concept", "source_file": "docs/features/connection-manager.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "docs_features_connection_manager_delete_flow", "label": "Delete flow (useConfirm, disconnect and close tabs, delete_connection removes file entry, keyring entry and driver)", "file_type": "concept", "source_file": "docs/features/connection-manager.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "docs_features_connection_manager_srv_untested", "label": "mongodb+srv:// resolves DNS when connecting; not tested against a real srv cluster", "file_type": "concept", "source_file": "docs/features/connection-manager.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}], "edges": [{"source": "docs_features_connection_manager", "target": "docs_features_connection_manager_connection_model", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/connection-manager.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connection_manager", "target": "docs_features_connection_manager_host_options", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/connection-manager.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connection_manager", "target": "docs_features_connection_manager_connections_json", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/connection-manager.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connection_manager", "target": "docs_features_connection_manager_v1_migration", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/connection-manager.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connection_manager", "target": "docs_features_connection_manager_connection_ui", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/connection-manager.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connection_manager", "target": "docs_features_connection_manager_form_validation", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/connection-manager.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connection_manager", "target": "docs_features_connection_manager_rust_normalize", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/connection-manager.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connection_manager", "target": "docs_features_connection_manager_rust_generated_id", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/connection-manager.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connection_manager", "target": "docs_features_connection_manager_delete_flow", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/connection-manager.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connection_manager", "target": "docs_features_connection_manager_srv_untested", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/connection-manager.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connection_manager", "target": "docs_features_credential_storage", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/connection-manager.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connection_manager", "target": "docs_features_connect_and_explorer", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/connection-manager.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connection_manager", "target": "docs_features_connection_test", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/connection-manager.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connection_manager", "target": "src_tauri_src_connector_commands_update_connection_update_connection", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/connection-manager.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connection_manager", "target": "src_domains_connections_services_connectionserviceipc_connectionserviceipc", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/connection-manager.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connection_manager", "target": "docs_readme_connection_issues", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/features/connection-manager.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connection_manager", "target": "docs_features_credential_storage_auth_mechanisms_limit", "relation": "conceptually_related_to", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/features/connection-manager.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connection_manager_connection_model", "target": "src_tauri_src_connector_models_connection_connection", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/connection-manager.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connection_manager_connection_model", "target": "src_tauri_src_connector_models_connection_connectorkind", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/connection-manager.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connection_manager_connection_model", "target": "src_tauri_src_connector_models_connection_connectiontarget", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/features/connection-manager.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connection_manager_connection_model", "target": "src_domains_connections_models_connection_connection", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/features/connection-manager.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connection_manager_connection_model", "target": "docs_features_connection_manager_host_options", "relation": "conceptually_related_to", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/connection-manager.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connection_manager_host_options", "target": "src_tauri_src_connector_models_options_connectionoptions", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/connection-manager.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connection_manager_connections_json", "target": "src_tauri_src_connector_repositories_file_repository_filerepository", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/connection-manager.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connection_manager_connections_json", "target": "src_tauri_src_connector_repositories_storage_wrapper_connectionstorage", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/features/connection-manager.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connection_manager_v1_migration", "target": "src_tauri_src_connector_repositories_migrations_migrate_v1", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/connection-manager.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connection_manager_v1_migration", "target": "src_tauri_src_connector_repositories_file_repository_filerepository_load_storage", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/features/connection-manager.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connection_manager_v1_migration", "target": "src_tauri_src_connector_repositories_file_repository_filerepository_migrate_from_v1", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/features/connection-manager.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connection_manager_connection_ui", "target": "src_components_connection_connectiongrid", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/connection-manager.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connection_manager_connection_ui", "target": "src_components_connection_connectiondialog", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/connection-manager.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connection_manager_connection_ui", "target": "src_components_connection_connectionselector", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/connection-manager.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connection_manager_connection_ui", "target": "src_components_appsidebar", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/features/connection-manager.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connection_manager_form_validation", "target": "src_components_connection_connectionform_connectionformschema", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/connection-manager.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connection_manager_rust_normalize", "target": "src_tauri_src_connector_models_normalize_normalize", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/connection-manager.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connection_manager_rust_normalize", "target": "src_tauri_src_connector_commands_error_repositoryerror", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/connection-manager.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connection_manager_rust_normalize", "target": "src_tauri_src_connector_models_uri_split_uri_credentials", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/features/connection-manager.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connection_manager_rust_generated_id", "target": "src_tauri_src_connector_commands_create_connection_create_connection", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/connection-manager.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connection_manager_delete_flow", "target": "src_composables_useconfirm_useconfirm", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/connection-manager.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connection_manager_delete_flow", "target": "src_tauri_src_connector_commands_delete_connection_delete_connection", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/connection-manager.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connection_manager_delete_flow", "target": "docs_features_tabs_close_tabs_of_connection", "relation": "conceptually_related_to", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/features/connection-manager.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connection_manager_srv_untested", "target": "docs_architecture_overview_build_status", "relation": "conceptually_related_to", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/features/connection-manager.md", "source_location": null, "weight": 1.0}], "hyperedges": []} \ No newline at end of file diff --git a/graphify-out/cache/semantic/pd5fd89c46bb5/53084f47cdcb88b46538e014624a362fbd28fb51a86758e980f8e87a29294a4a.json b/graphify-out/cache/semantic/pd5fd89c46bb5/53084f47cdcb88b46538e014624a362fbd28fb51a86758e980f8e87a29294a4a.json new file mode 100644 index 0000000..78fd06e --- /dev/null +++ b/graphify-out/cache/semantic/pd5fd89c46bb5/53084f47cdcb88b46538e014624a362fbd28fb51a86758e980f8e87a29294a4a.json @@ -0,0 +1 @@ +{"nodes": [{"id": "index", "label": "index.html (Vite entry page: #app mount point, default.css, /src/main.ts)", "file_type": "code", "source_file": "index.html", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}], "edges": [{"source": "index", "target": "src_main", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "index.html", "source_location": null, "weight": 1.0}, {"source": "index", "target": "src_app", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "index.html", "source_location": null, "weight": 1.0}, {"source": "index", "target": "docs_features_app_shell_dark_theme", "relation": "conceptually_related_to", "confidence": "INFERRED", "confidence_score": 0.75, "source_file": "index.html", "source_location": null, "weight": 1.0}], "hyperedges": []} \ No newline at end of file diff --git a/graphify-out/cache/semantic/pd5fd89c46bb5/5517f0111ced7f4aae4213e82b63a8d7f2facee50925b9e0f989c20eb34a0a95.json b/graphify-out/cache/semantic/pd5fd89c46bb5/5517f0111ced7f4aae4213e82b63a8d7f2facee50925b9e0f989c20eb34a0a95.json new file mode 100644 index 0000000..5352859 --- /dev/null +++ b/graphify-out/cache/semantic/pd5fd89c46bb5/5517f0111ced7f4aae4213e82b63a8d7f2facee50925b9e0f989c20eb34a0a95.json @@ -0,0 +1 @@ +{"nodes": [{"id": "docs_features_query_tab", "label": "Query tab feature (query-tab.md)", "file_type": "document", "source_file": "docs/features/query-tab.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "docs_features_query_tab_bson_regex", "label": "A regex is sent as a BSON regex, not the $regex operator, so it also works inside $in", "file_type": "rationale", "source_file": "docs/features/query-tab.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "docs_features_query_tab_estimate_inaccuracy", "label": "Estimated count may be off (after an unclean shutdown, on a sharded cluster)", "file_type": "concept", "source_file": "docs/features/query-tab.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "docs_features_query_tab_extended_json_format", "label": "Extended JSON as the UI-Rust query interchange format (FindQuery; bson turns it into BSON types)", "file_type": "rationale", "source_file": "docs/features/query-tab.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "docs_features_query_tab_find_and_count", "label": "find (filter, sort, projection, skip, limit 1-10000, allowDiskUse) with a concurrent count: estimatedDocumentCount for an empty filter, countDocuments otherwise", "file_type": "rationale", "source_file": "docs/features/query-tab.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "Counting a big collection with countDocuments scans it; the UI marks the number as estimate."}, {"id": "docs_features_query_tab_limit_change", "label": "A new limit runs the typed query from the first page, and only takes effect when the inputs parse", "file_type": "concept", "source_file": "docs/features/query-tab.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "docs_features_query_tab_mongo_autocomplete", "label": "MongoDB autocomplete in the single-line CodeMirror QueryInput (operators, shell functions, field names of the last result)", "file_type": "concept", "source_file": "docs/features/query-tab.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "docs_features_query_tab_paging", "label": "Paging repeats the last executed query with another skip (\"51-100 of 1,234\", \"~\" marks an estimate)", "file_type": "rationale", "source_file": "docs/features/query-tab.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "Typing does not change what the next page means."}, {"id": "docs_features_query_tab_parse_query_texts", "label": "parseQueryTexts: empty or { } means none; a parse error names the input and nothing is sent", "file_type": "concept", "source_file": "docs/features/query-tab.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "docs_features_query_tab_query_view", "label": "Query view TabViewQuery (filter/sort/projection inputs, Execute/Cancel, limit 10-200 default 50, Refresh, table/JSON switch, runs on open; Execute, Mod+Enter and a new limit run the typed query from page one)", "file_type": "concept", "source_file": "docs/features/query-tab.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "docs_features_query_tab_shell_function_mapping", "label": "Checked shell function -> extended JSON mapping (ObjectId -> $oid, ISODate/Date -> $date UTC, NumberLong/Int/Decimal, Timestamp, BinData/UUID -> $binary, RegExp -> $regularExpression)", "file_type": "rationale", "source_file": "docs/features/query-tab.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "What bson cannot read silently matched nothing (e.g. ISODate(\"2024-01-01\"), which bson only reads as a full RFC 3339 date)."}, {"id": "docs_features_query_tab_shell_query_parser", "label": "Shell-syntax query parser (generateTokens + createParser().parseQuery: unquoted and dotted keys, single quotes, regex literals, trailing comma)", "file_type": "concept", "source_file": "docs/features/query-tab.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "JSON does not allow unquoted keys, single quotes and shell functions; the history gives no reason for not using a library."}, {"id": "docs_features_query_tab_unsupported_syntax", "label": "Find only: no aggregation, explain, $where, comments, NumberDouble, MinKey or MaxKey", "file_type": "concept", "source_file": "docs/features/query-tab.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}], "edges": [{"source": "docs_features_query_tab_extended_json_format", "target": "docs_features_query_tab_shell_function_mapping", "relation": "conceptually_related_to", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/query-tab.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_query_tab_find_and_count", "target": "docs_features_query_tab_estimate_inaccuracy", "relation": "conceptually_related_to", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/query-tab.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_query_tab", "target": "docs_features_document_views", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/query-tab.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_query_tab", "target": "docs_features_query_tab_bson_regex", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/query-tab.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_query_tab", "target": "docs_features_query_tab_estimate_inaccuracy", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/query-tab.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_query_tab", "target": "docs_features_query_tab_extended_json_format", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/query-tab.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_query_tab", "target": "docs_features_query_tab_find_and_count", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/query-tab.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_query_tab", "target": "docs_features_query_tab_mongo_autocomplete", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/query-tab.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_query_tab", "target": "docs_features_query_tab_paging", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/query-tab.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_query_tab", "target": "docs_features_query_tab_parse_query_texts", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/query-tab.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_query_tab", "target": "docs_features_query_tab_query_view", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/query-tab.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_query_tab", "target": "docs_features_query_tab_shell_function_mapping", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/query-tab.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_query_tab", "target": "docs_features_query_tab_shell_query_parser", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/query-tab.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_query_tab", "target": "docs_features_query_tab_unsupported_syntax", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/query-tab.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_query_tab_extended_json_format", "target": "src_tauri_src_database_models_find_document_findquery", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/query-tab.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_query_tab_find_and_count", "target": "src_tauri_src_database_commands_start_find_documents_start_find_documents", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/query-tab.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_query_tab_find_and_count", "target": "src_tauri_src_drivers_mongo_official_driver_mongodbofficialdriver_find_documents", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/query-tab.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_query_tab_mongo_autocomplete", "target": "src_components_query_queryinput", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/query-tab.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_query_tab_mongo_autocomplete", "target": "src_domains_mongo_index_usemongoautocomplete", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/query-tab.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_query_tab_parse_query_texts", "target": "src_components_query_querystate_parsequerytexts", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/query-tab.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_query_tab_query_view", "target": "src_components_tabs_views_tabviewquery", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/query-tab.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_query_tab_query_view", "target": "src_domains_connections_services_connectionserviceipc_connectionserviceipc_finddocuments", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/query-tab.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_query_tab_shell_function_mapping", "target": "src_utils_queryparser_transformfunction_transformfunction", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/query-tab.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_query_tab_shell_query_parser", "target": "src_utils_queryparser_generatetokens_generatetokens", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/query-tab.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_query_tab_shell_query_parser", "target": "src_utils_queryparser_parser_createparser", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/query-tab.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_query_tab_unsupported_syntax", "target": "docs_readme_unbuilt_features", "relation": "conceptually_related_to", "confidence": "INFERRED", "confidence_score": 0.75, "source_file": "docs/features/query-tab.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_query_tab_bson_regex", "target": "src_utils_queryparser_transformfunction_regextoextendedjson", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/features/query-tab.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_query_tab_extended_json_format", "target": "src_tauri_src_drivers_documents_json_to_document", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/features/query-tab.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_query_tab_find_and_count", "target": "src_tauri_src_drivers_mongo_official_driver_max_limit", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/features/query-tab.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_query_tab_paging", "target": "src_components_query_querystate_pageinfo", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/features/query-tab.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_query_tab_query_view", "target": "src_domains_shortcuts_models_commands_commands_event_execute", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/features/query-tab.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_query_tab_shell_function_mapping", "target": "src_utils_queryparser_transformfunction_toisodate", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/features/query-tab.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_query_tab_shell_function_mapping", "target": "src_utils_queryparser_transformfunction_uuidtobase64", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/features/query-tab.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_query_tab_parse_query_texts", "target": "docs_features_query_tab_shell_query_parser", "relation": "conceptually_related_to", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/features/query-tab.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_query_tab", "target": "docs_readme_query_issues", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/features/query-tab.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_query_tab", "target": "docs_features_query_tab_limit_change", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/query-tab.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_query_tab_limit_change", "target": "src_components_tabs_views_tabviewquery_changelimit", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/features/query-tab.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_query_tab_limit_change", "target": "docs_features_query_tab_parse_query_texts", "relation": "conceptually_related_to", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/features/query-tab.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_query_tab_limit_change", "target": "docs_features_query_tab_paging", "relation": "conceptually_related_to", "confidence": "INFERRED", "confidence_score": 0.75, "source_file": "docs/features/query-tab.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_query_tab_query_view", "target": "docs_features_query_tab_limit_change", "relation": "conceptually_related_to", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/query-tab.md", "source_location": null, "weight": 1.0}], "hyperedges": []} \ No newline at end of file diff --git a/graphify-out/cache/semantic/pd5fd89c46bb5/5b737ac979cdc35d8f1d56be3e9d23deff459a495df8b592bce26a1bcff71e06.json b/graphify-out/cache/semantic/pd5fd89c46bb5/5b737ac979cdc35d8f1d56be3e9d23deff459a495df8b592bce26a1bcff71e06.json new file mode 100644 index 0000000..a3819de --- /dev/null +++ b/graphify-out/cache/semantic/pd5fd89c46bb5/5b737ac979cdc35d8f1d56be3e9d23deff459a495df8b592bce26a1bcff71e06.json @@ -0,0 +1 @@ +{"nodes": [{"id": "docs_features_connect_and_explorer", "label": "Connect and explorer feature (connect-and-explorer.md)", "file_type": "document", "source_file": "docs/features/connect-and-explorer.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "docs_features_connect_and_explorer_connect_flow", "label": "Connect flow (connectTo -> start_connect task -> ConnectionHandler.open -> list databases; a failed first command drops the driver)", "file_type": "rationale", "source_file": "docs/features/connect-and-explorer.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "The client connects lazily, so listing is the first contact with the server; the grid gives way only once a connection is connected (issue #27)."}, {"id": "docs_features_connect_and_explorer_driver_pool", "label": "Driver pool: one DatabaseDriver per connection id, shared without a lock (locked only to look up and insert, not while a driver loads its password and connects); dropped on disconnect, edit, delete or a failed first command", "file_type": "rationale", "source_file": "docs/features/connect-and-explorer.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "The mongodb client runs several commands of one connection at once, so a slow query in one tab does not hold up another tab or the sidebar. Reused across commands, and dropped when it may be wrong because it is created before the server is contacted (issue #25)."}, {"id": "docs_features_connect_and_explorer_explorer_sidebar", "label": "Explorer sidebar (AppSidebar: node per open connection with refresh/edit/disconnect, lazily listed and cached collections, active-tab highlight)", "file_type": "concept", "source_file": "docs/features/connect-and-explorer.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "docs_features_connect_and_explorer_list_databases_privilege", "label": "Connecting needs the right to list databases; no field to name a database directly", "file_type": "concept", "source_file": "docs/features/connect-and-explorer.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "docs_features_connect_and_explorer_no_session_restore", "label": "Open connections and tabs are not restored after a restart", "file_type": "concept", "source_file": "docs/features/connect-and-explorer.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "docs_features_connect_and_explorer_server_selection_timeout", "label": "A host that does not answer waits for the server selection timeout (the connection's option, else the driver's 30 s) unless the user cancels", "file_type": "concept", "source_file": "docs/features/connect-and-explorer.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "docs_features_connect_and_explorer_sessions", "label": "Connection sessions (connectionStore.sessions: status, databases, cached collections, refreshing; one per open connection)", "file_type": "concept", "source_file": "docs/features/connect-and-explorer.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}], "edges": [{"source": "docs_features_connect_and_explorer_connect_flow", "target": "docs_features_background_tasks_task_pattern", "relation": "conceptually_related_to", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/connect-and-explorer.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connect_and_explorer", "target": "docs_features_background_tasks", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/connect-and-explorer.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connect_and_explorer", "target": "docs_features_connect_and_explorer_connect_flow", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/connect-and-explorer.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connect_and_explorer", "target": "docs_features_connect_and_explorer_driver_pool", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/connect-and-explorer.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connect_and_explorer", "target": "docs_features_connect_and_explorer_explorer_sidebar", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/connect-and-explorer.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connect_and_explorer", "target": "docs_features_connect_and_explorer_list_databases_privilege", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/connect-and-explorer.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connect_and_explorer", "target": "docs_features_connect_and_explorer_no_session_restore", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/connect-and-explorer.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connect_and_explorer", "target": "docs_features_connect_and_explorer_server_selection_timeout", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/connect-and-explorer.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connect_and_explorer", "target": "docs_features_connect_and_explorer_sessions", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/connect-and-explorer.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connect_and_explorer", "target": "docs_features_connection_manager", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/connect-and-explorer.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connect_and_explorer", "target": "docs_features_tabs", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/connect-and-explorer.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connect_and_explorer_connect_flow", "target": "src_components_connection_connectiongrid", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/connect-and-explorer.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connect_and_explorer_connect_flow", "target": "src_domains_connections_services_connectionserviceipc_connectionserviceipc_connect", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/connect-and-explorer.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connect_and_explorer_connect_flow", "target": "src_stores_connectionstore_useconnectionstore", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/connect-and-explorer.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connect_and_explorer_connect_flow", "target": "src_tauri_src_connection_handler_connection_handler_connectionhandler_open", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/connect-and-explorer.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connect_and_explorer_connect_flow", "target": "src_tauri_src_database_commands_start_connect_start_connect", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/connect-and-explorer.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connect_and_explorer_connect_flow", "target": "src_tauri_src_drivers_mongo_official_driver_mongodbofficialdriver_list_databases", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/connect-and-explorer.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connect_and_explorer_driver_pool", "target": "src_tauri_src_connection_handler_connection_handler_connectionhandler", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/connect-and-explorer.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connect_and_explorer_driver_pool", "target": "src_tauri_src_connection_handler_connection_handler_connectionhandler_with_factory", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/connect-and-explorer.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connect_and_explorer_driver_pool", "target": "src_tauri_src_drivers_driver_databasedriver", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/connect-and-explorer.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connect_and_explorer_explorer_sidebar", "target": "src_components_appsidebar", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/connect-and-explorer.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connect_and_explorer_explorer_sidebar", "target": "src_tauri_src_database_commands_disconnect_disconnect", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/connect-and-explorer.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connect_and_explorer_explorer_sidebar", "target": "src_tauri_src_database_commands_list_collections_list_collections", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/connect-and-explorer.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connect_and_explorer_sessions", "target": "src_stores_connectionstore_useconnectionstore", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/connect-and-explorer.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connect_and_explorer_explorer_sidebar", "target": "docs_features_tabs_one_tab_per_collection", "relation": "conceptually_related_to", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/features/connect-and-explorer.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connect_and_explorer_no_session_restore", "target": "docs_architecture_overview_app_data", "relation": "conceptually_related_to", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/features/connect-and-explorer.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connect_and_explorer", "target": "docs_readme_other_issues", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/features/connect-and-explorer.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connect_and_explorer_connect_flow", "target": "src_components_connection_connectionselector", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/features/connect-and-explorer.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connect_and_explorer_driver_pool", "target": "src_tauri_src_connection_handler_connection_handler_connectionhandler_get_or_connect", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/features/connect-and-explorer.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connect_and_explorer_explorer_sidebar", "target": "src_domains_shortcuts_models_commands_commands_connection_disconnect", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/features/connect-and-explorer.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connect_and_explorer_server_selection_timeout", "target": "docs_features_connection_manager_host_options", "relation": "conceptually_related_to", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/features/connect-and-explorer.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connect_and_explorer_sessions", "target": "src_stores_connectionstore_connectionsession", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/features/connect-and-explorer.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connect_and_explorer_driver_pool", "target": "src_tauri_src_connection_handler_connection_handler_connectionhandler_load", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.75, "source_file": "docs/features/connect-and-explorer.md", "source_location": null, "weight": 1.0}], "hyperedges": [{"id": "driver_leaves_pool_occasions", "label": "Occasions on which a driver leaves the pool (disconnect, edit, delete, failed first command)", "nodes": ["docs_features_connect_and_explorer_driver_pool", "src_tauri_src_connection_handler_connection_handler_connectionhandler_disconnect", "src_tauri_src_connection_handler_connection_handler_connectionhandler_open", "src_tauri_src_database_commands_disconnect_disconnect", "src_tauri_src_connector_commands_update_connection_update_connection", "src_tauri_src_connector_commands_delete_connection_delete_connection"], "relation": "participate_in", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/features/connect-and-explorer.md"}]} \ No newline at end of file diff --git a/graphify-out/cache/semantic/pd5fd89c46bb5/60895eb5e5885644186cf8c715c7071d85381c328b2b382b4dbf195a574e3914.json b/graphify-out/cache/semantic/pd5fd89c46bb5/60895eb5e5885644186cf8c715c7071d85381c328b2b382b4dbf195a574e3914.json new file mode 100644 index 0000000..74b7f3c --- /dev/null +++ b/graphify-out/cache/semantic/pd5fd89c46bb5/60895eb5e5885644186cf8c715c7071d85381c328b2b382b4dbf195a574e3914.json @@ -0,0 +1 @@ +{"nodes": [{"id": "docs_features_connection_test", "label": "Connection test feature (connection-test.md)", "file_type": "document", "source_file": "docs/features/connection-test.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "docs_features_connection_test_staged_test", "label": "Staged connection test (Initialize, Ping Database, Reading status, Detecting version, Connected)", "file_type": "rationale", "source_file": "docs/features/connection-test.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "A failure shows where it stopped; the titles exist only in Rust (TEST_STAGES) and are reported as pending first."}, {"id": "docs_features_connection_test_progress_event", "label": "test-connection-progress Tauri event ({ testId, stageIndex, stage }) filtered by the store own test id", "file_type": "rationale", "source_file": "docs/features/connection-test.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "Each stage shows when it really finishes, and the test id lets a listener take only its own test."}, {"id": "docs_features_connection_test_stage_status", "label": "Stage states: pending, passed, failed, skipped (with an optional detail)", "file_type": "concept", "source_file": "docs/features/connection-test.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "docs_features_connection_test_serverstatus_skip", "label": "serverStatus stage skipped on unauthorized (code 13); version read from buildInfo, which needs no privilege", "file_type": "rationale", "source_file": "docs/features/connection-test.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "A user who may only read one database can connect and read, so the test must not fail for them."}, {"id": "docs_features_connection_test_unpooled_driver", "label": "The test builds its own driver through the factory, not a pooled one, from form values incl. the typed password (id empty)", "file_type": "concept", "source_file": "docs/features/connection-test.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "docs_features_connection_test_stepper", "label": "TestStagesDisplay horizontal stepper (check, cross, dash for skipped, spinner on the running stage)", "file_type": "concept", "source_file": "docs/features/connection-test.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}], "edges": [{"source": "docs_features_connection_test", "target": "docs_features_connection_test_staged_test", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/connection-test.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connection_test", "target": "docs_features_connection_test_progress_event", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/connection-test.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connection_test", "target": "docs_features_connection_test_stage_status", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/connection-test.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connection_test", "target": "docs_features_connection_test_serverstatus_skip", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/connection-test.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connection_test", "target": "docs_features_connection_test_unpooled_driver", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/connection-test.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connection_test", "target": "docs_features_connection_test_stepper", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/connection-test.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connection_test", "target": "docs_features_connect_and_explorer_server_selection_timeout", "relation": "conceptually_related_to", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/features/connection-test.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connection_test", "target": "docs_features_background_tasks_task_pattern", "relation": "conceptually_related_to", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/features/connection-test.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connection_test_staged_test", "target": "src_tauri_src_drivers_mongo_official_driver_test_stages", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/connection-test.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connection_test_staged_test", "target": "src_tauri_src_drivers_mongo_official_driver_mongodbofficialdriver_test_connection", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/connection-test.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connection_test_staged_test", "target": "src_stores_connectionstore_useconnectionstore", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/connection-test.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connection_test_staged_test", "target": "src_components_connection_connectiondialog", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/connection-test.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connection_test_progress_event", "target": "src_tauri_src_database_commands_start_test_connection_start_test_connection", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/connection-test.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connection_test_progress_event", "target": "src_tauri_src_database_commands_start_test_connection_testprogressevent", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/features/connection-test.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connection_test_progress_event", "target": "src_stores_connectionstore_testprogresspayload", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/features/connection-test.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connection_test_stage_status", "target": "src_tauri_src_drivers_driver_stagestatus", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/connection-test.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connection_test_stage_status", "target": "src_tauri_src_drivers_driver_teststage", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/connection-test.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connection_test_serverstatus_skip", "target": "src_tauri_src_drivers_mongo_official_driver_is_unauthorized", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/features/connection-test.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connection_test_serverstatus_skip", "target": "docs_features_connection_test_stage_status", "relation": "conceptually_related_to", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/connection-test.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connection_test_unpooled_driver", "target": "src_tauri_src_drivers_factory_databasedriverfactory", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/features/connection-test.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connection_test_unpooled_driver", "target": "docs_features_credential_storage_password_to_webview", "relation": "conceptually_related_to", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/features/connection-test.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connection_test_stepper", "target": "src_components_connection_teststagesdisplay", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/connection-test.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connection_test_progress_event", "target": "docs_features_tabs_one_view_per_tab", "relation": "semantically_similar_to", "confidence": "INFERRED", "confidence_score": 0.75, "source_file": "docs/features/connection-test.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connection_test_serverstatus_skip", "target": "docs_features_error_handling_substring_classification", "relation": "semantically_similar_to", "confidence": "INFERRED", "confidence_score": 0.65, "source_file": "docs/features/connection-test.md", "source_location": null, "weight": 1.0}], "hyperedges": []} \ No newline at end of file diff --git a/graphify-out/cache/semantic/pd5fd89c46bb5/6aae81c8cb139ee464f9ea1168e183488df5280509081f60e279c562f21efccf.json b/graphify-out/cache/semantic/pd5fd89c46bb5/6aae81c8cb139ee464f9ea1168e183488df5280509081f60e279c562f21efccf.json new file mode 100644 index 0000000..711075d --- /dev/null +++ b/graphify-out/cache/semantic/pd5fd89c46bb5/6aae81c8cb139ee464f9ea1168e183488df5280509081f60e279c562f21efccf.json @@ -0,0 +1 @@ +{"nodes": [{"id": "claude", "label": "CLAUDE.md (working guide)", "file_type": "document", "source_file": "CLAUDE.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "claude_browser_preview_ipc_stub", "label": "UI-only browser preview needs a window.__TAURI_INTERNALS__ stand-in answering IPC with test data", "file_type": "concept", "source_file": "CLAUDE.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "claude_clippy_baseline", "label": "cargo clippy baseline: 2 module_inception warnings (connection_handler, task_manager)", "file_type": "concept", "source_file": "CLAUDE.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "claude_coding_conventions", "label": "Coding conventions (match file indentation, @/ alias, thiserror, log macros, spawn_blocking for keyring calls, short why-comments)", "file_type": "rationale", "source_file": "CLAUDE.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "claude_driver_method_recipe", "label": "Adding database work: DatabaseDriver method -> MongoDbOfficialDriver -> task command (follow start_connect.rs)", "file_type": "rationale", "source_file": "CLAUDE.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "claude_forms_rule", "label": "Forms on TanStack Form with a zod 4 schema (Standard Schema, no adapter) and shadcn-vue Field components", "file_type": "rationale", "source_file": "CLAUDE.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "claude_git_workflow", "label": "Git workflow (feature branches off develop, /code-review before push, lowercase conventional commits without names or Claude co-author trailers; run tests, build and clippy locally before pushing anyway)", "file_type": "rationale", "source_file": "CLAUDE.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "claude_graphify_rules", "label": "graphify query-first rules (query/path/explain before grep; graphify update after code changes)", "file_type": "rationale", "source_file": "CLAUDE.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "query/path/explain return a scoped subgraph, usually much smaller than GRAPH_REPORT.md or raw grep output; graph.json is rewritten whole on every build, so its large diff is expected."}, {"id": "claude_ipc_service_rule", "label": "Components never call invoke: IPC goes through ConnectionServiceIPC (Result via tryCatch for CRUD, abortableInvoke for slow commands)", "file_type": "rationale", "source_file": "CLAUDE.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "claude_keep_in_sync_rules", "label": "Keep-in-sync rules (feature docs, command table, generate_handler!, ConnectionServiceIPC, TS models, STORAGE_VERSION, plans, README, graphify cache)", "file_type": "rationale", "source_file": "CLAUDE.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "Docs, IPC wrappers and TypeScript models drift from the code unless every change updates them together."}, {"id": "claude_logging_rule", "label": "Log with useLogger, never console.*; never log credentials, not even at trace", "file_type": "rationale", "source_file": "CLAUDE.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "claude_model_mirroring", "label": "TypeScript models mirror Rust structs (camelCase fields, enum values = Rust variant names)", "file_type": "rationale", "source_file": "CLAUDE.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "serde rename_all camelCase makes both sides one wire format, so both sides change together."}, {"id": "claude_no_gh_cli", "label": "gh is not installed: read issues and pull requests of the public repo through the GitHub REST API", "file_type": "concept", "source_file": "CLAUDE.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "claude_pnpm_pinned", "label": "pnpm 12.17.0 pinned (packageManager, devEngines); pnpm exec instead of npx; change only with pnpm self-update", "file_type": "rationale", "source_file": "CLAUDE.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "npm refuses to run because devEngines names pnpm; pnpm records its version in the lockfile, so a hand edit breaks --frozen-lockfile."}, {"id": "claude_shortcut_recipe", "label": "Adding a shortcut: command id in models/commands.ts, register in config/commands.ts, bind in config/default-binding.ts", "file_type": "rationale", "source_file": "CLAUDE.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "claude_tanstack_table_registered_features", "label": "Gotcha: TanStack Table 9 only exposes the APIs of registered features (documentTableFeatures)", "file_type": "concept", "source_file": "CLAUDE.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "claude_testing_policy", "label": "Unit tests for every piece of logic (regular and edge cases, failing collaborators)", "file_type": "rationale", "source_file": "CLAUDE.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "Logic is pulled out of components into composables or utils so it can be tested without mounting; MongoDB and the keyring sit behind traits where tests must replace them."}, {"id": "claude_typescript_6_pin", "label": "TypeScript stays on 6.x (vue-tsc cannot run on TypeScript 7; tsconfig uses paths without baseUrl)", "file_type": "concept", "source_file": "CLAUDE.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "TypeScript 7 no longer ships lib/tsc, which vue-tsc needs; TypeScript 6 deprecates baseUrl."}, {"id": "claude_untested_hotspots", "label": "Untested code worth covering when touched (formatCellValue, useColumnDragDrop, extractErrorMessage, abortableInvoke, TaskManager)", "file_type": "concept", "source_file": "CLAUDE.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "claude_vue_tsc_attribute_gotcha", "label": "Gotcha: vue-tsc 3 breaks on a // inside a static attribute value (e.g. a mongodb+srv:// placeholder)", "file_type": "concept", "source_file": "CLAUDE.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "claude_ci_workflow", "label": "CI (.github/workflows/ci.yml) runs pnpm test:frontend --run, pnpm build, cargo test and cargo clippy on Linux, macOS and Windows for pushes to develop and main and every pull request, plus the integration tests on Linux against a mongo:8.2 service container", "file_type": "concept", "source_file": "CLAUDE.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "Checks still run locally before pushing; a new integration test that needs data or users adds them to the job's 'Create the test user and data' step."}, {"id": "claude_linux_only_runtime", "label": "Gotcha: the app is only run on Linux; macOS and Windows are compiled and tested in CI but never started; keep platform code (cfg!(target_os), isMacPlatform) small and document what is unchecked per platform", "file_type": "rationale", "source_file": "CLAUDE.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "Nobody has started the app on macOS or Windows, so behaviour there is unverified."}, {"id": "claude_integration_tests", "label": "Integration tests against a real MongoDB (cargo test -- --ignored with MONGODB_TEST_HOST/PORT/USER/PASSWORD); run locally when the driver changed, and in CI on Linux", "file_type": "concept", "source_file": "CLAUDE.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "claude_tauri_prerequisites", "label": "Tauri prerequisites on Ubuntu 26.04 (libwebkit2gtk-4.1-dev, build-essential, libxdo-dev, libssl-dev, libayatana-appindicator3-dev, librsvg2-dev, ...); without them no Rust code compiles, though a stale target/ can make cargo check look fine", "file_type": "concept", "source_file": "CLAUDE.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "claude_mongodb_kernel_gotcha", "label": "MongoDB 8.0 and 8.3 refuse to start on Linux 6.19+ (SERVER-121912); mongo:8.2 runs", "file_type": "rationale", "source_file": "CLAUDE.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "mongo:8 resolves to 8.3, which failed the CI service container on the GitHub runners"}], "edges": [{"source": "claude", "target": "claude_browser_preview_ipc_stub", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude", "target": "claude_clippy_baseline", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude", "target": "claude_coding_conventions", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude", "target": "claude_driver_method_recipe", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude", "target": "claude_forms_rule", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude", "target": "claude_git_workflow", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude", "target": "claude_graphify_rules", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude", "target": "claude_ipc_service_rule", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude", "target": "claude_keep_in_sync_rules", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude", "target": "claude_logging_rule", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude", "target": "claude_model_mirroring", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude", "target": "claude_pnpm_pinned", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude", "target": "claude_shortcut_recipe", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude", "target": "claude_tanstack_table_registered_features", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude", "target": "claude_testing_policy", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude", "target": "claude_typescript_6_pin", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude", "target": "claude_untested_hotspots", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude", "target": "docs_architecture_decisions", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude", "target": "docs_architecture_overview", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude", "target": "docs_readme", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude", "target": "readme", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude", "target": "src_tauri_src_database_models_find_document_findquery", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude", "target": "src_tauri_src_drivers_mongo_official_driver_test_stages", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude_clippy_baseline", "target": "src_tauri_src_connection_handler_connection_handler", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude_clippy_baseline", "target": "src_tauri_src_task_manager_task_manager", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude_driver_method_recipe", "target": "src_tauri_src_database_commands_start_connect_start_connect", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude_driver_method_recipe", "target": "src_tauri_src_drivers_driver_databasedriver", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude_driver_method_recipe", "target": "src_tauri_src_drivers_mongo_official_driver_mongodbofficialdriver", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude_forms_rule", "target": "src_components_connection_connectiondialog", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude_forms_rule", "target": "src_components_connection_connectionform", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude_ipc_service_rule", "target": "src_composables_useabortablecommand_abortableinvoke", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude_ipc_service_rule", "target": "src_domains_connections_services_connectionserviceipc_connectionserviceipc", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude_ipc_service_rule", "target": "src_utils_result_trycatch", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude_keep_in_sync_rules", "target": "docs_architecture_overview_ipc_command_table", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude_keep_in_sync_rules", "target": "docs_readme_plans_folder", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude_keep_in_sync_rules", "target": "src_domains_connections_services_connectionserviceipc_connectionserviceipc", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude_keep_in_sync_rules", "target": "src_tauri_src_connector_repositories_storage_wrapper_storage_version", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude_keep_in_sync_rules", "target": "src_tauri_src_lib", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude_logging_rule", "target": "src_composables_uselogger_uselogger", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude_pnpm_pinned", "target": "package_devengines", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude_pnpm_pinned", "target": "package_packagemanager", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude_pnpm_pinned", "target": "pnpm_workspace", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude_shortcut_recipe", "target": "src_domains_shortcuts_config_commands_initcommands", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude_shortcut_recipe", "target": "src_domains_shortcuts_config_default_binding_defaultbindings", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude_shortcut_recipe", "target": "src_domains_shortcuts_models_commands_commands", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude_tanstack_table_registered_features", "target": "src_composables_table_usedocumenttable_documenttablefeatures", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude_testing_policy", "target": "src_tauri_src_connector_repositories_credentials_credential_store_memorycredentialstore", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude_testing_policy", "target": "src_test_setup", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude_untested_hotspots", "target": "src_composables_table_usecolumndragdrop_usecolumndragdrop", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude_untested_hotspots", "target": "src_composables_useabortablecommand_abortableinvoke", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude_untested_hotspots", "target": "src_tauri_src_task_manager_task_manager_taskmanager", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude_untested_hotspots", "target": "src_utils_errormessage_extracterrormessage", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude_untested_hotspots", "target": "src_utils_formatcellvalues_formatcellvalue", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude_model_mirroring", "target": "src_domains_connections_models_connection_connection", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.75, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude_model_mirroring", "target": "src_tauri_src_connector_models_connection_connection", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.75, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude_driver_method_recipe", "target": "docs_features_background_tasks_task_pattern", "relation": "conceptually_related_to", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude_keep_in_sync_rules", "target": "readme_graphify", "relation": "conceptually_related_to", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude_logging_rule", "target": "docs_features_logging_no_credentials", "relation": "conceptually_related_to", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude_shortcut_recipe", "target": "docs_features_shortcuts_command_registry_pattern", "relation": "conceptually_related_to", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude", "target": "docs_architecture_decisions_confirm_dialog", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude", "target": "docs_architecture_overview_managed_state", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude", "target": "docs_architecture_overview_provider_registry", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude", "target": "docs_features_app_shell_dark_theme", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude", "target": "docs_features_connect_and_explorer_driver_pool", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude", "target": "docs_features_connect_and_explorer_sessions", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude", "target": "docs_features_connection_manager_v1_migration", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude", "target": "docs_features_credential_storage_credential_hydration", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude", "target": "docs_features_credential_storage_secret_service_required", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude", "target": "docs_features_error_handling_three_field_error_shape", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude", "target": "docs_features_error_handling_toasts", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude", "target": "docs_features_logging_log_level_by_build", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude", "target": "docs_features_query_tab_extended_json_format", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude", "target": "docs_features_tabs_one_view_per_tab", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude_keep_in_sync_rules", "target": "docs_readme_feature_file_template", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude_forms_rule", "target": "docs_architecture_decisions_tanstack_form_zod", "relation": "conceptually_related_to", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude_pnpm_pinned", "target": "docs_architecture_decisions_pnpm_pin", "relation": "conceptually_related_to", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude_typescript_6_pin", "target": "docs_architecture_decisions_latest_packages", "relation": "conceptually_related_to", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude", "target": "claude_vue_tsc_attribute_gotcha", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude", "target": "claude_no_gh_cli", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude", "target": "src_tauri_src_connection_handler_connection_handler_connectionhandler", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude", "target": "src_tauri_src_connection_handler_connection_handler_connectionhandler_with_factory", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude_vue_tsc_attribute_gotcha", "target": "claude_typescript_6_pin", "relation": "conceptually_related_to", "confidence": "INFERRED", "confidence_score": 0.75, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude_no_gh_cli", "target": "claude_git_workflow", "relation": "conceptually_related_to", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude", "target": "claude_ci_workflow", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude", "target": "claude_linux_only_runtime", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude", "target": "claude_integration_tests", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude", "target": "claude_tauri_prerequisites", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude", "target": "github_workflows_ci", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude_ci_workflow", "target": "github_workflows_ci", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude_ci_workflow", "target": "github_workflows_ci_seed_test_data", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude_ci_workflow", "target": "github_workflows_ci_mongo_service", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude_ci_workflow", "target": "package_scripts_test_frontend", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude_ci_workflow", "target": "package_scripts_build", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude_ci_workflow", "target": "claude_integration_tests", "relation": "conceptually_related_to", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude_ci_workflow", "target": "claude_git_workflow", "relation": "conceptually_related_to", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude_ci_workflow", "target": "docs_architecture_decisions_ci_github_actions", "relation": "conceptually_related_to", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude_linux_only_runtime", "target": "src_domains_shortcuts_utils_eventtokeystring_ismacplatform", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude_linux_only_runtime", "target": "src_tauri_src_lib", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude_linux_only_runtime", "target": "claude_ci_workflow", "relation": "conceptually_related_to", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude_integration_tests", "target": "docs_architecture_decisions_testing_strategy", "relation": "conceptually_related_to", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude_tauri_prerequisites", "target": "readme_dev_setup", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude_mongodb_kernel_gotcha", "target": "github_workflows_ci_mongo_service", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}], "hyperedges": []} \ No newline at end of file diff --git a/graphify-out/cache/semantic/pd5fd89c46bb5/6deb167bdbe1a8d074ebab4d71dd2e89c15e8090bbdbc7d9006d7bb48cb26200.json b/graphify-out/cache/semantic/pd5fd89c46bb5/6deb167bdbe1a8d074ebab4d71dd2e89c15e8090bbdbc7d9006d7bb48cb26200.json new file mode 100644 index 0000000..6743f7d --- /dev/null +++ b/graphify-out/cache/semantic/pd5fd89c46bb5/6deb167bdbe1a8d074ebab4d71dd2e89c15e8090bbdbc7d9006d7bb48cb26200.json @@ -0,0 +1 @@ +{"nodes": [{"id": "docs_architecture_decisions", "label": "Decision log (decisions.md)", "file_type": "document", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "docs_architecture_decisions_background_tasks", "label": "Decision: testing, connecting and finding documents run as cancellable background tasks with an id", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "A slow connection froze the UI and there was no way to give up (issue #27). Rejected: a timeout only."}, {"id": "docs_architecture_decisions_basic_auth_only", "label": "Decision: only basic authentication (username, password, authentication database) for the MVP", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "\"For MVP, Basic is sufficient\" (issue #3). Rejected: other mechanisms for now."}, {"id": "docs_architecture_decisions_branching_model", "label": "Decision: feature branches off develop merged through GitHub pull requests; develop goes to main from time to time", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "The history records no reason."}, {"id": "docs_architecture_decisions_bundled_roboto", "label": "Decision: Roboto bundled with the app", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "The font of the built app looked wrong (issue #30). Rejected: the system font."}, {"id": "docs_architecture_decisions_client_options_instead_of_url", "label": "Decision: the driver gets ClientOptions instead of a URL string", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "No hand-made percent-encoding, and srv strings work. Rejected: fixing the URL building."}, {"id": "docs_architecture_decisions_codemirror_query_input", "label": "Decision: the query input is a single-line CodeMirror editor with MongoDB autocomplete", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "The history gives no reason."}, {"id": "docs_architecture_decisions_commands_take_connection_id", "label": "Decision: commands that talk to MongoDB take a connection id; Rust loads the saved connection and its password", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "The UI copy can be stale after an edit, and the password no longer passes through the web view for these calls. Rejected: sending the connection object."}, {"id": "docs_architecture_decisions_confirm_dialog", "label": "Decision: a ConfirmDialog with useConfirm() instead of the browser confirm()", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "It fits the app and will be needed for deleting data."}, {"id": "docs_architecture_decisions_connection_model_v2", "label": "Decision: connection model version 2 (target + options for host targets; connectionType dropped; v1 migrated and kept as connections.v1.json)", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "Connection strings and options (issue #4) were out of reach with a host field and a hand-built URL; connectionType was never read. Rejected: keeping connectionType; options for both modes overriding the string."}, {"id": "docs_architecture_decisions_connection_string_credentials", "label": "Decision: a connection string keeps all its options; its credentials move out on save and go back only in memory before parsing", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "One place per setting and no password in connections.json; the driver checks authMechanism against the user info. Rejected: storing the string as typed."}, {"id": "docs_architecture_decisions_connection_test_skips_serverstatus", "label": "Decision: the connection test skips serverStatus without the privilege, reads the version from buildInfo, marks failing stages failed, and its events carry a test id", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "A user who may only read one database failed the test although connecting worked; a failed stage stayed grey; two tests could not be told apart. Rejected: dropping the status stage."}, {"id": "docs_architecture_decisions_credential_store_trait_driver_factory", "label": "Decision: a CredentialStore trait and a driver factory in ConnectionHandler", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "Repository and pool can be tested without the keyring and without MongoDB; the commands no longer downcast the repository."}, {"id": "docs_architecture_decisions_credentials_stay_in_rust", "label": "Decision: credentials stay in Rust; get_connections and get_connection never return a password", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "Passwords should not be exposed to the web view, and the grid should not hit the keyring for every connection. Rejected: hydrating passwords when listing."}, {"id": "docs_architecture_decisions_dependency_injection", "label": "Decision: dependency injection on both sides (Tauri managed state in Rust, provider registry with useDomain in Vue)", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "Services are created once and can be swapped behind an interface (ConnectionService, ConnectionRepository, LogStrategy)."}, {"id": "docs_architecture_decisions_docs_and_working_rules", "label": "Decision: docs/ documents features, architecture, decisions and plans; CLAUDE.md holds the working rules", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "The project is being revived; its state had to be written down first, and work should follow the same rules as the other projects."}, {"id": "docs_architecture_decisions_driver_leaves_pool", "label": "Decision: a driver leaves the pool on disconnect, edit, delete and when its first command fails", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "A driver is created before the server is contacted, so a wrong password or an edited host stuck until restart. Rejected: keeping drivers for the life of the process."}, {"id": "docs_architecture_decisions_driver_per_connection", "label": "Decision: one driver per connection id kept for the life of the process (later revised)", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "Reuse the MongoDB client connection pool across commands."}, {"id": "docs_architecture_decisions_driver_trait_factory", "label": "Decision: database access through a DatabaseDriver trait with a factory per ConnectorKind", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "Room for more drivers or a second MongoDB implementation."}, {"id": "docs_architecture_decisions_edit_dialog_shows_password", "label": "Decision: the edit dialog shows the stored password, fetched from the keyring only when it opens", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "Users wanted to see and fix the password they saved; partly reverses \"credentials never reach the frontend\" of PR #23. Rejected: keeping the password write-only."}, {"id": "docs_architecture_decisions_estimated_count", "label": "Decision: an empty filter is counted with estimatedDocumentCount (marked as estimate), a filter exactly", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "countDocuments scans a big collection. Rejected: always counting exactly; no count."}, {"id": "docs_architecture_decisions_graphify_knowledge_graph", "label": "Decision: Claude Code gets a graphify knowledge graph; only the semantic cache is committed, each clone builds the graph", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "Same setup as smtp-relay: the cache saves the model cost of reading the docs again, and graph.json is rewritten whole on every rebuild. Rejected: committing graph.json; ignoring graphify-out/ completely."}, {"id": "docs_architecture_decisions_host_options", "label": "Decision: host options TLS, CA file, invalid certificates, replica set, server selection timeout", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "What issue #4 asked for, without client certificates. Rejected: more fields such as read preference; those go into a connection string."}, {"id": "docs_architecture_decisions_keyring_4", "label": "Decision: keyring 4 with its default v1 mode (Keychain, Credential Manager, Secret Service); reverses the keyutils cache", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "All packages were updated; keyring 4 moved its stores into separate crates and v1 keeps the API; keyring 3 passwords stay readable. Rejected: keyring-core with chosen store crates; staying on keyring 3.6."}, {"id": "docs_architecture_decisions_latest_packages", "label": "Decision: all packages to their latest release, except TypeScript 7 and Tauri 3", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "Old versions would only make later updates bigger; vue-tsc cannot run on TypeScript 7 (no lib/tsc); Tauri 3 is an alpha. Rejected: staying on old versions; TypeScript 7 without template type-checking."}, {"id": "docs_architecture_decisions_linux_keyring_backend", "label": "Decision (superseded): Linux keyring via linux-native-sync-persistent with vendored OpenSSL", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "keyutils for fast kernel-level access during a session, Secret Service for persistence; vendored bundles OpenSSL. Rejected: sync-secret-service alone."}, {"id": "docs_architecture_decisions_log_level_by_build", "label": "Decision: logs at Debug in development builds, Info in release builds", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "Release builds logged everything at debug."}, {"id": "docs_architecture_decisions_mod_key_shortcuts", "label": "Decision: shortcuts on mod (Cmd on macOS, Ctrl elsewhere); on macOS the default menu without \"Close Window\"", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "Mac users expect Cmd; Cmd+W would otherwise close the app. Not checked on a Mac."}, {"id": "docs_architecture_decisions_new_error_variants", "label": "Decision: CredentialsUnavailable, ConnectionNotFound and StorageFailed variants; a failed find carries the server message", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "A keyring failure read as a host problem; a broken query only said \"check your permissions\"."}, {"id": "docs_architecture_decisions_no_credentials_in_logs", "label": "Decision: no credentials in logs; the connection URI logged only in debug builds with the password masked", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "The URI carries the password."}, {"id": "docs_architecture_decisions_one_view_per_tab", "label": "Decision: every tab has exactly one view keyed by its own id; mod+enter runs only in the active tab", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "Views multiplied and ran duplicate queries, and Ctrl+Enter ran everywhere (issue #42)."}, {"id": "docs_architecture_decisions_optional_connection_test", "label": "Decision: testing a connection is optional; a connection can be saved untested", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "Issue #26 gives no reason. Rejected: requiring a successful test before saving."}, {"id": "docs_architecture_decisions_os_keyring", "label": "Decision: passwords stored in the OS keyring through the keyring crate", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "Uses the native store on Windows, macOS and Linux. Rejected: Tauri Stronghold (added 2026-02-15, reverted 2026-02-17)."}, {"id": "docs_architecture_decisions_own_query_parser", "label": "Decision: shell-style queries parsed by an own parser (src/utils/queryParser) into extended JSON, which bson turns into BSON in Rust", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "It accepts unquoted keys, single quotes and shell functions like ObjectId(...) that JSON does not; the history gives no reason for not using a library."}, {"id": "docs_architecture_decisions_paging_sort_projection", "label": "Decision: paging, total count, sort and projection in the query tab; paging repeats the executed query", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "Issue #12 listed them; typing must not change what the next page means."}, {"id": "docs_architecture_decisions_parser_checks_shell_functions", "label": "Decision: the query parser checks every shell function and produces only shapes bson reads as types; regexes as $regularExpression", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "Invalid or date-only values silently matched nothing."}, {"id": "docs_architecture_decisions_pnpm_pin", "label": "Decision: pnpm pinned to 12 in packageManager and devEngines; pnpm-workspace.yaml allows the esbuild and vue-demi build scripts", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "node_modules came from pnpm 10 while the machines run newer versions; same setup as smtp-relay. Rejected: no pin; pinning the installed 11.3."}, {"id": "docs_architecture_decisions_relaxed_extended_json", "label": "Decision: documents leave Rust as relaxed extended JSON, an Int64 beyond +/-2^53 as $numberLong", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "Readable dates in the JSON view and no silent change of large integers. Rejected: canonical extended JSON."}, {"id": "docs_architecture_decisions_several_connections", "label": "Decision: several connections open at once; disconnecting closes the connection tabs; each tab belongs to its connection", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "Working with two servers is common; tabs used to survive a switch of connection and query the new server. Rejected: closing all tabs on switching; keeping tabs per connection while only one is open."}, {"id": "docs_architecture_decisions_sidebar_node_per_connection", "label": "Decision: the sidebar has one node per open connection with refresh, edit and disconnect; the grid gives way once a connection is connected", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "Editing must work while connected; the view switches only on success (issue #27)."}, {"id": "docs_architecture_decisions_smtp_relay", "label": "smtp-relay (sibling project used as template for docs, pnpm pin and graphify setup)", "file_type": "concept", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "docs_architecture_decisions_streamed_test_stages", "label": "Decision: the connection test streams each stage as a Tauri event", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "Each stage shows when it really finishes, not all at once."}, {"id": "docs_architecture_decisions_table_default_view", "label": "Decision: documents shown in a table by default, JSON second; column order saved per connection, database and collection", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "The commit gives no reason for the default."}, {"id": "docs_architecture_decisions_tabs_stay_mounted", "label": "Decision: open tabs stay mounted (v-show and KeepAlive) so a tab keeps its query and results", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "Switching tabs lost the state. Rejected: v-if."}, {"id": "docs_architecture_decisions_tanstack_form_zod", "label": "Decision: forms on TanStack Form with zod 4 and shadcn-vue Field", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "@vee-validate/zod only supports zod 3; vee-validate 5 has been a beta since 2025; shadcn-vue documents TanStack Form. Rejected: vee-validate 5 beta, an own adapter for vee-validate 4, keeping zod 3."}, {"id": "docs_architecture_decisions_tauri_vue_stack", "label": "Decision: Tauri 2 with a Rust core and a Vue 3 web view", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "README: high-performance backend with secure system-level access; modern, responsive, maintainable frontend; goal is a free European alternative to Studio 3T."}, {"id": "docs_architecture_decisions_test_timeout", "label": "Decision: the connection test waits 10 s for a server unless the connection sets a timeout; connecting keeps the driver's 30 s; the waiting stage shows its seconds", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "A wrong port sat at Ping Database for 30 s without a sign of progress (37f0aea). Rejected: one shorter timeout for connecting too; a fixed timeout overriding the connection's."}, {"id": "docs_architecture_decisions_testing_strategy", "label": "Decision: vitest in happy-dom with Tauri mockIPC; Rust unit tests with an in-memory credential store and temp dirs; integration tests against a real MongoDB marked #[ignore]", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "The rule \"every piece of logic gets unit tests\"; keyring and MongoDB cannot be in the default run. Rejected: mounting components in tests (logic moved into modules instead)."}, {"id": "docs_architecture_decisions_toasts", "label": "Decision: errors shown as vue-sonner toasts instead of an error box per component", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "Cleaner, less duplicated code and styling, fits the theme (issue #36). Rejected: the error boxes of before."}, {"id": "docs_architecture_decisions_url_percent_encoding", "label": "Decision (superseded): password set on the URL with Url::set_password (percent-encoded)", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "Passwords with special characters failed with \"password must be URL encoded\" (issue #21)."}, {"id": "docs_architecture_decisions_user_message_errors", "label": "Decision: every error carries kind, message and user_message; the UI shows user_message", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "Raw driver errors were long and unreadable (issue #16). Rejected: showing the raw error."}, {"id": "docs_architecture_decisions_versioned_storage", "label": "Decision: connections stored as a versioned JSON object keyed by id", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "O(1) access by id, and a version to migrate the file when its shape changes. Rejected: a plain array of connections."}, {"id": "docs_architecture_decisions_virtualized_table", "label": "Decision: the table renders only visible rows (TanStack Virtual), caches formatted cells and sizes columns from a sample", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "Bigger result sets render fast (PR #44, issue #39)."}, {"id": "docs_architecture_decisions_window_size", "label": "Decision: the default window is 1200 x 900", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "The history gives no reason."}], "edges": [{"source": "docs_architecture_decisions_docs_and_working_rules", "target": "docs_architecture_decisions_smtp_relay", "relation": "conceptually_related_to", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_edit_dialog_shows_password", "target": "docs_architecture_decisions_credentials_stay_in_rust", "relation": "conceptually_related_to", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_graphify_knowledge_graph", "target": "docs_architecture_decisions_smtp_relay", "relation": "conceptually_related_to", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_keyring_4", "target": "docs_architecture_decisions_linux_keyring_backend", "relation": "conceptually_related_to", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_pnpm_pin", "target": "docs_architecture_decisions_smtp_relay", "relation": "conceptually_related_to", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_background_tasks", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_basic_auth_only", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_branching_model", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_bundled_roboto", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_client_options_instead_of_url", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_codemirror_query_input", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_commands_take_connection_id", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_confirm_dialog", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_connection_model_v2", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_connection_string_credentials", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_connection_test_skips_serverstatus", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_credential_store_trait_driver_factory", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_credentials_stay_in_rust", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_dependency_injection", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_docs_and_working_rules", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_driver_leaves_pool", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_driver_per_connection", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_driver_trait_factory", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_edit_dialog_shows_password", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_estimated_count", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_graphify_knowledge_graph", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_host_options", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_keyring_4", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_latest_packages", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_linux_keyring_backend", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_log_level_by_build", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_mod_key_shortcuts", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_new_error_variants", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_no_credentials_in_logs", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_one_view_per_tab", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_optional_connection_test", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_os_keyring", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_own_query_parser", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_paging_sort_projection", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_parser_checks_shell_functions", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_pnpm_pin", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_relaxed_extended_json", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_several_connections", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_sidebar_node_per_connection", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_smtp_relay", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_streamed_test_stages", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_table_default_view", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_tabs_stay_mounted", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_tanstack_form_zod", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_tauri_vue_stack", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_testing_strategy", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_toasts", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_url_percent_encoding", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_user_message_errors", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_versioned_storage", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_virtualized_table", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_window_size", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_background_tasks", "target": "src_tauri_src_task_manager_commands_await_task_result", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_background_tasks", "target": "src_tauri_src_task_manager_commands_cancel_task", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_confirm_dialog", "target": "src_components_confirmdialog", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_confirm_dialog", "target": "src_composables_useconfirm_useconfirm", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_credential_store_trait_driver_factory", "target": "src_tauri_src_connection_handler_connection_handler_connectionhandler", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_credential_store_trait_driver_factory", "target": "src_tauri_src_connector_repositories_credentials_credential_store_credentialstore", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_credentials_stay_in_rust", "target": "src_tauri_src_connector_commands_get_connections_get_connections", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_dependency_injection", "target": "src_domains_connections_services_connectionservice_connectionservice", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_dependency_injection", "target": "src_domains_index_usedomain", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_dependency_injection", "target": "src_domains_logger_strategies_logstrategy_logstrategy", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_docs_and_working_rules", "target": "claude", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_driver_trait_factory", "target": "src_tauri_src_connector_models_connection_connectorkind", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_driver_trait_factory", "target": "src_tauri_src_drivers_driver_databasedriver", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_new_error_variants", "target": "src_tauri_src_drivers_errors_drivererror_connectionnotfound", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_new_error_variants", "target": "src_tauri_src_drivers_errors_drivererror_credentialsunavailable", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_new_error_variants", "target": "src_tauri_src_drivers_errors_drivererror_storagefailed", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_os_keyring", "target": "docs_features_credential_storage_tauri_stronghold", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_pnpm_pin", "target": "package_devengines", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_pnpm_pin", "target": "package_packagemanager", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_pnpm_pin", "target": "pnpm_workspace", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_tauri_vue_stack", "target": "readme_studio_3t", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_tauri_vue_stack", "target": "readme_tech_stack", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_testing_strategy", "target": "claude_testing_policy", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_graphify_knowledge_graph", "target": "claude_graphify_rules", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.75, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_connection_test_skips_serverstatus", "target": "src_tauri_src_drivers_mongo_official_driver_is_unauthorized", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.75, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_tanstack_form_zod", "target": "src_components_connection_connectionform_connectionformschema", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.75, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_branching_model", "target": "claude_git_workflow", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_codemirror_query_input", "target": "docs_features_query_tab_mongo_autocomplete", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_commands_take_connection_id", "target": "docs_features_credential_storage_credential_hydration", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_confirm_dialog", "target": "docs_features_connection_manager_delete_flow", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_connection_test_skips_serverstatus", "target": "docs_features_connection_test_progress_event", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_driver_per_connection", "target": "docs_features_connect_and_explorer_driver_pool", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_edit_dialog_shows_password", "target": "docs_features_credential_storage_password_to_webview", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_optional_connection_test", "target": "docs_features_connection_test", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_os_keyring", "target": "docs_features_credential_storage_keyring_crate", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_parser_checks_shell_functions", "target": "docs_features_query_tab_bson_regex", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_tauri_vue_stack", "target": "docs_architecture_overview_rust_core", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_tauri_vue_stack", "target": "docs_architecture_overview_web_view", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_codemirror_query_input", "target": "src_components_query_queryinput", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_commands_take_connection_id", "target": "src_tauri_src_connection_handler_connection_handler_connectionhandler_get_or_connect", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_connection_model_v2", "target": "src_tauri_src_connector_models_connection_connectiontarget", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_connection_model_v2", "target": "src_tauri_src_connector_repositories_migrations_migrate_v1", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_connection_model_v2", "target": "src_tauri_src_connector_repositories_storage_wrapper_storage_version", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_connection_string_credentials", "target": "src_tauri_src_connector_models_uri_split_uri_credentials", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_connection_string_credentials", "target": "src_tauri_src_drivers_client_options_with_user_info", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_credential_store_trait_driver_factory", "target": "src_tauri_src_connection_handler_connection_handler_connectionhandler_with_factory", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_docs_and_working_rules", "target": "docs_readme", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_driver_leaves_pool", "target": "src_tauri_src_connection_handler_connection_handler_connectionhandler_disconnect", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_driver_leaves_pool", "target": "src_tauri_src_connection_handler_connection_handler_connectionhandler_open", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_driver_per_connection", "target": "src_tauri_src_connection_handler_connection_handler_connectionhandler", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_edit_dialog_shows_password", "target": "src_tauri_src_connector_commands_get_connection_password_get_connection_password", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_host_options", "target": "src_tauri_src_connector_models_options_connectionoptions", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_keyring_4", "target": "src_tauri_src_connector_repositories_credentials_credential_service_credentialservice", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_log_level_by_build", "target": "src_tauri_src_lib", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_new_error_variants", "target": "src_tauri_src_drivers_errors_drivererror_finddocumentsfailed", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_one_view_per_tab", "target": "src_domains_shortcuts_models_commands_commands_event_execute", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_os_keyring", "target": "src_tauri_src_connector_repositories_credentials_credential_service_credentialservice", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_own_query_parser", "target": "src_utils_queryparser_parser_createparser", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_parser_checks_shell_functions", "target": "src_utils_queryparser_transformfunction_transformfunction", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_relaxed_extended_json", "target": "src_tauri_src_drivers_documents_document_to_json", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_sidebar_node_per_connection", "target": "src_components_appsidebar", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_testing_strategy", "target": "src_tauri_src_connector_repositories_credentials_credential_store_memorycredentialstore", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_testing_strategy", "target": "src_test_setup", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_versioned_storage", "target": "src_tauri_src_connector_repositories_storage_wrapper_connectionstorage", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_client_options_instead_of_url", "target": "docs_architecture_decisions_url_percent_encoding", "relation": "conceptually_related_to", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_connection_model_v2", "target": "docs_architecture_decisions_versioned_storage", "relation": "conceptually_related_to", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_driver_leaves_pool", "target": "docs_architecture_decisions_driver_per_connection", "relation": "conceptually_related_to", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_background_tasks", "target": "docs_features_background_tasks_task_pattern", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_basic_auth_only", "target": "docs_features_credential_storage_auth_mechanisms_limit", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_bundled_roboto", "target": "docs_features_app_shell_bundled_roboto", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_connection_model_v2", "target": "docs_features_connection_manager_connection_model", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_connection_model_v2", "target": "docs_features_connection_manager_v1_migration", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_connection_string_credentials", "target": "docs_features_connection_manager_rust_normalize", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_connection_string_credentials", "target": "docs_features_credential_storage_user_info_reinsertion", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_connection_test_skips_serverstatus", "target": "docs_features_connection_test_serverstatus_skip", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_credential_store_trait_driver_factory", "target": "docs_features_credential_storage_credential_store_trait", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_credentials_stay_in_rust", "target": "docs_features_credential_storage_credential_hydration", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_dependency_injection", "target": "docs_architecture_overview_managed_state", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_dependency_injection", "target": "docs_architecture_overview_provider_registry", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_driver_leaves_pool", "target": "docs_features_connect_and_explorer_driver_pool", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_estimated_count", "target": "docs_features_query_tab_find_and_count", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_graphify_knowledge_graph", "target": "readme_graphify", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_host_options", "target": "docs_features_connection_manager_host_options", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_keyring_4", "target": "docs_features_credential_storage_keyring_crate", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_linux_keyring_backend", "target": "docs_features_credential_storage_linux_backend", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_log_level_by_build", "target": "docs_features_logging_log_level_by_build", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_mod_key_shortcuts", "target": "docs_features_shortcuts_macos_menu", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_mod_key_shortcuts", "target": "docs_features_shortcuts_mod_key", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_new_error_variants", "target": "docs_features_error_handling_server_message_on_find", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_no_credentials_in_logs", "target": "docs_features_logging_no_credentials", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_one_view_per_tab", "target": "docs_features_tabs_one_view_per_tab", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_own_query_parser", "target": "docs_features_query_tab_shell_query_parser", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_paging_sort_projection", "target": "docs_features_query_tab_paging", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_parser_checks_shell_functions", "target": "docs_features_query_tab_shell_function_mapping", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_relaxed_extended_json", "target": "docs_features_document_views_relaxed_extended_json", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_several_connections", "target": "docs_features_connect_and_explorer_sessions", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_several_connections", "target": "docs_features_tabs_close_tabs_of_connection", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_sidebar_node_per_connection", "target": "docs_features_connect_and_explorer_explorer_sidebar", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_streamed_test_stages", "target": "docs_features_connection_test_progress_event", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_table_default_view", "target": "docs_features_document_views_column_order_persistence", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_tabs_stay_mounted", "target": "docs_features_tabs_kept_alive_views", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_tanstack_form_zod", "target": "docs_features_connection_manager_form_validation", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_toasts", "target": "docs_features_error_handling_toasts", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_user_message_errors", "target": "docs_features_error_handling_three_field_error_shape", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_versioned_storage", "target": "docs_features_connection_manager_connections_json", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_virtualized_table", "target": "docs_features_document_views_table_view", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_window_size", "target": "docs_features_app_shell_window_config", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_client_options_instead_of_url", "target": "src_tauri_src_drivers_client_options_client_options", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_driver_trait_factory", "target": "src_tauri_src_drivers_factory_databasedriverfactory", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_test_timeout", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_test_timeout", "target": "docs_features_connection_test_test_timeout", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_test_timeout", "target": "docs_features_connection_test_running_time", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_test_timeout", "target": "src_tauri_src_drivers_mongo_official_driver_test_server_selection_timeout", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_test_timeout", "target": "docs_architecture_decisions_background_tasks", "relation": "conceptually_related_to", "confidence": "INFERRED", "confidence_score": 0.75, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}], "hyperedges": []} \ No newline at end of file diff --git a/graphify-out/cache/semantic/pd5fd89c46bb5/7fe20ca22729c59cde575e5cfa5a92ef98c889fcd5093cab35576b25aadaf73c.json b/graphify-out/cache/semantic/pd5fd89c46bb5/7fe20ca22729c59cde575e5cfa5a92ef98c889fcd5093cab35576b25aadaf73c.json new file mode 100644 index 0000000..4d056e4 --- /dev/null +++ b/graphify-out/cache/semantic/pd5fd89c46bb5/7fe20ca22729c59cde575e5cfa5a92ef98c889fcd5093cab35576b25aadaf73c.json @@ -0,0 +1 @@ +{"nodes": [{"id": "pnpm_workspace", "label": "pnpm-workspace.yaml", "file_type": "code", "source_file": "pnpm-workspace.yaml", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "pnpm_workspace_allowbuilds", "label": "allowBuilds (esbuild, vue-demi)", "file_type": "code", "source_file": "pnpm-workspace.yaml", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}], "edges": [{"source": "pnpm_workspace", "target": "pnpm_workspace_allowbuilds", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "pnpm-workspace.yaml", "source_location": null, "weight": 1.0}, {"source": "pnpm_workspace", "target": "package_packagemanager", "relation": "conceptually_related_to", "confidence": "INFERRED", "confidence_score": 0.75, "source_file": "pnpm-workspace.yaml", "source_location": null, "weight": 1.0}], "hyperedges": []} \ No newline at end of file diff --git a/graphify-out/cache/semantic/pd5fd89c46bb5/852bf5b9bea2dda8350559bd9be6e46c729c37b2a8f8bc338f30512163008fde.json b/graphify-out/cache/semantic/pd5fd89c46bb5/852bf5b9bea2dda8350559bd9be6e46c729c37b2a8f8bc338f30512163008fde.json new file mode 100644 index 0000000..d69ee3d --- /dev/null +++ b/graphify-out/cache/semantic/pd5fd89c46bb5/852bf5b9bea2dda8350559bd9be6e46c729c37b2a8f8bc338f30512163008fde.json @@ -0,0 +1 @@ +{"nodes": [{"id": "docs_architecture_decisions", "label": "Decision log (decisions.md)", "file_type": "document", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "docs_architecture_decisions_background_tasks", "label": "Decision: testing, connecting and finding documents run as cancellable background tasks with an id", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "A slow connection froze the UI and there was no way to give up (issue #27). Rejected: a timeout only."}, {"id": "docs_architecture_decisions_basic_auth_only", "label": "Decision: only basic authentication (username, password, authentication database) for the MVP", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "\"For MVP, Basic is sufficient\" (issue #3). Rejected: other mechanisms for now."}, {"id": "docs_architecture_decisions_branching_model", "label": "Decision: feature branches off develop merged through GitHub pull requests; develop goes to main from time to time", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "The history records no reason."}, {"id": "docs_architecture_decisions_bundled_roboto", "label": "Decision: Roboto bundled with the app", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "The font of the built app looked wrong (issue #30). Rejected: the system font."}, {"id": "docs_architecture_decisions_client_options_instead_of_url", "label": "Decision: the driver gets ClientOptions instead of a URL string", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "No hand-made percent-encoding, and srv strings work. Rejected: fixing the URL building."}, {"id": "docs_architecture_decisions_codemirror_query_input", "label": "Decision: the query input is a single-line CodeMirror editor with MongoDB autocomplete", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "The history gives no reason."}, {"id": "docs_architecture_decisions_commands_take_connection_id", "label": "Decision: commands that talk to MongoDB take a connection id; Rust loads the saved connection and its password", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "The UI copy can be stale after an edit, and the password no longer passes through the web view for these calls. Rejected: sending the connection object."}, {"id": "docs_architecture_decisions_confirm_dialog", "label": "Decision: a ConfirmDialog with useConfirm() instead of the browser confirm()", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "It fits the app and will be needed for deleting data."}, {"id": "docs_architecture_decisions_connection_model_v2", "label": "Decision: connection model version 2 (target + options for host targets; connectionType dropped; v1 migrated and kept as connections.v1.json)", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "Connection strings and options (issue #4) were out of reach with a host field and a hand-built URL; connectionType was never read. Rejected: keeping connectionType; options for both modes overriding the string."}, {"id": "docs_architecture_decisions_connection_string_credentials", "label": "Decision: a connection string keeps all its options; its credentials move out on save and go back only in memory before parsing", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "One place per setting and no password in connections.json; the driver checks authMechanism against the user info. Rejected: storing the string as typed."}, {"id": "docs_architecture_decisions_connection_test_skips_serverstatus", "label": "Decision: the connection test skips serverStatus without the privilege, reads the version from buildInfo, marks failing stages failed, and its events carry a test id", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "A user who may only read one database failed the test although connecting worked; a failed stage stayed grey; two tests could not be told apart. Rejected: dropping the status stage."}, {"id": "docs_architecture_decisions_credential_store_trait_driver_factory", "label": "Decision: a CredentialStore trait and a driver factory in ConnectionHandler", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "Repository and pool can be tested without the keyring and without MongoDB; the commands no longer downcast the repository."}, {"id": "docs_architecture_decisions_credentials_stay_in_rust", "label": "Decision: credentials stay in Rust; get_connections and get_connection never return a password", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "Passwords should not be exposed to the web view, and the grid should not hit the keyring for every connection. Rejected: hydrating passwords when listing."}, {"id": "docs_architecture_decisions_dependency_injection", "label": "Decision: dependency injection on both sides (Tauri managed state in Rust, provider registry with useDomain in Vue)", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "Services are created once and can be swapped behind an interface (ConnectionService, ConnectionRepository, LogStrategy)."}, {"id": "docs_architecture_decisions_docs_and_working_rules", "label": "Decision: docs/ documents features, architecture, decisions and plans; CLAUDE.md holds the working rules", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "The project is being revived; its state had to be written down first, and work should follow the same rules as the other projects."}, {"id": "docs_architecture_decisions_driver_leaves_pool", "label": "Decision: a driver leaves the pool on disconnect, edit, delete and when its first command fails", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "A driver is created before the server is contacted, so a wrong password or an edited host stuck until restart. Rejected: keeping drivers for the life of the process."}, {"id": "docs_architecture_decisions_driver_per_connection", "label": "Decision: one driver per connection id kept for the life of the process (later revised)", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "Reuse the MongoDB client connection pool across commands."}, {"id": "docs_architecture_decisions_driver_trait_factory", "label": "Decision: database access through a DatabaseDriver trait with a factory per ConnectorKind", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "Room for more drivers or a second MongoDB implementation."}, {"id": "docs_architecture_decisions_edit_dialog_shows_password", "label": "Decision: the edit dialog shows the stored password, fetched from the keyring only when it opens", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "Users wanted to see and fix the password they saved; partly reverses \"credentials never reach the frontend\" of PR #23. Rejected: keeping the password write-only."}, {"id": "docs_architecture_decisions_estimated_count", "label": "Decision: an empty filter is counted with estimatedDocumentCount (marked as estimate), a filter exactly", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "countDocuments scans a big collection. Rejected: always counting exactly; no count."}, {"id": "docs_architecture_decisions_graphify_knowledge_graph", "label": "Decision: Claude Code gets a graphify knowledge graph; only the semantic cache is committed, each clone builds the graph", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "Same setup as smtp-relay: the cache saves the model cost of reading the docs again, and graph.json is rewritten whole on every rebuild. Rejected: committing graph.json; ignoring graphify-out/ completely."}, {"id": "docs_architecture_decisions_host_options", "label": "Decision: host options TLS, CA file, invalid certificates, replica set, server selection timeout", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "What issue #4 asked for, without client certificates. Rejected: more fields such as read preference; those go into a connection string."}, {"id": "docs_architecture_decisions_keyring_4", "label": "Decision: keyring 4 with its default v1 mode (Keychain, Credential Manager, Secret Service); reverses the keyutils cache", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "All packages were updated; keyring 4 moved its stores into separate crates and v1 keeps the API; keyring 3 passwords stay readable. Rejected: keyring-core with chosen store crates; staying on keyring 3.6."}, {"id": "docs_architecture_decisions_latest_packages", "label": "Decision: all packages to their latest release, except TypeScript 7 and Tauri 3", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "Old versions would only make later updates bigger; vue-tsc cannot run on TypeScript 7 (no lib/tsc); Tauri 3 is an alpha. Rejected: staying on old versions; TypeScript 7 without template type-checking."}, {"id": "docs_architecture_decisions_linux_keyring_backend", "label": "Decision (superseded): Linux keyring via linux-native-sync-persistent with vendored OpenSSL", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "keyutils for fast kernel-level access during a session, Secret Service for persistence; vendored bundles OpenSSL. Rejected: sync-secret-service alone."}, {"id": "docs_architecture_decisions_log_level_by_build", "label": "Decision: logs at Debug in development builds, Info in release builds", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "Release builds logged everything at debug."}, {"id": "docs_architecture_decisions_mod_key_shortcuts", "label": "Decision: shortcuts on mod (Cmd on macOS, Ctrl elsewhere); on macOS the default menu without \"Close Window\"", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "Mac users expect Cmd; Cmd+W would otherwise close the app. Not checked on a Mac."}, {"id": "docs_architecture_decisions_new_error_variants", "label": "Decision: CredentialsUnavailable, ConnectionNotFound and StorageFailed variants; a failed find carries the server message", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "A keyring failure read as a host problem; a broken query only said \"check your permissions\"."}, {"id": "docs_architecture_decisions_no_credentials_in_logs", "label": "Decision: no credentials in logs; the connection URI logged only in debug builds with the password masked", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "The URI carries the password."}, {"id": "docs_architecture_decisions_one_view_per_tab", "label": "Decision: every tab has exactly one view keyed by its own id; mod+enter runs only in the active tab", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "Views multiplied and ran duplicate queries, and Ctrl+Enter ran everywhere (issue #42)."}, {"id": "docs_architecture_decisions_optional_connection_test", "label": "Decision: testing a connection is optional; a connection can be saved untested", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "Issue #26 gives no reason. Rejected: requiring a successful test before saving."}, {"id": "docs_architecture_decisions_os_keyring", "label": "Decision: passwords stored in the OS keyring through the keyring crate", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "Uses the native store on Windows, macOS and Linux. Rejected: Tauri Stronghold (added 2026-02-15, reverted 2026-02-17)."}, {"id": "docs_architecture_decisions_own_query_parser", "label": "Decision: shell-style queries parsed by an own parser (src/utils/queryParser) into extended JSON, which bson turns into BSON in Rust", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "It accepts unquoted keys, single quotes and shell functions like ObjectId(...) that JSON does not; the history gives no reason for not using a library."}, {"id": "docs_architecture_decisions_paging_sort_projection", "label": "Decision: paging, total count, sort and projection in the query tab; paging repeats the executed query", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "Issue #12 listed them; typing must not change what the next page means."}, {"id": "docs_architecture_decisions_parser_checks_shell_functions", "label": "Decision: the query parser checks every shell function and produces only shapes bson reads as types; regexes as $regularExpression", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "Invalid or date-only values silently matched nothing."}, {"id": "docs_architecture_decisions_pnpm_pin", "label": "Decision: pnpm pinned to 12 in packageManager and devEngines; pnpm-workspace.yaml allows the esbuild and vue-demi build scripts", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "node_modules came from pnpm 10 while the machines run newer versions; same setup as smtp-relay. Rejected: no pin; pinning the installed 11.3."}, {"id": "docs_architecture_decisions_relaxed_extended_json", "label": "Decision: documents leave Rust as relaxed extended JSON, an Int64 beyond +/-2^53 as $numberLong", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "Readable dates in the JSON view and no silent change of large integers. Rejected: canonical extended JSON."}, {"id": "docs_architecture_decisions_several_connections", "label": "Decision: several connections open at once; disconnecting closes the connection tabs; each tab belongs to its connection", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "Working with two servers is common; tabs used to survive a switch of connection and query the new server. Rejected: closing all tabs on switching; keeping tabs per connection while only one is open."}, {"id": "docs_architecture_decisions_sidebar_node_per_connection", "label": "Decision: the sidebar has one node per open connection with refresh, edit and disconnect; the grid gives way once a connection is connected", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "Editing must work while connected; the view switches only on success (issue #27)."}, {"id": "docs_architecture_decisions_smtp_relay", "label": "smtp-relay (sibling project used as template for docs, pnpm pin and graphify setup)", "file_type": "concept", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "docs_architecture_decisions_streamed_test_stages", "label": "Decision: the connection test streams each stage as a Tauri event", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "Each stage shows when it really finishes, not all at once."}, {"id": "docs_architecture_decisions_table_default_view", "label": "Decision: documents shown in a table by default, JSON second; column order saved per connection, database and collection", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "The commit gives no reason for the default."}, {"id": "docs_architecture_decisions_tabs_stay_mounted", "label": "Decision: open tabs stay mounted (v-show and KeepAlive) so a tab keeps its query and results", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "Switching tabs lost the state. Rejected: v-if."}, {"id": "docs_architecture_decisions_tanstack_form_zod", "label": "Decision: forms on TanStack Form with zod 4 and shadcn-vue Field", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "@vee-validate/zod only supports zod 3; vee-validate 5 has been a beta since 2025; shadcn-vue documents TanStack Form. Rejected: vee-validate 5 beta, an own adapter for vee-validate 4, keeping zod 3."}, {"id": "docs_architecture_decisions_tauri_vue_stack", "label": "Decision: Tauri 2 with a Rust core and a Vue 3 web view", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "README: high-performance backend with secure system-level access; modern, responsive, maintainable frontend; goal is a free European alternative to Studio 3T."}, {"id": "docs_architecture_decisions_test_timeout", "label": "Decision: the connection test waits 10 s for a server unless the connection sets a timeout; connecting keeps the driver's 30 s; the waiting stage shows its seconds", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "A wrong port sat at Ping Database for 30 s without a sign of progress (37f0aea). Rejected: one shorter timeout for connecting too; a fixed timeout overriding the connection's."}, {"id": "docs_architecture_decisions_testing_strategy", "label": "Decision: vitest in happy-dom with Tauri mockIPC; Rust unit tests with an in-memory credential store and temp dirs; integration tests against a real MongoDB marked #[ignore]", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "The rule \"every piece of logic gets unit tests\"; keyring and MongoDB cannot be in the default run. Rejected: mounting components in tests (logic moved into modules instead)."}, {"id": "docs_architecture_decisions_toasts", "label": "Decision: errors shown as vue-sonner toasts instead of an error box per component", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "Cleaner, less duplicated code and styling, fits the theme (issue #36). Rejected: the error boxes of before."}, {"id": "docs_architecture_decisions_url_percent_encoding", "label": "Decision (superseded): password set on the URL with Url::set_password (percent-encoded)", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "Passwords with special characters failed with \"password must be URL encoded\" (issue #21)."}, {"id": "docs_architecture_decisions_user_message_errors", "label": "Decision: every error carries kind, message and user_message; the UI shows user_message", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "Raw driver errors were long and unreadable (issue #16). Rejected: showing the raw error."}, {"id": "docs_architecture_decisions_versioned_storage", "label": "Decision: connections stored as a versioned JSON object keyed by id", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "O(1) access by id, and a version to migrate the file when its shape changes. Rejected: a plain array of connections."}, {"id": "docs_architecture_decisions_virtualized_table", "label": "Decision: the table renders only visible rows (TanStack Virtual), caches formatted cells and sizes columns from a sample", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "Bigger result sets render fast (PR #44, issue #39)."}, {"id": "docs_architecture_decisions_window_size", "label": "Decision: the default window is 1200 x 900", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "The history gives no reason."}, {"id": "docs_architecture_decisions_ci_github_actions", "label": "Decision: CI on GitHub Actions (frontend tests, build, cargo test, cargo clippy on Linux, macOS and Windows for develop, main and every pull request; integration tests on Linux only against a mongo:8.2 service container; no installers)", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "The app is only developed on Linux; nobody could build it on macOS or Windows. The integration tests only ran when someone remembered them, and service containers exist only on Linux runners (b69ba0b, 9e6cc5c). Rejected: building installers in CI (no signing yet); integration tests on every platform."}], "edges": [{"source": "docs_architecture_decisions_docs_and_working_rules", "target": "docs_architecture_decisions_smtp_relay", "relation": "conceptually_related_to", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_edit_dialog_shows_password", "target": "docs_architecture_decisions_credentials_stay_in_rust", "relation": "conceptually_related_to", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_graphify_knowledge_graph", "target": "docs_architecture_decisions_smtp_relay", "relation": "conceptually_related_to", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_keyring_4", "target": "docs_architecture_decisions_linux_keyring_backend", "relation": "conceptually_related_to", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_pnpm_pin", "target": "docs_architecture_decisions_smtp_relay", "relation": "conceptually_related_to", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_background_tasks", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_basic_auth_only", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_branching_model", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_bundled_roboto", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_client_options_instead_of_url", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_codemirror_query_input", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_commands_take_connection_id", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_confirm_dialog", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_connection_model_v2", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_connection_string_credentials", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_connection_test_skips_serverstatus", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_credential_store_trait_driver_factory", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_credentials_stay_in_rust", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_dependency_injection", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_docs_and_working_rules", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_driver_leaves_pool", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_driver_per_connection", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_driver_trait_factory", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_edit_dialog_shows_password", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_estimated_count", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_graphify_knowledge_graph", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_host_options", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_keyring_4", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_latest_packages", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_linux_keyring_backend", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_log_level_by_build", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_mod_key_shortcuts", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_new_error_variants", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_no_credentials_in_logs", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_one_view_per_tab", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_optional_connection_test", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_os_keyring", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_own_query_parser", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_paging_sort_projection", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_parser_checks_shell_functions", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_pnpm_pin", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_relaxed_extended_json", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_several_connections", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_sidebar_node_per_connection", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_smtp_relay", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_streamed_test_stages", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_table_default_view", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_tabs_stay_mounted", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_tanstack_form_zod", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_tauri_vue_stack", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_testing_strategy", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_toasts", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_url_percent_encoding", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_user_message_errors", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_versioned_storage", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_virtualized_table", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_window_size", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_background_tasks", "target": "src_tauri_src_task_manager_commands_await_task_result", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_background_tasks", "target": "src_tauri_src_task_manager_commands_cancel_task", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_confirm_dialog", "target": "src_components_confirmdialog", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_confirm_dialog", "target": "src_composables_useconfirm_useconfirm", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_credential_store_trait_driver_factory", "target": "src_tauri_src_connection_handler_connection_handler_connectionhandler", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_credential_store_trait_driver_factory", "target": "src_tauri_src_connector_repositories_credentials_credential_store_credentialstore", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_credentials_stay_in_rust", "target": "src_tauri_src_connector_commands_get_connections_get_connections", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_dependency_injection", "target": "src_domains_connections_services_connectionservice_connectionservice", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_dependency_injection", "target": "src_domains_index_usedomain", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_dependency_injection", "target": "src_domains_logger_strategies_logstrategy_logstrategy", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_docs_and_working_rules", "target": "claude", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_driver_trait_factory", "target": "src_tauri_src_connector_models_connection_connectorkind", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_driver_trait_factory", "target": "src_tauri_src_drivers_driver_databasedriver", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_new_error_variants", "target": "src_tauri_src_drivers_errors_drivererror_connectionnotfound", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_new_error_variants", "target": "src_tauri_src_drivers_errors_drivererror_credentialsunavailable", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_new_error_variants", "target": "src_tauri_src_drivers_errors_drivererror_storagefailed", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_os_keyring", "target": "docs_features_credential_storage_tauri_stronghold", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_pnpm_pin", "target": "package_devengines", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_pnpm_pin", "target": "package_packagemanager", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_pnpm_pin", "target": "pnpm_workspace", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_tauri_vue_stack", "target": "readme_studio_3t", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_tauri_vue_stack", "target": "readme_tech_stack", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_testing_strategy", "target": "claude_testing_policy", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_graphify_knowledge_graph", "target": "claude_graphify_rules", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.75, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_connection_test_skips_serverstatus", "target": "src_tauri_src_drivers_mongo_official_driver_is_unauthorized", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.75, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_tanstack_form_zod", "target": "src_components_connection_connectionform_connectionformschema", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.75, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_branching_model", "target": "claude_git_workflow", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_codemirror_query_input", "target": "docs_features_query_tab_mongo_autocomplete", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_commands_take_connection_id", "target": "docs_features_credential_storage_credential_hydration", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_confirm_dialog", "target": "docs_features_connection_manager_delete_flow", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_connection_test_skips_serverstatus", "target": "docs_features_connection_test_progress_event", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_driver_per_connection", "target": "docs_features_connect_and_explorer_driver_pool", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_edit_dialog_shows_password", "target": "docs_features_credential_storage_password_to_webview", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_optional_connection_test", "target": "docs_features_connection_test", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_os_keyring", "target": "docs_features_credential_storage_keyring_crate", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_parser_checks_shell_functions", "target": "docs_features_query_tab_bson_regex", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_tauri_vue_stack", "target": "docs_architecture_overview_rust_core", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_tauri_vue_stack", "target": "docs_architecture_overview_web_view", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_codemirror_query_input", "target": "src_components_query_queryinput", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_commands_take_connection_id", "target": "src_tauri_src_connection_handler_connection_handler_connectionhandler_get_or_connect", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_connection_model_v2", "target": "src_tauri_src_connector_models_connection_connectiontarget", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_connection_model_v2", "target": "src_tauri_src_connector_repositories_migrations_migrate_v1", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_connection_model_v2", "target": "src_tauri_src_connector_repositories_storage_wrapper_storage_version", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_connection_string_credentials", "target": "src_tauri_src_connector_models_uri_split_uri_credentials", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_connection_string_credentials", "target": "src_tauri_src_drivers_client_options_with_user_info", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_credential_store_trait_driver_factory", "target": "src_tauri_src_connection_handler_connection_handler_connectionhandler_with_factory", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_docs_and_working_rules", "target": "docs_readme", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_driver_leaves_pool", "target": "src_tauri_src_connection_handler_connection_handler_connectionhandler_disconnect", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_driver_leaves_pool", "target": "src_tauri_src_connection_handler_connection_handler_connectionhandler_open", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_driver_per_connection", "target": "src_tauri_src_connection_handler_connection_handler_connectionhandler", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_edit_dialog_shows_password", "target": "src_tauri_src_connector_commands_get_connection_password_get_connection_password", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_host_options", "target": "src_tauri_src_connector_models_options_connectionoptions", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_keyring_4", "target": "src_tauri_src_connector_repositories_credentials_credential_service_credentialservice", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_log_level_by_build", "target": "src_tauri_src_lib", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_new_error_variants", "target": "src_tauri_src_drivers_errors_drivererror_finddocumentsfailed", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_one_view_per_tab", "target": "src_domains_shortcuts_models_commands_commands_event_execute", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_os_keyring", "target": "src_tauri_src_connector_repositories_credentials_credential_service_credentialservice", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_own_query_parser", "target": "src_utils_queryparser_parser_createparser", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_parser_checks_shell_functions", "target": "src_utils_queryparser_transformfunction_transformfunction", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_relaxed_extended_json", "target": "src_tauri_src_drivers_documents_document_to_json", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_sidebar_node_per_connection", "target": "src_components_appsidebar", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_testing_strategy", "target": "src_tauri_src_connector_repositories_credentials_credential_store_memorycredentialstore", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_testing_strategy", "target": "src_test_setup", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_versioned_storage", "target": "src_tauri_src_connector_repositories_storage_wrapper_connectionstorage", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_client_options_instead_of_url", "target": "docs_architecture_decisions_url_percent_encoding", "relation": "conceptually_related_to", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_connection_model_v2", "target": "docs_architecture_decisions_versioned_storage", "relation": "conceptually_related_to", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_driver_leaves_pool", "target": "docs_architecture_decisions_driver_per_connection", "relation": "conceptually_related_to", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_background_tasks", "target": "docs_features_background_tasks_task_pattern", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_basic_auth_only", "target": "docs_features_credential_storage_auth_mechanisms_limit", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_bundled_roboto", "target": "docs_features_app_shell_bundled_roboto", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_connection_model_v2", "target": "docs_features_connection_manager_connection_model", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_connection_model_v2", "target": "docs_features_connection_manager_v1_migration", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_connection_string_credentials", "target": "docs_features_connection_manager_rust_normalize", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_connection_string_credentials", "target": "docs_features_credential_storage_user_info_reinsertion", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_connection_test_skips_serverstatus", "target": "docs_features_connection_test_serverstatus_skip", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_credential_store_trait_driver_factory", "target": "docs_features_credential_storage_credential_store_trait", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_credentials_stay_in_rust", "target": "docs_features_credential_storage_credential_hydration", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_dependency_injection", "target": "docs_architecture_overview_managed_state", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_dependency_injection", "target": "docs_architecture_overview_provider_registry", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_driver_leaves_pool", "target": "docs_features_connect_and_explorer_driver_pool", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_estimated_count", "target": "docs_features_query_tab_find_and_count", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_graphify_knowledge_graph", "target": "readme_graphify", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_host_options", "target": "docs_features_connection_manager_host_options", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_keyring_4", "target": "docs_features_credential_storage_keyring_crate", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_linux_keyring_backend", "target": "docs_features_credential_storage_linux_backend", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_log_level_by_build", "target": "docs_features_logging_log_level_by_build", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_mod_key_shortcuts", "target": "docs_features_shortcuts_macos_menu", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_mod_key_shortcuts", "target": "docs_features_shortcuts_mod_key", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_new_error_variants", "target": "docs_features_error_handling_server_message_on_find", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_no_credentials_in_logs", "target": "docs_features_logging_no_credentials", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_one_view_per_tab", "target": "docs_features_tabs_one_view_per_tab", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_own_query_parser", "target": "docs_features_query_tab_shell_query_parser", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_paging_sort_projection", "target": "docs_features_query_tab_paging", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_parser_checks_shell_functions", "target": "docs_features_query_tab_shell_function_mapping", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_relaxed_extended_json", "target": "docs_features_document_views_relaxed_extended_json", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_several_connections", "target": "docs_features_connect_and_explorer_sessions", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_several_connections", "target": "docs_features_tabs_close_tabs_of_connection", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_sidebar_node_per_connection", "target": "docs_features_connect_and_explorer_explorer_sidebar", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_streamed_test_stages", "target": "docs_features_connection_test_progress_event", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_table_default_view", "target": "docs_features_document_views_column_order_persistence", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_tabs_stay_mounted", "target": "docs_features_tabs_kept_alive_views", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_tanstack_form_zod", "target": "docs_features_connection_manager_form_validation", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_toasts", "target": "docs_features_error_handling_toasts", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_user_message_errors", "target": "docs_features_error_handling_three_field_error_shape", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_versioned_storage", "target": "docs_features_connection_manager_connections_json", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_virtualized_table", "target": "docs_features_document_views_table_view", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_window_size", "target": "docs_features_app_shell_window_config", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_client_options_instead_of_url", "target": "src_tauri_src_drivers_client_options_client_options", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_driver_trait_factory", "target": "src_tauri_src_drivers_factory_databasedriverfactory", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_test_timeout", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_test_timeout", "target": "docs_features_connection_test_test_timeout", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_test_timeout", "target": "docs_features_connection_test_running_time", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_test_timeout", "target": "src_tauri_src_drivers_mongo_official_driver_test_server_selection_timeout", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_test_timeout", "target": "docs_architecture_decisions_background_tasks", "relation": "conceptually_related_to", "confidence": "INFERRED", "confidence_score": 0.75, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_ci_github_actions", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_ci_github_actions", "target": "github_workflows_ci", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_ci_github_actions", "target": "github_workflows_ci_integration_job", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_ci_github_actions", "target": "docs_features_app_shell_ci", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_ci_github_actions", "target": "docs_architecture_decisions_testing_strategy", "relation": "conceptually_related_to", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_ci_github_actions", "target": "claude_integration_tests", "relation": "conceptually_related_to", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_ci_github_actions", "target": "docs_features_app_shell_no_ci_release", "relation": "conceptually_related_to", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_ci_github_actions", "target": "docs_architecture_decisions_branching_model", "relation": "conceptually_related_to", "confidence": "INFERRED", "confidence_score": 0.75, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}], "hyperedges": []} \ No newline at end of file diff --git a/graphify-out/cache/semantic/pd5fd89c46bb5/9027d3c975a2745a59811acaaf0d97a3c7433223a85ae570acf2c9e55eb51efc.json b/graphify-out/cache/semantic/pd5fd89c46bb5/9027d3c975a2745a59811acaaf0d97a3c7433223a85ae570acf2c9e55eb51efc.json new file mode 100644 index 0000000..a5510cc --- /dev/null +++ b/graphify-out/cache/semantic/pd5fd89c46bb5/9027d3c975a2745a59811acaaf0d97a3c7433223a85ae570acf2c9e55eb51efc.json @@ -0,0 +1 @@ +{"nodes": [{"id": "docs_features_document_views", "label": "Document views feature (document-views.md)", "file_type": "document", "source_file": "docs/features/document-views.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "docs_features_document_views_relaxed_extended_json", "label": "Relaxed extended JSON input ($oid, $date, $numberDecimal, $binary; Int64 beyond +/-2^53 kept as $numberLong at any depth)", "file_type": "rationale", "source_file": "docs/features/document-views.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "Readable dates, and no silent change of a large Int64 that a JavaScript number would alter."}, {"id": "docs_features_document_views_table_view", "label": "Virtualized table view (TanStack Table 9 with registered features + TanStack Virtual, 33 px rows, 20 rows overscan)", "file_type": "concept", "source_file": "docs/features/document-views.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "Virtual rows, cached cell text and sampled column widths so bigger results render fast (PR #44, issue #39)."}, {"id": "docs_features_document_views_column_union", "label": "Columns = union of top-level fields (_id first, rest alphabetical; a missing field is an empty cell)", "file_type": "concept", "source_file": "docs/features/document-views.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "docs_features_document_views_cell_formatting", "label": "Extended-JSON cell formatting (formatCellValue, cached per document and field; 120-char truncation with tooltip)", "file_type": "concept", "source_file": "docs/features/document-views.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "docs_features_document_views_client_side_sort", "label": "Header sort runs in the web view over the loaded page only (server sort via the query tab sort input)", "file_type": "concept", "source_file": "docs/features/document-views.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "docs_features_document_views_column_order_persistence", "label": "Column order saved in localStorage (monolens:columnOrder:::), merged with current fields", "file_type": "rationale", "source_file": "docs/features/document-views.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "A reordered collection looks the same next time."}, {"id": "docs_features_document_views_column_sizing", "label": "Column widths from the 90th percentile of text lengths in the first 500 documents (60-500 px, resizable, not saved)", "file_type": "concept", "source_file": "docs/features/document-views.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "docs_features_document_views_json_view", "label": "JSON view (one collapsible read-only CodeMirror editor per document, One Dark, max 384 px)", "file_type": "concept", "source_file": "docs/features/document-views.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "docs_features_document_views_read_only", "label": "Read-only document views (no add, edit or delete)", "file_type": "concept", "source_file": "docs/features/document-views.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "docs_features_document_views_nested_values_flat", "label": "Nested values shown as flat JSON text in a cell (issue #39)", "file_type": "concept", "source_file": "docs/features/document-views.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}], "edges": [{"source": "docs_features_document_views", "target": "docs_features_document_views_relaxed_extended_json", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/document-views.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_document_views", "target": "docs_features_document_views_table_view", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/document-views.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_document_views", "target": "docs_features_document_views_column_union", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/document-views.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_document_views", "target": "docs_features_document_views_cell_formatting", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/document-views.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_document_views", "target": "docs_features_document_views_client_side_sort", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/document-views.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_document_views", "target": "docs_features_document_views_column_order_persistence", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/document-views.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_document_views", "target": "docs_features_document_views_column_sizing", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/document-views.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_document_views", "target": "docs_features_document_views_json_view", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/document-views.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_document_views", "target": "docs_features_document_views_read_only", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/document-views.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_document_views", "target": "docs_features_document_views_nested_values_flat", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/document-views.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_document_views_read_only", "target": "docs_readme_document_writing_issues", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/features/document-views.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_document_views_nested_values_flat", "target": "docs_readme_display_issues", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/features/document-views.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_document_views_relaxed_extended_json", "target": "src_tauri_src_drivers_documents", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/document-views.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_document_views_relaxed_extended_json", "target": "src_tauri_src_drivers_documents_document_to_json", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/features/document-views.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_document_views_relaxed_extended_json", "target": "src_tauri_src_drivers_documents_max_safe_integer", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/features/document-views.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_document_views_table_view", "target": "src_components_documents_table_tabledocumentsview", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/document-views.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_document_views_table_view", "target": "src_composables_table_usedocumenttable_usedocumenttable", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/document-views.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_document_views_table_view", "target": "src_composables_table_usedocumenttable_documenttablefeatures", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/document-views.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_document_views_cell_formatting", "target": "src_utils_formatcellvalues_formatcellvalue", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/document-views.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_document_views_cell_formatting", "target": "src_composables_table_useformattedvaluecache_useformattedvaluecache", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/document-views.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_document_views_cell_formatting", "target": "docs_features_document_views_relaxed_extended_json", "relation": "conceptually_related_to", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/features/document-views.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_document_views_column_order_persistence", "target": "src_composables_table_usecolumndragdrop_usecolumndragdrop", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/features/document-views.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_document_views_column_order_persistence", "target": "src_composables_table_usedocumenttable_usedocumenttable", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/features/document-views.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_document_views_column_sizing", "target": "src_composables_table_usecolumnsizing_computecolumnwidths", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/features/document-views.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_document_views_json_view", "target": "src_components_documents_json_jsondocumentseditor", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/document-views.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_document_views_json_view", "target": "docs_architecture_overview_codemirror", "relation": "conceptually_related_to", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/features/document-views.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_document_views_client_side_sort", "target": "docs_features_query_tab_query_view", "relation": "conceptually_related_to", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/document-views.md", "source_location": null, "weight": 1.0}], "hyperedges": []} \ No newline at end of file diff --git a/graphify-out/cache/semantic/pd5fd89c46bb5/913ff821ea54602dd3e231825ed6e2a4ec98a00b7f499e5b59c41ea0b0e9ae83.json b/graphify-out/cache/semantic/pd5fd89c46bb5/913ff821ea54602dd3e231825ed6e2a4ec98a00b7f499e5b59c41ea0b0e9ae83.json new file mode 100644 index 0000000..f9cd7b7 --- /dev/null +++ b/graphify-out/cache/semantic/pd5fd89c46bb5/913ff821ea54602dd3e231825ed6e2a4ec98a00b7f499e5b59c41ea0b0e9ae83.json @@ -0,0 +1 @@ +{"nodes": [{"id": "docs_features_tabs", "label": "Tabs feature (tabs.md)", "file_type": "document", "source_file": "docs/features/tabs.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "docs_features_tabs_tab_model", "label": "Tab model { id, context: { connectionId, database, collection }, type: TabKind }", "file_type": "concept", "source_file": "docs/features/tabs.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "docs_features_tabs_tab_history", "label": "Tab history (closing returns to the previously used tab)", "file_type": "rationale", "source_file": "docs/features/tabs.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "A history instead of \"the tab to the left\"."}, {"id": "docs_features_tabs_one_tab_per_collection", "label": "One tab per collection and kind (hasContext; the sidebar switches instead of adding)", "file_type": "rationale", "source_file": "docs/features/tabs.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "docs_features_tabs_kept_alive_views", "label": "Tab views stay mounted (v-show) so a tab keeps its query and results", "file_type": "rationale", "source_file": "docs/features/tabs.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "docs_features_tabs_one_view_per_tab", "label": "One view per tab keyed by its own id; the view reads its own tab, never the active one", "file_type": "rationale", "source_file": "docs/features/tabs.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "Before, views keyed by the active tab multiplied, each new copy ran its query, and Ctrl+Enter ran in all of them (issue #42)."}, {"id": "docs_features_tabs_close_tabs_of_connection", "label": "A tab belongs to its connection: closeTabsOf(connectionId) on disconnect; the label shows connection and database", "file_type": "rationale", "source_file": "docs/features/tabs.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "docs_features_tabs_placeholder_tab_kinds", "label": "Placeholder tab kinds (INDEXES, AGGREGATION, EXPORTER, IMPORTER, STATISTICS) mapped to TabViewQuery", "file_type": "concept", "source_file": "docs/features/tabs.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "docs_features_tabs_memory_cost", "label": "Every open tab keeps its view and results in memory", "file_type": "concept", "source_file": "docs/features/tabs.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}], "edges": [{"source": "docs_features_tabs", "target": "docs_features_tabs_tab_model", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/tabs.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_tabs", "target": "docs_features_tabs_tab_history", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/tabs.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_tabs", "target": "docs_features_tabs_one_tab_per_collection", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/tabs.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_tabs", "target": "docs_features_tabs_kept_alive_views", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/tabs.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_tabs", "target": "docs_features_tabs_one_view_per_tab", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/tabs.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_tabs", "target": "docs_features_tabs_close_tabs_of_connection", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/tabs.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_tabs", "target": "docs_features_tabs_placeholder_tab_kinds", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/tabs.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_tabs", "target": "docs_features_tabs_memory_cost", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/tabs.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_tabs", "target": "docs_features_query_tab", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/tabs.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_tabs", "target": "docs_features_connect_and_explorer_no_session_restore", "relation": "conceptually_related_to", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/features/tabs.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_tabs_tab_model", "target": "src_domains_tabs_models_tab_tab", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/tabs.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_tabs_tab_model", "target": "src_domains_tabs_models_tab_kind_tabkind", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/tabs.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_tabs_tab_model", "target": "src_domains_context_models_context_context", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/tabs.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_tabs_tab_history", "target": "src_stores_tabsstore_usetabsstore", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/tabs.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_tabs_one_tab_per_collection", "target": "src_stores_tabsstore_usetabsstore", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/tabs.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_tabs_one_tab_per_collection", "target": "src_components_appsidebar_handlecollectionselect", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/tabs.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_tabs_kept_alive_views", "target": "src_components_tabs_tabscontainer", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/tabs.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_tabs_one_view_per_tab", "target": "src_components_tabs_views_tabviewquery", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/tabs.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_tabs_one_view_per_tab", "target": "src_components_tabs_tabscontainer", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/tabs.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_tabs_close_tabs_of_connection", "target": "src_stores_tabsstore_usetabsstore", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/tabs.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_tabs_close_tabs_of_connection", "target": "src_components_tabs_tabscontainer_connectionname", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/features/tabs.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_tabs_placeholder_tab_kinds", "target": "src_domains_tabs_models_tab_kind_tabkind", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/tabs.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_tabs_memory_cost", "target": "docs_features_tabs_kept_alive_views", "relation": "conceptually_related_to", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/features/tabs.md", "source_location": null, "weight": 1.0}], "hyperedges": []} \ No newline at end of file diff --git a/graphify-out/cache/semantic/pd5fd89c46bb5/926384da047e29f1459e73bb5340e0281816ccf639d202b2bb8dee8398f09eda.json b/graphify-out/cache/semantic/pd5fd89c46bb5/926384da047e29f1459e73bb5340e0281816ccf639d202b2bb8dee8398f09eda.json new file mode 100644 index 0000000..c3ade93 --- /dev/null +++ b/graphify-out/cache/semantic/pd5fd89c46bb5/926384da047e29f1459e73bb5340e0281816ccf639d202b2bb8dee8398f09eda.json @@ -0,0 +1 @@ +{"nodes": [{"id": "docs_features_background_tasks", "label": "Background tasks feature (background-tasks.md)", "file_type": "document", "source_file": "docs/features/background-tasks.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "docs_features_background_tasks_task_pattern", "label": "Cancellable background task pattern (start_* -> TaskManager.spawn_task -> uuid -> await_task_result / cancel_task; JoinHandle and AbortHandle maps)", "file_type": "rationale", "source_file": "docs/features/background-tasks.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "A task registry of our own instead of a timeout: the user decides when to give up, and the same mechanism serves every slow command (issue #27)."}, {"id": "docs_features_background_tasks_error_unwrapping", "label": "Task error unwrapping (TaskError::Failed carries the original error as JSON; abortableInvoke parses it back; Cancelled -> TaskCancelledError)", "file_type": "rationale", "source_file": "docs/features/background-tasks.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "So the toast shows the driver message and not \"The operation failed\"."}, {"id": "docs_features_background_tasks_abort_controllers", "label": "AbortControllers: one for the test, one per connecting connection, one per query tab (a new query cancels the running one)", "file_type": "concept", "source_file": "docs/features/background-tasks.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "docs_features_background_tasks_orphan_tasks", "label": "Never-awaited tasks stay in both task maps", "file_type": "concept", "source_file": "docs/features/background-tasks.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "docs_features_background_tasks_abort_not_server_side", "label": "Abort drops the future; a MongoDB operation already on the server is not killed", "file_type": "concept", "source_file": "docs/features/background-tasks.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}], "edges": [{"source": "docs_features_background_tasks", "target": "docs_features_background_tasks_task_pattern", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/background-tasks.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_background_tasks", "target": "docs_features_background_tasks_error_unwrapping", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/background-tasks.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_background_tasks", "target": "docs_features_background_tasks_abort_controllers", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/background-tasks.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_background_tasks", "target": "docs_features_background_tasks_orphan_tasks", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/background-tasks.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_background_tasks", "target": "docs_features_background_tasks_abort_not_server_side", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/background-tasks.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_background_tasks_task_pattern", "target": "src_tauri_src_task_manager_task_manager_taskmanager", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/background-tasks.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_background_tasks_task_pattern", "target": "src_tauri_src_task_manager_task_manager_taskmanager_spawn_task", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/background-tasks.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_background_tasks_task_pattern", "target": "src_tauri_src_task_manager_commands_await_task_result", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/background-tasks.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_background_tasks_task_pattern", "target": "src_tauri_src_task_manager_commands_cancel_task", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/background-tasks.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_background_tasks_task_pattern", "target": "src_tauri_src_database_commands_start_test_connection_start_test_connection", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/background-tasks.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_background_tasks_task_pattern", "target": "src_tauri_src_database_commands_start_connect_start_connect", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/background-tasks.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_background_tasks_task_pattern", "target": "src_tauri_src_database_commands_start_find_documents_start_find_documents", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/background-tasks.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_background_tasks_task_pattern", "target": "src_composables_useabortablecommand_abortableinvoke", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/background-tasks.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_background_tasks_error_unwrapping", "target": "src_tauri_src_task_manager_task_error_taskerror", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/background-tasks.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_background_tasks_error_unwrapping", "target": "src_composables_useabortablecommand_taskcancellederror", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/background-tasks.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_background_tasks_error_unwrapping", "target": "src_composables_useabortablecommand_abortableinvoke", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/background-tasks.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_background_tasks_error_unwrapping", "target": "docs_features_error_handling_three_field_error_shape", "relation": "conceptually_related_to", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/features/background-tasks.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_background_tasks_abort_controllers", "target": "src_stores_connectionstore_useconnectionstore", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/background-tasks.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_background_tasks_abort_controllers", "target": "src_components_tabs_views_tabviewquery", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/background-tasks.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_background_tasks_abort_controllers", "target": "src_domains_connections_services_connectionserviceipc_connectionserviceipc_testconnection", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/background-tasks.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_background_tasks_abort_controllers", "target": "src_domains_connections_services_connectionserviceipc_connectionserviceipc_connect", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/background-tasks.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_background_tasks_abort_controllers", "target": "src_domains_connections_services_connectionserviceipc_connectionserviceipc_finddocuments", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/background-tasks.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_background_tasks_orphan_tasks", "target": "src_tauri_src_task_manager_task_manager_taskmanager", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/features/background-tasks.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_background_tasks", "target": "claude_untested_hotspots", "relation": "conceptually_related_to", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/features/background-tasks.md", "source_location": null, "weight": 1.0}], "hyperedges": [{"id": "cancellable_background_task_flow", "label": "Cancellable background task flow (start_* command, spawn_task, await_task_result, cancel_task, abortableInvoke)", "nodes": ["src_tauri_src_task_manager_task_manager_taskmanager_spawn_task", "src_tauri_src_task_manager_commands_await_task_result", "src_tauri_src_task_manager_commands_cancel_task", "src_composables_useabortablecommand_abortableinvoke", "src_tauri_src_database_commands_start_connect_start_connect", "src_tauri_src_database_commands_start_test_connection_start_test_connection", "src_tauri_src_database_commands_start_find_documents_start_find_documents", "src_tauri_src_task_manager_task_error_taskerror"], "relation": "participate_in", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/background-tasks.md"}]} \ No newline at end of file diff --git a/graphify-out/cache/semantic/pd5fd89c46bb5/9415bb768611dfe0194eb38dfe413ebec00b7e5fe8ba39868007dc59c83271f4.json b/graphify-out/cache/semantic/pd5fd89c46bb5/9415bb768611dfe0194eb38dfe413ebec00b7e5fe8ba39868007dc59c83271f4.json new file mode 100644 index 0000000..8cd8f92 --- /dev/null +++ b/graphify-out/cache/semantic/pd5fd89c46bb5/9415bb768611dfe0194eb38dfe413ebec00b7e5fe8ba39868007dc59c83271f4.json @@ -0,0 +1 @@ +{"nodes": [{"id": "docs_features_shortcuts", "label": "Keyboard shortcuts feature (shortcuts.md)", "file_type": "document", "source_file": "docs/features/shortcuts.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "docs_features_shortcuts_command_registry_pattern", "label": "Command registry decoupled from key bindings (CommandRegistry: id -> { id, name, execute }, subscribers notified after execute)", "file_type": "rationale", "source_file": "docs/features/shortcuts.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "Bindings are a separate list so they can later be configured or listed in a command palette (issue #22)."}, {"id": "docs_features_shortcuts_event_commands", "label": "Event commands through the registry (event.execute has no action of its own; each TabViewQuery subscribes and runs only if its tab is active)", "file_type": "rationale", "source_file": "docs/features/shortcuts.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "docs_features_shortcuts_default_bindings", "label": "Default bindings (Mod+W tab.close, Mod+Shift+D connection.disconnect, Mod+Enter event.execute)", "file_type": "concept", "source_file": "docs/features/shortcuts.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "docs_features_shortcuts_mod_key", "label": "mod key string (Cmd on macOS, Ctrl elsewhere) built by eventToKeyString", "file_type": "rationale", "source_file": "docs/features/shortcuts.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "Mac users expect Cmd."}, {"id": "docs_features_shortcuts_macos_menu", "label": "macOS menu without \"Close Window\" (menu.rs, used by lib.rs on macOS only)", "file_type": "rationale", "source_file": "docs/features/shortcuts.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "Tauri default menu binds Cmd+W to \"Close Window\", which would close the app."}, {"id": "docs_features_shortcuts_global_listener", "label": "Global keydown listener in the shortcuts plugin (keys act while typing; bindings fixed and not listed)", "file_type": "concept", "source_file": "docs/features/shortcuts.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}], "edges": [{"source": "docs_features_shortcuts", "target": "docs_features_shortcuts_command_registry_pattern", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/shortcuts.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_shortcuts", "target": "docs_features_shortcuts_event_commands", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/shortcuts.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_shortcuts", "target": "docs_features_shortcuts_default_bindings", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/shortcuts.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_shortcuts", "target": "docs_features_shortcuts_mod_key", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/shortcuts.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_shortcuts", "target": "docs_features_shortcuts_macos_menu", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/shortcuts.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_shortcuts", "target": "docs_features_shortcuts_global_listener", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/shortcuts.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_shortcuts_command_registry_pattern", "target": "src_domains_shortcuts_registry_commandregistry_commandregistry", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/shortcuts.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_shortcuts_event_commands", "target": "src_components_tabs_views_tabviewquery", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/shortcuts.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_shortcuts_event_commands", "target": "src_domains_shortcuts_models_commands_commands_event_execute", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/features/shortcuts.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_shortcuts_event_commands", "target": "docs_features_tabs_one_view_per_tab", "relation": "conceptually_related_to", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/features/shortcuts.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_shortcuts_default_bindings", "target": "src_domains_shortcuts_config_default_binding_defaultbindings", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/shortcuts.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_shortcuts_default_bindings", "target": "src_stores_tabsstore_usetabsstore", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/shortcuts.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_shortcuts_default_bindings", "target": "src_domains_shortcuts_models_commands_commands_tab_close", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/features/shortcuts.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_shortcuts_default_bindings", "target": "src_domains_shortcuts_models_commands_commands_connection_disconnect", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/features/shortcuts.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_shortcuts_mod_key", "target": "src_domains_shortcuts_utils_eventtokeystring_eventtokeystring", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/shortcuts.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_shortcuts_macos_menu", "target": "src_tauri_src_menu_macos_menu", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/shortcuts.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_shortcuts_macos_menu", "target": "src_tauri_src_lib", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/shortcuts.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_shortcuts_global_listener", "target": "src_plugins_shortcuts_shortcuts", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/shortcuts.md", "source_location": null, "weight": 1.0}], "hyperedges": []} \ No newline at end of file diff --git a/graphify-out/cache/semantic/pd5fd89c46bb5/9736468dea1cfeca7864986c884df29b37a64c8a2a5c01dd9f92eca2d9eaf7ea.json b/graphify-out/cache/semantic/pd5fd89c46bb5/9736468dea1cfeca7864986c884df29b37a64c8a2a5c01dd9f92eca2d9eaf7ea.json new file mode 100644 index 0000000..7310322 --- /dev/null +++ b/graphify-out/cache/semantic/pd5fd89c46bb5/9736468dea1cfeca7864986c884df29b37a64c8a2a5c01dd9f92eca2d9eaf7ea.json @@ -0,0 +1 @@ +{"nodes": [{"id": "docs_features_connection_manager", "label": "Connection manager feature (connection-manager.md)", "file_type": "document", "source_file": "docs/features/connection-manager.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "docs_features_connection_manager_auth_database_default", "label": "Authentication database empty by default: admin for a host, the string's authSource (else admin) for a connection string", "file_type": "rationale", "source_file": "docs/features/connection-manager.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "A filled admin overrode a connection string's authSource (review fix 8fe2ca0)."}, {"id": "docs_features_connection_manager_connection_model", "label": "Connection model v2 (id, name, connector MongoDb, target Host/Uri, authentication NONE/BASIC, options)", "file_type": "concept", "source_file": "docs/features/connection-manager.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "docs_features_connection_manager_connection_ui", "label": "Connection management UI (ConnectionGrid start screen, header Connections dropdown, sidebar edit, ConnectionDialog with host and string modes)", "file_type": "concept", "source_file": "docs/features/connection-manager.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "docs_features_connection_manager_connections_json", "label": "connections.json version 2 ({ version: 2, connections: { : ... } }; whole file read per call, written per change)", "file_type": "concept", "source_file": "docs/features/connection-manager.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "docs_features_connection_manager_delete_flow", "label": "Delete flow (useConfirm, disconnect and close tabs, delete_connection removes file entry, keyring entry and driver)", "file_type": "concept", "source_file": "docs/features/connection-manager.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "docs_features_connection_manager_form_validation", "label": "Form validation with TanStack Form and a zod schema (connectionForm.ts) on test or save (missing name or host, a host that is a connection string, port 1-65535, timeout 1-3600, scheme, credentials)", "file_type": "rationale", "source_file": "docs/features/connection-manager.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "The form validates instead of disabling buttons, so the user sees what is missing."}, {"id": "docs_features_connection_manager_host_options", "label": "Host-only options (tls, tlsCaFile, tlsAllowInvalidCertificates, replicaSet, serverSelectionTimeoutSecs; empty timeout is the driver's 30 s, 10 s for the connection test)", "file_type": "concept", "source_file": "docs/features/connection-manager.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "A connection string carries its own options; anything else (read preference, several hosts) goes into a connection string."}, {"id": "docs_features_connection_manager_rust_generated_id", "label": "Connection id (uuid v4) generated in Rust on create", "file_type": "concept", "source_file": "docs/features/connection-manager.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "docs_features_connection_manager_rust_normalize", "label": "Rust-side check before storing and before a test (models/normalize.rs, refuses host:port in the host field) moving credentials out of connection strings (models/uri.rs); refusal is RepositoryError::Invalid", "file_type": "rationale", "source_file": "docs/features/connection-manager.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "No password in connections.json, and the stored string can be shown; the test normalizes like saving (review fix 8fe2ca0)."}, {"id": "docs_features_connection_manager_srv_untested", "label": "mongodb+srv:// resolves DNS when connecting; not tested against a real srv cluster", "file_type": "concept", "source_file": "docs/features/connection-manager.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "docs_features_connection_manager_v1_migration", "label": "Version 1 file migrated on first load (repositories/migrations.rs) and kept as connections.v1.json", "file_type": "rationale", "source_file": "docs/features/connection-manager.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "Another version, or no version, is an error; a v1 uri that is a connection string becomes a string target with its password moved to the keyring."}], "edges": [{"source": "docs_features_connection_manager_connection_model", "target": "docs_features_connection_manager_host_options", "relation": "conceptually_related_to", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/connection-manager.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connection_manager", "target": "docs_features_connection_manager_connection_model", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/connection-manager.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connection_manager", "target": "docs_features_connection_manager_connection_ui", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/connection-manager.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connection_manager", "target": "docs_features_connection_manager_connections_json", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/connection-manager.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connection_manager", "target": "docs_features_connection_manager_delete_flow", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/connection-manager.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connection_manager", "target": "docs_features_connection_manager_form_validation", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/connection-manager.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connection_manager", "target": "docs_features_connection_manager_host_options", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/connection-manager.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connection_manager", "target": "docs_features_connection_manager_rust_generated_id", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/connection-manager.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connection_manager", "target": "docs_features_connection_manager_rust_normalize", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/connection-manager.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connection_manager", "target": "docs_features_connection_manager_srv_untested", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/connection-manager.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connection_manager", "target": "docs_features_connection_manager_v1_migration", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/connection-manager.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connection_manager", "target": "docs_features_connection_test", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/connection-manager.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connection_manager", "target": "docs_features_credential_storage", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/connection-manager.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connection_manager", "target": "src_domains_connections_services_connectionserviceipc_connectionserviceipc", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/connection-manager.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connection_manager", "target": "src_tauri_src_connector_commands_update_connection_update_connection", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/connection-manager.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connection_manager_connection_model", "target": "src_tauri_src_connector_models_connection_connection", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/connection-manager.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connection_manager_connection_model", "target": "src_tauri_src_connector_models_connection_connectorkind", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/connection-manager.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connection_manager_connection_ui", "target": "src_components_connection_connectiondialog", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/connection-manager.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connection_manager_connection_ui", "target": "src_components_connection_connectiongrid", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/connection-manager.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connection_manager_connection_ui", "target": "src_components_connection_connectionselector", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/connection-manager.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connection_manager_connections_json", "target": "src_tauri_src_connector_repositories_file_repository_filerepository", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/connection-manager.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connection_manager_delete_flow", "target": "src_composables_useconfirm_useconfirm", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/connection-manager.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connection_manager_delete_flow", "target": "src_tauri_src_connector_commands_delete_connection_delete_connection", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/connection-manager.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connection_manager_form_validation", "target": "src_components_connection_connectionform_connectionformschema", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/connection-manager.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connection_manager_host_options", "target": "src_tauri_src_connector_models_options_connectionoptions", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/connection-manager.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connection_manager_rust_generated_id", "target": "src_tauri_src_connector_commands_create_connection_create_connection", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/connection-manager.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connection_manager_rust_normalize", "target": "src_tauri_src_connector_commands_error_repositoryerror", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/connection-manager.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connection_manager_rust_normalize", "target": "src_tauri_src_connector_models_normalize_normalize", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/connection-manager.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connection_manager_v1_migration", "target": "src_tauri_src_connector_repositories_migrations_migrate_v1", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/connection-manager.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connection_manager_delete_flow", "target": "docs_features_tabs_close_tabs_of_connection", "relation": "conceptually_related_to", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/features/connection-manager.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connection_manager_srv_untested", "target": "docs_architecture_overview_build_status", "relation": "conceptually_related_to", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/features/connection-manager.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connection_manager_connection_model", "target": "src_domains_connections_models_connection_connection", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/features/connection-manager.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connection_manager_connection_ui", "target": "src_components_appsidebar", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/features/connection-manager.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connection_manager_connections_json", "target": "src_tauri_src_connector_repositories_storage_wrapper_connectionstorage", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/features/connection-manager.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connection_manager_rust_normalize", "target": "src_tauri_src_connector_models_uri_split_uri_credentials", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/features/connection-manager.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connection_manager_v1_migration", "target": "src_tauri_src_connector_repositories_file_repository_filerepository_load_storage", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/features/connection-manager.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connection_manager_v1_migration", "target": "src_tauri_src_connector_repositories_file_repository_filerepository_migrate_from_v1", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/features/connection-manager.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connection_manager", "target": "docs_features_credential_storage_auth_mechanisms_limit", "relation": "conceptually_related_to", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/features/connection-manager.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connection_manager", "target": "docs_readme_connection_issues", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/features/connection-manager.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connection_manager_connection_model", "target": "src_tauri_src_connector_models_connection_connectiontarget", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/features/connection-manager.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connection_manager", "target": "docs_features_connection_manager_auth_database_default", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/connection-manager.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connection_manager_auth_database_default", "target": "src_tauri_src_drivers_client_options_default_auth_source", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/features/connection-manager.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connection_manager_auth_database_default", "target": "src_tauri_src_drivers_client_options_the_form_authentication_database_wins_over_the_string", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/features/connection-manager.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connection_manager_auth_database_default", "target": "src_components_connection_connectionform_defaultformvalues", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.75, "source_file": "docs/features/connection-manager.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connection_manager_connection_model", "target": "docs_features_connection_manager_auth_database_default", "relation": "conceptually_related_to", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/connection-manager.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connection_manager_rust_normalize", "target": "src_tauri_src_connector_models_normalize_refuses_a_port_after_the_host", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/features/connection-manager.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connection_manager_rust_normalize", "target": "src_tauri_src_connector_models_normalize_split_host_port", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/features/connection-manager.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connection_manager_rust_normalize", "target": "src_tauri_src_database_commands_start_test_connection_credentials_in_a_connection_string_are_used_like_on_save", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/features/connection-manager.md", "source_location": null, "weight": 1.0}], "hyperedges": []} \ No newline at end of file diff --git a/graphify-out/cache/semantic/pd5fd89c46bb5/9a80f8467f9a41203072e09544174b8af985cd43420d4c11b66b1f0345011a8b.json b/graphify-out/cache/semantic/pd5fd89c46bb5/9a80f8467f9a41203072e09544174b8af985cd43420d4c11b66b1f0345011a8b.json new file mode 100644 index 0000000..eb0562a --- /dev/null +++ b/graphify-out/cache/semantic/pd5fd89c46bb5/9a80f8467f9a41203072e09544174b8af985cd43420d4c11b66b1f0345011a8b.json @@ -0,0 +1 @@ +{"nodes": [{"id": "claude", "label": "CLAUDE.md (working guide)", "file_type": "document", "source_file": "CLAUDE.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "claude_browser_preview_ipc_stub", "label": "UI-only browser preview needs a window.__TAURI_INTERNALS__ stand-in answering IPC with test data", "file_type": "concept", "source_file": "CLAUDE.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "claude_clippy_baseline", "label": "cargo clippy baseline: 2 module_inception warnings (connection_handler, task_manager)", "file_type": "concept", "source_file": "CLAUDE.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "claude_coding_conventions", "label": "Coding conventions (match file indentation, @/ alias, thiserror, log macros, spawn_blocking for keyring calls, short why-comments)", "file_type": "rationale", "source_file": "CLAUDE.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "claude_driver_method_recipe", "label": "Adding database work: DatabaseDriver method -> MongoDbOfficialDriver -> task command (follow start_connect.rs)", "file_type": "rationale", "source_file": "CLAUDE.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "claude_forms_rule", "label": "Forms on TanStack Form with a zod 4 schema (Standard Schema, no adapter) and shadcn-vue Field components", "file_type": "rationale", "source_file": "CLAUDE.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "claude_git_workflow", "label": "Git workflow (feature branches off develop, /code-review before push, lowercase conventional commits, no CI: run tests, build and clippy locally)", "file_type": "rationale", "source_file": "CLAUDE.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "claude_graphify_rules", "label": "graphify query-first rules (query/path/explain before grep; graphify update after code changes)", "file_type": "rationale", "source_file": "CLAUDE.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "claude_ipc_service_rule", "label": "Components never call invoke: IPC goes through ConnectionServiceIPC (Result via tryCatch for CRUD, abortableInvoke for slow commands)", "file_type": "rationale", "source_file": "CLAUDE.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "claude_keep_in_sync_rules", "label": "Keep-in-sync rules (feature docs, command table, generate_handler!, ConnectionServiceIPC, TS models, STORAGE_VERSION, plans, README, graphify cache)", "file_type": "rationale", "source_file": "CLAUDE.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "Docs, IPC wrappers and TypeScript models drift from the code unless every change updates them together."}, {"id": "claude_logging_rule", "label": "Log with useLogger, never console.*; never log credentials, not even at trace", "file_type": "rationale", "source_file": "CLAUDE.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "claude_model_mirroring", "label": "TypeScript models mirror Rust structs (camelCase fields, enum values = Rust variant names)", "file_type": "rationale", "source_file": "CLAUDE.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "serde rename_all camelCase makes both sides one wire format, so both sides change together."}, {"id": "claude_no_gh_cli", "label": "gh is not installed: read issues and pull requests of the public repo through the GitHub REST API", "file_type": "concept", "source_file": "CLAUDE.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "claude_pnpm_pinned", "label": "pnpm 12.17.0 pinned (packageManager, devEngines); pnpm exec instead of npx; change only with pnpm self-update", "file_type": "rationale", "source_file": "CLAUDE.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "npm refuses to run because devEngines names pnpm; pnpm records its version in the lockfile, so a hand edit breaks --frozen-lockfile."}, {"id": "claude_shortcut_recipe", "label": "Adding a shortcut: command id in models/commands.ts, register in config/commands.ts, bind in config/default-binding.ts", "file_type": "rationale", "source_file": "CLAUDE.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "claude_tanstack_table_registered_features", "label": "Gotcha: TanStack Table 9 only exposes the APIs of registered features (documentTableFeatures)", "file_type": "concept", "source_file": "CLAUDE.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "claude_testing_policy", "label": "Unit tests for every piece of logic (regular and edge cases, failing collaborators)", "file_type": "rationale", "source_file": "CLAUDE.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "Logic is pulled out of components into composables or utils so it can be tested without mounting; MongoDB and the keyring sit behind traits where tests must replace them."}, {"id": "claude_typescript_6_pin", "label": "TypeScript stays on 6.x (vue-tsc cannot run on TypeScript 7; tsconfig uses paths without baseUrl)", "file_type": "concept", "source_file": "CLAUDE.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "TypeScript 7 no longer ships lib/tsc, which vue-tsc needs; TypeScript 6 deprecates baseUrl."}, {"id": "claude_untested_hotspots", "label": "Untested code worth covering when touched (formatCellValue, useColumnDragDrop, extractErrorMessage, abortableInvoke, TaskManager)", "file_type": "concept", "source_file": "CLAUDE.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "claude_vue_tsc_attribute_gotcha", "label": "Gotcha: vue-tsc 3 breaks on a // inside a static attribute value (e.g. a mongodb+srv:// placeholder)", "file_type": "concept", "source_file": "CLAUDE.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}], "edges": [{"source": "claude", "target": "claude_browser_preview_ipc_stub", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude", "target": "claude_clippy_baseline", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude", "target": "claude_coding_conventions", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude", "target": "claude_driver_method_recipe", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude", "target": "claude_forms_rule", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude", "target": "claude_git_workflow", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude", "target": "claude_graphify_rules", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude", "target": "claude_ipc_service_rule", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude", "target": "claude_keep_in_sync_rules", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude", "target": "claude_logging_rule", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude", "target": "claude_model_mirroring", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude", "target": "claude_pnpm_pinned", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude", "target": "claude_shortcut_recipe", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude", "target": "claude_tanstack_table_registered_features", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude", "target": "claude_testing_policy", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude", "target": "claude_typescript_6_pin", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude", "target": "claude_untested_hotspots", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude", "target": "docs_architecture_decisions", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude", "target": "docs_architecture_overview", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude", "target": "docs_readme", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude", "target": "readme", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude", "target": "src_tauri_src_database_models_find_document_findquery", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude", "target": "src_tauri_src_drivers_mongo_official_driver_test_stages", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude_clippy_baseline", "target": "src_tauri_src_connection_handler_connection_handler", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude_clippy_baseline", "target": "src_tauri_src_task_manager_task_manager", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude_driver_method_recipe", "target": "src_tauri_src_database_commands_start_connect_start_connect", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude_driver_method_recipe", "target": "src_tauri_src_drivers_driver_databasedriver", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude_driver_method_recipe", "target": "src_tauri_src_drivers_mongo_official_driver_mongodbofficialdriver", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude_forms_rule", "target": "src_components_connection_connectiondialog", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude_forms_rule", "target": "src_components_connection_connectionform", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude_ipc_service_rule", "target": "src_composables_useabortablecommand_abortableinvoke", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude_ipc_service_rule", "target": "src_domains_connections_services_connectionserviceipc_connectionserviceipc", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude_ipc_service_rule", "target": "src_utils_result_trycatch", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude_keep_in_sync_rules", "target": "docs_architecture_overview_ipc_command_table", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude_keep_in_sync_rules", "target": "docs_readme_plans_folder", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude_keep_in_sync_rules", "target": "src_domains_connections_services_connectionserviceipc_connectionserviceipc", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude_keep_in_sync_rules", "target": "src_tauri_src_connector_repositories_storage_wrapper_storage_version", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude_keep_in_sync_rules", "target": "src_tauri_src_lib", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude_logging_rule", "target": "src_composables_uselogger_uselogger", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude_pnpm_pinned", "target": "package_devengines", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude_pnpm_pinned", "target": "package_packagemanager", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude_pnpm_pinned", "target": "pnpm_workspace", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude_shortcut_recipe", "target": "src_domains_shortcuts_config_commands_initcommands", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude_shortcut_recipe", "target": "src_domains_shortcuts_config_default_binding_defaultbindings", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude_shortcut_recipe", "target": "src_domains_shortcuts_models_commands_commands", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude_tanstack_table_registered_features", "target": "src_composables_table_usedocumenttable_documenttablefeatures", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude_testing_policy", "target": "src_tauri_src_connector_repositories_credentials_credential_store_memorycredentialstore", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude_testing_policy", "target": "src_test_setup", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude_untested_hotspots", "target": "src_composables_table_usecolumndragdrop_usecolumndragdrop", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude_untested_hotspots", "target": "src_composables_useabortablecommand_abortableinvoke", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude_untested_hotspots", "target": "src_tauri_src_task_manager_task_manager_taskmanager", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude_untested_hotspots", "target": "src_utils_errormessage_extracterrormessage", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude_untested_hotspots", "target": "src_utils_formatcellvalues_formatcellvalue", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude_model_mirroring", "target": "src_domains_connections_models_connection_connection", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.75, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude_model_mirroring", "target": "src_tauri_src_connector_models_connection_connection", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.75, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude_driver_method_recipe", "target": "docs_features_background_tasks_task_pattern", "relation": "conceptually_related_to", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude_git_workflow", "target": "docs_features_app_shell_no_ci_release", "relation": "conceptually_related_to", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude_keep_in_sync_rules", "target": "readme_graphify", "relation": "conceptually_related_to", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude_logging_rule", "target": "docs_features_logging_no_credentials", "relation": "conceptually_related_to", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude_shortcut_recipe", "target": "docs_features_shortcuts_command_registry_pattern", "relation": "conceptually_related_to", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude", "target": "docs_architecture_decisions_confirm_dialog", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude", "target": "docs_architecture_overview_managed_state", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude", "target": "docs_architecture_overview_provider_registry", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude", "target": "docs_features_app_shell_dark_theme", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude", "target": "docs_features_connect_and_explorer_driver_pool", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude", "target": "docs_features_connect_and_explorer_sessions", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude", "target": "docs_features_connection_manager_v1_migration", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude", "target": "docs_features_credential_storage_credential_hydration", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude", "target": "docs_features_credential_storage_secret_service_required", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude", "target": "docs_features_error_handling_three_field_error_shape", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude", "target": "docs_features_error_handling_toasts", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude", "target": "docs_features_logging_log_level_by_build", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude", "target": "docs_features_query_tab_extended_json_format", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude", "target": "docs_features_tabs_one_view_per_tab", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude_keep_in_sync_rules", "target": "docs_readme_feature_file_template", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude_forms_rule", "target": "docs_architecture_decisions_tanstack_form_zod", "relation": "conceptually_related_to", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude_pnpm_pinned", "target": "docs_architecture_decisions_pnpm_pin", "relation": "conceptually_related_to", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude_typescript_6_pin", "target": "docs_architecture_decisions_latest_packages", "relation": "conceptually_related_to", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude", "target": "claude_vue_tsc_attribute_gotcha", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude", "target": "claude_no_gh_cli", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude", "target": "src_tauri_src_connection_handler_connection_handler_connectionhandler", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude", "target": "src_tauri_src_connection_handler_connection_handler_connectionhandler_with_factory", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude_vue_tsc_attribute_gotcha", "target": "claude_typescript_6_pin", "relation": "conceptually_related_to", "confidence": "INFERRED", "confidence_score": 0.75, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}, {"source": "claude_no_gh_cli", "target": "claude_git_workflow", "relation": "conceptually_related_to", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "CLAUDE.md", "source_location": null, "weight": 1.0}], "hyperedges": []} \ No newline at end of file diff --git a/graphify-out/cache/semantic/pd5fd89c46bb5/9ca4a4b1e8c77f5397649adb54fd9444fe0f685c8afec7bc9090db8b06933130.json b/graphify-out/cache/semantic/pd5fd89c46bb5/9ca4a4b1e8c77f5397649adb54fd9444fe0f685c8afec7bc9090db8b06933130.json new file mode 100644 index 0000000..0ad5488 --- /dev/null +++ b/graphify-out/cache/semantic/pd5fd89c46bb5/9ca4a4b1e8c77f5397649adb54fd9444fe0f685c8afec7bc9090db8b06933130.json @@ -0,0 +1 @@ +{"nodes": [{"id": "docs_architecture_decisions", "label": "Decision log (decisions.md)", "file_type": "document", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "docs_architecture_decisions_latest_packages", "label": "Decision: all packages to their latest release, except TypeScript 7 and Tauri 3", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "Old versions would only make later updates bigger; vue-tsc cannot run on TypeScript 7 (no lib/tsc); Tauri 3 is an alpha. Rejected: staying on old versions; TypeScript 7 without template type-checking."}, {"id": "docs_architecture_decisions_pnpm_pin", "label": "Decision: pnpm pinned to 12 in packageManager and devEngines; pnpm-workspace.yaml allows the esbuild and vue-demi build scripts", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "node_modules came from pnpm 10 while the machines run newer versions; same setup as smtp-relay. Rejected: no pin; pinning the installed 11.3."}, {"id": "docs_architecture_decisions_credential_store_trait_driver_factory", "label": "Decision: a CredentialStore trait and a driver factory in ConnectionHandler", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "Repository and pool can be tested without the keyring and without MongoDB; the commands no longer downcast the repository."}, {"id": "docs_architecture_decisions_driver_trait_factory", "label": "Decision: database access through a DatabaseDriver trait with a factory per ConnectorKind", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "Room for more drivers or a second MongoDB implementation."}, {"id": "docs_architecture_decisions_dependency_injection", "label": "Decision: dependency injection on both sides (Tauri managed state in Rust, provider registry with useDomain in Vue)", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "Services are created once and can be swapped behind an interface (ConnectionService, ConnectionRepository, LogStrategy)."}, {"id": "docs_architecture_decisions_tauri_vue_stack", "label": "Decision: Tauri 2 with a Rust core and a Vue 3 web view", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "README: high-performance backend with secure system-level access; modern, responsive, maintainable frontend; goal is a free European alternative to Studio 3T."}, {"id": "docs_architecture_decisions_commands_take_connection_id", "label": "Decision: commands that talk to MongoDB take a connection id; Rust loads the saved connection and its password", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "The UI copy can be stale after an edit, and the password no longer passes through the web view for these calls. Rejected: sending the connection object."}, {"id": "docs_architecture_decisions_driver_leaves_pool", "label": "Decision: a driver leaves the pool on disconnect, edit, delete and when its first command fails", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "A driver is created before the server is contacted, so a wrong password or an edited host stuck until restart. Rejected: keeping drivers for the life of the process."}, {"id": "docs_architecture_decisions_connection_model_v2", "label": "Decision: connection model version 2 (target + options for host targets; connectionType dropped; v1 migrated and kept as connections.v1.json)", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "Connection strings and options (issue #4) were out of reach with a host field and a hand-built URL; connectionType was never read. Rejected: keeping connectionType; options for both modes overriding the string."}, {"id": "docs_architecture_decisions_connection_string_credentials", "label": "Decision: a connection string keeps all its options; its credentials move out on save and go back only in memory before parsing", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "One place per setting and no password in connections.json; the driver checks authMechanism against the user info. Rejected: storing the string as typed."}, {"id": "docs_architecture_decisions_client_options_instead_of_url", "label": "Decision: the driver gets ClientOptions instead of a URL string", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "No hand-made percent-encoding, and srv strings work. Rejected: fixing the URL building."}, {"id": "docs_architecture_decisions_host_options", "label": "Decision: host options TLS, CA file, invalid certificates, replica set, server selection timeout", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "What issue #4 asked for, without client certificates. Rejected: more fields such as read preference; those go into a connection string."}, {"id": "docs_architecture_decisions_no_credentials_in_logs", "label": "Decision: no credentials in logs; the connection URI logged only in debug builds with the password masked", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "The URI carries the password."}, {"id": "docs_architecture_decisions_edit_dialog_shows_password", "label": "Decision: the edit dialog shows the stored password, fetched from the keyring only when it opens", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "Users wanted to see and fix the password they saved; partly reverses \"credentials never reach the frontend\" of PR #23. Rejected: keeping the password write-only."}, {"id": "docs_architecture_decisions_optional_connection_test", "label": "Decision: testing a connection is optional; a connection can be saved untested", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "Issue #26 gives no reason. Rejected: requiring a successful test before saving."}, {"id": "docs_architecture_decisions_credentials_stay_in_rust", "label": "Decision: credentials stay in Rust; get_connections and get_connection never return a password", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "Passwords should not be exposed to the web view, and the grid should not hit the keyring for every connection. Rejected: hydrating passwords when listing."}, {"id": "docs_architecture_decisions_versioned_storage", "label": "Decision: connections stored as a versioned JSON object keyed by id", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "O(1) access by id, and a version to migrate the file when its shape changes. Rejected: a plain array of connections."}, {"id": "docs_architecture_decisions_keyring_4", "label": "Decision: keyring 4 with its default v1 mode (Keychain, Credential Manager, Secret Service); reverses the keyutils cache", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "All packages were updated; keyring 4 moved its stores into separate crates and v1 keeps the API; keyring 3 passwords stay readable. Rejected: keyring-core with chosen store crates; staying on keyring 3.6."}, {"id": "docs_architecture_decisions_linux_keyring_backend", "label": "Decision (superseded): Linux keyring via linux-native-sync-persistent with vendored OpenSSL", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "keyutils for fast kernel-level access during a session, Secret Service for persistence; vendored bundles OpenSSL. Rejected: sync-secret-service alone."}, {"id": "docs_architecture_decisions_os_keyring", "label": "Decision: passwords stored in the OS keyring through the keyring crate", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "Uses the native store on Windows, macOS and Linux. Rejected: Tauri Stronghold (added 2026-02-15, reverted 2026-02-17)."}, {"id": "docs_architecture_decisions_url_percent_encoding", "label": "Decision (superseded): password set on the URL with Url::set_password (percent-encoded)", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "Passwords with special characters failed with \"password must be URL encoded\" (issue #21)."}, {"id": "docs_architecture_decisions_basic_auth_only", "label": "Decision: only basic authentication (username, password, authentication database) for the MVP", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "\"For MVP, Basic is sufficient\" (issue #3). Rejected: other mechanisms for now."}, {"id": "docs_architecture_decisions_connection_test_skips_serverstatus", "label": "Decision: the connection test skips serverStatus without the privilege, reads the version from buildInfo, marks failing stages failed, and its events carry a test id", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "A user who may only read one database failed the test although connecting worked; a failed stage stayed grey; two tests could not be told apart. Rejected: dropping the status stage."}, {"id": "docs_architecture_decisions_new_error_variants", "label": "Decision: CredentialsUnavailable, ConnectionNotFound and StorageFailed variants; a failed find carries the server message", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "A keyring failure read as a host problem; a broken query only said \"check your permissions\"."}, {"id": "docs_architecture_decisions_log_level_by_build", "label": "Decision: logs at Debug in development builds, Info in release builds", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "Release builds logged everything at debug."}, {"id": "docs_architecture_decisions_toasts", "label": "Decision: errors shown as vue-sonner toasts instead of an error box per component", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "Cleaner, less duplicated code and styling, fits the theme (issue #36). Rejected: the error boxes of before."}, {"id": "docs_architecture_decisions_background_tasks", "label": "Decision: testing, connecting and finding documents run as cancellable background tasks with an id", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "A slow connection froze the UI and there was no way to give up (issue #27). Rejected: a timeout only."}, {"id": "docs_architecture_decisions_streamed_test_stages", "label": "Decision: the connection test streams each stage as a Tauri event", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "Each stage shows when it really finishes, not all at once."}, {"id": "docs_architecture_decisions_user_message_errors", "label": "Decision: every error carries kind, message and user_message; the UI shows user_message", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "Raw driver errors were long and unreadable (issue #16). Rejected: showing the raw error."}, {"id": "docs_architecture_decisions_driver_per_connection", "label": "Decision: one driver per connection id kept for the life of the process (later revised)", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "Reuse the MongoDB client connection pool across commands."}, {"id": "docs_architecture_decisions_several_connections", "label": "Decision: several connections open at once; disconnecting closes the connection tabs; each tab belongs to its connection", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "Working with two servers is common; tabs used to survive a switch of connection and query the new server. Rejected: closing all tabs on switching; keeping tabs per connection while only one is open."}, {"id": "docs_architecture_decisions_one_view_per_tab", "label": "Decision: every tab has exactly one view keyed by its own id; mod+enter runs only in the active tab", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "Views multiplied and ran duplicate queries, and Ctrl+Enter ran everywhere (issue #42)."}, {"id": "docs_architecture_decisions_paging_sort_projection", "label": "Decision: paging, total count, sort and projection in the query tab; paging repeats the executed query", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "Issue #12 listed them; typing must not change what the next page means."}, {"id": "docs_architecture_decisions_estimated_count", "label": "Decision: an empty filter is counted with estimatedDocumentCount (marked as estimate), a filter exactly", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "countDocuments scans a big collection. Rejected: always counting exactly; no count."}, {"id": "docs_architecture_decisions_relaxed_extended_json", "label": "Decision: documents leave Rust as relaxed extended JSON, an Int64 beyond +/-2^53 as $numberLong", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "Readable dates in the JSON view and no silent change of large integers. Rejected: canonical extended JSON."}, {"id": "docs_architecture_decisions_parser_checks_shell_functions", "label": "Decision: the query parser checks every shell function and produces only shapes bson reads as types; regexes as $regularExpression", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "Invalid or date-only values silently matched nothing."}, {"id": "docs_architecture_decisions_virtualized_table", "label": "Decision: the table renders only visible rows (TanStack Virtual), caches formatted cells and sizes columns from a sample", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "Bigger result sets render fast (PR #44, issue #39)."}, {"id": "docs_architecture_decisions_table_default_view", "label": "Decision: documents shown in a table by default, JSON second; column order saved per connection, database and collection", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "The commit gives no reason for the default."}, {"id": "docs_architecture_decisions_codemirror_query_input", "label": "Decision: the query input is a single-line CodeMirror editor with MongoDB autocomplete", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "The history gives no reason."}, {"id": "docs_architecture_decisions_own_query_parser", "label": "Decision: shell-style queries parsed by an own parser (src/utils/queryParser) into extended JSON, which bson turns into BSON in Rust", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "It accepts unquoted keys, single quotes and shell functions like ObjectId(...) that JSON does not; the history gives no reason for not using a library."}, {"id": "docs_architecture_decisions_tabs_stay_mounted", "label": "Decision: open tabs stay mounted (v-show and KeepAlive) so a tab keeps its query and results", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "Switching tabs lost the state. Rejected: v-if."}, {"id": "docs_architecture_decisions_tanstack_form_zod", "label": "Decision: forms on TanStack Form with zod 4 and shadcn-vue Field", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "@vee-validate/zod only supports zod 3; vee-validate 5 has been a beta since 2025; shadcn-vue documents TanStack Form. Rejected: vee-validate 5 beta, an own adapter for vee-validate 4, keeping zod 3."}, {"id": "docs_architecture_decisions_confirm_dialog", "label": "Decision: a ConfirmDialog with useConfirm() instead of the browser confirm()", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "It fits the app and will be needed for deleting data."}, {"id": "docs_architecture_decisions_mod_key_shortcuts", "label": "Decision: shortcuts on mod (Cmd on macOS, Ctrl elsewhere); on macOS the default menu without \"Close Window\"", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "Mac users expect Cmd; Cmd+W would otherwise close the app. Not checked on a Mac."}, {"id": "docs_architecture_decisions_sidebar_node_per_connection", "label": "Decision: the sidebar has one node per open connection with refresh, edit and disconnect; the grid gives way once a connection is connected", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "Editing must work while connected; the view switches only on success (issue #27)."}, {"id": "docs_architecture_decisions_bundled_roboto", "label": "Decision: Roboto bundled with the app", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "The font of the built app looked wrong (issue #30). Rejected: the system font."}, {"id": "docs_architecture_decisions_window_size", "label": "Decision: the default window is 1200 x 900", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "The history gives no reason."}, {"id": "docs_architecture_decisions_testing_strategy", "label": "Decision: vitest in happy-dom with Tauri mockIPC; Rust unit tests with an in-memory credential store and temp dirs; integration tests against a real MongoDB marked #[ignore]", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "The rule \"every piece of logic gets unit tests\"; keyring and MongoDB cannot be in the default run. Rejected: mounting components in tests (logic moved into modules instead)."}, {"id": "docs_architecture_decisions_docs_and_working_rules", "label": "Decision: docs/ documents features, architecture, decisions and plans; CLAUDE.md holds the working rules", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "The project is being revived; its state had to be written down first, and work should follow the same rules as the other projects."}, {"id": "docs_architecture_decisions_graphify_knowledge_graph", "label": "Decision: Claude Code gets a graphify knowledge graph; only the semantic cache is committed, each clone builds the graph", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "Same setup as smtp-relay: the cache saves the model cost of reading the docs again, and graph.json is rewritten whole on every rebuild. Rejected: committing graph.json; ignoring graphify-out/ completely."}, {"id": "docs_architecture_decisions_branching_model", "label": "Decision: feature branches off develop merged through GitHub pull requests; develop goes to main from time to time", "file_type": "rationale", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "The history records no reason."}, {"id": "docs_architecture_decisions_smtp_relay", "label": "smtp-relay (sibling project used as template for docs, pnpm pin and graphify setup)", "file_type": "concept", "source_file": "docs/architecture/decisions.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}], "edges": [{"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_latest_packages", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_pnpm_pin", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_credential_store_trait_driver_factory", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_driver_trait_factory", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_dependency_injection", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_tauri_vue_stack", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_commands_take_connection_id", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_driver_leaves_pool", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_connection_model_v2", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_connection_string_credentials", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_client_options_instead_of_url", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_host_options", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_no_credentials_in_logs", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_edit_dialog_shows_password", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_optional_connection_test", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_credentials_stay_in_rust", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_versioned_storage", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_keyring_4", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_linux_keyring_backend", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_os_keyring", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_url_percent_encoding", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_basic_auth_only", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_connection_test_skips_serverstatus", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_new_error_variants", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_log_level_by_build", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_toasts", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_background_tasks", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_streamed_test_stages", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_user_message_errors", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_driver_per_connection", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_several_connections", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_one_view_per_tab", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_paging_sort_projection", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_estimated_count", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_relaxed_extended_json", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_parser_checks_shell_functions", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_virtualized_table", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_table_default_view", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_codemirror_query_input", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_own_query_parser", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_tabs_stay_mounted", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_tanstack_form_zod", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_confirm_dialog", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_mod_key_shortcuts", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_sidebar_node_per_connection", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_bundled_roboto", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_window_size", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_testing_strategy", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_docs_and_working_rules", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_graphify_knowledge_graph", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_branching_model", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions", "target": "docs_architecture_decisions_smtp_relay", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_pnpm_pin", "target": "pnpm_workspace", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_pnpm_pin", "target": "package_packagemanager", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_pnpm_pin", "target": "package_devengines", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_pnpm_pin", "target": "docs_architecture_decisions_smtp_relay", "relation": "conceptually_related_to", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_credential_store_trait_driver_factory", "target": "src_tauri_src_connector_repositories_credentials_credential_store_credentialstore", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_credential_store_trait_driver_factory", "target": "src_tauri_src_connection_handler_connection_handler_connectionhandler", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_credential_store_trait_driver_factory", "target": "src_tauri_src_connection_handler_connection_handler_connectionhandler_with_factory", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_credential_store_trait_driver_factory", "target": "docs_features_credential_storage_credential_store_trait", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_driver_trait_factory", "target": "src_tauri_src_drivers_driver_databasedriver", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_driver_trait_factory", "target": "src_tauri_src_connector_models_connection_connectorkind", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_driver_trait_factory", "target": "src_tauri_src_drivers_factory_databasedriverfactory", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_dependency_injection", "target": "docs_architecture_overview_provider_registry", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_dependency_injection", "target": "docs_architecture_overview_managed_state", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_dependency_injection", "target": "src_domains_index_usedomain", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_dependency_injection", "target": "src_domains_connections_services_connectionservice_connectionservice", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_dependency_injection", "target": "src_domains_logger_strategies_logstrategy_logstrategy", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_tauri_vue_stack", "target": "readme_tech_stack", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_tauri_vue_stack", "target": "readme_studio_3t", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_tauri_vue_stack", "target": "docs_architecture_overview_rust_core", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_tauri_vue_stack", "target": "docs_architecture_overview_web_view", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_commands_take_connection_id", "target": "docs_features_credential_storage_credential_hydration", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_commands_take_connection_id", "target": "src_tauri_src_connection_handler_connection_handler_connectionhandler_get_or_connect", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_driver_leaves_pool", "target": "docs_features_connect_and_explorer_driver_pool", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_driver_leaves_pool", "target": "src_tauri_src_connection_handler_connection_handler_connectionhandler_disconnect", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_driver_leaves_pool", "target": "src_tauri_src_connection_handler_connection_handler_connectionhandler_open", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_driver_leaves_pool", "target": "docs_architecture_decisions_driver_per_connection", "relation": "conceptually_related_to", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_connection_model_v2", "target": "docs_features_connection_manager_connection_model", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_connection_model_v2", "target": "docs_features_connection_manager_v1_migration", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_connection_model_v2", "target": "src_tauri_src_connector_models_connection_connectiontarget", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_connection_model_v2", "target": "src_tauri_src_connector_repositories_storage_wrapper_storage_version", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_connection_model_v2", "target": "src_tauri_src_connector_repositories_migrations_migrate_v1", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_connection_model_v2", "target": "docs_architecture_decisions_versioned_storage", "relation": "conceptually_related_to", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_connection_string_credentials", "target": "docs_features_connection_manager_rust_normalize", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_connection_string_credentials", "target": "docs_features_credential_storage_user_info_reinsertion", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_connection_string_credentials", "target": "src_tauri_src_connector_models_uri_split_uri_credentials", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_connection_string_credentials", "target": "src_tauri_src_drivers_client_options_with_user_info", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_client_options_instead_of_url", "target": "src_tauri_src_drivers_client_options_client_options", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_client_options_instead_of_url", "target": "docs_architecture_decisions_url_percent_encoding", "relation": "conceptually_related_to", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_host_options", "target": "docs_features_connection_manager_host_options", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_host_options", "target": "src_tauri_src_connector_models_options_connectionoptions", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_no_credentials_in_logs", "target": "docs_features_logging_no_credentials", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_edit_dialog_shows_password", "target": "src_tauri_src_connector_commands_get_connection_password_get_connection_password", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_edit_dialog_shows_password", "target": "docs_architecture_decisions_credentials_stay_in_rust", "relation": "conceptually_related_to", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_edit_dialog_shows_password", "target": "docs_features_credential_storage_password_to_webview", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_optional_connection_test", "target": "docs_features_connection_test", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_credentials_stay_in_rust", "target": "src_tauri_src_connector_commands_get_connections_get_connections", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_credentials_stay_in_rust", "target": "docs_features_credential_storage_credential_hydration", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_versioned_storage", "target": "docs_features_connection_manager_connections_json", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_versioned_storage", "target": "src_tauri_src_connector_repositories_storage_wrapper_connectionstorage", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_keyring_4", "target": "docs_architecture_decisions_linux_keyring_backend", "relation": "conceptually_related_to", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_keyring_4", "target": "docs_features_credential_storage_keyring_crate", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_keyring_4", "target": "src_tauri_src_connector_repositories_credentials_credential_service_credentialservice", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_linux_keyring_backend", "target": "docs_features_credential_storage_linux_backend", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_os_keyring", "target": "docs_features_credential_storage_tauri_stronghold", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_os_keyring", "target": "docs_features_credential_storage_keyring_crate", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_os_keyring", "target": "src_tauri_src_connector_repositories_credentials_credential_service_credentialservice", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_basic_auth_only", "target": "docs_features_credential_storage_auth_mechanisms_limit", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_connection_test_skips_serverstatus", "target": "docs_features_connection_test_serverstatus_skip", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_connection_test_skips_serverstatus", "target": "docs_features_connection_test_progress_event", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_connection_test_skips_serverstatus", "target": "src_tauri_src_drivers_mongo_official_driver_is_unauthorized", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.75, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_new_error_variants", "target": "src_tauri_src_drivers_errors_drivererror_credentialsunavailable", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_new_error_variants", "target": "src_tauri_src_drivers_errors_drivererror_connectionnotfound", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_new_error_variants", "target": "src_tauri_src_drivers_errors_drivererror_storagefailed", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_new_error_variants", "target": "src_tauri_src_drivers_errors_drivererror_finddocumentsfailed", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_new_error_variants", "target": "docs_features_error_handling_server_message_on_find", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_log_level_by_build", "target": "docs_features_logging_log_level_by_build", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_log_level_by_build", "target": "src_tauri_src_lib", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_toasts", "target": "docs_features_error_handling_toasts", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_background_tasks", "target": "docs_features_background_tasks_task_pattern", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_background_tasks", "target": "src_tauri_src_task_manager_commands_await_task_result", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_background_tasks", "target": "src_tauri_src_task_manager_commands_cancel_task", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_streamed_test_stages", "target": "docs_features_connection_test_progress_event", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_user_message_errors", "target": "docs_features_error_handling_three_field_error_shape", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_driver_per_connection", "target": "docs_features_connect_and_explorer_driver_pool", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_driver_per_connection", "target": "src_tauri_src_connection_handler_connection_handler_connectionhandler", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_several_connections", "target": "docs_features_connect_and_explorer_sessions", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_several_connections", "target": "docs_features_tabs_close_tabs_of_connection", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_one_view_per_tab", "target": "docs_features_tabs_one_view_per_tab", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_one_view_per_tab", "target": "src_domains_shortcuts_models_commands_commands_event_execute", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_paging_sort_projection", "target": "docs_features_query_tab_paging", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_estimated_count", "target": "docs_features_query_tab_find_and_count", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_relaxed_extended_json", "target": "docs_features_document_views_relaxed_extended_json", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_relaxed_extended_json", "target": "src_tauri_src_drivers_documents_document_to_json", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_parser_checks_shell_functions", "target": "docs_features_query_tab_shell_function_mapping", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_parser_checks_shell_functions", "target": "docs_features_query_tab_bson_regex", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_parser_checks_shell_functions", "target": "src_utils_queryparser_transformfunction_transformfunction", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_virtualized_table", "target": "docs_features_document_views_table_view", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_table_default_view", "target": "docs_features_document_views_column_order_persistence", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_codemirror_query_input", "target": "docs_features_query_tab_mongo_autocomplete", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_codemirror_query_input", "target": "src_components_query_queryinput", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_own_query_parser", "target": "docs_features_query_tab_shell_query_parser", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_own_query_parser", "target": "src_utils_queryparser_parser_createparser", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_tabs_stay_mounted", "target": "docs_features_tabs_kept_alive_views", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_tanstack_form_zod", "target": "docs_features_connection_manager_form_validation", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_tanstack_form_zod", "target": "src_components_connection_connectionform_connectionformschema", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.75, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_confirm_dialog", "target": "src_components_confirmdialog", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_confirm_dialog", "target": "src_composables_useconfirm_useconfirm", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_confirm_dialog", "target": "docs_features_connection_manager_delete_flow", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_mod_key_shortcuts", "target": "docs_features_shortcuts_mod_key", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_mod_key_shortcuts", "target": "docs_features_shortcuts_macos_menu", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_sidebar_node_per_connection", "target": "docs_features_connect_and_explorer_explorer_sidebar", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_sidebar_node_per_connection", "target": "src_components_appsidebar", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_bundled_roboto", "target": "docs_features_app_shell_bundled_roboto", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_window_size", "target": "docs_features_app_shell_window_config", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_testing_strategy", "target": "claude_testing_policy", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_testing_strategy", "target": "src_test_setup", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_testing_strategy", "target": "src_tauri_src_connector_repositories_credentials_credential_store_memorycredentialstore", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_docs_and_working_rules", "target": "claude", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_docs_and_working_rules", "target": "docs_readme", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_docs_and_working_rules", "target": "docs_architecture_decisions_smtp_relay", "relation": "conceptually_related_to", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_graphify_knowledge_graph", "target": "readme_graphify", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_graphify_knowledge_graph", "target": "claude_graphify_rules", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.75, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_graphify_knowledge_graph", "target": "docs_architecture_decisions_smtp_relay", "relation": "conceptually_related_to", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_decisions_branching_model", "target": "claude_git_workflow", "relation": "rationale_for", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/architecture/decisions.md", "source_location": null, "weight": 1.0}], "hyperedges": []} \ No newline at end of file diff --git a/graphify-out/cache/semantic/pd5fd89c46bb5/9fdbf7a2acd78e65f1b7572a2b9d6a7357376992063dd7d718a6fe4cc2fa72f2.json b/graphify-out/cache/semantic/pd5fd89c46bb5/9fdbf7a2acd78e65f1b7572a2b9d6a7357376992063dd7d718a6fe4cc2fa72f2.json new file mode 100644 index 0000000..47f7b7b --- /dev/null +++ b/graphify-out/cache/semantic/pd5fd89c46bb5/9fdbf7a2acd78e65f1b7572a2b9d6a7357376992063dd7d718a6fe4cc2fa72f2.json @@ -0,0 +1 @@ +{"nodes": [{"id": "docs_features_app_shell", "label": "App shell feature (app-shell.md)", "file_type": "document", "source_file": "docs/features/app-shell.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "docs_features_app_shell_window_config", "label": "Window config (title Monolens, 1200 x 900, identifier org.monolens, v0.1.0, all bundle targets)", "file_type": "concept", "source_file": "docs/features/app-shell.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "docs_features_app_shell_layout", "label": "App layout (sticky header with Connections dropdown; ConnectionGrid or AppSidebar + TabsContainer; toasts; one ConfirmDialog)", "file_type": "concept", "source_file": "docs/features/app-shell.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "docs_features_app_shell_dark_theme", "label": "Dark-only OKLCH theme (default.css on :root, green primary, success/warning/info; unused .dark block)", "file_type": "concept", "source_file": "docs/features/app-shell.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "docs_features_app_shell_shadcn_vue", "label": "shadcn-vue UI kit on reka-ui (new-york, neutral, @lucide/vue; generated by the CLI, then owned by the repo)", "file_type": "concept", "source_file": "docs/features/app-shell.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "The history gives no reason; the README names Vue 3 and Tailwind."}, {"id": "docs_features_app_shell_bundled_roboto", "label": "Bundled Roboto font (400/500/700 TTF, OFL)", "file_type": "concept", "source_file": "docs/features/app-shell.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "docs_features_app_shell_package_set", "label": "Package set 2026-11-03 (Tauri 2.12, Vue 3.5, Pinia 4, Vite 8, TypeScript 6, vitest 5, TanStack Table 9 / Form 1, zod 4, pnpm 12)", "file_type": "concept", "source_file": "docs/features/app-shell.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "docs_features_app_shell_no_csp", "label": "No Content Security Policy (csp: null)", "file_type": "concept", "source_file": "docs/features/app-shell.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "The web view only loads the bundled UI, but a CSP would limit the damage of injected content."}, {"id": "docs_features_app_shell_no_ci_release", "label": "No CI, release pipeline, code signing or auto-update", "file_type": "concept", "source_file": "docs/features/app-shell.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "docs_features_app_shell_unused_opener", "label": "tauri-plugin-opener registered but unused by the UI", "file_type": "concept", "source_file": "docs/features/app-shell.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "docs_features_app_shell_app_icons", "label": "App icons generated with pnpm tauri icon from monolens.png", "file_type": "concept", "source_file": "docs/features/app-shell.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}], "edges": [{"source": "docs_features_app_shell", "target": "docs_features_app_shell_window_config", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/app-shell.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_app_shell", "target": "docs_features_app_shell_layout", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/app-shell.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_app_shell", "target": "docs_features_app_shell_dark_theme", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/app-shell.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_app_shell", "target": "docs_features_app_shell_shadcn_vue", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/app-shell.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_app_shell", "target": "docs_features_app_shell_bundled_roboto", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/app-shell.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_app_shell", "target": "docs_features_app_shell_package_set", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/app-shell.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_app_shell", "target": "docs_features_app_shell_no_csp", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/app-shell.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_app_shell", "target": "docs_features_app_shell_no_ci_release", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/app-shell.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_app_shell", "target": "docs_features_app_shell_unused_opener", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/app-shell.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_app_shell", "target": "docs_features_app_shell_app_icons", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/app-shell.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_app_shell", "target": "docs_features_shortcuts", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/app-shell.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_app_shell", "target": "docs_features_error_handling", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/app-shell.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_app_shell", "target": "pnpm_workspace", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/app-shell.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_app_shell", "target": "docs_architecture_decisions_latest_packages", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/features/app-shell.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_app_shell", "target": "docs_architecture_decisions_pnpm_pin", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/features/app-shell.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_app_shell_window_config", "target": "src_tauri_tauri_conf_app_windows", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/app-shell.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_app_shell_window_config", "target": "src_tauri_tauri_conf_identifier", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/app-shell.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_app_shell_no_csp", "target": "src_tauri_tauri_conf_security_csp", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/app-shell.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_app_shell_layout", "target": "src_app", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/app-shell.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_app_shell_layout", "target": "src_components_header", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/app-shell.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_app_shell_layout", "target": "src_components_connection_connectiongrid", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/app-shell.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_app_shell_layout", "target": "src_components_appsidebar", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/app-shell.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_app_shell_layout", "target": "src_components_tabs_tabscontainer", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/app-shell.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_app_shell_layout", "target": "src_components_confirmdialog", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/app-shell.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_app_shell_package_set", "target": "claude_typescript_6_pin", "relation": "conceptually_related_to", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/features/app-shell.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_app_shell_unused_opener", "target": "src_tauri_src_lib", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.75, "source_file": "docs/features/app-shell.md", "source_location": null, "weight": 1.0}], "hyperedges": []} \ No newline at end of file diff --git a/graphify-out/cache/semantic/pd5fd89c46bb5/a205db9c4387ed609f1ce7f43ee1d49d7b3fcb80826f1eff13e2851ef48140dd.json b/graphify-out/cache/semantic/pd5fd89c46bb5/a205db9c4387ed609f1ce7f43ee1d49d7b3fcb80826f1eff13e2851ef48140dd.json new file mode 100644 index 0000000..71687ee --- /dev/null +++ b/graphify-out/cache/semantic/pd5fd89c46bb5/a205db9c4387ed609f1ce7f43ee1d49d7b3fcb80826f1eff13e2851ef48140dd.json @@ -0,0 +1 @@ +{"nodes": [{"id": "docs_features_connection_test", "label": "Connection test feature (connection-test.md)", "file_type": "document", "source_file": "docs/features/connection-test.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "docs_features_connection_test_progress_event", "label": "test-connection-progress Tauri event ({ testId, stageIndex, stage }) filtered by the store own test id", "file_type": "rationale", "source_file": "docs/features/connection-test.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "Each stage shows when it really finishes, and the test id lets a listener take only its own test."}, {"id": "docs_features_connection_test_running_time", "label": "Running time of the waiting stage in seconds, shown from 2 s on (useElapsedSeconds)", "file_type": "concept", "source_file": "docs/features/connection-test.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "docs_features_connection_test_server_only", "label": "The connection test checks the server, not the rights on a particular database", "file_type": "concept", "source_file": "docs/features/connection-test.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "docs_features_connection_test_serverstatus_skip", "label": "serverStatus stage skipped on unauthorized (code 13); version read from buildInfo, which needs no privilege", "file_type": "rationale", "source_file": "docs/features/connection-test.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "A user who may only read one database can connect and read, so the test must not fail for them."}, {"id": "docs_features_connection_test_stage_status", "label": "Stage states: pending, passed, failed, skipped (with an optional detail)", "file_type": "concept", "source_file": "docs/features/connection-test.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "docs_features_connection_test_staged_test", "label": "Staged connection test (Initialize, Ping Database, Reading status, Detecting version, Connected)", "file_type": "rationale", "source_file": "docs/features/connection-test.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "A failure shows where it stopped; the titles exist only in Rust (TEST_STAGES) and are reported as pending first."}, {"id": "docs_features_connection_test_stepper", "label": "TestStagesDisplay horizontal stepper (check, cross, dash for skipped, spinner and from 2 s the running seconds on the running stage, detail under the title)", "file_type": "concept", "source_file": "docs/features/connection-test.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "docs_features_connection_test_test_timeout", "label": "Connection test server selection timeout: 10 s unless the connection sets one (option field or serverSelectionTimeoutMS); connecting keeps 30 s", "file_type": "rationale", "source_file": "docs/features/connection-test.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "A wrong port sat at Ping Database for 30 s and looked like a hang; a connection's own timeout wins in both."}, {"id": "docs_features_connection_test_unpooled_driver", "label": "The test builds its own driver through the factory (create_for_test), not a pooled one, from form values incl. the typed password (id empty)", "file_type": "concept", "source_file": "docs/features/connection-test.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}], "edges": [{"source": "docs_features_connection_test_serverstatus_skip", "target": "docs_features_connection_test_stage_status", "relation": "conceptually_related_to", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/connection-test.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connection_test", "target": "docs_features_connection_test_progress_event", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/connection-test.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connection_test", "target": "docs_features_connection_test_serverstatus_skip", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/connection-test.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connection_test", "target": "docs_features_connection_test_stage_status", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/connection-test.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connection_test", "target": "docs_features_connection_test_staged_test", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/connection-test.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connection_test", "target": "docs_features_connection_test_stepper", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/connection-test.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connection_test", "target": "docs_features_connection_test_unpooled_driver", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/connection-test.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connection_test_progress_event", "target": "src_tauri_src_database_commands_start_test_connection_start_test_connection", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/connection-test.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connection_test_stage_status", "target": "src_tauri_src_drivers_driver_stagestatus", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/connection-test.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connection_test_stage_status", "target": "src_tauri_src_drivers_driver_teststage", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/connection-test.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connection_test_staged_test", "target": "src_components_connection_connectiondialog", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/connection-test.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connection_test_staged_test", "target": "src_stores_connectionstore_useconnectionstore", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/connection-test.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connection_test_staged_test", "target": "src_tauri_src_drivers_mongo_official_driver_mongodbofficialdriver_test_connection", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/connection-test.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connection_test_staged_test", "target": "src_tauri_src_drivers_mongo_official_driver_test_stages", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/connection-test.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connection_test_stepper", "target": "src_components_connection_teststagesdisplay", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/connection-test.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connection_test_serverstatus_skip", "target": "docs_features_error_handling_substring_classification", "relation": "semantically_similar_to", "confidence": "INFERRED", "confidence_score": 0.65, "source_file": "docs/features/connection-test.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connection_test_progress_event", "target": "docs_features_tabs_one_view_per_tab", "relation": "semantically_similar_to", "confidence": "INFERRED", "confidence_score": 0.75, "source_file": "docs/features/connection-test.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connection_test", "target": "docs_features_background_tasks_task_pattern", "relation": "conceptually_related_to", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/features/connection-test.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connection_test_unpooled_driver", "target": "docs_features_credential_storage_password_to_webview", "relation": "conceptually_related_to", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/features/connection-test.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connection_test_serverstatus_skip", "target": "src_tauri_src_drivers_mongo_official_driver_is_unauthorized", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/features/connection-test.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connection_test_unpooled_driver", "target": "src_tauri_src_drivers_factory_databasedriverfactory", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/features/connection-test.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connection_test", "target": "docs_features_connect_and_explorer_server_selection_timeout", "relation": "conceptually_related_to", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/features/connection-test.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connection_test_progress_event", "target": "src_stores_connectionstore_testprogresspayload", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/features/connection-test.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connection_test_progress_event", "target": "src_tauri_src_database_commands_start_test_connection_testprogressevent", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/features/connection-test.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connection_test", "target": "docs_features_connection_test_test_timeout", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/connection-test.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connection_test", "target": "docs_features_connection_test_running_time", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/connection-test.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connection_test", "target": "docs_features_connection_test_server_only", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/connection-test.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connection_test", "target": "src_stores_test_connectionstore_test", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/connection-test.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connection_test_test_timeout", "target": "src_tauri_src_drivers_mongo_official_driver_test_server_selection_timeout", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/connection-test.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connection_test_test_timeout", "target": "src_tauri_src_drivers_client_options_client_options", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/connection-test.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connection_test_test_timeout", "target": "src_tauri_src_drivers_client_options_the_fallback_timeout_applies_only_without_a_timeout_of_the_connection", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/features/connection-test.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connection_test_test_timeout", "target": "docs_features_connect_and_explorer_server_selection_timeout", "relation": "conceptually_related_to", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/features/connection-test.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connection_test_test_timeout", "target": "docs_features_connection_manager_host_options", "relation": "conceptually_related_to", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/features/connection-test.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connection_test_running_time", "target": "src_composables_useelapsedseconds_useelapsedseconds", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/connection-test.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connection_test_running_time", "target": "src_components_connection_teststagesdisplay_elapsedseconds", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/features/connection-test.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connection_test_stepper", "target": "docs_features_connection_test_running_time", "relation": "conceptually_related_to", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/connection-test.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connection_test_unpooled_driver", "target": "src_tauri_src_drivers_factory_databasedriverfactory_create_for_test", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/connection-test.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_connection_test_server_only", "target": "docs_features_connect_and_explorer_list_databases_privilege", "relation": "conceptually_related_to", "confidence": "INFERRED", "confidence_score": 0.65, "source_file": "docs/features/connection-test.md", "source_location": null, "weight": 1.0}], "hyperedges": [{"id": "connection_test_flow", "label": "Connection test flow from the dialog through the store, IPC, task and test driver to the stepper", "nodes": ["src_components_connection_connectiondialog", "src_stores_connectionstore_useconnectionstore", "src_domains_connections_services_connectionserviceipc_connectionserviceipc_testconnection", "src_tauri_src_database_commands_start_test_connection_start_test_connection", "src_tauri_src_drivers_factory_databasedriverfactory_create_for_test", "src_tauri_src_drivers_mongo_official_driver_mongodbofficialdriver_test_connection", "src_components_connection_teststagesdisplay"], "relation": "form", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/connection-test.md"}, {"id": "connection_test_timeout_and_running_time", "label": "The connection test's own 10 s server selection timeout and the running time of the waiting stage", "nodes": ["docs_features_connection_test_test_timeout", "src_tauri_src_drivers_mongo_official_driver_test_server_selection_timeout", "src_tauri_src_drivers_client_options_client_options", "docs_features_connection_test_running_time", "src_composables_useelapsedseconds_useelapsedseconds", "src_components_connection_teststagesdisplay"], "relation": "participate_in", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/features/connection-test.md"}]} \ No newline at end of file diff --git a/graphify-out/cache/semantic/pd5fd89c46bb5/a7489086076a0d4f5b3b7bee35c8173ed6dddc1b8d865b4822809d155cde0ce3.json b/graphify-out/cache/semantic/pd5fd89c46bb5/a7489086076a0d4f5b3b7bee35c8173ed6dddc1b8d865b4822809d155cde0ce3.json new file mode 100644 index 0000000..7a0ee3b --- /dev/null +++ b/graphify-out/cache/semantic/pd5fd89c46bb5/a7489086076a0d4f5b3b7bee35c8173ed6dddc1b8d865b4822809d155cde0ce3.json @@ -0,0 +1 @@ +{"nodes": [{"id": "docs_features_error_handling", "label": "Error handling feature (error-handling.md)", "file_type": "document", "source_file": "docs/features/error-handling.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "docs_features_error_handling_three_field_error_shape", "label": "Three-field IPC error shape { kind, message, user_message } (DriverError, CommandError, TaskError)", "file_type": "rationale", "source_file": "docs/features/error-handling.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "Driver errors are long and technical; the user gets a short sentence that says what to check, and the technical text stays for the code and logs (issue #16)."}, {"id": "docs_features_error_handling_extract_error_message", "label": "extractErrorMessage fallback order (user_message, message, Error.message, string, \"An unexpected error occurred.\")", "file_type": "concept", "source_file": "docs/features/error-handling.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "docs_features_error_handling_toasts", "label": "Error toasts (vue-sonner Toaster rich-colors, dark, top-center in App.vue)", "file_type": "concept", "source_file": "docs/features/error-handling.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "Toasts instead of error boxes (issue #36); the \"Show details\" toggle went away with them."}, {"id": "docs_features_error_handling_result_trycatch", "label": "Result and tryCatch for non-task IPC calls ({ data: null, error })", "file_type": "concept", "source_file": "docs/features/error-handling.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "docs_features_error_handling_connector_error_layering", "label": "RepositoryError and CredentialError inside the connector, surfaced through CommandError", "file_type": "concept", "source_file": "docs/features/error-handling.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "docs_features_error_handling_server_message_on_find", "label": "Query errors keep the server text (FindDocumentsFailed); InvalidQuery is already a user sentence", "file_type": "rationale", "source_file": "docs/features/error-handling.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "The server message names the mistake in the query."}, {"id": "docs_features_error_handling_substring_classification", "label": "Auth/timeout errors classified by substrings of the driver English message", "file_type": "concept", "source_file": "docs/features/error-handling.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "A changed driver text breaks the classification silently; unit tests in errors.rs cover today texts."}, {"id": "docs_features_error_handling_english_only", "label": "All messages are English (issue #31 i18n)", "file_type": "concept", "source_file": "docs/features/error-handling.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}], "edges": [{"source": "docs_features_error_handling", "target": "docs_features_error_handling_three_field_error_shape", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/error-handling.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_error_handling", "target": "docs_features_error_handling_extract_error_message", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/error-handling.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_error_handling", "target": "docs_features_error_handling_toasts", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/error-handling.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_error_handling", "target": "docs_features_error_handling_result_trycatch", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/error-handling.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_error_handling", "target": "docs_features_error_handling_connector_error_layering", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/error-handling.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_error_handling", "target": "docs_features_error_handling_server_message_on_find", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/error-handling.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_error_handling", "target": "docs_features_error_handling_substring_classification", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/error-handling.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_error_handling", "target": "docs_features_error_handling_english_only", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/error-handling.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_error_handling", "target": "docs_features_background_tasks_error_unwrapping", "relation": "conceptually_related_to", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/features/error-handling.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_error_handling_three_field_error_shape", "target": "src_tauri_src_drivers_errors_drivererror", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/error-handling.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_error_handling_three_field_error_shape", "target": "src_tauri_src_connector_commands_error_commanderror", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/error-handling.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_error_handling_three_field_error_shape", "target": "src_tauri_src_task_manager_task_error_taskerror", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/error-handling.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_error_handling_three_field_error_shape", "target": "src_tauri_src_drivers_errors_drivererror_user_message", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/features/error-handling.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_error_handling_extract_error_message", "target": "src_utils_errormessage_extracterrormessage", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/error-handling.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_error_handling_extract_error_message", "target": "docs_features_error_handling_three_field_error_shape", "relation": "conceptually_related_to", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/error-handling.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_error_handling_toasts", "target": "src_app", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/error-handling.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_error_handling_toasts", "target": "docs_features_error_handling_extract_error_message", "relation": "conceptually_related_to", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/error-handling.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_error_handling_result_trycatch", "target": "src_utils_result_trycatch", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/error-handling.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_error_handling_result_trycatch", "target": "src_utils_result_result", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/error-handling.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_error_handling_connector_error_layering", "target": "src_tauri_src_connector_commands_error_repositoryerror", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/error-handling.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_error_handling_connector_error_layering", "target": "src_tauri_src_connector_commands_error_credentialerror", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/error-handling.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_error_handling_server_message_on_find", "target": "src_tauri_src_drivers_errors_drivererror_finddocumentsfailed", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/error-handling.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_error_handling_server_message_on_find", "target": "src_tauri_src_drivers_errors_drivererror_invalidquery", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/error-handling.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_error_handling_substring_classification", "target": "src_tauri_src_drivers_errors_drivererror_is_auth_error", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/features/error-handling.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_error_handling_substring_classification", "target": "src_tauri_src_drivers_errors_drivererror_is_timeout_error", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/features/error-handling.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_error_handling_english_only", "target": "docs_readme_other_issues", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/features/error-handling.md", "source_location": null, "weight": 1.0}], "hyperedges": [{"id": "three_field_ipc_error_types", "label": "Errors serialized as { kind, message, user_message } across IPC", "nodes": ["src_tauri_src_drivers_errors_drivererror", "src_tauri_src_connector_commands_error_commanderror", "src_tauri_src_task_manager_task_error_taskerror", "src_utils_errormessage_extracterrormessage", "docs_features_error_handling_three_field_error_shape"], "relation": "implement", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/error-handling.md"}]} \ No newline at end of file diff --git a/graphify-out/cache/semantic/pd5fd89c46bb5/b52479d9660099bdd0dd165309517903a8b1b145ec8f1550f22be72075971c4c.json b/graphify-out/cache/semantic/pd5fd89c46bb5/b52479d9660099bdd0dd165309517903a8b1b145ec8f1550f22be72075971c4c.json new file mode 100644 index 0000000..17f7cd5 --- /dev/null +++ b/graphify-out/cache/semantic/pd5fd89c46bb5/b52479d9660099bdd0dd165309517903a8b1b145ec8f1550f22be72075971c4c.json @@ -0,0 +1 @@ +{"nodes": [{"id": "docs_architecture_overview", "label": "Architecture overview (overview.md)", "file_type": "document", "source_file": "docs/architecture/overview.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "docs_architecture_overview_web_view", "label": "Web view (Vue 3.5, Pinia 4, Tailwind 4, shadcn-vue/reka-ui, TanStack Form + zod 4, vue-sonner, @lucide/vue)", "file_type": "concept", "source_file": "docs/architecture/overview.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "docs_architecture_overview_rust_core", "label": "Rust core (Tauri 2.12, tokio): connector, database, task_manager, connection_handler, drivers", "file_type": "concept", "source_file": "docs/architecture/overview.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "docs_architecture_overview_ipc_command_table", "label": "Tauri IPC command table (12 commands from get_connections to await_task_result)", "file_type": "concept", "source_file": "docs/architecture/overview.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "docs_architecture_overview_managed_state", "label": "Tauri managed state: AppState (FileRepository + CredentialService), ConnectionHandler, TaskManager", "file_type": "concept", "source_file": "docs/architecture/overview.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "docs_architecture_overview_provider_registry", "label": "Provider registry ($providers., registerDomains, useDomain)", "file_type": "rationale", "source_file": "docs/architecture/overview.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "Plugins run outside components, so useDomain(Class, app) reads app.config.globalProperties instead of inject."}, {"id": "docs_architecture_overview_query_flow", "label": "How a query travels (sidebar click -> TabViewQuery -> parseQueryTexts -> findDocuments task -> pooled driver find + count -> table/JSON view)", "file_type": "concept", "source_file": "docs/architecture/overview.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "docs_architecture_overview_app_data", "label": "Data the app keeps (connections.json, OS keyring, localStorage column order, logs); sessions, tabs and results only in memory", "file_type": "concept", "source_file": "docs/architecture/overview.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "docs_architecture_overview_build_status", "label": "Build status 2026-11-03: 141 frontend tests, pnpm build passes, 81 Rust unit + 11 integration tests, 2 clippy warnings; tauri build, srv, TLS, macOS and Windows unchecked", "file_type": "concept", "source_file": "docs/architecture/overview.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "docs_architecture_overview_branches", "label": "Branches: develop current, main merged once (PR #35, 2026-02-22)", "file_type": "concept", "source_file": "docs/architecture/overview.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "docs_architecture_overview_mongodb_crate", "label": "mongodb crate 3.9 (bson 2.15)", "file_type": "concept", "source_file": "docs/architecture/overview.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "docs_architecture_overview_codemirror", "label": "CodeMirror 6 (lang-json, theme-one-dark, autocomplete)", "file_type": "concept", "source_file": "docs/architecture/overview.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "docs_architecture_overview_tanstack", "label": "TanStack Table 9 and TanStack Virtual 3", "file_type": "concept", "source_file": "docs/architecture/overview.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}], "edges": [{"source": "docs_architecture_overview", "target": "docs_architecture_overview_web_view", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/overview.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_overview", "target": "docs_architecture_overview_rust_core", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/overview.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_overview", "target": "docs_architecture_overview_ipc_command_table", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/overview.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_overview", "target": "docs_architecture_overview_managed_state", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/overview.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_overview", "target": "docs_architecture_overview_provider_registry", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/overview.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_overview", "target": "docs_architecture_overview_query_flow", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/overview.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_overview", "target": "docs_architecture_overview_app_data", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/overview.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_overview", "target": "docs_architecture_overview_build_status", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/overview.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_overview", "target": "docs_architecture_overview_branches", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/overview.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_overview", "target": "docs_architecture_overview_mongodb_crate", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/overview.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_overview", "target": "docs_architecture_overview_codemirror", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/overview.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_overview", "target": "docs_architecture_overview_tanstack", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/overview.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_overview", "target": "docs_features_background_tasks", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/overview.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_overview", "target": "docs_features_connect_and_explorer", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/overview.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_overview", "target": "docs_features_error_handling", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/overview.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_overview", "target": "docs_readme_working_state_round", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/overview.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_overview", "target": "src_utils_errormessage_extracterrormessage", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/overview.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_overview", "target": "src_tauri_src_task_manager_task_manager_taskmanager_spawn_task", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/overview.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_overview", "target": "docs_features_background_tasks_task_pattern", "relation": "conceptually_related_to", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/architecture/overview.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_overview_rust_core", "target": "src_tauri_src_lib", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/overview.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_overview_rust_core", "target": "src_tauri_src_menu_macos_menu", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/overview.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_overview_rust_core", "target": "src_tauri_src_drivers_driver_databasedriver", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/overview.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_overview_rust_core", "target": "src_tauri_src_drivers_factory_databasedriverfactory", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/overview.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_overview_rust_core", "target": "src_tauri_src_drivers_mongo_official_driver_mongodbofficialdriver", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/overview.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_overview_rust_core", "target": "src_tauri_src_connection_handler_connection_handler_connectionhandler_get_or_connect", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/overview.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_overview_managed_state", "target": "src_tauri_src_lib_appstate", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/overview.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_overview_managed_state", "target": "src_tauri_src_connection_handler_connection_handler_connectionhandler", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/overview.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_overview_managed_state", "target": "src_tauri_src_task_manager_task_manager_taskmanager", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/overview.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_overview_managed_state", "target": "src_tauri_src_connector_repositories_file_repository_filerepository", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/overview.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_overview_managed_state", "target": "src_tauri_src_connector_repositories_credentials_credential_service_credentialservice", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/overview.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_overview_ipc_command_table", "target": "src_tauri_src_connector_commands_get_connections_get_connections", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/overview.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_overview_ipc_command_table", "target": "src_tauri_src_connector_commands_create_connection_create_connection", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/overview.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_overview_ipc_command_table", "target": "src_tauri_src_connector_commands_update_connection_update_connection", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/overview.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_overview_ipc_command_table", "target": "src_tauri_src_connector_commands_delete_connection_delete_connection", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/overview.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_overview_ipc_command_table", "target": "src_tauri_src_connector_commands_get_connection_password_get_connection_password", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/overview.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_overview_ipc_command_table", "target": "src_tauri_src_database_commands_start_test_connection_start_test_connection", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/overview.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_overview_ipc_command_table", "target": "src_tauri_src_database_commands_start_connect_start_connect", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/overview.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_overview_ipc_command_table", "target": "src_tauri_src_database_commands_disconnect_disconnect", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/overview.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_overview_ipc_command_table", "target": "src_tauri_src_database_commands_list_collections_list_collections", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/overview.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_overview_ipc_command_table", "target": "src_tauri_src_database_commands_start_find_documents_start_find_documents", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/overview.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_overview_ipc_command_table", "target": "src_tauri_src_task_manager_commands_cancel_task", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/overview.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_overview_ipc_command_table", "target": "src_tauri_src_task_manager_commands_await_task_result", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/overview.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_overview_ipc_command_table", "target": "docs_features_connection_test_progress_event", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/overview.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_overview_provider_registry", "target": "src_domains_index_registerdomains", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/overview.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_overview_provider_registry", "target": "src_domains_index_usedomain", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/overview.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_overview_provider_registry", "target": "src_domains_shortcuts_registry_commandregistry_commandregistry", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/overview.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_overview_provider_registry", "target": "src_domains_connections_services_connectionserviceipc_connectionserviceipc", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/overview.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_overview_provider_registry", "target": "src_main", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/overview.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_overview_web_view", "target": "src_app", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/overview.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_overview_web_view", "target": "src_stores_connectionstore_useconnectionstore", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/overview.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_overview_web_view", "target": "src_stores_tabsstore_usetabsstore", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/overview.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_overview_web_view", "target": "src_components_connection_connectiongrid", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/overview.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_overview_web_view", "target": "src_components_appsidebar", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/overview.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_overview_web_view", "target": "src_components_tabs_tabscontainer", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/overview.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_overview_web_view", "target": "src_components_confirmdialog", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/overview.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_overview_query_flow", "target": "src_stores_tabsstore_usetabsstore", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/overview.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_overview_query_flow", "target": "src_components_tabs_views_tabviewquery", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/overview.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_overview_query_flow", "target": "src_components_query_querystate_parsequerytexts", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/overview.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_overview_query_flow", "target": "src_domains_connections_services_connectionserviceipc_connectionserviceipc_finddocuments", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/overview.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_overview_query_flow", "target": "src_tauri_src_database_commands_start_find_documents_start_find_documents", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/overview.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_overview_query_flow", "target": "src_tauri_src_task_manager_commands_await_task_result", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/overview.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_overview_query_flow", "target": "src_tauri_src_drivers_mongo_official_driver_mongodbofficialdriver_find_documents", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/architecture/overview.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_overview_query_flow", "target": "src_tauri_src_database_models_find_document_finddocumentsresult", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/architecture/overview.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_overview_app_data", "target": "src_tauri_src_connector_repositories_file_repository_filerepository", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/overview.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_overview_app_data", "target": "src_tauri_src_connector_repositories_credentials_credential_service_credentialservice", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/overview.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_overview_app_data", "target": "src_composables_table_usedocumenttable_usedocumenttable", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/overview.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_overview_app_data", "target": "docs_features_credential_storage_keyring_entry", "relation": "conceptually_related_to", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/architecture/overview.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_overview_app_data", "target": "docs_features_document_views_column_order_persistence", "relation": "conceptually_related_to", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/architecture/overview.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_overview_app_data", "target": "docs_features_connection_manager_connections_json", "relation": "conceptually_related_to", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/architecture/overview.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_overview_codemirror", "target": "src_components_query_queryinput", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/architecture/overview.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_overview_codemirror", "target": "src_components_documents_json_jsondocumentseditor", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/architecture/overview.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_overview_tanstack", "target": "src_components_documents_table_tabledocumentsview", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/architecture/overview.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_overview_tanstack", "target": "src_composables_table_usedocumenttable_usedocumenttable", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/architecture/overview.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_overview_build_status", "target": "docs_readme_working_state_round", "relation": "conceptually_related_to", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/overview.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_overview_build_status", "target": "claude_clippy_baseline", "relation": "conceptually_related_to", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/architecture/overview.md", "source_location": null, "weight": 1.0}, {"source": "docs_architecture_overview_branches", "target": "docs_architecture_decisions_branching_model", "relation": "conceptually_related_to", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/architecture/overview.md", "source_location": null, "weight": 1.0}], "hyperedges": [{"id": "query_execution_pipeline", "label": "How a query travels from the query tab to MongoDB and back", "nodes": ["src_components_tabs_views_tabviewquery", "src_components_query_querystate_parsequerytexts", "src_domains_connections_services_connectionserviceipc_connectionserviceipc_finddocuments", "src_tauri_src_database_commands_start_find_documents_start_find_documents", "src_tauri_src_connection_handler_connection_handler_connectionhandler_get_or_connect", "src_tauri_src_drivers_mongo_official_driver_mongodbofficialdriver_find_documents", "src_components_documents_table_tabledocumentsview", "src_components_documents_json_jsondocumentseditor"], "relation": "form", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/architecture/overview.md"}]} \ No newline at end of file diff --git a/graphify-out/cache/semantic/pd5fd89c46bb5/ba52a945f006df3d2f33b2103e9fe06bdd319bd0e5ba5796522fce31912fbb38.json b/graphify-out/cache/semantic/pd5fd89c46bb5/ba52a945f006df3d2f33b2103e9fe06bdd319bd0e5ba5796522fce31912fbb38.json new file mode 100644 index 0000000..b7ab7fd --- /dev/null +++ b/graphify-out/cache/semantic/pd5fd89c46bb5/ba52a945f006df3d2f33b2103e9fe06bdd319bd0e5ba5796522fce31912fbb38.json @@ -0,0 +1 @@ +{"nodes": [{"id": "docs_readme", "label": "Documentation index (docs/README.md)", "file_type": "document", "source_file": "docs/README.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "docs_readme_feature_file_template", "label": "Feature file template (Purpose, How it works, Decisions, Building steps, Limits and open points, Where the code is)", "file_type": "rationale", "source_file": "docs/README.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "Building steps come from the commit history; How it works describes the present, not the history; gaps in the history are stated, not filled."}, {"id": "docs_readme_plans_folder", "label": "docs/plans (plans as written before the work; implemented/ with status note; the feature file stays current)", "file_type": "rationale", "source_file": "docs/README.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "docs_readme_working_state_round", "label": "Working-state round of 2026-11-03 (plans/implemented/working-state.md): found bugs fixed, querying completed", "file_type": "concept", "source_file": "docs/README.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "docs_readme_unbuilt_features", "label": "Not built yet: writing data, indexes, aggregation, import, export, statistics", "file_type": "concept", "source_file": "docs/README.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "docs_readme_document_writing_issues", "label": "Writing-data issues (#5 CRUD, #6 add, #7 update, #9 delete, #43 command pattern with undo)", "file_type": "concept", "source_file": "docs/README.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "docs_readme_query_issues", "label": "Querying issues (#11 query builder, #41 drag-and-drop queries, #13 export query as code)", "file_type": "concept", "source_file": "docs/README.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "docs_readme_connection_issues", "label": "Connection issues (#40 quick connections, #45 stage types with production guard, #28 in-memory MongoDB)", "file_type": "concept", "source_file": "docs/README.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "docs_readme_display_issues", "label": "Display issue #39 table enhancements (nested objects, text size)", "file_type": "concept", "source_file": "docs/README.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "docs_readme_other_issues", "label": "Other open issues (#14 database statistics, #18 log file and rotation, #31 i18n)", "file_type": "concept", "source_file": "docs/README.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}], "edges": [{"source": "docs_readme", "target": "docs_readme_feature_file_template", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/README.md", "source_location": null, "weight": 1.0}, {"source": "docs_readme", "target": "docs_readme_plans_folder", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/README.md", "source_location": null, "weight": 1.0}, {"source": "docs_readme", "target": "docs_readme_working_state_round", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/README.md", "source_location": null, "weight": 1.0}, {"source": "docs_readme", "target": "docs_readme_unbuilt_features", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/README.md", "source_location": null, "weight": 1.0}, {"source": "docs_readme", "target": "docs_readme_document_writing_issues", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/README.md", "source_location": null, "weight": 1.0}, {"source": "docs_readme", "target": "docs_readme_query_issues", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/README.md", "source_location": null, "weight": 1.0}, {"source": "docs_readme", "target": "docs_readme_connection_issues", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/README.md", "source_location": null, "weight": 1.0}, {"source": "docs_readme", "target": "docs_readme_display_issues", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/README.md", "source_location": null, "weight": 1.0}, {"source": "docs_readme", "target": "docs_readme_other_issues", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/README.md", "source_location": null, "weight": 1.0}, {"source": "docs_readme", "target": "readme", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/README.md", "source_location": null, "weight": 1.0}, {"source": "docs_readme", "target": "docs_architecture_overview", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/README.md", "source_location": null, "weight": 1.0}, {"source": "docs_readme", "target": "docs_architecture_decisions", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/README.md", "source_location": null, "weight": 1.0}, {"source": "docs_readme", "target": "claude", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/README.md", "source_location": null, "weight": 1.0}, {"source": "docs_readme", "target": "docs_features_connection_manager", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/README.md", "source_location": null, "weight": 1.0}, {"source": "docs_readme", "target": "docs_features_credential_storage", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/README.md", "source_location": null, "weight": 1.0}, {"source": "docs_readme", "target": "docs_features_connection_test", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/README.md", "source_location": null, "weight": 1.0}, {"source": "docs_readme", "target": "docs_features_connect_and_explorer", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/README.md", "source_location": null, "weight": 1.0}, {"source": "docs_readme", "target": "docs_features_background_tasks", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/README.md", "source_location": null, "weight": 1.0}, {"source": "docs_readme", "target": "docs_features_error_handling", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/README.md", "source_location": null, "weight": 1.0}, {"source": "docs_readme", "target": "docs_features_tabs", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/README.md", "source_location": null, "weight": 1.0}, {"source": "docs_readme", "target": "docs_features_query_tab", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/README.md", "source_location": null, "weight": 1.0}, {"source": "docs_readme", "target": "docs_features_document_views", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/README.md", "source_location": null, "weight": 1.0}, {"source": "docs_readme", "target": "docs_features_shortcuts", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/README.md", "source_location": null, "weight": 1.0}, {"source": "docs_readme", "target": "docs_features_logging", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/README.md", "source_location": null, "weight": 1.0}, {"source": "docs_readme", "target": "docs_features_app_shell", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/README.md", "source_location": null, "weight": 1.0}, {"source": "docs_readme_unbuilt_features", "target": "docs_readme_document_writing_issues", "relation": "conceptually_related_to", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/README.md", "source_location": null, "weight": 1.0}, {"source": "docs_readme_unbuilt_features", "target": "docs_readme_query_issues", "relation": "conceptually_related_to", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/README.md", "source_location": null, "weight": 1.0}, {"source": "docs_readme_unbuilt_features", "target": "docs_readme_connection_issues", "relation": "conceptually_related_to", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/README.md", "source_location": null, "weight": 1.0}, {"source": "docs_readme_unbuilt_features", "target": "docs_readme_display_issues", "relation": "conceptually_related_to", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/README.md", "source_location": null, "weight": 1.0}, {"source": "docs_readme_unbuilt_features", "target": "docs_readme_other_issues", "relation": "conceptually_related_to", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/README.md", "source_location": null, "weight": 1.0}, {"source": "docs_readme_unbuilt_features", "target": "docs_features_tabs_placeholder_tab_kinds", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/README.md", "source_location": null, "weight": 1.0}, {"source": "docs_readme_unbuilt_features", "target": "src_domains_tabs_models_tab_kind_tabkind", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/README.md", "source_location": null, "weight": 1.0}], "hyperedges": []} \ No newline at end of file diff --git a/graphify-out/cache/semantic/pd5fd89c46bb5/bcd324b11d7af3aa1b66450b4ca51ace26394d08ba2b5dfac6910d41a32a6340.json b/graphify-out/cache/semantic/pd5fd89c46bb5/bcd324b11d7af3aa1b66450b4ca51ace26394d08ba2b5dfac6910d41a32a6340.json new file mode 100644 index 0000000..c159d40 --- /dev/null +++ b/graphify-out/cache/semantic/pd5fd89c46bb5/bcd324b11d7af3aa1b66450b4ca51ace26394d08ba2b5dfac6910d41a32a6340.json @@ -0,0 +1 @@ +{"nodes": [{"id": "docs_features_credential_storage", "label": "Credential storage feature (credential-storage.md)", "file_type": "document", "source_file": "docs/features/credential-storage.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "docs_features_credential_storage_credential_store_trait", "label": "CredentialStore trait (CredentialService = OS keyring; MemoryCredentialStore for tests, can be told to fail)", "file_type": "rationale", "source_file": "docs/features/credential-storage.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "The repository can be tested without the real keyring, and the commands no longer downcast the repository to reach it."}, {"id": "docs_features_credential_storage_keyring_crate", "label": "keyring 4 default v1 mode (Keychain, Credential Manager, Secret Service via zbus); calls in spawn_blocking", "file_type": "concept", "source_file": "docs/features/credential-storage.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "docs_features_credential_storage_keyring_entry", "label": "Keyring entry (service monolens-connections, user connection::basic_password)", "file_type": "concept", "source_file": "docs/features/credential-storage.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "docs_features_credential_storage_credential_hydration", "label": "Password hydrated right before use (ConnectorRepository::hydrate, called by ConnectionHandler when it builds a driver)", "file_type": "rationale", "source_file": "docs/features/credential-storage.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "Commands take a connection id, so the password never passes through the web view for them."}, {"id": "docs_features_credential_storage_password_writes", "label": "Password writes (create takes it out of the request; update writes for BASIC, deletes for NONE; delete ignores keyring errors)", "file_type": "concept", "source_file": "docs/features/credential-storage.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "docs_features_credential_storage_user_info_reinsertion", "label": "Connection-string user info put back percent-encoded, in memory, right before the driver parses it (client_options.rs)", "file_type": "rationale", "source_file": "docs/features/credential-storage.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "The driver checks some options against it (an authMechanism needs a username)."}, {"id": "docs_features_credential_storage_password_to_webview", "label": "Password reaches the web view only via the edit dialog and the typed test password", "file_type": "concept", "source_file": "docs/features/credential-storage.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "docs_features_credential_storage_credentials_unavailable", "label": "Unreadable keyring -> DriverError::CredentialsUnavailable (\"Edit the connection and enter it again\")", "file_type": "concept", "source_file": "docs/features/credential-storage.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "docs_features_credential_storage_tauri_stronghold", "label": "Tauri Stronghold (tried 2026-02-15, reverted 2026-02-17)", "file_type": "concept", "source_file": "docs/features/credential-storage.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "docs_features_credential_storage_linux_backend", "label": "Former Linux backend: keyutils + Secret Service with vendored OpenSSL (replaced by keyring 4)", "file_type": "concept", "source_file": "docs/features/credential-storage.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "docs_features_credential_storage_auth_mechanisms_limit", "label": "Only username/password (SCRAM); no X.509, LDAP, Kerberos, AWS or OIDC", "file_type": "concept", "source_file": "docs/features/credential-storage.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "docs_features_credential_storage_secret_service_required", "label": "Linux needs a Secret Service (GNOME Keyring, KWallet); behaviour without one untested", "file_type": "concept", "source_file": "docs/features/credential-storage.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}], "edges": [{"source": "docs_features_credential_storage", "target": "docs_features_credential_storage_credential_store_trait", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/credential-storage.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_credential_storage", "target": "docs_features_credential_storage_keyring_crate", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/credential-storage.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_credential_storage", "target": "docs_features_credential_storage_keyring_entry", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/credential-storage.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_credential_storage", "target": "docs_features_credential_storage_credential_hydration", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/credential-storage.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_credential_storage", "target": "docs_features_credential_storage_password_writes", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/credential-storage.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_credential_storage", "target": "docs_features_credential_storage_user_info_reinsertion", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/credential-storage.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_credential_storage", "target": "docs_features_credential_storage_password_to_webview", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/credential-storage.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_credential_storage", "target": "docs_features_credential_storage_credentials_unavailable", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/credential-storage.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_credential_storage", "target": "docs_features_credential_storage_tauri_stronghold", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/credential-storage.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_credential_storage", "target": "docs_features_credential_storage_linux_backend", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/credential-storage.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_credential_storage", "target": "docs_features_credential_storage_auth_mechanisms_limit", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/credential-storage.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_credential_storage", "target": "docs_features_credential_storage_secret_service_required", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/credential-storage.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_credential_storage", "target": "docs_features_logging_no_credentials", "relation": "conceptually_related_to", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/features/credential-storage.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_credential_storage_credential_store_trait", "target": "src_tauri_src_connector_repositories_credentials_credential_store_credentialstore", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/credential-storage.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_credential_storage_credential_store_trait", "target": "src_tauri_src_connector_repositories_credentials_credential_service_credentialservice", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/credential-storage.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_credential_storage_credential_store_trait", "target": "src_tauri_src_connector_repositories_credentials_credential_store_memorycredentialstore", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/credential-storage.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_credential_storage_credential_store_trait", "target": "src_tauri_src_connector_commands_error_credentialerror", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/credential-storage.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_credential_storage_keyring_crate", "target": "src_tauri_src_connector_repositories_credentials_credential_service_credentialservice", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/credential-storage.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_credential_storage_keyring_entry", "target": "src_tauri_src_connector_repositories_credentials_credential_service_credentialservice", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/credential-storage.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_credential_storage_credential_hydration", "target": "src_tauri_src_connector_repositories_connector_repository_connectorrepository_hydrate", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/credential-storage.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_credential_storage_credential_hydration", "target": "src_tauri_src_connection_handler_connection_handler_connectionhandler", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/credential-storage.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_credential_storage_credential_hydration", "target": "src_tauri_src_connector_commands_get_connections_get_connections", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/credential-storage.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_credential_storage_credential_hydration", "target": "src_tauri_src_connector_repositories_file_repository_filerepository_hydrate", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/features/credential-storage.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_credential_storage_password_writes", "target": "src_tauri_src_connector_commands_create_connection_create_connection", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/credential-storage.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_credential_storage_password_writes", "target": "src_tauri_src_connector_commands_update_connection_update_connection", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/credential-storage.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_credential_storage_password_writes", "target": "src_tauri_src_connector_commands_delete_connection_delete_connection", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/credential-storage.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_credential_storage_password_writes", "target": "docs_features_connection_manager_rust_normalize", "relation": "conceptually_related_to", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/features/credential-storage.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_credential_storage_user_info_reinsertion", "target": "src_tauri_src_drivers_client_options_with_user_info", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/features/credential-storage.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_credential_storage_user_info_reinsertion", "target": "src_tauri_src_drivers_client_options_credential", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/features/credential-storage.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_credential_storage_user_info_reinsertion", "target": "docs_features_connection_manager_rust_normalize", "relation": "conceptually_related_to", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/features/credential-storage.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_credential_storage_password_to_webview", "target": "src_tauri_src_connector_commands_get_connection_password_get_connection_password", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/credential-storage.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_credential_storage_password_to_webview", "target": "src_components_connection_connectiondialog", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/credential-storage.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_credential_storage_credentials_unavailable", "target": "src_tauri_src_drivers_errors_drivererror_credentialsunavailable", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/credential-storage.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_credential_storage_secret_service_required", "target": "docs_features_credential_storage_keyring_crate", "relation": "conceptually_related_to", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/credential-storage.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_credential_storage_linux_backend", "target": "docs_features_credential_storage_keyring_crate", "relation": "conceptually_related_to", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/credential-storage.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_credential_storage_credential_store_trait", "target": "docs_features_logging_log_strategy", "relation": "semantically_similar_to", "confidence": "INFERRED", "confidence_score": 0.75, "source_file": "docs/features/credential-storage.md", "source_location": null, "weight": 1.0}], "hyperedges": []} \ No newline at end of file diff --git a/graphify-out/cache/semantic/pd5fd89c46bb5/c309d83b87711a0d39c1c94ead5d9a64250121ed4f09a375a9451f68c3bcaa27.json b/graphify-out/cache/semantic/pd5fd89c46bb5/c309d83b87711a0d39c1c94ead5d9a64250121ed4f09a375a9451f68c3bcaa27.json new file mode 100644 index 0000000..bb21d48 --- /dev/null +++ b/graphify-out/cache/semantic/pd5fd89c46bb5/c309d83b87711a0d39c1c94ead5d9a64250121ed4f09a375a9451f68c3bcaa27.json @@ -0,0 +1 @@ +{"nodes": [{"id": "docs_features_logging", "label": "Logging feature (logging.md)", "file_type": "document", "source_file": "docs/features/logging.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "docs_features_logging_unified_log", "label": "Unified Rust and web view log via tauri-plugin-log (stdout + log dir; UI lines prefixed [WebView::])", "file_type": "rationale", "source_file": "docs/features/logging.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "A problem can be followed across the IPC boundary."}, {"id": "docs_features_logging_log_strategy", "label": "LogStrategy behind the Logger (TauriLogStrategy default forwards to @tauri-apps/plugin-log)", "file_type": "rationale", "source_file": "docs/features/logging.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "The target can change without touching the callers."}, {"id": "docs_features_logging_log_level_by_build", "label": "Log level Debug in development builds, Info in release builds (lib.rs)", "file_type": "concept", "source_file": "docs/features/logging.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "Less noise for users; Debug while developing."}, {"id": "docs_features_logging_no_credentials", "label": "No credentials in logs: no connection string, only hosts and settings; the dialog logs no form values", "file_type": "concept", "source_file": "docs/features/logging.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "docs_features_logging_no_rotation", "label": "No configured log rotation or size limits (issue #18)", "file_type": "concept", "source_file": "docs/features/logging.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}], "edges": [{"source": "docs_features_logging", "target": "docs_features_logging_unified_log", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/logging.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_logging", "target": "docs_features_logging_log_strategy", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/logging.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_logging", "target": "docs_features_logging_log_level_by_build", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/logging.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_logging", "target": "docs_features_logging_no_credentials", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/logging.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_logging", "target": "docs_features_logging_no_rotation", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/logging.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_logging_unified_log", "target": "src_tauri_src_lib", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/logging.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_logging_unified_log", "target": "src_domains_logger_strategies_taurilogstrategy_taurilogstrategy", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/logging.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_logging_log_strategy", "target": "src_domains_logger_logger_logger", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/logging.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_logging_log_strategy", "target": "src_domains_logger_strategies_logstrategy_logstrategy", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/logging.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_logging_log_strategy", "target": "src_domains_logger_strategies_taurilogstrategy_taurilogstrategy", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/logging.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_logging_log_strategy", "target": "src_composables_uselogger_uselogger", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/logging.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_logging_log_level_by_build", "target": "src_tauri_src_lib", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/logging.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_logging_no_credentials", "target": "src_tauri_src_drivers_mongo_official_driver_mongodbofficialdriver_connect", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "docs/features/logging.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_logging_no_credentials", "target": "src_components_connection_connectiondialog", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "docs/features/logging.md", "source_location": null, "weight": 1.0}, {"source": "docs_features_logging_no_rotation", "target": "docs_readme_other_issues", "relation": "references", "confidence": "INFERRED", "confidence_score": 0.95, "source_file": "docs/features/logging.md", "source_location": null, "weight": 1.0}], "hyperedges": []} \ No newline at end of file diff --git a/graphify-out/cache/semantic/pd5fd89c46bb5/cd39e5daf1933f3210a878aef1db1fc4a710260377e8c0a4e54107c29f8ad0d9.json b/graphify-out/cache/semantic/pd5fd89c46bb5/cd39e5daf1933f3210a878aef1db1fc4a710260377e8c0a4e54107c29f8ad0d9.json new file mode 100644 index 0000000..5197135 --- /dev/null +++ b/graphify-out/cache/semantic/pd5fd89c46bb5/cd39e5daf1933f3210a878aef1db1fc4a710260377e8c0a4e54107c29f8ad0d9.json @@ -0,0 +1 @@ +{"nodes": [{"id": "readme", "label": "README.md", "file_type": "document", "source_file": "README.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "readme_monolens", "label": "Monolens (free, open-source MongoDB GUI IDE)", "file_type": "concept", "source_file": "README.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "A secure and efficient European alternative to proprietary tools like Studio 3T."}, {"id": "readme_studio_3t", "label": "Studio 3T (proprietary MongoDB GUI)", "file_type": "concept", "source_file": "README.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "readme_apache_license", "label": "Apache-2.0 License", "file_type": "concept", "source_file": "README.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "readme_tech_stack", "label": "Tech stack: Rust + Tauri, Vue 3 + TailwindCSS", "file_type": "rationale", "source_file": "README.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "Rust and Tauri for a high-performance backend with secure system-level access; Vue 3 and Tailwind for a modern, responsive, maintainable frontend."}, {"id": "readme_feature_list", "label": "Current feature list (connections by host or string, keyring credentials, staged connection test, several connections, shell-syntax querying with paging, table and JSON views)", "file_type": "concept", "source_file": "README.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "readme_extensible_architecture", "label": "Extensible architecture (designed for editing data, aggregation, import/export)", "file_type": "concept", "source_file": "README.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "readme_dev_setup", "label": "Development setup (pnpm install, Rust, Tauri prerequisites, pnpm tauri dev)", "file_type": "concept", "source_file": "README.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}, {"id": "readme_graphify", "label": "graphify knowledge graph setup (pinned graphifyy 0.9.92, git hooks, committed semantic cache, .graphifyignore)", "file_type": "rationale", "source_file": "README.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null, "rationale": "Only graphify-out/cache/semantic/ is committed, so a build pays model cost only for changed docs; everyone uses the same version because an upgrade can invalidate the stored doc extraction."}], "edges": [{"source": "readme", "target": "readme_monolens", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "README.md", "source_location": null, "weight": 1.0}, {"source": "readme", "target": "readme_studio_3t", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "README.md", "source_location": null, "weight": 1.0}, {"source": "readme", "target": "readme_apache_license", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "README.md", "source_location": null, "weight": 1.0}, {"source": "readme", "target": "readme_tech_stack", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "README.md", "source_location": null, "weight": 1.0}, {"source": "readme", "target": "readme_feature_list", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "README.md", "source_location": null, "weight": 1.0}, {"source": "readme", "target": "readme_extensible_architecture", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "README.md", "source_location": null, "weight": 1.0}, {"source": "readme", "target": "readme_dev_setup", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "README.md", "source_location": null, "weight": 1.0}, {"source": "readme", "target": "readme_graphify", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "README.md", "source_location": null, "weight": 1.0}, {"source": "readme", "target": "docs_readme", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "README.md", "source_location": null, "weight": 1.0}, {"source": "readme_monolens", "target": "readme_studio_3t", "relation": "conceptually_related_to", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "README.md", "source_location": null, "weight": 1.0}, {"source": "readme_monolens", "target": "readme_apache_license", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "README.md", "source_location": null, "weight": 1.0}, {"source": "readme_graphify", "target": "claude_graphify_rules", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "README.md", "source_location": null, "weight": 1.0}, {"source": "readme_feature_list", "target": "docs_features_connection_manager", "relation": "conceptually_related_to", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "README.md", "source_location": null, "weight": 1.0}, {"source": "readme_feature_list", "target": "docs_features_credential_storage", "relation": "conceptually_related_to", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "README.md", "source_location": null, "weight": 1.0}, {"source": "readme_feature_list", "target": "docs_features_connection_test", "relation": "conceptually_related_to", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "README.md", "source_location": null, "weight": 1.0}, {"source": "readme_feature_list", "target": "docs_features_connect_and_explorer", "relation": "conceptually_related_to", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "README.md", "source_location": null, "weight": 1.0}, {"source": "readme_feature_list", "target": "docs_features_query_tab", "relation": "conceptually_related_to", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "README.md", "source_location": null, "weight": 1.0}, {"source": "readme_feature_list", "target": "docs_features_document_views", "relation": "conceptually_related_to", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "README.md", "source_location": null, "weight": 1.0}, {"source": "readme_extensible_architecture", "target": "docs_readme_unbuilt_features", "relation": "conceptually_related_to", "confidence": "INFERRED", "confidence_score": 0.85, "source_file": "README.md", "source_location": null, "weight": 1.0}, {"source": "readme_extensible_architecture", "target": "docs_architecture_decisions_driver_trait_factory", "relation": "semantically_similar_to", "confidence": "INFERRED", "confidence_score": 0.65, "source_file": "README.md", "source_location": null, "weight": 1.0}], "hyperedges": []} \ No newline at end of file diff --git a/index.html b/index.html index c802fab..8bb393c 100644 --- a/index.html +++ b/index.html @@ -2,10 +2,9 @@ - - monolens + Monolens diff --git a/package.json b/package.json index a9387fc..b754c0a 100644 --- a/package.json +++ b/package.json @@ -11,39 +11,47 @@ "test:frontend": "vitest" }, "dependencies": { - "@codemirror/autocomplete": "^6.20.1", + "@codemirror/autocomplete": "^6.20.4", "@codemirror/lang-json": "^6.0.2", "@codemirror/theme-one-dark": "^6.1.3", - "@codemirror/view": "^6.39.16", - "@tailwindcss/vite": "^4.2.1", - "@tanstack/vue-table": "^8.21.3", - "@tanstack/vue-virtual": "^3.13.21", - "@tauri-apps/api": "^2.10.1", - "@tauri-apps/plugin-log": "~2.8.0", - "@tauri-apps/plugin-opener": "^2.5.3", - "@vee-validate/zod": "^4.15.1", - "@vueuse/core": "^14.2.1", + "@codemirror/view": "^6.43.15", + "@lucide/vue": "^1.66.0", + "@tailwindcss/vite": "^4.3.3", + "@tanstack/vue-form": "^1.33.5", + "@tanstack/vue-table": "^9.3.1", + "@tanstack/vue-virtual": "^3.13.51", + "@tauri-apps/api": "^2.12.4", + "@tauri-apps/plugin-log": "~2.10.1", + "@tauri-apps/plugin-opener": "^2.7.2", + "@vueuse/core": "^15.0.0", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "codemirror": "^6.0.2", - "lucide-vue-next": "^0.563.0", - "pinia": "^3.0.4", - "reka-ui": "^2.8.2", - "tailwind-merge": "^3.5.0", - "tailwindcss": "^4.2.1", + "pinia": "^4.1.0", + "reka-ui": "^2.11.0", + "tailwind-merge": "^3.7.0", + "tailwindcss": "^4.3.3", "tw-animate-css": "^1.4.0", - "vee-validate": "^4.15.1", - "vue": "^3.5.29", + "vue": "^3.5.44", "vue-sonner": "^2.0.9", - "zod": "3.25.76" + "zod": "^4.6.7" }, "devDependencies": { - "@tauri-apps/cli": "^2.10.0", - "@types/node": "^25.3.3", - "@vitejs/plugin-vue": "^5.2.4", - "typescript": "~5.6.3", - "vite": "^6.4.1", - "vitest": "^4.0.18", - "vue-tsc": "^2.2.12" - } + "@tauri-apps/cli": "^2.12.4", + "@types/node": "^26.6.6", + "@vitejs/plugin-vue": "^6.0.10", + "happy-dom": "^21.0.4", + "typescript": "~6.0.3", + "vite": "^8.3.5", + "vitest": "^5.0.5", + "vue-tsc": "^3.3.14" + }, + "devEngines": { + "packageManager": { + "name": "pnpm", + "version": "12.17.0", + "onFail": "download" + } + }, + "packageManager": "pnpm@12.17.0" } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6f93224..25efb92 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1,3 +1,186 @@ +--- +lockfileVersion: '9.0' + +importers: + + .: + configDependencies: {} + packageManagerDependencies: + '@pnpm/exe': + specifier: 12.17.0 + version: 12.17.0 + pnpm: + specifier: 12.17.0 + version: 12.17.0 + +packages: + + '@pnpm/exe.android-arm64@12.17.0': + resolution: {integrity: sha512-gwu/F5YyoJp1bIbrIBl7B8JO3rkjkGlKikwQg9s0aVa5xWaBYIqPX+ZJqQ7I4D1YfbdI/Omfze0+6vnYiWHG6A==} + cpu: [arm64] + os: [android] + + '@pnpm/exe.android-x64@12.17.0': + resolution: {integrity: sha512-qUNhCvWyeBrUByNWkqqQ3Koy1gIAQ8UV5HDHXxoo5NUWVTde0Ld2BxweNwWJw8CNKpqEGceCUdjZ506JYpTjJg==} + cpu: [x64] + os: [android] + + '@pnpm/exe.darwin-arm64@12.17.0': + resolution: {integrity: sha512-0nkEqRLIbrm7dWl+p2EPpmyNKyM0bbdPEovdtMhHYbhkO+Vls38S+6emkx8kSTAr75BNXYXkCl4RtGinGG4Erg==} + cpu: [arm64] + os: [darwin] + + '@pnpm/exe.darwin-x64@12.17.0': + resolution: {integrity: sha512-w89cboNi4KCzmt/dSKrE4ijVNKICKiZsE9TcIWf77Tz+VpoDfuhPSmWPZK+iCHBvlI/9dIN+M3HiPPVsFnAx4Q==} + cpu: [x64] + os: [darwin] + + '@pnpm/exe.freebsd-x64@12.17.0': + resolution: {integrity: sha512-DJ0FOaUDXc71icSgiuBFEvqReqoSiS/rmnC6kltoiC3MzPyw3awcGbT/7FnnYzi/ziuoTC7v6vfBh0Vzv7VKOQ==} + cpu: [x64] + os: [freebsd] + + '@pnpm/exe.linux-arm64-musl@12.17.0': + resolution: {integrity: sha512-w5ZwZ4j8S804DGhMYlLLpc+G3Kkc+5GlDO++LLvO/nDIXBTHRhdxPfQBQU08q31k0JYfp3wXCQk0Y7DnsuVF6w==} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@pnpm/exe.linux-arm64@12.17.0': + resolution: {integrity: sha512-Cj2nIzWTVm6ZuhQHqL9SiJ11scBnEGxJyNg7tYQPzh9fVF5UVRkv+LbyfwdftnAtJerK/7HDpK/55WeQqp8OkA==} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@pnpm/exe.linux-ppc64@12.17.0': + resolution: {integrity: sha512-Gb//Wm15fXl+qWcxjNQmwlYDjGiJOnLU/A9uRZYmtkyZie2Yx8AHI6XkUIJioQz99TwVDKQ87ywD5Jtk8zcgyg==} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@pnpm/exe.linux-riscv64@12.17.0': + resolution: {integrity: sha512-+kItEcFjdVZEDzODsc7icb8cB+3AouCrEzOCezzxLITRhw86m9t3J/qyPdcFELBjeMnS+IXV8sJaSCQcpSqfgA==} + cpu: [riscv64] + os: [linux] + libc: [glibc] + + '@pnpm/exe.linux-s390x@12.17.0': + resolution: {integrity: sha512-jf5ghD2uDeknWQgK/EsCFNwlyL7BYLWfXe687g5UMVFclPLt524CREZ8Yl5hf4u7p3evCIPBN/m5I1QXUmJpHw==} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@pnpm/exe.linux-x64-musl@12.17.0': + resolution: {integrity: sha512-JGxrGSxkDkqRAUD49T9dOnKyJkQ3bWBeHnYH4o4mvj09R9eUzhWFi8oa6WPNyCO0/7/Max2gsSfgXOHj3QW7ug==} + cpu: [x64] + os: [linux] + libc: [musl] + + '@pnpm/exe.linux-x64@12.17.0': + resolution: {integrity: sha512-vQAusDzMofc/Ee4KOUYjE6FWeHOlMGQyvhR7W39QSamVeJWS16co/n+bhjdjwjI3UFh8IWOpamvTS+b4lvQUog==} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@pnpm/exe.win32-arm64@12.17.0': + resolution: {integrity: sha512-qDQh8rmyK5sxlaCSCADcmPrvytJabMCbxtJ0nCitzRo2j3/8BhzLjlOcWF3C6iPGhR5CbIb+btNy+VCVhd06mQ==} + cpu: [arm64] + os: [win32] + + '@pnpm/exe.win32-x64@12.17.0': + resolution: {integrity: sha512-qCo1OQlE3WrFZIAUK0O5EoupLaQ1fST7zNX8Qd+Tc4ojWgCvYA/ze0uP9hox8mSFC/JPAGRSG5o8gUBSq2rRxw==} + cpu: [x64] + os: [win32] + + '@pnpm/exe@12.17.0': + resolution: {integrity: sha512-Qmg3mFIQi/v0oa2OOosQlwEgJ+5av4KnUojIncC/Sb3ykw13iSm95USdLocH6T6j7ytxUsyLO9IU3mXHyMOsWg==} + engines: {node: '>=18'} + hasBin: true + + pnpm@12.17.0: + resolution: {integrity: sha512-uNIj6+wEtYContIG2qcdG8y2oe4dUu+pXMOw23UnFWHfjw2OLEOt05xewIAYr3qKlRCOcJD5Win4hye3pA/VPQ==} + engines: {node: '>=18'} + hasBin: true + +snapshots: + + '@pnpm/exe.android-arm64@12.17.0': + optional: true + + '@pnpm/exe.android-x64@12.17.0': + optional: true + + '@pnpm/exe.darwin-arm64@12.17.0': + optional: true + + '@pnpm/exe.darwin-x64@12.17.0': + optional: true + + '@pnpm/exe.freebsd-x64@12.17.0': + optional: true + + '@pnpm/exe.linux-arm64-musl@12.17.0': + optional: true + + '@pnpm/exe.linux-arm64@12.17.0': + optional: true + + '@pnpm/exe.linux-ppc64@12.17.0': + optional: true + + '@pnpm/exe.linux-riscv64@12.17.0': + optional: true + + '@pnpm/exe.linux-s390x@12.17.0': + optional: true + + '@pnpm/exe.linux-x64-musl@12.17.0': + optional: true + + '@pnpm/exe.linux-x64@12.17.0': + optional: true + + '@pnpm/exe.win32-arm64@12.17.0': + optional: true + + '@pnpm/exe.win32-x64@12.17.0': + optional: true + + '@pnpm/exe@12.17.0': + optionalDependencies: + '@pnpm/exe.android-arm64': 12.17.0 + '@pnpm/exe.android-x64': 12.17.0 + '@pnpm/exe.darwin-arm64': 12.17.0 + '@pnpm/exe.darwin-x64': 12.17.0 + '@pnpm/exe.freebsd-x64': 12.17.0 + '@pnpm/exe.linux-arm64': 12.17.0 + '@pnpm/exe.linux-arm64-musl': 12.17.0 + '@pnpm/exe.linux-ppc64': 12.17.0 + '@pnpm/exe.linux-riscv64': 12.17.0 + '@pnpm/exe.linux-s390x': 12.17.0 + '@pnpm/exe.linux-x64': 12.17.0 + '@pnpm/exe.linux-x64-musl': 12.17.0 + '@pnpm/exe.win32-arm64': 12.17.0 + '@pnpm/exe.win32-x64': 12.17.0 + + pnpm@12.17.0: + optionalDependencies: + '@pnpm/exe.android-arm64': 12.17.0 + '@pnpm/exe.android-x64': 12.17.0 + '@pnpm/exe.darwin-arm64': 12.17.0 + '@pnpm/exe.darwin-x64': 12.17.0 + '@pnpm/exe.freebsd-x64': 12.17.0 + '@pnpm/exe.linux-arm64': 12.17.0 + '@pnpm/exe.linux-arm64-musl': 12.17.0 + '@pnpm/exe.linux-ppc64': 12.17.0 + '@pnpm/exe.linux-riscv64': 12.17.0 + '@pnpm/exe.linux-s390x': 12.17.0 + '@pnpm/exe.linux-x64': 12.17.0 + '@pnpm/exe.linux-x64-musl': 12.17.0 + '@pnpm/exe.win32-arm64': 12.17.0 + '@pnpm/exe.win32-x64': 12.17.0 + +--- lockfileVersion: '9.0' settings: @@ -9,8 +192,8 @@ importers: .: dependencies: '@codemirror/autocomplete': - specifier: ^6.20.1 - version: 6.20.1 + specifier: ^6.20.4 + version: 6.20.4 '@codemirror/lang-json': specifier: ^6.0.2 version: 6.0.2 @@ -18,32 +201,35 @@ importers: specifier: ^6.1.3 version: 6.1.3 '@codemirror/view': - specifier: ^6.39.16 - version: 6.39.16 + specifier: ^6.43.15 + version: 6.43.15 + '@lucide/vue': + specifier: ^1.66.0 + version: 1.66.0(vue@3.5.44(typescript@6.0.3)) '@tailwindcss/vite': - specifier: ^4.2.1 - version: 4.2.1(vite@6.4.1(@types/node@25.3.3)(jiti@2.6.1)(lightningcss@1.31.1)) + specifier: ^4.3.3 + version: 4.3.3(vite@8.3.5(@types/node@26.6.6)(jiti@2.7.0)) + '@tanstack/vue-form': + specifier: ^1.33.5 + version: 1.33.5(vue@3.5.44(typescript@6.0.3)) '@tanstack/vue-table': - specifier: ^8.21.3 - version: 8.21.3(vue@3.5.29(typescript@5.6.3)) + specifier: ^9.3.1 + version: 9.3.1(vue@3.5.44(typescript@6.0.3)) '@tanstack/vue-virtual': - specifier: ^3.13.21 - version: 3.13.21(vue@3.5.29(typescript@5.6.3)) + specifier: ^3.13.51 + version: 3.13.51(vue@3.5.44(typescript@6.0.3)) '@tauri-apps/api': - specifier: ^2.10.1 - version: 2.10.1 + specifier: ^2.12.4 + version: 2.12.4 '@tauri-apps/plugin-log': - specifier: ~2.8.0 - version: 2.8.0 + specifier: ~2.10.1 + version: 2.10.1 '@tauri-apps/plugin-opener': - specifier: ^2.5.3 - version: 2.5.3 - '@vee-validate/zod': - specifier: ^4.15.1 - version: 4.15.1(vue@3.5.29(typescript@5.6.3))(zod@3.25.76) + specifier: ^2.7.2 + version: 2.7.2 '@vueuse/core': - specifier: ^14.2.1 - version: 14.2.1(vue@3.5.29(typescript@5.6.3)) + specifier: ^15.0.0 + version: 15.0.0(vue@3.5.44(typescript@6.0.3)) class-variance-authority: specifier: ^0.7.1 version: 0.7.1 @@ -53,278 +239,124 @@ importers: codemirror: specifier: ^6.0.2 version: 6.0.2 - lucide-vue-next: - specifier: ^0.563.0 - version: 0.563.0(vue@3.5.29(typescript@5.6.3)) pinia: - specifier: ^3.0.4 - version: 3.0.4(typescript@5.6.3)(vue@3.5.29(typescript@5.6.3)) + specifier: ^4.1.0 + version: 4.1.0(@vue/devtools-api@8.2.1)(typescript@6.0.3)(vue@3.5.44(typescript@6.0.3)) reka-ui: - specifier: ^2.8.2 - version: 2.8.2(vue@3.5.29(typescript@5.6.3)) + specifier: ^2.11.0 + version: 2.11.0(vue@3.5.44(typescript@6.0.3)) tailwind-merge: - specifier: ^3.5.0 - version: 3.5.0 + specifier: ^3.7.0 + version: 3.7.0 tailwindcss: - specifier: ^4.2.1 - version: 4.2.1 + specifier: ^4.3.3 + version: 4.3.3 tw-animate-css: specifier: ^1.4.0 version: 1.4.0 - vee-validate: - specifier: ^4.15.1 - version: 4.15.1(vue@3.5.29(typescript@5.6.3)) vue: - specifier: ^3.5.29 - version: 3.5.29(typescript@5.6.3) + specifier: ^3.5.44 + version: 3.5.44(typescript@6.0.3) vue-sonner: specifier: ^2.0.9 version: 2.0.9 zod: - specifier: 3.25.76 - version: 3.25.76 + specifier: ^4.6.7 + version: 4.6.7 devDependencies: '@tauri-apps/cli': - specifier: ^2.10.0 - version: 2.10.0 + specifier: ^2.12.4 + version: 2.12.4 '@types/node': - specifier: ^25.3.3 - version: 25.3.3 + specifier: ^26.6.6 + version: 26.6.6 '@vitejs/plugin-vue': - specifier: ^5.2.4 - version: 5.2.4(vite@6.4.1(@types/node@25.3.3)(jiti@2.6.1)(lightningcss@1.31.1))(vue@3.5.29(typescript@5.6.3)) + specifier: ^6.0.10 + version: 6.0.10(vite@8.3.5(@types/node@26.6.6)(jiti@2.7.0))(vue@3.5.44(typescript@6.0.3)) + happy-dom: + specifier: ^21.0.4 + version: 21.0.4 typescript: - specifier: ~5.6.3 - version: 5.6.3 + specifier: ~6.0.3 + version: 6.0.3 vite: - specifier: ^6.4.1 - version: 6.4.1(@types/node@25.3.3)(jiti@2.6.1)(lightningcss@1.31.1) + specifier: ^8.3.5 + version: 8.3.5(@types/node@26.6.6)(jiti@2.7.0) vitest: - specifier: ^4.0.18 - version: 4.0.18(@types/node@25.3.3)(jiti@2.6.1)(lightningcss@1.31.1) + specifier: ^5.0.5 + version: 5.0.5(@types/node@26.6.6)(happy-dom@21.0.4)(vite@8.3.5(@types/node@26.6.6)(jiti@2.7.0)) vue-tsc: - specifier: ^2.2.12 - version: 2.2.12(typescript@5.6.3) + specifier: ^3.3.14 + version: 3.3.14(typescript@6.0.3) packages: - '@babel/helper-string-parser@7.27.1': - resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} + '@babel/helper-string-parser@7.29.11': + resolution: {integrity: sha512-snZbO7MEDOof+hvNa97EjEY7qD7h2kwvQr5AJC3ZhQudKW5dnej6k3kP9NQV88hJwaS8vDqRLFo9rivLEhle2g==} engines: {node: '>=6.9.0'} - '@babel/helper-validator-identifier@7.28.5': - resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==} + '@babel/helper-validator-identifier@7.29.7': + resolution: {integrity: sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==} engines: {node: '>=6.9.0'} - '@babel/parser@7.29.0': - resolution: {integrity: sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww==} + '@babel/parser@7.29.12': + resolution: {integrity: sha512-WJ7JBp/Q4+z/ImydtI7jS6NVco5++632ExBemSX+Ysb+G1lCwQ5BVo3SY+jGGOcZXI4ZccbZSUSJLNBW6eEpJg==} engines: {node: '>=6.0.0'} hasBin: true - '@babel/types@7.29.0': - resolution: {integrity: sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==} + '@babel/types@7.29.11': + resolution: {integrity: sha512-WCsemW40Cpc6gouC1SrY6PBaZ+oLRlEVY9XBvZBI7il/HyUrb2nJ505wE1MHEFyDZNnrWDEuy4jL50WVHfTTSQ==} engines: {node: '>=6.9.0'} - '@codemirror/autocomplete@6.20.1': - resolution: {integrity: sha512-1cvg3Vz1dSSToCNlJfRA2WSI4ht3K+WplO0UMOgmUYPivCyy2oueZY6Lx7M9wThm7SDUBViRmuT+OG/i8+ON9A==} + '@codemirror/autocomplete@6.20.4': + resolution: {integrity: sha512-yF8RhOf2SXlg00+qtupzLhrglcas61boqaONiNcbPIebHCpJocpqTBA6dyrtUrQO47hqyTdeob/3XnYilT7TaQ==} - '@codemirror/commands@6.10.2': - resolution: {integrity: sha512-vvX1fsih9HledO1c9zdotZYUZnE4xV0m6i3m25s5DIfXofuprk6cRcLUZvSk3CASUbwjQX21tOGbkY2BH8TpnQ==} + '@codemirror/commands@6.11.1': + resolution: {integrity: sha512-O/4hG3SC1YwcmQ0d2UVNDs+AsaNWd1iHVxbTeEBuqH+6bExAiPK3iS/BvpY6rZGURALv4ZD3sIgcCmRvw3ehBg==} '@codemirror/lang-json@6.0.2': resolution: {integrity: sha512-x2OtO+AvwEHrEwR0FyyPtfDUiloG3rnVTSZV1W8UteaLL8/MajQd8DpvUb2YVzC+/T18aSDv0H9mu+xw0EStoQ==} - '@codemirror/language@6.12.2': - resolution: {integrity: sha512-jEPmz2nGGDxhRTg3lTpzmIyGKxz3Gp3SJES4b0nAuE5SWQoKdT5GoQ69cwMmFd+wvFUhYirtDTr0/DRHpQAyWg==} + '@codemirror/language@6.13.1': + resolution: {integrity: sha512-zfvU2r3TyylQLMj/te+5CnQ0rSqZi8B1oTCBbQkagcLW4Xc6imab6qN+pEJhsuNOLaJqiRyBXp9UhWA1DhVvDw==} + + '@codemirror/lint@6.9.7': + resolution: {integrity: sha512-28/+iWLYxKxsvGYhSYL7zaCZqLz5+FFFDq9tVsvGv9kv8RY4fFAchJ5WX9M3YrrRlTIsECjsXPqeNgnSmNP2dg==} - '@codemirror/lint@6.9.5': - resolution: {integrity: sha512-GElsbU9G7QT9xXhpUg1zWGmftA/7jamh+7+ydKRuT0ORpWS3wOSP0yT1FOlIZa7mIJjpVPipErsyvVqB9cfTFA==} + '@codemirror/search@6.7.2': + resolution: {integrity: sha512-gUYkYhT2+n/+VGZ+8EzE5WFkYZUZYm1VOKDudIsNqh42uRVQJ0a6Yss9sdKT3MeOYfuL1N6AZA57oza0Oyr0LA==} - '@codemirror/search@6.6.0': - resolution: {integrity: sha512-koFuNXcDvyyotWcgOnZGmY7LZqEOXZaaxD/j6n18TCLx2/9HieZJ5H6hs1g8FiRxBD0DNfs0nXn17g872RmYdw==} + '@codemirror/state@6.7.6': + resolution: {integrity: sha512-kAz+AncRtKuIknedxT1bq4XwXv4UowhbkHU1myPrtVb/jZtImWuV5BXzv5vK6i3kYACsdiZiQKFQQ5Mq7elW8w==} - '@codemirror/state@6.5.4': - resolution: {integrity: sha512-8y7xqG/hpB53l25CIoit9/ngxdfoG+fx+V3SHBrinnhOtLvKHRyAJJuHzkWrR4YXXLX8eXBsejgAAxHUOdW1yw==} + '@codemirror/streamparser@6.0.0': + resolution: {integrity: sha512-JTFPF6xrfjt0mnfGPgzcLiMwYmvIyNaoizp8BYzv8FE8KrT6GBGDZ190rtt+O7Wke3gG/MFHyiY9YGNeimof4w==} '@codemirror/theme-one-dark@6.1.3': resolution: {integrity: sha512-NzBdIvEJmx6fjeremiGp3t/okrLPYT0d9orIc7AFun8oZcRk58aejkqhv6spnz4MLAevrKNPMQYXEWMg4s+sKA==} - '@codemirror/view@6.39.16': - resolution: {integrity: sha512-m6S22fFpKtOWhq8HuhzsI1WzUP/hB9THbDj0Tl5KX4gbO6Y91hwBl7Yky33NdvB6IffuRFiBxf1R8kJMyXmA4Q==} - - '@esbuild/aix-ppc64@0.25.12': - resolution: {integrity: sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==} - engines: {node: '>=18'} - cpu: [ppc64] - os: [aix] - - '@esbuild/android-arm64@0.25.12': - resolution: {integrity: sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==} - engines: {node: '>=18'} - cpu: [arm64] - os: [android] - - '@esbuild/android-arm@0.25.12': - resolution: {integrity: sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==} - engines: {node: '>=18'} - cpu: [arm] - os: [android] - - '@esbuild/android-x64@0.25.12': - resolution: {integrity: sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==} - engines: {node: '>=18'} - cpu: [x64] - os: [android] + '@codemirror/view@6.43.15': + resolution: {integrity: sha512-HHsfpqFeHyXLs983pSRe38Xajs0vFWZQ2BuX0m7Ki8w3jZ5iD8cuR3lBz2/5/X53Dhf32SBHKh6rtSTB6tKeGw==} - '@esbuild/darwin-arm64@0.25.12': - resolution: {integrity: sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==} - engines: {node: '>=18'} - cpu: [arm64] - os: [darwin] + '@floating-ui/core@1.8.0': + resolution: {integrity: sha512-0CIZ5itps/8x7BG8dEIhs53BvCUH2PCoogtakwRTut+Arm58sJooJ0AuZhLw2HJYIR5cMLNPBSS728sPho2khQ==} - '@esbuild/darwin-x64@0.25.12': - resolution: {integrity: sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==} - engines: {node: '>=18'} - cpu: [x64] - os: [darwin] + '@floating-ui/dom@1.8.0': + resolution: {integrity: sha512-yXSrzeHZBTZadLOlfyhCkJHNeLJnHRnRInwdZ40L7ZiaAtrBwoYlsDrX3v5zB1Utk7CLfzcOVnVVWoXEky7Ceg==} - '@esbuild/freebsd-arm64@0.25.12': - resolution: {integrity: sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==} - engines: {node: '>=18'} - cpu: [arm64] - os: [freebsd] + '@floating-ui/utils@0.2.12': + resolution: {integrity: sha512-HpCo8tmWzLVad5s2d19EhAz5zqrrQ6s69qd6moPMQvkOuSwDT1YgRfWSVuc4ennqrgv3OHppiOGMQ7oC13yIww==} - '@esbuild/freebsd-x64@0.25.12': - resolution: {integrity: sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==} - engines: {node: '>=18'} - cpu: [x64] - os: [freebsd] - - '@esbuild/linux-arm64@0.25.12': - resolution: {integrity: sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==} - engines: {node: '>=18'} - cpu: [arm64] - os: [linux] - - '@esbuild/linux-arm@0.25.12': - resolution: {integrity: sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==} - engines: {node: '>=18'} - cpu: [arm] - os: [linux] - - '@esbuild/linux-ia32@0.25.12': - resolution: {integrity: sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==} - engines: {node: '>=18'} - cpu: [ia32] - os: [linux] - - '@esbuild/linux-loong64@0.25.12': - resolution: {integrity: sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==} - engines: {node: '>=18'} - cpu: [loong64] - os: [linux] - - '@esbuild/linux-mips64el@0.25.12': - resolution: {integrity: sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==} - engines: {node: '>=18'} - cpu: [mips64el] - os: [linux] - - '@esbuild/linux-ppc64@0.25.12': - resolution: {integrity: sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==} - engines: {node: '>=18'} - cpu: [ppc64] - os: [linux] - - '@esbuild/linux-riscv64@0.25.12': - resolution: {integrity: sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==} - engines: {node: '>=18'} - cpu: [riscv64] - os: [linux] - - '@esbuild/linux-s390x@0.25.12': - resolution: {integrity: sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==} - engines: {node: '>=18'} - cpu: [s390x] - os: [linux] - - '@esbuild/linux-x64@0.25.12': - resolution: {integrity: sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==} - engines: {node: '>=18'} - cpu: [x64] - os: [linux] - - '@esbuild/netbsd-arm64@0.25.12': - resolution: {integrity: sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==} - engines: {node: '>=18'} - cpu: [arm64] - os: [netbsd] - - '@esbuild/netbsd-x64@0.25.12': - resolution: {integrity: sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==} - engines: {node: '>=18'} - cpu: [x64] - os: [netbsd] - - '@esbuild/openbsd-arm64@0.25.12': - resolution: {integrity: sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==} - engines: {node: '>=18'} - cpu: [arm64] - os: [openbsd] - - '@esbuild/openbsd-x64@0.25.12': - resolution: {integrity: sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==} - engines: {node: '>=18'} - cpu: [x64] - os: [openbsd] - - '@esbuild/openharmony-arm64@0.25.12': - resolution: {integrity: sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==} - engines: {node: '>=18'} - cpu: [arm64] - os: [openharmony] - - '@esbuild/sunos-x64@0.25.12': - resolution: {integrity: sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==} - engines: {node: '>=18'} - cpu: [x64] - os: [sunos] - - '@esbuild/win32-arm64@0.25.12': - resolution: {integrity: sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==} - engines: {node: '>=18'} - cpu: [arm64] - os: [win32] - - '@esbuild/win32-ia32@0.25.12': - resolution: {integrity: sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==} - engines: {node: '>=18'} - cpu: [ia32] - os: [win32] - - '@esbuild/win32-x64@0.25.12': - resolution: {integrity: sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==} - engines: {node: '>=18'} - cpu: [x64] - os: [win32] - - '@floating-ui/core@1.7.4': - resolution: {integrity: sha512-C3HlIdsBxszvm5McXlB8PeOEWfBhcGBTZGkGlWc2U0KFY5IwG5OQEuQ8rq52DZmcHDlPLd+YFBK+cZcytwIFWg==} - - '@floating-ui/dom@1.7.5': - resolution: {integrity: sha512-N0bD2kIPInNHUHehXhMke1rBGs1dwqvC9O9KYMyyjK7iXt7GAhnro7UlcuYcGdS/yYOlq0MAVgrow8IbWJwyqg==} - - '@floating-ui/utils@0.2.10': - resolution: {integrity: sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==} - - '@floating-ui/vue@1.1.10': - resolution: {integrity: sha512-vdf8f6rHnFPPLRsmL4p12wYl+Ux4mOJOkjzKEMYVnwdf7UFdvBtHlLvQyx8iKG5vhPRbDRgZxdtpmyigDPjzYg==} + '@floating-ui/vue@2.0.1': + resolution: {integrity: sha512-043dyEKD9TDSfz080YH2vobubgg+NIo/9Fw4RAl7sEMpD8Am1DNrrSTl/GpMBjyGHoD/FbJY2QLawY+HSQsEIA==} + peerDependencies: + vue: '>=3.3.0' - '@internationalized/date@3.11.0': - resolution: {integrity: sha512-BOx5huLAWhicM9/ZFs84CzP+V3gBW6vlpM02yzsdYC7TGlZJX1OJiEEHcSayF00Z+3jLlm4w79amvSt6RqKN3Q==} + '@internationalized/date@3.12.4': + resolution: {integrity: sha512-M1dEn4c1U1HsSlaVR8upZtSqvXrTkHDfv18H01uCSJyjVLDxnBR38v/fMxecmlwXKR4i9HeZcmgQAPE6A+aGJQ==} - '@internationalized/number@3.6.5': - resolution: {integrity: sha512-6hY4Kl4HPBvtfS62asS/R22JzNNy8vi/Ssev7x6EobfCp+9QIB2hKvI2EtbdJ0VSQacxVNtqhE/NmF/NZ0gm6g==} + '@internationalized/number@3.6.9': + resolution: {integrity: sha512-GgWy2caO/+hV9sT/oCTWGHvbjmJMPT9kkXzoKRLnkEq7adghhR7UprhvY9sXrKfrp3NcDfhF6QQHXR1KfQbB8g==} '@jridgewell/gen-mapping@0.3.13': resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} @@ -336,234 +368,200 @@ packages: resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} engines: {node: '>=6.0.0'} - '@jridgewell/sourcemap-codec@1.5.5': - resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} + '@jridgewell/sourcemap-codec@1.6.0': + resolution: {integrity: sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw==} '@jridgewell/trace-mapping@0.3.31': resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} - '@lezer/common@1.5.1': - resolution: {integrity: sha512-6YRVG9vBkaY7p1IVxL4s44n5nUnaNnGM2/AckNgYOnxTG2kWh1vR8BMxPseWPjRNpb5VtXnMpeYAEAADoRV1Iw==} + '@lezer/common@1.5.5': + resolution: {integrity: sha512-Pw7PVCndruF1pb3ebCxpSVpkMHSDjwRCLcTRw+RizUKco5QbWylwq2g4lnG/RYN7yTTQDXE9F77MwtkAyphrjg==} - '@lezer/highlight@1.2.3': - resolution: {integrity: sha512-qXdH7UqTvGfdVBINrgKhDsVTJTxactNNxLk7+UMwZhU13lMHaOBlJe9Vqp907ya56Y3+ed2tlqzys7jDkTmW0g==} + '@lezer/highlight@1.2.5': + resolution: {integrity: sha512-O1GMVKgtf5YspFaRzpmqkVgMtIk2HG9uMAbH6wdAIdNh3TVzCsch/+cRi+6l2//UGDKe6NB9BW3cvuR5ezMQ0w==} '@lezer/json@1.0.3': resolution: {integrity: sha512-BP9KzdF9Y35PDpv04r0VeSTKDeox5vVr3efE7eBbx3r4s3oNLfunchejZhjArmeieBH+nVOpgIiBJpEAv8ilqQ==} - '@lezer/lr@1.4.8': - resolution: {integrity: sha512-bPWa0Pgx69ylNlMlPvBPryqeLYQjyJjqPx+Aupm5zydLIF3NE+6MMLT8Yi23Bd9cif9VS00aUebn+6fDIGBcDA==} + '@lezer/lr@1.4.12': + resolution: {integrity: sha512-On9HU4oMMiHPMkQDgoTnGahjgG5n/MkN38CgG77sMraKLL+3JBbeQNi1FcCXV9QRqwF9/BUXP3caHyJC1fBOTQ==} - '@marijn/find-cluster-break@1.0.2': - resolution: {integrity: sha512-l0h88YhZFyKdXIFNfSWpyjStDjGHwZ/U7iobcK1cQQD8sejsONdQtTVU+1wVN1PBw40PiiHB1vA5S7VTfQiP9g==} + '@lucide/vue@1.66.0': + resolution: {integrity: sha512-cyALYgAzCD2uZ557pM5I/dmFAyTKqtwY7PrdU67gzITkTH+V635u7Uv7VBpQ5hqvmlGqRtbqSDx08LCJHU+GAw==} + peerDependencies: + vue: '>=3.0.1' - '@rollup/rollup-android-arm-eabi@4.59.0': - resolution: {integrity: sha512-upnNBkA6ZH2VKGcBj9Fyl9IGNPULcjXRlg0LLeaioQWueH30p6IXtJEbKAgvyv+mJaMxSm1l6xwDXYjpEMiLMg==} + '@marijn/find-cluster-break@1.0.4': + resolution: {integrity: sha512-Wy0V7+SGUjnF9/TkiM1hKVDPj7jKXduPNboMVtHTA8dySMURWqfg/JZ9E2Sq8JgSJmkl7k7Qe9FLeMSrSraWmQ==} + + '@oxc-project/types@0.156.0': + resolution: {integrity: sha512-1Duyg48TdJNdyhEB8Xn1L36bT9Pv6GUtlKjHxOys8xkaF/rQq3XJE+bAurSK/AgIz5mGFsb77cn9Pb20+WCScw==} + + '@rolldown/binding-android-arm-eabi@1.2.16': + resolution: {integrity: sha512-ia3DwAs0OsMGS7+IConptSiyk9pEXkOx/AbJ2n/lAGxH3QkWQus1t8d7gnAT38FCU2sQu8lUbeR6AiZFv4JxaA==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [android] - '@rollup/rollup-android-arm64@4.59.0': - resolution: {integrity: sha512-hZ+Zxj3SySm4A/DylsDKZAeVg0mvi++0PYVceVyX7hemkw7OreKdCvW2oQ3T1FMZvCaQXqOTHb8qmBShoqk69Q==} + '@rolldown/binding-android-arm64@1.2.16': + resolution: {integrity: sha512-kx5wBiMB6E7TEvpl2B9gjLU004BkvEm2Dr941BFjx/SontPwSzNuQUIoVKxHHoSjpFIfHMmcguyLOtyn0hObRg==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [android] - '@rollup/rollup-darwin-arm64@4.59.0': - resolution: {integrity: sha512-W2Psnbh1J8ZJw0xKAd8zdNgF9HRLkdWwwdWqubSVk0pUuQkoHnv7rx4GiF9rT4t5DIZGAsConRE3AxCdJ4m8rg==} + '@rolldown/binding-darwin-arm64@1.2.16': + resolution: {integrity: sha512-qslZrXnKJU4cUpjoAmtAo/KQM9e2IRMlmpig6Zpwj+8KWxW8v+nkAf9216Bsek9lOC1jvg2yCjygCFo2f83msQ==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [darwin] - '@rollup/rollup-darwin-x64@4.59.0': - resolution: {integrity: sha512-ZW2KkwlS4lwTv7ZVsYDiARfFCnSGhzYPdiOU4IM2fDbL+QGlyAbjgSFuqNRbSthybLbIJ915UtZBtmuLrQAT/w==} + '@rolldown/binding-darwin-x64@1.2.16': + resolution: {integrity: sha512-FdF+y48ND13eAyM2mQn+85kvAqNlvRWROy2rhgA463X/0Gv4vGtazZtfaF1mRBLscYvtOlh4QnLjOWiFzX69OQ==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [darwin] - '@rollup/rollup-freebsd-arm64@4.59.0': - resolution: {integrity: sha512-EsKaJ5ytAu9jI3lonzn3BgG8iRBjV4LxZexygcQbpiU0wU0ATxhNVEpXKfUa0pS05gTcSDMKpn3Sx+QB9RlTTA==} - cpu: [arm64] - os: [freebsd] - - '@rollup/rollup-freebsd-x64@4.59.0': - resolution: {integrity: sha512-d3DuZi2KzTMjImrxoHIAODUZYoUUMsuUiY4SRRcJy6NJoZ6iIqWnJu9IScV9jXysyGMVuW+KNzZvBLOcpdl3Vg==} + '@rolldown/binding-freebsd-x64@1.2.16': + resolution: {integrity: sha512-ddjqFM5e95AgLbUPtKIS7V1KTVlA0RUgCVr5mn+/IEhsdy3pr+Vuj/HZ7nlMN2FyxIwUktOH1LEPIQXO1v9y1Q==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [freebsd] - '@rollup/rollup-linux-arm-gnueabihf@4.59.0': - resolution: {integrity: sha512-t4ONHboXi/3E0rT6OZl1pKbl2Vgxf9vJfWgmUoCEVQVxhW6Cw/c8I6hbbu7DAvgp82RKiH7TpLwxnJeKv2pbsw==} - cpu: [arm] - os: [linux] - libc: [glibc] - - '@rollup/rollup-linux-arm-musleabihf@4.59.0': - resolution: {integrity: sha512-CikFT7aYPA2ufMD086cVORBYGHffBo4K8MQ4uPS/ZnY54GKj36i196u8U+aDVT2LX4eSMbyHtyOh7D7Zvk2VvA==} + '@rolldown/binding-linux-arm-gnueabihf@1.2.16': + resolution: {integrity: sha512-tHdKWbFkFBI687iim3LIyIvyL7j66VvQYkDPss5Stn/qyE2tWkR1YD0dXmXPFKKqT/o5ZEJVsaRsWS2meQwGOA==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] - libc: [musl] - '@rollup/rollup-linux-arm64-gnu@4.59.0': - resolution: {integrity: sha512-jYgUGk5aLd1nUb1CtQ8E+t5JhLc9x5WdBKew9ZgAXg7DBk0ZHErLHdXM24rfX+bKrFe+Xp5YuJo54I5HFjGDAA==} + '@rolldown/binding-linux-arm64-gnu@1.2.16': + resolution: {integrity: sha512-FJiLGAblPBMlBEdbOqw6kW6QcXDWAf/oJu7gYs9ZJZwmg+K+/zjFLxPTIY9izJUzxzc24FHvmseyA0IvJSl17w==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-arm64-musl@4.59.0': - resolution: {integrity: sha512-peZRVEdnFWZ5Bh2KeumKG9ty7aCXzzEsHShOZEFiCQlDEepP1dpUl/SrUNXNg13UmZl+gzVDPsiCwnV1uI0RUA==} + '@rolldown/binding-linux-arm64-musl@1.2.16': + resolution: {integrity: sha512-hrJpblMsrQ58U9veWpbXzkhV5zStunds6HWLprqq1qauvgBr1E6P+24kLj6lSLI9vjRn/RYpjprhkHQPKGWpNg==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [musl] - '@rollup/rollup-linux-loong64-gnu@4.59.0': - resolution: {integrity: sha512-gbUSW/97f7+r4gHy3Jlup8zDG190AuodsWnNiXErp9mT90iCy9NKKU0Xwx5k8VlRAIV2uU9CsMnEFg/xXaOfXg==} - cpu: [loong64] - os: [linux] - libc: [glibc] - - '@rollup/rollup-linux-loong64-musl@4.59.0': - resolution: {integrity: sha512-yTRONe79E+o0FWFijasoTjtzG9EBedFXJMl888NBEDCDV9I2wGbFFfJQQe63OijbFCUZqxpHz1GzpbtSFikJ4Q==} - cpu: [loong64] - os: [linux] - libc: [musl] - - '@rollup/rollup-linux-ppc64-gnu@4.59.0': - resolution: {integrity: sha512-sw1o3tfyk12k3OEpRddF68a1unZ5VCN7zoTNtSn2KndUE+ea3m3ROOKRCZxEpmT9nsGnogpFP9x6mnLTCaoLkA==} + '@rolldown/binding-linux-ppc64-gnu@1.2.16': + resolution: {integrity: sha512-qJd/dC6j6CwLG9/s/RP2nb1RZ0GIgj7Ioj7x/SYa3RzjVrlzrmH0fhIOpfvqQDeIsdP0loi+kHaMhJzY1PBIaA==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [ppc64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-ppc64-musl@4.59.0': - resolution: {integrity: sha512-+2kLtQ4xT3AiIxkzFVFXfsmlZiG5FXYW7ZyIIvGA7Bdeuh9Z0aN4hVyXS/G1E9bTP/vqszNIN/pUKCk/BTHsKA==} - cpu: [ppc64] - os: [linux] - libc: [musl] - - '@rollup/rollup-linux-riscv64-gnu@4.59.0': - resolution: {integrity: sha512-NDYMpsXYJJaj+I7UdwIuHHNxXZ/b/N2hR15NyH3m2qAtb/hHPA4g4SuuvrdxetTdndfj9b1WOmy73kcPRoERUg==} - cpu: [riscv64] - os: [linux] - libc: [glibc] - - '@rollup/rollup-linux-riscv64-musl@4.59.0': - resolution: {integrity: sha512-nLckB8WOqHIf1bhymk+oHxvM9D3tyPndZH8i8+35p/1YiVoVswPid2yLzgX7ZJP0KQvnkhM4H6QZ5m0LzbyIAg==} - cpu: [riscv64] - os: [linux] - libc: [musl] - - '@rollup/rollup-linux-s390x-gnu@4.59.0': - resolution: {integrity: sha512-oF87Ie3uAIvORFBpwnCvUzdeYUqi2wY6jRFWJAy1qus/udHFYIkplYRW+wo+GRUP4sKzYdmE1Y3+rY5Gc4ZO+w==} + '@rolldown/binding-linux-s390x-gnu@1.2.16': + resolution: {integrity: sha512-Vx7Xg5uUJuNcujJjI70OICaH0EYj80BtOLZTSN7uOYg8QOLlyKnaG0IW7Fn/J0IpJsaKzmDKMhOQv1JBM9m9vg==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [s390x] os: [linux] libc: [glibc] - '@rollup/rollup-linux-x64-gnu@4.59.0': - resolution: {integrity: sha512-3AHmtQq/ppNuUspKAlvA8HtLybkDflkMuLK4DPo77DfthRb71V84/c4MlWJXixZz4uruIH4uaa07IqoAkG64fg==} + '@rolldown/binding-linux-x64-gnu@1.2.16': + resolution: {integrity: sha512-TNKhgE3uJTsTzatUSSYt0Kgo+Oa2uSlSjz4r1aEIea7HX6ULBym4fAlYUhG1i6pjNf2njfCUAfKwr6C3E9Xg1w==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-x64-musl@4.59.0': - resolution: {integrity: sha512-2UdiwS/9cTAx7qIUZB/fWtToJwvt0Vbo0zmnYt7ED35KPg13Q0ym1g442THLC7VyI6JfYTP4PiSOWyoMdV2/xg==} + '@rolldown/binding-linux-x64-musl@1.2.16': + resolution: {integrity: sha512-7chXdpjXZ8qG0+mIxECN3cT9kvhZ3/oZhMX6Pp876P7CDrIy8nJswva6Mq357JR7eYXfuskwZxMeV1SzoZdHrA==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [musl] - '@rollup/rollup-openbsd-x64@4.59.0': - resolution: {integrity: sha512-M3bLRAVk6GOwFlPTIxVBSYKUaqfLrn8l0psKinkCFxl4lQvOSz8ZrKDz2gxcBwHFpci0B6rttydI4IpS4IS/jQ==} - cpu: [x64] - os: [openbsd] - - '@rollup/rollup-openharmony-arm64@4.59.0': - resolution: {integrity: sha512-tt9KBJqaqp5i5HUZzoafHZX8b5Q2Fe7UjYERADll83O4fGqJ49O1FsL6LpdzVFQcpwvnyd0i+K/VSwu/o/nWlA==} + '@rolldown/binding-openharmony-arm64@1.2.16': + resolution: {integrity: sha512-Kwjt+7uNTJK5/MDkGWGVO2KQPnj8yAV75dMpfwyHZrJSXWHrniOB0aUEWlVYf3H1hOqFLIStLdM5QXq6bnKmOQ==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [openharmony] - '@rollup/rollup-win32-arm64-msvc@4.59.0': - resolution: {integrity: sha512-V5B6mG7OrGTwnxaNUzZTDTjDS7F75PO1ae6MJYdiMu60sq0CqN5CVeVsbhPxalupvTX8gXVSU9gq+Rx1/hvu6A==} + '@rolldown/binding-win32-arm64-msvc@1.2.16': + resolution: {integrity: sha512-/LH6WfjDRXXJz/p5xl4TmU+jpcnwcxbKEQBW/GZE0JHfN0lGIyTtsUEGaQ/q/GikkGJ2yLKe6pwm7AnEWO8pdw==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.59.0': - resolution: {integrity: sha512-UKFMHPuM9R0iBegwzKF4y0C4J9u8C6MEJgFuXTBerMk7EJ92GFVFYBfOZaSGLu6COf7FxpQNqhNS4c4icUPqxA==} - cpu: [ia32] - os: [win32] - - '@rollup/rollup-win32-x64-gnu@4.59.0': - resolution: {integrity: sha512-laBkYlSS1n2L8fSo1thDNGrCTQMmxjYY5G0WFWjFFYZkKPjsMBsgJfGf4TLxXrF6RyhI60L8TMOjBMvXiTcxeA==} + '@rolldown/binding-win32-x64-msvc@1.2.16': + resolution: {integrity: sha512-1lv1KvX0Z871zfk7vw+dnXMMnHNyDwMzWG8Kv3HxDXMOhzCX4SHc7HK319fInP0i+0p9D8brkfzL7pJOaGl+tw==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [win32] - '@rollup/rollup-win32-x64-msvc@4.59.0': - resolution: {integrity: sha512-2HRCml6OztYXyJXAvdDXPKcawukWY2GpR5/nxKp4iBgiO3wcoEGkAaqctIbZcNB6KlUQBIqt8VYkNSj2397EfA==} - cpu: [x64] - os: [win32] + '@rolldown/pluginutils@1.0.1': + resolution: {integrity: sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==} - '@standard-schema/spec@1.1.0': - resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} + '@swc/helpers@0.5.23': + resolution: {integrity: sha512-5lSsMOTXURePglDfvuAQUqkGek9Hg2kksOYay2m0+XR++b2NWYL/4sWyuvVBIs8oKnJaxkdi9whaL/sqN13afw==} - '@swc/helpers@0.5.19': - resolution: {integrity: sha512-QamiFeIK3txNjgUTNppE6MiG3p7TdninpZu0E0PbqVh1a9FNLT2FRhisaa4NcaX52XVhA5l7Pk58Ft7Sqi/2sA==} + '@tailwindcss/node@4.3.3': + resolution: {integrity: sha512-/T8IKEsf9VTU6tLjgC7+sv2mOPtQxzE2jMw7u4Tt40Tx+QSZxpzh95/H6cMKoja9XuW7iMdLJYBB0o9G1CaAgg==} - '@tailwindcss/node@4.2.1': - resolution: {integrity: sha512-jlx6sLk4EOwO6hHe1oCGm1Q4AN/s0rSrTTPBGPM0/RQ6Uylwq17FuU8IeJJKEjtc6K6O07zsvP+gDO6MMWo7pg==} - - '@tailwindcss/oxide-android-arm64@4.2.1': - resolution: {integrity: sha512-eZ7G1Zm5EC8OOKaesIKuw77jw++QJ2lL9N+dDpdQiAB/c/B2wDh0QPFHbkBVrXnwNugvrbJFk1gK2SsVjwWReg==} + '@tailwindcss/oxide-android-arm64@4.3.3': + resolution: {integrity: sha512-Y85A2gmPSkl5Ve5qR86GL4HT509cFqQh1aes9p3sSkyTPwt0Pppf3GkwGe4JPACcRYjgJIEhQgM6dBClnr0NYw==} engines: {node: '>= 20'} cpu: [arm64] os: [android] - '@tailwindcss/oxide-darwin-arm64@4.2.1': - resolution: {integrity: sha512-q/LHkOstoJ7pI1J0q6djesLzRvQSIfEto148ppAd+BVQK0JYjQIFSK3JgYZJa+Yzi0DDa52ZsQx2rqytBnf8Hw==} + '@tailwindcss/oxide-darwin-arm64@4.3.3': + resolution: {integrity: sha512-BiaWatpBcERQFDlOjRDpIVXuFK5PJez5SA4JMg6VYZdBYU+qKfV/vqjcIs+IYmtitf1xYQZTwXvU/8y4lfZUGw==} engines: {node: '>= 20'} cpu: [arm64] os: [darwin] - '@tailwindcss/oxide-darwin-x64@4.2.1': - resolution: {integrity: sha512-/f/ozlaXGY6QLbpvd/kFTro2l18f7dHKpB+ieXz+Cijl4Mt9AI2rTrpq7V+t04nK+j9XBQHnSMdeQRhbGyt6fw==} + '@tailwindcss/oxide-darwin-x64@4.3.3': + resolution: {integrity: sha512-fAeUqfV5ndhxRwai8cXGzdLvul9utWOmeTkv69unv4ZXixjn61Z+p9lCWdwOwA3TYboG3BwdVuN/RDjhBRl0mw==} engines: {node: '>= 20'} cpu: [x64] os: [darwin] - '@tailwindcss/oxide-freebsd-x64@4.2.1': - resolution: {integrity: sha512-5e/AkgYJT/cpbkys/OU2Ei2jdETCLlifwm7ogMC7/hksI2fC3iiq6OcXwjibcIjPung0kRtR3TxEITkqgn0TcA==} + '@tailwindcss/oxide-freebsd-x64@4.3.3': + resolution: {integrity: sha512-iyf5bV6+wnAlflVeEy7R25dupxTNECZN5QMI0qNT6eT+EgaGdZcKhGkr5SdoaWiLJ3spLqIY9VCeSGrwmtg4kw==} engines: {node: '>= 20'} cpu: [x64] os: [freebsd] - '@tailwindcss/oxide-linux-arm-gnueabihf@4.2.1': - resolution: {integrity: sha512-Uny1EcVTTmerCKt/1ZuKTkb0x8ZaiuYucg2/kImO5A5Y/kBz41/+j0gxUZl+hTF3xkWpDmHX+TaWhOtba2Fyuw==} + '@tailwindcss/oxide-linux-arm-gnueabihf@4.3.3': + resolution: {integrity: sha512-aAYUprJAJQWWbRrPvtjdroZ56Md+JM8pMiopS6xGEwDfLhqj+2ver2p4nU4Mb3CRqcMmNBjo8KkUgcxhkzVQGQ==} engines: {node: '>= 20'} cpu: [arm] os: [linux] - '@tailwindcss/oxide-linux-arm64-gnu@4.2.1': - resolution: {integrity: sha512-CTrwomI+c7n6aSSQlsPL0roRiNMDQ/YzMD9EjcR+H4f0I1SQ8QqIuPnsVp7QgMkC1Qi8rtkekLkOFjo7OlEFRQ==} + '@tailwindcss/oxide-linux-arm64-gnu@4.3.3': + resolution: {integrity: sha512-nDxldcEENOxZRzC2uu9jrutZdAAQtb+8WWDCSnWL1zvBk1+FN+x6MtDViPB5AJMfttVCUhehGWus3XBPgatM/w==} engines: {node: '>= 20'} cpu: [arm64] os: [linux] libc: [glibc] - '@tailwindcss/oxide-linux-arm64-musl@4.2.1': - resolution: {integrity: sha512-WZA0CHRL/SP1TRbA5mp9htsppSEkWuQ4KsSUumYQnyl8ZdT39ntwqmz4IUHGN6p4XdSlYfJwM4rRzZLShHsGAQ==} + '@tailwindcss/oxide-linux-arm64-musl@4.3.3': + resolution: {integrity: sha512-Md44bD6veX/PC5iyF8cDVnw4HBIANZepRZZ7a8DQOvkfo5WUBwcp6iAuCUz23u+4SUkhJlD3eL7hNdW8ezd/kA==} engines: {node: '>= 20'} cpu: [arm64] os: [linux] libc: [musl] - '@tailwindcss/oxide-linux-x64-gnu@4.2.1': - resolution: {integrity: sha512-qMFzxI2YlBOLW5PhblzuSWlWfwLHaneBE0xHzLrBgNtqN6mWfs+qYbhryGSXQjFYB1Dzf5w+LN5qbUTPhW7Y5g==} + '@tailwindcss/oxide-linux-x64-gnu@4.3.3': + resolution: {integrity: sha512-tx7us1muwOKAKWao2v/GaafFeQboE6aj88vC6ziN2NCGcRm8gWUhwjzg+YdVB1e4boAtdtma4L43onunI6NS4w==} engines: {node: '>= 20'} cpu: [x64] os: [linux] libc: [glibc] - '@tailwindcss/oxide-linux-x64-musl@4.2.1': - resolution: {integrity: sha512-5r1X2FKnCMUPlXTWRYpHdPYUY6a1Ar/t7P24OuiEdEOmms5lyqjDRvVY1yy9Rmioh+AunQ0rWiOTPE8F9A3v5g==} + '@tailwindcss/oxide-linux-x64-musl@4.3.3': + resolution: {integrity: sha512-SJxX60smvHgasZoBy11dX6YRjXJFovwWBoedhbQPOBzgFWBHGB+TVPWB9BxzR7TTxU8FQZAI2AyiNCMzFm8Img==} engines: {node: '>= 20'} cpu: [x64] os: [linux] libc: [musl] - '@tailwindcss/oxide-wasm32-wasi@4.2.1': - resolution: {integrity: sha512-MGFB5cVPvshR85MTJkEvqDUnuNoysrsRxd6vnk1Lf2tbiqNlXpHYZqkqOQalydienEWOHHFyyuTSYRsLfxFJ2Q==} + '@tailwindcss/oxide-wasm32-wasi@4.3.3': + resolution: {integrity: sha512-jx1+rPhY/5Ympkktd656HBWEBLxP7dH06losBLjjf5vgCODXvi9KhtftWcMIwTFIDqBr7cRnQkdLnAG+IOlGvQ==} engines: {node: '>=14.0.0'} cpu: [wasm32] bundledDependencies: @@ -574,129 +572,158 @@ packages: - '@emnapi/wasi-threads' - tslib - '@tailwindcss/oxide-win32-arm64-msvc@4.2.1': - resolution: {integrity: sha512-YlUEHRHBGnCMh4Nj4GnqQyBtsshUPdiNroZj8VPkvTZSoHsilRCwXcVKnG9kyi0ZFAS/3u+qKHBdDc81SADTRA==} + '@tailwindcss/oxide-win32-arm64-msvc@4.3.3': + resolution: {integrity: sha512-3rc292Ca2ceK6Ulcc/bAVnTs/3nDtoPhyEKlgPv+yQJQi/JS/AMJlqzxvlDacL1nekbrcf6bTqp/jV4qgnPxNQ==} engines: {node: '>= 20'} cpu: [arm64] os: [win32] - '@tailwindcss/oxide-win32-x64-msvc@4.2.1': - resolution: {integrity: sha512-rbO34G5sMWWyrN/idLeVxAZgAKWrn5LiR3/I90Q9MkA67s6T1oB0xtTe+0heoBvHSpbU9Mk7i6uwJnpo4u21XQ==} + '@tailwindcss/oxide-win32-x64-msvc@4.3.3': + resolution: {integrity: sha512-yJ0pwIVc/nYeGoV02WtsN8KYyLQv7kyI2wDnkezyJlGGjkd4QLwDGAwl47YpPJeuI0M0ObaXGSPjvWDPeTPggw==} engines: {node: '>= 20'} cpu: [x64] os: [win32] - '@tailwindcss/oxide@4.2.1': - resolution: {integrity: sha512-yv9jeEFWnjKCI6/T3Oq50yQEOqmpmpfzG1hcZsAOaXFQPfzWprWrlHSdGPEF3WQTi8zu8ohC9Mh9J470nT5pUw==} + '@tailwindcss/oxide@4.3.3': + resolution: {integrity: sha512-krXjAikiaFSPaK/FkAQT5UTx3VormQaiZ5hBFlJZ9UFQGB/rwg1MZIhHAG9smMQRTdyJxP6Qt5MwMtdyU5FWrA==} engines: {node: '>= 20'} - '@tailwindcss/vite@4.2.1': - resolution: {integrity: sha512-TBf2sJjYeb28jD2U/OhwdW0bbOsxkWPwQ7SrqGf9sVcoYwZj7rkXljroBO9wKBut9XnmQLXanuDUeqQK0lGg/w==} + '@tailwindcss/vite@4.3.3': + resolution: {integrity: sha512-yYU8cogLeSh/ms2jh8Fj7jaba/EWa7Ja6GoUqYZaraEuCI5YS6ms6ObZgjjedm+jm6XZjdNRWBpPP6Z86oOxcw==} peerDependencies: - vite: ^5.2.0 || ^6 || ^7 + vite: ^5.2.0 || ^6 || ^7 || ^8 - '@tanstack/table-core@8.21.3': - resolution: {integrity: sha512-ldZXEhOBb8Is7xLs01fR3YEc3DERiz5silj8tnGkFZytt1abEvl/GhUmCE0PMLaMPTa3Jk4HbKmRlHmu+gCftg==} - engines: {node: '>=12'} + '@tanstack/devtools-event-client@0.4.4': + resolution: {integrity: sha512-6T5Yop/793YI+H+5J8Hsyj4kCih9sl4t3ElLgKioW5hk3ocn+ZdSJ94tT7vL7uabxSugWYBZlOTMPzEw2puvQw==} + engines: {node: '>=18'} + hasBin: true - '@tanstack/virtual-core@3.13.21': - resolution: {integrity: sha512-ww+fmLHyCbPSf7JNbWZP3g7wl6SdNo3ah5Aiw+0e9FDErkVHLKprYUrwTm7dF646FtEkN/KkAKPYezxpmvOjxw==} + '@tanstack/form-core@1.33.5': + resolution: {integrity: sha512-3dfx9MBP0aq5sXKteikG629X9oviptrQj0IFRk9YGcb+lB7Kv5x8S17oOSk1wUWgjQZ4xVJEMbKwOAODymocgA==} - '@tanstack/vue-table@8.21.3': - resolution: {integrity: sha512-rusRyd77c5tDPloPskctMyPLFEQUeBzxdQ+2Eow4F7gDPlPOB1UnnhzfpdvqZ8ZyX2rRNGmqNnQWm87OI2OQPw==} - engines: {node: '>=12'} + '@tanstack/pacer-lite@0.1.1': + resolution: {integrity: sha512-y/xtNPNt/YeyoVxE/JCx+T7yjEzpezmbb+toK8DDD1P4m7Kzs5YR956+7OKexG3f8aXgC3rLZl7b1V+yNUSy5w==} + engines: {node: '>=18'} + + '@tanstack/store@0.11.2': + resolution: {integrity: sha512-sJ4mjol8uQsHV0gOJzzjwXfh2Fwm+Sz0+8deqiTm4jGbMdjzNSW+xZCFm0kUa870uhd8yi+DpKnZb5Kc4apa0Q==} + + '@tanstack/table-core@9.3.1': + resolution: {integrity: sha512-zQvlxSNi2qGM3L4jh85Z6hqMfdIR5qOfD9KHOU32nuKFJGN0RlJ7UvV1lsPb1fPVhpFYI0VP3jJGxLdLDpTKjQ==} + engines: {node: '>=20'} + + '@tanstack/virtual-core@3.19.10': + resolution: {integrity: sha512-/ltpH0qUCs5XQYE7fIhgA+aqgC5NzSfLTDQkC+N1wOOdvIYx1FH/kz2LGfSeanNbekqy3WBu6mNIbqXhxE/pew==} + + '@tanstack/vue-form@1.33.5': + resolution: {integrity: sha512-IvNlJ1LGFqt2pkwwCM+ltMWeBpF/qUeP9vlQwknMJphrIjJtZfmVr+VaN4BsOEbZQVK2vDkglqT+u64LTTjyoA==} + peerDependencies: + vue: ^3.4.0 + + '@tanstack/vue-store@0.11.2': + resolution: {integrity: sha512-wZmkfsvc0dNvOg/Xb/a7uTPKejleI0dR3HHi0t3ZyVM/23MJ7AC0bH6C7rp2seSY4mIYo0pLLbGJGZ/UpO93Zw==} + peerDependencies: + '@vue/composition-api': ^1.2.1 + vue: ^2.5.0 || ^3.0.0 + peerDependenciesMeta: + '@vue/composition-api': + optional: true + + '@tanstack/vue-table@9.3.1': + resolution: {integrity: sha512-7z7nS4UP91rLZ01suGxQBT611ogfBrs7/E+lsKeYS9XdHSRUAzclFvQ4B10F0bC/Q8BrzgXiNH3QzumK7b0Tow==} + engines: {node: '>=20'} peerDependencies: vue: '>=3.2' - '@tanstack/vue-virtual@3.13.21': - resolution: {integrity: sha512-zneUNdQTcUhoDl6+ek+/O4S9gSZRAc2q7VLscZ4WZnFfZcHc3M7OyVCfSDC3hGuwFqzfL8Cx5bZF6zbGCYwXmw==} + '@tanstack/vue-virtual@3.13.51': + resolution: {integrity: sha512-0Q57TrTPrNBhQsURZzbDQ4HVC+ukhX/7Ma2h1UXNECyRAHFFRrrEiETtH6TyUHm6E1fmei4HHhu4yAYiTkuviQ==} peerDependencies: vue: ^2.7.0 || ^3.0.0 - '@tauri-apps/api@2.10.1': - resolution: {integrity: sha512-hKL/jWf293UDSUN09rR69hrToyIXBb8CjGaWC7gfinvnQrBVvnLr08FeFi38gxtugAVyVcTa5/FD/Xnkb1siBw==} + '@tauri-apps/api@2.12.4': + resolution: {integrity: sha512-pVmfis+4zT8H0Qhp3xPZx9Ju7kpzvA6rr/PSJPyuwfvucTQ8sVt61NYU9KMkK703UFlYadFmjKA4HhybOtN0gg==} - '@tauri-apps/cli-darwin-arm64@2.10.0': - resolution: {integrity: sha512-avqHD4HRjrMamE/7R/kzJPcAJnZs0IIS+1nkDP5b+TNBn3py7N2aIo9LIpy+VQq0AkN8G5dDpZtOOBkmWt/zjA==} + '@tauri-apps/cli-darwin-arm64@2.12.4': + resolution: {integrity: sha512-OxqKSckIkoJ6vX2AjifIJpTasFC0Hbdoo7gEiCmAjauABO3U8m2MRsLWe+QTiBwDx1mIrvgA+rvI7tvDnbnHVg==} engines: {node: '>= 10'} cpu: [arm64] os: [darwin] - '@tauri-apps/cli-darwin-x64@2.10.0': - resolution: {integrity: sha512-keDmlvJRStzVFjZTd0xYkBONLtgBC9eMTpmXnBXzsHuawV2q9PvDo2x6D5mhuoMVrJ9QWjgaPKBBCFks4dK71Q==} + '@tauri-apps/cli-darwin-x64@2.12.4': + resolution: {integrity: sha512-fz76/77wMMJOaaUN6n3CybRG0P/MTEjjqYk81mGPyhi9m6Ue5T+LWBnJP/AkPooNatBPI2vtulGqomyMJ44lCQ==} engines: {node: '>= 10'} cpu: [x64] os: [darwin] - '@tauri-apps/cli-linux-arm-gnueabihf@2.10.0': - resolution: {integrity: sha512-e5u0VfLZsMAC9iHaOEANumgl6lfnJx0Dtjkd8IJpysZ8jp0tJ6wrIkto2OzQgzcYyRCKgX72aKE0PFgZputA8g==} + '@tauri-apps/cli-linux-arm-gnueabihf@2.12.4': + resolution: {integrity: sha512-K0w8Bof6Lt8QKLAYhRy0yy36ZH5iA/35VuSOvnxyjvCUZ+A3JKsUTlALviH/sVgAlisaPzzmeJSH8XOn4Q2HBA==} engines: {node: '>= 10'} cpu: [arm] os: [linux] - '@tauri-apps/cli-linux-arm64-gnu@2.10.0': - resolution: {integrity: sha512-YrYYk2dfmBs5m+OIMCrb+JH/oo+4FtlpcrTCgiFYc7vcs6m3QDd1TTyWu0u01ewsCtK2kOdluhr/zKku+KP7HA==} + '@tauri-apps/cli-linux-arm64-gnu@2.12.4': + resolution: {integrity: sha512-j5rOnjmRUwWrm1oosf0zBYl4D+fqydgUROFCk0hvKZxvbTT8SOERes0QB7rBV/XWNy+kM6pCKzb3oSm2sRglzg==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] libc: [glibc] - '@tauri-apps/cli-linux-arm64-musl@2.10.0': - resolution: {integrity: sha512-GUoPdVJmrJRIXFfW3Rkt+eGK9ygOdyISACZfC/bCSfOnGt8kNdQIQr5WRH9QUaTVFIwxMlQyV3m+yXYP+xhSVA==} + '@tauri-apps/cli-linux-arm64-musl@2.12.4': + resolution: {integrity: sha512-aOue3Fy6Jb7+oWcfAVUsVHfeTybSCt2CZoU06aO4jv4d2Ks/KnaTPwMAK3CLEduGOvBLY1lbBBrIyomqLLH5Sg==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] libc: [musl] - '@tauri-apps/cli-linux-riscv64-gnu@2.10.0': - resolution: {integrity: sha512-JO7s3TlSxshwsoKNCDkyvsx5gw2QAs/Y2GbR5UE2d5kkU138ATKoPOtxn8G1fFT1aDW4LH0rYAAfBpGkDyJJnw==} + '@tauri-apps/cli-linux-riscv64-gnu@2.12.4': + resolution: {integrity: sha512-Fx6GShY/RvST+t6LMlTHAwd66+1tEaAAGrnU7cysVfaiRP1BKng5Vl6rVmO6rSLOA1Sp1dRzNYM4f0OWQGb7qA==} engines: {node: '>= 10'} cpu: [riscv64] os: [linux] libc: [glibc] - '@tauri-apps/cli-linux-x64-gnu@2.10.0': - resolution: {integrity: sha512-Uvh4SUUp4A6DVRSMWjelww0GnZI3PlVy7VS+DRF5napKuIehVjGl9XD0uKoCoxwAQBLctvipyEK+pDXpJeoHng==} + '@tauri-apps/cli-linux-x64-gnu@2.12.4': + resolution: {integrity: sha512-tNzZSa79Z1nRpHxEpQ38lsAFpKlFlYvoXzjTV+lRRn/zSf3wrdUC3wnLcq2XEadchPu4ttK0zivhYzr+XGD19Q==} engines: {node: '>= 10'} cpu: [x64] os: [linux] libc: [glibc] - '@tauri-apps/cli-linux-x64-musl@2.10.0': - resolution: {integrity: sha512-AP0KRK6bJuTpQ8kMNWvhIpKUkQJfcPFeba7QshOQZjJ8wOS6emwTN4K5g/d3AbCMo0RRdnZWwu67MlmtJyxC1Q==} + '@tauri-apps/cli-linux-x64-musl@2.12.4': + resolution: {integrity: sha512-jmZ1pr0xEqUhuEusWtt9Yt+GF6lCnGHUmIL+KIknpbjK/XQ2qkhDBhaxK/JMHM2A8dAacVXCxXJk2EXpwhQUfA==} engines: {node: '>= 10'} cpu: [x64] os: [linux] libc: [musl] - '@tauri-apps/cli-win32-arm64-msvc@2.10.0': - resolution: {integrity: sha512-97DXVU3dJystrq7W41IX+82JEorLNY+3+ECYxvXWqkq7DBN6FsA08x/EFGE8N/b0LTOui9X2dvpGGoeZKKV08g==} + '@tauri-apps/cli-win32-arm64-msvc@2.12.4': + resolution: {integrity: sha512-fGo8pgEHZxdSYgZ/J98m1usJ7NHKmKbq7k6uT0x1rpOq6Bg2YVzzpaAblWk710f5LHYBJIL5QIFS5IrD+4gmTg==} engines: {node: '>= 10'} cpu: [arm64] os: [win32] - '@tauri-apps/cli-win32-ia32-msvc@2.10.0': - resolution: {integrity: sha512-EHyQ1iwrWy1CwMalEm9z2a6L5isQ121pe7FcA2xe4VWMJp+GHSDDGvbTv/OPdkt2Lyr7DAZBpZHM6nvlHXEc4A==} + '@tauri-apps/cli-win32-ia32-msvc@2.12.4': + resolution: {integrity: sha512-ezl8sQIcjYsye3TUnTH5VcWmWzccZASlQm7hlZt0dYyj1XD0qpVW47u2xYVrIKui0F8uQv/7QGZixdWiClBsbg==} engines: {node: '>= 10'} cpu: [ia32] os: [win32] - '@tauri-apps/cli-win32-x64-msvc@2.10.0': - resolution: {integrity: sha512-NTpyQxkpzGmU6ceWBTY2xRIEaS0ZLbVx1HE1zTA3TY/pV3+cPoPPOs+7YScr4IMzXMtOw7tLw5LEXo5oIG3qaQ==} + '@tauri-apps/cli-win32-x64-msvc@2.12.4': + resolution: {integrity: sha512-DJhDeCqjLDNMWYFy7ZS8cfeAHX2RK0RbQMLiA3bMVVkXRDJ4NBYiHriGqsUhsr9iG41aTb+dJGkFPmPw1gWwVw==} engines: {node: '>= 10'} cpu: [x64] os: [win32] - '@tauri-apps/cli@2.10.0': - resolution: {integrity: sha512-ZwT0T+7bw4+DPCSWzmviwq5XbXlM0cNoleDKOYPFYqcZqeKY31KlpoMW/MOON/tOFBPgi31a2v3w9gliqwL2+Q==} + '@tauri-apps/cli@2.12.4': + resolution: {integrity: sha512-JI9bHfudSRJikGOrjbXEm6RjzzvmKuLc+JixB1NE8hNzQkNlCCIC3pTES3uypkR9lxHrsbxzcnSwhI2IxBQphw==} engines: {node: '>= 10'} hasBin: true - '@tauri-apps/plugin-log@2.8.0': - resolution: {integrity: sha512-a+7rOq3MJwpTOLLKbL8d0qGZ85hgHw5pNOWusA9o3cf7cEgtYHiGY/+O8fj8MvywQIGqFv0da2bYQDlrqLE7rw==} + '@tauri-apps/plugin-log@2.10.1': + resolution: {integrity: sha512-QYgxDunUZP+Cd3ev+8ItOc3Q6uUFkEhRHdTolxUtlC/8SGsc5IyVjGgFclprkAx736HUCUYfh2vqUr3qwNfTTw==} - '@tauri-apps/plugin-opener@2.5.3': - resolution: {integrity: sha512-CCcUltXMOfUEArbf3db3kCE7Ggy1ExBEBl51Ko2ODJ6GDYHRp1nSNlQm5uNCFY5k7/ufaK5Ib3Du/Zir19IYQQ==} + '@tauri-apps/plugin-opener@2.7.2': + resolution: {integrity: sha512-LyybP1VSzTMQiaFYcsbAse47KpivlyXdLZMQzGLEnkgQacGESnpjAfFoQ6h5sqWgAdZDH9szYjPzHMpv0Sz+TQ==} '@types/chai@5.2.3': resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==} @@ -704,129 +731,134 @@ packages: '@types/deep-eql@4.0.2': resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==} - '@types/estree@1.0.8': - resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} + '@types/estree@1.0.10': + resolution: {integrity: sha512-lTyOMnOcmRBzdCv6/4zUZCrEhV0xrqQiUZBZtVcbmLHBXKLnN8q89v/wKRF/Q0HbwVsqbIG3o5HDoFl9sduM+g==} - '@types/node@25.3.3': - resolution: {integrity: sha512-DpzbrH7wIcBaJibpKo9nnSQL0MTRdnWttGyE5haGwK86xgMOkFLp7vEyfQPGLOJh5wNYiJ3V9PmUMDhV9u8kkQ==} + '@types/node@26.6.6': + resolution: {integrity: sha512-MzQj+8Kx3XI40OmUAE6Qoh58ZMNstNNMwn2nfd3qWsfe7nDNSqJNzes4njhz6AHwrSHOffSLwXU6yb474Q4aog==} '@types/web-bluetooth@0.0.21': resolution: {integrity: sha512-oIQLCGWtcFZy2JW77j9k8nHzAOpqMHLQejDA48XXMWH6tjCQHz5RCFz1bzsmROyL6PUm+LLnUiI4BCn221inxA==} - '@vee-validate/zod@4.15.1': - resolution: {integrity: sha512-329Z4TDBE5Vx0FdbA8S4eR9iGCFFUNGbxjpQ20ff5b5wGueScjocUIx9JHPa79LTG06RnlUR4XogQsjN4tecKA==} - peerDependencies: - zod: ^3.24.0 + '@types/whatwg-mimetype@3.0.2': + resolution: {integrity: sha512-c2AKvDT8ToxLIOUlN51gTiHXflsfIFisS4pO7pDPoKouJCESkhZnEy623gwP9laCy5lnLDAw1vAzu2vM2YLOrA==} - '@vitejs/plugin-vue@5.2.4': - resolution: {integrity: sha512-7Yx/SXSOcQq5HiiV3orevHUFn+pmMB4cgbEkDYgnkUWb0WfeQ/wa2yFv6D5ICiCQOVpjA7vYDXrC7AGO8yjDHA==} - engines: {node: ^18.0.0 || >=20.0.0} - peerDependencies: - vite: ^5.0.0 || ^6.0.0 - vue: ^3.2.25 + '@types/ws@8.18.2': + resolution: {integrity: sha512-67MQl+fpWKVTT1NYdnmo3U4sc/xPo/zQBncVnI74qmQa0z/b+1g6iYqNmGCPbxO+zz2aklb08a0oHfegiVd0/w==} - '@vitest/expect@4.0.18': - resolution: {integrity: sha512-8sCWUyckXXYvx4opfzVY03EOiYVxyNrHS5QxX3DAIi5dpJAAkyJezHCP77VMX4HKA2LDT/Jpfo8i2r5BE3GnQQ==} + '@vitejs/plugin-vue@6.0.10': + resolution: {integrity: sha512-b80aUQh3n+qEzzZm5oQp3rT2QImeCx58K6eFl/RkJ3d65KW0kTuv5lNsLRVAjbW9YRUpo/wzmvzMgV34bTAItA==} + engines: {node: ^20.19.0 || >=22.12.0} + peerDependencies: + vite: ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 + vue: ^3.2.25 || ^3.6.0-rc.10 - '@vitest/mocker@4.0.18': - resolution: {integrity: sha512-HhVd0MDnzzsgevnOWCBj5Otnzobjy5wLBe4EdeeFGv8luMsGcYqDuFRMcttKWZA5vVO8RFjexVovXvAM4JoJDQ==} + '@vitest/mocker@5.0.5': + resolution: {integrity: sha512-xLL3lkDegJZw+MGoGaCCsFayDZthGgpAtr3Vziyfc4p7xH89K7KRPcRBBh/kEYm0Aqkt5DtTmkYpBtRV/FrHZg==} peerDependencies: - msw: ^2.4.9 - vite: ^6.0.0 || ^7.0.0-0 + msw: ^2.4.9 || ^3.0.0 + vite: ^6.0.0 || ^7.0.0 || ^8.0.0 peerDependenciesMeta: msw: optional: true vite: optional: true - '@vitest/pretty-format@4.0.18': - resolution: {integrity: sha512-P24GK3GulZWC5tz87ux0m8OADrQIUVDPIjjj65vBXYG17ZeU3qD7r+MNZ1RNv4l8CGU2vtTRqixrOi9fYk/yKw==} - - '@vitest/runner@4.0.18': - resolution: {integrity: sha512-rpk9y12PGa22Jg6g5M3UVVnTS7+zycIGk9ZNGN+m6tZHKQb7jrP7/77WfZy13Y/EUDd52NDsLRQhYKtv7XfPQw==} + '@vitest/spy@5.0.5': + resolution: {integrity: sha512-lwJRExuH417257Q5wv9QYugN9IMJkoxcrKrYxGPQkBzl7wlNpTUxD/5yNX4ESKVUIShKPgkGchYPFMWOOk0wzw==} - '@vitest/snapshot@4.0.18': - resolution: {integrity: sha512-PCiV0rcl7jKQjbgYqjtakly6T1uwv/5BQ9SwBLekVg/EaYeQFPiXcgrC2Y7vDMA8dM1SUEAEV82kgSQIlXNMvA==} + '@volar/language-core@2.4.30': + resolution: {integrity: sha512-73fjwEV8wat6aOZ6U/y/hqtE30ocrIWECg67b0txGczOMNw+MffLmvJ5fnFfN9drVUEYZzLdaNo6VAy6G6avMw==} - '@vitest/spy@4.0.18': - resolution: {integrity: sha512-cbQt3PTSD7P2OARdVW3qWER5EGq7PHlvE+QfzSC0lbwO+xnt7+XH06ZzFjFRgzUX//JmpxrCu92VdwvEPlWSNw==} + '@volar/language-service@2.4.30': + resolution: {integrity: sha512-w/xDsASiqC71ggCOm7sTDXDNk/l72XEptodeRC68tgZ0e5QM4hM3zAqBGC7mBctvhrpYwKfMG+t9xcUo2hEFtA==} + peerDependencies: + typescript: <7 + peerDependenciesMeta: + typescript: + optional: true - '@vitest/utils@4.0.18': - resolution: {integrity: sha512-msMRKLMVLWygpK3u2Hybgi4MNjcYJvwTb0Ru09+fOyCXIgT5raYP041DRRdiJiI3k/2U6SEbAETB3YtBrUkCFA==} + '@volar/source-map@2.4.30': + resolution: {integrity: sha512-zym1ob/KwDDyN+0jGVtxwbxJSRRnUvv5HspCtz3yEfVVdCG4/dzxJktMGPAgemqRuVs81O0o/y0q3qOUErR8kg==} - '@volar/language-core@2.4.15': - resolution: {integrity: sha512-3VHw+QZU0ZG9IuQmzT68IyN4hZNd9GchGPhbD9+pa8CVv7rnoOZwo7T8weIbrRmihqy3ATpdfXFnqRrfPVK6CA==} + '@volar/typescript@2.4.30': + resolution: {integrity: sha512-/DgA5tgYFPfrLFuSjIMXMGHk1LQv2+P8eny6+BhGNzFJELWIW7UZlc56ZLIRFhLsPpMfIFxM8/HvOPBggrjTZg==} + peerDependencies: + typescript: <7 + peerDependenciesMeta: + typescript: + optional: true - '@volar/source-map@2.4.15': - resolution: {integrity: sha512-CPbMWlUN6hVZJYGcU/GSoHu4EnCHiLaXI9n8c9la6RaI9W5JHX+NqG+GSQcB0JdC2FIBLdZJwGsfKyBB71VlTg==} + '@vue/compiler-core@3.5.44': + resolution: {integrity: sha512-k34nCytbK8PQ1gR2mGROYOw6Pnv/mumqIdrZ3x2tbyXZDWm7ViKGLTfdB+DRrWqEQsLgGJvn0WN9jBif5zCTLQ==} - '@volar/typescript@2.4.15': - resolution: {integrity: sha512-2aZ8i0cqPGjXb4BhkMsPYDkkuc2ZQ6yOpqwAuNwUoncELqoy5fRgOQtLR9gB0g902iS0NAkvpIzs27geVyVdPg==} + '@vue/compiler-dom@3.5.44': + resolution: {integrity: sha512-W5AoBFWdw1lQAKiHowkfgFKFab82oSRT6sGsPfw/q1GRjsy/ZJFLHGhJyBRIFN9APLTUduDH9U2JAiPOIyKuYg==} - '@vue/compiler-core@3.5.29': - resolution: {integrity: sha512-cuzPhD8fwRHk8IGfmYaR4eEe4cAyJEL66Ove/WZL7yWNL134nqLddSLwNRIsFlnnW1kK+p8Ck3viFnC0chXCXw==} + '@vue/compiler-sfc@3.5.44': + resolution: {integrity: sha512-dzHmQ4jXTpdr5FjIQUeykVakN/uzqFbUo9/C5ikBFg3a3PeM6uRxeczx1ZbXBML0/zJstEh3ygYipzR5Z7b2Pw==} - '@vue/compiler-dom@3.5.29': - resolution: {integrity: sha512-n0G5o7R3uBVmVxjTIYcz7ovr8sy7QObFG8OQJ3xGCDNhbG60biP/P5KnyY8NLd81OuT1WJflG7N4KWYHaeeaIg==} + '@vue/compiler-ssr@3.5.44': + resolution: {integrity: sha512-xr5gb1JVHlfXJwSlRz1FeP6uxoazdzHuMTVLE7up+4Vrqxk219CvxLPxD9NXBt47yvZW7+p4DXfyp4O1fkByIA==} - '@vue/compiler-sfc@3.5.29': - resolution: {integrity: sha512-oJZhN5XJs35Gzr50E82jg2cYdZQ78wEwvRO6Y63TvLVTc+6xICzJHP1UIecdSPPYIbkautNBanDiWYa64QSFIA==} + '@vue/devtools-api@8.2.1': + resolution: {integrity: sha512-6u4vXBlIBAC1wMplIZgpyPn7uh/s4Bf6F5bMzvLv+EdJ0aHs/+4B7Ygv864EStQSjRbsRzTko/kUG1A1IejQ3A==} - '@vue/compiler-ssr@3.5.29': - resolution: {integrity: sha512-Y/ARJZE6fpjzL5GH/phJmsFwx3g6t2KmHKHx5q+MLl2kencADKIrhH5MLF6HHpRMmlRAYBRSvv347Mepf1zVNw==} + '@vue/devtools-kit@8.2.1': + resolution: {integrity: sha512-FIGIuq3AWReEpbAHY/cRGeHDfI0qOb8OCQ3YjbEAX04uaxIDbGc9rhkbVcG7rnfHPXE3RsU5KrWOu9V/okd8AQ==} - '@vue/compiler-vue2@2.7.16': - resolution: {integrity: sha512-qYC3Psj9S/mfu9uVi5WvNZIzq+xnXMhOwbTFKKDD7b1lhpnn71jXSFdTQ+WsIEk0ONCd7VV2IMm7ONl6tbQ86A==} + '@vue/devtools-shared@8.2.1': + resolution: {integrity: sha512-Fkac7lUdGReh6pVOi3AYPRGe82LQqRmAfThW7RRligOAP0ZA/Z1z9XLHDM9dv34pV2HRc79DK8uKPeG2fLnA/g==} - '@vue/devtools-api@7.7.9': - resolution: {integrity: sha512-kIE8wvwlcZ6TJTbNeU2HQNtaxLx3a84aotTITUuL/4bzfPxzajGBOoqjMhwZJ8L9qFYDU/lAYMEEm11dnZOD6g==} + '@vue/language-core@3.3.14': + resolution: {integrity: sha512-kRB+3hHg0c+BdD1XLAvwjVMQd5pC05zijSlPDRI3ERvC/s04JlRs5upy2KjeMg/9m9oD5zuMVh9yVvkKfZqoJg==} - '@vue/devtools-kit@7.7.9': - resolution: {integrity: sha512-PyQ6odHSgiDVd4hnTP+aDk2X4gl2HmLDfiyEnn3/oV+ckFDuswRs4IbBT7vacMuGdwY/XemxBoh302ctbsptuA==} + '@vue/reactivity@3.5.44': + resolution: {integrity: sha512-u+QJdKeBBEADkPdevsL+6E12QlYVfBD8Zcw5qjDQKtamI6l14KyqazJMobhCKelhWN5zpyKUiEDoTyHE93cqWw==} - '@vue/devtools-shared@7.7.9': - resolution: {integrity: sha512-iWAb0v2WYf0QWmxCGy0seZNDPdO3Sp5+u78ORnyeonS6MT4PC7VPrryX2BpMJrwlDeaZ6BD4vP4XKjK0SZqaeA==} + '@vue/runtime-core@3.5.44': + resolution: {integrity: sha512-uEa/ndwjhrrhnOfb2p+sZy6zeoamJOISqaLSJGk/8R6nABT1ZldpzcJoPzKj+zHVomVBPcmQYyC2r4LtvDV6CQ==} - '@vue/language-core@2.2.12': - resolution: {integrity: sha512-IsGljWbKGU1MZpBPN+BvPAdr55YPkj2nB/TBNGNC32Vy2qLG25DYu/NBN2vNtZqdRbTRjaoYrahLrToim2NanA==} - peerDependencies: - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true + '@vue/runtime-dom@3.5.44': + resolution: {integrity: sha512-3fc4ARcXVhVBaJ3ujWiNZJARD6pkyRcwG+ZYhMzjKtKUp7MAY0HLwr7r3Smep3Li6ZaWT6ewdmx8XqdaD5oNZg==} - '@vue/reactivity@3.5.29': - resolution: {integrity: sha512-zcrANcrRdcLtmGZETBxWqIkoQei8HaFpZWx/GHKxx79JZsiZ8j1du0VUJtu4eJjgFvU/iKL5lRXFXksVmI+5DA==} + '@vue/server-renderer@3.5.44': + resolution: {integrity: sha512-R4S1UoH4h5Frrauv8Z5XE5TxV9oBiUSgWIC4TyVK1FK+RocRnOLwFA5Xcmo2011RlgJp+AbZCnw6afrhlgRuUQ==} - '@vue/runtime-core@3.5.29': - resolution: {integrity: sha512-8DpW2QfdwIWOLqtsNcds4s+QgwSaHSJY/SUe04LptianUQ/0xi6KVsu/pYVh+HO3NTVvVJjIPL2t6GdeKbS4Lg==} + '@vue/shared@3.5.44': + resolution: {integrity: sha512-H82XLH0DjmSOdbEwO15x3QTo1f5PWEtv237eLpYKc0mqLu+B8hnq16lFWmuBmNurkeiG3wr+oM1FhOll/YVFRg==} - '@vue/runtime-dom@3.5.29': - resolution: {integrity: sha512-AHvvJEtcY9tw/uk+s/YRLSlxxQnqnAkjqvK25ZiM4CllCZWzElRAoQnCM42m9AHRLNJ6oe2kC5DCgD4AUdlvXg==} + '@vueuse/core@14.4.0': + resolution: {integrity: sha512-X4WHz1HlCzCBoYXesUkifzzWBAcZgXG8Fi5iNPQg/epdzOB3gu8Fawj3hvuwYR1nGcXGnvxwYYcUC/71++svtQ==} + peerDependencies: + vue: ^3.5.0 - '@vue/server-renderer@3.5.29': - resolution: {integrity: sha512-G/1k6WK5MusLlbxSE2YTcqAAezS+VuwHhOvLx2KnQU7G2zCH6KIb+5Wyt6UjMq7a3qPzNEjJXs1hvAxDclQH+g==} + '@vueuse/core@15.0.0': + resolution: {integrity: sha512-Zv67h5mrk7YHDJsRObx+DXaCBz6RKBUpxqLesJ+3xZNPG6yuQs7ja7kg3veFrrouYLWAqydQ7CwGEbLVxB43Ng==} + engines: {node: '>=22'} peerDependencies: - vue: 3.5.29 + vue: ^3.5.0 + + '@vueuse/metadata@14.4.0': + resolution: {integrity: sha512-swx/255R6JyHZFJhx845iz5CRWDZdCfvkZOpACWc5+c5WHcG24mv8gUT1WIdFQaHt6dq79rvILd9QnCWiyVm9g==} - '@vue/shared@3.5.29': - resolution: {integrity: sha512-w7SR0A5zyRByL9XUkCfdLs7t9XOHUyJ67qPGQjOou3p6GvBeBW+AVjUUmlxtZ4PIYaRvE+1LmK44O4uajlZwcg==} + '@vueuse/metadata@15.0.0': + resolution: {integrity: sha512-sfWE5LADAKJ0KnjtGliMdSN8Ij/L7uP6rXIZlgPloAI4zvvefbw6H5Lt7KJA6RqQOYgVqWF5Guj50HHMIzygBg==} + engines: {node: '>=22'} - '@vueuse/core@14.2.1': - resolution: {integrity: sha512-3vwDzV+GDUNpdegRY6kzpLm4Igptq+GA0QkJ3W61Iv27YWwW/ufSlOfgQIpN6FZRMG0mkaz4gglJRtq5SeJyIQ==} + '@vueuse/shared@14.4.0': + resolution: {integrity: sha512-JRgY90Sz8DDtPMsaDflvPMp9xYk69JZAmbuDvAquUVXKr2gEjqtzGNTTthLfckH0BzBqvnu31gb4a8TGLRe79g==} peerDependencies: vue: ^3.5.0 - '@vueuse/metadata@14.2.1': - resolution: {integrity: sha512-1ButlVtj5Sb/HDtIy1HFr1VqCP4G6Ypqt5MAo0lCgjokrk2mvQKsK2uuy0vqu/Ks+sHfuHo0B9Y9jn9xKdjZsw==} - - '@vueuse/shared@14.2.1': - resolution: {integrity: sha512-shTJncjV9JTI4oVNyF1FQonetYAiTBd+Qj7cY89SWbXSkx7gyhrgtEdF2ZAVWS1S3SHlaROO6F2IesJxQEkZBw==} + '@vueuse/shared@15.0.0': + resolution: {integrity: sha512-vbNXhGrbV53xPOMSyOzMHQRvhMgZf7oNDw2RChUetVxwTIEkInQ1joZ9JbT0G3z2n2AJNjvmCWeM9ySYrnvogA==} + engines: {node: '>=22'} peerDependencies: vue: ^3.5.0 - alien-signals@1.0.13: - resolution: {integrity: sha512-OGj9yyTnJEttvzhTUWuscOvtqxq5vrhF7vL9oS0xJ2mK0ItPYP1/y+vCFebfxoEyAz0++1AIwJ5CMr+Fk3nDmg==} + alien-signals@3.2.1: + resolution: {integrity: sha512-I8FjmltrfnDFoZedi5CG8DghVYNhzb/Ijluz7tCSJH0xpd0484Kowhbb1XDYOxfJpU1p5wnM2X54dA+IfGyD1g==} aria-hidden@1.2.6: resolution: {integrity: sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==} @@ -836,17 +868,15 @@ packages: resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} engines: {node: '>=12'} - balanced-match@1.0.2: - resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} - birpc@2.9.0: resolution: {integrity: sha512-KrayHS5pBi69Xi9JmvoqrIgYGDkD6mcSe/i6YKi3w5kekCLzrX4+nawcXqrj2tIp50Kw/mT/s3p+GVK0A0sKxw==} - brace-expansion@2.0.2: - resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==} + buffer-image-size@0.6.4: + resolution: {integrity: sha512-nEh+kZOPY1w+gcCMobZ6ETUp9WfibndnosbpwB1iJk/8Gt5ZF2bhS6+B6bPYz424KtwsR6Rflc3tCz1/ghX2dQ==} + engines: {node: '>=4.0'} - chai@6.2.2: - resolution: {integrity: sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==} + chai@6.3.0: + resolution: {integrity: sha512-XWAtwJ6OHO+tj0EKCs0Y2UamnyOxseZWltU4x2U2wh8g4AigdjwvtUjvLP2tqkA/avxHEtzxNaqGq/YGNwckKg==} engines: {node: '>=18'} class-variance-authority@0.7.1: @@ -859,41 +889,29 @@ packages: codemirror@6.0.2: resolution: {integrity: sha512-VhydHotNW5w1UGK0Qj96BwSk/Zqbp9WbnyK2W/eVMv4QyF41INRGpjUhFJY7/uDNuudSc33a/PKr4iDqRduvHw==} - copy-anything@4.0.5: - resolution: {integrity: sha512-7Vv6asjS4gMOuILabD3l739tsaxFQmC+a7pLZm02zyvs8p977bL3zEgq3yDk5rn9B0PbYgIv++jmHcuUab4RhA==} - engines: {node: '>=18'} - - crelt@1.0.6: - resolution: {integrity: sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==} + crelt@1.0.7: + resolution: {integrity: sha512-aK6BbWfhf4U/wCcLHKPJl/xa6VkVstRaPywWtMKGwuOLc/wZTyQYuoxgvZnNsBvv7Kg3YTBQYYBCggcviQczuA==} csstype@3.2.3: resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} - de-indent@1.0.2: - resolution: {integrity: sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==} - - defu@6.1.4: - resolution: {integrity: sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==} + defu@6.1.7: + resolution: {integrity: sha512-7z22QmUWiQ/2d0KkdYmANbRUVABpZ9SNYyH5vx6PZ+nE5bcC0l7uFvEfHlyld/HcGBFTL536ClDt3DEcSlEJAQ==} detect-libc@2.1.2: resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} engines: {node: '>=8'} - enhanced-resolve@5.20.0: - resolution: {integrity: sha512-/ce7+jQ1PQ6rVXwe+jKEg5hW5ciicHwIQUagZkp6IufBoY3YDgdTTY1azVs0qoRgVmvsNB+rbjLJxDAeHHtwsQ==} + enhanced-resolve@5.26.0: + resolution: {integrity: sha512-9vhedylFonb2YGogzUKX6+Ja72gOJbN1QHAqdrvqLwhdl/QWbKopzoUC9EbQNVsAns/bx/4uyqalrQAoy1IByw==} engines: {node: '>=10.13.0'} entities@7.0.1: resolution: {integrity: sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==} engines: {node: '>=0.12'} - es-module-lexer@1.7.0: - resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==} - - esbuild@0.25.12: - resolution: {integrity: sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==} - engines: {node: '>=18'} - hasBin: true + es-module-lexer@2.3.2: + resolution: {integrity: sha512-poHGpORABojJJucnV9KbOavETW8lBVnphkW77ER5/BQ5Fz7oXSoCNek7IH3vR5nRjdsEz926ibFYX8KtLQmdyw==} estree-walker@2.0.2: resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} @@ -901,8 +919,8 @@ packages: estree-walker@3.0.3: resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} - expect-type@1.3.0: - resolution: {integrity: sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==} + expect-type@1.4.0: + resolution: {integrity: sha512-KfYbmpRm0VbLjEvVa9yGwCi9GI34xvi7A/HXYWQO65CSD2u3MczUJSuwXKFIxlGsgBQizV9q5J9NHj4VG0n+pA==} engines: {node: '>=12.0.0'} fdir@6.5.0: @@ -922,263 +940,300 @@ packages: graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} - he@1.2.0: - resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==} - hasBin: true + happy-dom@21.0.4: + resolution: {integrity: sha512-tFm+GaLBgo6o0awHnvHaNoraVo27qJ+2tZEtZ+cIa08hXbeitrxAJajLI05hsUy/2/YTKcUjQYzHe5mCcraLBw==} + engines: {node: '>=20.0.0'} hookable@5.5.3: resolution: {integrity: sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==} - is-what@5.5.0: - resolution: {integrity: sha512-oG7cgbmg5kLYae2N5IVd3jm2s+vldjxJzK1pcu9LfpGuQ93MQSzo0okvRna+7y5ifrD+20FE8FvjusyGaz14fw==} - engines: {node: '>=18'} - - jiti@2.6.1: - resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==} + jiti@2.7.0: + resolution: {integrity: sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==} hasBin: true - lightningcss-android-arm64@1.31.1: - resolution: {integrity: sha512-HXJF3x8w9nQ4jbXRiNppBCqeZPIAfUo8zE/kOEGbW5NZvGc/K7nMxbhIr+YlFlHW5mpbg/YFPdbnCh1wAXCKFg==} + lightningcss-android-arm64@1.32.0: + resolution: {integrity: sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [android] + + lightningcss-android-arm64@1.33.0: + resolution: {integrity: sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [android] - lightningcss-darwin-arm64@1.31.1: - resolution: {integrity: sha512-02uTEqf3vIfNMq3h/z2cJfcOXnQ0GRwQrkmPafhueLb2h7mqEidiCzkE4gBMEH65abHRiQvhdcQ+aP0D0g67sg==} + lightningcss-darwin-arm64@1.32.0: + resolution: {integrity: sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [darwin] + + lightningcss-darwin-arm64@1.33.0: + resolution: {integrity: sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg==} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [darwin] - lightningcss-darwin-x64@1.31.1: - resolution: {integrity: sha512-1ObhyoCY+tGxtsz1lSx5NXCj3nirk0Y0kB/g8B8DT+sSx4G9djitg9ejFnjb3gJNWo7qXH4DIy2SUHvpoFwfTA==} + lightningcss-darwin-x64@1.32.0: + resolution: {integrity: sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [darwin] + + lightningcss-darwin-x64@1.33.0: + resolution: {integrity: sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [darwin] - lightningcss-freebsd-x64@1.31.1: - resolution: {integrity: sha512-1RINmQKAItO6ISxYgPwszQE1BrsVU5aB45ho6O42mu96UiZBxEXsuQ7cJW4zs4CEodPUioj/QrXW1r9pLUM74A==} + lightningcss-freebsd-x64@1.32.0: + resolution: {integrity: sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [freebsd] + + lightningcss-freebsd-x64@1.33.0: + resolution: {integrity: sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [freebsd] - lightningcss-linux-arm-gnueabihf@1.31.1: - resolution: {integrity: sha512-OOCm2//MZJ87CdDK62rZIu+aw9gBv4azMJuA8/KB74wmfS3lnC4yoPHm0uXZ/dvNNHmnZnB8XLAZzObeG0nS1g==} + lightningcss-linux-arm-gnueabihf@1.32.0: + resolution: {integrity: sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==} + engines: {node: '>= 12.0.0'} + cpu: [arm] + os: [linux] + + lightningcss-linux-arm-gnueabihf@1.33.0: + resolution: {integrity: sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ==} engines: {node: '>= 12.0.0'} cpu: [arm] os: [linux] - lightningcss-linux-arm64-gnu@1.31.1: - resolution: {integrity: sha512-WKyLWztD71rTnou4xAD5kQT+982wvca7E6QoLpoawZ1gP9JM0GJj4Tp5jMUh9B3AitHbRZ2/H3W5xQmdEOUlLg==} + lightningcss-linux-arm64-gnu@1.32.0: + resolution: {integrity: sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + lightningcss-linux-arm64-gnu@1.33.0: + resolution: {integrity: sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg==} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [linux] libc: [glibc] - lightningcss-linux-arm64-musl@1.31.1: - resolution: {integrity: sha512-mVZ7Pg2zIbe3XlNbZJdjs86YViQFoJSpc41CbVmKBPiGmC4YrfeOyz65ms2qpAobVd7WQsbW4PdsSJEMymyIMg==} + lightningcss-linux-arm64-musl@1.32.0: + resolution: {integrity: sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + libc: [musl] + + lightningcss-linux-arm64-musl@1.33.0: + resolution: {integrity: sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ==} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [linux] libc: [musl] - lightningcss-linux-x64-gnu@1.31.1: - resolution: {integrity: sha512-xGlFWRMl+0KvUhgySdIaReQdB4FNudfUTARn7q0hh/V67PVGCs3ADFjw+6++kG1RNd0zdGRlEKa+T13/tQjPMA==} + lightningcss-linux-x64-gnu@1.32.0: + resolution: {integrity: sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + libc: [glibc] + + lightningcss-linux-x64-gnu@1.33.0: + resolution: {integrity: sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [linux] libc: [glibc] - lightningcss-linux-x64-musl@1.31.1: - resolution: {integrity: sha512-eowF8PrKHw9LpoZii5tdZwnBcYDxRw2rRCyvAXLi34iyeYfqCQNA9rmUM0ce62NlPhCvof1+9ivRaTY6pSKDaA==} + lightningcss-linux-x64-musl@1.32.0: + resolution: {integrity: sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + libc: [musl] + + lightningcss-linux-x64-musl@1.33.0: + resolution: {integrity: sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [linux] libc: [musl] - lightningcss-win32-arm64-msvc@1.31.1: - resolution: {integrity: sha512-aJReEbSEQzx1uBlQizAOBSjcmr9dCdL3XuC/6HLXAxmtErsj2ICo5yYggg1qOODQMtnjNQv2UHb9NpOuFtYe4w==} + lightningcss-win32-arm64-msvc@1.32.0: + resolution: {integrity: sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [win32] + + lightningcss-win32-arm64-msvc@1.33.0: + resolution: {integrity: sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA==} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [win32] - lightningcss-win32-x64-msvc@1.31.1: - resolution: {integrity: sha512-I9aiFrbd7oYHwlnQDqr1Roz+fTz61oDDJX7n9tYF9FJymH1cIN1DtKw3iYt6b8WZgEjoNwVSncwF4wx/ZedMhw==} + lightningcss-win32-x64-msvc@1.32.0: + resolution: {integrity: sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [win32] + + lightningcss-win32-x64-msvc@1.33.0: + resolution: {integrity: sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [win32] - lightningcss@1.31.1: - resolution: {integrity: sha512-l51N2r93WmGUye3WuFoN5k10zyvrVs0qfKBhyC5ogUQ6Ew6JUSswh78mbSO+IU3nTWsyOArqPCcShdQSadghBQ==} + lightningcss@1.32.0: + resolution: {integrity: sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==} engines: {node: '>= 12.0.0'} - lucide-vue-next@0.563.0: - resolution: {integrity: sha512-zsE/lCKtmaa7bGfhSpN84br1K9YoQ5pCN+2oKWjQQG3Lo6ufUUKBuHSjNFI6RvUevxaajNXb8XwFUKeTXG3sIA==} - peerDependencies: - vue: '>=3.0.1' + lightningcss@1.33.0: + resolution: {integrity: sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==} + engines: {node: '>= 12.0.0'} magic-string@0.30.21: resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} - minimatch@9.0.9: - resolution: {integrity: sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==} - engines: {node: '>=16 || 14 >=14.17'} - - mitt@3.0.1: - resolution: {integrity: sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==} + magic-string@1.4.5: + resolution: {integrity: sha512-oLLC2+YvQUfAa+vxoufE/yc1icb87M9iXIRqAoR/2EhE/8cb3SF/+Gi0c2By1B+5Vz2e2vQtUvHiu1+63Znh+w==} muggle-string@0.4.1: resolution: {integrity: sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==} - nanoid@3.3.11: - resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} + nanoid@3.3.20: + resolution: {integrity: sha512-uKdg2G3GNCKQn9byYOpxbGqrT2fGO5KRt5J/8b3pok8rT6qxGWF6hxMyJiEYtAf+FVyYuD9hRaDqX5uPFYJ4ZQ==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true - obug@2.1.1: - resolution: {integrity: sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==} + nostics@1.3.0: + resolution: {integrity: sha512-wzRsrJwNlFbqIRQt8aCeL2WE8USip/uuvGSOZV1D02CxX8ABTMbw8IywKjH7yWHETMXbWJI4OBpihg9rmz2Vyg==} + + obug@3.0.0: + resolution: {integrity: sha512-5vvB5+W7ePv+p3uqxi+RcW1XAzLW0/hxt3/4X4Lc4qHudzOhmBiBwOY6DRob4WnanAEGvNcLjF+KNOufrUoEQw==} + engines: {node: '>=12.20.0'} - ohash@2.0.11: - resolution: {integrity: sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==} + ohash@2.0.12: + resolution: {integrity: sha512-65S/5gk9YSsaRjcyf7Nfa6h/d3E8/1gslpXfI4W7Dxn/oap8IKRuNT5VXkLQ1YFKIEg4apRY4Pj6aiwFzrDdmw==} path-browserify@1.0.1: resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==} - pathe@2.0.3: - resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} - - perfect-debounce@1.0.0: - resolution: {integrity: sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==} + perfect-debounce@2.1.0: + resolution: {integrity: sha512-LjgdTytVFXeUgtHZr9WYViYSM/g8MkcTPYDlPa3cDqMirHjKiSZPYd6DoL7pK8AJQr+uWkQvCjHNdiMqsrJs+g==} picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} - picomatch@4.0.3: - resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} + picomatch@4.0.7: + resolution: {integrity: sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==} engines: {node: '>=12'} - pinia@3.0.4: - resolution: {integrity: sha512-l7pqLUFTI/+ESXn6k3nu30ZIzW5E2WZF/LaHJEpoq6ElcLD+wduZoB2kBN19du6K/4FDpPMazY2wJr+IndBtQw==} + pinia@4.1.0: + resolution: {integrity: sha512-+5/HMuVjYQfKlCD5xQ7zfXn1xUzYFEa7pd0DQK5SaMfZCaFRTHlaH/c02N1FoGOdlzSiwf5EOPgJx//1QDw6gw==} peerDependencies: - typescript: '>=4.5.0' + '@vue/devtools-api': ^8.2.1 + typescript: '>=5.6.0' vue: ^3.5.11 peerDependenciesMeta: typescript: optional: true - postcss@8.5.6: - resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} + postcss@8.5.32: + resolution: {integrity: sha512-LJlKCCAUFKOuZxipUdYw1NzIITEsPcgyOG9LkvRJvuKj/bRzbuRY0ARXvL0pWGpsZvn8jku+LMep2eU0seHp8g==} engines: {node: ^10 || ^12 || >=14} - reka-ui@2.8.2: - resolution: {integrity: sha512-8lTKcJhmG+D3UyJxhBnNnW/720sLzm0pbA9AC1MWazmJ5YchJAyTSl+O00xP/kxBmEN0fw5JqWVHguiFmsGjzA==} + reka-ui@2.11.0: + resolution: {integrity: sha512-ilkpm4bIAol4jwzm4tpm3WVcffyGOpqksNZL1k1qO9QmIwAA+q8jroxjkUGLEn/GddfdExFfvofWfQiKZszUeQ==} peerDependencies: - vue: '>= 3.2.0' - - rfdc@1.4.1: - resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==} + vue: '>= 3.4.0' - rollup@4.59.0: - resolution: {integrity: sha512-2oMpl67a3zCH9H79LeMcbDhXW/UmWG/y2zuqnF2jQq5uq9TbM9TVyXvA4+t+ne2IIkBdrLpAaRQAvo7YI/Yyeg==} - engines: {node: '>=18.0.0', npm: '>=8.0.0'} + rolldown@1.2.16: + resolution: {integrity: sha512-bWvlMpFKiTfDKfnK7FC2MPgPeWCf/Kpwsq49ApdW+FQaBZb6O6dZ4MAPbPUjp7hYQrw/RobqCzn64BRrQecGHw==} + engines: {node: ^20.19.0 || >=22.12.0} hasBin: true - siginfo@2.0.0: - resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} - - source-map-js@1.2.1: - resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} - engines: {node: '>=0.10.0'} - - speakingurl@14.0.1: - resolution: {integrity: sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ==} + source-map-js@1.2.2: + resolution: {integrity: sha512-KGj/8Y43x35aZVDtt+J4mK1hoLGHULMYfSkODJNQjNDC3oW1PqPoxMwo0pLUsWM/UEGzON/NxeHywEfNXNP3Vw==} engines: {node: '>=0.10.0'} - stackback@0.0.2: - resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} - - std-env@3.10.0: - resolution: {integrity: sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==} + std-env@4.3.0: + resolution: {integrity: sha512-OtU/EgQ1kIm5KwqQpBC6ZEMXrZRui11w8zgfTWp8cdO9B8OaPsbA8bTHO2P+HNo1VlUTGMVBwPhydu6poeXiag==} - style-mod@4.1.3: - resolution: {integrity: sha512-i/n8VsZydrugj3Iuzll8+x/00GH2vnYsk1eomD8QiRrSAeW6ItbCQDtfXCeJHd0iwiNagqjQkvpvREEPtW3IoQ==} + style-mod@4.1.4: + resolution: {integrity: sha512-XXWIQt633/EpAFx8aZDOTjBzrCaGmhvEQlQo6MVPfa2OzO2cWo+4hV9h+6UkHYlXGfy+ODXKUdP7Pthmcu5ATw==} - superjson@2.2.6: - resolution: {integrity: sha512-H+ue8Zo4vJmV2nRjpx86P35lzwDT3nItnIsocgumgr0hHMQ+ZGq5vrERg9kJBo5AWGmxZDhzDo+WVIJqkB0cGA==} - engines: {node: '>=16'} + tailwind-merge@3.7.0: + resolution: {integrity: sha512-XPPUyAc+cvspz3lHTcR/QgPfW2A0lv/xQNIjX3HGhLR+Nq2lHaLq5MtTesHn8GUr3W3DguT2KT5x3NVgRtYwmA==} - tailwind-merge@3.5.0: - resolution: {integrity: sha512-I8K9wewnVDkL1NTGoqWmVEIlUcB9gFriAEkXkfCjX5ib8ezGxtR3xD7iZIxrfArjEsH7F1CHD4RFUtxefdqV/A==} + tailwindcss@4.3.3: + resolution: {integrity: sha512-gOhV3P7ufE62QDGg1zVaTgCR+EtPv92k2nIhVcVKcLmxT1sUBsQGhnZj175j+MqRt4zLF7ic+sCYjfhxMxj7YQ==} - tailwindcss@4.2.1: - resolution: {integrity: sha512-/tBrSQ36vCleJkAOsy9kbNTgaxvGbyOamC30PRePTQe/o1MFwEKHQk4Cn7BNGaPtjp+PuUrByJehM1hgxfq4sw==} - - tapable@2.3.0: - resolution: {integrity: sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==} + tapable@2.3.3: + resolution: {integrity: sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==} engines: {node: '>=6'} - tinybench@2.9.0: - resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} + tinybench@6.2.3: + resolution: {integrity: sha512-Al01C2JIBnO2CXCAPbpAl6sUiOZHeThGtHYNTa51JRw9PyWOCdr+9Ong05R4qg/Sk3+H645E8ov1h2c58ityNw==} + engines: {node: '>=20.0.0'} - tinyexec@1.0.2: - resolution: {integrity: sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==} + tinyexec@1.3.1: + resolution: {integrity: sha512-GCvB3aoys96IuDFBMcTB46JOR6mdMtAToqwiW8JlWhsoh1mhHi/xn9ss/Dg7N555GiJyEt2qzoG/NHCwM6h1EA==} engines: {node: '>=18'} - tinyglobby@0.2.15: - resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} + tinyglobby@0.2.17: + resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==} engines: {node: '>=12.0.0'} - tinyrainbow@3.0.3: - resolution: {integrity: sha512-PSkbLUoxOFRzJYjjxHJt9xro7D+iilgMX/C9lawzVuYiIdcihh9DXmVibBe8lmcFrRi/VzlPjBxbN7rH24q8/Q==} - engines: {node: '>=14.0.0'} - - tslib@2.8.1: - resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + tslib@2.9.1: + resolution: {integrity: sha512-a4Ux9VKuIMHsOS7ENbEq6hK2ZGYIT27Rtl0E4eD9KqwxhKiQeMZE/9xb3IdYwWaSOyIBQLExhQgXDTYoiCxnWA==} tw-animate-css@1.4.0: resolution: {integrity: sha512-7bziOlRqH0hJx80h/3mbicLW7o8qLsH5+RaLR2t+OHM3D0JlWGODQKQ4cxbK7WlvmUxpcj6Kgu6EKqjrGFe3QQ==} - type-fest@4.41.0: - resolution: {integrity: sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==} - engines: {node: '>=16'} - - typescript@5.6.3: - resolution: {integrity: sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==} + typescript@6.0.3: + resolution: {integrity: sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==} engines: {node: '>=14.17'} hasBin: true - undici-types@7.18.2: - resolution: {integrity: sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==} - - vee-validate@4.15.1: - resolution: {integrity: sha512-DkFsiTwEKau8VIxyZBGdO6tOudD+QoUBPuHj3e6QFqmbfCRj1ArmYWue9lEp6jLSWBIw4XPlDLjFIZNLdRAMSg==} - peerDependencies: - vue: ^3.4.26 + undici-types@8.9.0: + resolution: {integrity: sha512-KTDyRTYX8sWmKXAikPHHSyc63CRPETMctyjKFupcC6OBLXT3xsN0e9aF7m+mIXutFWpUXuedtowG7iLOzp0kQg==} - vite@6.4.1: - resolution: {integrity: sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g==} - engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} + vite@8.3.5: + resolution: {integrity: sha512-z1YpjieJ9g0ebTmQpLBv/oq9E3F9EIlOe+7LahW8SrE3LTiG3udBRUXQOlYXP9b+IC2q2phHeVxj9xGlw34OEQ==} + engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: - '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0 + '@types/node': ^20.19.0 || >=22.12.0 + '@vitejs/devtools': ^0.7.1 || ^0.8.0 + esbuild: ^0.27.0 || ^0.28.0 jiti: '>=1.21.0' - less: '*' - lightningcss: ^1.21.0 - sass: '*' - sass-embedded: '*' - stylus: '*' - sugarss: '*' + less: ^4.0.0 + sass: ^1.70.0 + sass-embedded: ^1.70.0 + stylus: '>=0.54.8' + sugarss: ^5.0.0 terser: ^5.16.0 tsx: ^4.8.1 yaml: ^2.4.2 peerDependenciesMeta: '@types/node': optional: true + '@vitejs/devtools': + optional: true + esbuild: + optional: true jiti: optional: true less: optional: true - lightningcss: - optional: true sass: optional: true sass-embedded: @@ -1194,20 +1249,23 @@ packages: yaml: optional: true - vitest@4.0.18: - resolution: {integrity: sha512-hOQuK7h0FGKgBAas7v0mSAsnvrIgAvWmRFjmzpJ7SwFHH3g1k2u37JtYwOwmEKhK6ZO3v9ggDBBm0La1LCK4uQ==} - engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0} + vitest@5.0.5: + resolution: {integrity: sha512-cPODOMF8YnkiiYtDdHo6n4wWmBrOBBbOIYH2d33M4l3CdNn72h+qMmO6uXffUN6A5AZFXNND/d+krS9mgkXRfg==} + engines: {node: ^22.12.0 || ^24.0.0 || >=26.0.0} hasBin: true peerDependencies: '@edge-runtime/vm': '*' '@opentelemetry/api': ^1.9.0 - '@types/node': ^20.0.0 || ^22.0.0 || >=24.0.0 - '@vitest/browser-playwright': 4.0.18 - '@vitest/browser-preview': 4.0.18 - '@vitest/browser-webdriverio': 4.0.18 - '@vitest/ui': 4.0.18 + '@types/node': ^22.0.0 || >=24.0.0 + '@vitest/browser-playwright': 5.0.5 + '@vitest/browser-preview': 5.0.5 + '@vitest/browser-webdriverio': ^5.0.0-beta.5 || >=5.0.0 + '@vitest/coverage-istanbul': 5.0.5 + '@vitest/coverage-v8': 5.0.5 + '@vitest/ui': 5.0.5 happy-dom: '*' jsdom: '*' + vite: ^6.4.0 || ^7.0.0 || ^8.0.0 peerDependenciesMeta: '@edge-runtime/vm': optional: true @@ -1221,6 +1279,10 @@ packages: optional: true '@vitest/browser-webdriverio': optional: true + '@vitest/coverage-istanbul': + optional: true + '@vitest/coverage-v8': + optional: true '@vitest/ui': optional: true happy-dom: @@ -1228,8 +1290,21 @@ packages: jsdom: optional: true - vscode-uri@3.1.0: - resolution: {integrity: sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==} + vscode-jsonrpc@10.0.0: + resolution: {integrity: sha512-USSfxg0vDIpTQqlQBGxF3mBARyoS8cU7LHLpbYNh6dOJt32dlskxPWB0Irrh5TllTmyf7oNLAvqDqzklbwwnpA==} + engines: {node: '>=14.17.0'} + + vscode-languageserver-protocol@3.18.5: + resolution: {integrity: sha512-FW4fBMzZ99N8xeob4py0l050wgCoRgTpEq5Xcsb9lM1bc29UKGR/wSV+4knaqFxssFHajy7OzNiKP/m3U1ZEuA==} + + vscode-languageserver-textdocument@1.0.16: + resolution: {integrity: sha512-qeNEuOi9a3qkqRf7Gfez2WL2VwlBTLCNekHBGO9kxPVUp9XuY15yfTJ9jwvOwKI0I2MhUUfZq95NSp+LK1CLQg==} + + vscode-languageserver-types@3.18.5: + resolution: {integrity: sha512-kPd6+JQNz3HMJvjKlqUxERSlZHAntl/aGpsk7ImwIh0ewDKDcjgAEyVSYciPiOJD3ftbK9IrfiqUeTZLSbCizw==} + + vscode-uri@3.2.0: + resolution: {integrity: sha512-m2gXo3bn0G1kT9InzMf07fTbqMbGtyckj3bH5ktLO+1Ssv+yiATZ4dhwaQv9UZWxJh6E9IFGnQyjgWVDWVBDrg==} vue-demi@0.14.10: resolution: {integrity: sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==} @@ -1256,14 +1331,14 @@ packages: nuxt: optional: true - vue-tsc@2.2.12: - resolution: {integrity: sha512-P7OP77b2h/Pmk+lZdJ0YWs+5tJ6J2+uOQPo7tlBnY44QqQSPYvS0qVT4wqDJgwrZaLe47etJLLQRFia71GYITw==} + vue-tsc@3.3.14: + resolution: {integrity: sha512-URAu2H9AuMZMg9GCgnqbaHHHd8pBZRAwNV2e2yYS8VDsoTVXHiOSo663OS6DkEvauQ2yixn5TPFJ9WV9QIspkA==} hasBin: true peerDependencies: typescript: '>=5.0.0' - vue@3.5.29: - resolution: {integrity: sha512-BZqN4Ze6mDQVNAni0IHeMJ5mwr8VAJ3MQC9FmprRhcBYENw+wOAAjRj8jfmN6FLl0j96OXbR+CjWhmAmM+QGnA==} + vue@3.5.44: + resolution: {integrity: sha512-dvvmaOCG7lAMkJTPqPwWeGMsPS6DIsFsfZDUgBp9V6mHf9b3+/wyVwYR4Es+WV6uJC0FKRxp81Obcq0ZwUcGFQ==} peerDependencies: typescript: '*' peerDependenciesMeta: @@ -1273,196 +1348,134 @@ packages: w3c-keyname@2.2.8: resolution: {integrity: sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==} - why-is-node-running@2.3.0: - resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} - engines: {node: '>=8'} + whatwg-mimetype@3.0.0: + resolution: {integrity: sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==} + engines: {node: '>=12'} + + why-is-node-running@3.2.1: + resolution: {integrity: sha512-Tb2FUhB4vUsGQlfSquQLYkApkuPAFQXGFzxWKHHumVz2dK+X1RUm/HnID4+TfIGYJ1kTcwOaCk/buYCEJr6YjQ==} + engines: {node: '>=20.11'} hasBin: true - zod@3.25.76: - resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} + ws@8.22.1: + resolution: {integrity: sha512-oyL6OPYme/CmiFvNuGzeoxCJ4nOrZE2O1FwM9MWM1AsGri6nLXB2pg+lNA8aLeXMSav0J6eIVS1j6vUPzUtK9g==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + + zod@4.6.7: + resolution: {integrity: sha512-g8W2D2kU6a8untc053fX1uVBKzM7yqAxW/3zwk83+6RyF2+SKOuV+c0wBDhUwzV1PaGapmZH+rmcUzPf+3CGwg==} snapshots: - '@babel/helper-string-parser@7.27.1': {} + '@babel/helper-string-parser@7.29.11': {} - '@babel/helper-validator-identifier@7.28.5': {} + '@babel/helper-validator-identifier@7.29.7': {} - '@babel/parser@7.29.0': + '@babel/parser@7.29.12': dependencies: - '@babel/types': 7.29.0 + '@babel/types': 7.29.11 - '@babel/types@7.29.0': + '@babel/types@7.29.11': dependencies: - '@babel/helper-string-parser': 7.27.1 - '@babel/helper-validator-identifier': 7.28.5 + '@babel/helper-string-parser': 7.29.11 + '@babel/helper-validator-identifier': 7.29.7 - '@codemirror/autocomplete@6.20.1': + '@codemirror/autocomplete@6.20.4': dependencies: - '@codemirror/language': 6.12.2 - '@codemirror/state': 6.5.4 - '@codemirror/view': 6.39.16 - '@lezer/common': 1.5.1 + '@codemirror/language': 6.13.1 + '@codemirror/state': 6.7.6 + '@codemirror/view': 6.43.15 + '@lezer/common': 1.5.5 - '@codemirror/commands@6.10.2': + '@codemirror/commands@6.11.1': dependencies: - '@codemirror/language': 6.12.2 - '@codemirror/state': 6.5.4 - '@codemirror/view': 6.39.16 - '@lezer/common': 1.5.1 + '@codemirror/language': 6.13.1 + '@codemirror/state': 6.7.6 + '@codemirror/view': 6.43.15 + '@lezer/common': 1.5.5 '@codemirror/lang-json@6.0.2': dependencies: - '@codemirror/language': 6.12.2 + '@codemirror/language': 6.13.1 '@lezer/json': 1.0.3 - '@codemirror/language@6.12.2': + '@codemirror/language@6.13.1': dependencies: - '@codemirror/state': 6.5.4 - '@codemirror/view': 6.39.16 - '@lezer/common': 1.5.1 - '@lezer/highlight': 1.2.3 - '@lezer/lr': 1.4.8 - style-mod: 4.1.3 + '@codemirror/state': 6.7.6 + '@codemirror/streamparser': 6.0.0 + '@codemirror/view': 6.43.15 + '@lezer/common': 1.5.5 + '@lezer/highlight': 1.2.5 + '@lezer/lr': 1.4.12 + style-mod: 4.1.4 - '@codemirror/lint@6.9.5': + '@codemirror/lint@6.9.7': dependencies: - '@codemirror/state': 6.5.4 - '@codemirror/view': 6.39.16 - crelt: 1.0.6 + '@codemirror/state': 6.7.6 + '@codemirror/view': 6.43.15 + crelt: 1.0.7 - '@codemirror/search@6.6.0': + '@codemirror/search@6.7.2': dependencies: - '@codemirror/state': 6.5.4 - '@codemirror/view': 6.39.16 - crelt: 1.0.6 + '@codemirror/state': 6.7.6 + '@codemirror/view': 6.43.15 + crelt: 1.0.7 - '@codemirror/state@6.5.4': + '@codemirror/state@6.7.6': dependencies: - '@marijn/find-cluster-break': 1.0.2 + '@marijn/find-cluster-break': 1.0.4 + + '@codemirror/streamparser@6.0.0': {} '@codemirror/theme-one-dark@6.1.3': dependencies: - '@codemirror/language': 6.12.2 - '@codemirror/state': 6.5.4 - '@codemirror/view': 6.39.16 - '@lezer/highlight': 1.2.3 + '@codemirror/language': 6.13.1 + '@codemirror/state': 6.7.6 + '@codemirror/view': 6.43.15 + '@lezer/highlight': 1.2.5 - '@codemirror/view@6.39.16': + '@codemirror/view@6.43.15': dependencies: - '@codemirror/state': 6.5.4 - crelt: 1.0.6 - style-mod: 4.1.3 + '@codemirror/state': 6.7.6 + crelt: 1.0.7 + style-mod: 4.1.4 w3c-keyname: 2.2.8 - '@esbuild/aix-ppc64@0.25.12': - optional: true - - '@esbuild/android-arm64@0.25.12': - optional: true - - '@esbuild/android-arm@0.25.12': - optional: true - - '@esbuild/android-x64@0.25.12': - optional: true - - '@esbuild/darwin-arm64@0.25.12': - optional: true - - '@esbuild/darwin-x64@0.25.12': - optional: true - - '@esbuild/freebsd-arm64@0.25.12': - optional: true - - '@esbuild/freebsd-x64@0.25.12': - optional: true - - '@esbuild/linux-arm64@0.25.12': - optional: true - - '@esbuild/linux-arm@0.25.12': - optional: true - - '@esbuild/linux-ia32@0.25.12': - optional: true - - '@esbuild/linux-loong64@0.25.12': - optional: true - - '@esbuild/linux-mips64el@0.25.12': - optional: true - - '@esbuild/linux-ppc64@0.25.12': - optional: true - - '@esbuild/linux-riscv64@0.25.12': - optional: true - - '@esbuild/linux-s390x@0.25.12': - optional: true - - '@esbuild/linux-x64@0.25.12': - optional: true - - '@esbuild/netbsd-arm64@0.25.12': - optional: true - - '@esbuild/netbsd-x64@0.25.12': - optional: true - - '@esbuild/openbsd-arm64@0.25.12': - optional: true - - '@esbuild/openbsd-x64@0.25.12': - optional: true - - '@esbuild/openharmony-arm64@0.25.12': - optional: true - - '@esbuild/sunos-x64@0.25.12': - optional: true - - '@esbuild/win32-arm64@0.25.12': - optional: true - - '@esbuild/win32-ia32@0.25.12': - optional: true - - '@esbuild/win32-x64@0.25.12': - optional: true - - '@floating-ui/core@1.7.4': + '@floating-ui/core@1.8.0': dependencies: - '@floating-ui/utils': 0.2.10 + '@floating-ui/utils': 0.2.12 - '@floating-ui/dom@1.7.5': + '@floating-ui/dom@1.8.0': dependencies: - '@floating-ui/core': 1.7.4 - '@floating-ui/utils': 0.2.10 + '@floating-ui/core': 1.8.0 + '@floating-ui/utils': 0.2.12 - '@floating-ui/utils@0.2.10': {} + '@floating-ui/utils@0.2.12': {} - '@floating-ui/vue@1.1.10(vue@3.5.29(typescript@5.6.3))': + '@floating-ui/vue@2.0.1(vue@3.5.44(typescript@6.0.3))': dependencies: - '@floating-ui/dom': 1.7.5 - '@floating-ui/utils': 0.2.10 - vue-demi: 0.14.10(vue@3.5.29(typescript@5.6.3)) - transitivePeerDependencies: - - '@vue/composition-api' - - vue + '@floating-ui/dom': 1.8.0 + '@floating-ui/utils': 0.2.12 + vue: 3.5.44(typescript@6.0.3) - '@internationalized/date@3.11.0': + '@internationalized/date@3.12.4': dependencies: - '@swc/helpers': 0.5.19 + '@swc/helpers': 0.5.23 - '@internationalized/number@3.6.5': + '@internationalized/number@3.6.9': dependencies: - '@swc/helpers': 0.5.19 + '@swc/helpers': 0.5.23 '@jridgewell/gen-mapping@0.3.13': dependencies: - '@jridgewell/sourcemap-codec': 1.5.5 + '@jridgewell/sourcemap-codec': 1.6.0 '@jridgewell/trace-mapping': 0.3.31 '@jridgewell/remapping@2.3.5': @@ -1472,250 +1485,255 @@ snapshots: '@jridgewell/resolve-uri@3.1.2': {} - '@jridgewell/sourcemap-codec@1.5.5': {} + '@jridgewell/sourcemap-codec@1.6.0': {} '@jridgewell/trace-mapping@0.3.31': dependencies: '@jridgewell/resolve-uri': 3.1.2 - '@jridgewell/sourcemap-codec': 1.5.5 + '@jridgewell/sourcemap-codec': 1.6.0 - '@lezer/common@1.5.1': {} + '@lezer/common@1.5.5': {} - '@lezer/highlight@1.2.3': + '@lezer/highlight@1.2.5': dependencies: - '@lezer/common': 1.5.1 + '@lezer/common': 1.5.5 '@lezer/json@1.0.3': dependencies: - '@lezer/common': 1.5.1 - '@lezer/highlight': 1.2.3 - '@lezer/lr': 1.4.8 + '@lezer/common': 1.5.5 + '@lezer/highlight': 1.2.5 + '@lezer/lr': 1.4.12 - '@lezer/lr@1.4.8': + '@lezer/lr@1.4.12': dependencies: - '@lezer/common': 1.5.1 - - '@marijn/find-cluster-break@1.0.2': {} - - '@rollup/rollup-android-arm-eabi@4.59.0': - optional: true - - '@rollup/rollup-android-arm64@4.59.0': - optional: true - - '@rollup/rollup-darwin-arm64@4.59.0': - optional: true - - '@rollup/rollup-darwin-x64@4.59.0': - optional: true - - '@rollup/rollup-freebsd-arm64@4.59.0': - optional: true - - '@rollup/rollup-freebsd-x64@4.59.0': - optional: true - - '@rollup/rollup-linux-arm-gnueabihf@4.59.0': - optional: true + '@lezer/common': 1.5.5 - '@rollup/rollup-linux-arm-musleabihf@4.59.0': - optional: true + '@lucide/vue@1.66.0(vue@3.5.44(typescript@6.0.3))': + dependencies: + vue: 3.5.44(typescript@6.0.3) - '@rollup/rollup-linux-arm64-gnu@4.59.0': - optional: true + '@marijn/find-cluster-break@1.0.4': {} - '@rollup/rollup-linux-arm64-musl@4.59.0': - optional: true + '@oxc-project/types@0.156.0': {} - '@rollup/rollup-linux-loong64-gnu@4.59.0': + '@rolldown/binding-android-arm-eabi@1.2.16': optional: true - '@rollup/rollup-linux-loong64-musl@4.59.0': + '@rolldown/binding-android-arm64@1.2.16': optional: true - '@rollup/rollup-linux-ppc64-gnu@4.59.0': + '@rolldown/binding-darwin-arm64@1.2.16': optional: true - '@rollup/rollup-linux-ppc64-musl@4.59.0': + '@rolldown/binding-darwin-x64@1.2.16': optional: true - '@rollup/rollup-linux-riscv64-gnu@4.59.0': + '@rolldown/binding-freebsd-x64@1.2.16': optional: true - '@rollup/rollup-linux-riscv64-musl@4.59.0': + '@rolldown/binding-linux-arm-gnueabihf@1.2.16': optional: true - '@rollup/rollup-linux-s390x-gnu@4.59.0': + '@rolldown/binding-linux-arm64-gnu@1.2.16': optional: true - '@rollup/rollup-linux-x64-gnu@4.59.0': + '@rolldown/binding-linux-arm64-musl@1.2.16': optional: true - '@rollup/rollup-linux-x64-musl@4.59.0': + '@rolldown/binding-linux-ppc64-gnu@1.2.16': optional: true - '@rollup/rollup-openbsd-x64@4.59.0': + '@rolldown/binding-linux-s390x-gnu@1.2.16': optional: true - '@rollup/rollup-openharmony-arm64@4.59.0': + '@rolldown/binding-linux-x64-gnu@1.2.16': optional: true - '@rollup/rollup-win32-arm64-msvc@4.59.0': + '@rolldown/binding-linux-x64-musl@1.2.16': optional: true - '@rollup/rollup-win32-ia32-msvc@4.59.0': + '@rolldown/binding-openharmony-arm64@1.2.16': optional: true - '@rollup/rollup-win32-x64-gnu@4.59.0': + '@rolldown/binding-win32-arm64-msvc@1.2.16': optional: true - '@rollup/rollup-win32-x64-msvc@4.59.0': + '@rolldown/binding-win32-x64-msvc@1.2.16': optional: true - '@standard-schema/spec@1.1.0': {} + '@rolldown/pluginutils@1.0.1': {} - '@swc/helpers@0.5.19': + '@swc/helpers@0.5.23': dependencies: - tslib: 2.8.1 + tslib: 2.9.1 - '@tailwindcss/node@4.2.1': + '@tailwindcss/node@4.3.3': dependencies: '@jridgewell/remapping': 2.3.5 - enhanced-resolve: 5.20.0 - jiti: 2.6.1 - lightningcss: 1.31.1 + enhanced-resolve: 5.26.0 + jiti: 2.7.0 + lightningcss: 1.32.0 magic-string: 0.30.21 - source-map-js: 1.2.1 - tailwindcss: 4.2.1 + source-map-js: 1.2.2 + tailwindcss: 4.3.3 - '@tailwindcss/oxide-android-arm64@4.2.1': + '@tailwindcss/oxide-android-arm64@4.3.3': optional: true - '@tailwindcss/oxide-darwin-arm64@4.2.1': + '@tailwindcss/oxide-darwin-arm64@4.3.3': optional: true - '@tailwindcss/oxide-darwin-x64@4.2.1': + '@tailwindcss/oxide-darwin-x64@4.3.3': optional: true - '@tailwindcss/oxide-freebsd-x64@4.2.1': + '@tailwindcss/oxide-freebsd-x64@4.3.3': optional: true - '@tailwindcss/oxide-linux-arm-gnueabihf@4.2.1': + '@tailwindcss/oxide-linux-arm-gnueabihf@4.3.3': optional: true - '@tailwindcss/oxide-linux-arm64-gnu@4.2.1': + '@tailwindcss/oxide-linux-arm64-gnu@4.3.3': optional: true - '@tailwindcss/oxide-linux-arm64-musl@4.2.1': + '@tailwindcss/oxide-linux-arm64-musl@4.3.3': optional: true - '@tailwindcss/oxide-linux-x64-gnu@4.2.1': + '@tailwindcss/oxide-linux-x64-gnu@4.3.3': optional: true - '@tailwindcss/oxide-linux-x64-musl@4.2.1': + '@tailwindcss/oxide-linux-x64-musl@4.3.3': optional: true - '@tailwindcss/oxide-wasm32-wasi@4.2.1': + '@tailwindcss/oxide-wasm32-wasi@4.3.3': optional: true - '@tailwindcss/oxide-win32-arm64-msvc@4.2.1': + '@tailwindcss/oxide-win32-arm64-msvc@4.3.3': optional: true - '@tailwindcss/oxide-win32-x64-msvc@4.2.1': + '@tailwindcss/oxide-win32-x64-msvc@4.3.3': optional: true - '@tailwindcss/oxide@4.2.1': + '@tailwindcss/oxide@4.3.3': optionalDependencies: - '@tailwindcss/oxide-android-arm64': 4.2.1 - '@tailwindcss/oxide-darwin-arm64': 4.2.1 - '@tailwindcss/oxide-darwin-x64': 4.2.1 - '@tailwindcss/oxide-freebsd-x64': 4.2.1 - '@tailwindcss/oxide-linux-arm-gnueabihf': 4.2.1 - '@tailwindcss/oxide-linux-arm64-gnu': 4.2.1 - '@tailwindcss/oxide-linux-arm64-musl': 4.2.1 - '@tailwindcss/oxide-linux-x64-gnu': 4.2.1 - '@tailwindcss/oxide-linux-x64-musl': 4.2.1 - '@tailwindcss/oxide-wasm32-wasi': 4.2.1 - '@tailwindcss/oxide-win32-arm64-msvc': 4.2.1 - '@tailwindcss/oxide-win32-x64-msvc': 4.2.1 + '@tailwindcss/oxide-android-arm64': 4.3.3 + '@tailwindcss/oxide-darwin-arm64': 4.3.3 + '@tailwindcss/oxide-darwin-x64': 4.3.3 + '@tailwindcss/oxide-freebsd-x64': 4.3.3 + '@tailwindcss/oxide-linux-arm-gnueabihf': 4.3.3 + '@tailwindcss/oxide-linux-arm64-gnu': 4.3.3 + '@tailwindcss/oxide-linux-arm64-musl': 4.3.3 + '@tailwindcss/oxide-linux-x64-gnu': 4.3.3 + '@tailwindcss/oxide-linux-x64-musl': 4.3.3 + '@tailwindcss/oxide-wasm32-wasi': 4.3.3 + '@tailwindcss/oxide-win32-arm64-msvc': 4.3.3 + '@tailwindcss/oxide-win32-x64-msvc': 4.3.3 + + '@tailwindcss/vite@4.3.3(vite@8.3.5(@types/node@26.6.6)(jiti@2.7.0))': + dependencies: + '@tailwindcss/node': 4.3.3 + '@tailwindcss/oxide': 4.3.3 + tailwindcss: 4.3.3 + vite: 8.3.5(@types/node@26.6.6)(jiti@2.7.0) + + '@tanstack/devtools-event-client@0.4.4': {} + + '@tanstack/form-core@1.33.5': + dependencies: + '@tanstack/devtools-event-client': 0.4.4 + '@tanstack/pacer-lite': 0.1.1 + '@tanstack/store': 0.11.2 - '@tailwindcss/vite@4.2.1(vite@6.4.1(@types/node@25.3.3)(jiti@2.6.1)(lightningcss@1.31.1))': + '@tanstack/pacer-lite@0.1.1': {} + + '@tanstack/store@0.11.2': {} + + '@tanstack/table-core@9.3.1': dependencies: - '@tailwindcss/node': 4.2.1 - '@tailwindcss/oxide': 4.2.1 - tailwindcss: 4.2.1 - vite: 6.4.1(@types/node@25.3.3)(jiti@2.6.1)(lightningcss@1.31.1) + '@tanstack/store': 0.11.2 - '@tanstack/table-core@8.21.3': {} + '@tanstack/virtual-core@3.19.10': {} + + '@tanstack/vue-form@1.33.5(vue@3.5.44(typescript@6.0.3))': + dependencies: + '@tanstack/form-core': 1.33.5 + '@tanstack/vue-store': 0.11.2(vue@3.5.44(typescript@6.0.3)) + vue: 3.5.44(typescript@6.0.3) + transitivePeerDependencies: + - '@vue/composition-api' - '@tanstack/virtual-core@3.13.21': {} + '@tanstack/vue-store@0.11.2(vue@3.5.44(typescript@6.0.3))': + dependencies: + '@tanstack/store': 0.11.2 + vue: 3.5.44(typescript@6.0.3) + vue-demi: 0.14.10(vue@3.5.44(typescript@6.0.3)) - '@tanstack/vue-table@8.21.3(vue@3.5.29(typescript@5.6.3))': + '@tanstack/vue-table@9.3.1(vue@3.5.44(typescript@6.0.3))': dependencies: - '@tanstack/table-core': 8.21.3 - vue: 3.5.29(typescript@5.6.3) + '@tanstack/store': 0.11.2 + '@tanstack/table-core': 9.3.1 + vue: 3.5.44(typescript@6.0.3) - '@tanstack/vue-virtual@3.13.21(vue@3.5.29(typescript@5.6.3))': + '@tanstack/vue-virtual@3.13.51(vue@3.5.44(typescript@6.0.3))': dependencies: - '@tanstack/virtual-core': 3.13.21 - vue: 3.5.29(typescript@5.6.3) + '@tanstack/virtual-core': 3.19.10 + vue: 3.5.44(typescript@6.0.3) - '@tauri-apps/api@2.10.1': {} + '@tauri-apps/api@2.12.4': {} - '@tauri-apps/cli-darwin-arm64@2.10.0': + '@tauri-apps/cli-darwin-arm64@2.12.4': optional: true - '@tauri-apps/cli-darwin-x64@2.10.0': + '@tauri-apps/cli-darwin-x64@2.12.4': optional: true - '@tauri-apps/cli-linux-arm-gnueabihf@2.10.0': + '@tauri-apps/cli-linux-arm-gnueabihf@2.12.4': optional: true - '@tauri-apps/cli-linux-arm64-gnu@2.10.0': + '@tauri-apps/cli-linux-arm64-gnu@2.12.4': optional: true - '@tauri-apps/cli-linux-arm64-musl@2.10.0': + '@tauri-apps/cli-linux-arm64-musl@2.12.4': optional: true - '@tauri-apps/cli-linux-riscv64-gnu@2.10.0': + '@tauri-apps/cli-linux-riscv64-gnu@2.12.4': optional: true - '@tauri-apps/cli-linux-x64-gnu@2.10.0': + '@tauri-apps/cli-linux-x64-gnu@2.12.4': optional: true - '@tauri-apps/cli-linux-x64-musl@2.10.0': + '@tauri-apps/cli-linux-x64-musl@2.12.4': optional: true - '@tauri-apps/cli-win32-arm64-msvc@2.10.0': + '@tauri-apps/cli-win32-arm64-msvc@2.12.4': optional: true - '@tauri-apps/cli-win32-ia32-msvc@2.10.0': + '@tauri-apps/cli-win32-ia32-msvc@2.12.4': optional: true - '@tauri-apps/cli-win32-x64-msvc@2.10.0': + '@tauri-apps/cli-win32-x64-msvc@2.12.4': optional: true - '@tauri-apps/cli@2.10.0': + '@tauri-apps/cli@2.12.4': optionalDependencies: - '@tauri-apps/cli-darwin-arm64': 2.10.0 - '@tauri-apps/cli-darwin-x64': 2.10.0 - '@tauri-apps/cli-linux-arm-gnueabihf': 2.10.0 - '@tauri-apps/cli-linux-arm64-gnu': 2.10.0 - '@tauri-apps/cli-linux-arm64-musl': 2.10.0 - '@tauri-apps/cli-linux-riscv64-gnu': 2.10.0 - '@tauri-apps/cli-linux-x64-gnu': 2.10.0 - '@tauri-apps/cli-linux-x64-musl': 2.10.0 - '@tauri-apps/cli-win32-arm64-msvc': 2.10.0 - '@tauri-apps/cli-win32-ia32-msvc': 2.10.0 - '@tauri-apps/cli-win32-x64-msvc': 2.10.0 + '@tauri-apps/cli-darwin-arm64': 2.12.4 + '@tauri-apps/cli-darwin-x64': 2.12.4 + '@tauri-apps/cli-linux-arm-gnueabihf': 2.12.4 + '@tauri-apps/cli-linux-arm64-gnu': 2.12.4 + '@tauri-apps/cli-linux-arm64-musl': 2.12.4 + '@tauri-apps/cli-linux-riscv64-gnu': 2.12.4 + '@tauri-apps/cli-linux-x64-gnu': 2.12.4 + '@tauri-apps/cli-linux-x64-musl': 2.12.4 + '@tauri-apps/cli-win32-arm64-msvc': 2.12.4 + '@tauri-apps/cli-win32-ia32-msvc': 2.12.4 + '@tauri-apps/cli-win32-x64-msvc': 2.12.4 - '@tauri-apps/plugin-log@2.8.0': + '@tauri-apps/plugin-log@2.10.1': dependencies: - '@tauri-apps/api': 2.10.1 + '@tauri-apps/api': 2.12.4 - '@tauri-apps/plugin-opener@2.5.3': + '@tauri-apps/plugin-opener@2.7.2': dependencies: - '@tauri-apps/api': 2.10.1 + '@tauri-apps/api': 2.12.4 '@types/chai@5.2.3': dependencies: @@ -1724,198 +1742,179 @@ snapshots: '@types/deep-eql@4.0.2': {} - '@types/estree@1.0.8': {} + '@types/estree@1.0.10': {} - '@types/node@25.3.3': + '@types/node@26.6.6': dependencies: - undici-types: 7.18.2 + undici-types: 8.9.0 '@types/web-bluetooth@0.0.21': {} - '@vee-validate/zod@4.15.1(vue@3.5.29(typescript@5.6.3))(zod@3.25.76)': - dependencies: - type-fest: 4.41.0 - vee-validate: 4.15.1(vue@3.5.29(typescript@5.6.3)) - zod: 3.25.76 - transitivePeerDependencies: - - vue + '@types/whatwg-mimetype@3.0.2': {} - '@vitejs/plugin-vue@5.2.4(vite@6.4.1(@types/node@25.3.3)(jiti@2.6.1)(lightningcss@1.31.1))(vue@3.5.29(typescript@5.6.3))': + '@types/ws@8.18.2': dependencies: - vite: 6.4.1(@types/node@25.3.3)(jiti@2.6.1)(lightningcss@1.31.1) - vue: 3.5.29(typescript@5.6.3) + '@types/node': 26.6.6 - '@vitest/expect@4.0.18': + '@vitejs/plugin-vue@6.0.10(vite@8.3.5(@types/node@26.6.6)(jiti@2.7.0))(vue@3.5.44(typescript@6.0.3))': dependencies: - '@standard-schema/spec': 1.1.0 - '@types/chai': 5.2.3 - '@vitest/spy': 4.0.18 - '@vitest/utils': 4.0.18 - chai: 6.2.2 - tinyrainbow: 3.0.3 + '@rolldown/pluginutils': 1.0.1 + vite: 8.3.5(@types/node@26.6.6)(jiti@2.7.0) + vue: 3.5.44(typescript@6.0.3) - '@vitest/mocker@4.0.18(vite@6.4.1(@types/node@25.3.3)(jiti@2.6.1)(lightningcss@1.31.1))': + '@vitest/mocker@5.0.5(vite@8.3.5(@types/node@26.6.6)(jiti@2.7.0))': dependencies: - '@vitest/spy': 4.0.18 + '@jridgewell/trace-mapping': 0.3.31 + '@vitest/spy': 5.0.5 estree-walker: 3.0.3 - magic-string: 0.30.21 + magic-string: 1.4.5 optionalDependencies: - vite: 6.4.1(@types/node@25.3.3)(jiti@2.6.1)(lightningcss@1.31.1) + vite: 8.3.5(@types/node@26.6.6)(jiti@2.7.0) - '@vitest/pretty-format@4.0.18': - dependencies: - tinyrainbow: 3.0.3 - - '@vitest/runner@4.0.18': - dependencies: - '@vitest/utils': 4.0.18 - pathe: 2.0.3 - - '@vitest/snapshot@4.0.18': - dependencies: - '@vitest/pretty-format': 4.0.18 - magic-string: 0.30.21 - pathe: 2.0.3 + '@vitest/spy@5.0.5': {} - '@vitest/spy@4.0.18': {} - - '@vitest/utils@4.0.18': + '@volar/language-core@2.4.30': dependencies: - '@vitest/pretty-format': 4.0.18 - tinyrainbow: 3.0.3 + '@volar/source-map': 2.4.30 - '@volar/language-core@2.4.15': + '@volar/language-service@2.4.30(typescript@6.0.3)': dependencies: - '@volar/source-map': 2.4.15 + '@volar/language-core': 2.4.30 + vscode-languageserver-protocol: 3.18.5 + vscode-languageserver-textdocument: 1.0.16 + vscode-uri: 3.2.0 + optionalDependencies: + typescript: 6.0.3 - '@volar/source-map@2.4.15': {} + '@volar/source-map@2.4.30': {} - '@volar/typescript@2.4.15': + '@volar/typescript@2.4.30(typescript@6.0.3)': dependencies: - '@volar/language-core': 2.4.15 + '@volar/language-core': 2.4.30 + '@volar/language-service': 2.4.30(typescript@6.0.3) path-browserify: 1.0.1 - vscode-uri: 3.1.0 + vscode-uri: 3.2.0 + optionalDependencies: + typescript: 6.0.3 - '@vue/compiler-core@3.5.29': + '@vue/compiler-core@3.5.44': dependencies: - '@babel/parser': 7.29.0 - '@vue/shared': 3.5.29 + '@babel/parser': 7.29.12 + '@vue/shared': 3.5.44 entities: 7.0.1 estree-walker: 2.0.2 - source-map-js: 1.2.1 + source-map-js: 1.2.2 - '@vue/compiler-dom@3.5.29': + '@vue/compiler-dom@3.5.44': dependencies: - '@vue/compiler-core': 3.5.29 - '@vue/shared': 3.5.29 + '@vue/compiler-core': 3.5.44 + '@vue/shared': 3.5.44 - '@vue/compiler-sfc@3.5.29': + '@vue/compiler-sfc@3.5.44': dependencies: - '@babel/parser': 7.29.0 - '@vue/compiler-core': 3.5.29 - '@vue/compiler-dom': 3.5.29 - '@vue/compiler-ssr': 3.5.29 - '@vue/shared': 3.5.29 + '@babel/parser': 7.29.12 + '@vue/compiler-core': 3.5.44 + '@vue/compiler-dom': 3.5.44 + '@vue/compiler-ssr': 3.5.44 + '@vue/shared': 3.5.44 estree-walker: 2.0.2 magic-string: 0.30.21 - postcss: 8.5.6 - source-map-js: 1.2.1 - - '@vue/compiler-ssr@3.5.29': - dependencies: - '@vue/compiler-dom': 3.5.29 - '@vue/shared': 3.5.29 + postcss: 8.5.32 + source-map-js: 1.2.2 - '@vue/compiler-vue2@2.7.16': + '@vue/compiler-ssr@3.5.44': dependencies: - de-indent: 1.0.2 - he: 1.2.0 + '@vue/compiler-dom': 3.5.44 + '@vue/shared': 3.5.44 - '@vue/devtools-api@7.7.9': + '@vue/devtools-api@8.2.1': dependencies: - '@vue/devtools-kit': 7.7.9 + '@vue/devtools-kit': 8.2.1 - '@vue/devtools-kit@7.7.9': + '@vue/devtools-kit@8.2.1': dependencies: - '@vue/devtools-shared': 7.7.9 + '@vue/devtools-shared': 8.2.1 birpc: 2.9.0 hookable: 5.5.3 - mitt: 3.0.1 - perfect-debounce: 1.0.0 - speakingurl: 14.0.1 - superjson: 2.2.6 + perfect-debounce: 2.1.0 - '@vue/devtools-shared@7.7.9': - dependencies: - rfdc: 1.4.1 + '@vue/devtools-shared@8.2.1': {} - '@vue/language-core@2.2.12(typescript@5.6.3)': + '@vue/language-core@3.3.14': dependencies: - '@volar/language-core': 2.4.15 - '@vue/compiler-dom': 3.5.29 - '@vue/compiler-vue2': 2.7.16 - '@vue/shared': 3.5.29 - alien-signals: 1.0.13 - minimatch: 9.0.9 + '@volar/language-core': 2.4.30 + '@vue/compiler-dom': 3.5.44 + '@vue/shared': 3.5.44 + alien-signals: 3.2.1 muggle-string: 0.4.1 path-browserify: 1.0.1 - optionalDependencies: - typescript: 5.6.3 + picomatch: 4.0.7 - '@vue/reactivity@3.5.29': + '@vue/reactivity@3.5.44': dependencies: - '@vue/shared': 3.5.29 + '@vue/shared': 3.5.44 - '@vue/runtime-core@3.5.29': + '@vue/runtime-core@3.5.44': dependencies: - '@vue/reactivity': 3.5.29 - '@vue/shared': 3.5.29 + '@vue/reactivity': 3.5.44 + '@vue/shared': 3.5.44 - '@vue/runtime-dom@3.5.29': + '@vue/runtime-dom@3.5.44': dependencies: - '@vue/reactivity': 3.5.29 - '@vue/runtime-core': 3.5.29 - '@vue/shared': 3.5.29 + '@vue/reactivity': 3.5.44 + '@vue/runtime-core': 3.5.44 + '@vue/shared': 3.5.44 csstype: 3.2.3 - '@vue/server-renderer@3.5.29(vue@3.5.29(typescript@5.6.3))': + '@vue/server-renderer@3.5.44': dependencies: - '@vue/compiler-ssr': 3.5.29 - '@vue/shared': 3.5.29 - vue: 3.5.29(typescript@5.6.3) + '@vue/compiler-ssr': 3.5.44 + '@vue/runtime-dom': 3.5.44 + '@vue/shared': 3.5.44 + + '@vue/shared@3.5.44': {} - '@vue/shared@3.5.29': {} + '@vueuse/core@14.4.0(vue@3.5.44(typescript@6.0.3))': + dependencies: + '@types/web-bluetooth': 0.0.21 + '@vueuse/metadata': 14.4.0 + '@vueuse/shared': 14.4.0(vue@3.5.44(typescript@6.0.3)) + vue: 3.5.44(typescript@6.0.3) - '@vueuse/core@14.2.1(vue@3.5.29(typescript@5.6.3))': + '@vueuse/core@15.0.0(vue@3.5.44(typescript@6.0.3))': dependencies: '@types/web-bluetooth': 0.0.21 - '@vueuse/metadata': 14.2.1 - '@vueuse/shared': 14.2.1(vue@3.5.29(typescript@5.6.3)) - vue: 3.5.29(typescript@5.6.3) + '@vueuse/metadata': 15.0.0 + '@vueuse/shared': 15.0.0(vue@3.5.44(typescript@6.0.3)) + vue: 3.5.44(typescript@6.0.3) - '@vueuse/metadata@14.2.1': {} + '@vueuse/metadata@14.4.0': {} - '@vueuse/shared@14.2.1(vue@3.5.29(typescript@5.6.3))': + '@vueuse/metadata@15.0.0': {} + + '@vueuse/shared@14.4.0(vue@3.5.44(typescript@6.0.3))': + dependencies: + vue: 3.5.44(typescript@6.0.3) + + '@vueuse/shared@15.0.0(vue@3.5.44(typescript@6.0.3))': dependencies: - vue: 3.5.29(typescript@5.6.3) + vue: 3.5.44(typescript@6.0.3) - alien-signals@1.0.13: {} + alien-signals@3.2.1: {} aria-hidden@1.2.6: dependencies: - tslib: 2.8.1 + tslib: 2.9.1 assertion-error@2.0.1: {} - balanced-match@1.0.2: {} - birpc@2.9.0: {} - brace-expansion@2.0.2: + buffer-image-size@0.6.4: dependencies: - balanced-match: 1.0.2 + '@types/node': 26.6.6 - chai@6.2.2: {} + chai@6.3.0: {} class-variance-authority@0.7.1: dependencies: @@ -1925,363 +1924,341 @@ snapshots: codemirror@6.0.2: dependencies: - '@codemirror/autocomplete': 6.20.1 - '@codemirror/commands': 6.10.2 - '@codemirror/language': 6.12.2 - '@codemirror/lint': 6.9.5 - '@codemirror/search': 6.6.0 - '@codemirror/state': 6.5.4 - '@codemirror/view': 6.39.16 + '@codemirror/autocomplete': 6.20.4 + '@codemirror/commands': 6.11.1 + '@codemirror/language': 6.13.1 + '@codemirror/lint': 6.9.7 + '@codemirror/search': 6.7.2 + '@codemirror/state': 6.7.6 + '@codemirror/view': 6.43.15 - copy-anything@4.0.5: - dependencies: - is-what: 5.5.0 - - crelt@1.0.6: {} + crelt@1.0.7: {} csstype@3.2.3: {} - de-indent@1.0.2: {} - - defu@6.1.4: {} + defu@6.1.7: {} detect-libc@2.1.2: {} - enhanced-resolve@5.20.0: + enhanced-resolve@5.26.0: dependencies: graceful-fs: 4.2.11 - tapable: 2.3.0 + tapable: 2.3.3 entities@7.0.1: {} - es-module-lexer@1.7.0: {} - - esbuild@0.25.12: - optionalDependencies: - '@esbuild/aix-ppc64': 0.25.12 - '@esbuild/android-arm': 0.25.12 - '@esbuild/android-arm64': 0.25.12 - '@esbuild/android-x64': 0.25.12 - '@esbuild/darwin-arm64': 0.25.12 - '@esbuild/darwin-x64': 0.25.12 - '@esbuild/freebsd-arm64': 0.25.12 - '@esbuild/freebsd-x64': 0.25.12 - '@esbuild/linux-arm': 0.25.12 - '@esbuild/linux-arm64': 0.25.12 - '@esbuild/linux-ia32': 0.25.12 - '@esbuild/linux-loong64': 0.25.12 - '@esbuild/linux-mips64el': 0.25.12 - '@esbuild/linux-ppc64': 0.25.12 - '@esbuild/linux-riscv64': 0.25.12 - '@esbuild/linux-s390x': 0.25.12 - '@esbuild/linux-x64': 0.25.12 - '@esbuild/netbsd-arm64': 0.25.12 - '@esbuild/netbsd-x64': 0.25.12 - '@esbuild/openbsd-arm64': 0.25.12 - '@esbuild/openbsd-x64': 0.25.12 - '@esbuild/openharmony-arm64': 0.25.12 - '@esbuild/sunos-x64': 0.25.12 - '@esbuild/win32-arm64': 0.25.12 - '@esbuild/win32-ia32': 0.25.12 - '@esbuild/win32-x64': 0.25.12 + es-module-lexer@2.3.2: {} estree-walker@2.0.2: {} estree-walker@3.0.3: dependencies: - '@types/estree': 1.0.8 + '@types/estree': 1.0.10 - expect-type@1.3.0: {} + expect-type@1.4.0: {} - fdir@6.5.0(picomatch@4.0.3): + fdir@6.5.0(picomatch@4.0.7): optionalDependencies: - picomatch: 4.0.3 + picomatch: 4.0.7 fsevents@2.3.3: optional: true graceful-fs@4.2.11: {} - he@1.2.0: {} + happy-dom@21.0.4: + dependencies: + '@types/node': 26.6.6 + '@types/whatwg-mimetype': 3.0.2 + '@types/ws': 8.18.2 + buffer-image-size: 0.6.4 + entities: 7.0.1 + whatwg-mimetype: 3.0.0 + ws: 8.22.1 + transitivePeerDependencies: + - bufferutil + - utf-8-validate hookable@5.5.3: {} - is-what@5.5.0: {} + jiti@2.7.0: {} + + lightningcss-android-arm64@1.32.0: + optional: true + + lightningcss-android-arm64@1.33.0: + optional: true + + lightningcss-darwin-arm64@1.32.0: + optional: true + + lightningcss-darwin-arm64@1.33.0: + optional: true + + lightningcss-darwin-x64@1.32.0: + optional: true + + lightningcss-darwin-x64@1.33.0: + optional: true + + lightningcss-freebsd-x64@1.32.0: + optional: true + + lightningcss-freebsd-x64@1.33.0: + optional: true + + lightningcss-linux-arm-gnueabihf@1.32.0: + optional: true + + lightningcss-linux-arm-gnueabihf@1.33.0: + optional: true - jiti@2.6.1: {} + lightningcss-linux-arm64-gnu@1.32.0: + optional: true - lightningcss-android-arm64@1.31.1: + lightningcss-linux-arm64-gnu@1.33.0: optional: true - lightningcss-darwin-arm64@1.31.1: + lightningcss-linux-arm64-musl@1.32.0: optional: true - lightningcss-darwin-x64@1.31.1: + lightningcss-linux-arm64-musl@1.33.0: optional: true - lightningcss-freebsd-x64@1.31.1: + lightningcss-linux-x64-gnu@1.32.0: optional: true - lightningcss-linux-arm-gnueabihf@1.31.1: + lightningcss-linux-x64-gnu@1.33.0: optional: true - lightningcss-linux-arm64-gnu@1.31.1: + lightningcss-linux-x64-musl@1.32.0: optional: true - lightningcss-linux-arm64-musl@1.31.1: + lightningcss-linux-x64-musl@1.33.0: optional: true - lightningcss-linux-x64-gnu@1.31.1: + lightningcss-win32-arm64-msvc@1.32.0: optional: true - lightningcss-linux-x64-musl@1.31.1: + lightningcss-win32-arm64-msvc@1.33.0: optional: true - lightningcss-win32-arm64-msvc@1.31.1: + lightningcss-win32-x64-msvc@1.32.0: optional: true - lightningcss-win32-x64-msvc@1.31.1: + lightningcss-win32-x64-msvc@1.33.0: optional: true - lightningcss@1.31.1: + lightningcss@1.32.0: + dependencies: + detect-libc: 2.1.2 + optionalDependencies: + lightningcss-android-arm64: 1.32.0 + lightningcss-darwin-arm64: 1.32.0 + lightningcss-darwin-x64: 1.32.0 + lightningcss-freebsd-x64: 1.32.0 + lightningcss-linux-arm-gnueabihf: 1.32.0 + lightningcss-linux-arm64-gnu: 1.32.0 + lightningcss-linux-arm64-musl: 1.32.0 + lightningcss-linux-x64-gnu: 1.32.0 + lightningcss-linux-x64-musl: 1.32.0 + lightningcss-win32-arm64-msvc: 1.32.0 + lightningcss-win32-x64-msvc: 1.32.0 + + lightningcss@1.33.0: dependencies: detect-libc: 2.1.2 optionalDependencies: - lightningcss-android-arm64: 1.31.1 - lightningcss-darwin-arm64: 1.31.1 - lightningcss-darwin-x64: 1.31.1 - lightningcss-freebsd-x64: 1.31.1 - lightningcss-linux-arm-gnueabihf: 1.31.1 - lightningcss-linux-arm64-gnu: 1.31.1 - lightningcss-linux-arm64-musl: 1.31.1 - lightningcss-linux-x64-gnu: 1.31.1 - lightningcss-linux-x64-musl: 1.31.1 - lightningcss-win32-arm64-msvc: 1.31.1 - lightningcss-win32-x64-msvc: 1.31.1 - - lucide-vue-next@0.563.0(vue@3.5.29(typescript@5.6.3)): - dependencies: - vue: 3.5.29(typescript@5.6.3) + lightningcss-android-arm64: 1.33.0 + lightningcss-darwin-arm64: 1.33.0 + lightningcss-darwin-x64: 1.33.0 + lightningcss-freebsd-x64: 1.33.0 + lightningcss-linux-arm-gnueabihf: 1.33.0 + lightningcss-linux-arm64-gnu: 1.33.0 + lightningcss-linux-arm64-musl: 1.33.0 + lightningcss-linux-x64-gnu: 1.33.0 + lightningcss-linux-x64-musl: 1.33.0 + lightningcss-win32-arm64-msvc: 1.33.0 + lightningcss-win32-x64-msvc: 1.33.0 magic-string@0.30.21: dependencies: - '@jridgewell/sourcemap-codec': 1.5.5 + '@jridgewell/sourcemap-codec': 1.6.0 - minimatch@9.0.9: + magic-string@1.4.5: dependencies: - brace-expansion: 2.0.2 - - mitt@3.0.1: {} + '@jridgewell/sourcemap-codec': 1.6.0 muggle-string@0.4.1: {} - nanoid@3.3.11: {} + nanoid@3.3.20: {} - obug@2.1.1: {} + nostics@1.3.0: {} - ohash@2.0.11: {} + obug@3.0.0: {} - path-browserify@1.0.1: {} + ohash@2.0.12: {} - pathe@2.0.3: {} + path-browserify@1.0.1: {} - perfect-debounce@1.0.0: {} + perfect-debounce@2.1.0: {} picocolors@1.1.1: {} - picomatch@4.0.3: {} + picomatch@4.0.7: {} - pinia@3.0.4(typescript@5.6.3)(vue@3.5.29(typescript@5.6.3)): + pinia@4.1.0(@vue/devtools-api@8.2.1)(typescript@6.0.3)(vue@3.5.44(typescript@6.0.3)): dependencies: - '@vue/devtools-api': 7.7.9 - vue: 3.5.29(typescript@5.6.3) + '@vue/devtools-api': 8.2.1 + nostics: 1.3.0 + vue: 3.5.44(typescript@6.0.3) optionalDependencies: - typescript: 5.6.3 + typescript: 6.0.3 - postcss@8.5.6: + postcss@8.5.32: dependencies: - nanoid: 3.3.11 + nanoid: 3.3.20 picocolors: 1.1.1 - source-map-js: 1.2.1 + source-map-js: 1.2.2 - reka-ui@2.8.2(vue@3.5.29(typescript@5.6.3)): + reka-ui@2.11.0(vue@3.5.44(typescript@6.0.3)): dependencies: - '@floating-ui/dom': 1.7.5 - '@floating-ui/vue': 1.1.10(vue@3.5.29(typescript@5.6.3)) - '@internationalized/date': 3.11.0 - '@internationalized/number': 3.6.5 - '@tanstack/vue-virtual': 3.13.21(vue@3.5.29(typescript@5.6.3)) - '@vueuse/core': 14.2.1(vue@3.5.29(typescript@5.6.3)) - '@vueuse/shared': 14.2.1(vue@3.5.29(typescript@5.6.3)) + '@floating-ui/vue': 2.0.1(vue@3.5.44(typescript@6.0.3)) + '@internationalized/date': 3.12.4 + '@internationalized/number': 3.6.9 + '@tanstack/vue-virtual': 3.13.51(vue@3.5.44(typescript@6.0.3)) + '@vueuse/core': 14.4.0(vue@3.5.44(typescript@6.0.3)) + '@vueuse/shared': 14.4.0(vue@3.5.44(typescript@6.0.3)) aria-hidden: 1.2.6 - defu: 6.1.4 - ohash: 2.0.11 - vue: 3.5.29(typescript@5.6.3) - transitivePeerDependencies: - - '@vue/composition-api' - - rfdc@1.4.1: {} + defu: 6.1.7 + ohash: 2.0.12 + vue: 3.5.44(typescript@6.0.3) - rollup@4.59.0: + rolldown@1.2.16: dependencies: - '@types/estree': 1.0.8 + '@oxc-project/types': 0.156.0 + '@rolldown/pluginutils': 1.0.1 optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.59.0 - '@rollup/rollup-android-arm64': 4.59.0 - '@rollup/rollup-darwin-arm64': 4.59.0 - '@rollup/rollup-darwin-x64': 4.59.0 - '@rollup/rollup-freebsd-arm64': 4.59.0 - '@rollup/rollup-freebsd-x64': 4.59.0 - '@rollup/rollup-linux-arm-gnueabihf': 4.59.0 - '@rollup/rollup-linux-arm-musleabihf': 4.59.0 - '@rollup/rollup-linux-arm64-gnu': 4.59.0 - '@rollup/rollup-linux-arm64-musl': 4.59.0 - '@rollup/rollup-linux-loong64-gnu': 4.59.0 - '@rollup/rollup-linux-loong64-musl': 4.59.0 - '@rollup/rollup-linux-ppc64-gnu': 4.59.0 - '@rollup/rollup-linux-ppc64-musl': 4.59.0 - '@rollup/rollup-linux-riscv64-gnu': 4.59.0 - '@rollup/rollup-linux-riscv64-musl': 4.59.0 - '@rollup/rollup-linux-s390x-gnu': 4.59.0 - '@rollup/rollup-linux-x64-gnu': 4.59.0 - '@rollup/rollup-linux-x64-musl': 4.59.0 - '@rollup/rollup-openbsd-x64': 4.59.0 - '@rollup/rollup-openharmony-arm64': 4.59.0 - '@rollup/rollup-win32-arm64-msvc': 4.59.0 - '@rollup/rollup-win32-ia32-msvc': 4.59.0 - '@rollup/rollup-win32-x64-gnu': 4.59.0 - '@rollup/rollup-win32-x64-msvc': 4.59.0 - fsevents: 2.3.3 - - siginfo@2.0.0: {} + '@rolldown/binding-android-arm-eabi': 1.2.16 + '@rolldown/binding-android-arm64': 1.2.16 + '@rolldown/binding-darwin-arm64': 1.2.16 + '@rolldown/binding-darwin-x64': 1.2.16 + '@rolldown/binding-freebsd-x64': 1.2.16 + '@rolldown/binding-linux-arm-gnueabihf': 1.2.16 + '@rolldown/binding-linux-arm64-gnu': 1.2.16 + '@rolldown/binding-linux-arm64-musl': 1.2.16 + '@rolldown/binding-linux-ppc64-gnu': 1.2.16 + '@rolldown/binding-linux-s390x-gnu': 1.2.16 + '@rolldown/binding-linux-x64-gnu': 1.2.16 + '@rolldown/binding-linux-x64-musl': 1.2.16 + '@rolldown/binding-openharmony-arm64': 1.2.16 + '@rolldown/binding-win32-arm64-msvc': 1.2.16 + '@rolldown/binding-win32-x64-msvc': 1.2.16 - source-map-js@1.2.1: {} + source-map-js@1.2.2: {} - speakingurl@14.0.1: {} + std-env@4.3.0: {} - stackback@0.0.2: {} - - std-env@3.10.0: {} - - style-mod@4.1.3: {} - - superjson@2.2.6: - dependencies: - copy-anything: 4.0.5 + style-mod@4.1.4: {} - tailwind-merge@3.5.0: {} + tailwind-merge@3.7.0: {} - tailwindcss@4.2.1: {} + tailwindcss@4.3.3: {} - tapable@2.3.0: {} + tapable@2.3.3: {} - tinybench@2.9.0: {} + tinybench@6.2.3: {} - tinyexec@1.0.2: {} + tinyexec@1.3.1: {} - tinyglobby@0.2.15: + tinyglobby@0.2.17: dependencies: - fdir: 6.5.0(picomatch@4.0.3) - picomatch: 4.0.3 + fdir: 6.5.0(picomatch@4.0.7) + picomatch: 4.0.7 - tinyrainbow@3.0.3: {} - - tslib@2.8.1: {} + tslib@2.9.1: {} tw-animate-css@1.4.0: {} - type-fest@4.41.0: {} - - typescript@5.6.3: {} + typescript@6.0.3: {} - undici-types@7.18.2: {} - - vee-validate@4.15.1(vue@3.5.29(typescript@5.6.3)): - dependencies: - '@vue/devtools-api': 7.7.9 - type-fest: 4.41.0 - vue: 3.5.29(typescript@5.6.3) + undici-types@8.9.0: {} - vite@6.4.1(@types/node@25.3.3)(jiti@2.6.1)(lightningcss@1.31.1): + vite@8.3.5(@types/node@26.6.6)(jiti@2.7.0): dependencies: - esbuild: 0.25.12 - fdir: 6.5.0(picomatch@4.0.3) - picomatch: 4.0.3 - postcss: 8.5.6 - rollup: 4.59.0 - tinyglobby: 0.2.15 + lightningcss: 1.33.0 + picomatch: 4.0.7 + postcss: 8.5.32 + rolldown: 1.2.16 + tinyglobby: 0.2.17 optionalDependencies: - '@types/node': 25.3.3 + '@types/node': 26.6.6 fsevents: 2.3.3 - jiti: 2.6.1 - lightningcss: 1.31.1 - - vitest@4.0.18(@types/node@25.3.3)(jiti@2.6.1)(lightningcss@1.31.1): - dependencies: - '@vitest/expect': 4.0.18 - '@vitest/mocker': 4.0.18(vite@6.4.1(@types/node@25.3.3)(jiti@2.6.1)(lightningcss@1.31.1)) - '@vitest/pretty-format': 4.0.18 - '@vitest/runner': 4.0.18 - '@vitest/snapshot': 4.0.18 - '@vitest/spy': 4.0.18 - '@vitest/utils': 4.0.18 - es-module-lexer: 1.7.0 - expect-type: 1.3.0 - magic-string: 0.30.21 - obug: 2.1.1 - pathe: 2.0.3 - picomatch: 4.0.3 - std-env: 3.10.0 - tinybench: 2.9.0 - tinyexec: 1.0.2 - tinyglobby: 0.2.15 - tinyrainbow: 3.0.3 - vite: 6.4.1(@types/node@25.3.3)(jiti@2.6.1)(lightningcss@1.31.1) - why-is-node-running: 2.3.0 + jiti: 2.7.0 + + vitest@5.0.5(@types/node@26.6.6)(happy-dom@21.0.4)(vite@8.3.5(@types/node@26.6.6)(jiti@2.7.0)): + dependencies: + '@types/chai': 5.2.3 + '@vitest/mocker': 5.0.5(vite@8.3.5(@types/node@26.6.6)(jiti@2.7.0)) + chai: 6.3.0 + es-module-lexer: 2.3.2 + expect-type: 1.4.0 + magic-string: 1.4.5 + obug: 3.0.0 + picomatch: 4.0.7 + std-env: 4.3.0 + tinybench: 6.2.3 + tinyexec: 1.3.1 + tinyglobby: 0.2.17 + vite: 8.3.5(@types/node@26.6.6)(jiti@2.7.0) + why-is-node-running: 3.2.1 optionalDependencies: - '@types/node': 25.3.3 + '@types/node': 26.6.6 + happy-dom: 21.0.4 transitivePeerDependencies: - - jiti - - less - - lightningcss - msw - - sass - - sass-embedded - - stylus - - sugarss - - terser - - tsx - - yaml - vscode-uri@3.1.0: {} + vscode-jsonrpc@10.0.0: {} - vue-demi@0.14.10(vue@3.5.29(typescript@5.6.3)): + vscode-languageserver-protocol@3.18.5: dependencies: - vue: 3.5.29(typescript@5.6.3) + vscode-jsonrpc: 10.0.0 + vscode-languageserver-types: 3.18.5 + + vscode-languageserver-textdocument@1.0.16: {} + + vscode-languageserver-types@3.18.5: {} + + vscode-uri@3.2.0: {} + + vue-demi@0.14.10(vue@3.5.44(typescript@6.0.3)): + dependencies: + vue: 3.5.44(typescript@6.0.3) vue-sonner@2.0.9: {} - vue-tsc@2.2.12(typescript@5.6.3): + vue-tsc@3.3.14(typescript@6.0.3): dependencies: - '@volar/typescript': 2.4.15 - '@vue/language-core': 2.2.12(typescript@5.6.3) - typescript: 5.6.3 + '@volar/typescript': 2.4.30(typescript@6.0.3) + '@vue/language-core': 3.3.14 + typescript: 6.0.3 - vue@3.5.29(typescript@5.6.3): + vue@3.5.44(typescript@6.0.3): dependencies: - '@vue/compiler-dom': 3.5.29 - '@vue/compiler-sfc': 3.5.29 - '@vue/runtime-dom': 3.5.29 - '@vue/server-renderer': 3.5.29(vue@3.5.29(typescript@5.6.3)) - '@vue/shared': 3.5.29 + '@vue/compiler-dom': 3.5.44 + '@vue/compiler-sfc': 3.5.44 + '@vue/runtime-dom': 3.5.44 + '@vue/server-renderer': 3.5.44 + '@vue/shared': 3.5.44 optionalDependencies: - typescript: 5.6.3 + typescript: 6.0.3 w3c-keyname@2.2.8: {} - why-is-node-running@2.3.0: - dependencies: - siginfo: 2.0.0 - stackback: 0.0.2 + whatwg-mimetype@3.0.0: {} + + why-is-node-running@3.2.1: {} + + ws@8.22.1: {} - zod@3.25.76: {} + zod@4.6.7: {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml new file mode 100644 index 0000000..a23eae0 --- /dev/null +++ b/pnpm-workspace.yaml @@ -0,0 +1,3 @@ +allowBuilds: + esbuild: true + vue-demi: true diff --git a/public/tauri.svg b/public/tauri.svg deleted file mode 100644 index 31b62c9..0000000 --- a/public/tauri.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/public/vite.svg b/public/vite.svg deleted file mode 100644 index e7b8dfb..0000000 --- a/public/vite.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index d0f68bf..fd39a4b 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -9,14 +9,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" [[package]] -name = "ahash" -version = "0.7.8" +name = "aes" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9" +checksum = "fc0fc36d7440d015b45468ba08a21e80e056ebbd014c611c6298bb7503e04bd1" dependencies = [ - "getrandom 0.2.17", - "once_cell", - "version_check", + "cipher", + "cpubits", + "cpufeatures 0.3.1", ] [[package]] @@ -34,24 +34,24 @@ dependencies = [ [[package]] name = "aho-corasick" -version = "1.1.4" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +checksum = "c982642fa9e8606056828ee9a8505737230110bb1099153c79efe865c59d12ba" dependencies = [ "memchr", ] [[package]] name = "alloc-no-stdlib" -version = "2.0.4" +version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3" +checksum = "c2fb6cfd47bf496ff64095c20eaba0c201404ee38714d4142fcfa1dc334fcc7a" [[package]] name = "alloc-stdlib" -version = "0.2.2" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94fb8275041c72129eb51b7d0322c29b8387a0386127718b096429201a5d6ece" +checksum = "0b5c1865780388bfa186411ab5f247819487fc4864c6e9c3106611fa347586e1" dependencies = [ "alloc-no-stdlib", ] @@ -75,24 +75,29 @@ dependencies = [ [[package]] name = "android_system_properties" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +checksum = "ae221649c9976a6f6c56ae1facf410f3ddb33cc661c4b7b61020a912d4237fbc" dependencies = [ "libc", ] [[package]] name = "anyhow" -version = "1.0.102" +version = "1.0.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" +checksum = "330a5ed07fa54e4702c9d6c4174f74427fc0ef6e214bbd677ae50a5099946470" [[package]] -name = "arrayvec" -version = "0.7.6" +name = "apple-native-keyring-store" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" +checksum = "2b350bfd03649e07aa05c0a81b3e15934374e585c98204a57e20b9d49f49bb9a" +dependencies = [ + "keyring-core", + "log", + "security-framework", +] [[package]] name = "async-broadcast" @@ -120,9 +125,9 @@ dependencies = [ [[package]] name = "async-executor" -version = "1.14.0" +version = "1.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c96bf972d85afc50bf5ab8fe2d54d1586b4e0b46c97c50a0c9e71e2f7bcd812a" +checksum = "f426a31126c735126297bc5ea7a521263c9faff4f63ca415267ee625651c4f37" dependencies = [ "async-task", "concurrent-queue", @@ -152,9 +157,9 @@ dependencies = [ [[package]] name = "async-lock" -version = "3.4.2" +version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "290f7f2596bd5b78a9fec8088ccd89180d7f9f55b94b0576823bbbdc72ee8311" +checksum = "0876700b0d007ad25d5508865a4259c3af9af6d296bf663a0d586d0bc3369f6f" dependencies = [ "event-listener", "event-listener-strategy", @@ -181,20 +186,20 @@ dependencies = [ [[package]] name = "async-recursion" -version = "1.1.1" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" +checksum = "4f8abc12baad266b1c8cec146854c195b5864b4221d4b2ca7296a7ae82d9e451" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 3.0.9", ] [[package]] name = "async-signal" -version = "0.2.13" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43c070bbf59cd3570b6b2dd54cd772527c7c3620fce8be898406dd3ed6adc64c" +checksum = "52b5aaafa020cf5053a01f2a60e8ff5dccf550f0f77ec54a4e47285ac2bab485" dependencies = [ "async-io", "async-lock", @@ -216,13 +221,13 @@ checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" [[package]] name = "async-trait" -version = "0.1.89" +version = "0.1.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" +checksum = "82f6aeea286b8eb4dd3431a1be1b59d290ace00f5bfd8e2a159bc2a05e2c1667" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 3.0.9", ] [[package]] @@ -256,9 +261,9 @@ checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" [[package]] name = "autocfg" -version = "1.5.0" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" +checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" [[package]] name = "base64" @@ -272,6 +277,27 @@ version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" +[[package]] +name = "base64" +version = "0.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac07cdecf99051d9a5238b80f35af32cdeba5b336e55d957b318b50137e18da5" + +[[package]] +name = "bit-set" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3" +dependencies = [ + "bit-vec", +] + +[[package]] +name = "bit-vec" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" + [[package]] name = "bitflags" version = "1.3.2" @@ -280,18 +306,18 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.11.0" +version = "2.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" +checksum = "3ded4057c258ba199e2d26386d3af3780957ecaee6c4ef4041c6b4b8b97c0b06" dependencies = [ "serde_core", ] [[package]] name = "bitvec" -version = "1.0.1" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +checksum = "ddcec3d12c579d40898fe0a9a358a803c23e9c52ca3c425707f81c9436211837" dependencies = [ "funty", "radium", @@ -309,55 +335,50 @@ dependencies = [ ] [[package]] -name = "block2" -version = "0.6.2" +name = "block-buffer" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdeb9d870516001442e364c5220d3574d2da8dc765554b4a617230d33fa58ef5" +checksum = "d2f6c7dbe95a6ed67ad9f18e57daf93a2f034c524b99fd2b76d18fdfeb6660aa" dependencies = [ - "objc2", + "hybrid-array", ] [[package]] -name = "blocking" -version = "1.6.2" +name = "block-padding" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e83f8d02be6967315521be875afa792a316e28d57b5a2d401897e2a7921b7f21" +checksum = "710f1dd022ef4e93f8a438b4ba958de7f64308434fa6a87104481645cc30068b" dependencies = [ - "async-channel", - "async-task", - "futures-io", - "futures-lite", - "piper", + "hybrid-array", ] [[package]] -name = "borsh" -version = "1.6.0" +name = "block2" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1da5ab77c1437701eeff7c88d968729e7766172279eab0676857b3d63af7a6f" +checksum = "cdeb9d870516001442e364c5220d3574d2da8dc765554b4a617230d33fa58ef5" dependencies = [ - "borsh-derive", - "cfg_aliases", + "objc2", ] [[package]] -name = "borsh-derive" -version = "1.6.0" +name = "blocking" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0686c856aa6aac0c4498f936d7d6a02df690f614c03e4d906d1018062b5c5e2c" +checksum = "a70e4329df6cb94385eed412ec92375c3cdd8a6e502493d1229b6414e4036dfa" dependencies = [ - "once_cell", - "proc-macro-crate 3.4.0", - "proc-macro2", - "quote", - "syn 2.0.117", + "async-channel", + "async-task", + "futures-io", + "futures-lite", + "piper", ] [[package]] name = "brotli" -version = "8.0.2" +version = "9.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bd8b9603c7aa97359dbd97ecf258968c95f3adddd6db2f7e7a5bef101c84560" +checksum = "627b7cfb0578f0eafce087a90fcd40add1231900b69e4d4de2d456260a4f5d81" dependencies = [ "alloc-no-stdlib", "alloc-stdlib", @@ -366,30 +387,39 @@ dependencies = [ [[package]] name = "brotli-decompressor" -version = "5.0.0" +version = "6.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "874bb8112abecc98cbd6d81ea4fa7e94fb9449648c93cc89aa40c81c24d7de03" +checksum = "34c6783f42c21c952ad7112b354f0ba2f10760e9decea5b1b60523d6e270faaf" dependencies = [ "alloc-no-stdlib", "alloc-stdlib", ] +[[package]] +name = "bs58" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf88ba1141d185c399bee5288d850d63b8369520c1eafc32a0430b5b6c287bf4" +dependencies = [ + "tinyvec", +] + [[package]] name = "bson" version = "2.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7969a9ba84b0ff843813e7249eed1678d9b6607ce5a3b8f0a47af3fcf7978e6e" dependencies = [ - "ahash 0.8.12", + "ahash", "base64 0.22.1", "bitvec", "getrandom 0.2.17", "getrandom 0.3.4", "hex", - "indexmap 2.13.0", + "indexmap 2.14.2", "js-sys", "once_cell", - "rand 0.9.2", + "rand 0.9.5", "serde", "serde_bytes", "serde_json", @@ -399,49 +429,15 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.20.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" - -[[package]] -name = "byte-unit" -version = "5.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c6d47a4e2961fb8721bcfc54feae6455f2f64e7054f9bc67e875f0e77f4c58d" -dependencies = [ - "rust_decimal", - "schemars 1.2.1", - "serde", - "utf8-width", -] - -[[package]] -name = "bytecheck" -version = "0.6.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23cdc57ce23ac53c931e88a43d06d070a6fd142f2617be5855eb75efc9beb1c2" -dependencies = [ - "bytecheck_derive", - "ptr_meta", - "simdutf8", -] - -[[package]] -name = "bytecheck_derive" -version = "0.6.12" +version = "3.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3db406d29fbcd95542e92559bed4d8ad92636d1ca8b3b72ede10b4bcc010e659" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] +checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" [[package]] name = "bytemuck" -version = "1.25.0" +version = "1.25.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8efb64bd706a16a1bdde310ae86b351e4d21550d98d056f22f8a7f7a2183fec" +checksum = "95832e849adfb21180ccb6826a99da14e5d266ae5c2e668e1602cf234f153797" [[package]] name = "byteorder" @@ -451,9 +447,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.11.1" +version = "1.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" +checksum = "fc652a48c352aef3ea3aed32080501cf3ef6ed5da78602a020c991775b0aff04" dependencies = [ "serde", ] @@ -464,7 +460,7 @@ version = "0.18.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ca26ef0159422fb77631dc9d17b102f253b876fe1586b03b803e63a309b4ee2" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "cairo-sys-rs", "glib", "libc", @@ -485,9 +481,9 @@ dependencies = [ [[package]] name = "camino" -version = "1.2.2" +version = "1.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e629a66d692cb9ff1a1c664e41771b3dcaf961985a9774c0eb0bd1b51cf60a48" +checksum = "bbbad30e4b4c14a39e3cc8aed085a12a327257c316619c93581e017bc52be591" dependencies = [ "serde_core", ] @@ -512,24 +508,34 @@ dependencies = [ "semver", "serde", "serde_json", - "thiserror 2.0.18", + "thiserror 2.0.21", ] [[package]] name = "cargo_toml" -version = "0.22.3" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "374b7c592d9c00c1f4972ea58390ac6b18cbb6ab79011f3bdc90a0b82ca06b77" +checksum = "82f4b26e751e711a5302649417f2da046dce6391b2ea30a4820f37462314f0b9" dependencies = [ + "semver", "serde", - "toml 0.9.12+spec-1.1.0", + "toml 1.1.8+spec-1.1.0", +] + +[[package]] +name = "cbc" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce2dc9ee5f88d11e0beb842c88b33c8a5cf0d1329c4b19494af42b07dbfe8896" +dependencies = [ + "cipher", ] [[package]] name = "cc" -version = "1.2.56" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aebf35691d1bfb0ac386a69bac2fde4dd276fb618cf8bf4f5318fe285e821bb2" +checksum = "375989decec1a0fa424442bed520fa12ede43a48475ce8c7417d2c69b32b5f6b" dependencies = [ "find-msvc-tools", "shlex", @@ -543,13 +549,13 @@ checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" [[package]] name = "cfb" -version = "0.7.3" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d38f2da7a0a2c4ccf0065be06397cc26a81f4e528be095826eee9d4adbb8c60f" +checksum = "a347dcabdae9c31b0825fd6a8bed285ec9c2acb89c47827126d52fa4f59cece3" dependencies = [ - "byteorder", "fnv", "uuid", + "web-time", ] [[package]] @@ -564,33 +570,54 @@ dependencies = [ [[package]] name = "cfg-if" -version = "1.0.4" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" +checksum = "4e7648175b45a9a48536d676f68d918270699102aa8dab5496df06904c914600" [[package]] -name = "cfg_aliases" -version = "0.2.1" +name = "chacha20" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" +checksum = "65c35e4b699c7e15ccbe7ee35c005e4fc0a278d22238a2857e6ce2dadeda1b06" +dependencies = [ + "cfg-if", + "cpufeatures 0.3.1", + "rand_core 0.10.1", +] [[package]] name = "chrono" -version = "0.4.44" +version = "0.4.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c673075a2e0e5f4a1dde27ce9dee1ea4558c7ffe648f576438a20ca1d2acc4b0" +checksum = "1aa79e62e7697b8e29b513a68abacf485adcd1fe8284a4316c5ae868e6633327" dependencies = [ "iana-time-zone", "num-traits", "serde", - "windows-link 0.2.1", + "windows-link", +] + +[[package]] +name = "cipher" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8cf2a2c93cd704877c0858356ed03480ff301ee950b43f1cbe4573b088bfa6c" +dependencies = [ + "crypto-common 0.2.2", + "inout", ] +[[package]] +name = "cmov" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c9ea0ac24bc397ab3c98583a3c9ba74fa56b09a4449bbe172b9b1ddb016027a" + [[package]] name = "combine" -version = "4.6.7" +version = "4.6.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" +checksum = "4356eba6976435498d05a311ee9fc486da3bc81c200d3f0d7dd9703b41913f92" dependencies = [ "bytes", "memchr", @@ -605,6 +632,12 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "const-oid" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6ef517f0926dd24a1582492c791b6a4818a4d94e789a334894aa15b0d12f55c" + [[package]] name = "const-random" version = "0.1.18" @@ -625,12 +658,6 @@ dependencies = [ "tiny-keccak", ] -[[package]] -name = "convert_case" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" - [[package]] name = "convert_case" version = "0.10.0" @@ -642,9 +669,9 @@ dependencies = [ [[package]] name = "cookie" -version = "0.18.1" +version = "0.18.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ddef33a339a91ea89fb53151bd0a4689cfce27055c291dfa69945475d22c747" +checksum = "1a373e3602691c3cdea496d2f0ee5935151e6168fe87739483c463db1b2f2f87" dependencies = [ "time", "version_check", @@ -678,14 +705,14 @@ checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] name = "core-graphics" -version = "0.24.0" +version = "0.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa95a34622365fa5bbf40b20b75dba8dfa8c94c734aea8ac9a5ca38af14316f1" +checksum = "064badf302c3194842cf2c5d61f56cc88e54a759313879cdf03abdd27d0c3b97" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "core-foundation 0.10.1", "core-graphics-types", - "foreign-types 0.5.0", + "foreign-types", "libc", ] @@ -695,11 +722,17 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d44a101f213f6c4cdc1853d4b78aef6db6bdfa3468798cc1d9912f4735013eb" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "core-foundation 0.10.1", "libc", ] +[[package]] +name = "cpubits" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15b85f9c39137c3a891689859392b1bd49812121d0d61c9caf00d46ed5ce06ae" + [[package]] name = "cpufeatures" version = "0.2.17" @@ -709,11 +742,20 @@ dependencies = [ "libc", ] +[[package]] +name = "cpufeatures" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ca28b0ae3115b884660db4118d803791fd6756b6e88f39c0f3f7859060d7566" +dependencies = [ + "libc", +] + [[package]] name = "crc32fast" -version = "1.5.0" +version = "1.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" +checksum = "5f97d3416d5337d15c4b0d78c8c91df922ca143f57ec20e2d1c188333047ef04" dependencies = [ "cfg-if", ] @@ -726,27 +768,27 @@ checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b" [[package]] name = "crossbeam-channel" -version = "0.5.15" +version = "0.5.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2" +checksum = "98b0cc327b5bc766e7fda9c9260cc0fa81b43a8e240440422dff70788e3f9ef1" dependencies = [ "crossbeam-utils", ] [[package]] name = "crossbeam-epoch" -version = "0.9.18" +version = "0.9.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +checksum = "dc74980687109a3b14c72fd458107bf0baa1da1a1a805e178d15501ba9b86d9d" dependencies = [ "crossbeam-utils", ] [[package]] name = "crossbeam-utils" -version = "0.8.21" +version = "0.8.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" +checksum = "a31eee39dddec8330830986fcd7625edb5a24ec90ea038215273bbc3adb08ac6" [[package]] name = "crunchy" @@ -764,48 +806,58 @@ dependencies = [ "typenum", ] +[[package]] +name = "crypto-common" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce6e4c961d6cd6c9a86db418387425e8bdeaf05b3c8bc1411e6dca4c252f1453" +dependencies = [ + "hybrid-array", +] + [[package]] name = "cssparser" -version = "0.29.6" +version = "0.37.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f93d03419cb5950ccfd3daf3ff1c7a36ace64609a1a8746d493df1ca0afde0fa" +checksum = "8c9cdaae01d5ed7882b04d795e7f752f46ff52d2fa3b50a20d28c464510bba98" dependencies = [ "cssparser-macros", "dtoa-short", "itoa", - "matches", - "phf 0.10.1", - "proc-macro2", - "quote", + "phf", "smallvec", - "syn 1.0.109", ] [[package]] name = "cssparser-macros" -version = "0.6.1" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13b588ba4ac1a99f7f2964d24b3d896ddc6bf847ee3855dbd4366f058cfcd331" +checksum = "d045de693cb712d0b22c6a64be5b953f67b3ce00ab5ad3dd5d8b441886ab8e1a" dependencies = [ "quote", - "syn 2.0.117", + "syn 3.0.9", ] [[package]] name = "ctor" -version = "0.2.9" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "914a755b7c2d4af2bdcff7ce1739e2db9a1b81a9b07123d8015786ae03c0980d" + +[[package]] +name = "ctutils" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a2785755761f3ddc1492979ce1e48d2c00d09311c39e4466429188f3dd6501" +checksum = "261af4dc7a06ea8a527a8717d8cbba3417530d6ea14e1edb3de6b67806be474f" dependencies = [ - "quote", - "syn 2.0.117", + "cmov", ] [[package]] name = "darling" -version = "0.21.3" +version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cdf337090841a411e2a7f3deb9187445851f91b309c0c0a29e05f74a00a48c0" +checksum = "ed17f5901b6630b993ca003def43f2f8ef4014fc13b047b57aad617ff32bc2ec" dependencies = [ "darling_core", "darling_macro", @@ -813,64 +865,82 @@ dependencies = [ [[package]] name = "darling_core" -version = "0.21.3" +version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1247195ecd7e3c85f83c8d2a366e4210d588e802133e1e355180a9870b517ea4" +checksum = "6837e2cf7485aaae18f86181d2f0e9a7ed297a025e220aeabf63fdebd3a2ddff" dependencies = [ - "fnv", "ident_case", "proc-macro2", "quote", "strsim", - "syn 2.0.117", + "syn 3.0.9", ] [[package]] name = "darling_macro" -version = "0.21.3" +version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d38308df82d1080de0afee5d069fa14b0326a88c14f15c5ccda35b4a6c414c81" +checksum = "2ac7135c3ef02b2f7833bbeb1be5ba7f966dcde8a87c6b87f65a778d71a02785" dependencies = [ "darling_core", "quote", - "syn 2.0.117", + "syn 3.0.9", ] [[package]] name = "data-encoding" -version = "2.10.0" +version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7a1e2f27636f116493b8b860f5546edb47c8d8f8ea73e1d2a20be88e28d1fea" +checksum = "4583a4551df46e2792f82ceeac45e850d2e2d5debba0b91f102385cda5b11f06" [[package]] name = "dbus" -version = "0.9.10" +version = "0.9.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21b3aa68d7e7abee336255bd7248ea965cc393f3e70411135a6f6a4b651345d4" +checksum = "3ab69f03cc8c4340c9c8e315114e1658e6775a9b16a04357973aa21cec22b32e" dependencies = [ "libc", "libdbus-sys", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] -name = "dbus-secret-service" -version = "4.1.0" +name = "defmt" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "708b509edf7889e53d7efb0ffadd994cc6c2345ccb62f55cfd6b0682165e4fa6" +checksum = "e4a0795ac740cb54d7b3775c9c17345f6aaecf884c01733b39d6017738e0e3e1" dependencies = [ - "dbus", - "openssl", - "zeroize", + "bitflags 1.3.2", + "defmt-macros", +] + +[[package]] +name = "defmt-macros" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "840bb056bd99c495c9188198a258ab787c79f306d864db3e6e8451442d6d8366" +dependencies = [ + "defmt-parser", + "proc-macro2", + "quote", + "syn 3.0.9", +] + +[[package]] +name = "defmt-parser" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71958f69fb2de856c5dd51ac9377e986ca1aa5919f931bf5395a807277f889a0" +dependencies = [ + "thiserror 2.0.21", ] [[package]] name = "deranged" -version = "0.5.8" +version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" +checksum = "9e9de72ce2ad1f90dc62fa25f0f430ef85eb4b0d8fa0be4f30373bc40a21d28e" dependencies = [ - "powerfmt", "serde_core", ] @@ -882,31 +952,18 @@ checksum = "d65d7ce8132b7c0e54497a4d9a55a1c2a0912a0d786cf894472ba818fba45762" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] name = "derive-where" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef941ded77d15ca19b40374869ac6000af1c9f2a4c0f3d4c70926287e6364a8f" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "derive_more" -version = "0.99.20" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6edb4b64a43d977b8e99788fe3a04d483834fba1215a7e02caa415b626497f7f" +checksum = "2e2b94854e8576378ccda7c8de8a66ed8b4e8acbd2c50ec3418ea6c8aaf4b567" dependencies = [ - "convert_case 0.4.0", "proc-macro2", "quote", - "rustc_version", - "syn 2.0.117", + "syn 3.0.9", ] [[package]] @@ -924,11 +981,11 @@ version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "799a97264921d8623a957f6c3b9011f3b5492f557bbb7a5a19b7fa6d06ba8dcb" dependencies = [ - "convert_case 0.10.0", + "convert_case", "proc-macro2", "quote", "rustc_version", - "syn 2.0.117", + "syn 2.0.119", "unicode-xid", ] @@ -938,16 +995,27 @@ version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ - "block-buffer", - "crypto-common", - "subtle", + "block-buffer 0.10.4", + "crypto-common 0.1.7", +] + +[[package]] +name = "digest" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1dd6dbb5841937940781866fa1281a1ff7bd3bf827091440879f9994983d5c2" +dependencies = [ + "block-buffer 0.12.1", + "const-oid", + "crypto-common 0.2.2", + "ctutils", ] [[package]] name = "dirs" -version = "6.0.0" +version = "7.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3e8aa94d75141228480295a7d0e7feb620b1a5ad9f12bc40be62411e38cce4e" +checksum = "8d57d423b3c82e89b9a24ca3091fee61f456a26edbd28d26c65906f4bc1dcd8f" dependencies = [ "dirs-sys", ] @@ -964,31 +1032,27 @@ dependencies = [ "windows-sys 0.61.2", ] -[[package]] -name = "dispatch" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b" - [[package]] name = "dispatch2" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e0e367e4e7da84520dedcac1901e4da967309406d1e51017ae1abfb97adbd38" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", + "block2", + "libc", "objc2", ] [[package]] name = "displaydoc" -version = "0.2.5" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +checksum = "c6232dd377dcc64799954cbd3a9bb882e9cdc1308ccd87b1c098f1fb2eaf82a8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 3.0.9", ] [[package]] @@ -1011,7 +1075,22 @@ checksum = "0fbbb781877580993a8707ec48672673ec7b81eeba04cfd2310bd28c08e47c8f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", +] + +[[package]] +name = "dom_query" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fac5fca71e65e94cc718a6e2af65d6e0f9c6027751c2aa562fbb5087fda639bc" +dependencies = [ + "bit-set", + "cssparser", + "foldhash", + "html5ever", + "precomputed-hash", + "selectors", + "tendril", ] [[package]] @@ -1050,18 +1129,24 @@ version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" +[[package]] +name = "either" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e9c71c2167ca323c882b99918929403426e2373ea17242ff5653e0d5e1058be" + [[package]] name = "embed-resource" -version = "3.0.6" +version = "3.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55a075fc573c64510038d7ee9abc7990635863992f83ebc52c8b433b8411a02e" +checksum = "6168487b5e7eb2673e6efe1200b1848016e436a6ca0b8031bf04bd9d493a249f" dependencies = [ "cc", "memchr", "rustc_version", - "toml 0.9.12+spec-1.1.0", + "toml 1.1.8+spec-1.1.0", "vswhom", - "winreg 0.55.0", + "winreg", ] [[package]] @@ -1076,18 +1161,6 @@ version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "66b7e2430c6dff6a955451e2cfc438f09cea1965a9d6f87f7e3b90decc014099" -[[package]] -name = "enum-as-inner" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1e6a265c649f3f5979b601d26f1d05ada116434c87741c9493cb56218f76cbc" -dependencies = [ - "heck 0.5.0", - "proc-macro2", - "quote", - "syn 2.0.117", -] - [[package]] name = "enumflags2" version = "0.7.12" @@ -1106,7 +1179,7 @@ checksum = "67c78a4d8fdf9953a5c9d458f9efe940fd97a0cab0941c075a813ac594733827" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -1121,15 +1194,15 @@ dependencies = [ [[package]] name = "equivalent" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" +checksum = "00d174d5400e5e8fd687ad1049e2f578285fa914201b1af7e8b112a4546bd826" [[package]] name = "erased-serde" -version = "0.4.9" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89e8918065695684b2b0702da20382d5ae6065cf3327bc2d6436bd49a71ce9f3" +checksum = "1e59b668157f32d3459d4bd634861a06ffa2503a461ca5f0916b9f0252605e66" dependencies = [ "serde", "serde_core", @@ -1148,11 +1221,10 @@ dependencies = [ [[package]] name = "event-listener" -version = "5.4.1" +version = "5.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13b66accf52311f30a0db42147dadea9850cb48cd070028831ae5f5d4b856ab" +checksum = "5a23add41df1562121a9393cb065eab5146a1242410f23a644851e90cfd669d2" dependencies = [ - "concurrent-queue", "parking", "pin-project-lite", ] @@ -1169,9 +1241,9 @@ dependencies = [ [[package]] name = "fastrand" -version = "2.3.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" +checksum = "da7c62ceae207dd37ea5b845da6a0696c799f85e97da1ab5b7910be3c1c80223" [[package]] name = "fdeflate" @@ -1203,18 +1275,19 @@ dependencies = [ [[package]] name = "find-msvc-tools" -version = "0.1.9" +version = "0.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" +checksum = "1c66915cc5391097de7a2b0b972e8505aa33c0df401fd3a6811dbdb32881ba48" [[package]] name = "flate2" -version = "1.1.9" +version = "1.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c" +checksum = "6e634e2e0ebac1ee034020da1ca582e17ffe4e0f5e985823721e168928136dcb" dependencies = [ "crc32fast", - "miniz_oxide", + "miniz_oxide 0.9.1", + "zlib-rs", ] [[package]] @@ -1225,18 +1298,9 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "foldhash" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" - -[[package]] -name = "foreign-types" -version = "0.3.2" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" -dependencies = [ - "foreign-types-shared 0.1.1", -] +checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" [[package]] name = "foreign-types" @@ -1245,26 +1309,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965" dependencies = [ "foreign-types-macros", - "foreign-types-shared 0.3.1", + "foreign-types-shared", ] [[package]] name = "foreign-types-macros" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" +checksum = "ea5190182e6915eb873ddbc16e23b711b6eb1f9c00a0d0a3a91b5f6228475225" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 3.0.9", ] -[[package]] -name = "foreign-types-shared" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" - [[package]] name = "foreign-types-shared" version = "0.3.1" @@ -1286,21 +1344,11 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" -[[package]] -name = "futf" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df420e2e84819663797d1ec6544b13c5be84629e7bb00dc960d6917db2987843" -dependencies = [ - "mac", - "new_debug_unreachable", -] - [[package]] name = "futures" -version = "0.3.32" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d" +checksum = "9a31d2a3fbaaeb2af2368bbdd904aa8e812d3c04a1ee10d3171f52d556e5d0a3" dependencies = [ "futures-channel", "futures-core", @@ -1313,9 +1361,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.32" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" +checksum = "b1f9e3d69d39e4862ffed03ed071a76f9a13ba1d9109d355b0f0aa6b15e393c4" dependencies = [ "futures-core", "futures-sink", @@ -1323,15 +1371,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.32" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" +checksum = "92d699e522242e69e3003b94ecc1f960f3a5e015aa7c5d7486e65ad01dd94f5e" [[package]] name = "futures-executor" -version = "0.3.32" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d" +checksum = "031b47cf1a3c6cc8bc2fc76cd437f521619387907d469316e7c0bc278f1f5432" dependencies = [ "futures-core", "futures-task", @@ -1340,9 +1388,9 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.32" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" +checksum = "53c0fa8157de1303bfffdaa1cc2a673bfffb60102f76b0ef4441659124373fed" [[package]] name = "futures-lite" @@ -1359,32 +1407,32 @@ dependencies = [ [[package]] name = "futures-macro" -version = "0.3.32" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" +checksum = "9fb9654ba8355388abeb8dcb4fc62f511300867002afc858860463bdd9fe0c44" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 3.0.9", ] [[package]] name = "futures-sink" -version = "0.3.32" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" +checksum = "1944426bf7d03f1d14f708785e4b33efd750b36d48a157b836b3efc15ede8e1d" [[package]] name = "futures-task" -version = "0.3.32" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" +checksum = "cd417de3d1d015fc3bfd2b1ea46dfc7bab72ef86f1cc7cc9c78e728b34a6d1fd" [[package]] name = "futures-util" -version = "0.3.32" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +checksum = "0d50a92467f8ba5dd6e3ee5d4bd04d73ab2e4e1c44474a0674821dfce14b79bc" dependencies = [ "futures-channel", "futures-core", @@ -1397,15 +1445,6 @@ dependencies = [ "slab", ] -[[package]] -name = "fxhash" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" -dependencies = [ - "byteorder", -] - [[package]] name = "gdk" version = "0.18.2" @@ -1515,17 +1554,6 @@ dependencies = [ "version_check", ] -[[package]] -name = "getrandom" -version = "0.1.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" -dependencies = [ - "cfg-if", - "libc", - "wasi 0.9.0+wasi-snapshot-preview1", -] - [[package]] name = "getrandom" version = "0.2.17" @@ -1535,7 +1563,7 @@ dependencies = [ "cfg-if", "js-sys", "libc", - "wasi 0.11.1+wasi-snapshot-preview1", + "wasi", "wasm-bindgen", ] @@ -1548,22 +1576,21 @@ dependencies = [ "cfg-if", "js-sys", "libc", - "r-efi", + "r-efi 5.3.0", "wasip2", "wasm-bindgen", ] [[package]] name = "getrandom" -version = "0.4.1" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "139ef39800118c7683f2fd3c98c1b23c09ae076556b435f8e9064ae108aaeeec" +checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099" dependencies = [ "cfg-if", "libc", - "r-efi", - "wasip2", - "wasip3", + "r-efi 6.0.0", + "rand_core 0.10.1", ] [[package]] @@ -1604,7 +1631,7 @@ version = "0.18.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "233daaf6e83ae6a12a52055f568f9d7cf4671dabb78ff9560ab6da230ce00ee5" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "futures-channel", "futures-core", "futures-executor", @@ -1632,7 +1659,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -1647,9 +1674,9 @@ dependencies = [ [[package]] name = "glob" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" +checksum = "e4eba85ea1d0a966a983acd07deee566e67395d2d96b6fb39e62b5a833f1eb0b" [[package]] name = "gobject-sys" @@ -1711,7 +1738,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -1719,24 +1746,12 @@ name = "hashbrown" version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" -dependencies = [ - "ahash 0.7.8", -] - -[[package]] -name = "hashbrown" -version = "0.15.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" -dependencies = [ - "foldhash", -] [[package]] name = "hashbrown" -version = "0.16.1" +version = "0.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" +checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" [[package]] name = "heck" @@ -1752,9 +1767,9 @@ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "hermit-abi" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" +checksum = "e17592d60ebacc7d5e169f4663c5f84f9161cc90328abcfe8456f41e4dfcb284" [[package]] name = "hex" @@ -1763,77 +1778,108 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" [[package]] -name = "hickory-proto" -version = "0.25.2" +name = "hickory-net" +version = "0.26.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8a6fe56c0038198998a6f217ca4e7ef3a5e51f46163bd6dd60b5c71ca6c6502" +checksum = "c480823ed7c2c5d0f09c41020cb6b7c28029ce60ec42dc942158dcf22f8e0a4d" dependencies = [ "async-trait", "cfg-if", "data-encoding", - "enum-as-inner", "futures-channel", "futures-io", "futures-util", + "hickory-proto", + "idna", + "ipnet", + "jni 0.22.4", + "rand 0.10.3", + "thiserror 2.0.21", + "tinyvec", + "tokio", + "tracing", + "url", +] + +[[package]] +name = "hickory-proto" +version = "0.26.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12b92608f679a6fa515dd1d15c1ff89443026e391200a2c840c7afcba482893d" +dependencies = [ + "data-encoding", "idna", "ipnet", + "jni 0.22.4", "once_cell", - "rand 0.9.2", + "prefix-trie", + "rand 0.10.3", "ring", - "thiserror 2.0.18", + "thiserror 2.0.21", "tinyvec", - "tokio", "tracing", "url", ] [[package]] name = "hickory-resolver" -version = "0.25.2" +version = "0.26.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc62a9a99b0bfb44d2ab95a7208ac952d31060efc16241c87eaf36406fecf87a" +checksum = "3f3da5255c95d5a716857d54b5b8f4e8d67c3484d3beaaaae2ce25063b3ba981" dependencies = [ "cfg-if", "futures-util", + "hickory-net", "hickory-proto", "ipconfig", + "ipnet", + "jni 0.22.4", "moka", + "ndk-context", "once_cell", "parking_lot", - "rand 0.9.2", + "rand 0.10.3", "resolv-conf", "smallvec", - "thiserror 2.0.18", + "system-configuration", + "thiserror 2.0.21", "tokio", "tracing", ] +[[package]] +name = "hkdf" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4aaa26c720c68b866f2c96ef5c1264b3e6f473fe5d4ce61cd44bbe913e553018" +dependencies = [ + "hmac", +] + [[package]] name = "hmac" -version = "0.12.1" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +checksum = "6303bc9732ae41b04cb554b844a762b4115a61bfaa81e3e83050991eeb56863f" dependencies = [ - "digest", + "digest 0.11.3", ] [[package]] name = "html5ever" -version = "0.29.1" +version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b7410cae13cbc75623c98ac4cbfd1f0bedddf3227afc24f370cf0f50a44a11c" +checksum = "46a1761807faccc9a19e86944bbf40610014066306f96edcdedc2fb714bcb7b8" dependencies = [ "log", - "mac", "markup5ever", - "match_token", ] [[package]] name = "http" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a" +checksum = "918d3568bebf352712bc2ef3d46a8bcf1a75b373be6539de198e9105cbbf9ce0" dependencies = [ "bytes", "itoa", @@ -1841,9 +1887,9 @@ dependencies = [ [[package]] name = "http-body" -version = "1.0.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +checksum = "ca2a8f2913ee65f60facd6a5905613afaa448497a0230cc41ce022d93290bc2c" dependencies = [ "bytes", "http", @@ -1851,9 +1897,9 @@ dependencies = [ [[package]] name = "http-body-util" -version = "0.1.3" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" +checksum = "23169fe34a5fbcdd3f3862e78fb9b6fccd5f02a6dc6f732547005d45631ce71c" dependencies = [ "bytes", "futures-core", @@ -1868,22 +1914,29 @@ version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" +[[package]] +name = "hybrid-array" +version = "0.4.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27f864f10dfb56725ce5ce5472bc52252c8f93a4ab86327122cebf62c5f59a17" +dependencies = [ + "typenum", +] + [[package]] name = "hyper" -version = "1.8.1" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ab2d4f250c3d7b1c9fcdff1cece94ea4e2dfbec68614f7b87cb205f24ca9d11" +checksum = "2c3e324da4c95177d6291d4c8730197c0d1822f8a9766814a4a44fa5ab797c9c" dependencies = [ "atomic-waker", "bytes", - "futures-channel", "futures-core", "http", "http-body", "httparse", "itoa", "pin-project-lite", - "pin-utils", "smallvec", "tokio", "want", @@ -1891,22 +1944,23 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.20" +version = "0.1.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0" +checksum = "ddc03d96684f9226b8a787cdb71488417b53ab5ea8fdb1dac946cb9431cc8bff" dependencies = [ - "base64 0.22.1", + "base64 0.23.1", "bytes", "futures-channel", "futures-util", "http", "http-body", + "httparse", "hyper", "ipnet", "libc", "percent-encoding", "pin-project-lite", - "socket2 0.6.2", + "socket2", "tokio", "tower-service", "tracing", @@ -1924,7 +1978,7 @@ dependencies = [ "js-sys", "log", "wasm-bindgen", - "windows-core 0.62.2", + "windows-core", ] [[package]] @@ -1938,9 +1992,9 @@ dependencies = [ [[package]] name = "ico" -version = "0.5.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e795dff5605e0f04bff85ca41b51a96b83e80b281e96231bcaaf1ac35103371" +checksum = "d75e94d66081e4d8dcd098bd8dc5254800fea14a26389b298410158d834de167" dependencies = [ "byteorder", "png", @@ -1948,12 +2002,13 @@ dependencies = [ [[package]] name = "icu_collections" -version = "2.1.1" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c6b649701667bbe825c3b7e6388cb521c23d88644678e83c0c4d0a621a34b43" +checksum = "fe090a42a0d3140fed8c63263ed1fecc02422cb2f4bfe55cd8de2de1b8e105dd" dependencies = [ "displaydoc", "potential_utf", + "utf8_iter", "yoke", "zerofrom", "zerovec", @@ -1961,9 +2016,9 @@ dependencies = [ [[package]] name = "icu_locale_core" -version = "2.1.1" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edba7861004dd3714265b4db54a3c390e880ab658fec5f7db895fae2046b5bb6" +checksum = "25d29b57c17c6528d0dc4c2141cfa2d08200465acb537a3909c8b9b8a4a984c0" dependencies = [ "displaydoc", "litemap", @@ -1974,9 +2029,9 @@ dependencies = [ [[package]] name = "icu_normalizer" -version = "2.1.1" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f6c8828b67bf8908d82127b2054ea1b4427ff0230ee9141c54251934ab1b599" +checksum = "404a3b8c486f205c87fe7f23ab6894d84a4ad76d6e4671ae1981ba7def87c599" dependencies = [ "icu_collections", "icu_normalizer_data", @@ -1988,16 +2043,17 @@ dependencies = [ [[package]] name = "icu_normalizer_data" -version = "2.1.1" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a" +checksum = "20f0088bfb7441f7a37fa0c9e20cb1e9cba1cac6b9ce8cd2a6dc44294f8e8cbc" [[package]] name = "icu_properties" -version = "2.1.2" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "020bfc02fe870ec3a66d93e677ccca0562506e5872c650f893269e08615d74ec" +checksum = "2ea83d991c56158f7cf898bf86436c1dfe7c07ee82fe88951f2695c9f37a379f" dependencies = [ + "displaydoc", "icu_collections", "icu_locale_core", "icu_properties_data", @@ -2008,15 +2064,15 @@ dependencies = [ [[package]] name = "icu_properties_data" -version = "2.1.2" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "616c294cf8d725c6afcd8f55abc17c56464ef6211f9ed59cccffe534129c77af" +checksum = "b98f29af82d02b163fe50161388350e743bb3107cc8e8c2f7314cb505faddfcf" [[package]] name = "icu_provider" -version = "2.1.1" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85962cf0ce02e1e0a629cc34e7ca3e373ce20dda4c4d7294bbd0bf1fdb59e614" +checksum = "413099315636c588ee8f011190b329b971a09a8fae9d157e01dcae8379468514" dependencies = [ "displaydoc", "icu_locale_core", @@ -2027,12 +2083,6 @@ dependencies = [ "zerovec", ] -[[package]] -name = "id-arena" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" - [[package]] name = "ident_case" version = "1.0.1" @@ -2052,9 +2102,9 @@ dependencies = [ [[package]] name = "idna_adapter" -version = "1.2.1" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" +checksum = "cb68373c0d6620ef8105e855e7745e18b0d00d3bdb07fb532e434244cdb9a714" dependencies = [ "icu_normalizer", "icu_properties", @@ -2073,50 +2123,54 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.13.0" +version = "2.14.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" +checksum = "cc4e190f5d26ca7051642629da2c52fc03bde85a03197c99408dcd291734c855" dependencies = [ "equivalent", - "hashbrown 0.16.1", + "hashbrown 0.17.1", "serde", "serde_core", ] [[package]] name = "infer" -version = "0.19.0" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a588916bfdfd92e71cacef98a63d9b1f0d74d6599980d11894290e7ddefffcf7" +checksum = "f4200d433cbd5178df7797c9c2e75b348b728e39631cf14520d1e2fc424201f4" dependencies = [ "cfb", ] [[package]] -name = "ipconfig" -version = "0.3.2" +name = "inout" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b58db92f96b720de98181bbbe63c831e87005ab460c1bf306eb2622b4707997f" +checksum = "3b5cf4372d022092674a83b8febf5865f7a573cc0c5baec5e49c879b6a20978f" dependencies = [ - "socket2 0.5.10", - "widestring", - "windows-sys 0.48.0", - "winreg 0.50.0", + "block-padding", + "hybrid-array", ] [[package]] -name = "ipnet" -version = "2.11.0" +name = "ipconfig" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" +checksum = "4d40460c0ce33d6ce4b0630ad68ff63d6661961c48b6dba35e5a4d81cfb48222" +dependencies = [ + "socket2", + "widestring", + "windows-registry", + "windows-result", + "windows-sys 0.61.2", +] [[package]] -name = "iri-string" -version = "0.7.10" +name = "ipnet" +version = "2.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c91338f0783edbd6195decb37bae672fd3b165faffb89bf7b9e6942f8b1a731a" +checksum = "791930b43c0d5973160d90a8f3894509f2b273430f5c5c73b668636d0287c5c0" dependencies = [ - "memchr", "serde", ] @@ -2141,9 +2195,9 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.17" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" [[package]] name = "javascriptcore-rs" @@ -2168,6 +2222,60 @@ dependencies = [ "system-deps", ] +[[package]] +name = "jiff" +version = "0.2.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ac703058abac89e5c39b0b879dce2b6b14a4ba36600c7e7e3d0c60a6883ef3c" +dependencies = [ + "defmt", + "jiff-core", + "jiff-static", + "jiff-tzdb-platform", + "log", + "portable-atomic", + "portable-atomic-util", + "serde_core", + "windows-link", +] + +[[package]] +name = "jiff-core" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aee43309eb53bafb8c36d35d6e444769c8f8e6206a9fbf7546f5db9d7d4a0e93" +dependencies = [ + "defmt", + "log", +] + +[[package]] +name = "jiff-static" +version = "0.2.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dc32470c67eb7d0e9ccb74303fa925313dc04cecb1aa048beaa6f391e36a93d" +dependencies = [ + "jiff-core", + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "jiff-tzdb" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa8377070c6bae868759445e5a77f66d84f0b72f3a054bfb00e6d038b8282da7" + +[[package]] +name = "jiff-tzdb-platform" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "875a5a69ac2bab1a891711cf5eccbec1ce0341ea805560dcd90b7a2e925132e8" +dependencies = [ + "jiff-tzdb", +] + [[package]] name = "jni" version = "0.21.1" @@ -2177,46 +2285,99 @@ dependencies = [ "cesu8", "cfg-if", "combine", - "jni-sys", + "jni-sys 0.3.1", "log", "thiserror 1.0.69", "walkdir", "windows-sys 0.45.0", ] +[[package]] +name = "jni" +version = "0.22.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5efd9a482cf3a427f00d6b35f14332adc7902ce91efb778580e180ff90fa3498" +dependencies = [ + "cfg-if", + "combine", + "jni-macros", + "jni-sys 0.4.1", + "log", + "simd_cesu8", + "thiserror 2.0.21", + "walkdir", + "windows-link", +] + +[[package]] +name = "jni-macros" +version = "0.22.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a00109accc170f0bdb141fed3e393c565b6f5e072365c3bd58f5b062591560a3" +dependencies = [ + "proc-macro2", + "quote", + "rustc_version", + "simd_cesu8", + "syn 2.0.119", +] + [[package]] name = "jni-sys" -version = "0.3.0" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41a652e1f9b6e0275df1f15b32661cf0d4b78d4d87ddec5e0c3c20f097433258" +dependencies = [ + "jni-sys 0.4.1", +] + +[[package]] +name = "jni-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6377a88cb3910bee9b0fa88d4f42e1d2da8e79915598f65fb0c7ee14c878af2" +dependencies = [ + "jni-sys-macros", +] + +[[package]] +name = "jni-sys-macros" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" +checksum = "38c0b942f458fe50cdac086d2f946512305e5631e720728f2a61aabcd47a6264" +dependencies = [ + "quote", + "syn 2.0.119", +] [[package]] name = "js-sys" -version = "0.3.91" +version = "0.3.107" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b49715b7073f385ba4bc528e5747d02e66cb39c6146efb66b781f131f0fb399c" +checksum = "8dc09d2154098bbf4ef54eab6b117a3c24cbbffe94046b16ee6d6d4510584d3a" dependencies = [ - "once_cell", + "cfg-if", + "futures-util", "wasm-bindgen", ] [[package]] name = "json-patch" -version = "3.0.1" +version = "4.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "863726d7afb6bc2590eeff7135d923545e5e964f004c2ccf8716c25e70a86f08" +checksum = "7421438de105a0827e44fadd05377727847d717c80ce29a229f85fd04c427b72" dependencies = [ "jsonptr", "serde", "serde_json", - "thiserror 1.0.69", + "thiserror 2.0.21", ] [[package]] name = "jsonptr" -version = "0.6.3" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dea2b27dd239b2556ed7a25ba842fe47fd602e7fc7433c2a8d6106d4d9edd70" +checksum = "a5a3cc660ba5d72bce0b3bb295bf20847ccbb40fd423f3f05b61273672e561fe" dependencies = [ "serde", "serde_json", @@ -2224,56 +2385,35 @@ dependencies = [ [[package]] name = "keyboard-types" -version = "0.7.0" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b750dcadc39a09dbadd74e118f6dd6598df77fa01df0cfcdc52c28dece74528a" +checksum = "0fbe853b403ae61a04233030ae8a79d94975281ed9770a1f9e246732b534b28d" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "serde", - "unicode-segmentation", ] [[package]] name = "keyring" -version = "3.6.3" +version = "4.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eebcc3aff044e5944a8fbaf69eb277d11986064cba30c468730e8b9909fb551c" +checksum = "2270074a3d26bcac93c1dc5d2845eb4c089e8d761ccf6e0ea266a16004640627" dependencies = [ - "byteorder", - "dbus-secret-service", - "linux-keyutils", - "log", - "openssl", - "security-framework 2.11.1", - "security-framework 3.7.0", - "windows-sys 0.60.2", - "zeroize", + "apple-native-keyring-store", + "keyring-core", + "windows-native-keyring-store", + "zbus-secret-service-keyring-store", ] [[package]] -name = "kuchikiki" -version = "0.8.8-speedreader" +name = "keyring-core" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02cb977175687f33fa4afa0c95c112b987ea1443e5a51c8f8ff27dc618270cc2" +checksum = "29fbf4b551298c67925f38cf341a1ada797a75f1837bdf8ae1fc8e2ebc6efb59" dependencies = [ - "cssparser", - "html5ever", - "indexmap 2.13.0", - "selectors", + "log", ] -[[package]] -name = "lazy_static" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" - -[[package]] -name = "leb128fmt" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" - [[package]] name = "libappindicator" version = "0.9.0" @@ -2300,9 +2440,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.182" +version = "0.2.191" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6800badb6cb2082ffd7b6a67e6125bb39f18782f793520caee8cb8846be06112" +checksum = "65cd87c9a6d60536580b316ad985726104730a5151c99845f2490428ef91240a" [[package]] name = "libdbus-sys" @@ -2310,7 +2450,6 @@ version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "328c4789d42200f1eeec05bd86c9c13c7f091d2ba9a6ea35acdf51f31bc0f043" dependencies = [ - "cc", "pkg-config", ] @@ -2326,20 +2465,10 @@ dependencies = [ [[package]] name = "libredox" -version = "0.1.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1744e39d1d6a9948f4f388969627434e31128196de472883b39f148769bfe30a" -dependencies = [ - "libc", -] - -[[package]] -name = "linux-keyutils" -version = "0.2.4" +version = "0.1.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "761e49ec5fd8a5a463f9b84e877c373d888935b71c6be78f3767fe2ae6bed18e" +checksum = "61ff90caf6077a803a240f62fdbe88645a890bbca49ef8174c3cb0404362171d" dependencies = [ - "bitflags 2.11.0", "libc", ] @@ -2351,9 +2480,9 @@ checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" [[package]] name = "litemap" -version = "0.8.1" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77" +checksum = "47d9d19d1d6efa0109d2f65ff4c85cddd50bd572e5a00127ab10987290bcefae" [[package]] name = "lock_api" @@ -2366,18 +2495,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" -dependencies = [ - "value-bag", -] - -[[package]] -name = "mac" -version = "0.1.1" +version = "0.4.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4" +checksum = "5d15f86d12da44cd8d4ec3c73984537eb7f4cadcb9776a575a0a8a05ca11005c" [[package]] name = "macro_magic" @@ -2388,7 +2508,7 @@ dependencies = [ "macro_magic_core", "macro_magic_macros", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -2402,7 +2522,7 @@ dependencies = [ "macro_magic_core_macros", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -2413,7 +2533,7 @@ checksum = "b02abfe41815b5bd98dbd4260173db2c116dda171dc0fe7838cb206333b83308" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -2424,55 +2544,35 @@ checksum = "73ea28ee64b88876bf45277ed9a5817c1817df061a74f2b988971a12570e5869" dependencies = [ "macro_magic_core", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] name = "markup5ever" -version = "0.14.1" +version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7a7213d12e1864c0f002f52c2923d4556935a43dec5e71355c2760e0f6e7a18" +checksum = "7122d987ec5f704ee56f6e5b41a7d93722e9aae27ae07cafa4036c4d3f9757de" dependencies = [ "log", - "phf 0.11.3", - "phf_codegen 0.11.3", - "string_cache", - "string_cache_codegen", "tendril", + "web_atoms", ] -[[package]] -name = "match_token" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88a9689d8d44bf9964484516275f5cd4c9b59457a6940c1d5d0ecbb94510a36b" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "matches" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" - [[package]] name = "md-5" -version = "0.10.6" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf" +checksum = "69b6441f590336821bb897fb28fc622898ccceb1d6cea3fde5ea86b090c4de98" dependencies = [ "cfg-if", - "digest", + "digest 0.11.3", ] [[package]] name = "memchr" -version = "2.8.0" +version = "2.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" +checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98" [[package]] name = "memoffset" @@ -2499,22 +2599,32 @@ dependencies = [ "simd-adler32", ] +[[package]] +name = "miniz_oxide" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b63fbc4a50860e98e7b2aa7804ded1db5cbc3aff9193adaff57a6931bf7c4b4c" +dependencies = [ + "adler2", + "simd-adler32", +] + [[package]] name = "mio" -version = "1.1.1" +version = "1.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc" +checksum = "1788edb87fdc09c7e26304471e2f5be8cdefb1b6930d6e3985fc02ff53bf86ee" dependencies = [ "libc", - "wasi 0.11.1+wasi-snapshot-preview1", + "wasi", "windows-sys 0.61.2", ] [[package]] name = "moka" -version = "0.12.14" +version = "0.12.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85f8024e1c8e71c778968af91d43700ce1d11b219d127d79fb2934153b82b42b" +checksum = "4293f18e7567a1caf3c584855554377025c65e0aa445344d04171f5ad63d19b9" dependencies = [ "crossbeam-channel", "crossbeam-epoch", @@ -2529,9 +2639,9 @@ dependencies = [ [[package]] name = "mongocrypt" -version = "0.3.2" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8da0cd419a51a5fb44819e290fbdb0665a54f21dead8923446a799c7f4d26ad9" +checksum = "8426a875ded61430d4a811dbfda7633b6b8af0225c547fc6c28b8b0aa7d79a13" dependencies = [ "bson", "mongocrypt-sys", @@ -2541,25 +2651,26 @@ dependencies = [ [[package]] name = "mongocrypt-sys" -version = "0.1.5+1.15.1" +version = "0.1.6+1.18.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "224484c5d09285a7b8cb0a0c117e847ebd14cb6e4470ecf68cdb89c503b0edb9" +checksum = "851fac73f7fe22f6a3ab87f720ce509cae7c9fd08e7dd27866cc232dee07ccf4" [[package]] name = "mongodb" -version = "3.5.1" +version = "3.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "803dd859e8afa084c255a8effd8000ff86f7c8076a50cd6d8c99e8f3496f75c2" +checksum = "af84443ae9878f59a6818686f339064479f42cdf293c4c637d8fc9c0a8c5eed6" dependencies = [ "base64 0.22.1", - "bitflags 2.11.0", + "bitflags 2.13.2", "bson", "derive-where", - "derive_more 2.1.1", + "derive_more", "futures-core", "futures-io", "futures-util", "hex", + "hickory-net", "hickory-proto", "hickory-resolver", "hmac", @@ -2569,20 +2680,19 @@ dependencies = [ "mongodb-internal-macros", "pbkdf2", "percent-encoding", - "rand 0.9.2", + "rand 0.9.5", "rustc_version_runtime", "rustls", - "rustversion", "serde", "serde_bytes", "serde_with", "sha1", - "sha2", - "socket2 0.6.2", + "sha2 0.11.1", + "socket2", "stringprep", "strsim", "take_mut", - "thiserror 2.0.18", + "thiserror 2.0.21", "tokio", "tokio-rustls", "tokio-util", @@ -2593,14 +2703,14 @@ dependencies = [ [[package]] name = "mongodb-internal-macros" -version = "3.5.1" +version = "3.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a973ef3dd3dbc6f6e65bbdecfd9ec5e781b9e7493b0f369a7c62e35d8e5ae2c8" +checksum = "504024bbb83ab1bf1512007f7288b54d3a6343043f3f619c26a153346773598d" dependencies = [ "macro_magic", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -2612,23 +2722,23 @@ dependencies = [ "keyring", "log", "mongodb", + "percent-encoding", "serde", "serde_json", "tauri", "tauri-build", "tauri-plugin-log", "tauri-plugin-opener", - "thiserror 2.0.18", + "thiserror 2.0.21", "tokio", - "url", "uuid", ] [[package]] name = "muda" -version = "0.17.1" +version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01c1738382f66ed56b3b9c8119e794a2e23148ac8ea214eda86622d4cb9d415a" +checksum = "cca139e57da4383727f189e43e66e84fa372347027fc996f9f1a007ec0a37996" dependencies = [ "crossbeam-channel", "dpi", @@ -2641,8 +2751,8 @@ dependencies = [ "once_cell", "png", "serde", - "thiserror 2.0.18", - "windows-sys 0.60.2", + "thiserror 2.0.21", + "windows-sys 0.61.2", ] [[package]] @@ -2651,8 +2761,8 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3f42e7bbe13d351b6bead8286a43aac9534b82bd3cc43e47037f012ebfd62d4" dependencies = [ - "bitflags 2.11.0", - "jni-sys", + "bitflags 2.13.2", + "jni-sys 0.3.1", "log", "ndk-sys", "num_enum", @@ -2672,7 +2782,7 @@ version = "0.6.0+11769913" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ee6cda3051665f1fb8d9e08fc35c96d5a244fb1be711a03b71118828afc9a873" dependencies = [ - "jni-sys", + "jni-sys 0.3.1", ] [[package]] @@ -2682,31 +2792,88 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" [[package]] -name = "nodrop" -version = "0.1.14" +name = "num" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" +checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23" +dependencies = [ + "num-bigint", + "num-complex", + "num-integer", + "num-iter", + "num-rational", + "num-traits", +] [[package]] -name = "num-conv" -version = "0.2.0" +name = "num-bigint" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf97ec579c3c42f953ef76dbf8d55ac91fb219dde70e49aa4a6b7d74e9919050" +checksum = "c89e69e7e0f03bea5ef08013795c25018e101932225a656383bd384495ecc367" +dependencies = [ + "num-integer", + "num-traits", +] [[package]] -name = "num-traits" -version = "0.2.19" +name = "num-complex" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" dependencies = [ - "autocfg", + "num-traits", ] [[package]] -name = "num_enum" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1207a7e20ad57b847bbddc6776b968420d38292bbfe2089accff5e19e82454c" +name = "num-conv" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "521739c6d2bac4aa25192232afe6841231376b2b26d4d9fae5ecf8ca5772e441" + +[[package]] +name = "num-integer" +version = "0.1.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ce2d95d4b3734dc35aa2f45e1aa22cd416814592a4f9d9205e11affd5b8e10b" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-iter" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c92800bd69a1eac91786bcfe9da64a897eb72911b8dc3095decbd07429e8048b" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" +dependencies = [ + "num-bigint", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_enum" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d0bca838442ec211fa11de3a8b0e0e8f3a4522575b5c4c06ed722e005036f26" dependencies = [ "num_enum_derive", "rustversion", @@ -2714,30 +2881,30 @@ dependencies = [ [[package]] name = "num_enum_derive" -version = "0.7.5" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff32365de1b6743cb203b710788263c44a03de03802daf96092f2da4fe6ba4d7" +checksum = "680998035259dcfcafe653688bf2aa6d3e2dc05e98be6ab46afb089dc84f1df8" dependencies = [ - "proc-macro-crate 3.4.0", + "proc-macro-crate 3.5.0", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] name = "num_threads" -version = "0.1.7" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9" +checksum = "cabcc654086861f9e1bc5115258de62ef92c79b20f4615a1b0cc57c4e82027d4" dependencies = [ "libc", ] [[package]] name = "objc2" -version = "0.6.4" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a12a8ed07aefc768292f076dc3ac8c48f3781c8f2d5851dd3d98950e8c5a89f" +checksum = "08849bbd4767dfae9457696856ae1c84fe4e0281bbe4a7abff2d0e06fb7981f8" dependencies = [ "objc2-encode", "objc2-exception-helper", @@ -2749,17 +2916,10 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d49e936b501e5c5bf01fda3a9452ff86dc3ea98ad5f283e1455153142d97518c" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "block2", - "libc", "objc2", - "objc2-cloud-kit", - "objc2-core-data", "objc2-core-foundation", - "objc2-core-graphics", - "objc2-core-image", - "objc2-core-text", - "objc2-core-video", "objc2-foundation", "objc2-quartz-core", ] @@ -2770,7 +2930,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73ad74d880bb43877038da939b7427bba67e9dd42004a18b809ba7d87cee241c" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "objc2", "objc2-foundation", ] @@ -2781,7 +2941,6 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b402a653efbb5e82ce4df10683b6b28027616a2715e90009947d50b8dd298fa" dependencies = [ - "bitflags 2.11.0", "objc2", "objc2-foundation", ] @@ -2792,7 +2951,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "dispatch2", "objc2", ] @@ -2803,7 +2962,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e022c9d066895efa1345f8e33e584b9f958da2fd4cd116792e15e07e4720a807" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "dispatch2", "objc2", "objc2-core-foundation", @@ -2821,28 +2980,25 @@ dependencies = [ ] [[package]] -name = "objc2-core-text" +name = "objc2-core-location" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cde0dfb48d25d2b4862161a4d5fcc0e3c24367869ad306b0c9ec0073bfed92d" +checksum = "ca347214e24bc973fc025fd0d36ebb179ff30536ed1f80252706db19ee452009" dependencies = [ - "bitflags 2.11.0", "objc2", - "objc2-core-foundation", - "objc2-core-graphics", + "objc2-foundation", ] [[package]] -name = "objc2-core-video" +name = "objc2-core-text" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d425caf1df73233f29fd8a5c3e5edbc30d2d4307870f802d18f00d83dc5141a6" +checksum = "0cde0dfb48d25d2b4862161a4d5fcc0e3c24367869ad306b0c9ec0073bfed92d" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "objc2", "objc2-core-foundation", "objc2-core-graphics", - "objc2-io-surface", ] [[package]] @@ -2866,9 +3022,8 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "block2", - "libc", "objc2", "objc2-core-foundation", ] @@ -2879,17 +3034,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "180788110936d59bab6bd83b6060ffdfffb3b922ba1396b312ae795e1de9d81d" dependencies = [ - "bitflags 2.11.0", - "objc2", - "objc2-core-foundation", -] - -[[package]] -name = "objc2-javascript-core" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a1e6550c4caed348956ce3370c9ffeca70bb1dbed4fa96112e7c6170e074586" -dependencies = [ + "bitflags 2.13.2", "objc2", "objc2-core-foundation", ] @@ -2900,32 +3045,40 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96c1358452b371bf9f104e21ec536d37a650eb10f7ee379fff67d2e08d537f1f" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "objc2", "objc2-core-foundation", "objc2-foundation", ] [[package]] -name = "objc2-security" +name = "objc2-ui-kit" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "709fe137109bd1e8b5a99390f77a7d8b2961dafc1a1c5db8f2e60329ad6d895a" +checksum = "d87d638e33c06f577498cbcc50491496a3ed4246998a7fbba7ccb98b1e7eab22" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", + "block2", "objc2", + "objc2-cloud-kit", + "objc2-core-data", "objc2-core-foundation", + "objc2-core-graphics", + "objc2-core-image", + "objc2-core-location", + "objc2-core-text", + "objc2-foundation", + "objc2-quartz-core", + "objc2-user-notifications", ] [[package]] -name = "objc2-ui-kit" +name = "objc2-user-notifications" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d87d638e33c06f577498cbcc50491496a3ed4246998a7fbba7ccb98b1e7eab22" +checksum = "9df9128cbbfef73cda168416ccf7f837b62737d748333bfe9ab71c245d76613e" dependencies = [ - "bitflags 2.11.0", "objc2", - "objc2-core-foundation", "objc2-foundation", ] @@ -2935,21 +3088,19 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b2e5aaab980c433cf470df9d7af96a7b46a9d892d521a2cbbb2f8a4c16751e7f" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "block2", "objc2", "objc2-app-kit", "objc2-core-foundation", "objc2-foundation", - "objc2-javascript-core", - "objc2-security", ] [[package]] name = "once_cell" -version = "1.21.3" +version = "1.21.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" dependencies = [ "critical-section", "portable-atomic", @@ -2957,62 +3108,13 @@ dependencies = [ [[package]] name = "open" -version = "5.3.3" +version = "5.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43bb73a7fa3799b198970490a51174027ba0d4ec504b03cd08caf513d40024bc" +checksum = "8d8c8b8e725ad26404d6896eb2f064294a0a7b4f1d9120210d85544684c4ca95" dependencies = [ "dunce", "is-wsl", "libc", - "pathdiff", -] - -[[package]] -name = "openssl" -version = "0.10.75" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08838db121398ad17ab8531ce9de97b244589089e290a384c900cb9ff7434328" -dependencies = [ - "bitflags 2.11.0", - "cfg-if", - "foreign-types 0.3.2", - "libc", - "once_cell", - "openssl-macros", - "openssl-sys", -] - -[[package]] -name = "openssl-macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "openssl-src" -version = "300.5.5+3.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f1787d533e03597a7934fd0a765f0d28e94ecc5fb7789f8053b1e699a56f709" -dependencies = [ - "cc", -] - -[[package]] -name = "openssl-sys" -version = "0.9.111" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82cab2d520aa75e3c58898289429321eb788c3106963d0dc886ec7a5f4adc321" -dependencies = [ - "cc", - "libc", - "openssl-src", - "pkg-config", - "vcpkg", ] [[package]] @@ -3082,22 +3184,16 @@ dependencies = [ "libc", "redox_syscall", "smallvec", - "windows-link 0.2.1", + "windows-link", ] -[[package]] -name = "pathdiff" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df94ce210e5bc13cb6651479fa48d14f601d9858cfe0467f43ae157023b938d3" - [[package]] name = "pbkdf2" -version = "0.12.2" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2" +checksum = "112d82ceb8c5bf524d9af484d4e4970c9fd5a0cc15ba14ad93dccd28873b0629" dependencies = [ - "digest", + "digest 0.11.3", ] [[package]] @@ -3108,136 +3204,55 @@ checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" [[package]] name = "phf" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3dfb61232e34fcb633f43d12c58f83c1df82962dcdfa565a4e866ffc17dafe12" -dependencies = [ - "phf_shared 0.8.0", -] - -[[package]] -name = "phf" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fabbf1ead8a5bcbc20f5f8b939ee3f5b0f6f281b6ad3468b84656b658b455259" -dependencies = [ - "phf_macros 0.10.0", - "phf_shared 0.10.0", - "proc-macro-hack", -] - -[[package]] -name = "phf" -version = "0.11.3" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078" +checksum = "c1562dc717473dbaa4c1f85a36410e03c047b2e7df7f45ee938fbef64ae7fadf" dependencies = [ - "phf_macros 0.11.3", - "phf_shared 0.11.3", -] - -[[package]] -name = "phf_codegen" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbffee61585b0411840d3ece935cce9cb6321f01c45477d30066498cd5e1a815" -dependencies = [ - "phf_generator 0.8.0", - "phf_shared 0.8.0", + "phf_macros", + "phf_shared", + "serde", ] [[package]] name = "phf_codegen" -version = "0.11.3" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aef8048c789fa5e851558d709946d6d79a8ff88c0440c587967f8e94bfb1216a" +checksum = "49aa7f9d80421bca176ca8dbfebe668cc7a2684708594ec9f3c0db0805d5d6e1" dependencies = [ - "phf_generator 0.11.3", - "phf_shared 0.11.3", + "phf_generator", + "phf_shared", ] [[package]] name = "phf_generator" -version = "0.8.0" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17367f0cc86f2d25802b2c26ee58a7b23faeccf78a396094c13dced0d0182526" +checksum = "135ace3a761e564ec88c03a77317a7c6b80bb7f7135ef2544dbe054243b89737" dependencies = [ - "phf_shared 0.8.0", - "rand 0.7.3", -] - -[[package]] -name = "phf_generator" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d5285893bb5eb82e6aaf5d59ee909a06a16737a8970984dd7746ba9283498d6" -dependencies = [ - "phf_shared 0.10.0", - "rand 0.8.5", -] - -[[package]] -name = "phf_generator" -version = "0.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d" -dependencies = [ - "phf_shared 0.11.3", - "rand 0.8.5", -] - -[[package]] -name = "phf_macros" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58fdf3184dd560f160dd73922bea2d5cd6e8f064bf4b13110abd81b03697b4e0" -dependencies = [ - "phf_generator 0.10.0", - "phf_shared 0.10.0", - "proc-macro-hack", - "proc-macro2", - "quote", - "syn 1.0.109", + "fastrand", + "phf_shared", ] [[package]] name = "phf_macros" -version = "0.11.3" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f84ac04429c13a7ff43785d75ad27569f2951ce0ffd30a3321230db2fc727216" +checksum = "812f032b54b1e759ccd5f8b6677695d5268c588701effba24601f6932f8269ef" dependencies = [ - "phf_generator 0.11.3", - "phf_shared 0.11.3", + "phf_generator", + "phf_shared", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] name = "phf_shared" -version = "0.8.0" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c00cf8b9eafe68dde5e9eaa2cef8ee84a9336a47d566ec55ca16589633b65af7" +checksum = "e57fef6bc5981e38c2ce2d63bfa546861309f875b8a75f092d1d54ae2d64f266" dependencies = [ - "siphasher 0.3.11", -] - -[[package]] -name = "phf_shared" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6796ad771acdc0123d2a88dc428b5e38ef24456743ddb1744ed628f9815c096" -dependencies = [ - "siphasher 0.3.11", -] - -[[package]] -name = "phf_shared" -version = "0.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5" -dependencies = [ - "siphasher 1.0.2", + "siphasher", ] [[package]] @@ -3246,12 +3261,6 @@ version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" -[[package]] -name = "pin-utils" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" - [[package]] name = "piper" version = "0.2.5" @@ -3265,18 +3274,18 @@ dependencies = [ [[package]] name = "pkg-config" -version = "0.3.32" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" +checksum = "f6b464fbc74e149a392436b17d523f769e057cb6877f6a5c4618bc6f11800548" [[package]] name = "plist" -version = "1.8.0" +version = "1.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "740ebea15c5d1428f910cd1a5f52cebf8d25006245ed8ade92702f4943d91e07" +checksum = "327a4cb2ad49dc48adfdc9da89e22c64d488b508c67a044d71ba729e4521a285" dependencies = [ - "base64 0.22.1", - "indexmap 2.13.0", + "base64 0.23.1", + "indexmap 2.14.2", "quick-xml", "serde", "time", @@ -3284,15 +3293,15 @@ dependencies = [ [[package]] name = "png" -version = "0.17.16" +version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82151a2fc869e011c153adc57cf2789ccb8d9906ce52c0b39a6b5697749d7526" +checksum = "60769b8b31b2a9f263dae2776c37b1b28ae246943cf719eb6946a1db05128a61" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.13.2", "crc32fast", "fdeflate", "flate2", - "miniz_oxide", + "miniz_oxide 0.8.9", ] [[package]] @@ -3311,24 +3320,33 @@ dependencies = [ [[package]] name = "portable-atomic" -version = "1.13.1" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05c8b63e8d9609db387f0324918f81d68fe27748f084ef092fb35954d0539a85" + +[[package]] +name = "portable-atomic-util" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49" +checksum = "10ab3eb7f3becc3a1cbc4f2c6f20267996cfc1a6467a873763411b136a122715" +dependencies = [ + "portable-atomic", +] [[package]] name = "potential_utf" -version = "0.1.4" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b73949432f5e2a09657003c25bca5e19a0e9c84f8058ca374f49e0ebe605af77" +checksum = "d83eb9bc6d8e5cf568e7a1101d60ee05e81ed50ea106026f3d18deeb046d7661" dependencies = [ "zerovec", ] [[package]] name = "powerfmt" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" +checksum = "4a6394b9e965e73d0a289ee54f589087e2c676aedf60885baf52c76b771e4958" [[package]] name = "ppv-lite86" @@ -3346,13 +3364,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" [[package]] -name = "prettyplease" -version = "0.2.37" +name = "prefix-trie" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" +checksum = "4cf6e3177f0684016a5c209b00882e15f8bdd3f3bb48f0491df10cd102d0c6e7" dependencies = [ - "proc-macro2", - "syn 2.0.117", + "either", + "ipnet", + "num-traits", ] [[package]] @@ -3377,11 +3396,11 @@ dependencies = [ [[package]] name = "proc-macro-crate" -version = "3.4.0" +version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "219cb19e96be00ab2e37d6e299658a0cfa83e52429179969b0f0121b4ac46983" +checksum = "e67ba7e9b2b56446f1d419b1d807906278ffa1a658a8a5d8a39dcb1f5a78614f" dependencies = [ - "toml_edit 0.23.10+spec-1.0.0", + "toml_edit 0.25.17+spec-1.1.0", ] [[package]] @@ -3408,55 +3427,29 @@ dependencies = [ "version_check", ] -[[package]] -name = "proc-macro-hack" -version = "0.5.20+deprecated" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" - [[package]] name = "proc-macro2" -version = "1.0.106" +version = "1.0.107" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9" dependencies = [ "unicode-ident", ] -[[package]] -name = "ptr_meta" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0738ccf7ea06b608c10564b31debd4f5bc5e197fc8bfe088f68ae5ce81e7a4f1" -dependencies = [ - "ptr_meta_derive", -] - -[[package]] -name = "ptr_meta_derive" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16b845dbfca988fa33db069c0e230574d15a3088f147a87b64c7589eb662c9ac" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "quick-xml" -version = "0.38.4" +version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b66c2058c55a409d601666cffe35f04333cf1013010882cec174a7467cd4e21c" +checksum = "41b1177fdf999d2321d3fb46ff47159d9c1fb9ad66a4879f8c50a0b504615e9b" dependencies = [ "memchr", ] [[package]] name = "quote" -version = "1.0.44" +version = "1.0.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21b2ebcf727b7760c461f091f9f0f539b77b8e87f2fd88131e7f1b433b3cece4" +checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001" dependencies = [ "proc-macro2", ] @@ -3468,64 +3461,36 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" [[package]] -name = "radium" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" - -[[package]] -name = "rand" -version = "0.7.3" +name = "r-efi" +version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" -dependencies = [ - "getrandom 0.1.16", - "libc", - "rand_chacha 0.2.2", - "rand_core 0.5.1", - "rand_hc", - "rand_pcg", -] +checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" [[package]] -name = "rand" -version = "0.8.5" +name = "radium" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" -dependencies = [ - "libc", - "rand_chacha 0.3.1", - "rand_core 0.6.4", -] +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" [[package]] name = "rand" -version = "0.9.2" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" +checksum = "b9ef1d0d795eb7d84685bca4f72f3649f064e6641543d3a8c415898726a57b41" dependencies = [ - "rand_chacha 0.9.0", + "rand_chacha", "rand_core 0.9.5", ] [[package]] -name = "rand_chacha" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" -dependencies = [ - "ppv-lite86", - "rand_core 0.5.1", -] - -[[package]] -name = "rand_chacha" -version = "0.3.1" +name = "rand" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +checksum = "65c9fb96cbc91e3478eaae79a69fcd3f1ae4ad052e471fe6732fff548984b4af" dependencies = [ - "ppv-lite86", - "rand_core 0.6.4", + "chacha20", + "getrandom 0.4.3", + "rand_core 0.10.1", ] [[package]] @@ -3538,24 +3503,6 @@ dependencies = [ "rand_core 0.9.5", ] -[[package]] -name = "rand_core" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" -dependencies = [ - "getrandom 0.1.16", -] - -[[package]] -name = "rand_core" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" -dependencies = [ - "getrandom 0.2.17", -] - [[package]] name = "rand_core" version = "0.9.5" @@ -3566,22 +3513,10 @@ dependencies = [ ] [[package]] -name = "rand_hc" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" -dependencies = [ - "rand_core 0.5.1", -] - -[[package]] -name = "rand_pcg" -version = "0.2.1" +name = "rand_core" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16abd0c1b639e9eb4d7c50c0b8100b0d0f849be2349829c740fe8e6eb4816429" -dependencies = [ - "rand_core 0.5.1", -] +checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69" [[package]] name = "raw-window-handle" @@ -3595,45 +3530,44 @@ version = "0.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", ] [[package]] name = "redox_users" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac" +checksum = "60dc65c0ff1a7ae1294b0c67b9f14baf70b644404010370171787bfac1038fc0" dependencies = [ - "getrandom 0.2.17", "libredox", - "thiserror 2.0.18", + "thiserror 2.0.21", ] [[package]] name = "ref-cast" -version = "1.0.25" +version = "1.0.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f354300ae66f76f1c85c5f84693f0ce81d747e2c3f21a45fef496d89c960bf7d" +checksum = "7e440fb4e4b4147295338efb76001ab9e4efc0e5839df2c47fc5ac2381d365c3" dependencies = [ "ref-cast-impl", ] [[package]] name = "ref-cast-impl" -version = "1.0.25" +version = "1.0.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" +checksum = "92ecd8964f8453721699a1ed72037b0db49ce2f5a5138486ee89bed6f67cdf3a" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 3.0.9", ] [[package]] name = "regex" -version = "1.12.3" +version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" +checksum = "f020237b6c8eed93db2e2cb53c00c60a8e1bc73da7d073199a1180401450218d" dependencies = [ "aho-corasick", "memchr", @@ -3643,9 +3577,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.14" +version = "0.4.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" +checksum = "ad8553b9b26413251cbf30e620595c7a41b3887f03da04579c0e6b0d6a06b4b2" dependencies = [ "aho-corasick", "memchr", @@ -3654,26 +3588,17 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" - -[[package]] -name = "rend" -version = "0.4.2" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71fe3824f5629716b1589be05dacd749f6aa084c87e00e016714a8cdfccc997c" -dependencies = [ - "bytecheck", -] +checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4" [[package]] name = "reqwest" -version = "0.13.2" +version = "0.13.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab3f43e3283ab1488b624b44b0e988d0acea0b3214e694730a055cb6b2efa801" +checksum = "16a1cfa75cc186dd73d5818e510e042e40927bccc9c236b061cea97e1eb08029" dependencies = [ - "base64 0.22.1", + "base64 0.23.1", "bytes", "futures-core", "futures-util", @@ -3722,49 +3647,10 @@ dependencies = [ ] [[package]] -name = "rkyv" -version = "0.7.46" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2297bf9c81a3f0dc96bc9521370b88f054168c29826a75e89c55ff196e7ed6a1" -dependencies = [ - "bitvec", - "bytecheck", - "bytes", - "hashbrown 0.12.3", - "ptr_meta", - "rend", - "rkyv_derive", - "seahash", - "tinyvec", - "uuid", -] - -[[package]] -name = "rkyv_derive" -version = "0.7.46" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84d7b42d4b8d06048d3ac8db0eb31bcb942cbeb709f0b5f2b2ebde398d3038f5" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "rust_decimal" -version = "1.40.0" +name = "rustc-hash" +version = "2.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61f703d19852dbf87cbc513643fa81428361eb6940f1ac14fd58155d295a3eb0" -dependencies = [ - "arrayvec", - "borsh", - "bytes", - "num-traits", - "rand 0.8.5", - "rkyv", - "serde", - "serde_json", -] +checksum = "6b1e7f9a428571be2dc5bc0505c13fb6bf936822b894ec87abf8a08a4e51742d" [[package]] name = "rustc_version" @@ -3787,11 +3673,11 @@ dependencies = [ [[package]] name = "rustix" -version = "1.1.4" +version = "1.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" +checksum = "e5e76742e5294153d4dde1207dbec5bdd81eef6fabdc0d2118babadabfd858aa" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "errno", "libc", "linux-raw-sys", @@ -3800,9 +3686,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.37" +version = "0.23.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "758025cb5fccfd3bc2fd74708fd4682be41d99e5dff73c377c0646c6012c73a4" +checksum = "48e13bd8c0e9365c43cfa5c9e8f9ad49d3c8444926c9aac819e0e4dc503c8fdf" dependencies = [ "log", "once_cell", @@ -3815,18 +3701,18 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.14.0" +version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be040f8b0a225e40375822a563fa9524378b9d63112f53e19ffff34df5d33fdd" +checksum = "2f4925028c7eb5d1fcdaf196971378ed9d2c1c4efc7dc5d011256f76c99c0a96" dependencies = [ "zeroize", ] [[package]] name = "rustls-webpki" -version = "0.103.9" +version = "0.103.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7df23109aa6c1567d1c575b9952556388da57401e4ace1d15f79eedad0d8f53" +checksum = "4443b1b6d39d7c663139942cc4558262562d2c84123bb6282ef177009ca8b281" dependencies = [ "ring", "rustls-pki-types", @@ -3835,9 +3721,9 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.22" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" +checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f" [[package]] name = "same-file" @@ -3877,9 +3763,9 @@ dependencies = [ [[package]] name = "schemars" -version = "1.2.1" +version = "1.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2b42f36aa1cd011945615b92222f6bf73c599a102a300334cd7f8dbeec726cc" +checksum = "ab508826f74a77ca9d5aba6ff19b522583ee3eaf28a19384ff3d0e5835fadf6e" dependencies = [ "dyn-clone", "ref-cast", @@ -3896,7 +3782,7 @@ dependencies = [ "proc-macro2", "quote", "serde_derive_internals", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -3906,22 +3792,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] -name = "seahash" -version = "4.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b" - -[[package]] -name = "security-framework" -version = "2.11.1" +name = "secret-service" +version = "5.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" +checksum = "5107b24b91445dd2aa449a258a1807b63240942157292354dc5bfdbeb8bc6db8" dependencies = [ - "bitflags 2.11.0", - "core-foundation 0.9.4", - "core-foundation-sys", - "libc", - "security-framework-sys", + "aes", + "cbc", + "futures-util", + "getrandom 0.4.3", + "hkdf", + "hybrid-array", + "num", + "once_cell", + "serde", + "sha2 0.11.1", + "zbus", ] [[package]] @@ -3930,7 +3816,7 @@ version = "3.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "core-foundation 0.10.1", "core-foundation-sys", "libc", @@ -3949,27 +3835,28 @@ dependencies = [ [[package]] name = "selectors" -version = "0.24.0" +version = "0.38.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c37578180969d00692904465fb7f6b3d50b9a2b952b87c23d0e2e5cb5013416" +checksum = "8adfa1c298912827b8a28b223b3b874357397ae706e6190acd9bf28cee99114d" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.13.2", "cssparser", - "derive_more 0.99.20", - "fxhash", + "derive_more", "log", - "phf 0.8.0", - "phf_codegen 0.8.0", + "new_debug_unreachable", + "phf", + "phf_codegen", "precomputed-hash", + "rustc-hash", "servo_arc", "smallvec", ] [[package]] name = "semver" -version = "1.0.27" +version = "1.0.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" +checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" dependencies = [ "serde", "serde_core", @@ -3977,9 +3864,9 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.228" +version = "1.0.229" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +checksum = "4148590afebada386688f18773da617792bf2ef03ffc1e4cbd2b1d45b023e0ba" dependencies = [ "serde_core", "serde_derive", @@ -4009,22 +3896,22 @@ dependencies = [ [[package]] name = "serde_core" -version = "1.0.228" +version = "1.0.229" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +checksum = "67dca2c9c51e58a4791a4b1ed58308b39c64224d349a935ab5039aa360942a48" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.228" +version = "1.0.229" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 3.0.9", ] [[package]] @@ -4035,16 +3922,17 @@ checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] name = "serde_json" -version = "1.0.149" +version = "1.0.154" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" +checksum = "e7e9cc8b1b85264074fbcc02a88680c4096b1e47df8f739dceb03bf482f04bd6" dependencies = [ - "indexmap 2.13.0", + "foldhash", + "indexmap 2.14.2", "itoa", "memchr", "serde", @@ -4054,13 +3942,13 @@ dependencies = [ [[package]] name = "serde_repr" -version = "0.1.20" +version = "0.1.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c" +checksum = "8d3b1629de253c70a0508c3899572da79ca359fdab27c7920ff00406df418906" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 3.0.9", ] [[package]] @@ -4074,26 +3962,28 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "1.0.4" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8bbf91e5a4d6315eee45e704372590b30e260ee83af6639d64557f51b067776" +checksum = "4e7523beb55eece201a2356bee0bbca0d1ab466c14c07703b2e0ee6d42cb0c2c" dependencies = [ "serde_core", ] [[package]] name = "serde_with" -version = "3.17.0" +version = "3.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "381b283ce7bc6b476d903296fb59d0d36633652b633b27f64db4fb46dcbfc3b9" +checksum = "df9adc193c780ef8f159aee8b61e2d5801aaa555e6eb0947fe45530ec506296f" dependencies = [ - "base64 0.22.1", + "base64 0.23.1", + "bs58", "chrono", "hex", "indexmap 1.9.3", - "indexmap 2.13.0", + "indexmap 2.14.2", + "jiff", "schemars 0.9.0", - "schemars 1.2.1", + "schemars 1.2.3", "serde_core", "serde_json", "serde_with_macros", @@ -4102,14 +3992,14 @@ dependencies = [ [[package]] name = "serde_with_macros" -version = "3.17.0" +version = "3.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6d4e30573c8cb306ed6ab1dca8423eec9a463ea0e155f45399455e0368b27e0" +checksum = "3e17bbc68e28663bbbb90df47e058aa7eda4fb445b89fe70457bb94fbccf6e49" dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.117", + "syn 3.0.9", ] [[package]] @@ -4131,28 +4021,27 @@ checksum = "772ee033c0916d670af7860b6e1ef7d658a4629a6d0b4c8c3e67f09b3765b75d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] name = "servo_arc" -version = "0.2.0" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d52aa42f8fdf0fed91e5ce7f23d8138441002fa31dca008acf47e6fd4721f741" +checksum = "170fb83ab34de17dc69aa7c67482b22218ddb85da56546f9bd6b929e32a05930" dependencies = [ - "nodrop", "stable_deref_trait", ] [[package]] name = "sha1" -version = "0.10.6" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +checksum = "aacc4cc499359472b4abe1bf11d0b12e688af9a805fa5e3016f9a386dc2d0214" dependencies = [ "cfg-if", - "cpufeatures", - "digest", + "cpufeatures 0.3.1", + "digest 0.11.3", ] [[package]] @@ -4162,15 +4051,26 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" dependencies = [ "cfg-if", - "cpufeatures", - "digest", + "cpufeatures 0.2.17", + "digest 0.10.7", +] + +[[package]] +name = "sha2" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47d7069beb7d6ac7b9acd1039986e73443f24234f41074da099d6f994ac9ad19" +dependencies = [ + "cfg-if", + "cpufeatures 0.3.1", + "digest 0.11.3", ] [[package]] name = "shlex" -version = "1.3.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" +checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" [[package]] name = "signal-hook-registry" @@ -4184,27 +4084,31 @@ dependencies = [ [[package]] name = "simd-adler32" -version = "0.3.8" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e320a6c5ad31d271ad523dcf3ad13e2767ad8b1cb8f047f75a8aeaf8da139da2" +checksum = "3a219298ac11a56ea9a6d2120044824d6f01aeb034955e7af7bc16858527deea" [[package]] -name = "simdutf8" -version = "0.1.5" +name = "simd_cesu8" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" +checksum = "11031e251abf8611c80f460e19dbdeb54a66db918e49c65a7065b46ac7aec520" +dependencies = [ + "rustc_version", + "simdutf8", +] [[package]] -name = "siphasher" -version = "0.3.11" +name = "simdutf8" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" +checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" [[package]] name = "siphasher" -version = "1.0.2" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2aa850e253778c88a04c3d7323b043aeda9d3e30d5971937c1855769763678e" +checksum = "33f4fe9184a62d842c9ef383018f3306d8ba224fd9d836f56d7288308847c256" [[package]] name = "slab" @@ -4214,28 +4118,18 @@ checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" [[package]] name = "smallvec" -version = "1.15.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" - -[[package]] -name = "socket2" -version = "0.5.10" +version = "1.16.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678" -dependencies = [ - "libc", - "windows-sys 0.52.0", -] +checksum = "d0d5d4b1981b8bbbb7aed204cae5726d683d5d6ac7e74ec54122ea28f315e7c8" [[package]] name = "socket2" -version = "0.6.2" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86f4aa3ad99f2088c990dfa82d367e19cb29268ed67c574d10d0a4bfe71f07e0" +checksum = "c3d1e2c7f27f8d4cb10542a02c49005dbd6e93095799d6f3be745fae9f8fedd4" dependencies = [ "libc", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -4292,27 +4186,32 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" +[[package]] +name = "static_vcruntime" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff7589a1859b09232522bb9edf076ca7fcf14ded96685d3fa629db73023ffbb7" + [[package]] name = "string_cache" -version = "0.8.9" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf776ba3fa74f83bf4b63c3dcbbf82173db2632ed8452cb2d891d33f459de70f" +checksum = "a18596f8c785a729f2819c0f6a7eae6ebeebdfffbfe4214ae6b087f690e31901" dependencies = [ "new_debug_unreachable", "parking_lot", - "phf_shared 0.11.3", + "phf_shared", "precomputed-hash", - "serde", ] [[package]] name = "string_cache_codegen" -version = "0.5.4" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c711928715f1fe0fe509c53b43e993a9a557babc2d0a3567d0a3006f1ac931a0" +checksum = "585635e46db231059f76c5849798146164652513eb9e8ab2685939dd90f29b69" dependencies = [ - "phf_generator 0.11.3", - "phf_shared 0.11.3", + "phf_generator", + "phf_shared", "proc-macro2", "quote", ] @@ -4342,9 +4241,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "swift-rs" -version = "1.0.7" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4057c98e2e852d51fdcfca832aac7b571f6b351ad159f9eda5db1655f8d0c4d7" +checksum = "cefd87076fd385308ee4aff597256c902f1fbb4d031de1558efc12f8ffefadd9" dependencies = [ "base64 0.21.7", "serde", @@ -4356,6 +4255,16 @@ name = "syn" version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.119" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297" dependencies = [ "proc-macro2", "quote", @@ -4364,9 +4273,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.117" +version = "3.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +checksum = "d78c8dee4c7bf0e14673097256fed6142ce9d3b85a408189d07482442145823b" dependencies = [ "proc-macro2", "quote", @@ -4384,13 +4293,34 @@ dependencies = [ [[package]] name = "synstructure" -version = "0.13.2" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" +checksum = "901704edd0dfe137f1987838ee4f259e4e063c31371bdb423f7ae38ec6f77f02" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 3.0.9", +] + +[[package]] +name = "system-configuration" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a13f3d0daba03132c0aa9767f98351b3488edc2c100cda2d2ec2b04f3d8d3c8b" +dependencies = [ + "bitflags 2.13.2", + "core-foundation 0.9.4", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" +dependencies = [ + "core-foundation-sys", + "libc", ] [[package]] @@ -4420,23 +4350,23 @@ checksum = "f764005d11ee5f36500a149ace24e00e3da98b0158b3e2d53a7495660d3f4d60" [[package]] name = "tao" -version = "0.34.5" +version = "0.37.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3a753bdc39c07b192151523a3f77cd0394aa75413802c883a0f6f6a0e5ee2e7" +checksum = "d5777b06fe6435126ae8d816eba269e050e1e10d302cf5461e9c49eccabc3548" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "block2", "core-foundation 0.10.1", "core-graphics", "crossbeam-channel", - "dispatch", + "dbus", + "dispatch2", "dlopen2", "dpi", "gdkwayland-sys", "gdkx11-sys", "gtk", - "jni", - "lazy_static", + "jni 0.21.1", "libc", "log", "ndk", @@ -4445,28 +4375,28 @@ dependencies = [ "objc2", "objc2-app-kit", "objc2-foundation", - "once_cell", + "objc2-ui-kit", "parking_lot", + "percent-encoding", "raw-window-handle", - "scopeguard", "tao-macros", "unicode-segmentation", "url", "windows", - "windows-core 0.61.2", + "windows-core", "windows-version", "x11-dl", ] [[package]] name = "tao-macros" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4e16beb8b2ac17db28eab8bca40e62dbfbb34c0fcdc6d9826b11b7b5d047dfd" +checksum = "5f7eeb6d99155545da6150a1795945f16ac9c178deb2a5f2e74d776107bd5849" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -4483,9 +4413,9 @@ checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" [[package]] name = "tauri" -version = "2.10.2" +version = "2.12.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "463ae8677aa6d0f063a900b9c41ecd4ac2b7ca82f0b058cc4491540e55b20129" +checksum = "5fb0244332cfb9cee7e2a568bf5da29a36ed8fdbd717f6f47a66b6b7b358c072" dependencies = [ "anyhow", "bytes", @@ -4498,7 +4428,7 @@ dependencies = [ "gtk", "heck 0.5.0", "http", - "jni", + "jni 0.21.1", "libc", "log", "mime", @@ -4522,7 +4452,7 @@ dependencies = [ "tauri-runtime", "tauri-runtime-wry", "tauri-utils", - "thiserror 2.0.18", + "thiserror 2.0.21", "tokio", "tray-icon", "url", @@ -4534,9 +4464,9 @@ dependencies = [ [[package]] name = "tauri-build" -version = "2.5.5" +version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca7bd893329425df750813e95bd2b643d5369d929438da96d5bbb7cc2c918f74" +checksum = "adddb0e648752aaac5da9c3c3c6a68e1e87cab55e3b757b18a9441ef8c3ac614" dependencies = [ "anyhow", "cargo_toml", @@ -4548,19 +4478,20 @@ dependencies = [ "semver", "serde", "serde_json", + "static_vcruntime", + "strsim", "tauri-utils", "tauri-winres", - "toml 0.9.12+spec-1.1.0", "walkdir", ] [[package]] name = "tauri-codegen" -version = "2.5.4" +version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aac423e5859d9f9ccdd32e3cf6a5866a15bedbf25aa6630bcb2acde9468f6ae3" +checksum = "fb37631cf71dab5e8ab55e1bdd701e18a52653d90b78e71d0e5598b1e0a61316" dependencies = [ - "base64 0.22.1", + "base64 0.23.1", "brotli", "ico", "json-patch", @@ -4571,10 +4502,10 @@ dependencies = [ "semver", "serde", "serde_json", - "sha2", - "syn 2.0.117", + "sha2 0.10.9", + "syn 2.0.119", "tauri-utils", - "thiserror 2.0.18", + "thiserror 2.0.21", "time", "url", "uuid", @@ -4583,23 +4514,23 @@ dependencies = [ [[package]] name = "tauri-macros" -version = "2.5.4" +version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b6a1bd2861ff0c8766b1d38b32a6a410f6dc6532d4ef534c47cfb2236092f59" +checksum = "0cc38db58854c10fb5f91a887ed8bf82d3239bb92b5d8db86eda09e96d9963d1" dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", "tauri-codegen", "tauri-utils", ] [[package]] name = "tauri-plugin" -version = "2.5.3" +version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "692a77abd8b8773e107a42ec0e05b767b8d2b7ece76ab36c6c3947e34df9f53f" +checksum = "67fb21a77e8f94e8beb3325e583102488659365af0a3948b7ea244c5b779c291" dependencies = [ "anyhow", "glob", @@ -4608,18 +4539,16 @@ dependencies = [ "serde", "serde_json", "tauri-utils", - "toml 0.9.12+spec-1.1.0", "walkdir", ] [[package]] name = "tauri-plugin-log" -version = "2.8.0" +version = "2.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7545bd67f070a4500432c826e2e0682146a1d6712aee22a2786490156b574d93" +checksum = "d83980f6765feb86cf2224959246be28b0488e14a23204a719f7f9deecfc64c9" dependencies = [ "android_logger", - "byte-unit", "fern", "log", "objc2", @@ -4630,15 +4559,15 @@ dependencies = [ "swift-rs", "tauri", "tauri-plugin", - "thiserror 2.0.18", + "thiserror 2.0.21", "time", ] [[package]] name = "tauri-plugin-opener" -version = "2.5.3" +version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc624469b06f59f5a29f874bbc61a2ed737c0f9c23ef09855a292c389c42e83f" +checksum = "53810be1b4fed22bb6ac3f4cab5a2b80d9391d1af436bda145607532bc0bb219" dependencies = [ "dunce", "glob", @@ -4650,7 +4579,7 @@ dependencies = [ "serde_json", "tauri", "tauri-plugin", - "thiserror 2.0.18", + "thiserror 2.0.21", "url", "windows", "zbus", @@ -4658,15 +4587,15 @@ dependencies = [ [[package]] name = "tauri-runtime" -version = "2.10.0" +version = "2.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b885ffeac82b00f1f6fd292b6e5aabfa7435d537cef57d11e38a489956535651" +checksum = "7c55766d99cea69055904d1735b93c2049302b2e09effd462120deb5581d78e9" dependencies = [ "cookie", "dpi", "gtk", "http", - "jni", + "jni 0.21.1", "objc2", "objc2-ui-kit", "objc2-web-kit", @@ -4674,7 +4603,7 @@ dependencies = [ "serde", "serde_json", "tauri-utils", - "thiserror 2.0.18", + "thiserror 2.0.21", "url", "webkit2gtk", "webview2-com", @@ -4683,18 +4612,16 @@ dependencies = [ [[package]] name = "tauri-runtime-wry" -version = "2.10.0" +version = "2.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5204682391625e867d16584fedc83fc292fb998814c9f7918605c789cd876314" +checksum = "e1aa9d8b7a120f879f44fdc1053f7b7dbf416bf0bbc6a72b714dec46b782b52f" dependencies = [ "gtk", "http", - "jni", + "jni 0.21.1", "log", "objc2", "objc2-app-kit", - "objc2-foundation", - "once_cell", "percent-encoding", "raw-window-handle", "softbuffer", @@ -4710,24 +4637,24 @@ dependencies = [ [[package]] name = "tauri-utils" -version = "2.8.2" +version = "2.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcd169fccdff05eff2c1033210b9b94acd07a47e6fa9a3431cf09cfd4f01c87e" +checksum = "362ab21cd4cc713f2a02b1a9b5688198b1541cfb6907c4281e9190e0f0ef4aa3" dependencies = [ "anyhow", "brotli", "cargo_metadata", "ctor", + "dom_query", "dunce", "glob", - "html5ever", "http", "infer", "json-patch", - "kuchikiki", "log", "memchr", - "phf 0.11.3", + "phf", + "plist", "proc-macro2", "quote", "regex", @@ -4738,8 +4665,8 @@ dependencies = [ "serde_json", "serde_with", "swift-rs", - "thiserror 2.0.18", - "toml 0.9.12+spec-1.1.0", + "thiserror 2.0.21", + "toml 1.1.8+spec-1.1.0", "url", "urlpattern", "uuid", @@ -4748,23 +4675,23 @@ dependencies = [ [[package]] name = "tauri-winres" -version = "0.3.5" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1087b111fe2b005e42dbdc1990fc18593234238d47453b0c99b7de1c9ab2c1e0" +checksum = "c52bb6904475a6ca662f15ef787f18ade61d5740a1ee12c45d3b748efdb6f796" dependencies = [ "dunce", "embed-resource", - "toml 0.9.12+spec-1.1.0", + "toml 1.1.8+spec-1.1.0", ] [[package]] name = "tempfile" -version = "3.26.0" +version = "3.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82a72c767771b47409d2345987fda8628641887d5466101319899796367354a0" +checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" dependencies = [ "fastrand", - "getrandom 0.4.1", + "getrandom 0.4.3", "once_cell", "rustix", "windows-sys 0.61.2", @@ -4772,13 +4699,11 @@ dependencies = [ [[package]] name = "tendril" -version = "0.4.3" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d24a120c5fc464a3458240ee02c299ebcb9d67b5249c8848b09d639dca8d7bb0" +checksum = "5fed54709c5b3a53d09bb1c113ea4f5ceafd1e772ddcb0030a82e1d56c087b08" dependencies = [ - "futf", - "mac", - "utf-8", + "new_debug_unreachable", ] [[package]] @@ -4792,11 +4717,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.18" +version = "2.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" +checksum = "09e52cb86a36cede5cb101bf8908837b3e4c6e5e59fe7fd85c23fb56200d189e" dependencies = [ - "thiserror-impl 2.0.18", + "thiserror-impl 2.0.21", ] [[package]] @@ -4807,28 +4732,27 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] name = "thiserror-impl" -version = "2.0.18" +version = "2.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" +checksum = "fe5197923287db20a58125f0bc85c062f7f2c892de97b18c356f9efb14b28524" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 3.0.9", ] [[package]] name = "time" -version = "0.3.47" +version = "0.3.55" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c" +checksum = "cdb87b95ec50ddfa440816d227a17b2ccbdda963a316a727fda0fc4334f7d134" dependencies = [ "deranged", - "itoa", "libc", "num-conv", "num_threads", @@ -4840,15 +4764,15 @@ dependencies = [ [[package]] name = "time-core" -version = "0.1.8" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca" +checksum = "9e1c906769ad99c88eaa54e728060edef082f8e358ff32030cb7c7d315e81109" [[package]] name = "time-macros" -version = "0.2.27" +version = "0.2.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e70e4c5a0e0a8a4823ad65dfe1a6930e4f4d756dcd9dd7939022b5e8c501215" +checksum = "7e689342a48d2ea927c87ea50cabf8594854bf940e9310208848d680d668ed85" dependencies = [ "num-conv", "time-core", @@ -4865,9 +4789,9 @@ dependencies = [ [[package]] name = "tinystr" -version = "0.8.2" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869" +checksum = "abc840c238154d58f9e86b2792261edfde565a17696e1cda1a449de27979801a" dependencies = [ "displaydoc", "zerovec", @@ -4875,51 +4799,42 @@ dependencies = [ [[package]] name = "tinyvec" -version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa5fdc3bce6191a1dbc8c02d5c8bffcf557bafa17c124c5264a458f1b0613fa" -dependencies = [ - "tinyvec_macros", -] - -[[package]] -name = "tinyvec_macros" -version = "0.1.1" +version = "1.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" +checksum = "fd3ca314f692efd6c868f8408f53fe444634a845f96c028b97d35f6a1f79f0ee" [[package]] name = "tokio" -version = "1.49.0" +version = "1.54.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72a2903cd7736441aac9df9d7688bd0ce48edccaadf181c3b90be801e81d3d86" +checksum = "b52fd3255258d26fac7264ab5a76b53c13367136dc011d3d6ad4f8ed070396a3" dependencies = [ "bytes", "libc", "mio", "pin-project-lite", "signal-hook-registry", - "socket2 0.6.2", + "socket2", "tokio-macros", "windows-sys 0.61.2", ] [[package]] name = "tokio-macros" -version = "2.6.0" +version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" +checksum = "78773a2a397f451582ce068015985c33193cf6dea8b74d2a639fe457b2f07b0e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 3.0.9", ] [[package]] name = "tokio-rustls" -version = "0.26.4" +version = "0.26.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" +checksum = "c9cc2678c2cdd569ef8215e2afd7954ada2ae20b4fdd2c5fe6139a3b02d105db" dependencies = [ "rustls", "tokio", @@ -4927,14 +4842,15 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.18" +version = "0.7.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098" +checksum = "e464cf451ba96ebfc6f9b6542f17ee8b8956e33f1e40d9690624e59d7a7f8a4b" dependencies = [ "bytes", "futures-core", "futures-io", "futures-sink", + "futures-util", "pin-project-lite", "tokio", ] @@ -4953,17 +4869,17 @@ dependencies = [ [[package]] name = "toml" -version = "0.9.12+spec-1.1.0" +version = "1.1.8+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf92845e79fc2e2def6a5d828f0801e29a2f8acc037becc5ab08595c7d5e9863" +checksum = "20489e00e4d8741d6be680764cc12e270655e375a20d1011e844a9c3379e678d" dependencies = [ - "indexmap 2.13.0", + "indexmap 2.14.2", "serde_core", - "serde_spanned 1.0.4", - "toml_datetime 0.7.5+spec-1.1.0", + "serde_spanned 1.1.2", + "toml_datetime 1.1.2+spec-1.1.0", "toml_parser", "toml_writer", - "winnow 0.7.14", + "winnow 1.0.4", ] [[package]] @@ -4977,9 +4893,9 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "0.7.5+spec-1.1.0" +version = "1.1.2+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92e1cfed4a3038bc5a127e35a2d360f145e1f4b971b551a2ba5fd7aedf7e1347" +checksum = "2b86d767906c6c42421dcba507eb9d203e779497710a47782a224bb871653053" dependencies = [ "serde_core", ] @@ -4990,7 +4906,7 @@ version = "0.19.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" dependencies = [ - "indexmap 2.13.0", + "indexmap 2.14.2", "toml_datetime 0.6.3", "winnow 0.5.40", ] @@ -5001,7 +4917,7 @@ version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "396e4d48bbb2b7554c944bde63101b5ae446cff6ec4a24227428f15eb72ef338" dependencies = [ - "indexmap 2.13.0", + "indexmap 2.14.2", "serde", "serde_spanned 0.6.9", "toml_datetime 0.6.3", @@ -5010,30 +4926,30 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.23.10+spec-1.0.0" +version = "0.25.17+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84c8b9f757e028cee9fa244aea147aab2a9ec09d5325a9b01e0a49730c2b5269" +checksum = "e3641d5bbb5349a79e1020a242d251efbc546ad8048d133958323ce9c40a9c9c" dependencies = [ - "indexmap 2.13.0", - "toml_datetime 0.7.5+spec-1.1.0", + "indexmap 2.14.2", + "toml_datetime 1.1.2+spec-1.1.0", "toml_parser", - "winnow 0.7.14", + "winnow 1.0.4", ] [[package]] name = "toml_parser" -version = "1.0.9+spec-1.1.0" +version = "1.1.5+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "702d4415e08923e7e1ef96cd5727c0dfed80b4d2fa25db9647fe5eb6f7c5a4c4" +checksum = "baa693a8032d7e1cada7d0041e96126df243179ff061456783ac7f12bda4744c" dependencies = [ - "winnow 0.7.14", + "winnow 1.0.4", ] [[package]] name = "toml_writer" -version = "1.0.6+spec-1.1.0" +version = "1.1.3+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab16f14aed21ee8bfd8ec22513f7287cd4a91aa92e44edfe2c17ddd004e92607" +checksum = "06bdbd8cfc056b8d2e2e85f29b56a3bdbecb527cef81eb39e3e7b98af4652770" [[package]] name = "tower" @@ -5052,20 +4968,20 @@ dependencies = [ [[package]] name = "tower-http" -version = "0.6.8" +version = "0.6.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8" +checksum = "4cfcf7e2740e6fc6d4d688b4ef00650406bb94adf4731e43c096c3a19fe40840" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.2", "bytes", "futures-util", "http", "http-body", - "iri-string", "pin-project-lite", "tower", "tower-layer", "tower-service", + "url", ] [[package]] @@ -5099,7 +5015,7 @@ checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -5113,9 +5029,9 @@ dependencies = [ [[package]] name = "tray-icon" -version = "0.21.3" +version = "0.25.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5e85aa143ceb072062fc4d6356c1b520a51d636e7bc8e77ec94be3608e5e80c" +checksum = "b2b9c52859a94554803ccd4a24b98f74148ebc73b90676d783f3490b1bff9d72" dependencies = [ "crossbeam-channel", "dirs", @@ -5129,8 +5045,8 @@ dependencies = [ "once_cell", "png", "serde", - "thiserror 2.0.18", - "windows-sys 0.60.2", + "thiserror 2.0.21", + "windows-sys 0.61.2", ] [[package]] @@ -5156,7 +5072,7 @@ checksum = "0e48cea23f68d1f78eb7bc092881b6bb88d3d6b5b7e6234f6f9c911da1ffb221" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -5167,60 +5083,19 @@ checksum = "bc7d623258602320d5c55d1bc22793b57daff0ec7efc270ea7d55ce1d5f5471c" [[package]] name = "typenum" -version = "1.19.0" +version = "1.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" +checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20" [[package]] name = "uds_windows" -version = "1.1.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89daebc3e6fd160ac4aa9fc8b3bf71e1f74fbf92367ae71fb83a037e8bf164b9" +checksum = "f2f6fb2847f6742cd76af783a2a2c49e9375d0a111c7bef6f71cd9e738c72d6e" dependencies = [ "memoffset", "tempfile", - "winapi", -] - -[[package]] -name = "unic-char-property" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8c57a407d9b6fa02b4795eb81c5b6652060a15a7903ea981f3d723e6c0be221" -dependencies = [ - "unic-char-range", -] - -[[package]] -name = "unic-char-range" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0398022d5f700414f6b899e10b8348231abf9173fa93144cbc1a43b9793c1fbc" - -[[package]] -name = "unic-common" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80d7ff825a6a654ee85a63e80f92f054f904f21e7d12da4e22f9834a4aaa35bc" - -[[package]] -name = "unic-ucd-ident" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e230a37c0381caa9219d67cf063aa3a375ffed5bf541a452db16e744bdab6987" -dependencies = [ - "unic-char-property", - "unic-char-range", - "unic-ucd-version", -] - -[[package]] -name = "unic-ucd-version" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96bd2f2237fe450fcd0a1d2f5f4e91711124f7857ba2e964247776ebeeb7b0c4" -dependencies = [ - "unic-common", + "windows-sys 0.61.2", ] [[package]] @@ -5231,30 +5106,30 @@ checksum = "5c1cb5db39152898a79168971543b1cb5020dff7fe43c8dc468b0885f5e29df5" [[package]] name = "unicode-ident" -version = "1.0.24" +version = "1.0.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" +checksum = "a2c754d6c33795a1c324727428e5a7dedb5b06195f9890bdbcba760d3e246563" [[package]] name = "unicode-normalization" -version = "0.1.25" +version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fd4f6878c9cb28d874b009da9e8d183b5abc80117c40bbd187a1fde336be6e8" +checksum = "75cd3fb03d5288b3ba9a1ee103ac7e9cbb59f0b03d64f8ddfcc04dd42758a85d" dependencies = [ "tinyvec", ] [[package]] name = "unicode-properties" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7df058c713841ad818f1dc5d3fd88063241cc61f49f5fbea4b951e8cf5a8d71d" +checksum = "993ad287eb20e8ba755312801cfe1413e0f25d604075863acaddca10b04c1833" [[package]] name = "unicode-segmentation" -version = "1.12.0" +version = "1.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" +checksum = "c6f5d3c3b1bf09027a88a6bc961fc00497d651009560b5463668dc81b0fa87a8" [[package]] name = "unicode-xid" @@ -5283,58 +5158,43 @@ dependencies = [ [[package]] name = "urlpattern" -version = "0.3.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70acd30e3aa1450bc2eece896ce2ad0d178e9c079493819301573dae3c37ba6d" +checksum = "df16f50ef4cc145211879a3867ba757076b25dfee812040dcb0658bd9ae7904b" dependencies = [ + "icu_properties", "regex", "serde", - "unic-ucd-ident", "url", ] [[package]] -name = "utf-8" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" - -[[package]] -name = "utf8-width" -version = "0.1.8" +name = "utf8_iter" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1292c0d970b54115d14f2492fe0170adf21d68a1de108eebc51c1df4f346a091" +checksum = "af3bd9d3e571c26fcce9d033b87ea5ab061b8d2ffe9b7a5675d810d68f03edf0" +dependencies = [ + "utf_types", +] [[package]] -name = "utf8_iter" -version = "1.0.4" +name = "utf_types" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" +checksum = "3a3b425286980a367278453f3b6d28cdcb134b574e5a35824a6ff5eebad50bb6" [[package]] name = "uuid" -version = "1.21.0" +version = "1.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b672338555252d43fd2240c714dc444b8c6fb0a5c5335e65a07bba7742735ddb" +checksum = "4a0e2d11620c50695fe12bbbfde96b3477fe606e7284c22d505c12ce51214514" dependencies = [ - "getrandom 0.4.1", + "getrandom 0.4.3", "js-sys", "serde_core", "wasm-bindgen", ] -[[package]] -name = "value-bag" -version = "1.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ba6f5989077681266825251a52748b8c1d8a4ad098cc37e440103d0ea717fc0" - -[[package]] -name = "vcpkg" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" - [[package]] name = "version-compare" version = "0.2.1" @@ -5379,19 +5239,13 @@ dependencies = [ [[package]] name = "want" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +checksum = "ec4cdd0dd910afe868b7ef477227d8d538b46b3075031afee8a9f2acb0a2ed0b" dependencies = [ "try-lock", ] -[[package]] -name = "wasi" -version = "0.9.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" - [[package]] name = "wasi" version = "0.11.1+wasi-snapshot-preview1" @@ -5400,30 +5254,20 @@ checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] name = "wasip2" -version = "1.0.2+wasi-0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5" -dependencies = [ - "wit-bindgen", -] - -[[package]] -name = "wasip3" -version = "0.4.0+wasi-0.3.0-rc-2026-01-06" +version = "1.0.4+wasi-0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" +checksum = "b67efb37e106e55ce722a510d6b5f9c17f083e5fc79afc2badeb12cc313d9487" dependencies = [ "wit-bindgen", ] [[package]] name = "wasm-bindgen" -version = "0.2.114" +version = "0.2.130" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6532f9a5c1ece3798cb1c2cfdba640b9b3ba884f5db45973a6f442510a87d38e" +checksum = "a0181c14ddd6c7b8b0b0132a5f1ea2f32be0d893cd3ea97b6e3b28886b8a3d1c" dependencies = [ "cfg-if", - "once_cell", "rustversion", "wasm-bindgen-macro", "wasm-bindgen-shared", @@ -5431,23 +5275,20 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.64" +version = "0.4.80" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9c5522b3a28661442748e09d40924dfb9ca614b21c00d3fd135720e48b67db8" +checksum = "5e14e0327cb5368ad60ccb22d04a9827a693c95a926d8d125e9fa39e0baec501" dependencies = [ - "cfg-if", - "futures-util", "js-sys", - "once_cell", + "tokio", "wasm-bindgen", - "web-sys", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.114" +version = "0.2.130" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18a2d50fcf105fb33bb15f00e7a77b772945a2ee45dcf454961fd843e74c18e6" +checksum = "6e931a02a39abdbf66966fc35902b5ed58344750a70885bed4ebb3d8eaf28ed9" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -5455,46 +5296,24 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.114" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03ce4caeaac547cdf713d280eda22a730824dd11e6b8c3ca9e42247b25c631e3" -dependencies = [ - "bumpalo", - "proc-macro2", - "quote", - "syn 2.0.117", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.114" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75a326b8c223ee17883a4251907455a2431acc2791c98c26279376490c378c16" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "wasm-encoder" -version = "0.244.0" +version = "0.2.130" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" +checksum = "2ca4b54280e455bd831e33a6b8e734baf432fe63fbc35c42ac14ce67a043a53a" dependencies = [ - "leb128fmt", - "wasmparser", + "bumpalo", + "proc-macro2", + "quote", + "syn 3.0.9", + "wasm-bindgen-shared", ] [[package]] -name = "wasm-metadata" -version = "0.244.0" +name = "wasm-bindgen-shared" +version = "0.2.130" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" +checksum = "3b1f9c3798725016c6ee5f2f57869ec0831e8bbc0ab0310a86e787e97f41389e" dependencies = [ - "anyhow", - "indexmap 2.13.0", - "wasm-encoder", - "wasmparser", + "unicode-ident", ] [[package]] @@ -5511,27 +5330,37 @@ dependencies = [ ] [[package]] -name = "wasmparser" -version = "0.244.0" +name = "web-sys" +version = "0.3.107" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" +checksum = "d7d179596ff5ec0f6de4db64d54011bde6289cc49603d3a6e4ce3f329207ab88" dependencies = [ - "bitflags 2.11.0", - "hashbrown 0.15.5", - "indexmap 2.13.0", - "semver", + "js-sys", + "wasm-bindgen", ] [[package]] -name = "web-sys" -version = "0.3.91" +name = "web-time" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "854ba17bb104abfb26ba36da9729addc7ce7f06f5c0f90f3c391f8461cca21f9" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" dependencies = [ "js-sys", "wasm-bindgen", ] +[[package]] +name = "web_atoms" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba8b815c1b593dc0baf78dd0f4fc8fdb2de53198fb1163738093e9a311c33fb3" +dependencies = [ + "phf", + "phf_codegen", + "string_cache", + "string_cache_codegen", +] + [[package]] name = "webkit2gtk" version = "2.0.2" @@ -5578,25 +5407,23 @@ dependencies = [ [[package]] name = "webpki-roots" -version = "1.0.6" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22cfaf3c063993ff62e73cb4311efde4db1efb31ab78a3e5c457939ad5cc0bed" +checksum = "f5f280bbb2b287bc8e7de7e3852945a0ff50d1bbcb5ed42b6c7a568c546df171" dependencies = [ "rustls-pki-types", ] [[package]] name = "webview2-com" -version = "0.38.2" +version = "0.39.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7130243a7a5b33c54a444e54842e6a9e133de08b5ad7b5861cd8ed9a6a5bc96a" +checksum = "3f89fca7a704cee10dcb3654c1dbb8941d1783132f1917358af75bec37a7d7e6" dependencies = [ "webview2-com-macros", "webview2-com-sys", "windows", - "windows-core 0.61.2", - "windows-implement", - "windows-interface", + "windows-core", ] [[package]] @@ -5607,18 +5434,18 @@ checksum = "67a921c1b6914c367b2b823cd4cde6f96beec77d30a939c8199bb377cf9b9b54" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] name = "webview2-com-sys" -version = "0.38.2" +version = "0.39.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "381336cfffd772377d291702245447a5251a2ffa5bad679c99e61bc48bacbf9c" +checksum = "b3a07132775117d6065853d9d1178157b8c90e228de47129d6bce2c7edebedfb" dependencies = [ - "thiserror 2.0.18", + "thiserror 2.0.21", "windows", - "windows-core 0.61.2", + "windows-core", ] [[package]] @@ -5660,52 +5487,39 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "window-vibrancy" -version = "0.6.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9bec5a31f3f9362f2258fd0e9c9dd61a9ca432e7306cc78c444258f0dce9a9c" +checksum = "111e51caca442cafd9bab396628ac4d814880eaea4e63dfd76a12e9f342b5580" dependencies = [ "objc2", "objc2-app-kit", "objc2-core-foundation", "objc2-foundation", + "objc2-quartz-core", "raw-window-handle", - "windows-sys 0.59.0", + "windows-sys 0.61.2", "windows-version", ] [[package]] name = "windows" -version = "0.61.3" +version = "0.62.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9babd3a767a4c1aef6900409f85f5d53ce2544ccdfaa86dad48c91782c6d6893" +checksum = "527fadee13e0c05939a6a05d5bd6eec6cd2e3dbd648b9f8e447c6518133d8580" dependencies = [ "windows-collections", - "windows-core 0.61.2", + "windows-core", "windows-future", - "windows-link 0.1.3", "windows-numerics", ] [[package]] name = "windows-collections" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8" -dependencies = [ - "windows-core 0.61.2", -] - -[[package]] -name = "windows-core" -version = "0.61.2" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" +checksum = "23b2d95af1a8a14a3c7367e1ed4fc9c20e0a26e79551b1454d72583c97cc6610" dependencies = [ - "windows-implement", - "windows-interface", - "windows-link 0.1.3", - "windows-result 0.3.4", - "windows-strings 0.4.2", + "windows-core", ] [[package]] @@ -5716,19 +5530,19 @@ checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" dependencies = [ "windows-implement", "windows-interface", - "windows-link 0.2.1", - "windows-result 0.4.1", - "windows-strings 0.5.1", + "windows-link", + "windows-result", + "windows-strings", ] [[package]] name = "windows-future" -version = "0.2.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e" +checksum = "e1d6f90251fe18a279739e78025bd6ddc52a7e22f921070ccdc67dde84c605cb" dependencies = [ - "windows-core 0.61.2", - "windows-link 0.1.3", + "windows-core", + "windows-link", "windows-threading", ] @@ -5740,7 +5554,7 @@ checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -5751,15 +5565,9 @@ checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] -[[package]] -name = "windows-link" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" - [[package]] name = "windows-link" version = "0.2.1" @@ -5767,40 +5575,46 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" [[package]] -name = "windows-numerics" -version = "0.2.0" +name = "windows-native-keyring-store" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1" +checksum = "063426e76fdec7438d56bb777f67e318a84a25c707b07e575cb8b78e10c028f8" dependencies = [ - "windows-core 0.61.2", - "windows-link 0.1.3", + "byteorder", + "keyring-core", + "regex", + "windows-sys 0.61.2", + "zeroize", ] [[package]] -name = "windows-result" -version = "0.3.4" +name = "windows-numerics" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" +checksum = "6e2e40844ac143cdb44aead537bbf727de9b044e107a0f1220392177d15b0f26" dependencies = [ - "windows-link 0.1.3", + "windows-core", + "windows-link", ] [[package]] -name = "windows-result" -version = "0.4.1" +name = "windows-registry" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" +checksum = "02752bf7fbdcce7f2a27a742f798510f3e5ad88dbe84871e5168e2120c3d5720" dependencies = [ - "windows-link 0.2.1", + "windows-link", + "windows-result", + "windows-strings", ] [[package]] -name = "windows-strings" -version = "0.4.2" +name = "windows-result" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" dependencies = [ - "windows-link 0.1.3", + "windows-link", ] [[package]] @@ -5809,7 +5623,7 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" dependencies = [ - "windows-link 0.2.1", + "windows-link", ] [[package]] @@ -5821,15 +5635,6 @@ dependencies = [ "windows-targets 0.42.2", ] -[[package]] -name = "windows-sys" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" -dependencies = [ - "windows-targets 0.48.5", -] - [[package]] name = "windows-sys" version = "0.52.0" @@ -5839,31 +5644,13 @@ dependencies = [ "windows-targets 0.52.6", ] -[[package]] -name = "windows-sys" -version = "0.59.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" -dependencies = [ - "windows-targets 0.52.6", -] - -[[package]] -name = "windows-sys" -version = "0.60.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" -dependencies = [ - "windows-targets 0.53.5", -] - [[package]] name = "windows-sys" version = "0.61.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" dependencies = [ - "windows-link 0.2.1", + "windows-link", ] [[package]] @@ -5881,21 +5668,6 @@ dependencies = [ "windows_x86_64_msvc 0.42.2", ] -[[package]] -name = "windows-targets" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" -dependencies = [ - "windows_aarch64_gnullvm 0.48.5", - "windows_aarch64_msvc 0.48.5", - "windows_i686_gnu 0.48.5", - "windows_i686_msvc 0.48.5", - "windows_x86_64_gnu 0.48.5", - "windows_x86_64_gnullvm 0.48.5", - "windows_x86_64_msvc 0.48.5", -] - [[package]] name = "windows-targets" version = "0.52.6" @@ -5905,37 +5677,20 @@ dependencies = [ "windows_aarch64_gnullvm 0.52.6", "windows_aarch64_msvc 0.52.6", "windows_i686_gnu 0.52.6", - "windows_i686_gnullvm 0.52.6", + "windows_i686_gnullvm", "windows_i686_msvc 0.52.6", "windows_x86_64_gnu 0.52.6", "windows_x86_64_gnullvm 0.52.6", "windows_x86_64_msvc 0.52.6", ] -[[package]] -name = "windows-targets" -version = "0.53.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" -dependencies = [ - "windows-link 0.2.1", - "windows_aarch64_gnullvm 0.53.1", - "windows_aarch64_msvc 0.53.1", - "windows_i686_gnu 0.53.1", - "windows_i686_gnullvm 0.53.1", - "windows_i686_msvc 0.53.1", - "windows_x86_64_gnu 0.53.1", - "windows_x86_64_gnullvm 0.53.1", - "windows_x86_64_msvc 0.53.1", -] - [[package]] name = "windows-threading" -version = "0.1.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b66463ad2e0ea3bbf808b7f1d371311c80e115c0b71d60efc142cafbcfb057a6" +checksum = "3949bd5b99cafdf1c7ca86b43ca564028dfe27d66958f2470940f73d86d75b37" dependencies = [ - "windows-link 0.1.3", + "windows-link", ] [[package]] @@ -5944,7 +5699,7 @@ version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e4060a1da109b9d0326b7262c8e12c84df67cc0dbc9e33cf49e01ccc2eb63631" dependencies = [ - "windows-link 0.2.1", + "windows-link", ] [[package]] @@ -5953,180 +5708,90 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" - [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" - [[package]] name = "windows_aarch64_msvc" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" -[[package]] -name = "windows_aarch64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" - [[package]] name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" -[[package]] -name = "windows_aarch64_msvc" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" - [[package]] name = "windows_i686_gnu" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" -[[package]] -name = "windows_i686_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" - [[package]] name = "windows_i686_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" -[[package]] -name = "windows_i686_gnu" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" - [[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" -[[package]] -name = "windows_i686_gnullvm" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" - [[package]] name = "windows_i686_msvc" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" -[[package]] -name = "windows_i686_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" - [[package]] name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" -[[package]] -name = "windows_i686_msvc" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" - [[package]] name = "windows_x86_64_gnu" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" -[[package]] -name = "windows_x86_64_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" - [[package]] name = "windows_x86_64_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" -[[package]] -name = "windows_x86_64_gnu" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" - [[package]] name = "windows_x86_64_gnullvm" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" - [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" - [[package]] name = "windows_x86_64_msvc" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" -[[package]] -name = "windows_x86_64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" - [[package]] name = "windows_x86_64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" -[[package]] -name = "windows_x86_64_msvc" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" - [[package]] name = "winnow" version = "0.5.40" @@ -6138,147 +5803,54 @@ dependencies = [ [[package]] name = "winnow" -version = "0.7.14" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a5364e9d77fcdeeaa6062ced926ee3381faa2ee02d3eb83a5c27a8825540829" +checksum = "23b97319f7b8343df12cc98938e5c3eb436064524c8d2b4e30a1d3a36eecdf81" dependencies = [ "memchr", ] [[package]] name = "winreg" -version = "0.50.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" -dependencies = [ - "cfg-if", - "windows-sys 0.48.0", -] - -[[package]] -name = "winreg" -version = "0.55.0" +version = "0.56.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb5a765337c50e9ec252c2069be9bf91c7df47afb103b642ba3a53bf8101be97" +checksum = "7d6f32a0ff4a9f6f01231eb2059cc85479330739333e0e58cadf03b6af2cca10" dependencies = [ "cfg-if", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] name = "wit-bindgen" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" -dependencies = [ - "wit-bindgen-rust-macro", -] - -[[package]] -name = "wit-bindgen-core" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" -dependencies = [ - "anyhow", - "heck 0.5.0", - "wit-parser", -] - -[[package]] -name = "wit-bindgen-rust" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" -dependencies = [ - "anyhow", - "heck 0.5.0", - "indexmap 2.13.0", - "prettyplease", - "syn 2.0.117", - "wasm-metadata", - "wit-bindgen-core", - "wit-component", -] - -[[package]] -name = "wit-bindgen-rust-macro" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a" -dependencies = [ - "anyhow", - "prettyplease", - "proc-macro2", - "quote", - "syn 2.0.117", - "wit-bindgen-core", - "wit-bindgen-rust", -] - -[[package]] -name = "wit-component" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" -dependencies = [ - "anyhow", - "bitflags 2.11.0", - "indexmap 2.13.0", - "log", - "serde", - "serde_derive", - "serde_json", - "wasm-encoder", - "wasm-metadata", - "wasmparser", - "wit-parser", -] - -[[package]] -name = "wit-parser" -version = "0.244.0" +version = "0.57.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" -dependencies = [ - "anyhow", - "id-arena", - "indexmap 2.13.0", - "log", - "semver", - "serde", - "serde_derive", - "serde_json", - "unicode-xid", - "wasmparser", -] +checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" [[package]] name = "writeable" -version = "0.6.2" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" +checksum = "3ad82d2a33cdc9674dc7465672f271e096168fcdbe0f799d9e6db8c5892679dc" [[package]] name = "wry" -version = "0.54.2" +version = "0.57.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb26159b420aa77684589a744ae9a9461a95395b848764ad12290a14d960a11a" +checksum = "759c50ddad0ef9913bb5496cb9d509a50433a3b78cb0238d61105526589cf0e9" dependencies = [ - "base64 0.22.1", + "base64 0.23.1", "block2", "cookie", "crossbeam-channel", "dirs", + "dom_query", "dpi", "dunce", "gdkx11", "gtk", - "html5ever", "http", "javascriptcore-rs", - "jni", - "kuchikiki", + "jni 0.21.1", "libc", "ndk", "objc2", @@ -6287,19 +5859,18 @@ dependencies = [ "objc2-foundation", "objc2-ui-kit", "objc2-web-kit", - "once_cell", "percent-encoding", "raw-window-handle", - "sha2", + "sha2 0.10.9", "soup3", "tao-macros", - "thiserror 2.0.18", + "thiserror 2.0.21", "url", "webkit2gtk", "webkit2gtk-sys", "webview2-com", "windows", - "windows-core 0.61.2", + "windows-core", "windows-version", "x11-dl", ] @@ -6336,9 +5907,9 @@ dependencies = [ [[package]] name = "yoke" -version = "0.8.1" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72d6e5c6afb84d73944e5cedb052c4680d5657337201555f9f2a16b7406d4954" +checksum = "417ef3fcf6c300b7dbd25c85531a746931c02ad536f3cf4786ed1e88223340b3" dependencies = [ "stable_deref_trait", "yoke-derive", @@ -6347,21 +5918,21 @@ dependencies = [ [[package]] name = "yoke-derive" -version = "0.8.1" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" +checksum = "ec8ebde2db3681e8c9980cc27822030e68752690ddfa9473e739aeb4dbde6d71" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 3.0.9", "synstructure", ] [[package]] name = "zbus" -version = "5.14.0" +version = "5.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca82f95dbd3943a40a53cfded6c2d0a2ca26192011846a1810c4256ef92c60bc" +checksum = "5db4be7c075cb421e4b7ee645541604239bd243ba7c357511f4ff3a74b555907" dependencies = [ "async-broadcast", "async-executor", @@ -6386,22 +5957,33 @@ dependencies = [ "uds_windows", "uuid", "windows-sys 0.61.2", - "winnow 0.7.14", + "winnow 1.0.4", "zbus_macros", "zbus_names", "zvariant", ] +[[package]] +name = "zbus-secret-service-keyring-store" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74801d001b9e7729adb4f1825b67b398185fed424749aa3d8bacf70417137d9a" +dependencies = [ + "keyring-core", + "secret-service", + "zbus", +] + [[package]] name = "zbus_macros" -version = "5.14.0" +version = "5.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "897e79616e84aac4b2c46e9132a4f63b93105d54fe8c0e8f6bffc21fa8d49222" +checksum = "2990635d09ade6df1868f72f8cac69a876a90981e8bd3c40b1be413f8dc88f40" dependencies = [ - "proc-macro-crate 3.4.0", + "proc-macro-crate 3.5.0", "proc-macro2", "quote", - "syn 2.0.117", + "syn 3.0.9", "zbus_names", "zvariant", "zvariant_utils", @@ -6409,81 +5991,76 @@ dependencies = [ [[package]] name = "zbus_names" -version = "4.3.1" +version = "4.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffd8af6d5b78619bab301ff3c560a5bd22426150253db278f164d6cf3b72c50f" +checksum = "812a12be4ead7a59e18ea8f047d08add0066b73bc62fc1467066a2472ae088dd" dependencies = [ "serde", - "winnow 0.7.14", + "winnow 1.0.4", "zvariant", ] +[[package]] +name = "zcheapstr" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "939f186d9ecebe044315a5cb6d6f78568bbc21152adc954d5a0c2f58bdbc9920" +dependencies = [ + "serde", +] + [[package]] name = "zerocopy" -version = "0.8.40" +version = "0.8.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a789c6e490b576db9f7e6b6d661bcc9799f7c0ac8352f56ea20193b2681532e5" +checksum = "e5fe1f8f1b06191a00962174c61aa5005e0bb391a6d80d07e24d115c01a92ed8" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.40" +version = "0.8.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f65c489a7071a749c849713807783f70672b28094011623e200cb86dcb835953" +checksum = "863ad3ac83293fb4d740aedbfdc9240dd8d1a50c1099acd76ce80ce7c7230c7f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] name = "zerofrom" -version = "0.1.6" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" +checksum = "0ec05a11813ea801ff6d75110ad09cd0824ddba17dfe17128ea0d5f68e6c5272" dependencies = [ "zerofrom-derive", ] [[package]] name = "zerofrom-derive" -version = "0.1.6" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" +checksum = "f75b4683f6c7f45248d4d64056a24298c6281e0993356d7d1b4a1a962ef10d4a" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 3.0.9", "synstructure", ] [[package]] name = "zeroize" -version = "1.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" -dependencies = [ - "zeroize_derive", -] - -[[package]] -name = "zeroize_derive" -version = "1.4.3" +version = "1.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85a5b4158499876c763cb03bc4e49185d3cccbabb15b33c627f7884f43db852e" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] +checksum = "e13084392c5e4bc371903e2935a5eaeed24905a7511356b883835e18a78f6879" [[package]] name = "zerotrie" -version = "0.2.3" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a59c17a5562d507e4b54960e8569ebee33bee890c70aa3fe7b97e85a9fd7851" +checksum = "4ea269c3bd32f0a32c321907a2ae912ba6f4649bb0fc764a15627e99a7095a3f" dependencies = [ "displaydoc", "yoke", @@ -6492,9 +6069,9 @@ dependencies = [ [[package]] name = "zerovec" -version = "0.11.5" +version = "0.11.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002" +checksum = "f58a2799cfe9a1670c7ea8edbd52ef8edb93cb37a7f83bf31ba9e35d9dae6a40" dependencies = [ "yoke", "zerofrom", @@ -6503,57 +6080,64 @@ dependencies = [ [[package]] name = "zerovec-derive" -version = "0.11.2" +version = "0.11.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" +checksum = "c3743ddbf47738d929cd1ae2e199d40d0162979dfe31aef8ae646d2bf00bf97a" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 3.0.9", ] +[[package]] +name = "zlib-rs" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b268e58e7c693d7c271f93ffc4ba3b380412554231c85bf61ca7af91042a4112" + [[package]] name = "zmij" -version = "1.0.21" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" +checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b" [[package]] name = "zvariant" -version = "5.10.0" +version = "5.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5708299b21903bbe348e94729f22c49c55d04720a004aa350f1f9c122fd2540b" +checksum = "80bf6d6cd6a4b601acb64cfb8284cdc643fcfa75ef26ae3c46ec8aa4e8a1fbb7" dependencies = [ "endi", "enumflags2", "serde", - "winnow 0.7.14", + "winnow 1.0.4", + "zcheapstr", "zvariant_derive", "zvariant_utils", ] [[package]] name = "zvariant_derive" -version = "5.10.0" +version = "5.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b59b012ebe9c46656f9cc08d8da8b4c726510aef12559da3e5f1bf72780752c" +checksum = "ef2672456ba1cb54b5eaec136e63135b3c7495fb938e53b28a62e633496d486d" dependencies = [ - "proc-macro-crate 3.4.0", + "proc-macro-crate 3.5.0", "proc-macro2", "quote", - "syn 2.0.117", + "syn 3.0.9", "zvariant_utils", ] [[package]] name = "zvariant_utils" -version = "3.3.0" +version = "4.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f75c23a64ef8f40f13a6989991e643554d9bef1d682a281160cf0c1bc389c5e9" +checksum = "bad0294361a320b694a328460dc73add56c306150f5cb6bfafc44446120008a3" dependencies = [ "proc-macro2", "quote", "serde", - "syn 2.0.117", - "winnow 0.7.14", + "syn 3.0.9", + "winnow 1.0.4", ] diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index be75ba4..f1eb2ab 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -22,13 +22,16 @@ tauri = { version = "2", features = [] } tauri-plugin-opener = "2" serde = { version = "1", features = ["derive"] } serde_json = "1" -mongodb = "3.5.0" -futures = "0.3.31" -uuid = "1.20.0" -tokio = { version = "1.49.0", features = ["fs"] } -async-trait = "0.1" -thiserror = "2.0.18" -log = "0.4.29" +mongodb = "3.9.1" +futures = "0.3.34" +uuid = "1.29.0" +tokio = { version = "1.54.1", features = ["fs"] } +async-trait = "0.1.92" +thiserror = "2.0.21" +log = "0.4.35" tauri-plugin-log = "2" -url = "2.5.8" -keyring = { version = "3.6.3", features = ["apple-native", "windows-native", "linux-native-sync-persistent", "vendored" ] } +percent-encoding = "2.3.2" +keyring = "4.2.0" + +[dev-dependencies] +tokio = { version = "1.54.1", features = ["macros", "rt-multi-thread"] } diff --git a/src-tauri/src/connection_handler/connection_handler.rs b/src-tauri/src/connection_handler/connection_handler.rs index 4683371..2408fa2 100644 --- a/src-tauri/src/connection_handler/connection_handler.rs +++ b/src-tauri/src/connection_handler/connection_handler.rs @@ -1,50 +1,344 @@ use crate::{ - connector::models::connection::Connection, + connector::{ + commands::error::RepositoryError, models::connection::Connection, + repositories::connector_repository::ConnectorRepository, + }, drivers::{driver::DatabaseDriver, errors::DriverError, factory::DatabaseDriverFactory}, }; use std::{collections::HashMap, sync::Arc}; use tokio::sync::Mutex; +/// The mongodb client is safe to share, so several commands of one connection run at the same time. +pub type SharedDriver = Arc; +type DriverFactory = Box Result, DriverError> + Send + Sync>; + +/// One connected driver per saved connection, created on first use. +/// A driver leaves the pool on disconnect, when its connection is edited or deleted, +/// and when its first command fails, so the next use builds it from the saved settings again. pub struct ConnectionHandler { - connection_pool: Mutex>>>>, + connection_pool: Mutex>, + factory: DriverFactory, +} + +impl Default for ConnectionHandler { + fn default() -> Self { + Self::new() + } } impl ConnectionHandler { pub fn new() -> Self { + Self::with_factory(Box::new(DatabaseDriverFactory::create)) + } + + pub fn with_factory(factory: DriverFactory) -> Self { ConnectionHandler { connection_pool: Mutex::new(HashMap::new()), + factory, } } + /// The saved connection with its password, for a driver. + async fn load( + repository: &dyn ConnectorRepository, + connection_id: &str, + ) -> Result { + let connection = repository.get(connection_id).await.map_err(|e| match e { + RepositoryError::NotFound(id) => DriverError::ConnectionNotFound(id), + other => DriverError::StorageFailed(other.to_string()), + })?; + + repository + .hydrate(&connection) + .await + .map_err(|e| DriverError::CredentialsUnavailable(e.to_string())) + } + pub async fn get_or_connect( &self, - connection: Connection, - ) -> Result>>, DriverError> { - let mut pool = self.connection_pool.lock().await; - - if let Some(driver) = pool.get(&connection.id) { + repository: &dyn ConnectorRepository, + connection_id: &str, + ) -> Result { + if let Some(driver) = self.connection_pool.lock().await.get(connection_id) { return Ok(Arc::clone(driver)); } - let mut driver = DatabaseDriverFactory::create(connection.clone())?; - + // loading (keyring) and connecting (dns for srv strings) can take long; + // the pool stays unlocked meanwhile, so other connections don't wait + let connection = Self::load(repository, connection_id).await?; + let mut driver = (self.factory)(connection)?; driver.connect().await?; + let shared_driver: SharedDriver = Arc::from(driver); - let shared_driver = Arc::from(Mutex::new(driver)); + // a parallel call may have connected the same connection first; keep that driver + let mut pool = self.connection_pool.lock().await; + Ok(Arc::clone( + pool.entry(connection_id.to_string()).or_insert(shared_driver), + )) + } - pool.insert(connection.id.clone(), Arc::clone(&shared_driver)); + /// Connects and lists the databases. The client only reaches the server with this first + /// command, so a failure (wrong password, host down) drops the driver again. + pub async fn open( + &self, + repository: &dyn ConnectorRepository, + connection_id: &str, + ) -> Result, DriverError> { + let driver = self.get_or_connect(repository, connection_id).await?; + let result = driver.list_databases().await; - return Ok(shared_driver); + if result.is_err() { + self.disconnect(connection_id).await?; + } + + result } + /// Drops the driver; its client closes once running commands finish with it. pub async fn disconnect(&self, connection_id: &str) -> Result<(), DriverError> { - let mut pool = self.connection_pool.lock().await; + self.connection_pool.lock().await.remove(connection_id); + Ok(()) + } + + #[cfg(test)] + async fn is_pooled(&self, connection_id: &str) -> bool { + self.connection_pool.lock().await.contains_key(connection_id) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::connector::commands::error::CredentialError; + use crate::connector::models::{ + authentication::{AuthenticationKind, AuthenticationOptions}, + connection::{ConnectionTarget, ConnectorKind}, + create_connection::CreateConnection, + options::ConnectionOptions, + }; + use crate::database::models::find_document::{FindDocumentsResult, FindQuery}; + use crate::drivers::driver::{ProgressCallback, TestStage}; + use async_trait::async_trait; + use std::sync::atomic::{AtomicUsize, Ordering}; - if let Some(lock) = pool.remove(connection_id) { - let mut driver = lock.lock().await; - driver.disconnect().await?; + #[derive(Default)] + struct Calls { + created: AtomicUsize, + } + + struct FakeDriver { + connection: Connection, + } + + #[async_trait] + impl DatabaseDriver for FakeDriver { + async fn connect(&mut self) -> Result<(), DriverError> { + if self.connection.name == "refuses" { + return Err(DriverError::UriMalformed("bad".into())); + } + Ok(()) } - Ok(()) + async fn test_connection(&self, _: ProgressCallback) -> Result, DriverError> { + Ok(vec![]) + } + + async fn list_databases(&self) -> Result, DriverError> { + match self.connection.authentication.password.as_deref() { + Some("wrong") => Err(DriverError::ListDatabasesFailed("Authentication failed".into())), + _ => Ok(vec![format!("db of {}", self.connection.name)]), + } + } + + async fn list_collections(&self, _: &str) -> Result, DriverError> { + Ok(vec![]) + } + + async fn find_documents(&self, _: &FindQuery) -> Result { + Ok(FindDocumentsResult { documents: vec![], total_count: 0, total_count_estimated: false }) + } + } + + /// Connections and passwords in memory, standing in for the file repository. + #[derive(Default)] + struct MemoryRepository { + connections: std::sync::Mutex>, + passwords: std::sync::Mutex>, + } + + impl MemoryRepository { + fn add(&self, id: &str, name: &str, password: Option<&str>) { + let connection = Connection { + id: id.into(), + name: name.into(), + connector: ConnectorKind::MongoDb, + target: ConnectionTarget::Host { host: "h".into(), port: 1 }, + authentication: AuthenticationOptions { + kind: if password.is_some() { AuthenticationKind::BASIC } else { AuthenticationKind::NONE }, + username: password.map(|_| "u".to_string()), + password: None, + database: None, + }, + options: ConnectionOptions::default(), + }; + self.connections.lock().unwrap().insert(id.into(), connection); + if let Some(password) = password { + self.passwords.lock().unwrap().insert(id.into(), password.into()); + } + } + + fn set_password(&self, id: &str, password: &str) { + self.passwords.lock().unwrap().insert(id.into(), password.into()); + } + + fn forget_password(&self, id: &str) { + self.passwords.lock().unwrap().remove(id); + } + } + + #[async_trait] + impl ConnectorRepository for MemoryRepository { + async fn list(&self) -> Result, RepositoryError> { + Ok(self.connections.lock().unwrap().values().cloned().collect()) + } + async fn get(&self, id: &str) -> Result { + self.connections.lock().unwrap().get(id).cloned().ok_or_else(|| RepositoryError::NotFound(id.into())) + } + async fn save(&self, _: CreateConnection) -> Result { + unimplemented!() + } + async fn update(&self, _: &str, _: &Connection) -> Result<(), RepositoryError> { + unimplemented!() + } + async fn delete(&self, _: &str) -> Result<(), RepositoryError> { + unimplemented!() + } + async fn get_password(&self, id: &str) -> Result, RepositoryError> { + Ok(self.passwords.lock().unwrap().get(id).cloned()) + } + async fn hydrate(&self, connection: &Connection) -> Result { + let mut hydrated = connection.clone(); + if hydrated.authentication.kind == AuthenticationKind::BASIC { + let password = self.passwords.lock().unwrap().get(&connection.id).cloned() + .ok_or_else(|| RepositoryError::Credential(CredentialError::NotFound(connection.id.clone())))?; + hydrated.authentication.password = Some(password); + } + Ok(hydrated) + } + } + + fn handler() -> (ConnectionHandler, Arc) { + let calls = Arc::new(Calls::default()); + let factory_calls = Arc::clone(&calls); + let handler = ConnectionHandler::with_factory(Box::new(move |connection| { + factory_calls.created.fetch_add(1, Ordering::SeqCst); + Ok(Box::new(FakeDriver { connection }) as Box) + })); + (handler, calls) + } + + #[tokio::test] + async fn creates_a_driver_once_and_reuses_it() { + let (handler, calls) = handler(); + let repository = MemoryRepository::default(); + repository.add("a", "A", None); + + assert_eq!(handler.open(&repository, "a").await.unwrap(), vec!["db of A"]); + handler.get_or_connect(&repository, "a").await.unwrap(); + + assert_eq!(calls.created.load(Ordering::SeqCst), 1); + assert!(handler.is_pooled("a").await); + } + + #[tokio::test] + async fn disconnect_drops_the_driver_and_the_next_use_reads_the_settings_again() { + let (handler, calls) = handler(); + let repository = MemoryRepository::default(); + repository.add("a", "A", None); + handler.open(&repository, "a").await.unwrap(); + + repository.add("a", "Renamed", None); + handler.disconnect("a").await.unwrap(); + + assert!(!handler.is_pooled("a").await); + assert_eq!(handler.open(&repository, "a").await.unwrap(), vec!["db of Renamed"]); + assert_eq!(calls.created.load(Ordering::SeqCst), 2); + } + + #[tokio::test] + async fn disconnecting_an_unknown_connection_does_nothing() { + let (handler, _calls) = handler(); + handler.disconnect("nope").await.unwrap(); + assert!(!handler.is_pooled("nope").await); + } + + #[tokio::test] + async fn two_commands_of_one_connection_share_one_driver_at_the_same_time() { + let (handler, calls) = handler(); + let repository = MemoryRepository::default(); + repository.add("a", "A", None); + + let (first, second) = tokio::join!( + handler.get_or_connect(&repository, "a"), + handler.get_or_connect(&repository, "a"), + ); + + // both may build a driver, but the pool keeps one and both callers get it + assert!(Arc::ptr_eq(&first.unwrap(), &second.unwrap())); + assert!(calls.created.load(Ordering::SeqCst) <= 2); + assert_eq!(handler.connection_pool.lock().await.len(), 1); + } + + #[tokio::test] + async fn a_failing_first_command_drops_the_driver_so_a_new_password_works() { + let (handler, calls) = handler(); + let repository = MemoryRepository::default(); + repository.add("a", "A", Some("wrong")); + + assert!(matches!(handler.open(&repository, "a").await, Err(DriverError::ListDatabasesFailed(_)))); + assert!(!handler.is_pooled("a").await); + + repository.set_password("a", "right"); + assert_eq!(handler.open(&repository, "a").await.unwrap(), vec!["db of A"]); + assert_eq!(calls.created.load(Ordering::SeqCst), 2); + } + + #[tokio::test] + async fn an_unknown_connection_is_not_found() { + let (handler, calls) = handler(); + let repository = MemoryRepository::default(); + assert!(matches!(handler.open(&repository, "x").await, Err(DriverError::ConnectionNotFound(id)) if id == "x")); + assert_eq!(calls.created.load(Ordering::SeqCst), 0); + } + + #[tokio::test] + async fn a_missing_password_is_a_credential_error() { + let (handler, _calls) = handler(); + let repository = MemoryRepository::default(); + repository.add("a", "A", Some("pw")); + repository.forget_password("a"); + + assert!(matches!(handler.open(&repository, "a").await, Err(DriverError::CredentialsUnavailable(_)))); + assert!(!handler.is_pooled("a").await); + } + + #[tokio::test] + async fn a_driver_that_fails_to_connect_is_not_pooled() { + let (handler, _calls) = handler(); + let repository = MemoryRepository::default(); + repository.add("a", "refuses", None); + + assert!(matches!(handler.get_or_connect(&repository, "a").await, Err(DriverError::UriMalformed(_)))); + assert!(!handler.is_pooled("a").await); + } + + #[tokio::test] + async fn the_password_reaches_the_driver() { + let (handler, _calls) = handler(); + let repository = MemoryRepository::default(); + repository.add("a", "A", Some("pw")); + + let driver = handler.get_or_connect(&repository, "a").await.unwrap(); + assert!(driver.list_databases().await.is_ok()); } } diff --git a/src-tauri/src/connector/commands/create_connection.rs b/src-tauri/src/connector/commands/create_connection.rs index 0f43423..b3947c3 100644 --- a/src-tauri/src/connector/commands/create_connection.rs +++ b/src-tauri/src/connector/commands/create_connection.rs @@ -14,5 +14,5 @@ pub async fn create_connection( .connection_repository .save(connection) .await - .map_err(|e| CommandError::new(e.to_string(), "repository", "Failed to create connection.")) + .map_err(|e| CommandError::from_repository(e, "Failed to create connection.")) } diff --git a/src-tauri/src/connector/commands/delete_connection.rs b/src-tauri/src/connector/commands/delete_connection.rs index aa281bf..979c0f2 100644 --- a/src-tauri/src/connector/commands/delete_connection.rs +++ b/src-tauri/src/connector/commands/delete_connection.rs @@ -1,13 +1,20 @@ use tauri::State; +use crate::connection_handler::connection_handler::ConnectionHandler; use crate::connector::commands::error::CommandError; use crate::AppState; #[tauri::command] -pub async fn delete_connection(state: State<'_, AppState>, id: String) -> Result<(), CommandError> { +pub async fn delete_connection( + state: State<'_, AppState>, + connection_handler: State<'_, ConnectionHandler>, + id: String, +) -> Result<(), CommandError> { + let _ = connection_handler.disconnect(&id).await; + state .connection_repository .delete(&id) .await - .map_err(|e| CommandError::new(e.to_string(), "repository", "Failed to delete connection.")) + .map_err(|e| CommandError::from_repository(e, "Failed to delete connection.")) } diff --git a/src-tauri/src/connector/commands/error.rs b/src-tauri/src/connector/commands/error.rs index d1f47d4..cb0fec2 100644 --- a/src-tauri/src/connector/commands/error.rs +++ b/src-tauri/src/connector/commands/error.rs @@ -22,6 +22,16 @@ impl CommandError { } } +impl CommandError { + /// A connection that fails validation tells the user why; other failures get `user_message`. + pub fn from_repository(err: RepositoryError, user_message: &str) -> Self { + match err { + RepositoryError::Invalid(message) => CommandError::new(message.clone(), "invalid", message), + other => CommandError::new(other.to_string(), "repository", user_message), + } + } +} + impl From for CommandError { fn from(err: std::io::Error) -> Self { CommandError { @@ -57,6 +67,9 @@ pub enum RepositoryError { #[error("Connection not found: {0}")] NotFound(String), + #[error("{0}")] + Invalid(String), + #[error("IO error: {0}")] Io(#[from] std::io::Error), @@ -72,6 +85,9 @@ pub enum RepositoryError { #[derive(Debug, Error)] pub enum CredentialError { + #[error("No password stored for connection {0}")] + NotFound(String), + #[error("Keyring error: {0}")] Keyring(#[from] keyring::Error), diff --git a/src-tauri/src/connector/commands/get_connection.rs b/src-tauri/src/connector/commands/get_connection.rs deleted file mode 100644 index fac6ac7..0000000 --- a/src-tauri/src/connector/commands/get_connection.rs +++ /dev/null @@ -1,17 +0,0 @@ -use tauri::State; - -use crate::connector::commands::error::CommandError; -use crate::connector::models::connection::Connection; -use crate::AppState; - -#[tauri::command] -pub async fn get_connection( - state: State<'_, AppState>, - id: String, -) -> Result { - state - .connection_repository - .get(&id) - .await - .map_err(|e| CommandError::new(e.to_string(), "repository", "Failed to load connection.")) -} diff --git a/src-tauri/src/connector/commands/get_connection_password.rs b/src-tauri/src/connector/commands/get_connection_password.rs index 47f0515..52c31de 100644 --- a/src-tauri/src/connector/commands/get_connection_password.rs +++ b/src-tauri/src/connector/commands/get_connection_password.rs @@ -1,7 +1,6 @@ use tauri::State; use crate::connector::commands::error::CommandError; -use crate::connector::repositories::file_repository::FileRepository; use crate::AppState; #[tauri::command] @@ -9,21 +8,10 @@ pub async fn get_connection_password( state: State<'_, AppState>, id: String, ) -> Result, CommandError> { - let repository = &state.connection_repository; - - let file_repo = repository - .as_any() - .downcast_ref::() - .ok_or_else(|| { - CommandError::new( - "Unsupported repository type", - "repository", - "Could not retrieve password.", - ) - })?; - - match file_repo.credential_service.get_password(&id).await { - Ok(password) => Ok(Some(password)), - Err(_) => Ok(None), - } + // the edit dialog shows an empty field when the password can't be read + Ok(state + .connection_repository + .get_password(&id) + .await + .unwrap_or(None)) } diff --git a/src-tauri/src/connector/commands/get_connections.rs b/src-tauri/src/connector/commands/get_connections.rs index 3577003..32b6e86 100644 --- a/src-tauri/src/connector/commands/get_connections.rs +++ b/src-tauri/src/connector/commands/get_connections.rs @@ -10,5 +10,5 @@ pub async fn get_connections(state: State<'_, AppState>) -> Result, + connection_handler: State<'_, ConnectionHandler>, id: String, connection: Connection, ) -> Result<(), CommandError> { @@ -14,5 +16,9 @@ pub async fn update_connection( .connection_repository .update(&id, &connection) .await - .map_err(|e| CommandError::new(e.to_string(), "repository", "Failed to update connection.")) + .map_err(|e| CommandError::from_repository(e, "Failed to update connection."))?; + + // the next command connects with the new settings + let _ = connection_handler.disconnect(&id).await; + Ok(()) } diff --git a/src-tauri/src/connector/models/authentication.rs b/src-tauri/src/connector/models/authentication.rs index dbf6703..5124ff0 100644 --- a/src-tauri/src/connector/models/authentication.rs +++ b/src-tauri/src/connector/models/authentication.rs @@ -1,12 +1,12 @@ use serde::{Deserialize, Serialize}; -#[derive(Debug, Serialize, Deserialize, Clone)] +#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] pub enum AuthenticationKind { NONE, BASIC, } -#[derive(Debug, Serialize, Deserialize, Clone)] +#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] pub struct AuthenticationOptions { pub kind: AuthenticationKind, pub username: Option, diff --git a/src-tauri/src/connector/models/connection.rs b/src-tauri/src/connector/models/connection.rs index b7dd6d2..1d25c73 100644 --- a/src-tauri/src/connector/models/connection.rs +++ b/src-tauri/src/connector/models/connection.rs @@ -1,39 +1,35 @@ use serde::{Deserialize, Serialize}; use crate::connector::models::{ - authentication::AuthenticationOptions, connection_type::ConnectionType, create_connection::CreateConnection, + authentication::AuthenticationOptions, create_connection::CreateConnection, + options::ConnectionOptions, }; use uuid::Uuid; -#[derive(Serialize, Debug, Deserialize, Clone)] +#[derive(Serialize, Debug, Deserialize, Clone, PartialEq)] pub enum ConnectorKind { MongoDb, } +/// Where the server is: host and port with option fields, or a connection string. +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] +#[serde(tag = "kind")] +pub enum ConnectionTarget { + Host { host: String, port: u16 }, + /// `mongodb://` or `mongodb+srv://`, stored without user info + Uri { uri: String }, +} + #[derive(Serialize, Debug, Deserialize, Clone)] #[serde(rename_all = "camelCase")] pub struct Connection { pub id: String, pub name: String, - pub uri: String, - pub port: u16, pub connector: ConnectorKind, - pub connection_type: ConnectionType, + pub target: ConnectionTarget, pub authentication: AuthenticationOptions, -} - -impl Default for Connection { - fn default() -> Self { - Connection { - id: Uuid::new_v4().to_string(), - name: String::new(), - uri: String::new(), - port: 27017, - connector: ConnectorKind::MongoDb, - connection_type: ConnectionType::Standalone, - authentication: AuthenticationOptions::default(), - } - } + #[serde(default)] + pub options: ConnectionOptions, } impl From for Connection { @@ -41,25 +37,10 @@ impl From for Connection { Connection { id: Uuid::new_v4().to_string(), name: entity.name, - uri: entity.uri, - authentication: entity.authentication, - connection_type: entity.connection_type, connector: entity.connector, - port: entity.port - } - } -} - -impl Connection { - pub fn new(value: Connection) -> Self { - Connection { - id: Uuid::new_v4().to_string(), - name: value.name, - uri: value.uri, - port: value.port, - connector: value.connector, - connection_type: value.connection_type, - authentication: value.authentication, + target: entity.target, + authentication: entity.authentication, + options: entity.options, } } } diff --git a/src-tauri/src/connector/models/connection_type.rs b/src-tauri/src/connector/models/connection_type.rs deleted file mode 100644 index eb8f53c..0000000 --- a/src-tauri/src/connector/models/connection_type.rs +++ /dev/null @@ -1,9 +0,0 @@ -use serde::{Deserialize, Serialize}; - -#[derive(Debug, Serialize, Deserialize, Clone)] -pub enum ConnectionType { - Standalone, - ReplicaSet, - ShardedCluster, - DnsSeedList, -} diff --git a/src-tauri/src/connector/models/create_connection.rs b/src-tauri/src/connector/models/create_connection.rs index a332385..cf43990 100644 --- a/src-tauri/src/connector/models/create_connection.rs +++ b/src-tauri/src/connector/models/create_connection.rs @@ -1,15 +1,18 @@ use serde::{Deserialize, Serialize}; -use crate::connector::models::{authentication::AuthenticationOptions, connection::ConnectorKind, connection_type::ConnectionType}; - +use crate::connector::models::{ + authentication::AuthenticationOptions, + connection::{ConnectionTarget, ConnectorKind}, + options::ConnectionOptions, +}; #[derive(Serialize, Debug, Deserialize, Clone)] #[serde(rename_all = "camelCase")] pub struct CreateConnection { pub name: String, - pub uri: String, - pub port: u16, pub connector: ConnectorKind, - pub connection_type: ConnectionType, + pub target: ConnectionTarget, pub authentication: AuthenticationOptions, -} \ No newline at end of file + #[serde(default)] + pub options: ConnectionOptions, +} diff --git a/src-tauri/src/connector/models/mod.rs b/src-tauri/src/connector/models/mod.rs index e75a511..6d0f3c2 100644 --- a/src-tauri/src/connector/models/mod.rs +++ b/src-tauri/src/connector/models/mod.rs @@ -1,4 +1,6 @@ pub mod authentication; pub mod connection; -pub mod connection_type; -pub mod create_connection; \ No newline at end of file +pub mod create_connection; +pub mod normalize; +pub mod options; +pub mod uri; diff --git a/src-tauri/src/connector/models/normalize.rs b/src-tauri/src/connector/models/normalize.rs new file mode 100644 index 0000000..c942e27 --- /dev/null +++ b/src-tauri/src/connector/models/normalize.rs @@ -0,0 +1,161 @@ +use crate::connector::models::{ + authentication::{AuthenticationKind, AuthenticationOptions}, + connection::ConnectionTarget, + uri::{is_connection_string, split_uri_credentials}, +}; + +/// `db.local:27018` typed into the host field: the host and the port after it. +pub fn split_host_port(host: &str) -> Option<(&str, u16)> { + let (name, port) = host.rsplit_once(':')?; + // an IPv6 address has more colons and stays as it is + if name.is_empty() || name.contains(':') { + return None; + } + port.parse().ok().map(|port| (name, port)) +} + +/// Checks a connection before it is stored and moves credentials out of a connection string: +/// the username into the authentication, the password to where the repository keeps passwords. +/// Credentials typed into the form win over the ones in the string. +pub fn normalize( + name: &str, + target: ConnectionTarget, + mut auth: AuthenticationOptions, +) -> Result<(ConnectionTarget, AuthenticationOptions), String> { + if name.trim().is_empty() { + return Err(String::from("The connection needs a name.")); + } + + let target = match target { + ConnectionTarget::Host { host, port } => { + let host = host.trim().to_string(); + if host.is_empty() { + return Err(String::from("The connection needs a host.")); + } + if is_connection_string(&host) || host.contains("://") { + return Err(String::from( + "Enter mongodb:// and mongodb+srv:// addresses as a connection string, not as a host.", + )); + } + if split_host_port(&host).is_some() { + return Err(String::from("Enter the port in the port field, not after the host.")); + } + if port == 0 { + return Err(String::from("The port must be between 1 and 65535.")); + } + ConnectionTarget::Host { host, port } + } + ConnectionTarget::Uri { uri } => { + let split = split_uri_credentials(uri.trim())?; + if split.username.is_some() || split.password.is_some() { + auth.kind = AuthenticationKind::BASIC; + } + if auth.username.as_deref().unwrap_or("").is_empty() { + if let Some(username) = split.username { + auth.username = Some(username); + } + } + if auth.password.is_none() { + auth.password = split.password; + } + ConnectionTarget::Uri { uri: split.uri } + } + }; + + if auth.kind == AuthenticationKind::BASIC + && auth.username.as_deref().unwrap_or("").trim().is_empty() + { + return Err(String::from("Authentication needs a username.")); + } + + Ok((target, auth)) +} + +#[cfg(test)] +mod tests { + use super::*; + + fn none() -> AuthenticationOptions { + AuthenticationOptions::default() + } + + fn basic(username: &str, password: Option<&str>) -> AuthenticationOptions { + AuthenticationOptions { + kind: AuthenticationKind::BASIC, + username: Some(username.into()), + password: password.map(String::from), + database: None, + } + } + + fn uri(value: &str) -> ConnectionTarget { + ConnectionTarget::Uri { uri: value.into() } + } + + #[test] + fn trims_a_host() { + let (target, _) = normalize("Local", ConnectionTarget::Host { host: " localhost ".into(), port: 27017 }, none()).unwrap(); + assert_eq!(target, ConnectionTarget::Host { host: "localhost".into(), port: 27017 }); + } + + #[test] + fn refuses_a_missing_name_host_or_port() { + assert!(normalize(" ", ConnectionTarget::Host { host: "h".into(), port: 1 }, none()).is_err()); + assert!(normalize("n", ConnectionTarget::Host { host: " ".into(), port: 1 }, none()).is_err()); + assert!(normalize("n", ConnectionTarget::Host { host: "h".into(), port: 0 }, none()).is_err()); + } + + #[test] + fn refuses_a_connection_string_in_the_host_field() { + let err = normalize("n", ConnectionTarget::Host { host: "mongodb+srv://c.example.net".into(), port: 27017 }, none()).unwrap_err(); + assert!(err.contains("connection string")); + } + + #[test] + fn refuses_a_port_after_the_host() { + let err = normalize("n", ConnectionTarget::Host { host: "db.local:27018".into(), port: 27017 }, none()).unwrap_err(); + assert!(err.contains("port field")); + } + + #[test] + fn splits_host_and_port_but_leaves_ipv6_and_names_alone() { + assert_eq!(split_host_port("db.local:27018"), Some(("db.local", 27018))); + assert_eq!(split_host_port("db.local"), None); + assert_eq!(split_host_port("::1"), None); + assert_eq!(split_host_port("fe80::1:27017"), None); + assert_eq!(split_host_port(":27017"), None); + assert_eq!(split_host_port("h:port"), None); + } + + #[test] + fn refuses_basic_authentication_without_username() { + assert!(normalize("n", ConnectionTarget::Host { host: "h".into(), port: 1 }, basic(" ", Some("pw"))).is_err()); + } + + #[test] + fn moves_credentials_out_of_a_connection_string() { + let (target, auth) = normalize("n", uri("mongodb://ada:pw@h1,h2/?replicaSet=rs"), none()).unwrap(); + assert_eq!(target, uri("mongodb://h1,h2/?replicaSet=rs")); + assert_eq!(auth.kind, AuthenticationKind::BASIC); + assert_eq!(auth.username.as_deref(), Some("ada")); + assert_eq!(auth.password.as_deref(), Some("pw")); + } + + #[test] + fn typed_credentials_win_over_the_string() { + let (_, auth) = normalize("n", uri("mongodb://ada:pw@h"), basic("bob", Some("typed"))).unwrap(); + assert_eq!(auth.username.as_deref(), Some("bob")); + assert_eq!(auth.password.as_deref(), Some("typed")); + } + + #[test] + fn a_string_without_credentials_keeps_the_authentication() { + let (_, auth) = normalize("n", uri("mongodb://h"), none()).unwrap(); + assert_eq!(auth, none()); + } + + #[test] + fn refuses_a_string_with_another_scheme() { + assert!(normalize("n", uri("http://h"), none()).is_err()); + } +} diff --git a/src-tauri/src/connector/models/options.rs b/src-tauri/src/connector/models/options.rs new file mode 100644 index 0000000..ec0c357 --- /dev/null +++ b/src-tauri/src/connector/models/options.rs @@ -0,0 +1,13 @@ +use serde::{Deserialize, Serialize}; + +/// Settings for a connection by host and port. A connection string carries its own. +#[derive(Serialize, Deserialize, Debug, Clone, Default, PartialEq)] +#[serde(rename_all = "camelCase", default)] +pub struct ConnectionOptions { + pub tls: bool, + pub tls_ca_file: Option, + pub tls_allow_invalid_certificates: bool, + pub replica_set: Option, + /// `None` means the driver's default (30 s) + pub server_selection_timeout_secs: Option, +} diff --git a/src-tauri/src/connector/models/uri.rs b/src-tauri/src/connector/models/uri.rs new file mode 100644 index 0000000..0e372e3 --- /dev/null +++ b/src-tauri/src/connector/models/uri.rs @@ -0,0 +1,137 @@ +use percent_encoding::percent_decode_str; + +const SCHEMES: [&str; 2] = ["mongodb://", "mongodb+srv://"]; + +/// A connection string split into the string without user info and the credentials it carried. +#[derive(Debug, PartialEq)] +pub struct UriCredentials { + pub uri: String, + pub username: Option, + pub password: Option, +} + +pub fn is_connection_string(value: &str) -> bool { + SCHEMES.iter().any(|scheme| value.starts_with(scheme)) +} + +/// Takes `user:password@` out of a connection string and percent-decodes both. +/// The hosts, the path and the options stay as they are. +pub fn split_uri_credentials(value: &str) -> Result { + let scheme = SCHEMES + .iter() + .find(|scheme| value.starts_with(*scheme)) + .ok_or_else(|| String::from("A connection string starts with mongodb:// or mongodb+srv://"))?; + + let rest = &value[scheme.len()..]; + let authority_end = rest.find(['/', '?']).unwrap_or(rest.len()); + let authority = &rest[..authority_end]; + + // the last @ ends the user info, an @ in a password must be percent-encoded anyway + let (user_info, hosts) = match authority.rfind('@') { + Some(at) => (Some(&authority[..at]), &authority[at + 1..]), + None => (None, authority), + }; + + if hosts.is_empty() { + return Err(String::from("The connection string names no host")); + } + + let decode = |part: &str| { + percent_decode_str(part) + .decode_utf8() + .map(|decoded| decoded.into_owned()) + .map_err(|_| String::from("The credentials in the connection string are not valid UTF-8")) + }; + + let (username, password) = match user_info { + None => (None, None), + Some(info) => match info.split_once(':') { + Some((user, pass)) => (Some(decode(user)?), Some(decode(pass)?)), + None => (Some(decode(info)?), None), + }, + }; + + Ok(UriCredentials { + uri: format!("{scheme}{hosts}{}", &rest[authority_end..]), + username: username.filter(|u| !u.is_empty()), + password, + }) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn recognises_both_schemes() { + assert!(is_connection_string("mongodb://localhost")); + assert!(is_connection_string("mongodb+srv://cluster0.example.net")); + assert!(!is_connection_string("localhost")); + assert!(!is_connection_string("http://localhost")); + assert!(!is_connection_string("")); + } + + #[test] + fn leaves_a_string_without_user_info_alone() { + let split = split_uri_credentials("mongodb://localhost:27017/?replicaSet=rs0").unwrap(); + assert_eq!( + split, + UriCredentials { uri: "mongodb://localhost:27017/?replicaSet=rs0".into(), username: None, password: None } + ); + } + + #[test] + fn takes_user_and_password_out() { + let split = split_uri_credentials("mongodb+srv://ada:s3cr3t@cluster0.example.net/shop?retryWrites=true").unwrap(); + assert_eq!(split.uri, "mongodb+srv://cluster0.example.net/shop?retryWrites=true"); + assert_eq!(split.username.as_deref(), Some("ada")); + assert_eq!(split.password.as_deref(), Some("s3cr3t")); + } + + #[test] + fn decodes_percent_encoded_credentials() { + let split = split_uri_credentials("mongodb://us%40er:p%3Ass%40w%2Frd@db1:27017,db2:27018/?authSource=admin").unwrap(); + assert_eq!(split.uri, "mongodb://db1:27017,db2:27018/?authSource=admin"); + assert_eq!(split.username.as_deref(), Some("us@er")); + assert_eq!(split.password.as_deref(), Some("p:ss@w/rd")); + } + + #[test] + fn takes_a_user_without_password_out() { + let split = split_uri_credentials("mongodb://ada@localhost").unwrap(); + assert_eq!(split.uri, "mongodb://localhost"); + assert_eq!(split.username.as_deref(), Some("ada")); + assert_eq!(split.password, None); + } + + #[test] + fn keeps_an_empty_password() { + let split = split_uri_credentials("mongodb://ada:@localhost").unwrap(); + assert_eq!(split.password.as_deref(), Some("")); + } + + #[test] + fn handles_a_string_that_ends_after_the_host() { + let split = split_uri_credentials("mongodb://u:p@localhost").unwrap(); + assert_eq!(split.uri, "mongodb://localhost"); + } + + #[test] + fn an_at_in_the_options_is_not_user_info() { + let split = split_uri_credentials("mongodb://localhost/?appName=a@b").unwrap(); + assert_eq!(split.uri, "mongodb://localhost/?appName=a@b"); + assert_eq!(split.username, None); + } + + #[test] + fn refuses_other_schemes_and_missing_hosts() { + assert!(split_uri_credentials("localhost:27017").is_err()); + assert!(split_uri_credentials("mongodb://").is_err()); + assert!(split_uri_credentials("mongodb://user:pw@/db").is_err()); + } + + #[test] + fn refuses_credentials_that_are_not_utf8() { + assert!(split_uri_credentials("mongodb://%FF:pw@localhost").is_err()); + } +} diff --git a/src-tauri/src/connector/repositories/connector_repository.rs b/src-tauri/src/connector/repositories/connector_repository.rs index 32df29e..2fd0e9d 100644 --- a/src-tauri/src/connector/repositories/connector_repository.rs +++ b/src-tauri/src/connector/repositories/connector_repository.rs @@ -1,15 +1,11 @@ -use std::any::Any; - use async_trait::async_trait; use crate::connector::commands::error::RepositoryError; use crate::connector::models::create_connection::CreateConnection; use crate::connector::models::connection::Connection; -use crate::connector::repositories::storage_wrapper::ConnectionStorage; #[async_trait] pub trait ConnectorRepository: Send + Sync { - fn as_any(&self) -> &dyn Any; async fn list(&self) -> Result, RepositoryError>; async fn get(&self, id: &str) -> Result; async fn save( @@ -22,6 +18,8 @@ pub trait ConnectorRepository: Send + Sync { connection: &Connection, ) -> Result<(), RepositoryError>; async fn delete(&self, id: &str) -> Result<(), RepositoryError>; - async fn load_storage(&self) -> Result; - async fn write_storage(&self, storage: &ConnectionStorage) -> Result<(), RepositoryError>; + /// The stored password, `None` when there is none. + async fn get_password(&self, id: &str) -> Result, RepositoryError>; + /// The connection with its password filled in when it uses authentication. + async fn hydrate(&self, connection: &Connection) -> Result; } diff --git a/src-tauri/src/connector/repositories/credentials/credential_service.rs b/src-tauri/src/connector/repositories/credentials/credential_service.rs index fbfce93..4429251 100644 --- a/src-tauri/src/connector/repositories/credentials/credential_service.rs +++ b/src-tauri/src/connector/repositories/credentials/credential_service.rs @@ -1,7 +1,10 @@ +use async_trait::async_trait; use keyring::Entry; use crate::connector::commands::error::CredentialError; +use crate::connector::repositories::credentials::credential_store::CredentialStore; +/// Passwords in the OS keyring: Keychain, Credential Manager or Secret Service. pub struct CredentialService { service_name: String, } @@ -13,7 +16,21 @@ impl CredentialService { } } - pub async fn set_password( + fn get_key(&self, connection_id: &str) -> String { + format!("connection:{}:basic_password", connection_id) + } + + fn not_found(connection_id: &str, err: keyring::Error) -> CredentialError { + match err { + keyring::Error::NoEntry => CredentialError::NotFound(connection_id.to_string()), + other => CredentialError::Keyring(other), + } + } +} + +#[async_trait] +impl CredentialStore for CredentialService { + async fn set_password( &self, connection_id: &str, password: &str, @@ -22,6 +39,7 @@ impl CredentialService { let key = self.get_key(connection_id); let pass = password.to_string(); + // keyring calls block on the platform store tokio::task::spawn_blocking(move || { let entry = Entry::new(&service, &key)?; entry.set_password(&pass)?; @@ -32,39 +50,21 @@ impl CredentialService { Ok(()) } - pub async fn get_password( - &self, - connection_id: &str, - ) -> Result { + async fn get_password(&self, connection_id: &str) -> Result { let service = self.service_name.clone(); let key = self.get_key(connection_id); - let password = tokio::task::spawn_blocking(move || { - let entry = Entry::new(&service, &key)?; - entry.get_password() - }) - .await??; - - Ok(password) + tokio::task::spawn_blocking(move || Entry::new(&service, &key)?.get_password()) + .await? + .map_err(|e| Self::not_found(connection_id, e)) } - pub async fn delete_password( - &self, - connection_id: &str, - ) -> Result<(), CredentialError> { + async fn delete_password(&self, connection_id: &str) -> Result<(), CredentialError> { let service = self.service_name.clone(); let key = self.get_key(connection_id); - tokio::task::spawn_blocking(move || { - let entry = Entry::new(&service, &key)?; - entry.delete_credential() - }) - .await??; - - Ok(()) - } - - fn get_key(&self, connection_id: &str) -> String { - format!("connection:{}:basic_password", connection_id) + tokio::task::spawn_blocking(move || Entry::new(&service, &key)?.delete_credential()) + .await? + .map_err(|e| Self::not_found(connection_id, e)) } } diff --git a/src-tauri/src/connector/repositories/credentials/credential_store.rs b/src-tauri/src/connector/repositories/credentials/credential_store.rs new file mode 100644 index 0000000..9020a74 --- /dev/null +++ b/src-tauri/src/connector/repositories/credentials/credential_store.rs @@ -0,0 +1,86 @@ +use async_trait::async_trait; + +use crate::connector::commands::error::CredentialError; + +/// Where connection passwords live: the OS keyring in the app, memory in tests. +#[async_trait] +pub trait CredentialStore: Send + Sync { + async fn set_password(&self, connection_id: &str, password: &str) + -> Result<(), CredentialError>; + /// `CredentialError::NotFound` when nothing is stored for the connection. + async fn get_password(&self, connection_id: &str) -> Result; + async fn delete_password(&self, connection_id: &str) -> Result<(), CredentialError>; +} + +#[cfg(test)] +pub mod memory { + use std::collections::HashMap; + use std::sync::Mutex; + + use async_trait::async_trait; + + use super::CredentialStore; + use crate::connector::commands::error::CredentialError; + + #[derive(Default)] + pub struct MemoryCredentialStore { + passwords: Mutex>, + fail: Mutex, + } + + impl MemoryCredentialStore { + pub fn stored(&self, connection_id: &str) -> Option { + self.passwords.lock().unwrap().get(connection_id).cloned() + } + + pub fn insert(&self, connection_id: &str, password: &str) { + self.passwords + .lock() + .unwrap() + .insert(connection_id.to_string(), password.to_string()); + } + + /// Every following call fails like a locked or missing keyring would. + pub fn fail_from_now_on(&self) { + *self.fail.lock().unwrap() = true; + } + + fn check(&self) -> Result<(), CredentialError> { + if *self.fail.lock().unwrap() { + return Err(CredentialError::Keyring(keyring::Error::NoStorageAccess( + "test store fails".into(), + ))); + } + Ok(()) + } + } + + #[async_trait] + impl CredentialStore for MemoryCredentialStore { + async fn set_password( + &self, + connection_id: &str, + password: &str, + ) -> Result<(), CredentialError> { + self.check()?; + self.insert(connection_id, password); + Ok(()) + } + + async fn get_password(&self, connection_id: &str) -> Result { + self.check()?; + self.stored(connection_id) + .ok_or_else(|| CredentialError::NotFound(connection_id.to_string())) + } + + async fn delete_password(&self, connection_id: &str) -> Result<(), CredentialError> { + self.check()?; + self.passwords + .lock() + .unwrap() + .remove(connection_id) + .map(|_| ()) + .ok_or_else(|| CredentialError::NotFound(connection_id.to_string())) + } + } +} diff --git a/src-tauri/src/connector/repositories/credentials/hydrate_connection_with_credentials.rs b/src-tauri/src/connector/repositories/credentials/hydrate_connection_with_credentials.rs deleted file mode 100644 index 8aebc75..0000000 --- a/src-tauri/src/connector/repositories/credentials/hydrate_connection_with_credentials.rs +++ /dev/null @@ -1,17 +0,0 @@ -use crate::{connector::{models::{authentication::AuthenticationKind, connection::Connection}, repositories::file_repository::FileRepository}, drivers::errors::DriverError}; - -pub async fn hydrate_connection_with_credentials( - repository: &FileRepository, - connection: &Connection, -) -> Result { - let mut c = connection.clone(); - if let AuthenticationKind::BASIC = c.authentication.kind { - let password = repository - .credential_service - .get_password(&c.id) - .await - .map_err(|_| DriverError::ConnectionFailed("Could not fetch credentials".into()))?; - c.authentication.password = Some(password); - } - Ok(c) -} \ No newline at end of file diff --git a/src-tauri/src/connector/repositories/credentials/mod.rs b/src-tauri/src/connector/repositories/credentials/mod.rs index 45a0f03..47feb60 100644 --- a/src-tauri/src/connector/repositories/credentials/mod.rs +++ b/src-tauri/src/connector/repositories/credentials/mod.rs @@ -1,2 +1,2 @@ pub mod credential_service; -pub mod hydrate_connection_with_credentials; \ No newline at end of file +pub mod credential_store; diff --git a/src-tauri/src/connector/repositories/file_repository.rs b/src-tauri/src/connector/repositories/file_repository.rs index 1edd23f..a5494e0 100644 --- a/src-tauri/src/connector/repositories/file_repository.rs +++ b/src-tauri/src/connector/repositories/file_repository.rs @@ -1,37 +1,91 @@ -use std::any::Any; use std::path::PathBuf; +use std::sync::Arc; use async_trait::async_trait; use tokio::fs; -use crate::connector::commands::error::RepositoryError; +use crate::connector::commands::error::{CredentialError, RepositoryError}; use crate::connector::models::authentication::AuthenticationKind; use crate::connector::models::connection::Connection; use crate::connector::models::create_connection::CreateConnection; +use crate::connector::models::normalize::normalize; use crate::connector::repositories::connector_repository::ConnectorRepository; -use crate::connector::repositories::credentials::credential_service::CredentialService; +use crate::connector::repositories::credentials::credential_store::CredentialStore; +use crate::connector::repositories::migrations::migrate_v1; use crate::connector::repositories::storage_wrapper::{ConnectionStorage, STORAGE_VERSION}; pub struct FileRepository { base_path: PathBuf, - pub credential_service: CredentialService, + credentials: Arc, } impl FileRepository { - pub fn new(base_path: PathBuf, credential_service: crate::connector::repositories::credentials::credential_service::CredentialService) -> Self { + pub fn new(base_path: PathBuf, credentials: Arc) -> Self { FileRepository { base_path: base_path.join("connections.json"), - credential_service, + credentials, } } + + async fn load_storage(&self) -> Result { + if !fs::try_exists(&self.base_path).await? { + return Ok(ConnectionStorage::new()); + } + + let content = fs::read_to_string(&self.base_path).await?; + + if content.trim().is_empty() { + return Ok(ConnectionStorage::new()); + } + + let value: serde_json::Value = serde_json::from_str(&content)?; + let version = value.get("version").and_then(serde_json::Value::as_u64); + + match version { + Some(v) if v == u64::from(STORAGE_VERSION) => Ok(serde_json::from_value(value)?), + Some(1) => self.migrate_from_v1(&content, &value).await, + other => Err(RepositoryError::MigrationError { + from: other.and_then(|v| u32::try_from(v).ok()).unwrap_or(0), + to: STORAGE_VERSION, + }), + } + } + + async fn migrate_from_v1( + &self, + content: &str, + value: &serde_json::Value, + ) -> Result { + let (storage, moved) = migrate_v1(value)?; + + for password in moved { + self.credentials + .set_password(&password.connection_id, &password.password) + .await?; + } + + // the old file stays next to the new one, in case the migration loses something + fs::write(self.base_path.with_file_name("connections.v1.json"), content).await?; + self.write_storage(&storage).await?; + log::info!("Migrated connections.json from version 1 to {}", STORAGE_VERSION); + + Ok(storage) + } + + async fn write_storage(&self, storage: &ConnectionStorage) -> Result<(), RepositoryError> { + if let Some(parent) = self.base_path.parent() { + fs::create_dir_all(parent).await?; + } + + let json = serde_json::to_string_pretty(storage)?; + fs::write(&self.base_path, json).await?; + + Ok(()) + } } #[async_trait] impl ConnectorRepository for FileRepository { - fn as_any(&self) -> &dyn Any { - self - } - async fn list(&self) -> Result, RepositoryError> { let storage = self.load_storage().await?; Ok(storage.connections.values().cloned().collect()) @@ -43,14 +97,19 @@ impl ConnectorRepository for FileRepository { ) -> Result { let mut storage = self.load_storage().await?; + let (target, authentication) = + normalize(&connection.name, connection.target, connection.authentication) + .map_err(RepositoryError::Invalid)?; + connection.target = target; + connection.authentication = authentication; + let password = connection.authentication.password.take(); let new_connection = Connection::from(connection); if let Some(pass) = password { - self.credential_service + self.credentials .set_password(&new_connection.id, &pass) - .await - .map_err(|e| RepositoryError::Credential(e))?; + .await?; } storage @@ -85,21 +144,30 @@ impl ConnectorRepository for FileRepository { .get_mut(id) .ok_or_else(|| RepositoryError::NotFound(id.to_string()))?; + let (target, authentication) = normalize( + &connection.name, + connection.target.clone(), + connection.authentication.clone(), + ) + .map_err(RepositoryError::Invalid)?; + let mut connection = connection.clone(); + connection.target = target; + connection.authentication = authentication; + match connection.authentication.kind { AuthenticationKind::BASIC => { + // without a new password the stored one stays if let Some(password) = &connection.authentication.password { - self.credential_service - .set_password(id, password) - .await - .map_err(|e| RepositoryError::Credential(e))?; + self.credentials.set_password(id, password).await?; } } AuthenticationKind::NONE => { - let _ = self.credential_service.delete_password(id).await; + let _ = self.credentials.delete_password(id).await; } } - let mut clean = connection.clone(); + let mut clean = connection; + clean.id = id.to_string(); clean.authentication.password = None; *existing = clean; @@ -116,44 +184,369 @@ impl ConnectorRepository for FileRepository { return Err(RepositoryError::NotFound(id.to_string())); } - let _ = self.credential_service.delete_password(id).await; + let _ = self.credentials.delete_password(id).await; self.write_storage(&storage).await?; Ok(()) } - async fn load_storage(&self) -> Result { - if !fs::try_exists(&self.base_path).await? { - return Ok(ConnectionStorage::new()); + async fn get_password(&self, id: &str) -> Result, RepositoryError> { + match self.credentials.get_password(id).await { + Ok(password) => Ok(Some(password)), + Err(CredentialError::NotFound(_)) => Ok(None), + Err(e) => Err(e.into()), } + } - let content = fs::read_to_string(&self.base_path).await?; + async fn hydrate(&self, connection: &Connection) -> Result { + let mut hydrated = connection.clone(); + if let AuthenticationKind::BASIC = hydrated.authentication.kind { + hydrated.authentication.password = + Some(self.credentials.get_password(&hydrated.id).await?); + } + Ok(hydrated) + } +} - if content.trim().is_empty() { - return Ok(ConnectionStorage::new()); +#[cfg(test)] +mod tests { + use super::*; + use crate::connector::models::authentication::AuthenticationOptions; + use crate::connector::models::connection::{ConnectionTarget, ConnectorKind}; + use crate::connector::models::options::ConnectionOptions; + use crate::connector::repositories::credentials::credential_store::memory::MemoryCredentialStore; + + /// A directory of its own per test, removed again when the test ends. + struct TempDir(PathBuf); + + impl TempDir { + fn new() -> Self { + let dir = std::env::temp_dir().join(format!("monolens-test-{}", uuid::Uuid::new_v4())); + std::fs::create_dir_all(&dir).unwrap(); + TempDir(dir) } - let storage: ConnectionStorage = serde_json::from_str(&content)?; + fn file(&self) -> PathBuf { + self.0.join("connections.json") + } + } - if storage.version != STORAGE_VERSION { - return Err(RepositoryError::MigrationError { - from: storage.version, - to: STORAGE_VERSION, - }); + impl Drop for TempDir { + fn drop(&mut self) { + let _ = std::fs::remove_dir_all(&self.0); } + } - Ok(storage) + fn setup() -> (TempDir, Arc, FileRepository) { + let dir = TempDir::new(); + let store = Arc::new(MemoryCredentialStore::default()); + let repository = FileRepository::new(dir.0.clone(), store.clone()); + (dir, store, repository) } - async fn write_storage(&self, storage: &ConnectionStorage) -> Result<(), RepositoryError> { - if let Some(parent) = self.base_path.parent() { - fs::create_dir_all(parent).await?; + fn create(password: Option<&str>) -> CreateConnection { + CreateConnection { + name: "Local".into(), + connector: ConnectorKind::MongoDb, + target: ConnectionTarget::Host { host: "localhost".into(), port: 27017 }, + options: ConnectionOptions::default(), + authentication: AuthenticationOptions { + kind: if password.is_some() { + AuthenticationKind::BASIC + } else { + AuthenticationKind::NONE + }, + username: password.map(|_| "root".to_string()), + password: password.map(String::from), + database: Some("admin".into()), + }, } + } - let json = serde_json::to_string_pretty(storage)?; - fs::write(&self.base_path, json).await?; + #[tokio::test] + async fn lists_nothing_without_a_file() { + let (_dir, _store, repository) = setup(); + assert!(repository.list().await.unwrap().is_empty()); + } - Ok(()) + #[tokio::test] + async fn lists_nothing_for_an_empty_file() { + let (dir, _store, repository) = setup(); + std::fs::write(dir.file(), " \n").unwrap(); + assert!(repository.list().await.unwrap().is_empty()); + } + + #[tokio::test] + async fn save_keeps_the_password_out_of_the_file() { + let (dir, store, repository) = setup(); + + let saved = repository.save(create(Some("s3cr3t"))).await.unwrap(); + + assert!(!saved.id.is_empty()); + assert_eq!(saved.authentication.password, None); + assert_eq!(store.stored(&saved.id).as_deref(), Some("s3cr3t")); + let file = std::fs::read_to_string(dir.file()).unwrap(); + assert!(!file.contains("s3cr3t")); + assert!(file.contains("\"version\": 2")); + assert_eq!(repository.get(&saved.id).await.unwrap().name, "Local"); + } + + #[tokio::test] + async fn save_without_authentication_stores_no_password() { + let (_dir, store, repository) = setup(); + let saved = repository.save(create(None)).await.unwrap(); + assert_eq!(store.stored(&saved.id), None); + } + + #[tokio::test] + async fn save_fails_when_the_keyring_fails_and_writes_nothing() { + let (dir, store, repository) = setup(); + store.fail_from_now_on(); + + let result = repository.save(create(Some("pw"))).await; + + assert!(matches!(result, Err(RepositoryError::Credential(_)))); + assert!(!dir.file().exists()); + } + + #[tokio::test] + async fn get_of_an_unknown_id_is_not_found() { + let (_dir, _store, repository) = setup(); + assert!(matches!( + repository.get("nope").await, + Err(RepositoryError::NotFound(id)) if id == "nope" + )); + } + + #[tokio::test] + async fn update_with_a_password_replaces_it() { + let (dir, store, repository) = setup(); + let saved = repository.save(create(Some("old"))).await.unwrap(); + + let mut changed = saved.clone(); + changed.name = "Renamed".into(); + changed.authentication.password = Some("new".into()); + repository.update(&saved.id, &changed).await.unwrap(); + + assert_eq!(store.stored(&saved.id).as_deref(), Some("new")); + assert_eq!(repository.get(&saved.id).await.unwrap().name, "Renamed"); + assert!(!std::fs::read_to_string(dir.file()).unwrap().contains("new\"")); + } + + #[tokio::test] + async fn update_without_a_password_keeps_the_stored_one() { + let (_dir, store, repository) = setup(); + let saved = repository.save(create(Some("old"))).await.unwrap(); + + repository.update(&saved.id, &saved).await.unwrap(); + + assert_eq!(store.stored(&saved.id).as_deref(), Some("old")); + } + + #[tokio::test] + async fn update_to_no_authentication_deletes_the_password() { + let (_dir, store, repository) = setup(); + let saved = repository.save(create(Some("old"))).await.unwrap(); + + let mut changed = saved.clone(); + changed.authentication.kind = AuthenticationKind::NONE; + repository.update(&saved.id, &changed).await.unwrap(); + + assert_eq!(store.stored(&saved.id), None); + } + + #[tokio::test] + async fn update_keeps_the_id_of_the_path() { + let (_dir, _store, repository) = setup(); + let saved = repository.save(create(None)).await.unwrap(); + + let mut changed = saved.clone(); + changed.id = "something-else".into(); + repository.update(&saved.id, &changed).await.unwrap(); + + assert_eq!(repository.get(&saved.id).await.unwrap().id, saved.id); + } + + #[tokio::test] + async fn update_of_an_unknown_id_is_not_found() { + let (_dir, _store, repository) = setup(); + let saved = repository.save(create(None)).await.unwrap(); + assert!(matches!( + repository.update("nope", &saved).await, + Err(RepositoryError::NotFound(_)) + )); + } + + #[tokio::test] + async fn delete_removes_connection_and_password() { + let (_dir, store, repository) = setup(); + let saved = repository.save(create(Some("pw"))).await.unwrap(); + + repository.delete(&saved.id).await.unwrap(); + + assert!(repository.list().await.unwrap().is_empty()); + assert_eq!(store.stored(&saved.id), None); + } + + #[tokio::test] + async fn delete_works_without_a_stored_password() { + let (_dir, _store, repository) = setup(); + let saved = repository.save(create(None)).await.unwrap(); + repository.delete(&saved.id).await.unwrap(); + assert!(repository.list().await.unwrap().is_empty()); + } + + #[tokio::test] + async fn delete_of_an_unknown_id_is_not_found() { + let (_dir, _store, repository) = setup(); + assert!(matches!( + repository.delete("nope").await, + Err(RepositoryError::NotFound(_)) + )); + } + + #[tokio::test] + async fn get_password_is_none_when_nothing_is_stored() { + let (_dir, store, repository) = setup(); + let saved = repository.save(create(None)).await.unwrap(); + assert_eq!(repository.get_password(&saved.id).await.unwrap(), None); + + store.insert(&saved.id, "pw"); + assert_eq!(repository.get_password(&saved.id).await.unwrap().as_deref(), Some("pw")); + } + + #[tokio::test] + async fn get_password_reports_a_failing_keyring() { + let (_dir, store, repository) = setup(); + store.fail_from_now_on(); + assert!(matches!( + repository.get_password("x").await, + Err(RepositoryError::Credential(CredentialError::Keyring(_))) + )); + } + + #[tokio::test] + async fn hydrate_fills_the_password_for_basic_authentication() { + let (_dir, _store, repository) = setup(); + let saved = repository.save(create(Some("pw"))).await.unwrap(); + + let hydrated = repository.hydrate(&saved).await.unwrap(); + + assert_eq!(hydrated.authentication.password.as_deref(), Some("pw")); + } + + #[tokio::test] + async fn hydrate_leaves_connections_without_authentication_alone() { + let (_dir, store, repository) = setup(); + let saved = repository.save(create(None)).await.unwrap(); + store.fail_from_now_on(); + + let hydrated = repository.hydrate(&saved).await.unwrap(); + + assert_eq!(hydrated.authentication.password, None); + } + + #[tokio::test] + async fn hydrate_fails_when_the_password_is_missing() { + let (_dir, store, repository) = setup(); + let saved = repository.save(create(Some("pw"))).await.unwrap(); + store.fail_from_now_on(); + assert!(repository.hydrate(&saved).await.is_err()); + } + + #[tokio::test] + async fn a_file_of_another_version_is_refused() { + let (dir, _store, repository) = setup(); + std::fs::write(dir.file(), r#"{ "version": 99, "connections": {} }"#).unwrap(); + assert!(matches!( + repository.list().await, + Err(RepositoryError::MigrationError { from: 99, to: STORAGE_VERSION }) + )); + } + + #[tokio::test] + async fn a_corrupt_file_is_a_serialization_error() { + let (dir, _store, repository) = setup(); + std::fs::write(dir.file(), "{ not json").unwrap(); + assert!(matches!( + repository.list().await, + Err(RepositoryError::Serialization(_)) + )); + } + + #[tokio::test] + async fn save_moves_credentials_out_of_a_connection_string() { + let (dir, store, repository) = setup(); + let mut connection = create(None); + connection.target = ConnectionTarget::Uri { uri: "mongodb://ada:s3cr3t@h1,h2/?replicaSet=rs".into() }; + + let saved = repository.save(connection).await.unwrap(); + + assert_eq!(saved.target, ConnectionTarget::Uri { uri: "mongodb://h1,h2/?replicaSet=rs".into() }); + assert_eq!(saved.authentication.kind, AuthenticationKind::BASIC); + assert_eq!(saved.authentication.username.as_deref(), Some("ada")); + assert_eq!(store.stored(&saved.id).as_deref(), Some("s3cr3t")); + assert!(!std::fs::read_to_string(dir.file()).unwrap().contains("s3cr3t")); + } + + #[tokio::test] + async fn save_refuses_an_invalid_connection_with_its_reason() { + let (dir, _store, repository) = setup(); + let mut connection = create(None); + connection.target = ConnectionTarget::Host { host: "mongodb://h".into(), port: 27017 }; + + let result = repository.save(connection).await; + + assert!(matches!(result, Err(RepositoryError::Invalid(reason)) if reason.contains("connection string"))); + assert!(!dir.file().exists()); + } + + #[tokio::test] + async fn update_refuses_an_invalid_connection() { + let (_dir, _store, repository) = setup(); + let saved = repository.save(create(None)).await.unwrap(); + let mut changed = saved.clone(); + changed.name = " ".into(); + assert!(matches!(repository.update(&saved.id, &changed).await, Err(RepositoryError::Invalid(_)))); + } + + #[tokio::test] + async fn a_version_1_file_is_migrated_written_back_and_kept_as_backup() { + let (dir, store, repository) = setup(); + let v1 = r#"{ "version": 1, "connections": { + "a": { "id": "a", "name": "Local", "uri": "localhost", "port": 27017, "connector": "MongoDb", + "connectionType": "Standalone", + "authentication": { "kind": "BASIC", "username": "root", "password": null, "database": "admin" } }, + "b": { "id": "b", "name": "Atlas", "uri": "mongodb+srv://ada:pw@c.example.net", "port": 27017, + "connector": "MongoDb", "connectionType": "DnsSeedList", + "authentication": { "kind": "NONE", "username": null, "password": null, "database": null } } } }"#; + std::fs::write(dir.file(), v1).unwrap(); + store.insert("a", "kept"); + + let mut connections = repository.list().await.unwrap(); + connections.sort_by(|x, y| x.id.cmp(&y.id)); + + assert_eq!(connections[0].target, ConnectionTarget::Host { host: "localhost".into(), port: 27017 }); + assert_eq!(connections[1].target, ConnectionTarget::Uri { uri: "mongodb+srv://c.example.net".into() }); + assert_eq!(store.stored("a").as_deref(), Some("kept")); + assert_eq!(store.stored("b").as_deref(), Some("pw")); + let file = std::fs::read_to_string(dir.file()).unwrap(); + assert!(file.contains("\"version\": 2")); + assert!(!file.contains("connectionType")); + assert_eq!(std::fs::read_to_string(dir.0.join("connections.v1.json")).unwrap(), v1); + + // the second load reads version 2 and doesn't migrate again + assert_eq!(repository.list().await.unwrap().len(), 2); + } + + #[tokio::test] + async fn a_file_without_version_is_refused() { + let (dir, _store, repository) = setup(); + std::fs::write(dir.file(), r#"{ "connections": {} }"#).unwrap(); + assert!(matches!( + repository.list().await, + Err(RepositoryError::MigrationError { from: 0, .. }) + )); } } diff --git a/src-tauri/src/connector/repositories/migrations.rs b/src-tauri/src/connector/repositories/migrations.rs new file mode 100644 index 0000000..c834565 --- /dev/null +++ b/src-tauri/src/connector/repositories/migrations.rs @@ -0,0 +1,169 @@ +use serde_json::Value; + +use crate::connector::commands::error::RepositoryError; +use crate::connector::models::{ + authentication::{AuthenticationKind, AuthenticationOptions}, + connection::{Connection, ConnectionTarget, ConnectorKind}, + options::ConnectionOptions, + normalize::split_host_port, + uri::{is_connection_string, split_uri_credentials}, +}; +use crate::connector::repositories::storage_wrapper::{ConnectionStorage, STORAGE_VERSION}; + +/// A password found in a version 1 connection string; it moves to the credential store. +#[derive(Debug, PartialEq)] +pub struct MovedPassword { + pub connection_id: String, + pub password: String, +} + +/// Version 1 kept the host in `uri` next to `port`, and a `connectionType` nothing read. +/// A `uri` that is a connection string becomes a `Uri` target without its credentials. +pub fn migrate_v1(value: &Value) -> Result<(ConnectionStorage, Vec), RepositoryError> { + let invalid = || RepositoryError::MigrationError { from: 1, to: STORAGE_VERSION }; + + let connections = value + .get("connections") + .and_then(Value::as_object) + .ok_or_else(invalid)?; + + let mut storage = ConnectionStorage::new(); + let mut moved = Vec::new(); + + for (key, old) in connections { + let id = old.get("id").and_then(Value::as_str).unwrap_or(key).to_string(); + let name = old.get("name").and_then(Value::as_str).ok_or_else(invalid)?.to_string(); + let uri = old.get("uri").and_then(Value::as_str).ok_or_else(invalid)?.trim(); + let port = old + .get("port") + .and_then(Value::as_u64) + .and_then(|port| u16::try_from(port).ok()) + .unwrap_or(27017); + + let mut authentication: AuthenticationOptions = match old.get("authentication") { + Some(auth) => serde_json::from_value(auth.clone()).map_err(|_| invalid())?, + None => AuthenticationOptions::default(), + }; + authentication.password = None; + + let target = if is_connection_string(uri) { + let split = split_uri_credentials(uri).map_err(|_| invalid())?; + if split.username.is_some() || split.password.is_some() { + authentication.kind = AuthenticationKind::BASIC; + } + if authentication.username.as_deref().unwrap_or("").is_empty() { + authentication.username = split.username; + } + if let Some(password) = split.password { + moved.push(MovedPassword { connection_id: id.clone(), password }); + } + ConnectionTarget::Uri { uri: split.uri } + } else { + // version 1 accepted "host:port" in the host field; the port field won + let host = split_host_port(uri).map_or(uri, |(name, _)| name); + ConnectionTarget::Host { host: host.to_string(), port } + }; + + storage.connections.insert( + id.clone(), + Connection { + id, + name, + connector: ConnectorKind::MongoDb, + target, + authentication, + options: ConnectionOptions::default(), + }, + ); + } + + Ok((storage, moved)) +} + +#[cfg(test)] +mod tests { + use super::*; + use serde_json::json; + + fn v1(connections: Value) -> Value { + json!({ "version": 1, "connections": connections }) + } + + #[test] + fn turns_host_and_port_into_a_host_target() { + let (storage, moved) = migrate_v1(&v1(json!({ + "a": { + "id": "a", "name": "Local", "uri": "localhost", "port": 27018, + "connector": "MongoDb", "connectionType": "Standalone", + "authentication": { "kind": "BASIC", "username": "root", "password": null, "database": "admin" } + } + }))) + .unwrap(); + + let connection = &storage.connections["a"]; + assert_eq!(storage.version, STORAGE_VERSION); + assert_eq!(connection.name, "Local"); + assert_eq!(connection.target, ConnectionTarget::Host { host: "localhost".into(), port: 27018 }); + assert_eq!(connection.authentication.kind, AuthenticationKind::BASIC); + assert_eq!(connection.authentication.username.as_deref(), Some("root")); + assert_eq!(connection.authentication.database.as_deref(), Some("admin")); + assert_eq!(connection.options, ConnectionOptions::default()); + assert!(moved.is_empty()); + } + + #[test] + fn turns_a_connection_string_into_a_uri_target_and_moves_its_password() { + let (storage, moved) = migrate_v1(&v1(json!({ + "b": { + "id": "b", "name": "Atlas", "uri": "mongodb+srv://ada:pw@cluster0.example.net/?retryWrites=true", + "port": 27017, "connector": "MongoDb", "connectionType": "DnsSeedList", + "authentication": { "kind": "NONE", "username": null, "password": null, "database": null } + } + }))) + .unwrap(); + + let connection = &storage.connections["b"]; + assert_eq!(connection.target, ConnectionTarget::Uri { uri: "mongodb+srv://cluster0.example.net/?retryWrites=true".into() }); + assert_eq!(connection.authentication.kind, AuthenticationKind::BASIC); + assert_eq!(connection.authentication.username.as_deref(), Some("ada")); + assert_eq!(moved, vec![MovedPassword { connection_id: "b".into(), password: "pw".into() }]); + } + + #[test] + fn defaults_a_missing_port_and_authentication_and_takes_the_key_as_id() { + let (storage, _) = migrate_v1(&v1(json!({ "k": { "name": "N", "uri": "db.local" } }))).unwrap(); + let connection = &storage.connections["k"]; + assert_eq!(connection.id, "k"); + assert_eq!(connection.target, ConnectionTarget::Host { host: "db.local".into(), port: 27017 }); + assert_eq!(connection.authentication, AuthenticationOptions::default()); + } + + #[test] + fn drops_a_password_that_was_stored_in_the_file() { + let (storage, _) = migrate_v1(&v1(json!({ + "a": { "name": "N", "uri": "h", "authentication": { "kind": "BASIC", "username": "u", "password": "leaked", "database": null } } + }))) + .unwrap(); + assert_eq!(storage.connections["a"].authentication.password, None); + } + + #[test] + fn takes_a_port_out_of_the_host_and_keeps_the_port_field() { + let (storage, _) = migrate_v1(&v1(json!({ "a": { "name": "N", "uri": "db.local:27018", "port": 27019 } }))).unwrap(); + assert_eq!(storage.connections["a"].target, ConnectionTarget::Host { host: "db.local".into(), port: 27019 }); + } + + #[test] + fn migrates_an_empty_file() { + let (storage, moved) = migrate_v1(&v1(json!({}))).unwrap(); + assert!(storage.connections.is_empty()); + assert!(moved.is_empty()); + } + + #[test] + fn refuses_connections_without_name_or_uri() { + assert!(migrate_v1(&v1(json!({ "a": { "uri": "h" } }))).is_err()); + assert!(migrate_v1(&v1(json!({ "a": { "name": "N" } }))).is_err()); + assert!(migrate_v1(&json!({ "version": 1 })).is_err()); + } +} diff --git a/src-tauri/src/connector/repositories/mod.rs b/src-tauri/src/connector/repositories/mod.rs index fe2611c..3117c9f 100644 --- a/src-tauri/src/connector/repositories/mod.rs +++ b/src-tauri/src/connector/repositories/mod.rs @@ -1,4 +1,5 @@ pub mod connector_repository; pub mod file_repository; pub mod credentials; +pub mod migrations; pub mod storage_wrapper; \ No newline at end of file diff --git a/src-tauri/src/connector/repositories/storage_wrapper.rs b/src-tauri/src/connector/repositories/storage_wrapper.rs index 61e1731..b20891d 100644 --- a/src-tauri/src/connector/repositories/storage_wrapper.rs +++ b/src-tauri/src/connector/repositories/storage_wrapper.rs @@ -1,9 +1,10 @@ use std::collections::HashMap; use serde::{Serialize, Deserialize}; -use crate::connector::{ models::connection::Connection}; +use crate::connector::models::connection::Connection; -pub const STORAGE_VERSION: u32 = 1; +/// 1: `uri` held the host, plus `port` and `connectionType`. 2: `target` and `options`. +pub const STORAGE_VERSION: u32 = 2; #[derive(Serialize, Deserialize, Debug)] pub struct ConnectionStorage { @@ -19,3 +20,9 @@ impl ConnectionStorage { } } } + +impl Default for ConnectionStorage { + fn default() -> Self { + Self::new() + } +} diff --git a/src-tauri/src/database/commands/disconnect.rs b/src-tauri/src/database/commands/disconnect.rs new file mode 100644 index 0000000..7ebb942 --- /dev/null +++ b/src-tauri/src/database/commands/disconnect.rs @@ -0,0 +1,13 @@ +use tauri::State; + +use crate::connection_handler::connection_handler::ConnectionHandler; +use crate::drivers::errors::DriverError; + +/// Drops the connected driver; the next command builds it from the saved settings again. +#[tauri::command] +pub async fn disconnect( + connection_handler: State<'_, ConnectionHandler>, + connection_id: String, +) -> Result<(), DriverError> { + connection_handler.disconnect(&connection_id).await +} diff --git a/src-tauri/src/database/commands/get_databases.rs b/src-tauri/src/database/commands/get_databases.rs deleted file mode 100644 index 966e659..0000000 --- a/src-tauri/src/database/commands/get_databases.rs +++ /dev/null @@ -1,31 +0,0 @@ -use crate::AppState; -use crate::connection_handler::connection_handler::ConnectionHandler; -use crate::connector::repositories::credentials::hydrate_connection_with_credentials::hydrate_connection_with_credentials; -use crate::connector::repositories::file_repository::FileRepository; -use crate::connector::models::connection::Connection; -use crate::drivers::errors::DriverError; -use tauri::State; - -#[tauri::command] -pub async fn get_databases( - connection_handler: State<'_, ConnectionHandler>, - app_state: State<'_, AppState>, - connection: Connection, -) -> Result, DriverError> { - // get repository from AppState - let repository = &app_state.connection_repository; - - // hydrate_connection_with_credentials needs a concrete type - let hydrated_connection = if let Some(file_repo) = repository.as_any().downcast_ref::() { - hydrate_connection_with_credentials(file_repo, &connection).await? - } else { - return Err(DriverError::ConnectionFailed("Unsupported repository type".into())); - }; - - let driver_lock = connection_handler - .get_or_connect(hydrated_connection) - .await?; - - let driver = driver_lock.lock().await; - driver.list_databases().await -} diff --git a/src-tauri/src/database/commands/list_collections.rs b/src-tauri/src/database/commands/list_collections.rs index 7a1030d..d44197d 100644 --- a/src-tauri/src/database/commands/list_collections.rs +++ b/src-tauri/src/database/commands/list_collections.rs @@ -1,19 +1,19 @@ use tauri::State; use crate::connection_handler::connection_handler::ConnectionHandler; -use crate::connector::models::connection::Connection; use crate::drivers::errors::DriverError; +use crate::AppState; #[tauri::command] pub async fn list_collections( connection_handler: State<'_, ConnectionHandler>, - connection: Connection, + app_state: State<'_, AppState>, + connection_id: String, database_name: String, ) -> Result, DriverError> { - let driver_lock = connection_handler - .get_or_connect(connection) + let driver = connection_handler + .get_or_connect(app_state.connection_repository.as_ref(), &connection_id) .await?; - let driver = driver_lock.lock().await; driver.list_collections(&database_name).await } diff --git a/src-tauri/src/database/commands/mod.rs b/src-tauri/src/database/commands/mod.rs index 3c8e1d0..7669421 100644 --- a/src-tauri/src/database/commands/mod.rs +++ b/src-tauri/src/database/commands/mod.rs @@ -1,6 +1,5 @@ -pub mod get_databases; +pub mod disconnect; pub mod list_collections; pub mod start_connect; -pub mod start_test_connection; -pub mod test_connection; pub mod start_find_documents; +pub mod start_test_connection; diff --git a/src-tauri/src/database/commands/start_connect.rs b/src-tauri/src/database/commands/start_connect.rs index ee011a2..df3d94b 100644 --- a/src-tauri/src/database/commands/start_connect.rs +++ b/src-tauri/src/database/commands/start_connect.rs @@ -1,52 +1,22 @@ use crate::{ - AppState, - connection_handler::connection_handler::ConnectionHandler, - connector::{ - models::{connection::Connection}, - repositories::{ - credentials::hydrate_connection_with_credentials::hydrate_connection_with_credentials, - file_repository::FileRepository - } - }, - drivers::{driver::DatabaseDriver, errors::DriverError}, task_manager::task_manager::TaskManager + connection_handler::connection_handler::ConnectionHandler, drivers::errors::DriverError, + task_manager::task_manager::TaskManager, AppState, }; -use std::sync::Arc; use tauri::{Manager, State}; -use tokio::sync::Mutex; #[tauri::command] pub async fn start_connect( app_handle: tauri::AppHandle, task_manager: State<'_, TaskManager>, - connection: Connection, + connection_id: String, ) -> Result { let id = task_manager .spawn_task(async move { let app_state = app_handle.state::(); let connection_handler = app_handle.state::(); - let repository = &app_state.connection_repository; - - let hydrated_connection = - if let Some(file_repo) = repository.as_any().downcast_ref::() { - hydrate_connection_with_credentials(file_repo, &connection) - .await - .map_err(|e| serde_json::to_value(&e).unwrap_or_default())? - } else { - return Err(serde_json::to_value(&DriverError::ConnectionFailed( - "Unsupported repository type".into(), - )) - .unwrap_or_default()); - }; - - let driver_lock: Arc>> = connection_handler - .get_or_connect(hydrated_connection) - .await - .map_err(|e| serde_json::to_value(&e).unwrap_or_default())?; - - let driver = driver_lock.lock().await; - let databases: Vec = driver - .list_databases() + let databases = connection_handler + .open(app_state.connection_repository.as_ref(), &connection_id) .await .map_err(|e| serde_json::to_value(&e).unwrap_or_default())?; diff --git a/src-tauri/src/database/commands/start_find_documents.rs b/src-tauri/src/database/commands/start_find_documents.rs index 6861d06..ca79abd 100644 --- a/src-tauri/src/database/commands/start_find_documents.rs +++ b/src-tauri/src/database/commands/start_find_documents.rs @@ -1,57 +1,30 @@ use crate::{ AppState, connection_handler::connection_handler::ConnectionHandler, - connector::{ - models::{connection::Connection}, - repositories::{ - credentials::hydrate_connection_with_credentials::hydrate_connection_with_credentials, - file_repository::FileRepository - } - }, - drivers::{driver::DatabaseDriver, errors::DriverError}, task_manager::task_manager::TaskManager + database::models::find_document::FindQuery, + drivers::errors::DriverError, task_manager::task_manager::TaskManager }; -use std::sync::Arc; use tauri::{Manager, State}; -use tokio::sync::Mutex; #[tauri::command] pub async fn start_find_documents( app_handle: tauri::AppHandle, task_manager: State<'_, TaskManager>, - connection: Connection, - database_name: String, - collection_name: String, - filter: serde_json::Value, - skip: u64, - limit: i64 + connection_id: String, + query: FindQuery, ) -> Result { let id = task_manager .spawn_task(async move { let app_state = app_handle.state::(); let connection_handler = app_handle.state::(); - let repository = &app_state.connection_repository; - - let hydrated_connection = - if let Some(file_repo) = repository.as_any().downcast_ref::() { - hydrate_connection_with_credentials(file_repo, &connection) - .await - .map_err(|e| serde_json::to_value(&e).unwrap_or_default())? - } else { - return Err(serde_json::to_value(&DriverError::ConnectionFailed( - "Unsupported repository type".into(), - )) - .unwrap_or_default()); - }; - - let driver_lock: Arc>> = connection_handler - .get_or_connect(hydrated_connection) + let driver = connection_handler + .get_or_connect(app_state.connection_repository.as_ref(), &connection_id) .await .map_err(|e| serde_json::to_value(&e).unwrap_or_default())?; - let driver = driver_lock.lock().await; let documents = driver - .find_documents(&database_name, &collection_name, filter, skip, limit) + .find_documents(&query) .await .map_err(|e| serde_json::to_value(&e).unwrap_or_default())?; diff --git a/src-tauri/src/database/commands/start_test_connection.rs b/src-tauri/src/database/commands/start_test_connection.rs index 249edc4..db697f2 100644 --- a/src-tauri/src/database/commands/start_test_connection.rs +++ b/src-tauri/src/database/commands/start_test_connection.rs @@ -1,5 +1,5 @@ use crate::{ - connector::models::connection::Connection, + connector::models::{connection::Connection, normalize::normalize}, drivers::{driver::TestStage, errors::DriverError, factory::DatabaseDriverFactory}, task_manager::task_manager::TaskManager, }; @@ -9,20 +9,35 @@ use tauri::{Emitter, State}; #[derive(Debug, Serialize, Clone)] #[serde(rename_all = "camelCase")] pub struct TestProgressEvent { - pub task_id: String, + /// the id the ui chose for this test, so its listener ignores other tests + pub test_id: String, pub stage_index: usize, pub stage: TestStage, } +/// The form data checked like on save, so credentials in a connection string are used the same way. +fn prepared(connection: Connection) -> Result { + let (target, authentication) = normalize( + &connection.name, + connection.target.clone(), + connection.authentication.clone(), + ) + .map_err(DriverError::InvalidConnection)?; + Ok(Connection { target, authentication, ..connection }) +} + #[tauri::command] pub async fn start_test_connection( app_handle: tauri::AppHandle, task_manager: State<'_, TaskManager>, connection: Connection, + test_id: String, ) -> Result { let id = task_manager .spawn_task(async move { - let mut driver = DatabaseDriverFactory::create(connection) + let connection = + prepared(connection).map_err(|e| serde_json::to_value(&e).unwrap_or_default())?; + let mut driver = DatabaseDriverFactory::create_for_test(connection) .map_err(|e| serde_json::to_value(&e).unwrap_or_default())?; driver @@ -33,14 +48,12 @@ pub async fn start_test_connection( let result = driver .test_connection(Box::new({ let app = app_handle.clone(); + let test_id = test_id.clone(); move |index, stage| { - // We don't have the task_id inside here, but the frontend - // subscribes right before starting, so it captures all events. - // We'll use an empty task_id — the frontend ignores it. let _ = app.emit( "test-connection-progress", TestProgressEvent { - task_id: String::new(), + test_id: test_id.clone(), stage_index: index, stage: stage.clone(), }, @@ -62,3 +75,41 @@ pub async fn start_test_connection( Ok(id) } + +#[cfg(test)] +mod tests { + use super::*; + use crate::connector::models::{ + authentication::{AuthenticationKind, AuthenticationOptions}, + connection::{ConnectionTarget, ConnectorKind}, + options::ConnectionOptions, + }; + + fn form(target: ConnectionTarget) -> Connection { + Connection { + id: String::new(), + name: "test".into(), + connector: ConnectorKind::MongoDb, + target, + authentication: AuthenticationOptions::default(), + options: ConnectionOptions::default(), + } + } + + #[test] + fn credentials_in_a_connection_string_are_used_like_on_save() { + let connection = prepared(form(ConnectionTarget::Uri { uri: "mongodb://ada:pw@h/".into() })).unwrap(); + + assert_eq!(connection.target, ConnectionTarget::Uri { uri: "mongodb://h/".into() }); + assert_eq!(connection.authentication.kind, AuthenticationKind::BASIC); + assert_eq!(connection.authentication.username.as_deref(), Some("ada")); + assert_eq!(connection.authentication.password.as_deref(), Some("pw")); + } + + #[test] + fn an_invalid_connection_says_why() { + let err = prepared(form(ConnectionTarget::Host { host: "h:1".into(), port: 27017 })).unwrap_err(); + assert!(matches!(&err, DriverError::InvalidConnection(_))); + assert!(err.user_message().contains("port field")); + } +} diff --git a/src-tauri/src/database/commands/test_connection.rs b/src-tauri/src/database/commands/test_connection.rs deleted file mode 100644 index d033a1c..0000000 --- a/src-tauri/src/database/commands/test_connection.rs +++ /dev/null @@ -1,15 +0,0 @@ -use crate::{ - connector::models::connection::Connection, - drivers::{driver::TestStage, errors::DriverError, factory::DatabaseDriverFactory}, -}; - -#[tauri::command] -pub async fn test_connection(connection: Connection) -> Result, DriverError> { - let mut driver = DatabaseDriverFactory::create(connection)?; - - driver.connect().await?; - - let result = driver.test_connection(Box::new(|_, _| {})).await?; - - Ok(result) -} diff --git a/src-tauri/src/database/models/find_document.rs b/src-tauri/src/database/models/find_document.rs index 6b08850..88b5f76 100644 --- a/src-tauri/src/database/models/find_document.rs +++ b/src-tauri/src/database/models/find_document.rs @@ -1,8 +1,29 @@ use serde::{Deserialize, Serialize}; +/// One page of a find, as the query tab asks for it. +#[derive(Debug, Deserialize, Clone)] +#[serde(rename_all = "camelCase")] +pub struct FindQuery { + pub database_name: String, + pub collection_name: String, + /// extended JSON objects; null or {} means none + #[serde(default)] + pub filter: serde_json::Value, + #[serde(default)] + pub sort: serde_json::Value, + #[serde(default)] + pub projection: serde_json::Value, + #[serde(default)] + pub skip: u64, + pub limit: i64, +} + #[derive(Debug, Serialize, Deserialize, Clone)] +#[serde(rename_all = "camelCase")] pub struct FindDocumentsResult { pub documents: Vec, - #[serde(rename = "totalCount")] pub total_count: u64, -} \ No newline at end of file + /// an empty filter is counted from the collection's metadata, which is fast but may be off + #[serde(default)] + pub total_count_estimated: bool, +} diff --git a/src-tauri/src/drivers/client_options.rs b/src-tauri/src/drivers/client_options.rs new file mode 100644 index 0000000..1dca22b --- /dev/null +++ b/src-tauri/src/drivers/client_options.rs @@ -0,0 +1,324 @@ +use std::path::PathBuf; +use std::time::Duration; + +use mongodb::options::{ClientOptions, Credential, ServerAddress, Tls, TlsOptions}; +use percent_encoding::{utf8_percent_encode, NON_ALPHANUMERIC}; + +use crate::connector::models::{ + authentication::{AuthenticationKind, AuthenticationOptions}, + connection::{Connection, ConnectionTarget}, + options::ConnectionOptions, +}; +use crate::drivers::errors::DriverError; + +const APP_NAME: &str = "Monolens"; +const DEFAULT_AUTH_SOURCE: &str = "admin"; + +/// The driver's options for a connection, without building a URL string by hand. +/// A connection string is parsed by the driver (an srv string resolves DNS here). +/// `fallback_timeout` is the server selection timeout when the connection sets none. +pub async fn client_options( + connection: &Connection, + fallback_timeout: Option, +) -> Result { + let mut options = match &connection.target { + ConnectionTarget::Host { host, port } => host_options(host, *port, &connection.options), + ConnectionTarget::Uri { uri } => { + let full = with_user_info(uri, &connection.authentication); + ClientOptions::parse(&full) + .await + .map_err(|e| DriverError::UriMalformed(e.to_string()))? + } + }; + + options.app_name = Some(APP_NAME.to_string()); + // a timeout of the option field or of the connection string wins + options.server_selection_timeout = options.server_selection_timeout.or(fallback_timeout); + options.credential = credential(&connection.authentication, options.credential.take()); + + Ok(options) +} + +/// Stored connection strings carry no credentials. The driver validates some options against +/// them (an `authMechanism` needs a username), so they go back in, encoded, right before parsing. +fn with_user_info(uri: &str, auth: &AuthenticationOptions) -> String { + let Some(username) = non_empty(&auth.username) else { + return uri.to_string(); + }; + if auth.kind != AuthenticationKind::BASIC { + return uri.to_string(); + } + let Some(scheme_end) = uri.find("://").map(|i| i + 3) else { + return uri.to_string(); + }; + + let mut user_info = utf8_percent_encode(&username, NON_ALPHANUMERIC).to_string(); + if let Some(password) = &auth.password { + user_info.push(':'); + user_info.push_str(&utf8_percent_encode(password, NON_ALPHANUMERIC).to_string()); + } + + format!("{}{}@{}", &uri[..scheme_end], user_info, &uri[scheme_end..]) +} + +fn non_empty(value: &Option) -> Option { + value.as_ref().map(|v| v.trim()).filter(|v| !v.is_empty()).map(String::from) +} + +fn host_options(host: &str, port: u16, settings: &ConnectionOptions) -> ClientOptions { + let mut options = ClientOptions::builder() + .hosts(vec![ServerAddress::Tcp { + host: host.to_string(), + port: Some(port), + }]) + .build(); + + if settings.tls { + options.tls = Some(Tls::Enabled( + TlsOptions::builder() + .ca_file_path(non_empty(&settings.tls_ca_file).map(PathBuf::from)) + .allow_invalid_certificates(settings.tls_allow_invalid_certificates) + .build(), + )); + } + + options.repl_set_name = non_empty(&settings.replica_set); + options.server_selection_timeout = settings + .server_selection_timeout_secs + .map(|secs| Duration::from_secs(u64::from(secs))); + + options +} + +/// The authentication database is the form's, else the string's `authSource`, else admin. +/// A mechanism set in the connection string stays. +fn credential(auth: &AuthenticationOptions, from_uri: Option) -> Option { + match auth.kind { + AuthenticationKind::NONE => None, + AuthenticationKind::BASIC => { + let source = non_empty(&auth.database) + .or_else(|| from_uri.as_ref().and_then(|c| c.source.clone())) + .unwrap_or_else(|| DEFAULT_AUTH_SOURCE.to_string()); + + let mut credential = from_uri.unwrap_or_default(); + credential.username = auth.username.clone(); + credential.password = auth.password.clone(); + credential.source = Some(source); + Some(credential) + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::connector::models::connection::ConnectorKind; + use mongodb::options::AuthMechanism; + + fn connection(target: ConnectionTarget, authentication: AuthenticationOptions, options: ConnectionOptions) -> Connection { + Connection { + id: "id".into(), + name: "test".into(), + connector: ConnectorKind::MongoDb, + target, + authentication, + options, + } + } + + fn host() -> ConnectionTarget { + ConnectionTarget::Host { host: "db.local".into(), port: 27018 } + } + + fn uri(value: &str) -> ConnectionTarget { + ConnectionTarget::Uri { uri: value.into() } + } + + fn basic(database: Option<&str>) -> AuthenticationOptions { + AuthenticationOptions { + kind: AuthenticationKind::BASIC, + username: Some("ada".into()), + password: Some("p@ss:w/rd".into()), + database: database.map(String::from), + } + } + + #[tokio::test] + async fn host_mode_uses_host_and_port_and_driver_defaults() { + let options = client_options(&connection(host(), AuthenticationOptions::default(), ConnectionOptions::default()), None) + .await + .unwrap(); + + assert_eq!(options.hosts, vec![ServerAddress::Tcp { host: "db.local".into(), port: Some(27018) }]); + assert_eq!(options.app_name.as_deref(), Some("Monolens")); + assert!(options.credential.is_none()); + assert!(options.tls.is_none()); + assert!(options.repl_set_name.is_none()); + assert!(options.server_selection_timeout.is_none()); + } + + #[tokio::test] + async fn host_mode_applies_tls_replica_set_and_timeout() { + let settings = ConnectionOptions { + tls: true, + tls_ca_file: Some("/etc/ssl/ca.pem".into()), + tls_allow_invalid_certificates: true, + replica_set: Some("rs0".into()), + server_selection_timeout_secs: Some(5), + }; + + let options = client_options(&connection(host(), AuthenticationOptions::default(), settings), None).await.unwrap(); + + match options.tls { + Some(Tls::Enabled(tls)) => { + assert_eq!(tls.ca_file_path, Some(PathBuf::from("/etc/ssl/ca.pem"))); + assert_eq!(tls.allow_invalid_certificates, Some(true)); + } + other => panic!("expected tls, got {other:?}"), + } + assert_eq!(options.repl_set_name.as_deref(), Some("rs0")); + assert_eq!(options.server_selection_timeout, Some(Duration::from_secs(5))); + } + + #[tokio::test] + async fn host_mode_ignores_blank_option_fields() { + let settings = ConnectionOptions { + tls: true, + tls_ca_file: Some(" ".into()), + replica_set: Some("".into()), + ..ConnectionOptions::default() + }; + + let options = client_options(&connection(host(), AuthenticationOptions::default(), settings), None).await.unwrap(); + + match options.tls { + Some(Tls::Enabled(tls)) => assert_eq!(tls.ca_file_path, None), + other => panic!("expected tls, got {other:?}"), + } + assert!(options.repl_set_name.is_none()); + } + + #[tokio::test] + async fn basic_authentication_sets_the_credential_unencoded() { + let options = client_options(&connection(host(), basic(Some("shop")), ConnectionOptions::default()), None) + .await + .unwrap(); + + let credential = options.credential.unwrap(); + assert_eq!(credential.username.as_deref(), Some("ada")); + assert_eq!(credential.password.as_deref(), Some("p@ss:w/rd")); + assert_eq!(credential.source.as_deref(), Some("shop")); + } + + #[tokio::test] + async fn the_authentication_database_defaults_to_admin() { + let options = client_options(&connection(host(), basic(Some(" ")), ConnectionOptions::default()), None) + .await + .unwrap(); + assert_eq!(options.credential.unwrap().source.as_deref(), Some("admin")); + } + + #[tokio::test] + async fn uri_mode_parses_hosts_and_options_from_the_string() { + let options = client_options(&connection( + uri("mongodb://h1:27017,h2:27018/?replicaSet=rs0&tls=true&serverSelectionTimeoutMS=2000"), + AuthenticationOptions::default(), + // ignored for a connection string + ConnectionOptions { replica_set: Some("other".into()), ..ConnectionOptions::default() }, + ), None) + .await + .unwrap(); + + assert_eq!(options.hosts.len(), 2); + assert_eq!(options.repl_set_name.as_deref(), Some("rs0")); + assert!(matches!(options.tls, Some(Tls::Enabled(_)))); + assert_eq!(options.server_selection_timeout, Some(Duration::from_millis(2000))); + assert!(options.credential.is_none()); + } + + #[tokio::test] + async fn uri_mode_keeps_auth_source_and_mechanism_of_the_string() { + let options = client_options(&connection( + uri("mongodb://h/?authSource=shop&authMechanism=SCRAM-SHA-256"), + basic(None), + ConnectionOptions::default(), + ), None) + .await + .unwrap(); + + let credential = options.credential.unwrap(); + assert_eq!(credential.source.as_deref(), Some("shop")); + assert_eq!(credential.mechanism, Some(AuthMechanism::ScramSha256)); + assert_eq!(credential.username.as_deref(), Some("ada")); + } + + #[tokio::test] + async fn the_form_authentication_database_wins_over_the_string() { + let options = client_options(&connection(uri("mongodb://h/?authSource=shop"), basic(Some("admin")), ConnectionOptions::default()), None) + .await + .unwrap(); + assert_eq!(options.credential.unwrap().source.as_deref(), Some("admin")); + } + + #[tokio::test] + async fn no_authentication_drops_a_credential_of_the_string() { + let options = client_options(&connection(uri("mongodb://h/?authSource=shop"), AuthenticationOptions::default(), ConnectionOptions::default()), None) + .await + .unwrap(); + assert!(options.credential.is_none()); + } + + #[tokio::test] + async fn uri_mode_round_trips_credentials_with_special_characters() { + let options = client_options(&connection(uri("mongodb://h1,h2/shop"), basic(None), ConnectionOptions::default()), None) + .await + .unwrap(); + + let credential = options.credential.unwrap(); + assert_eq!(credential.username.as_deref(), Some("ada")); + assert_eq!(credential.password.as_deref(), Some("p@ss:w/rd")); + assert_eq!(credential.source.as_deref(), Some("shop")); + } + + #[test] + fn user_info_is_only_added_for_basic_authentication_with_a_username() { + assert_eq!(with_user_info("mongodb+srv://c.example.net/?x=1", &basic(None)), "mongodb+srv://ada:p%40ss%3Aw%2Frd@c.example.net/?x=1"); + assert_eq!(with_user_info("mongodb://h", &AuthenticationOptions::default()), "mongodb://h"); + let no_user = AuthenticationOptions { username: Some(" ".into()), ..basic(None) }; + assert_eq!(with_user_info("mongodb://h", &no_user), "mongodb://h"); + let no_password = AuthenticationOptions { password: None, ..basic(None) }; + assert_eq!(with_user_info("mongodb://h", &no_password), "mongodb://ada@h"); + } + + #[tokio::test] + async fn the_fallback_timeout_applies_only_without_a_timeout_of_the_connection() { + let fallback = Some(Duration::from_secs(10)); + let timeout = |options: ClientOptions| options.server_selection_timeout; + + let plain_host = connection(host(), AuthenticationOptions::default(), ConnectionOptions::default()); + assert_eq!(timeout(client_options(&plain_host, fallback).await.unwrap()), fallback); + + let host_with_timeout = connection( + host(), + AuthenticationOptions::default(), + ConnectionOptions { server_selection_timeout_secs: Some(5), ..ConnectionOptions::default() }, + ); + assert_eq!(timeout(client_options(&host_with_timeout, fallback).await.unwrap()), Some(Duration::from_secs(5))); + + let plain_string = connection(uri("mongodb://h/"), AuthenticationOptions::default(), ConnectionOptions::default()); + assert_eq!(timeout(client_options(&plain_string, fallback).await.unwrap()), fallback); + + let string_with_timeout = connection( + uri("mongodb://h/?serverSelectionTimeoutMS=2000"), + AuthenticationOptions::default(), + ConnectionOptions::default(), + ); + assert_eq!(timeout(client_options(&string_with_timeout, fallback).await.unwrap()), Some(Duration::from_millis(2000))); + } + + #[tokio::test] + async fn a_malformed_string_is_a_uri_error() { + let result = client_options(&connection(uri("mongodb://h/?replicaSet"), AuthenticationOptions::default(), ConnectionOptions::default()), None).await; + assert!(matches!(result, Err(DriverError::UriMalformed(_)))); + } +} diff --git a/src-tauri/src/drivers/documents.rs b/src-tauri/src/drivers/documents.rs new file mode 100644 index 0000000..f4f362c --- /dev/null +++ b/src-tauri/src/drivers/documents.rs @@ -0,0 +1,139 @@ +use mongodb::bson::{self, Bson, Document}; +use serde_json::{json, Value}; + +use crate::drivers::errors::DriverError; + +/// Integers beyond this lose precision as JavaScript numbers. +const MAX_SAFE_INTEGER: u64 = 9_007_199_254_740_991; + +/// A filter, sort or projection from the query tab: an extended JSON object, which bson's +/// serializer turns into BSON types (`{ "$oid": … }` becomes an ObjectId). Null means empty. +pub fn json_to_document(name: &str, value: &Value) -> Result { + match value { + Value::Null => Ok(Document::new()), + Value::Object(_) => bson::to_document(value) + .map_err(|e| DriverError::InvalidQuery(format!("The {name} could not be read: {e}"))), + other => Err(DriverError::InvalidQuery(format!( + "The {name} must be an object like {{ field: value }}, not {}", + kind_of(other) + ))), + } +} + +fn kind_of(value: &Value) -> &'static str { + match value { + Value::Null => "null", + Value::Bool(_) => "a boolean", + Value::Number(_) => "a number", + Value::String(_) => "a string", + Value::Array(_) => "an array", + Value::Object(_) => "an object", + } +} + +/// A document for the UI as relaxed extended JSON (dates as ISO strings), but an Int64 beyond +/// ±2^53 stays `{ "$numberLong": "…" }`, which a JavaScript number couldn't hold exactly. +pub fn document_to_json(document: Document) -> Value { + Value::Object( + document + .into_iter() + .map(|(key, value)| (key, bson_to_json(value))) + .collect(), + ) +} + +fn bson_to_json(value: Bson) -> Value { + match value { + Bson::Int64(n) if n.unsigned_abs() > MAX_SAFE_INTEGER => json!({ "$numberLong": n.to_string() }), + Bson::Document(document) => document_to_json(document), + Bson::Array(items) => Value::Array(items.into_iter().map(bson_to_json).collect()), + other => other.into_relaxed_extjson(), + } +} + +#[cfg(test)] +mod tests { + use super::*; + use mongodb::bson::{doc, oid::ObjectId, spec::BinarySubtype, Binary, DateTime, Decimal128}; + use std::str::FromStr; + + #[test] + fn null_and_empty_objects_are_empty_documents() { + assert_eq!(json_to_document("filter", &Value::Null).unwrap(), Document::new()); + assert_eq!(json_to_document("filter", &json!({})).unwrap(), Document::new()); + } + + #[test] + fn extended_json_becomes_bson_types() { + let document = json_to_document( + "filter", + &json!({ "_id": { "$oid": "507f1f77bcf86cd799439011" }, "at": { "$gte": { "$date": "2024-01-01T00:00:00.000Z" } } }), + ) + .unwrap(); + + assert!(matches!(document.get("_id"), Some(Bson::ObjectId(_)))); + assert!(matches!(document.get_document("at").unwrap().get("$gte"), Some(Bson::DateTime(_)))); + } + + #[test] + fn refuses_values_that_are_not_objects_and_names_the_part() { + for (value, kind) in [(json!(5), "a number"), (json!([1]), "an array"), (json!("x"), "a string"), (json!(true), "a boolean")] { + let err = json_to_document("sort", &value).unwrap_err(); + let DriverError::InvalidQuery(message) = &err else { panic!("{err:?}") }; + assert!(message.starts_with("The sort must be an object"), "{message}"); + assert!(message.ends_with(kind), "{message}"); + } + } + + #[test] + fn documents_become_relaxed_extended_json() { + let id = ObjectId::from_str("507f1f77bcf86cd799439011").unwrap(); + let value = document_to_json(doc! { + "_id": id, + "name": "Ada", + "age": 36_i32, + "visits": 12_i64, + "score": 1.5, + "at": DateTime::from_millis(1_704_067_200_000), + "price": Decimal128::from_str("1.10").unwrap(), + "tags": ["a", { "n": 1_i32 }], + "nested": { "deep": { "at": DateTime::from_millis(0) } }, + "bytes": Binary { subtype: BinarySubtype::Generic, bytes: vec![0, 1, 2] }, + "nothing": Bson::Null, + }); + + assert_eq!( + value, + json!({ + "_id": { "$oid": "507f1f77bcf86cd799439011" }, + "name": "Ada", + "age": 36, + "visits": 12, + "score": 1.5, + "at": { "$date": "2024-01-01T00:00:00Z" }, + "price": { "$numberDecimal": "1.10" }, + "tags": ["a", { "n": 1 }], + "nested": { "deep": { "at": { "$date": "1970-01-01T00:00:00Z" } } }, + "bytes": { "$binary": { "base64": "AAEC", "subType": "00" } }, + "nothing": null, + }) + ); + } + + #[test] + fn keeps_large_int64_exact_at_any_depth() { + let value = document_to_json(doc! { + "safe": 9_007_199_254_740_991_i64, + "big": 9_007_199_254_740_993_i64, + "negative": -9_007_199_254_740_993_i64, + "list": [9_007_199_254_740_993_i64], + "nested": { "big": i64::MAX }, + }); + + assert_eq!(value["safe"], json!(9_007_199_254_740_991_i64)); + assert_eq!(value["big"], json!({ "$numberLong": "9007199254740993" })); + assert_eq!(value["negative"], json!({ "$numberLong": "-9007199254740993" })); + assert_eq!(value["list"][0], json!({ "$numberLong": "9007199254740993" })); + assert_eq!(value["nested"]["big"], json!({ "$numberLong": i64::MAX.to_string() })); + } +} diff --git a/src-tauri/src/drivers/driver.rs b/src-tauri/src/drivers/driver.rs index 5389dd8..c725b9e 100644 --- a/src-tauri/src/drivers/driver.rs +++ b/src-tauri/src/drivers/driver.rs @@ -1,17 +1,33 @@ use async_trait::async_trait; use serde::{Deserialize, Serialize}; use super::errors::DriverError; -use crate::database::models::find_document::FindDocumentsResult; +use crate::database::models::find_document::{FindDocumentsResult, FindQuery}; -#[derive(Debug, Serialize, Deserialize, Clone)] +#[derive(Debug, Serialize, Deserialize, Clone, Copy, PartialEq)] +#[serde(rename_all = "lowercase")] +pub enum StageStatus { + Pending, + Passed, + Failed, + /// the check could not run, e.g. without the privilege; the test goes on + Skipped, +} + +#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] pub struct TestStage { - pub status: Option, + pub status: StageStatus, pub title: String, + /// e.g. the detected version, or why a stage was skipped + pub detail: Option, } impl TestStage { - pub fn new(status: Option, title: String) -> Self { - TestStage { status, title } + pub fn pending(title: &str) -> Self { + TestStage { + status: StageStatus::Pending, + title: title.to_string(), + detail: None, + } } } @@ -20,19 +36,11 @@ pub type ProgressCallback = Box; #[async_trait] pub trait DatabaseDriver: Send + Sync { async fn connect(&mut self) -> Result<(), DriverError>; - async fn disconnect(&mut self) -> Result<(), DriverError>; async fn test_connection( &self, on_progress: ProgressCallback, ) -> Result, DriverError>; async fn list_databases(&self) -> Result, DriverError>; async fn list_collections(&self, database_name: &str) -> Result, DriverError>; - async fn find_documents( - &self, - database_name: &str, - collection_name: &str, - filter: serde_json::Value, - skip: u64, - limit: i64 - ) -> Result; + async fn find_documents(&self, query: &FindQuery) -> Result; } diff --git a/src-tauri/src/drivers/errors.rs b/src-tauri/src/drivers/errors.rs index 4c04424..1190695 100644 --- a/src-tauri/src/drivers/errors.rs +++ b/src-tauri/src/drivers/errors.rs @@ -28,8 +28,23 @@ pub enum DriverError { #[error("Unknown driver type")] UnknownDriver, - #[error("Failed to find documents")] - FindDocumentsFailed, + #[error("Failed to find documents: {0}")] + FindDocumentsFailed(String), + + #[error("Invalid query: {0}")] + InvalidQuery(String), + + #[error("Invalid connection: {0}")] + InvalidConnection(String), + + #[error("Connection not found: {0}")] + ConnectionNotFound(String), + + #[error("Could not read the saved connections: {0}")] + StorageFailed(String), + + #[error("Could not read the password from the keyring: {0}")] + CredentialsUnavailable(String), } impl DriverError { @@ -80,7 +95,24 @@ impl DriverError { String::from("The connection URI is invalid. Please check the format.") } DriverError::UnknownDriver => String::from("Unsupported database driver."), - DriverError::FindDocumentsFailed => String::from("Failed to find documents. Check your permissions on this database.") + DriverError::FindDocumentsFailed(msg) => { + if Self::is_auth_error(msg) || msg.to_lowercase().contains("not authorized") { + String::from("Not allowed to read this collection. Check your permissions on this database.") + } else { + // the server's message names the mistake in the query + format!("The query failed: {msg}") + } + } + DriverError::InvalidQuery(msg) | DriverError::InvalidConnection(msg) => msg.clone(), + DriverError::ConnectionNotFound(_) => { + String::from("This connection no longer exists.") + } + DriverError::StorageFailed(_) => { + String::from("Could not read the saved connections.") + } + DriverError::CredentialsUnavailable(_) => { + String::from("The password of this connection could not be read from the system keyring. Edit the connection and enter it again.") + } } } @@ -113,7 +145,12 @@ impl DriverError { DriverError::ServerStatusFailed(_) => "ServerStatusFailed", DriverError::UriMalformed(_) => "UriMalformed", DriverError::UnknownDriver => "UnknownDriver", - DriverError::FindDocumentsFailed => "FindDocumentsFailed", + DriverError::FindDocumentsFailed(_) => "FindDocumentsFailed", + DriverError::InvalidQuery(_) => "InvalidQuery", + DriverError::InvalidConnection(_) => "InvalidConnection", + DriverError::ConnectionNotFound(_) => "ConnectionNotFound", + DriverError::StorageFailed(_) => "StorageFailed", + DriverError::CredentialsUnavailable(_) => "CredentialsUnavailable", } } } @@ -130,3 +167,59 @@ impl Serialize for DriverError { state.end() } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn recognises_authentication_failures_in_driver_messages() { + for err in [ + DriverError::ConnectionFailed("SCRAM failure: Authentication failed.".into()), + DriverError::PingFailed("Command failed: Unauthorized".into()), + DriverError::ListDatabasesFailed("AuthError: bad auth".into()), + DriverError::ServerStatusFailed("authentication failed".into()), + ] { + assert!(err.user_message().starts_with("Authentication failed"), "{err:?}"); + } + } + + #[test] + fn recognises_timeouts_when_pinging() { + let err = DriverError::PingFailed("Server selection timeout: No available servers".into()); + assert!(err.user_message().starts_with("Connection timed out")); + } + + #[test] + fn falls_back_to_a_generic_message() { + assert!(DriverError::ConnectionFailed("dns error".into()).user_message().starts_with("Failed to connect")); + assert!(DriverError::PingFailed("connection refused".into()).user_message().starts_with("Could not reach")); + } + + #[test] + fn credential_and_storage_failures_have_their_own_messages() { + assert!(DriverError::CredentialsUnavailable("locked".into()).user_message().contains("keyring")); + assert_eq!(DriverError::ConnectionNotFound("x".into()).user_message(), "This connection no longer exists."); + assert_eq!(DriverError::StorageFailed("io".into()).user_message(), "Could not read the saved connections."); + } + + #[test] + fn query_errors_pass_the_server_message_on() { + assert_eq!( + DriverError::FindDocumentsFailed("unknown top level operator: $foo".into()).user_message(), + "The query failed: unknown top level operator: $foo" + ); + assert!(DriverError::FindDocumentsFailed("not authorized on shop to execute command".into()) + .user_message() + .starts_with("Not allowed")); + assert_eq!(DriverError::InvalidQuery("The sort must be an object".into()).user_message(), "The sort must be an object"); + } + + #[test] + fn serializes_kind_message_and_user_message() { + let value = serde_json::to_value(DriverError::CredentialsUnavailable("locked".into())).unwrap(); + assert_eq!(value["kind"], "CredentialsUnavailable"); + assert_eq!(value["message"], "Could not read the password from the keyring: locked"); + assert!(value["user_message"].as_str().unwrap().contains("keyring")); + } +} diff --git a/src-tauri/src/drivers/factory.rs b/src-tauri/src/drivers/factory.rs index af722c0..2a23879 100644 --- a/src-tauri/src/drivers/factory.rs +++ b/src-tauri/src/drivers/factory.rs @@ -13,4 +13,11 @@ impl DatabaseDriverFactory { ConnectorKind::MongoDb => Ok(Box::new(MongoDbOfficialDriver::new(connection))), } } + + /// A driver for the connection test: it gives up sooner when no server answers. + pub fn create_for_test(connection: Connection) -> Result, DriverError> { + match connection.connector { + ConnectorKind::MongoDb => Ok(Box::new(MongoDbOfficialDriver::for_test(connection))), + } + } } diff --git a/src-tauri/src/drivers/mod.rs b/src-tauri/src/drivers/mod.rs index e7d99f4..8a03b2d 100644 --- a/src-tauri/src/drivers/mod.rs +++ b/src-tauri/src/drivers/mod.rs @@ -1,3 +1,5 @@ +pub mod client_options; +pub mod documents; pub mod driver; pub mod errors; pub mod factory; diff --git a/src-tauri/src/drivers/mongo_official_driver.rs b/src-tauri/src/drivers/mongo_official_driver.rs index fe26103..7a33f6b 100644 --- a/src-tauri/src/drivers/mongo_official_driver.rs +++ b/src-tauri/src/drivers/mongo_official_driver.rs @@ -1,16 +1,54 @@ use crate::{ - connector::models::{authentication::AuthenticationKind, connection::Connection}, database::models::find_document::FindDocumentsResult, drivers::{ - driver::{DatabaseDriver, ProgressCallback, TestStage}, + connector::models::connection::Connection, + database::models::find_document::{FindDocumentsResult, FindQuery}, + drivers::{ + client_options::client_options, + documents::{document_to_json, json_to_document}, + driver::{DatabaseDriver, ProgressCallback, StageStatus, TestStage}, errors::DriverError, } }; use async_trait::async_trait; use futures::StreamExt; -use mongodb::{bson::doc, Client}; +use mongodb::{ + bson::{doc, Document}, + Client, +}; +use std::time::Duration; + +const TEST_STAGES: [&str; 5] = [ + "Initialize", + "Ping Database", + "Reading status", + "Detecting version", + "Connected", +]; + +const MAX_LIMIT: i64 = 10_000; + +/// Without a timeout of its own, a connection waits the driver's 30 s for a server. +/// A test should say sooner that nothing answers, e.g. on a wrong port. +const TEST_SERVER_SELECTION_TIMEOUT: Duration = Duration::from_secs(10); + +/// The server's own message for a failed command, without the driver's wrapping. +fn find_failed(error: mongodb::error::Error) -> DriverError { + let message = match error.kind.as_ref() { + mongodb::error::ErrorKind::Command(command) => command.message.clone(), + _ => error.to_string(), + }; + log::warn!("find failed: {}", message); + DriverError::FindDocumentsFailed(message) +} + +/// MongoDB's error code 13, Unauthorized: the user lacks the privilege for a command. +fn is_unauthorized(error: &mongodb::error::Error) -> bool { + matches!(error.kind.as_ref(), mongodb::error::ErrorKind::Command(command) if command.code == 13) +} pub struct MongoDbOfficialDriver { client: Option, connection: Connection, + fallback_timeout: Option, } impl MongoDbOfficialDriver { @@ -18,56 +56,16 @@ impl MongoDbOfficialDriver { MongoDbOfficialDriver { client: None, connection, + fallback_timeout: None, } } - fn build_connection_string(&self) -> Result { - let conn = &self.connection; - let mut uri = match url::Url::parse(format!("mongodb://{}", &conn.uri).as_str()) { - Ok(res) => res, - Err(err) => { - log::warn!("Uri malformed: {}", err); - return Err(DriverError::UriMalformed(err.to_string())); - } - }; - - uri.set_port(Some(conn.port)).map_err(|_| { - DriverError::UriMalformed(format!("Could not set port to {}", conn.port)) - })?; - - let mut query: Vec = Vec::new(); - - match &conn.authentication.kind { - AuthenticationKind::BASIC => { - uri.set_username(conn.authentication.username.as_deref().unwrap_or("root")) - .map_err(|_| { - DriverError::UriMalformed(String::from("Could not set username")) - })?; - uri.set_password(conn.authentication.password.as_deref()) - .map_err(|_| { - DriverError::UriMalformed(String::from("Could not set password")) - })?; - } - AuthenticationKind::NONE => {} - }; - - let auth_db = conn.authentication.database.as_deref().unwrap_or("admin"); - - if !auth_db.is_empty() { - query.push(format!("authSource={}", auth_db)); - } - - uri.set_query(Some(query.join("&").as_str())); - - if cfg!(debug_assertions) { - let mut log_uri = uri.clone(); - if log_uri.password().is_some() { - let _ = log_uri.set_password(Some("***")); - } - log::debug!("Built connection query: {}", log_uri.as_str()); + /// A driver for the connection test, which gives up sooner when no server answers. + pub fn for_test(connection: Connection) -> Self { + MongoDbOfficialDriver { + fallback_timeout: Some(TEST_SERVER_SELECTION_TIMEOUT), + ..Self::new(connection) } - - Ok(uri.to_string()) } } @@ -75,10 +73,19 @@ impl MongoDbOfficialDriver { impl DatabaseDriver for MongoDbOfficialDriver { async fn connect(&mut self) -> Result<(), DriverError> { if self.client.is_none() { - let connection_string = self.build_connection_string()?; + let options = client_options(&self.connection, self.fallback_timeout).await?; + // never the credential: hosts and settings only + log::debug!( + "Client for {}: hosts {:?}, tls {}, replica set {:?}, timeout {:?}, authentication {}", + self.connection.name, + options.hosts, + options.tls.is_some(), + options.repl_set_name, + options.server_selection_timeout, + options.credential.is_some(), + ); self.client = Some( - Client::with_uri_str(&connection_string) - .await + Client::with_options(options) .map_err(|e| DriverError::ConnectionFailed(e.to_string()))?, ); } @@ -86,76 +93,82 @@ impl DatabaseDriver for MongoDbOfficialDriver { Ok(()) } - async fn disconnect(&mut self) -> Result<(), DriverError> { - self.client = None; - - Ok(()) - } - /// Testing Connection - /// ## Steps + /// ## Stages /// 1. Initialize - /// 2. Ping Database - /// 3. Reading stats - /// 4. Version detection + /// 2. Ping Database (also authenticates) + /// 3. Reading status (`serverStatus`, skipped without the privilege) + /// 4. Detecting version (`buildInfo`, needs no privilege) /// 5. Connected + /// + /// Every stage is reported as pending first, so the stage titles exist only here. async fn test_connection( &self, on_progress: ProgressCallback, ) -> Result, DriverError> { - let mut stages: Vec = vec![ - TestStage::new(None, String::from("Initialize")), - TestStage::new(None, String::from("Ping Database")), - TestStage::new(None, String::from("Reading status")), - TestStage::new(None, String::from("Detecting version")), - TestStage::new(None, String::from("Connected")), - ]; + let mut stages: Vec = TEST_STAGES.iter().map(|title| TestStage::pending(title)).collect(); + for (index, stage) in stages.iter().enumerate() { + on_progress(index, stage); + } + + let mut report = |index: usize, status: StageStatus, detail: Option| { + stages[index].status = status; + stages[index].detail = detail; + on_progress(index, &stages[index]); + }; // Stage 0: Initialize Connection - let client_ref = self - .client - .as_ref() - .ok_or(DriverError::ClientNotInitialized)?; - stages[0].status = Some(true); - on_progress(0, &stages[0]); + let Some(client_ref) = self.client.as_ref() else { + report(0, StageStatus::Failed, None); + return Err(DriverError::ClientNotInitialized); + }; + report(0, StageStatus::Passed, None); let default_database_name = self .connection .authentication .database - .as_ref() - .map(|s| s.as_str()) + .as_deref() + .filter(|name| !name.trim().is_empty()) .unwrap_or("admin"); let default_database = client_ref.database(default_database_name); // Stage 1: Ping Database - default_database - .run_command(doc! {"ping": 1}) - .await - .map_err(|e| DriverError::PingFailed(e.to_string()))?; - stages[1].status = Some(true); - on_progress(1, &stages[1]); + if let Err(e) = default_database.run_command(doc! {"ping": 1}).await { + report(1, StageStatus::Failed, None); + return Err(DriverError::PingFailed(e.to_string())); + } + report(1, StageStatus::Passed, None); // Stage 2: Reading Server status - let server_status = default_database - .run_command(doc! {"serverStatus": 1}) - .await - .map_err(|e| DriverError::ServerStatusFailed(e.to_string()))?; - stages[2].status = Some(true); - on_progress(2, &stages[2]); - - // Stage 3: Detecting MongoDB version - if server_status.get_str("version").is_ok() { - stages[3].status = Some(true); - } else { - stages[3].status = Some(false); + match default_database.run_command(doc! {"serverStatus": 1}).await { + Ok(_) => report(2, StageStatus::Passed, None), + Err(e) if is_unauthorized(&e) => report( + 2, + StageStatus::Skipped, + Some(String::from("No permission to read the server status")), + ), + Err(e) => { + report(2, StageStatus::Failed, None); + return Err(DriverError::ServerStatusFailed(e.to_string())); + } + } + + // Stage 3: Detecting MongoDB version; an unknown version doesn't stop the test + match default_database.run_command(doc! {"buildInfo": 1}).await { + Ok(info) => match info.get_str("version") { + Ok(version) => report(3, StageStatus::Passed, Some(format!("MongoDB {version}"))), + Err(_) => report(3, StageStatus::Failed, Some(String::from("The server reported no version"))), + }, + Err(e) => { + log::warn!("buildInfo failed: {}", e); + report(3, StageStatus::Failed, Some(String::from("Could not read the version"))); + } } - on_progress(3, &stages[3]); // Stage 4: Connected - stages[4].status = Some(true); - on_progress(4, &stages[4]); + report(4, StageStatus::Passed, None); Ok(stages) } @@ -178,62 +191,323 @@ impl DatabaseDriver for MongoDbOfficialDriver { .as_ref() .ok_or(DriverError::ClientNotInitialized)?; - let database = client.database(&database_name); + let database = client.database(database_name); database.list_collection_names().await.map_err(|e| { DriverError::ListCollectionsFailed(database_name.to_string(), e.to_string()) }) } - async fn find_documents( - &self, - database_name: &str, - collection_name: &str, - filter: serde_json::Value, - skip: u64, - limit: i64 - ) -> Result { + async fn find_documents(&self, query: &FindQuery) -> Result { let client = self .client .as_ref() .ok_or(DriverError::ClientNotInitialized)?; - let database = client.database(&database_name); - let collection = database.collection::(&collection_name); + if !(1..=MAX_LIMIT).contains(&query.limit) { + return Err(DriverError::InvalidQuery(format!( + "The limit must be between 1 and {MAX_LIMIT}." + ))); + } - let filter_doc = match mongodb::bson::to_document(&filter) { - Ok(doc) => doc, - Err(e) => { - log::warn!("Failed to convert filter to BSON document: {}", e); - return Err(DriverError::FindDocumentsFailed); + let filter = json_to_document("filter", &query.filter)?; + let sort = json_to_document("sort", &query.sort)?; + let projection = json_to_document("projection", &query.projection)?; + + let collection = client + .database(&query.database_name) + .collection::(&query.collection_name); + + let find = async { + let mut find = collection + .find(filter.clone()) + .skip(query.skip) + .limit(query.limit) + .allow_disk_use(true); + if !sort.is_empty() { + find = find.sort(sort); + } + if !projection.is_empty() { + find = find.projection(projection); + } + + let mut cursor = find.await.map_err(find_failed)?; + let mut documents = Vec::new(); + while let Some(document) = cursor.next().await { + documents.push(document_to_json(document.map_err(find_failed)?)); + } + Ok::<_, DriverError>(documents) + }; + + // an empty filter counts from the collection's metadata instead of scanning it + let estimated = filter.is_empty(); + let count = async { + if estimated { + collection.estimated_document_count().await + } else { + collection.count_documents(filter.clone()).await } + .map_err(find_failed) }; - let mut cursor = collection - .find(filter_doc.clone()) - .skip(skip) - .limit(limit) - .allow_disk_use(true) + let (documents, total_count) = futures::try_join!(find, count)?; + + Ok(FindDocumentsResult { + documents, + total_count, + total_count_estimated: estimated, + }) + } +} + +/// Against a real MongoDB, skipped by default. Run with a server and its root account: +/// MONGODB_TEST_HOST=127.0.0.1 MONGODB_TEST_PORT=27017 MONGODB_TEST_USER=root MONGODB_TEST_PASSWORD=… cargo test -- --ignored +#[cfg(test)] +mod integration { + use super::*; + use crate::connector::models::{ + authentication::{AuthenticationKind, AuthenticationOptions}, + connection::{ConnectionTarget, ConnectorKind}, + options::ConnectionOptions, + }; + + fn env(name: &str) -> String { + std::env::var(name).unwrap_or_else(|_| panic!("{name} is not set")) + } + + fn root(password: Option) -> AuthenticationOptions { + AuthenticationOptions { + kind: AuthenticationKind::BASIC, + username: Some(env("MONGODB_TEST_USER")), + password: Some(password.unwrap_or_else(|| env("MONGODB_TEST_PASSWORD"))), + database: Some("admin".into()), + } + } + + fn host() -> ConnectionTarget { + ConnectionTarget::Host { + host: env("MONGODB_TEST_HOST"), + port: env("MONGODB_TEST_PORT").parse().unwrap(), + } + } + + fn connection(target: ConnectionTarget, authentication: AuthenticationOptions) -> Connection { + Connection { + id: "integration".into(), + name: "integration".into(), + connector: ConnectorKind::MongoDb, + target, + authentication, + options: ConnectionOptions { + server_selection_timeout_secs: Some(5), + ..ConnectionOptions::default() + }, + } + } + + async fn connected(connection: Connection) -> MongoDbOfficialDriver { + let mut driver = MongoDbOfficialDriver::new(connection); + driver.connect().await.unwrap(); + driver + } + + /// Needs no MongoDB, only a port nothing listens on; slow, so it runs with the others. + #[tokio::test] + #[ignore] + async fn the_test_gives_up_after_its_own_timeout_when_no_server_answers() { + let port = std::net::TcpListener::bind("127.0.0.1:0").unwrap().local_addr().unwrap().port(); + let mut driver = MongoDbOfficialDriver::for_test(Connection { + options: ConnectionOptions::default(), + ..connection(ConnectionTarget::Host { host: "127.0.0.1".into(), port }, AuthenticationOptions::default()) + }); + driver.connect().await.unwrap(); + + let started = std::time::Instant::now(); + let err = driver.test_connection(Box::new(|_, _| {})).await.unwrap_err(); + + assert!(matches!(&err, DriverError::PingFailed(_)), "{err:?}"); + assert!(err.user_message().starts_with("Connection timed out"), "{}", err.user_message()); + let waited = started.elapsed(); + assert!(waited >= TEST_SERVER_SELECTION_TIMEOUT && waited < Duration::from_secs(20), "{waited:?}"); + } + + #[tokio::test] + #[ignore] + async fn connects_by_host_and_lists_databases() { + let driver = connected(connection(host(), root(None))).await; + let databases = driver.list_databases().await.unwrap(); + assert!(databases.contains(&"admin".to_string()), "{databases:?}"); + } + + #[tokio::test] + #[ignore] + async fn connects_by_connection_string_with_separate_credentials() { + let uri = format!( + "mongodb://{}:{}/?serverSelectionTimeoutMS=5000", + env("MONGODB_TEST_HOST"), + env("MONGODB_TEST_PORT") + ); + let driver = connected(connection(ConnectionTarget::Uri { uri }, root(None))).await; + assert!(driver.list_databases().await.unwrap().contains(&"admin".to_string())); + } + + fn statuses(stages: &[TestStage]) -> Vec { + stages.iter().map(|stage| stage.status).collect() + } + + #[tokio::test] + #[ignore] + async fn the_connection_test_passes_every_stage_for_root_and_names_the_version() { + let driver = connected(connection(host(), root(None))).await; + let reported = std::sync::Arc::new(std::sync::Mutex::new(Vec::new())); + let sink = std::sync::Arc::clone(&reported); + + let stages = driver + .test_connection(Box::new(move |index, stage| sink.lock().unwrap().push((index, stage.clone())))) .await - .map_err(|_| DriverError::FindDocumentsFailed)?; - - let mut documents = Vec::new(); - while let Some(result) = cursor.next().await { - match result { - Ok(doc) => documents.push(serde_json::to_value(doc).unwrap_or_default()), - Err(_) => { - log::warn!("Failed to read document from cursor"); - return Err(DriverError::FindDocumentsFailed); + .unwrap(); + + assert_eq!(statuses(&stages), vec![StageStatus::Passed; 5]); + assert!(stages[3].detail.as_deref().unwrap().starts_with("MongoDB "), "{:?}", stages[3]); + let reported = reported.lock().unwrap(); + // five pending stages first, then one report per stage + assert_eq!(reported.len(), 10); + assert!(reported[..5].iter().all(|(_, stage)| stage.status == StageStatus::Pending)); + } + + /// Needs a user `reader` with password `readerpw` and the role read on the database `shop`. + #[tokio::test] + #[ignore] + async fn a_user_without_the_server_status_privilege_skips_that_stage() { + let reader = AuthenticationOptions { + kind: AuthenticationKind::BASIC, + username: Some("reader".into()), + password: Some("readerpw".into()), + database: Some("shop".into()), + }; + let driver = connected(connection(host(), reader)).await; + + let stages = driver.test_connection(Box::new(|_, _| {})).await.unwrap(); + + assert_eq!( + statuses(&stages), + vec![StageStatus::Passed, StageStatus::Passed, StageStatus::Skipped, StageStatus::Passed, StageStatus::Passed] + ); + } + + #[tokio::test] + #[ignore] + async fn a_wrong_password_fails_the_ping_stage() { + let driver = connected(connection(host(), root(Some("wrong".into())))).await; + let failed = std::sync::Arc::new(std::sync::Mutex::new(None)); + let sink = std::sync::Arc::clone(&failed); + + let err = driver + .test_connection(Box::new(move |index, stage| { + if stage.status == StageStatus::Failed { + *sink.lock().unwrap() = Some(index); } - } + })) + .await + .unwrap_err(); + + assert!(err.user_message().starts_with("Authentication failed"), "{err:?}"); + assert_eq!(*failed.lock().unwrap(), Some(1)); + } + + fn users(filter: serde_json::Value, sort: serde_json::Value, projection: serde_json::Value, skip: u64, limit: i64) -> FindQuery { + FindQuery { + database_name: "shop".into(), + collection_name: "users".into(), + filter, + sort, + projection, + skip, + limit, } + } + + /// Needs the collection shop.users with exactly { name: "Ada", age: 36 } and { name: "Bob", age: 19 }. + #[tokio::test] + #[ignore] + async fn finds_with_sort_projection_and_paging_and_estimates_an_unfiltered_count() { + use serde_json::json; + let driver = connected(connection(host(), root(None))).await; + + let all = driver + .find_documents(&users(json!({}), json!({ "age": 1 }), json!({ "name": 1, "_id": 0 }), 0, 10)) + .await + .unwrap(); + assert_eq!(all.documents, vec![json!({ "name": "Bob" }), json!({ "name": "Ada" })]); + assert_eq!(all.total_count, 2); + assert!(all.total_count_estimated); + + let second = driver + .find_documents(&users(serde_json::Value::Null, json!({ "age": 1 }), json!({ "name": 1, "_id": 0 }), 1, 1)) + .await + .unwrap(); + assert_eq!(second.documents, vec![json!({ "name": "Ada" })]); + assert_eq!(second.total_count, 2); + } + + #[tokio::test] + #[ignore] + async fn counts_a_filter_exactly() { + use serde_json::json; + let driver = connected(connection(host(), root(None))).await; - // Get total count of matching documents without skip/limit and give up ownership as we do not need it further - let total_count = collection - .count_documents(filter_doc) + let older = driver + .find_documents(&users(json!({ "age": { "$gt": 20 } }), json!(null), json!({ "_id": 0 }), 0, 10)) .await - .map_err(|_| DriverError::FindDocumentsFailed)?; + .unwrap(); + + assert_eq!(older.documents, vec![json!({ "name": "Ada", "age": 36 })]); + assert_eq!(older.total_count, 1); + assert!(!older.total_count_estimated); + } + + #[tokio::test] + #[ignore] + async fn a_broken_query_returns_the_server_message() { + use serde_json::json; + let driver = connected(connection(host(), root(None))).await; + + let err = driver.find_documents(&users(json!({ "$foo": 1 }), json!(null), json!(null), 0, 10)).await.unwrap_err(); + + assert!(err.user_message().contains("unknown top level operator: $foo"), "{}", err.user_message()); + } + + #[tokio::test] + #[ignore] + async fn a_limit_out_of_range_is_refused_before_the_server() { + let driver = connected(connection(host(), root(None))).await; + let err = driver.find_documents(&users(serde_json::Value::Null, serde_json::Value::Null, serde_json::Value::Null, 0, 0)).await.unwrap_err(); + assert!(matches!(err, DriverError::InvalidQuery(_))); + } + + #[tokio::test] + #[ignore] + async fn reading_without_permission_says_so() { + let reader = AuthenticationOptions { + kind: AuthenticationKind::BASIC, + username: Some("reader".into()), + password: Some("readerpw".into()), + database: Some("shop".into()), + }; + let driver = connected(connection(host(), reader)).await; + let mut query = users(serde_json::Value::Null, serde_json::Value::Null, serde_json::Value::Null, 0, 10); + query.database_name = "other".into(); + + let err = driver.find_documents(&query).await.unwrap_err(); + + assert!(err.user_message().starts_with("Not allowed"), "{}", err.user_message()); + } - Ok(FindDocumentsResult { documents, total_count }) + #[tokio::test] + #[ignore] + async fn a_wrong_password_is_an_authentication_error() { + let driver = connected(connection(host(), root(Some("wrong".into())))).await; + let err = driver.list_databases().await.unwrap_err(); + assert!(err.user_message().starts_with("Authentication failed"), "{err:?}"); } } diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index c9599ff..295b8ec 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -7,6 +7,7 @@ use crate::{ }, connector::repositories::credentials::credential_service::CredentialService }; +use std::sync::Arc; use crate::task_manager::task_manager::TaskManager; @@ -14,6 +15,7 @@ pub mod connection_handler; pub mod connector; pub mod database; pub mod drivers; +pub mod menu; pub mod task_manager; pub struct AppState { @@ -28,10 +30,21 @@ pub fn create_app_state(repository: impl ConnectorRepository + 'static) -> AppSt #[cfg_attr(mobile, tauri::mobile_entry_point)] pub fn run() { - tauri::Builder::default() + let mut builder = tauri::Builder::default(); + + if cfg!(target_os = "macos") { + builder = builder.menu(menu::macos_menu::); + } + + builder .plugin( tauri_plugin_log::Builder::new() - .level(tauri_plugin_log::log::LevelFilter::Debug) + // debug output stays in development builds + .level(if cfg!(debug_assertions) { + tauri_plugin_log::log::LevelFilter::Debug + } else { + tauri_plugin_log::log::LevelFilter::Info + }) .build(), ) .setup(|app| { @@ -39,7 +52,7 @@ pub fn run() { log::debug!("Load config from path {}", path.display()); - let repository = FileRepository::new(path, CredentialService::new("monolens-connections")); + let repository = FileRepository::new(path, Arc::new(CredentialService::new("monolens-connections"))); let state = create_app_state(repository); app.manage(state); @@ -50,14 +63,12 @@ pub fn run() { }) .plugin(tauri_plugin_opener::init()) .invoke_handler(tauri::generate_handler![ - database::commands::test_connection::test_connection, database::commands::start_test_connection::start_test_connection, database::commands::start_connect::start_connect, - database::commands::get_databases::get_databases, + database::commands::disconnect::disconnect, database::commands::list_collections::list_collections, database::commands::start_find_documents::start_find_documents, connector::commands::get_connections::get_connections, - connector::commands::get_connection::get_connection, connector::commands::get_connection_password::get_connection_password, connector::commands::create_connection::create_connection, connector::commands::update_connection::update_connection, diff --git a/src-tauri/src/menu.rs b/src-tauri/src/menu.rs new file mode 100644 index 0000000..0ded4e8 --- /dev/null +++ b/src-tauri/src/menu.rs @@ -0,0 +1,66 @@ +use tauri::{ + menu::{AboutMetadata, Menu, PredefinedMenuItem, Submenu}, + AppHandle, Runtime, +}; + +/// Tauri's default macOS menu without "Close Window": its Cmd+W would close the app +/// instead of the active tab. The Edit menu stays, macOS routes copy and paste through it. +/// Compiled everywhere so a Linux build catches type errors, but only used on macOS. +pub fn macos_menu(app: &AppHandle) -> tauri::Result> { + let pkg_info = app.package_info(); + let about = AboutMetadata { + name: Some(pkg_info.name.clone()), + version: Some(pkg_info.version.to_string()), + ..Default::default() + }; + + Menu::with_items( + app, + &[ + &Submenu::with_items( + app, + pkg_info.name.clone(), + true, + &[ + &PredefinedMenuItem::about(app, None, Some(about))?, + &PredefinedMenuItem::separator(app)?, + &PredefinedMenuItem::services(app, None)?, + &PredefinedMenuItem::separator(app)?, + &PredefinedMenuItem::hide(app, None)?, + &PredefinedMenuItem::hide_others(app, None)?, + &PredefinedMenuItem::separator(app)?, + &PredefinedMenuItem::quit(app, None)?, + ], + )?, + &Submenu::with_items( + app, + "Edit", + true, + &[ + &PredefinedMenuItem::undo(app, None)?, + &PredefinedMenuItem::redo(app, None)?, + &PredefinedMenuItem::separator(app)?, + &PredefinedMenuItem::cut(app, None)?, + &PredefinedMenuItem::copy(app, None)?, + &PredefinedMenuItem::paste(app, None)?, + &PredefinedMenuItem::select_all(app, None)?, + ], + )?, + &Submenu::with_items( + app, + "View", + true, + &[&PredefinedMenuItem::fullscreen(app, None)?], + )?, + &Submenu::with_items( + app, + "Window", + true, + &[ + &PredefinedMenuItem::minimize(app, None)?, + &PredefinedMenuItem::maximize(app, None)?, + ], + )?, + ], + ) +} diff --git a/src/App.vue b/src/App.vue index 3f91fd4..b0818e4 100644 --- a/src/App.vue +++ b/src/App.vue @@ -8,6 +8,7 @@ import { SidebarProvider, SidebarInset } from '@/components/ui/sidebar'; import ConnectionGrid from '@/components/connection/ConnectionGrid.vue'; import { useConnectionStore } from '@/stores/connectionStore'; import TabsContainer from './components/tabs/TabsContainer.vue'; +import ConfirmDialog from '@/components/ConfirmDialog.vue'; const store = useConnectionStore(); @@ -18,11 +19,12 @@ onMounted(() => {