Compare commits

...
34 Commits
Author SHA1 Message Date
github-actions[bot] 06e7db85dc Bump versions for v13.11 2026-11-13 18:11:28 +00:00
Lauri Ojansivu a97fde20c9 Prepare v13.11 release 2026-11-13 20:10:27 +02:00
Lauri Ojansivu a22dbc5b34 CHANGELOG: REST integration deletion answers 404 for a missing integration.
Thanks to AhmedLukman and xet7 !
2026-11-13 19:43:09 +02:00
Lauri Ojansivu cbe8a56d9d Merge pull request #6900 from AhmedLukman/codex/fix-integration-delete-not-found
Fix integration deletion acknowledging missing records
2026-11-13 19:41:28 +02:00
Ahmed Lukman ab685a7f6f Fix integration deletion acknowledging missing records
Reuse the board-scoped integration lookup before deleting, after board-admin
authorization. Missing, wrong-board and already deleted integrations now return
the existing JSON 404 response instead of acknowledging an unmatched selector.

Extend route regressions for deletion, authorization and database errors. Add
live API and browser coverage for rejected and successful deletions, persisted
state and retention of other webhooks in settings.
2026-11-13 19:01:54 +03:00
Lauri Ojansivu 24d5b2cdfd CHANGELOG: file-integrity baselines of deleted files, the checklist resetInterval index, REST custom-field and ownership fixes.
Thanks to Raymond-Voogt, AhmedLukman and xet7 !
2026-11-13 17:45:45 +02:00
Lauri Ojansivu ec05ff0089 File-integrity scan no longer reports files WeKan deleted itself as missing.
The daily scan keeps one baseline per stored file and reports every baseline
whose file is gone as a StorageBleed. Nothing ever removed a baseline, so
every attachment purged by retention and every avatar deleted was reported
missing on every run from then on - one finding folded 1,164 times.

The baseline is now forgotten where WeKan deletes or moves a local file:
- Attachments and Avatars onAfterRemove, for every version path. This is
  the retention purge's path: Meteor-Files unlinks the version paths itself
  after that hook, without going through the store strategy, so a fix in
  the strategy's unlink() alone would not have covered it;
- the filesystem strategy's unlink() (moving a file to GridFS or cloud);
- the filesystem strategy's rename(), for the old path.

A file that vanishes any other way keeps its baseline and is still reported.
The removal is fire-and-forget and can never break a delete.

Baselines already orphaned before this fix stay reported until removed, as
described in the issue.

Thanks to Raymond-Voogt and xet7 !

Fixes #6898.
2026-11-13 17:45:05 +02:00
Lauri Ojansivu c37575f1fa Checklists have a partial index on resetInterval for the recurring-reset cron.
The recurring-checklist reset cron queries
resetInterval: { $exists: true, $nin: [null, 'none'] } on every tick, and with
no index that read the whole checklists collection (about 45,000 documents on
the reporting instance), reported as db.collection-scan on Admin Panel ->
Problems. A partial index on resetInterval, like the one #6825 added for
recurring cards, holds only the recurring checklists.

Thanks to Raymond-Voogt and xet7 !

Fixes #6899.
2026-11-13 17:45:05 +02:00
Lauri Ojansivu a8e25952aa REST takeOwnership awaits the board update, and removeMember awaits its user lookup.
PUT /api/users/:userId with action takeOwnership called the async
board.changeOwnership() without await: the reply was sent before the board
was changed, and a failed update was an unhandled rejection behind a 200.

Boards.removeMember tested `!ReactiveCache.getUser(memberId)`. On the server
that lookup is async and returns a Promise, which is always truthy, so the
#5330 branch that hard-removes a member whose account was deleted never ran
on the server; such a member was only deactivated and lingered.

Tests pin both, and fail the tree for any un-awaited changeOwnership() or
negated un-awaited ReactiveCache lookup in server/ and models/.

Thanks to xet7 !
2026-11-13 17:45:05 +02:00
Lauri Ojansivu 22bf506ea1 Merge pull request #6896 from AhmedLukman/codex/fix-ownership-board-selection
Fix ownership transfer selecting ordinary board memberships
2026-11-13 17:23:36 +02:00
Lauri Ojansivu 41ff6da28d Merge pull request #6897 from AhmedLukman/codex/fix-custom-field-update-atomicity
Fix partial REST custom-field updates
2026-11-13 17:23:19 +02:00
Ahmed Lukman a81ec6ecdf Fix partial REST custom-field updates
Save supported custom-field properties in one board-scoped update so schema validation rejects the entire change before any accompanying fields persist. Preserve the existing authorization, field allowlist and response behavior.

Add handler regressions and live database/browser coverage for rejected settings and types, accepted updates, false values, omitted fields and board scoping.
2026-11-13 17:00:17 +03:00
Ahmed Lukman 4b4db92a1b Fix ownership transfer selecting ordinary board memberships
Match the target user and administrator flag within the same board member, following the existing membership query pattern. Leave boards where the target is only a normal member unchanged.

Cover selection, successful transfers, unchanged unrelated memberships, repeated requests, authorization, and retained browser access.
2026-11-13 11:51:40 +03:00
github-actions[bot] e2bf75b774 Bump versions for v13.10 2026-11-13 04:11:48 +00:00
Lauri Ojansivu d6c7dbcde2 Prepare v13.10 release 2026-11-13 06:10:46 +02:00
Lauri Ojansivu 8b44646314 CHANGELOG: every snapcraft install attempt is bounded, and snap-auth-check has room for all five.
Thanks to xet7 !
2026-11-13 06:06:16 +02:00
Lauri Ojansivu 560ff74a79 Release: every snapcraft install attempt is bounded, and snap-auth-check has room for all five.
In the v13.09 release the Snap Store failed snap-auth-check's first two
install attempts and then let the third hang with no output, until the job's
10-minute timeout cancelled it - the retry added for v13.07 never got its
remaining turns. Each `snap install snapcraft` in release-all.yml now runs
under `timeout 180`, and an attempt cut short is aborted (`snap abort
--last=install`) so the next one does not meet snapd's change still in
progress. snap-auth-check's job timeout is 25 minutes, enough for five
attempts 30 seconds apart and the whoami after them.

tests/snapAuthPreflight.test.cjs requires every install in the workflows to
be bounded and followed by the abort, and fails when snap-auth-check's job
timeout cannot hold all five attempts.

Thanks to xet7 !
2026-11-13 06:05:56 +02:00
github-actions[bot] 7347ae8f9d Bump versions for v13.09 2026-11-12 19:07:47 +00:00
Lauri Ojansivu 6cbb026f14 Prepare v13.09 release 2026-11-12 21:06:44 +02:00
Lauri Ojansivu 80253cf5e1 Merge remote-tracking branch 'origin/main' 2026-11-12 21:06:21 +02:00
Lauri Ojansivu d8fc23a4f4 CHANGELOG: snap-auth-check retries installing snapcraft.
Thanks to xet7 !
2026-11-12 21:02:13 +02:00
Lauri Ojansivu 534646347c Release: snap-auth-check retries installing snapcraft, as the build job does.
The v13.08 release stopped in snap-auth-check before it asked the Snap Store
anything: "cannot install system snap snapd: Post
https://api.snapcraft.io/v2/snaps/refresh: ... Client.Timeout exceeded while
awaiting headers". The job installed snapcraft once, while the snap build
job already retried five times, 30 seconds apart. It retries the same way now,
and when every attempt fails it says the store did not answer - not that
SNAP_AUTH is wrong.

tests/snapAuthPreflight.test.cjs fails on any snapcraft install in any
workflow without the five-attempt retry, and on the outage being reported as
a credential problem.

Thanks to xet7 !
2026-11-12 21:01:37 +02:00
github-actions[bot] 779ba553ce Bump versions for v13.08 2026-11-12 17:15:00 +00:00
Lauri Ojansivu 3144a578b5 Prepare v13.08 release 2026-11-12 19:13:48 +02:00
Lauri Ojansivu 82a30561d7 CHANGELOG: list-sync-estimate's checkbox test and index order.
Thanks to xet7 !
2026-11-12 17:03:34 +02:00
Lauri Ojansivu af76406d12 Tests: list-sync-estimate's checkbox test no longer depends on index order.
"dotted checkbox writes emit false once and removing a valued field emits an
unset" read the card's activities with an unsorted find and expected them in
insertion order. On FerretDB with the pushdown of plain equality, that find is
served by activities' { cardId: 1, createdAt: -1 } index and returns the newest
first, so it failed every time against Weddy, which creates WeKan's indexes,
with ['unsetCustomField', 'setCustomField']. The order of an unsorted find is
no contract on MongoDB either once an index serves it; what the test pins is
one set and one unset, so it now compares the activity types sorted.

Thanks to xet7 !
2026-11-12 17:03:33 +02:00
Lauri Ojansivu f57ea060fe CHANGELOG: CodeQL alert 604 in the security alert download script.
Thanks to GitHub CodeQL and xet7 !
2026-11-12 16:07:40 +02:00
Lauri Ojansivu ed19817433 releases/sec: SUMMARY.md carries no secret scanning field, and kept secrets are owner-only.
Code scanning alert #604 (py/clear-text-storage-sensitive-data, CWE-312):
download_github_security.py wrote a secret scanning alert's
secret_type_display_name into SUMMARY.md. That is the secret's type, not the
secret, but SUMMARY.md is the one file that is never redacted and the one most
likely to be pasted somewhere, so it now says only "Secret scanning alert" and
points at secret-scanning/, where the type still is. When
--include-secret-values keeps the secrets, the files holding them are created
readable by their owner only.

tests/securityDownloadScript.test.cjs checks the summary of a redacted and of a
kept download for the secret, its type and its type name, the 0600 mode of
kept secrets, and that describe() reads no field of a secret scanning alert
and every secret scanning write passes the private flag.

Thanks to GitHub CodeQL and xet7 !
2026-11-12 16:07:39 +02:00
Lauri Ojansivu 2b897dbf63 CHANGELOG: REST card parent, integration URL validation, and the single-file 32-bit host Node.js.
Thanks to AhmedLukman and xet7 !
2026-11-12 16:06:09 +02:00
Lauri Ojansivu c35c672ffb Single-file: the 32-bit host Node.js is the target bundle's exact version.
The v13.07 single files for armhf, armv6 and armv7 all stopped on "The
bundle's Node.js is v26.13.0 but this is v26.13.1: a single executable must
be built by the same Node.js version it embeds." A 32-bit target's SEA blob is
written by a 32-bit Node.js, and the workflow borrowed the i386 bundle's.
Every bundle takes the newest Node.js of the pinned major its platform has, so
i386 had v26.13.1 from wekan/node-patches while 32-bit ARM still had v26.13.0.

The host is now resolved with releases/resolve-node-source.sh at the target
bundle's exact version, checked against its published SHA256, and refused
unless it reports that version. libatomic1:i386, which the bundle used to
supply in node-runtime/, comes from apt, so HOST_LIBS is gone.

tests/singleFile.test.cjs pins the exact version, the checksum before the
first run, and that the host is never taken from another platform's bundle.

Thanks to xet7 !
2026-11-12 16:05:46 +02:00
Lauri Ojansivu 642ba6e876 Merge pull request #6895 from AhmedLukman/codex/fix-integration-update-validation
Validate integration URLs before updating other fields
2026-11-12 15:59:53 +02:00
Lauri Ojansivu e9c5aa2cae Merge pull request #6894 from AhmedLukman/codex/fix-rest-card-parent
Fix parent assignment when creating cards through REST
2026-11-12 15:59:37 +02:00
Ahmed Lukman 1418cb8480 Validate integration URLs before updating other fields 2026-11-12 13:21:00 +03:00
Ahmed Lukman 552950ed63 Fix parent assignment when creating cards through REST
Read parentId from the request body so single-card creation validates and persists the requested parent, matching the bulk endpoint. Cover persistence, subtask rendering, empty parents and rejected creation requests.
2026-11-12 12:48:09 +03:00
44 changed files with 2275 additions and 791 deletions

No files matched your search

+26 -4
View File
@@ -4533,7 +4533,10 @@ jobs:
snap-auth-check:
needs: [prepare]
runs-on: ubuntu-24.04
timeout-minutes: 10
# Room for all five snapcraft install attempts (each at most 3 minutes,
# 30 s apart) and the whoami after them: at 10 minutes (v13.09) the job
# was cancelled during the third.
timeout-minutes: 25
steps:
- name: Ask the Snap Store whether SNAP_AUTH is usable
env:
@@ -4557,7 +4560,23 @@ jobs:
;;
esac
sudo snap install snapcraft --classic
# The Snap Store has bad minutes - a rate limit, or (v13.07) a refresh
# that timed out - so the install is retried as the build job's is;
# an outage is said as one, not as a broken credential. Each attempt
# is bounded: in v13.09 the third hung silently until the job was
# cancelled. An attempt cut short leaves snapd's install change
# running, so it is aborted before the next.
installed=""
for i in 1 2 3 4 5; do
if timeout 180 sudo snap install snapcraft --classic; then installed=1; break; fi
sudo snap abort --last=install >/dev/null 2>&1 || true
echo "snapcraft install attempt $i/5 failed (Snap Store unreachable, throttling or hanging); retrying in 30s..."
sleep 30
done
if [ -z "$installed" ]; then
echo "::error::snapcraft could not be installed after 5 attempts: the Snap Store did not answer. That is the store, not SNAP_AUTH - re-run the job."
exit 1
fi
# whoami writes its answer to stdout and its complaint to stderr; both
# matter, so both are captured.
@@ -4954,8 +4973,11 @@ jobs:
run: |
set -uo pipefail
for i in 1 2 3 4 5; do
sudo snap install snapcraft --classic && exit 0
echo "snapcraft install attempt $i/5 failed (Snap Store throttle?); retrying in 30s..."
# Bounded, as snap-auth-check's: a hanging store is a failed
# attempt, not a job that waits until it is cancelled.
timeout 180 sudo snap install snapcraft --classic && exit 0
sudo snap abort --last=install >/dev/null 2>&1 || true
echo "snapcraft install attempt $i/5 failed (Snap Store throttle or hang?); retrying in 30s..."
sleep 30
done
echo "::error::snapcraft install failed after 5 attempts (Snap Store rate limit)."
+33 -18
View File
@@ -150,27 +150,46 @@ jobs:
node-version: ${{ steps.fetch.outputs.node_version }}
# A 32-bit target's SEA blob must be written by a 32-bit Node.js: the blob
# stores its lengths as size_t (build.mjs refuses otherwise). The i386
# bundle's Node.js, of the same version, writes it for every 32-bit
# target - win32, i386 and 32-bit ARM read the same little-endian format.
# stores its lengths as size_t (build.mjs refuses otherwise). An i386
# Node.js writes it for every 32-bit target - win32, i386 and 32-bit ARM
# read the same little-endian format - but it has to be the SAME VERSION
# the target's bundle embeds (build.mjs checks that too). v13.07 took the
# i386 bundle's own Node.js, and the bundles resolve the newest Node.js of
# the pinned major per platform: i386 got v26.13.1 while 32-bit ARM still
# had v26.13.0, and armhf, armv6 and armv7 all stopped on "The bundle's
# Node.js is v26.13.0 but this is v26.13.1". So the host is resolved at
# the target bundle's exact version, from the same three sources and with
# the same published SHA256 as a bundle's Node.js.
- name: A 32-bit Node.js to build a 32-bit target
id: host32
if: steps.fetch.outputs.skip != 'true' && contains(fromJSON('["win32","i386","armv7","armhf","armv6"]'), matrix.target)
env:
GH_TOKEN: ${{ github.token }}
VERSION: ${{ steps.fetch.outputs.version }}
NODE_VERSION: ${{ steps.fetch.outputs.node_version }}
run: |
set -euo pipefail
zip="wekan-${VERSION}-i386.zip"
mkdir -p host32
gh release download "$TAG" --repo "$GITHUB_REPOSITORY" --pattern "$zip" --pattern "$zip.sha256sum" --dir host32 \
|| { echo "::error::$TARGET needs the i386 bundle's Node.js to build, and $TAG has no $zip."; exit 1; }
(cd host32 && sha256sum -c "$zip.sha256sum" && unzip -q "$zip" 'bundle/node' 'bundle/node-runtime/*' && rm -f "$zip")
mkdir -p host32 .tools/tmp
export TMPDIR="$PWD/.tools/tmp"
rc=0
src="$(bash releases/resolve-node-source.sh i386 "$NODE_VERSION")" || rc=$?
[ "$rc" = 0 ] || { echo "::error::$TARGET needs an i386 Node.js $NODE_VERSION to build its single file, and none of the three Node.js sources has one."; exit 1; }
node_url="$(printf '%s\n' "$src" | sed -n 's/^node_url=//p')"
node_kind="$(printf '%s\n' "$src" | sed -n 's/^node_kind=//p')"
node_member="$(printf '%s\n' "$src" | sed -n 's/^node_member=//p')"
node_sha256="$(printf '%s\n' "$src" | sed -n 's/^node_sha256=//p')"
bash releases/fetch.sh -o host32/download "$node_url"
echo "$node_sha256 host32/download" | sha256sum -c -
case "$node_kind" in
binary) mv host32/download host32/node ;;
tar.xz) tar -xJf host32/download -C host32 "$node_member" && mv "host32/$node_member" host32/node && rm -f host32/download ;;
*) echo "::error::An i386 Node.js comes as a binary or a tar.xz, not $node_kind."; exit 1 ;;
esac
chmod +x host32/node
sudo dpkg --add-architecture i386
sudo apt-get update -qq
sudo apt-get install -y -qq libc6:i386 libstdc++6:i386 >/dev/null
host="$PWD/host32/bundle/node"
[ -d host32/bundle/node-runtime ] && echo "libs=$PWD/host32/bundle/node-runtime" >> "$GITHUB_OUTPUT"
sudo apt-get install -y -qq libc6:i386 libstdc++6:i386 libatomic1:i386 >/dev/null
host="$PWD/host32/node"
got="$("$host" -p process.version)"
[ "$got" = "$NODE_VERSION" ] || { echo "::error::The i386 Node.js resolved for $NODE_VERSION reports $got."; exit 1; }
echo "node=$host" >> "$GITHUB_OUTPUT"
# A 32-bit ELF target's blob is injected in a 64-bit process, by
# LIEF 1.0.0 - the version Node.js vendors - not inside this 32-bit
@@ -187,20 +206,16 @@ jobs:
env:
VERSION: ${{ steps.fetch.outputs.version }}
HOST_NODE: ${{ steps.host32.outputs.node }}
HOST_LIBS: ${{ steps.host32.outputs.libs }}
SEA_PYTHON: ${{ steps.host32.outputs.python }}
run: |
set -euo pipefail
case "$TARGET" in win*) ext=.exe ;; *) ext= ;; esac
out="wekan-${VERSION}-${TARGET}-single-file${ext}"
# The i386 Node.js finds its own libraries; the libraries are ignored by
# the x64 Node.js that runs build.mjs (another architecture).
# Exit 3: this Node.js is one LIEF cannot inject the SEA into
# correctly (releases/single-file/sea-inject.mjs says which and why).
# That is a missing single file, not a failed release.
rc=0
LD_LIBRARY_PATH="${HOST_LIBS:-${LD_LIBRARY_PATH:-}}" \
node releases/single-file/build.mjs --bundle bundle --target "$TARGET" --version "$VERSION" --out "$out" \
node releases/single-file/build.mjs --bundle bundle --target "$TARGET" --version "$VERSION" --out "$out" \
${HOST_NODE:+--host-node "$HOST_NODE"} 2> build.err || rc=$?
cat build.err >&2
rm -rf bundle
+235 -213
View File
@@ -124,6 +124,241 @@ its reason.
</details>
# v13.11 2026-11-13 WeKan ® release
**In short:** The **file-integrity scan** stops reporting attachments and
avatars that WeKan deleted itself as missing, which had buried real
**StorageBleed** findings under thousands of false ones. The recurring-checklist
reset no longer scans every **checklist** on each tick. In the **REST API**, a
refused custom-field update changes nothing, ownership transfer takes only the
boards the user administers, and deleting a missing integration answers 404.
This release fixes the following bugs:
**The file-integrity scan** - what it counts as a missing file.
<details>
<summary><a href="https://github.com/wekan/wekan/commit/ec05ff008928c390b11fec225c564f2dd209ec7f">Files WeKan deleted or moved itself are no longer reported missing</a>. Thanks to Raymond-Voogt and xet7.</summary>
The daily scan keeps one baseline per stored file and reports a baseline whose
file is gone as a StorageBleed. Nothing removed a baseline, so every attachment
the retention purge deleted and every avatar a user deleted was reported on
every run from then on; one finding was folded 1,164 times. The baseline is now
forgotten in Attachments and Avatars `onAfterRemove`, for every version path.
That is the retention purge's path: Meteor-Files unlinks the files itself after
that hook, without going through the store strategy. It is also forgotten in
the filesystem strategy's `unlink()` and, for the old path, in `rename()`. A
file that vanishes any other way keeps its baseline and is still reported.
Baselines orphaned before this fix stay until removed, as
[#6898](https://github.com/wekan/wekan/issues/6898) describes.
`tests/integrityBaselineForgottenOnDelete.test.cjs` fails on a delete or rename
in the filesystem strategy that does not forget its baseline, and
`tests/playwright/specs/integrity-baseline-on-delete.e2e.js` deletes an avatar
in the UI.
</details>
**Database indexes** - the queries the background jobs make.
<details>
<summary><a href="https://github.com/wekan/wekan/commit/c37575f1faff4e8ca518c7eae0ba7e50d05c1fae">The recurring-checklist reset reads only recurring checklists</a>. Thanks to Raymond-Voogt and xet7.</summary>
The reset cron asks for every checklist with a `resetInterval` on each tick,
and with no index that read the whole collection (about 45,000 documents on the
reporting instance), shown as a collection scan on Admin Panel / Problems. A
partial index on `resetInterval`, like the one recurring cards got, holds only
the recurring checklists. `tests/checklistResetIndex.test.cjs` pins the index
and a selector that can use it.
</details>
**The REST API** - custom fields, ownership transfer and integrations.
<details>
<summary><a href="https://github.com/wekan/wekan/commit/a81ec6ecdfea8a0b0709d7c0cd463cfd051ca80f">A refused custom-field update leaves the whole field unchanged</a>. Thanks to AhmedLukman and xet7.</summary>
`PUT /api/boards/:boardId/custom-fields/:customFieldId` wrote each property
with its own update, so a refused `type` or `settings` left the name and flags
sent before it already saved. The properties are now saved in one board-scoped
update that the schema accepts or refuses as a whole. Authorization, the field
allowlist and the reply are unchanged. `tests/restCustomFieldUpdate.test.cjs`
and `tests/playwright/specs/rest-custom-field-update.e2e.js` cover both.
</details>
<details>
<summary><a href="https://github.com/wekan/wekan/commit/4b4db92a1b97eae43770e56082012073d87c4c9a">Ownership transfer takes only the boards the user administers</a>. Thanks to AhmedLukman and xet7.</summary>
`takeOwnership` selected boards where the user was a member and somebody, not
necessarily the user, was an administrator. The user and the administrator flag
are now matched on the same member, so a board where the user is an ordinary
member is left as it is. `tests/restOwnershipSelection.test.cjs` and
`tests/playwright/specs/rest-ownership-selection.e2e.js` cover it.
</details>
<details>
<summary><a href="https://github.com/wekan/wekan/commit/ab685a7f6fe0450a31b9150a14b17b909933c86f">Deleting a missing or another board's integration answers 404</a>. Thanks to AhmedLukman and xet7.</summary>
`DELETE /api/boards/:boardId/integrations/:intId` answered 200 for an
integration that did not exist, was on another board or was already deleted,
because the removal matched nothing and the reply did not check. It now looks
the integration up on that board first, after the board-admin check, like PUT
and the activities routes, and answers the same JSON 404. Server errors still
answer 500. `tests/restIntegrationWriteRoutes404.test.cjs` and
`tests/playwright/specs/rest-integration-update.e2e.js` cover it, including
the board's webhook settings.
</details>
<details>
<summary><a href="https://github.com/wekan/wekan/commit/a8e25952aa6b244b76891c3fa3cdc1779d35a78f">Ownership transfer finishes before it replies, and a deleted account leaves the board when removed</a>. Thanks to xet7.</summary>
`takeOwnership` called the async `changeOwnership()` without `await`, so the
reply was sent before the board changed and a failed update was an unhandled
rejection behind a 200. `Boards.removeMember` tested
`!ReactiveCache.getUser(id)`, which on the server is a Promise and always
truthy. Because of that, the branch that removes a member whose account was
deleted never ran there, and that member only lingered as inactive. Both
calls are awaited now. `tests/reactiveCacheNegatedAwait.test.cjs` fails on any
negated un-awaited `ReactiveCache` lookup in `server/` or `models/`.
</details>
Thanks to above GitHub users for their contributions and translators for their translations.
# v13.10 2026-11-13 WeKan ® release
**In short:** The release's **Snap Store** credential check bounds each
snapcraft install attempt, so a Snap Store that hangs no longer stops a release.
This release has the following developer-tooling fix:
**Release builds** - the snap jobs.
<details>
<summary><a href="https://github.com/wekan/wekan/commit/560ff74a79f8c998bd0aa4fd63664155eecd5f28">Every snapcraft install attempt is bounded, and snap-auth-check has room for all five</a>. Thanks to xet7.</summary>
In the v13.09 release the Snap Store failed snap-auth-check's first two
snapcraft install attempts and then let the third hang with no output, until
the job's 10-minute timeout cancelled it - the retry never got its remaining
turns. Each `snap install snapcraft` in the release workflow now runs under
`timeout 180`, an attempt cut short is aborted (`snap abort --last=install`)
so the next one does not meet snapd's change still in progress, and
snap-auth-check's job timeout is 25 minutes, enough for all five attempts.
`tests/snapAuthPreflight.test.cjs` fails on an unbounded install, a retry
without the abort, or a job timeout that cannot hold five attempts.
</details>
Thanks to above GitHub users for their contributions and translators for their translations.
# v13.09 2026-11-12 WeKan ® release
**In short:** The release's **Snap Store** credential check retries installing
snapcraft, so one Snap Store timeout no longer stops a release.
This release has the following developer-tooling fix:
**Release builds** - the snap jobs.
<details>
<summary><a href="https://github.com/wekan/wekan/commit/534646347c4b6467f539cb01171487d27fc7ddc7">snap-auth-check retries installing snapcraft, as the snap build job does</a>. Thanks to xet7.</summary>
The v13.08 release stopped in snap-auth-check before it asked the Snap Store
anything: the one `snap install snapcraft` timed out reaching api.snapcraft.io,
while the snap build job already retried five times. The check retries the
same way now, and when every attempt fails it says the store did not answer
rather than that SNAP_AUTH is wrong. `tests/snapAuthPreflight.test.cjs` fails on
any snapcraft install in any workflow without the retry.
</details>
Thanks to above GitHub users for their contributions and translators for their translations.
# v13.08 2026-11-12 WeKan ® release
**In short:** The **REST API** creates a card under the **parent** it is given,
and a refused **integration URL** no longer leaves the other fields of the same
update written. The **single-file** WeKan for **32-bit ARM** builds again, with
an i386 Node.js of the exact version its bundle embeds. A **GitHub CodeQL**
alert in the security alert download script is fixed.
This release fixes the following SECURITY ISSUES found by GitHub CodeQL code scanning:
<details>
<summary><a href="https://github.com/wekan/wekan/commit/ed19817433e14ef4f8cb7d9623f5d8b84a1794ff">The security alert download summary carries no secret scanning field, and kept secrets are owner-only</a>. Thanks to GitHub CodeQL and xet7.</summary>
Code scanning alert #604 (`py/clear-text-storage-sensitive-data`, CWE-312):
`releases/sec/download_github_security.py` wrote a secret scanning alert's type
name into `SUMMARY.md`. That was the type of the secret, not the secret, but
`SUMMARY.md` is never redacted, so it now names only the kind of finding and
points at `secret-scanning/`. With `--include-secret-values` the files that hold
the secrets are created readable by their owner only.
`tests/securityDownloadScript.test.cjs` pins both, and that no secret scanning
field reaches the summary. This is a maintainer tool, not WeKan's server, so
there is no attempt for Admin Panel → Problems to record.
</details>
and fixes the following bugs:
**The REST API** - creating cards and updating integrations.
<details>
<summary><a href="https://github.com/wekan/wekan/commit/552950ed637d6ddeb486322e3c08dd61dbfc13dc">A card created with a parentId is created under that parent</a>. Thanks to AhmedLukman and xet7.</summary>
`POST /api/boards/:boardId/lists/:listId/cards` read `parentId` from the URL
parameters, which that route does not have, so the parent in the request body
was ignored: it was never stored, and the visibility check for the parent
(GHSA-jvv9-498p-hxrg) never saw it. It is read from the body now, as the bulk
endpoint does. A parentId that is not a string is refused with 400, and a parent
on a board the caller cannot see is still refused and recorded under
ParentBleed. `tests/restCardParent.test.cjs` and
`tests/playwright/specs/rest-card-parent.e2e.js` cover a stored parent, the
subtask it renders as, an empty parent and the refused requests.
</details>
<details>
<summary><a href="https://github.com/wekan/wekan/commit/1418cb8480e0ca13ccecc54290b16c36d40f82a6">An integration update with a refused URL changes nothing</a>. Thanks to AhmedLukman and xet7.</summary>
`PUT /api/boards/:boardId/integrations/:intId` wrote `enabled` and `title`
before it checked the new URL against the SSRF rules (GHSA-hc3x-hq3m-663q), so a
request whose URL was refused with 400 had already changed those two fields. The
URL is validated first now, like the delivery settings, and a refused request
leaves the integration as it was. `tests/restIntegrationUpdate.test.cjs` and its
Playwright spec pin the order.
</details>
and has the following developer-tooling fixes:
**Release builds** - the single-file executables.
<details>
<summary><a href="https://github.com/wekan/wekan/commit/c35c672ffb398b68be22f257087db28738eb4701">The 32-bit host Node.js of a single file is its bundle's exact version</a>. Thanks to xet7.</summary>
The v13.07 single files for armhf, armv6 and armv7 stopped on "The bundle's
Node.js is v26.13.0 but this is v26.13.1". A 32-bit target's SEA blob is
written by a 32-bit Node.js, and `.github/workflows/single-file.yml` borrowed
the i386 bundle's. Each bundle takes the newest Node.js of the pinned major its
platform has, so i386 had v26.13.1 while 32-bit ARM had v26.13.0. The host is
now resolved with `releases/resolve-node-source.sh` at the target bundle's exact
version, checked against its published SHA256, and refused unless it reports
that version. `tests/singleFile.test.cjs` pins all three, and that the host is
never taken from another platform's bundle.
</details>
**Browser tests** - what a spec may rely on from the database.
- [list-sync-estimate's checkbox test no longer depends on the order an index returns](https://github.com/wekan/wekan/commit/af76406d127568b45b0e76659160a8821cdf00a2) -
it expected an unsorted find of activities in insertion order, which
activities' cardId/createdAt index returns newest first. Thanks to xet7.
Thanks to above GitHub users for their contributions and translators for their translations.
# v13.07 2026-11-12 WeKan ® release
**In short:** **ListLockAttachBleed** is fixed: when a list refuses a role
@@ -8393,216 +8628,3 @@ plan.
</details>
Thanks to above GitHub users for their contributions and translators for their translations.
# v12.42 2026-10-14 WeKan ® release
**In short:** a new **Version Control** board view shows a board as a
**GitHub** or **Gitea** issue tracker, with releases, a **wiki** and history
with undo, and a whole **GitHub organization** imports into it, its **GitHub
Actions** as Rules. Optional **Trello**, **Jira**, **GitHub** and **Gitea**
compatible APIs let their clients use WeKan as a backend. Old boards get their
keys in one pass, list sync keeps issue details current, any board view can be
a **read-only public view**, and WeKan has **Android** bundles.
This release adds the following new features:
**Board views** - a repository's issue tracker as a view, and which views the
public sees.
<details>
<summary><a href="https://github.com/wekan/wekan/commit/326d9bbf61">Version Control: the board as a GitHub or Gitea issue tracker</a>. Thanks to xet7.</summary>
Board → View → **Version Control**, in the board's own theme. A card is an
issue: open, or closed when archived, in a list named *Closed*, *Done*,
*Resolved*, *Merged* or *Wontfix*, or closed upstream. The issue number is
the upstream one of an imported issue, else the card number. The board's
labels are labels, and labels with a due date and the importers'
`milestone:` labels are milestones, with progress bars. Comments are the
conversation, attachments the files. The search box takes GitHub's words:
`is:open`, `label:`, `-label:`, `milestone:`, `author:`, `assignee:@me`,
`no:label` and `sort:`. **New issue**, **Close** and **Reopen** write through
the ordinary collections, so the **History** tab (the board's history table)
shows them and **Restore** undoes them. A synced issue is closed in its
tracker. **Import** opens the GitHub, GitLab, Gitea and Forgejo importers, and
each synced list has a **Sync now** button. Visitors see it read-only. Tested
by `tests/versionControlView.test.cjs`. See
[Version Control view](docs/Features/Board/Version-Control-View.md).
</details>
<details>
<summary><a href="https://github.com/wekan/wekan/commit/326d9bbf61">A public board's public views are for the public; its members keep theirs</a>. Thanks to xet7.</summary>
Board Settings → Board View's *Show on* and *Default on Public Board* now
apply to the people looking who are not members: visitors who are not logged
in, and logged-in users who are not on the board. The board's members get the
*Private* columns. So a public board can open read-only in Version Control,
Email, Chat or any other view for the world, and list only the views chosen
for it, while its members keep every view. Before, a public board's members
had the public menu too. `boardViewAudience` in
`models/lib/boardViewSettings.js`; `tests/boardViewSettings.test.cjs` covers
visitors, non-members, former members and members.
</details>
**Import and sync** - what an issue tracker's issues bring with them.
<details>
<summary><a href="https://github.com/wekan/wekan/commit/326d9bbf61">GitHub, GitLab, Gitea and Forgejo issues bring their files</a>. Thanks to xet7.</summary>
The files an issue or its comments link or carry become card attachments in
Admin Panel → Attachments → Default Storage. These are GitHub
`user-attachments` and `user-images` links, GitLab `/uploads/` links, and
Gitea and Forgejo assets and `/attachments/<uuid>` links, at most 50 per
issue. Each one is downloaded through the import's SSRF guard, which checks
every redirect hop, and the upload checks its type and size. One that cannot
be fetched is on the import's loss report. Ordinary links are not files.
`models/lib/issueAttachments.js`; `tests/issueAttachments.test.cjs`.
</details>
<details>
<summary><a href="https://github.com/wekan/wekan/commit/326d9bbf61">List sync can bring each issue's comments, labels, state and files</a>. Thanks to xet7.</summary>
A new Sync option for GitHub, GitLab, Gitea and Forgejo, off by default.
Comments come from one paged repository-wide list (GitHub and Gitea), or
from each issue's notes (GitLab). They are keyed by the tracker's comment id,
and an edit upstream updates them. A label sync added and the issue lost is
taken off, and labels added in WeKan stay. Each file is downloaded once. It
runs after a successful sync as a step of its own, so a failure is shown and
retried and never undoes the title and description sync. Gitea issues are
fetched with `type=issues`. A rate-limited answer is waited out once when it
frees up within a minute; otherwise the run ends with the time to try again.
`tests/listSyncIssueDetails.test.cjs`.
</details>
<details>
<summary><a href="https://github.com/wekan/wekan/commit/935e68dfdd">A whole GitHub organization imports, each repository as its own board</a>. Thanks to xet7.</summary>
Import → GitHub → **Import a whole GitHub organization**: every repository of
an organization or user becomes a board named `owner/repository`, opening in
Version Control. Issues go through the ordinary `importBoard`, run as the
person who started the job, with their comments, labels and files. Releases
are cards in a *Releases* list, with their files attached only when chosen.
`.github/workflows` files become Rules. Each wiki is read from its git
repository with isomorphic-git (1.42.4, pinned), with every request through
the SSRF guard and only to github.com. A page is a card in the *Wiki* list:
its `[[links]]` point at the linked pages' cards and are attached to them,
and each commit that changed it is a dated edit in the card's history, so
**Restore** brings back an older version. Version Control gains **Releases**
and **Wiki** tabs, the wiki laid out as on GitHub. The job shows progress, can
be cancelled, pauses on a restart and stops at a rate limit. The token is
kept in memory only. Tests: `tests/githubOrgImport.test.cjs`,
`tests/githubWiki.test.cjs` and `tests/workflowYaml.test.cjs`, the last
checking 52 real workflow files against js-yaml.
</details>
**Rules** - what GitHub Actions workflows needed, for everyone.
<details>
<summary><a href="https://github.com/wekan/wekan/commit/935e68dfdd">GitHub Actions workflows become Rules; Rules gain comments, cron and descriptions</a>. Thanks to xet7.</summary>
Issue events become triggers. Label, close, reopen, comment, assign and
create steps become actions, from the well-known actions or from `gh issue`
commands, and `actions/stale` becomes an aging rule. A build, test or script
is never run: such a workflow is a disabled rule with its YAML as its
description. For this, WeKan's Rules gain several parts, available to every
board:
- a **commented on** trigger;
- an **add comment** action, written as the person whose change ran the rule
and only where they may comment;
- **cron schedules** in UTC (`models/lib/cronSchedule.js`);
- a rule **description**, shown on the rule.
Tests: `tests/githubActionsRules.test.cjs`, `tests/cronSchedule.test.cjs` and
`tests/ruleCommentCron.test.cjs`.
</details>
**Optional APIs** - other trackers' clients, with WeKan as their backend.
<details>
<summary><a href="https://github.com/wekan/wekan/commit/86ec72fade">Trello, Jira, GitHub and Gitea compatible APIs, off until an admin ticks them</a>. Thanks to xet7.</summary>
Admin Panel → Settings → Visibility → **Optional APIs** turns on REST APIs
in Trello's, Jira's, GitHub's and Gitea's shape (#4930), each described with
the address a client uses. None has rights of its own: each translates a
request into the matching request to WeKan's own REST API, made with the
caller's WeKan token, so every WeKan permission applies unchanged. An API that
is not ticked answers 404, and `WITH_API` turns them all off. Trello covers
members, boards, lists, cards, labels, comments and checklists. In Jira a
board is a project (the board key), a card is an issue (`DEV-7`), lists are
statuses, and simple JQL works. In GitHub and Gitea a board is a repository and
a card an issue, open or closed as in Version Control. Deleting archives. See
[Optional APIs](docs/API/Optional-APIs.md). `tests/optionalApis.test.cjs`
fails if any of them writes past WeKan's REST API.
`server/lib/tests/compatApi.tests.js` runs them over real HTTP.
</details>
**Cards** - opening a card on a phone.
<details>
<summary><a href="https://github.com/wekan/wekan/commit/326d9bbf61">A QR code of the card's link in the card menu</a>. Thanks to xet7.</summary>
Card menu → **QR code** shows the card's link as a QR code, to open the card
on a phone or tablet. It is drawn in the browser by WeKan's own encoder
(`models/lib/qrCode.js`: byte mode, level M, versions 1–10), so the link is
sent to no QR service. `tests/qrCode.test.cjs` checks the encoder module for
module against qrcode-svg, which Meteor's accounts-2fa ships, and checks that
no outside QR service is used anywhere.
</details>
**Platforms** - where WeKan runs.
<details>
<summary><a href="https://github.com/wekan/wekan/commit/b8d9cb2565">Android bundles for Termux, for all four Android CPUs</a>. Thanks to xet7.</summary>
Every release that has wekan/node-patches' Android Node.js also has
`wekan-<version>-android-arm64.zip`, `-x64`, `-armv7` and `-x86`, with
FerretDB. Each is the release's Linux bundle rebuilt inside Termux's own
image with that Node.js, checked against its SHA256: every native addon is
built for it, and sharp against Termux's libvips. Before it is published,
each addon's architecture is checked, sharp must load, and the bundle must
start and answer HTTP 200. A CPU whose Android Node.js is not published yet
is skipped and builds by itself once it is. `tests/androidBundles.test.cjs`
covers the build and the workflow. See [Android](docs/Platforms/FOSS/OS/Android.md).
</details>
and fixes the following bugs:
**Board keys** - how old boards get theirs.
<details>
<summary><a href="https://github.com/wekan/wekan/commit/d0378f33c2">Old boards get their keys in one pass, in seconds rather than hours</a>. Thanks to Raymond-Voogt and xet7.</summary>
v12.41 gave each board without a key one at startup by re-reading every key
of the instance per board. It also counted up from 2 again for every board
sharing a title. That is quadratic: on 32,500 boards it ran for hours of slow
queries. Now the keys in use are read once, the new ones are planned in
memory, linearly, and written in batches, each only where the board still has
no key. A single new board reads only the keys that could collide with it,
through a new index. Tests: `tests/boardKeyBackfill.test.cjs` (32,500 boards
planned well under a second) and `server/lib/tests/boardKeys.tests.js`.
</details>
**Rules** - when a rule runs.
<details>
<summary><a href="https://github.com/wekan/wekan/commit/935e68dfdd">A disabled rule no longer runs on a schedule or from its button</a>. Thanks to xet7.</summary>
Turning a rule off (#2322) stopped it only for card events: the scheduler and
the rule's button ran it anyway, because neither looked at `enabled`. Both
check it now. Imported GitHub Actions workflows rely on this, since a
workflow that is code is imported disabled. Pinned by
`tests/ruleCommentCron.test.cjs`.
</details>
Thanks to above GitHub users for their contributions and translators for their translations.
+35 -36
View File
@@ -5,6 +5,41 @@ each one changed and the project status are in
Written by `node releases/changelog-short.mjs` from CHANGELOG-big.md:
make changes there.
# v13.11 2026-11-13 WeKan ® release
The **file-integrity scan** stops reporting attachments and
avatars that WeKan deleted itself as missing, which had buried real
**StorageBleed** findings under thousands of false ones. The recurring-checklist
reset no longer scans every **checklist** on each tick. In the **REST API**, a
refused custom-field update changes nothing, ownership transfer takes only the
boards the user administers, and deleting a missing integration answers 404.
[All changes in v13.11](CHANGELOG-big.md#v1311-2026-11-13-wekan--release)
# v13.10 2026-11-13 WeKan ® release
The release's **Snap Store** credential check bounds each
snapcraft install attempt, so a Snap Store that hangs no longer stops a release.
[All changes in v13.10](CHANGELOG-big.md#v1310-2026-11-13-wekan--release)
# v13.09 2026-11-12 WeKan ® release
The release's **Snap Store** credential check retries installing
snapcraft, so one Snap Store timeout no longer stops a release.
[All changes in v13.09](CHANGELOG-big.md#v1309-2026-11-12-wekan--release)
# v13.08 2026-11-12 WeKan ® release
The **REST API** creates a card under the **parent** it is given,
and a refused **integration URL** no longer leaves the other fields of the same
update written. The **single-file** WeKan for **32-bit ARM** builds again, with
an i386 Node.js of the exact version its bundle embeds. A **GitHub CodeQL**
alert in the security alert download script is fixed.
[All changes in v13.08](CHANGELOG-big.md#v1308-2026-11-12-wekan--release)
# v13.07 2026-11-12 WeKan ® release
**ListLockAttachBleed** is fixed: when a list refuses a role
@@ -15,39 +50,3 @@ the **indexes** that LDAP, card member and History queries need are added, and
Admin Panel → Problems can **Clear** resolved problems, which also age out.
[All changes in v13.07](CHANGELOG-big.md#v1307-2026-11-12-wekan--release)
# v13.06 2026-11-11 WeKan ® release
**CodeQL** alert #601 in a test is fixed, with two more tests
that had the same fault. A card's **overtime** set through the REST API is
saved where WeKan reads it, and cards that lost it before are repaired at
startup. Card **members and assignees** whose account was deleted can be removed
and added without an error, and clearing a card **date** no longer passes as a
date set to the rules, the due date count and the activity feed.
[All changes in v13.06](CHANGELOG-big.md#v1306-2026-11-11-wekan--release)
# v13.05 2026-11-10 WeKan ® release
WeKan is updated to **Meteor 3.6**. The **Android** bundles
build again, after v13.04's npm retry broke their install, and the **Super
Productivity** export no longer fails for a board with a card whose list is
archived.
[All changes in v13.05](CHANGELOG-big.md#v1305-2026-11-10-wekan--release)
# v13.04 2026-11-09 WeKan ® release
**ListLockBleed** is fixed: when a list refuses a role write
access, that refusal now also holds for the checklist and checklist-item REST
routes. The **Sandstorm** and **Android** bundles retry their npm install when
github.com is briefly unavailable.
[All changes in v13.04](CHANGELOG-big.md#v1304-2026-11-09-wekan--release)
# v13.03 2026-11-09 WeKan ® release
The amd64 **snaps** no longer fail the release when the Ubuntu
archive is briefly unreachable: the build waits for it and retries once.
[All changes in v13.03](CHANGELOG-big.md#v1303-2026-11-09-wekan--release)
+1 -1
View File
@@ -38,7 +38,7 @@ FROM --platform=$BUILDPLATFORM debian:trixie AS bundle
ARG TARGETARCH
ARG TARGETVARIANT
ARG VERSION=13.07
ARG VERSION=13.11
ARG DEBIAN_FRONTEND=noninteractive
COPY --chmod=755 releases/check-telemetry.py /tmp/check-telemetry.py
+1 -1
View File
@@ -1,5 +1,5 @@
appId: wekan-public/apps/77b94f60-dec9-0136-304e-16ff53095928
appVersion: "v13.07.0"
appVersion: "v13.11.0"
files:
userUploads:
- README.md
@@ -10,7 +10,7 @@ This is without container (without Docker or Snap).
Right click and download files 1-4:
1. [wekan-13.07-win64.zip](https://github.com/wekan/wekan/releases/download/v13.07/wekan-13.07-win64.zip) or newest available at https://github.com/wekan/wekan/releases/
1. [wekan-13.11-win64.zip](https://github.com/wekan/wekan/releases/download/v13.11/wekan-13.11-win64.zip) or newest available at https://github.com/wekan/wekan/releases/
2. [node.exe](https://nodejs.org/dist/latest-v26.x/win-x64/node.exe)
@@ -22,7 +22,7 @@ Right click and download files 1-4:
6. Double click `mongodb-windows-x86_64-7.0.34-signed.msi` . In installer, uncheck downloading MongoDB compass.
7. Unzip `wekan-13.07-win64.zip` , inside it is directory `bundle`, to it copy other files:
7. Unzip `wekan-13.11-win64.zip` , inside it is directory `bundle`, to it copy other files:
```
bundle (directory)
+5
View File
@@ -1,4 +1,5 @@
import { requireBoardMutation } from '/models/lib/boardMutationGuard';
import { forgetIntegrityBaselinesOfFiles } from '/server/lib/fileIntegrityForget';
import { ReactiveCache } from '/imports/reactiveCache';
import { Meteor } from 'meteor/meteor';
import { MongoInternals } from 'meteor/mongo';
@@ -301,6 +302,10 @@ Attachments.interceptDownload = function (http, fileObj, versionName) {
Attachments.onAfterRemove = function (filesInput) {
const files = normalizeRemovedFiles(filesInput);
// #6898: Meteor-Files unlinks the version paths itself after this hook
// (it returns nothing, so the default unlink runs), so this is where a
// removed file's integrity baseline is forgotten.
forgetIntegrityBaselinesOfFiles(files);
files.forEach(fileObj => {
if (!fileObj || !fileObj.versions) {
+4
View File
@@ -1,4 +1,5 @@
import { ReactiveCache } from '/imports/reactiveCache';
import { forgetIntegrityBaselinesOfFiles } from '/server/lib/fileIntegrityForget';
import { Meteor } from 'meteor/meteor';
import { isFileValid } from './fileValidation';
import { createBucket } from './lib/grid/createBucket';
@@ -296,6 +297,9 @@ Avatars.onAfterRemove = async function (filesInput) {
files = [];
}
// #6898: see Attachments.onAfterRemove - forget the removed files' baselines.
forgetIntegrityBaselinesOfFiles(files);
files.forEach(fileObj => {
if (!fileObj || !fileObj.versions) {
return;
+1 -1
View File
@@ -3097,7 +3097,7 @@ Boards.helpers({
// never be a meaningful (active) admin and the deactivate-by-index flow
// below would only leave it lingering, so hard-remove it by userId. This
// works whether or not the user still exists since matching is by userId.
if (memberIndex < 0 || !ReactiveCache.getUser(memberId)) {
if (memberIndex < 0 || !(await ReactiveCache.getUser(memberId))) {
return await Boards.updateAsync(this._id, {
$set: { members: pullMemberById(this.members, memberId) },
});
+5
View File
@@ -390,6 +390,11 @@ if (Meteor.isServer) {
Meteor.startup(async () => {
await ensureIndex(Checklists, { cardId: 1, sort: 1 });
await ensureIndex(Checklists, { boardId: 1 });
// #6899: the recurring-checklist reset cron reads the checklists with a
// resetInterval on every tick. Without an index that was a full scan of
// every checklist; a partial index holds only the recurring ones.
await ensureIndex(Checklists, { resetInterval: 1 },
{ partialFilterExpression: { resetInterval: { $exists: true } } });
});
}
+43
View File
@@ -0,0 +1,43 @@
'use strict';
// #6898: which fileIntegrity baseline ids belong to the files WeKan is about to
// delete or move away. Decided without a database
// (tests/integrityBaselineForgottenOnDelete.test.cjs).
//
// The daily scan (server/lib/fileIntegrityScan.js) keeps one baseline per file,
// keyed by its path, and reports every baseline whose file is gone as a
// StorageBleed. Nothing removed a baseline, so every attachment or avatar WeKan
// deleted itself was reported missing on every run from then on. The deletion
// paths now forget the baselines of the files they delete; a file that vanishes
// any OTHER way keeps its baseline and still alarms, which is the point.
const path = require('path');
// The path as recorded (versions.<v>.path, or the path unlink() resolved) and
// its absolute form: the scan records path.join(<storage root>, ...), and a
// relative recorded path would otherwise never match it.
function baselineIdsFor(paths) {
const ids = new Set();
for (const p of [].concat(paths || [])) {
if (typeof p !== 'string' || p === '') continue;
ids.add(p);
ids.add(path.resolve(p));
}
return [...ids];
}
// Every version path of the removed file documents.
function versionPathsOf(fileObjs) {
const out = [];
for (const fileObj of [].concat(fileObjs || [])) {
const versions = fileObj && fileObj.versions;
if (!versions || typeof versions !== 'object') continue;
for (const name of Object.keys(versions)) {
const p = versions[name] && versions[name].path;
if (typeof p === 'string' && p) out.push(p);
}
}
return out;
}
module.exports = { baselineIdsFor, versionPathsOf };
+17 -1
View File
@@ -42,6 +42,14 @@ const { hasEnoughDiskSpace } = require('./diskSpace');
// exporter uses it too and where plain Node can test it.
const { isPathInsideBase: isPathInside } = require('./storagePathContainment');
// #6898: drop the integrity baselines of files this module deletes or moves.
function forgetBaselines(paths) {
if (!Meteor.isServer) return;
try {
require('/server/lib/fileIntegrityForget').forgetIntegrityBaselinesSoon(paths);
} catch (e) { /* bookkeeping must never break a delete */ }
}
function tryRealPath(inputPath) {
try {
return fs.realpathSync(inputPath);
@@ -643,7 +651,12 @@ export class FileStoreStrategyFilesystem extends FileStoreStrategy {
// candidatePaths(). A delete that misses leaves the bytes on disk forever.
const filePath = this.resolveExistingPath()
|| this.version().path;
if (filePath) fs.unlink(filePath, () => {});
if (filePath) {
fs.unlink(filePath, () => {});
// #6898: a file WeKan deleted is accounted for; without this the daily
// integrity scan reported it as missing on every run from then on.
forgetBaselines([filePath, this.version().path]);
}
}
/** rename the file (physical)
@@ -666,6 +679,9 @@ export class FileStoreStrategyFilesystem extends FileStoreStrategy {
}
if (path.resolve(current) === path.resolve(newFilePath)) return;
fs.renameSync(current, newFilePath);
// #6898: the old path is gone on purpose; the scan records the new one as
// a first sighting instead of reporting the old one missing forever.
forgetBaselines([current, this.version().path]);
}
/** return the storage name
+214 -1
View File
@@ -14,7 +14,220 @@ Releases per day:
| 11 | 6 |
| 12 | 5 |
| 13 | 1 |
| 14 | 2 |
| 14 | 3 |
# v12.42 2026-10-14 WeKan ® release
**In short:** a new **Version Control** board view shows a board as a
**GitHub** or **Gitea** issue tracker, with releases, a **wiki** and history
with undo, and a whole **GitHub organization** imports into it, its **GitHub
Actions** as Rules. Optional **Trello**, **Jira**, **GitHub** and **Gitea**
compatible APIs let their clients use WeKan as a backend. Old boards get their
keys in one pass, list sync keeps issue details current, any board view can be
a **read-only public view**, and WeKan has **Android** bundles.
This release adds the following new features:
**Board views** - a repository's issue tracker as a view, and which views the
public sees.
<details>
<summary><a href="https://github.com/wekan/wekan/commit/326d9bbf61">Version Control: the board as a GitHub or Gitea issue tracker</a>. Thanks to xet7.</summary>
Board → View → **Version Control**, in the board's own theme. A card is an
issue: open, or closed when archived, in a list named *Closed*, *Done*,
*Resolved*, *Merged* or *Wontfix*, or closed upstream. The issue number is
the upstream one of an imported issue, else the card number. The board's
labels are labels, and labels with a due date and the importers'
`milestone:` labels are milestones, with progress bars. Comments are the
conversation, attachments the files. The search box takes GitHub's words:
`is:open`, `label:`, `-label:`, `milestone:`, `author:`, `assignee:@me`,
`no:label` and `sort:`. **New issue**, **Close** and **Reopen** write through
the ordinary collections, so the **History** tab (the board's history table)
shows them and **Restore** undoes them. A synced issue is closed in its
tracker. **Import** opens the GitHub, GitLab, Gitea and Forgejo importers, and
each synced list has a **Sync now** button. Visitors see it read-only. Tested
by `tests/versionControlView.test.cjs`. See
[Version Control view](docs/Features/Board/Version-Control-View.md).
</details>
<details>
<summary><a href="https://github.com/wekan/wekan/commit/326d9bbf61">A public board's public views are for the public; its members keep theirs</a>. Thanks to xet7.</summary>
Board Settings → Board View's *Show on* and *Default on Public Board* now
apply to the people looking who are not members: visitors who are not logged
in, and logged-in users who are not on the board. The board's members get the
*Private* columns. So a public board can open read-only in Version Control,
Email, Chat or any other view for the world, and list only the views chosen
for it, while its members keep every view. Before, a public board's members
had the public menu too. `boardViewAudience` in
`models/lib/boardViewSettings.js`; `tests/boardViewSettings.test.cjs` covers
visitors, non-members, former members and members.
</details>
**Import and sync** - what an issue tracker's issues bring with them.
<details>
<summary><a href="https://github.com/wekan/wekan/commit/326d9bbf61">GitHub, GitLab, Gitea and Forgejo issues bring their files</a>. Thanks to xet7.</summary>
The files an issue or its comments link or carry become card attachments in
Admin Panel → Attachments → Default Storage. These are GitHub
`user-attachments` and `user-images` links, GitLab `/uploads/` links, and
Gitea and Forgejo assets and `/attachments/<uuid>` links, at most 50 per
issue. Each one is downloaded through the import's SSRF guard, which checks
every redirect hop, and the upload checks its type and size. One that cannot
be fetched is on the import's loss report. Ordinary links are not files.
`models/lib/issueAttachments.js`; `tests/issueAttachments.test.cjs`.
</details>
<details>
<summary><a href="https://github.com/wekan/wekan/commit/326d9bbf61">List sync can bring each issue's comments, labels, state and files</a>. Thanks to xet7.</summary>
A new Sync option for GitHub, GitLab, Gitea and Forgejo, off by default.
Comments come from one paged repository-wide list (GitHub and Gitea), or
from each issue's notes (GitLab). They are keyed by the tracker's comment id,
and an edit upstream updates them. A label sync added and the issue lost is
taken off, and labels added in WeKan stay. Each file is downloaded once. It
runs after a successful sync as a step of its own, so a failure is shown and
retried and never undoes the title and description sync. Gitea issues are
fetched with `type=issues`. A rate-limited answer is waited out once when it
frees up within a minute; otherwise the run ends with the time to try again.
`tests/listSyncIssueDetails.test.cjs`.
</details>
<details>
<summary><a href="https://github.com/wekan/wekan/commit/935e68dfdd">A whole GitHub organization imports, each repository as its own board</a>. Thanks to xet7.</summary>
Import → GitHub → **Import a whole GitHub organization**: every repository of
an organization or user becomes a board named `owner/repository`, opening in
Version Control. Issues go through the ordinary `importBoard`, run as the
person who started the job, with their comments, labels and files. Releases
are cards in a *Releases* list, with their files attached only when chosen.
`.github/workflows` files become Rules. Each wiki is read from its git
repository with isomorphic-git (1.42.4, pinned), with every request through
the SSRF guard and only to github.com. A page is a card in the *Wiki* list:
its `[[links]]` point at the linked pages' cards and are attached to them,
and each commit that changed it is a dated edit in the card's history, so
**Restore** brings back an older version. Version Control gains **Releases**
and **Wiki** tabs, the wiki laid out as on GitHub. The job shows progress, can
be cancelled, pauses on a restart and stops at a rate limit. The token is
kept in memory only. Tests: `tests/githubOrgImport.test.cjs`,
`tests/githubWiki.test.cjs` and `tests/workflowYaml.test.cjs`, the last
checking 52 real workflow files against js-yaml.
</details>
**Rules** - what GitHub Actions workflows needed, for everyone.
<details>
<summary><a href="https://github.com/wekan/wekan/commit/935e68dfdd">GitHub Actions workflows become Rules; Rules gain comments, cron and descriptions</a>. Thanks to xet7.</summary>
Issue events become triggers. Label, close, reopen, comment, assign and
create steps become actions, from the well-known actions or from `gh issue`
commands, and `actions/stale` becomes an aging rule. A build, test or script
is never run: such a workflow is a disabled rule with its YAML as its
description. For this, WeKan's Rules gain several parts, available to every
board:
- a **commented on** trigger;
- an **add comment** action, written as the person whose change ran the rule
and only where they may comment;
- **cron schedules** in UTC (`models/lib/cronSchedule.js`);
- a rule **description**, shown on the rule.
Tests: `tests/githubActionsRules.test.cjs`, `tests/cronSchedule.test.cjs` and
`tests/ruleCommentCron.test.cjs`.
</details>
**Optional APIs** - other trackers' clients, with WeKan as their backend.
<details>
<summary><a href="https://github.com/wekan/wekan/commit/86ec72fade">Trello, Jira, GitHub and Gitea compatible APIs, off until an admin ticks them</a>. Thanks to xet7.</summary>
Admin Panel → Settings → Visibility → **Optional APIs** turns on REST APIs
in Trello's, Jira's, GitHub's and Gitea's shape (#4930), each described with
the address a client uses. None has rights of its own: each translates a
request into the matching request to WeKan's own REST API, made with the
caller's WeKan token, so every WeKan permission applies unchanged. An API that
is not ticked answers 404, and `WITH_API` turns them all off. Trello covers
members, boards, lists, cards, labels, comments and checklists. In Jira a
board is a project (the board key), a card is an issue (`DEV-7`), lists are
statuses, and simple JQL works. In GitHub and Gitea a board is a repository and
a card an issue, open or closed as in Version Control. Deleting archives. See
[Optional APIs](docs/API/Optional-APIs.md). `tests/optionalApis.test.cjs`
fails if any of them writes past WeKan's REST API.
`server/lib/tests/compatApi.tests.js` runs them over real HTTP.
</details>
**Cards** - opening a card on a phone.
<details>
<summary><a href="https://github.com/wekan/wekan/commit/326d9bbf61">A QR code of the card's link in the card menu</a>. Thanks to xet7.</summary>
Card menu → **QR code** shows the card's link as a QR code, to open the card
on a phone or tablet. It is drawn in the browser by WeKan's own encoder
(`models/lib/qrCode.js`: byte mode, level M, versions 1–10), so the link is
sent to no QR service. `tests/qrCode.test.cjs` checks the encoder module for
module against qrcode-svg, which Meteor's accounts-2fa ships, and checks that
no outside QR service is used anywhere.
</details>
**Platforms** - where WeKan runs.
<details>
<summary><a href="https://github.com/wekan/wekan/commit/b8d9cb2565">Android bundles for Termux, for all four Android CPUs</a>. Thanks to xet7.</summary>
Every release that has wekan/node-patches' Android Node.js also has
`wekan-<version>-android-arm64.zip`, `-x64`, `-armv7` and `-x86`, with
FerretDB. Each is the release's Linux bundle rebuilt inside Termux's own
image with that Node.js, checked against its SHA256: every native addon is
built for it, and sharp against Termux's libvips. Before it is published,
each addon's architecture is checked, sharp must load, and the bundle must
start and answer HTTP 200. A CPU whose Android Node.js is not published yet
is skipped and builds by itself once it is. `tests/androidBundles.test.cjs`
covers the build and the workflow. See [Android](docs/Platforms/FOSS/OS/Android.md).
</details>
and fixes the following bugs:
**Board keys** - how old boards get theirs.
<details>
<summary><a href="https://github.com/wekan/wekan/commit/d0378f33c2">Old boards get their keys in one pass, in seconds rather than hours</a>. Thanks to Raymond-Voogt and xet7.</summary>
v12.41 gave each board without a key one at startup by re-reading every key
of the instance per board. It also counted up from 2 again for every board
sharing a title. That is quadratic: on 32,500 boards it ran for hours of slow
queries. Now the keys in use are read once, the new ones are planned in
memory, linearly, and written in batches, each only where the board still has
no key. A single new board reads only the keys that could collide with it,
through a new index. Tests: `tests/boardKeyBackfill.test.cjs` (32,500 boards
planned well under a second) and `server/lib/tests/boardKeys.tests.js`.
</details>
**Rules** - when a rule runs.
<details>
<summary><a href="https://github.com/wekan/wekan/commit/935e68dfdd">A disabled rule no longer runs on a schedule or from its button</a>. Thanks to xet7.</summary>
Turning a rule off (#2322) stopped it only for card events: the scheduler and
the rule's button ran it anyway, because neither looked at `enabled`. Both
check it now. Imported GitHub Actions workflows rely on this, since a
workflow that is code is imported disabled. Pinned by
`tests/ruleCommentCron.test.cjs`.
</details>
Thanks to above GitHub users for their contributions and translators for their translations.
# v12.41 2026-10-14 WeKan ® release
+2 -2
View File
@@ -1,12 +1,12 @@
{
"name": "wekan",
"version": "v13.07.0",
"version": "v13.11.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "wekan",
"version": "v13.07.0",
"version": "v13.11.0",
"hasInstallScript": true,
"license": "MIT",
"dependencies": {
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "wekan",
"version": "v13.07.0",
"version": "v13.11.0",
"description": "Open-Source kanban",
"private": true,
"repository": {
+457 -447
View File
@@ -3,7 +3,7 @@
<head>
<meta charset="utf8" />
<title>WeKan REST API v13.07</title>
<title>WeKan REST API v13.11</title>
<!-- needed for adaptive design -->
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
@@ -12,483 +12,493 @@
margin: 0;
}
</style>
<link rel="modulepreload" href="https://cdn.redocly.com/redoc/v3.0.1/bundles/redoc.standalone.js" integrity="sha384-j07qRKHNNmcwU1/Y1elJc7pDLHXP/QfbQXTtSkbOJ7uYGJikqAzsFVbY2zR9FqOT" crossorigin="anonymous" /><style data-styled="true" data-styled-version="6.4.2">.kGPPnf{width:auto;display:inline-flex;align-items:center;justify-content:center;gap:var(--button-gap);margin:var(--button-margin);cursor:pointer;text-wrap:var(--button-text-wrap);color:var(--button-color);background-color:var(--button-bg-color);border:var(--button-border-width) var(--button-border-style) var(--button-border-color);box-shadow:var(--button-box-shadow);backdrop-filter:var(--button-backdrop-filter, none);font-weight:var(--button-font-weight);font-size:var(--button-font-size);line-height:var(--button-line-height);padding:var(--button-icon-padding);border-radius:var(--button-border-radius);}/*!sc*/
.kGPPnf svg{width:var(--button-icon-size);height:var(--button-icon-size);}/*!sc*/
.kGPPnf:hover,.kGPPnf[aria-expanded='true']{background-color:var(--button-bg-color-hover);color:var(--button-color-hover);border-color:var(--button-border-color-hover);}/*!sc*/
.kGPPnf:active,.kGPPnf.active{background-color:var(--button-bg-color-active);border-color:var(--button-border-color-active);color:var(--button-color-active);}/*!sc*/
.kGPPnf:focus-visible{outline:1px solid var(--button-border-color-focused);}/*!sc*/
.kGPPnf.button-tone-danger{color:var(--button-content-color-danger);border-color:var(--button-border-color-danger);background-color:var(--button-bg-color-danger);}/*!sc*/
.kGPPnf.button-tone-danger:hover,.kGPPnf.button-tone-danger[aria-expanded='true']{color:var(--button-content-color-danger-hover);border-color:var(--button-border-color-danger-hover);background-color:var(--button-bg-color-danger-hover);}/*!sc*/
.kGPPnf.button-tone-danger:active{color:var(--button-content-color-danger-pressed);border-color:var(--button-border-color-danger-pressed);background-color:var(--button-bg-color-danger-pressed);}/*!sc*/
.kGPPnf:disabled{pointer-events:none;background-color:var(--button-bg-color-disabled);color:var(--button-content-color-disabled);border-color:var(--button-border-color-disabled);border-width:var(--button-border-width-disabled);}/*!sc*/
data-styled.g11[id="sc-qdQOe"]{content:"kGPPnf,"}/*!sc*/
:root{--accordion-gap:var(--spacing-base);--accordion-border-radius:var(--border-radius-xl);--accordion-border:none;--accordion-line-height:var(--line-height-base);--accordion-font-size:var(--font-size-base);--accordion-font-family:var(--font-family-base);--accordion-font-weight:var(--font-weight-regular);--accordion-header-font-family:var(--font-family-base);--accordion-header-font-size:var(--font-size-base);--accordion-header-font-weight:var(--font-weight-medium, 500);--accordion-header-text-color:var(--text-color-primary);--accordion-header-padding:var(--spacing-sm) var(--spacing-sm) var(--spacing-xs) var(--spacing-base);--accordion-header-line-height:var(--line-height-base);--accordion-header-bg-color:transparent;--accordion-header-border:none;--accordion-body-font-family:var(--font-family-base);--accordion-body-font-size:var(--font-size-base);--accordion-body-font-weight:var(--font-weight-regular);--accordion-body-text-color:var(--text-color-secondary);--accordion-body-padding:0 var(--spacing-sm) var(--spacing-sm) var(--spacing-base);--accordion-body-bg-color:transparent;--accordion-body-border:none;--accordion-chevron-icon-color:var(--text-color-secondary);--accordion-chevron-icon-size:16px;--admonition-font-size:var(--font-size-base);--admonition-font-weight:var(--font-weight-regular);--admonition-line-height:var(--line-height-base);--admonition-heading-font-size:var(--font-size-base);--admonition-heading-font-weight:var(--font-weight-semibold);--admonition-heading-line-height:var(--line-height-base);--admonition-heading-letter-spacing:0;--admonition-heading-transform:none;--admonition-heading-text-color:var(--color-warm-grey-11);--admonition-gap:var(--spacing-sm);--admonition-text-gap:var(--spacing-xxs);--admonition-margin:var(--spacing-base) 0 var(--spacing-md) 0;--admonition-padding:var(--spacing-sm) var(--spacing-base);--admonition-icon-margin:calc(var(--spacing-unit) / 2) 0;--admonition-icon-size:18px;--admonition-border-radius:var(--border-radius-xl);--admonition-border-style:var(--border-style);--admonition-border-width:1px;--admonition-info-bg-color:var(--color-info-bg);--admonition-info-heading-text-color:var(--admonition-heading-text-color);--admonition-info-text-color:var(--admonition-heading-text-color);--admonition-info-icon-color:var(--color-info-active);--admonition-info-border-color:var(--color-info-border);--admonition-info-border-style:var(--admonition-border-style);--admonition-info-border-width:var(--admonition-border-width);--admonition-info-border:var(--admonition-info-border-width) var(--admonition-info-border-style) var(--admonition-info-border-color);--admonition-warning-bg-color:var(--color-warning-bg);--admonition-warning-text-color:var(--admonition-heading-text-color);--admonition-warning-heading-text-color:var(--admonition-heading-text-color);--admonition-warning-icon-color:var(--color-warning-active);--admonition-warning-border-color:var(--color-warning-border);--admonition-warning-border-style:var(--admonition-border-style);--admonition-warning-border-width:var(--admonition-border-width);--admonition-warning-border:var(--admonition-warning-border-width) var(--admonition-warning-border-style) var(--admonition-warning-border-color);--admonition-danger-bg-color:var(--color-error-bg);--admonition-danger-text-color:var(--admonition-heading-text-color);--admonition-danger-heading-text-color:var(--admonition-heading-text-color);--admonition-danger-icon-color:var(--color-error-active);--admonition-danger-border-color:var(--color-error-border);--admonition-danger-border-style:var(--admonition-border-style);--admonition-danger-border-width:var(--admonition-border-width);--admonition-danger-border:var(--admonition-danger-border-width) var(--admonition-danger-border-style) var(--admonition-danger-border-color);--admonition-success-bg-color:var(--color-success-bg);--admonition-success-text-color:var(--admonition-heading-text-color);--admonition-success-heading-text-color:var(--admonition-heading-text-color);--admonition-success-icon-color:var(--color-success-active);--admonition-success-border-color:var(--color-success-border);--admonition-success-border-style:var(--admonition-border-style);--admonition-success-border-width:var(--admonition-border-width);--admonition-success-border:var(--admonition-success-border-width) var(--admonition-success-border-style) var(--admonition-success-border-color);--admonition-idea-bg-color:var(--color-primary-bg, var(--admonition-default-bg-color-legacy));--admonition-idea-text-color:var(--admonition-heading-text-color);--admonition-idea-heading-text-color:var(--admonition-heading-text-color);--admonition-idea-icon-color:var(--color-primary-base, var(--admonition-default-icon-color-legacy));--admonition-idea-border-color:var(--color-primary-border, var(--admonition-default-border-color-legacy));--admonition-idea-border-style:var(--admonition-border-style);--admonition-idea-border-width:var(--admonition-border-width);--admonition-idea-border:var(--aLine truncated
<link rel="modulepreload" href="https://cdn.redocly.com/redoc/v3.1.0/bundles/redoc.standalone.js" integrity="sha384-JeMYOUn7XELj12XQpz8IRcyCi5pih2IAwvXmTcH3IAFVWrAXJVRNa64PuS3MIj9/" crossorigin="anonymous" /><style data-styled="true" data-styled-version="6.4.2">.fJsEIp{width:auto;display:inline-flex;align-items:center;justify-content:center;gap:var(--button-gap);margin:var(--button-margin);cursor:pointer;text-wrap:var(--button-text-wrap);color:var(--button-color);background-color:var(--button-bg-color);border:var(--button-border-width) var(--button-border-style) var(--button-border-color);box-shadow:var(--button-box-shadow);backdrop-filter:var(--button-backdrop-filter, none);font-weight:var(--button-font-weight);font-size:var(--button-font-size);line-height:var(--button-line-height);padding:var(--button-icon-padding);border-radius:var(--button-border-radius);}/*!sc*/
.fJsEIp svg{width:var(--button-icon-size);height:var(--button-icon-size);}/*!sc*/
.fJsEIp:hover,.fJsEIp[aria-expanded='true']{background-color:var(--button-bg-color-hover);color:var(--button-color-hover);border-color:var(--button-border-color-hover);}/*!sc*/
.fJsEIp:active,.fJsEIp.active{background-color:var(--button-bg-color-active);border-color:var(--button-border-color-active);color:var(--button-color-active);}/*!sc*/
.fJsEIp:focus-visible{outline:1px solid var(--button-border-color-focused);}/*!sc*/
.fJsEIp.button-tone-danger{color:var(--button-content-color-danger);border-color:var(--button-border-color-danger);background-color:var(--button-bg-color-danger);}/*!sc*/
.fJsEIp.button-tone-danger:hover,.fJsEIp.button-tone-danger[aria-expanded='true']{color:var(--button-content-color-danger-hover);border-color:var(--button-border-color-danger-hover);background-color:var(--button-bg-color-danger-hover);}/*!sc*/
.fJsEIp.button-tone-danger:active{color:var(--button-content-color-danger-pressed);border-color:var(--button-border-color-danger-pressed);background-color:var(--button-bg-color-danger-pressed);}/*!sc*/
.fJsEIp:disabled{pointer-events:none;background-color:var(--button-bg-color-disabled);color:var(--button-content-color-disabled);border-color:var(--button-border-color-disabled);border-width:var(--button-border-width-disabled);}/*!sc*/
data-styled.g18[id="sc-krVzYl"]{content:"fJsEIp,"}/*!sc*/
.eRqACJ{height:var(--menu-item-label-chevron-size);width:var(--menu-item-label-chevron-size);}/*!sc*/
.eRqACJ path{fill:var(--tree-content-color-default);}/*!sc*/
data-styled.g40[id="sc-kLEbxe"]{content:"eRqACJ,"}/*!sc*/
.gSVnPl{position:relative;display:flex;}/*!sc*/
data-styled.g49[id="sc-iUlCMS"]{content:"gSVnPl,"}/*!sc*/
.iwGQep{font-size:var(--md-content-font-size);font-family:var(--md-content-font-family);font-weight:var(--md-content-font-weight);line-height:var(--md-content-line-height);color:var(--md-content-text-color);padding:0;overflow-wrap:break-word;}/*!sc*/
.iwGQep a:not([role='button']){text-decoration:var(--link-decoration);text-underline-offset:var(--link-underline-offset);color:var(--link-color-primary);font-weight:var(--link-font-weight);}/*!sc*/
.iwGQep a:not([role='button']):visited{color:var(--link-color-visited);text-decoration:var(--link-decoration-visited);}/*!sc*/
.iwGQep a:not([role='button']):hover,.iwGQep a:not([role='button']):focus{text-decoration:var(--link-decoration-hover);color:var(--link-color-primary-hover);}/*!sc*/
.iwGQep a[name],.iwGQep [id]{scroll-margin-top:var(--navbar-offset);}/*!sc*/
.iwGQep img,.iwGQep iframe{max-width:100%;}/*!sc*/
.iwGQep .heading-anchor{font-weight:var(--heading-font-weight);font-family:var(--heading-font-family);position:relative;scroll-margin-top:var(--navbar-offset);}/*!sc*/
.iwGQep .heading-anchor .anchor.before{position:absolute;left:0;transform:translateX(-100%);padding-right:var(--heading-anchor-offset-right);}/*!sc*/
.iwGQep .heading-anchor .anchor.after{display:inline-block;padding-left:4px;}/*!sc*/
.iwGQep .heading-anchor .anchor svg{fill:var(--heading-anchor-color);visibility:hidden;background-image:var(--heading-anchor-icon);background-size:contain;background-repeat:no-repeat;background-position:center;}/*!sc*/
.iwGQep .heading-anchor:hover .anchor svg,.iwGQep .heading-anchor .anchor:focus svg{visibility:visible;}/*!sc*/
.iwGQep .heading-anchor h1,.iwGQep .heading-anchor h2,.iwGQep .heading-anchor h3,.iwGQep .heading-anchor h4,.iwGQep .heading-anchor h5,.iwGQep .heading-anchor h6{display:flex;align-items:flex-start;gap:var(--spacing-sm);margin:0;font:inherit;color:inherit;}/*!sc*/
.iwGQep .heading-level-1{font-size:var(--h1-font-size);font-weight:var(--h1-font-weight);font-family:var(--h1-font-family);line-height:var(--h1-line-height);color:var(--h1-text-color);text-transform:var(--h1-text-transform);margin:var(--h1-margin-top) 0 var(--h1-margin-bottom) 0;}/*!sc*/
.iwGQep .heading-level-1 h1 code{font-size:var(--h1-font-size);line-height:var(--h1-code-line-height);}/*!sc*/
.iwGQep .heading-level-2{font-size:var(--h2-font-size);font-weight:var(--h2-font-weight);font-family:var(--h2-font-family);line-height:var(--h2-line-height);color:var(--h2-text-color);text-transform:var(--h2-text-transform);margin:var(--h2-margin-top) 0 var(--h2-margin-bottom) 0;}/*!sc*/
.iwGQep .heading-level-2 h2 code{font-size:var(--h2-font-size);line-height:var(--h2-code-line-height);}/*!sc*/
.iwGQep .heading-level-3{font-size:var(--h3-font-size);font-weight:var(--h3-font-weight);font-family:var(--h3-font-family);line-height:var(--h3-line-height);color:var(--h3-text-color);text-transform:var(--h3-text-transform);margin:var(--h3-margin-top) 0 var(--h3-margin-bottom) 0;}/*!sc*/
.iwGQep .heading-level-3 h3 code{font-size:var(--h3-font-size);line-height:var(--h3-code-line-height);}/*!sc*/
.iwGQep .heading-level-4{font-size:var(--h4-font-size);font-weight:var(--h4-font-weight);font-family:var(--h4-font-family);line-height:var(--h4-line-height);color:var(--h4-text-color);text-transform:var(--h4-text-transform);margin:var(--h4-margin-top) 0 var(--h4-margin-bottom) 0;}/*!sc*/
.iwGQep .heading-level-4 h4 code{font-size:var(--h4-font-size);line-height:var(--h4-code-line-height);}/*!sc*/
.iwGQep .heading-level-5{font-size:var(--h5-font-size);font-weight:var(--h5-font-weight);font-family:var(--h5-font-family);line-height:var(--h5-line-height);color:var(--h5-text-color);text-transform:var(--h5-text-transform);margin:var(--h5-margin-top) 0 var(--h5-margin-bottom) 0;}/*!sc*/
.iwGQep .heading-level-5 h5 code{font-size:var(--h5-font-size);line-height:var(--h5-code-line-height);}/*!sc*/
.iwGQep .heading-level-6{font-size:var(--h6-font-size);font-weight:var(--h6-font-weight);font-family:var(--h6-font-family);line-height:var(--h6-line-height);color:var(--h6-text-color);text-transform:var(--h6-text-transform);margin:var(--h6-margin-top) 0 var(--h6-margin-bottom) 0;}/*!sc*/
.iwGQep .heading-level-6 h6 code{font-size:var(--h6-font-size);line-height:var(--h6-code-line-height);}/*!sc*/
.iwGQep strong{font-weight:var(--font-weight-bold);}/*!sc*/
.iwGQep hr{margin:var(--md-hr-margin-vertical) 0;border:var(--md-hr-border);}/*!sc*/
.iwGQep details:not([data-component-name='Markdoc/Accordion/Accordion']){margin:1.125em 0;}/*!sc*/
.iwGQep details:not([data-component-name='Markdoc/Accordion/Accordion']) summary{color:var(--text-color-secondary);font-weight:var(--font-weight-regular);padding:0;cursor:pointer;}/*!sc*/
.iwGQep details:not([data-component-name='Markdoc/Accordion/Accordion'])>:not(summary):first-of-type{margin-top:1.25em;}/*!sc*/
.iwGQep details:not([data-component-name='Markdoc/Accordion/Accordion'])>*:last-child{margin-bottom:1.25em;}/*!sc*/
.iwGQep details:not([data-component-name='Markdoc/Accordion/Accordion'])>:not(summary){margin-left:2em;}/*!sc*/
.iwGQep .md-table-wrapper{overflow-x:auto;}/*!sc*/
.iwGQep table.md{width:100%;overflow:hidden;word-break:keep-all;border-spacing:0;margin:var(--md-table-margin-vertical) 0;border-radius:var(--md-table-border-radius);border:1px solid var(--md-table-border-color);}/*!sc*/
.iwGQep table.md th,.iwGQep table.md td{padding:var(--md-table-cell-padding);color:var(--md-table-cell-text-color);font-size:var(--md-table-font-size);line-height:var(--md-table-line-height);vertical-align:var(--md-table-cell-vertical-align);}/*!sc*/
.iwGQep table.md thead td,.iwGQep table.md thead th,.iwGQep table.md tbody td{border-bottom:1px solid var(--md-table-border-color);}/*!sc*/
.iwGQep table.md tbody tr:last-child td{border-bottom:none;}/*!sc*/
.iwGQep table.md tfoot td{border-top:1px solid var(--md-table-border-color);padding:var(--md-table-footer-cell-padding);}/*!sc*/
.iwGQep table.md thead,.iwGQep table.md tfoot{background-color:var(--md-table-header-bg-color);}/*!sc*/
.iwGQep table.md thead:has(th:empty){display:none;}/*!sc*/
.iwGQep table.md thead:has(th:not(:empty)){display:table-header-group;}/*!sc*/
.iwGQep table.md tr{background-color:var(--md-table-stripe-bg-color);}/*!sc*/
.iwGQep table.md th{text-align:left;font-weight:var(--md-table-head-font-weight);color:var(--md-table-head-text-color);}/*!sc*/
.iwGQep table.md th[align='center']{text-align:center;}/*!sc*/
.iwGQep table.md th[align='right']{text-align:right;}/*!sc*/
.iwGQep table.md th[valign='top'],.iwGQep table.md td[valign='top']{vertical-align:top;}/*!sc*/
.iwGQep table.md th[valign='middle'],.iwGQep table.md td[valign='middle']{vertical-align:middle;}/*!sc*/
.iwGQep table.md th[valign='bottom'],.iwGQep table.md td[valign='bottom']{vertical-align:bottom;}/*!sc*/
.iwGQep p{margin:var(--md-paragraph-margin);}/*!sc*/
.iwGQep h1.md{font-size:var(--h1-font-size);font-weight:var(--h1-font-weight);font-family:var(--h1-font-family);line-height:var(--h1-line-height);color:var(--h1-text-color);text-transform:var(--h1-text-transform);margin:var(--h1-margin-top) 0 var(--h1-margin-bottom) 0;}/*!sc*/
.iwGQep h2.md{font-size:var(--h2-font-size);font-weight:var(--h2-font-weight);font-family:var(--h2-font-family);line-height:var(--h2-line-height);color:var(--h2-text-color);text-transform:var(--h2-text-transform);margin:var(--h2-margin-top) 0 var(--h2-margin-bottom) 0;}/*!sc*/
.iwGQep h3.md{font-size:var(--h3-font-size);font-weight:var(--h3-font-weight);font-family:var(--h3-font-family);line-height:var(--h3-line-height);color:var(--h3-text-color);text-transform:var(--h3-text-transform);margin:var(--h3-margin-top) 0 var(--h3-margin-bottom) 0;}/*!sc*/
.iwGQep h4.md{font-size:var(--h4-font-size);font-weight:var(--h4-font-weight);font-family:var(--h4-font-family);line-height:var(--h4-line-height);color:var(--h4-text-color);text-transform:var(--h4-text-transform);margin:var(--h4-margin-top) 0 var(--h4-margin-bottom) 0;}/*!sc*/
.iwGQep h5.md{font-size:var(--h5-font-size);font-weight:var(--h5-font-weight);font-family:var(--h5-font-family);line-height:var(--h5-line-height);color:var(--h5-text-color);text-transform:var(--h5-text-transform);margin:var(--h5-margin-top) 0 var(--h5-margin-bottom) 0;}/*!sc*/
.iwGQep h6.md{font-size:var(--h6-font-size);font-weight:var(--h6-font-weight);font-family:var(--h6-font-family);line-height:var(--h6-line-height);color:var(--h6-text-color);text-transform:var(--h6-text-transform);margin:var(--h6-margin-top) 0 var(--h6-margin-bottom) 0;}/*!sc*/
.iwGQep h1.md code{font-size:var(--h1-font-size);line-height:var(--h1-code-line-height);}/*!sc*/
.iwGQep h2.md code{font-size:var(--h2-font-size);line-height:var(--h2-code-line-height);}/*!sc*/
.iwGQep h3.md code{font-size:var(--h3-font-size);line-height:var(--h3-code-line-height);}/*!sc*/
.iwGQep h4.md code{font-size:var(--h4-font-size);line-height:var(--h4-code-line-height);}/*!sc*/
.iwGQep h5.md code{font-size:var(--h5-font-size);line-height:var(--h5-code-line-height);}/*!sc*/
.iwGQep h6.md code{font-size:var(--h6-font-size);line-height:var(--h6-code-line-height);}/*!sc*/
.iwGQep code{color:var(--inline-code-text-color);background-color:var(--inline-code-bg-color);border-radius:var(--inline-code-border-radius);padding:0.19rem var(--spacing-xxs);font-weight:var(--code-font-weight);word-break:break-word;font-size:var(--font-size-sm);font-family:var(--font-family-monospaced);line-height:var(--line-height-sm);}/*!sc*/
.iwGQep.iwGQep pre{white-space:var(--code-wrap, pre);background-color:var(--code-block-bg-color);font-family:var(--code-block-font-family);color:var(--code-block-text-color);padding:var(--code-block-padding);overflow-x:auto;line-height:var(--line-height-base);margin:var(--md-pre-margin);}/*!sc*/
.iwGQep.iwGQep pre code{background-color:transparent;color:inherit;font-size:inherit;font-family:inherit;border:none;padding:0;}/*!sc*/
.iwGQep blockquote{margin:var(--md-blockquote-margin-vertical) var(--md-blockquote-margin-horizontal);padding:var(--md-blockquote-padding-vertical) var(--md-blockquote-padding-horizontal);border-left:var(--md-blockquote-border-left);background-color:var(--md-blockquote-bg-color);color:var(--md-blockquote-text-color);box-shadow:var(--md-blockquote-box-shadow);}/*!sc*/
.iwGQep blockquote>p:first-child{margin-top:0;}/*!sc*/
.iwGQep img{max-width:100%;box-sizing:content-box;margin:0 auto;}/*!sc*/
.iwGQep img:only-child{display:block;}/*!sc*/
.iwGQep p>img:only-child,.iwGQep li>img:only-child{display:inline-block;}/*!sc*/
.iwGQep ul.md,.iwGQep ol.md{padding-left:var(--md-list-left-padding);margin:var(--md-list-margin);}/*!sc*/
.iwGQep ul.md ul,.iwGQep ol.md ul,.iwGQep ul.md ol,.iwGQep ol.md ol{margin-bottom:0;margin-top:0;}/*!sc*/
.iwGQep li{list-style:var(--md-list-item-style);}/*!sc*/
.iwGQep li:not(:last-child){margin-bottom:calc(var(--spacing-xxs) / 2);}/*!sc*/
.iwGQep li::marker{padding-left:0.25rem;}/*!sc*/
.iwGQep ul>li:has(input[type="checkbox"]){list-style:var(--md-checkbox-list-item-style);}/*!sc*/
.iwGQep ol>li{list-style:var(--md-numbered-list-item-style);}/*!sc*/
.iwGQep ol>li:before{content:counter(list-item);display:var(--md-numbered-list-number-display);color:var(--md-numbered-list-number-text-color);font-size:var(--md-numbered-list-number-font-size);font-family:var(--md-numbered-list-number-font-family);font-weight:var(--md-numbered-list-number-font-weight);background-color:var(--md-numbered-list-number-bg-color);border-radius:var(--md-numbered-list-number-border-radius);border:var(--md-numbered-list-number-border);margin-right:var(--md-numbered-list-number-margin-right);padding:var(--md-numbered-list-number-padding);}/*!sc*/
data-styled.g121[id="sc-dTCDEk"]{content:"iwGQep,"}/*!sc*/
.gbbcFn{position:relative;display:inline-flex;align-items:center;vertical-align:middle;}/*!sc*/
data-styled.g126[id="sc-fXmTIC"]{content:"gbbcFn,"}/*!sc*/
.mrXOz{position:absolute;top:50%;transform:translate(-100%, -50%);z-index:1;display:flex;align-items:center;min-width:24px;min-height:24px;justify-content:flex-end;left:0;padding-right:var(--heading-anchor-offset-right, 4px);}/*!sc*/
.mrXOz:hover svg{--link-icon-bg-color:var(
--deep-link-anchor-bg-color-hover,
var(--button-bg-color-secondary-hover)
);}/*!sc*/
.mrXOz:active svg{--link-icon-bg-color:var(
--deep-link-anchor-bg-color-pressed,
var(--button-bg-color-secondary-pressed)
);}/*!sc*/
.mrXOz.mrXOz[aria-current] svg,.mrXOz.mrXOz[data-tooltip-open] svg{visibility:visible;}/*!sc*/
.mrXOz.mrXOz[aria-current] svg{--link-icon-bg-color:var(--deep-link-anchor-bg-color-current, var(--color-blueberry-1));fill:var(--deep-link-anchor-color-current, var(--color-blueberry-6));}/*!sc*/
data-styled.g127[id="sc-fINhYD"]{content:"mrXOz,"}/*!sc*/
.kQBJPu{display:flex;}/*!sc*/
data-styled.g128[id="sc-gyMhpw"]{content:"kQBJPu,"}/*!sc*/
.dpfGSp{height:16px;width:16px;}/*!sc*/
.dpfGSp path{fill:currentColor;}/*!sc*/
data-styled.g148[id="sc-gJaedx"]{content:"dpfGSp,"}/*!sc*/
.fUJUji{display:flex;flex-direction:column;justify-content:space-between;width:100%;padding:0;}/*!sc*/
@media screen and (min-width: 1280px){.fUJUji{flex-direction:row;}}/*!sc*/
@media print{.fUJUji{flex-direction:column;}}/*!sc*/
data-styled.g392[id="sc-jddhbx"]{content:"fUJUji,"}/*!sc*/
.gypDir{display:flex;flex-direction:column;width:100%;min-width:0;padding:var(--spacing-sm) var(--panel-gap-horizontal);margin-bottom:var(--spacing-xs);}/*!sc*/
.gypDir:empty{display:none;}/*!sc*/
@media screen and (min-width: 1280px){.gypDir{width:calc(100% - var(--panel-samples-width));padding-left:calc(var(--panel-gap-horizontal) * 2);padding-right:var(--panel-gap-horizontal);padding-top:0;padding-bottom:0;}}/*!sc*/
@media screen and (max-width: 672px){.gypDir{padding-top:0;padding-bottom:var(--spacing-vertical);}}/*!sc*/
@media print{.gypDir{width:100%;padding-top:var(--spacing-vertical);padding-bottom:var(--spacing-vertical);}}/*!sc*/
.sc-fBVFoq+.sc-fBVFoq{padding-top:0;}/*!sc*/
.gypDir [data-testid="middle-panel"]{padding-left:0;padding-right:0;}/*!sc*/
.gypDir [data-testid="right-panel"]{padding-left:0;padding-right:0;}/*!sc*/
data-styled.g393[id="sc-fBVFoq"]{content:"gypDir,"}/*!sc*/
.fRogNd{margin-left:auto;--code-block-padding:var(--spacing-xs) 0 var(--spacing-xs) 20px;color:var(--panel-samples-text-color);width:100%;height:fit-content;padding-top:var(--panel-gap-vertical);padding-bottom:var(--panel-gap-vertical);padding-left:var(--panel-gap-horizontal);padding-right:var(--panel-gap-horizontal);margin-bottom:var(--spacing-sm);position:sticky;z-index:var(--panel-z-index);top:calc(var(--navbar-stack-height) + var(--panel-gap-vertical));}/*!sc*/
.fRogNd>[data-component-name='Panel/Panel']:not(:last-child){margin-bottom:var(--spacing-sm);}/*!sc*/
.fRogNd:has([data-component-name='BrokerPanel/BrokerItem']){padding-top:var(--spacing-lg);padding-bottom:0;}/*!sc*/
.fRogNd:has([data-testid='items-navigation-list']){position:static;}/*!sc*/
.fRogNd:empty{display:none;}/*!sc*/
@media screen and (min-width: 1280px){.fRogNd{width:var(--panel-samples-width);padding-top:0;padding-bottom:0;padding-left:var(--panel-gap-horizontal);padding-right:calc(var(--panel-gap-horizontal) * 2);}}/*!sc*/
@media print{.fRogNd{width:100%;padding-top:var(--spacing-vertical);padding-bottom:var(--spacing-vertical);}}/*!sc*/
data-styled.g394[id="sc-jaQWEH"]{content:"fRogNd,"}/*!sc*/
.gGwlUS{position:relative;display:flex;align-items:center;}/*!sc*/
.gGwlUS [data-deep-link-anchor] svg{visibility:hidden;}/*!sc*/
.gGwlUS:hover [data-deep-link-anchor] svg,.gGwlUS [data-deep-link-anchor] a:focus svg{visibility:visible;}/*!sc*/
data-styled.g396[id="sc-ibJlrt"]{content:"gGwlUS,"}/*!sc*/
.bnwVNj{margin:0;}/*!sc*/
data-styled.g404[id="sc-jlthoW"]{content:"bnwVNj,"}/*!sc*/
.dOYhCk{position:relative;display:flex;flex-direction:column;align-items:center;margin:0 auto;width:100%;max-width:100%;}/*!sc*/
@media screen and (min-width: 672px){.dOYhCk{max-width:var(--layout-three-panel-small-max-width);}}/*!sc*/
@media screen and (min-width: 960px){.dOYhCk{max-width:var(--layout-three-panel-medium-max-width);}}/*!sc*/
@media screen and (min-width: 1280px){.dOYhCk{max-width:var(--layout-three-panel-large-max-width);}}/*!sc*/
@media print{.dOYhCk{width:100%;padding:0;}}/*!sc*/
data-styled.g411[id="sc-eYwuas"]{content:"dOYhCk,"}/*!sc*/
.cHaeyh{min-height:var(--lazy-section-min-height, 100px);contain-intrinsic-size:var(--lazy-section-min-height, 100px);content-visibility:auto;}/*!sc*/
data-styled.g412[id="sc-hjcVnH"]{content:"cHaeyh,"}/*!sc*/
.eyyZKJ{width:100%;content-visibility:auto;contain-intrinsic-size:auto var(--lazy-section-min-height, 100px);}/*!sc*/
data-styled.g413[id="sc-erNUAS"]{content:"eyyZKJ,"}/*!sc*/
.ewcgoM{padding:var(--spacing-xs) 0 var(--spacing-xxl) 0;border-bottom:1px solid var(--border-color-secondary);}/*!sc*/
.ewcgoM [data-testid='middle-panel']>:first-child{padding-top:calc(var(--spacing-unit) * 7);}/*!sc*/
.ewcgoM [data-component-name='Markdown/Markdown'] :is(h1, h2, h3, h4, h5, h6){padding-top:var(--spacing-xs);}/*!sc*/
data-styled.g421[id="sc-kRiNQB"]{content:"ewcgoM,"}/*!sc*/
.bvyQbM{color:var(--h1-text-color);margin:var(--h1-margin-top) 0 var(--h1-margin-bottom);font-size:var(--h1-font-size);font-weight:var(--h1-font-weight);font-family:var(--h1-font-family);line-height:var(--h1-line-height);color:var(--h1-text-color);text-transform:var(--h1-text-transform);}/*!sc*/
data-styled.g432[id="sc-cujyXd"]{content:"bvyQbM,"}/*!sc*/
.dSsyNs{container-type:inline-size;container-name:api-docs-heading;position:relative;display:flex;align-items:baseline;gap:var(--spacing-base);margin-bottom:var(--spacing-base);flex-wrap:nowrap;gap:var(--spacing-xs);}/*!sc*/
.dSsyNs:has(h4){margin-bottom:var(--spacing-xs);}/*!sc*/
.dSsyNs:has(+ [data-gql-return-type-details]){margin-bottom:0;}/*!sc*/
@media screen and (max-width: 672px){.dSsyNs{flex-wrap:wrap;gap:var(--spacing-xs) 0;}}/*!sc*/
data-styled.g455[id="sc-clInUQ"]{content:"dSsyNs,"}/*!sc*/
.iVuEYQ{display:flex;align-items:center;flex-wrap:wrap;row-gap:var(--spacing-xs);flex:1 1 auto;min-width:0;}/*!sc*/
.iVuEYQ [data-deep-link-anchor] svg{visibility:hidden;}/*!sc*/
.iVuEYQ:hover [data-deep-link-anchor] svg,.iVuEYQ [data-deep-link-anchor] a:focus svg{visibility:visible;}/*!sc*/
.iVuEYQ>*:has(+ [data-response-codes-tablist]){margin-right:var(--spacing-base);}/*!sc*/
data-styled.g456[id="sc-cATlX"]{content:"iVuEYQ,"}/*!sc*/
.dvLNys{flex-shrink:0;margin-left:auto;}/*!sc*/
@media screen and (max-width: 672px){.dvLNys{flex-basis:100%;--page-actions-padding-left:0;}}/*!sc*/
data-styled.g458[id="sc-fzZtPW"]{content:"dvLNys,"}/*!sc*/
.ezAmwK{overflow-wrap:anywhere;word-break:break-word;min-width:0;position:relative;margin:0;display:flex;align-items:center;gap:var(--spacing-xs);flex-wrap:wrap;}/*!sc*/
.ezAmwK [data-deep-link-anchor]{position:absolute;top:0;left:0;height:1lh;}/*!sc*/
.ezAmwK+[data-component-name="Markdown/Markdown"]{margin-top:var(--h1-margin-bottom);}/*!sc*/
data-styled.g461[id="sc-fUuAyx"]{content:"ezAmwK,"}/*!sc*/
:where(:root){--accordion-gap:var(--spacing-base);--accordion-border-radius:var(--border-radius-xl);--accordion-border:none;--accordion-line-height:var(--line-height-base);--accordion-font-size:var(--font-size-base);--accordion-font-family:var(--font-family-base);--accordion-font-weight:var(--font-weight-regular);--accordion-header-font-family:var(--font-family-base);--accordion-header-font-size:var(--font-size-base);--accordion-header-font-weight:var(--font-weight-medium, 500);--accordion-header-text-color:var(--text-color-primary);--accordion-header-padding:var(--spacing-sm) var(--spacing-sm) var(--spacing-xs) var(--spacing-base);--accordion-header-line-height:var(--line-height-base);--accordion-header-bg-color:transparent;--accordion-header-border:none;--accordion-body-font-family:var(--font-family-base);--accordion-body-font-size:var(--font-size-base);--accordion-body-font-weight:var(--font-weight-regular);--accordion-body-text-color:var(--text-color-secondary);--accordion-body-padding:0 var(--spacing-sm) var(--spacing-sm) var(--spacing-base);--accordion-body-bg-color:transparent;--accordion-body-border:none;--accordion-chevron-icon-color:var(--text-color-secondary);--accordion-chevron-icon-size:16px;--admonition-font-size:var(--font-size-base);--admonition-font-weight:var(--font-weight-regular);--admonition-line-height:var(--line-height-base);--admonition-heading-font-size:var(--font-size-base);--admonition-heading-font-weight:var(--font-weight-semibold);--admonition-heading-line-height:var(--line-height-base);--admonition-heading-letter-spacing:0;--admonition-heading-transform:none;--admonition-heading-text-color:var(--color-warm-grey-11);--admonition-gap:var(--spacing-sm);--admonition-text-gap:var(--spacing-xxs);--admonition-margin:var(--spacing-base) 0 var(--spacing-md) 0;--admonition-padding:var(--spacing-sm) var(--spacing-base);--admonition-icon-margin:calc(var(--spacing-unit) / 2) 0;--admonition-icon-size:18px;--admonition-border-radius:var(--border-radius-xl);--admonition-border-style:var(--border-style);--admonition-border-width:1px;--admonition-info-bg-color:var(--color-info-bg);--admonition-info-heading-text-color:var(--admonition-heading-text-color);--admonition-info-text-color:var(--admonition-heading-text-color);--admonition-info-icon-color:var(--color-info-active);--admonition-info-border-color:var(--color-info-border);--admonition-info-border-style:var(--admonition-border-style);--admonition-info-border-width:var(--admonition-border-width);--admonition-info-border:var(--admonition-info-border-width) var(--admonition-info-border-style) var(--admonition-info-border-color);--admonition-warning-bg-color:var(--color-warning-bg);--admonition-warning-text-color:var(--admonition-heading-text-color);--admonition-warning-heading-text-color:var(--admonition-heading-text-color);--admonition-warning-icon-color:var(--color-warning-active);--admonition-warning-border-color:var(--color-warning-border);--admonition-warning-border-style:var(--admonition-border-style);--admonition-warning-border-width:var(--admonition-border-width);--admonition-warning-border:var(--admonition-warning-border-width) var(--admonition-warning-border-style) var(--admonition-warning-border-color);--admonition-danger-bg-color:var(--color-error-bg);--admonition-danger-text-color:var(--admonition-heading-text-color);--admonition-danger-heading-text-color:var(--admonition-heading-text-color);--admonition-danger-icon-color:var(--color-error-active);--admonition-danger-border-color:var(--color-error-border);--admonition-danger-border-style:var(--admonition-border-style);--admonition-danger-border-width:var(--admonition-border-width);--admonition-danger-border:var(--admonition-danger-border-width) var(--admonition-danger-border-style) var(--admonition-danger-border-color);--admonition-success-bg-color:var(--color-success-bg);--admonition-success-text-color:var(--admonition-heading-text-color);--admonition-success-heading-text-color:var(--admonition-heading-text-color);--admonition-success-icon-color:var(--color-success-active);--admonition-success-border-color:var(--color-success-border);--admonition-success-border-style:var(--admonition-border-style);--admonition-success-border-width:var(--admonition-border-width);--admonition-success-border:var(--admonition-success-border-width) var(--admonition-success-border-style) var(--admonition-success-border-color);--admonition-idea-bg-color:var(--color-primary-bg, var(--admonition-default-bg-color-legacy));--admonition-idea-text-color:var(--admonition-heading-text-color);--admonition-idea-heading-text-color:var(--admonition-heading-text-color);--admonition-idea-icon-color:var(--color-primary-base, var(--admonition-default-icon-color-legacy));--admonition-idea-border-color:var(--color-primary-border, var(--admonition-default-border-color-legacy));--admonition-idea-border-style:var(--admonition-border-style);--admonition-idea-border-width:var(--admonition-border-width);--admonition-idea-borderLine truncated
90vw,
calc(100vw - 2 * var(--image-lightbox-side-gutter))
);--image-lightbox-image-max-height:90vh;--image-lightbox-image-border-radius:var(--border-radius-xl);--image-lightbox-image-bg-placeholder:var(--bg-color-tonal);--image-lightbox-image-shadow:var(--bg-raised-shadow);--image-lightbox-close-size:40px;--image-lightbox-close-icon-size:18px;--image-lightbox-close-offset:var(--spacing-base);--image-lightbox-close-border-radius:var(--border-radius-xl);--image-lightbox-side-gutter:calc(
var(--image-lightbox-close-size) + var(--image-lightbox-close-offset) + var(--spacing-sm)
);--input-bg-color:var(--color-white);--input-bg-color-ghost:var(--color-white);--input-font-size:var(--font-size-base);--input-font-family:var(--font-family-base);--input-line-height:1.15em;--input-text-indent:16px;--input-padding:var(--spacing-xs);--input-padding-horizontal:var(--spacing-sm);--input-padding-vertical:var(--spacing-xxs);--input-height:40px;--input-border-radius:var(--border-radius-lg);--input-border-color:var(--color-warm-grey-3);--input-border:1px solid var(--input-border-color);--input-border-color-hover:var(--color-warm-grey-4);--input-border-hover:1px solid var(--input-border-color-hover);--input-border-color-focused:var(--color-info-base);--input-border-focus:1px solid var(--input-border-color-focused);--input-content-color-disabled:var(--color-warm-grey-4);--input-bg-color-disabled:var(--color-warm-grey-1);--input-border-color-disabled:var(--color-warm-grey-3);--input-border-disabled:1px solid var(--input-border-color-disabled);--input-content-color-active:var(--color-warm-grey-11);--input-content-placeholder-color:var(--color-warm-grey-5);--input-t-icon-color:var(--color-warm-grey-5);--checkbox-bg-color:var(--bg-color);--checkbox-checked-bg-color:var(--color-primary-base);--language-picker-dropdown-font-size:var(--dropdown-menu-font-size);--language-picker-dropdown-font-weight:var(--dropdown-menu-font-weight);--language-picker-dropdown-line-height:var(--dropdown-menu-line-height);--language-picker-dropdown-text-color:var(--dropdown-menu-text-color);--language-picker-dropdown-min-width:var(--dropdown-menu-min-width);--language-picker-dropdown-max-width:var(--dropdown-menu-max-width);--language-picker-dropdown-max-height:var(--dropdown-menu-max-height);--language-picker-dropdown-padding:var(--dropdown-menu-padding );--language-picker-dropdown-border-radius:var(--dropdown-menu-border-radius);--language-picker-dropdown-box-shadow:var(--dropdown-menu-box-shadow);--language-picker-dropdown-border-color:var(--dropdown-menu-border-color);--language-picker-dropdown-bg-color:var(--dropdown-menu-bg-color);--language-picker-dropdown-item-padding-horizontal:20px;--language-picker-dropdown-item-padding-vertical:var(--dropdown-menu-item-padding-vertical);--language-picker-dropdown-item-separator-padding-top:var(--dropdown-menu-item-separator-padding-top );--language-picker-dropdown-item-separator-padding-bottom:var(--dropdown-menu-item-separator-padding-bottom );--language-picker-dropdown-item-border-radius:var(--dropdown-menu-item-border-radius);--language-picker-dropdown-item-bg-color-active:var(--dropdown-menu-item-bg-color-active);--language-picker-dropdown-item-bg-color-hover:var(--dropdown-menu-item-bg-color-hover);--language-picker-dropdown-item-bg-color-disabled:var(--dropdown-menu-item-bg-color-disabled);--language-picker-dropdown-item-separator-border-color:var(--dropdown-menu-item-separator-border-color);--language-picker-dropdown-item-color-dangerous:var(--dropdown-menu-item-color-dangerous);--language-picker-dropdown-item-color-disabled:var(--dropdown-menu-item-color-disabled);--language-picker-dropdown-item-color-active:var(--dropdown-menu-item-color-active);--language-picker-dropdown-item-color-hover:var(--dropdown-menu-item-color-hover);--language-picker-dropdown-item-border-color-focused:var(--dropdown-menu-item-border-color-focused);--last-updated-text-color:var(--text-color-secondary);--last-updated-font-size:var(--font-size-sm);--last-updated-font-family:var(--font-family-base);--last-updated-line-height:var(--line-height-base);--link-color-primary:var(--link-color-primary-palette, var(--link-color-primary-legacy));--link-decoration:underline 1px var(--color-primary-base, var(--link-color-primary-legacy));--link-underline-offset:4px;--link-font-weight:var(--font-weight-medium);--link-color-primary-hover:var(--link-color-primary-palette, var(--link-color-primary-hover-legacy));--link-color-primary-pressed:var(--link-color-primary-palette, var(--link-color-primary-pressed-legacy));--link-decoration-hover:underline 2px var(--color-primary-base, var(--link-color-primary-hover-legacy));--link-color-inverse:var(--color-blue-5);--link-color-visited:var(--link-color-primary-palette, var(--link-color-visited-legacy));--link-decoration-visited:underline 1px var(--color-primary-base, var(--link-color-visited-legacy));--load-progress-bar-color:var(--color-info-base);--load-progress-bar-shadow:0 0 10px var(--load-progress-bar-color),0 0 5px var(--load-progress-bar-color);--load-progress-bar-height:2px;--logo-height:2rem;--logo-width:auto;--logo-margin:16px;--logo-max-width:285px;--logo-max-height:285px;--md-content-max-width:688px;--md-content-padding:25px 0px 25px 0px;--md-content-font-size:var(--font-size-base);--md-content-font-family:var(--font-family-base);--md-content-line-height:var(--line-height-base);--md-content-font-weight:var(--font-weight-regular);--md-content-text-color:var(--text-color-secondary);--md-blockquote-margin-vertical:1.5em;--md-blockquote-margin-horizontal:0;--mdLine truncated
:root .panel-request-schemas,:root .panel-response-schemas,:root .panel-callback-schemas,:root .panel-security-schemas,:root .panel-default{--panel-text-color:var(--text-color-primary);--panel-font-family-local:var(--panel-font-family);--panel-font-size-local:var(--panel-font-size);--panel-font-weight-local:var(--panel-font-weight);--panel-bg-color:var(--bg-color);--panel-border-local:var(--panel-border);--panel-border-radius-local:calc(1px + var(--panel-border-radius));--panel-chevron-icon-color:var(--text-color-primary);--panel-body-text-color:var(--panel-text-color);--panel-body-font-family-local:var(--panel-body-font-family);--panel-body-font-size-local:var(--panel-body-font-size);--panel-body-font-weight-local:var(--panel-body-font-weight);--panel-body-padding-local:var(--panel-body-padding);--panel-body-bg-color:var(--bg-color);--panel-body-border:var(--panel-body-border);--panel-heading-text-color:var(--panel-text-color);--panel-heading-font-family-local:var(--panel-heading-font-family);--panel-heading-font-size-local:var(--panel-heading-font-size);--panel-heading-font-weight-local:var(--panel-heading-font-weight);--panel-heading-line-height-local:var(--panel-heading-line-height);--panel-heading-padding-local:var(--panel-heading-padding);--panel-heading-bg-color:var(--bg-color);--panel-heading-border:var(--panel-heading-border);--panel-heading-white-space-local:var(--panel-heading-white-space);}/*!sc*/
:root .panel-samples-default{--text-color:var(--panel-samples-text-color);--panel-text-color:var(--panel-samples-text-color);--panel-font-family-local:var(--panel-samples-font-family);--panel-font-size-local:var(--panel-samples-font-size);--panel-font-weight-local:var(--panel-samples-font-weight);--panel-bg-color:var(--panel-samples-bg-color);--panel-border-local:var(--panel-samples-border);--panel-border-radius-local:calc(1px + var(--panel-border-radius));--panel-chevron-icon-color:var(--panel-samples-chevron-icon-color);--panel-body-text-color:var(--panel-samples-body-text-color);--panel-body-bg-color:var(--panel-samples-body-bg-color);--panel-body-font-family-local:var(--panel-samples-body-font-family);--panel-body-font-size-local:var(--panel-samples-body-font-size);--panel-body-font-weight-local:var(--panel-samples-body-font-weight);--panel-body-padding-local:0;--panel-body-border-local:var(--panel-samples-body-border);--panel-heading-text-color:var(--panel-samples-heading-text-color);--panel-heading-bg-color:var(--panel-samples-heading-bg-color);--panel-heading-font-family-local:var(--panel-samples-heading-font-family);--panel-heading-font-size-local:var(--panel-samples-heading-font-size);--panel-heading-font-weight-local:var(--panel-samples-heading-font-weight);--panel-heading-line-height-local:var(--panel-samples-heading-line-height);--panel-heading-padding-local:var(--panel-samples-heading-padding);--panel-heading-border:var(--panel-samples-heading-border);}/*!sc*/
:root .panel-api-docs,:root .panel-language-list,:root .panel-overview,:root .panel-download,:root .panel-mcp-overview,:root .panel-servers-list,:root .panel-request-samples,:root .panel-response-samples,:root .panel-messages-samples,:root .panel-callback-samples{--text-color:var(--panel-samples-text-color);--panel-text-color:var(--panel-samples-text-color);--panel-font-family-local:var(--panel-samples-font-family);--panel-font-size-local:var(--panel-samples-font-size);--panel-font-weight-local:var(--panel-samples-font-weight);--panel-bg-color:var(--panel-samples-bg-color);--panel-border-local:var(--panel-samples-border);--panel-border-radius-local:var(--panel-border-radius);--panel-chevron-icon-color:var(--panel-samples-chevron-icon-color);--panel-body-text-color:var(--panel-samples-body-text-color);--panel-body-bg-color:var(--panel-samples-body-bg-color);--panel-body-font-family-local:var(--panel-samples-body-font-family);--panel-body-font-size-local:var(--panel-samples-body-font-size);--panel-body-font-weight-local:var(--panel-samples-body-font-weight);--panel-body-padding-local:var(--panel-samples-body-padding);--panel-body-border-local:var(--panel-samples-body-border);--panel-heading-text-color:var(--panel-samples-heading-text-color);--panel-heading-bg-color:var(--panel-samples-heading-bg-color);--panel-heading-font-family-local:var(--panel-samples-heading-font-family);--panel-heading-font-size-local:var(--panel-samples-heading-font-size);--panel-heading-font-weight-local:var(--panel-samples-heading-font-weight);--panel-heading-line-height-local:var(--panel-samples-heading-line-height);--panel-heading-padding-local:var(--panel-samples-heading-padding);--panel-heading-border:var(--panel-samples-heading-border);--panel-heading-border-bottom:var(--panel-samples-border);}/*!sc*/
:root .panel-container-response-samples{--panel-z-index:0;}/*!sc*/
:root .panel-container-request-samples{--panel-z-index:1;}/*!sc*/
:root .button-size-small,:root .button-group-size-small .button{--button-font-size:var(--font-size-base);--button-border-radius:var(--border-radius-md);--button-padding:1px var(--spacing-sm);--button-line-height:var(--line-height-base);--button-icon-size:14px;--button-icon-padding:5px;--button-icon-left-padding:1px var(--spacing-sm) 1px 10px;--button-icon-right-padding:1px 10px 1px var(--spacing-sm);}/*!sc*/
:root .button-size-medium,:root .button-group-size-medium .button{--button-font-size:var(--font-size-base);--button-border-radius:var(--border-radius-lg);--button-padding:5px var(--spacing-sm);--button-line-height:var(--line-height-base);--button-icon-size:16px;--button-icon-padding:var(--spacing-xs);--button-icon-left-padding:5px var(--spacing-base) 5px 14px;--button-icon-right-padding:5px 14px 5px var(--spacing-base);}/*!sc*/
:root .button-size-large,:root .button-group-size-large .button{--button-font-size:var(--font-size-lg);--button-border-radius:var(--border-radius-xl);--button-padding:var(--spacing-xs) var(--spacing-md);--button-line-height:var(--line-height-lg);--button-icon-size:18px;--button-icon-padding:11px;--button-icon-left-padding:var(--spacing-xs) var(--spacing-md) var(--spacing-xs) 18px;--button-icon-right-padding:var(--spacing-xs) 18px var(--spacing-xs) var(--spacing-md);}/*!sc*/
:root .button-variant-primary{--button-color:var(--button-content-color-inverse);--button-color-hover:var(--button-content-color-inverse);--button-color-active:var(--button-content-color-inverse);--button-bg-color:var(--button-bg-color-primary);--button-bg-color-hover:var(--button-bg-color-primary-hover);--button-bg-color-active:var(--button-bg-color-primary-pressed);--button-border-color:transparent;--button-bg-color-danger:var(--button-bg-color-primary-danger);--button-bg-color-danger-hover:var(--button-bg-color-primary-danger-hover);--button-bg-color-danger-pressed:var(--button-bg-color-primary-danger-pressed);--button-border-color-danger:transparent;--button-border-color-danger-hover:transparent;--button-border-color-danger-pressed:transparent;--button-content-color-danger:var(--button-content-color-inverse);--button-content-color-danger-hover:var(--button-content-color-inverse);--button-content-color-danger-pressed:var(--button-content-color-inverse);--button-border-width-disabled:0px;}/*!sc*/
:root .button-variant-secondary{--button-bg-color:var(--button-bg-color-secondary);--button-bg-color-hover:var(--button-bg-color-secondary-hover);--button-bg-color-active:var(--button-bg-color-secondary-pressed);--button-border-color:transparent;--button-border-color-disabled:transparent;--button-bg-color-danger:var(--button-bg-color-secondary-danger);--button-bg-color-danger-hover:var(--button-bg-color-secondary-danger-hover);--button-bg-color-danger-pressed:var(--button-bg-color-secondary-danger-pressed);--button-border-color-danger:transparent;--button-border-color-danger-hover:transparent;--button-border-color-danger-pressed:transparent;}/*!sc*/
:root .button-variant-outlined,:root .button-group-outlined .button{--button-color:var(--button-content-color-custom);--button-color-hover:var(--button-content-color-custom-hover);--button-color-active:var(--button-content-color-custom-hover);--button-border-color:var(--border-color-primary);--button-border-color-hover:var(--border-color-primary);--button-border-color-active:var(--border-color-primary);--button-bg-color-disabled:transparent;--button-border-width:1px;--button-border-width-disabled:1px;--button-bg-color:transparent;--button-border-color-disabled:var(--color-warm-grey-2);--button-border-color-danger:var(--color-raspberry-6);--button-border-color-danger-hover:var(--color-raspberry-7);--button-border-color-danger-pressed:var(--color-raspberry-8);}/*!sc*/
:root .button-group-outlined .button{--button-bg-color-hover:var(--color-hover-base);}/*!sc*/
:root .button-variant-text{--button-bg-color-hover:var(--button-bg-color-secondary);--button-bg-color-active:var(--button-bg-color-secondary-hover);--button-bg-color-disabled:transparent;--button-border-color:transparent;--button-border-color-disabled:transparent;--button-bg-color-danger-hover:var(--button-bg-color-secondary-danger);--button-bg-color-danger-pressed:var(--button-bg-color-secondary-danger-hover);--button-border-color-danger:transparent;--button-border-color-danger-hover:transparent;--button-border-color-danger-pressed:transparent;}/*!sc*/
:root .button-variant-link{--button-color:var(--button-content-color-link);--button-color-hover:var(--button-content-color-link-hover);--button-color-active:var(--button-content-color-link-pressed);--button-bg-color-disabled:transparent;--button-border-color:transparent;--button-border-color-disabled:transparent;--button-border-color-danger:transparent;--button-border-color-danger-hover:transparent;--button-border-color-danger-pressed:transparent;--button-padding:0;--button-icon-left-padding:0;--button-icon-right-padding:0;}/*!sc*/
:root .button-variant-ghost{--button-color:var(--button-content-color-ghost);--button-color-hover:var(--button-content-color);--button-bg-color-disabled:transparent;--button-border-color:transparent;--button-border-color-disabled:transparent;--button-border-color-danger:transparent;--button-border-color-danger-hover:transparent;--button-border-color-danger-pressed:transparent;--button-padding:0;--button-icon-left-padding:0;--button-icon-right-padding:0;}/*!sc*/
:root .button-group-primary .button{--button-group-divider-color:var(--button-color);--button-group-divider-opacity:0.32;}/*!sc*/
:root .button-group-secondary .button{--button-group-divider-color:var(--button-color);--button-group-divider-opacity:0.12;}/*!sc*/
:root .button-group-outlined .button{--button-group-divider-color:var(--button-border-color);--button-group-divider-opacity:1;}/*!sc*/
:root .button-group-primary .button.button-tone-danger,:root .button-group-secondary .button.button-tone-danger{--button-group-divider-color:var(--button-content-color-danger);}/*!sc*/
:root .button-group-outlined .button.button-tone-danger{--button-group-divider-color:var(--button-border-color-danger);}/*!sc*/
:root .card-variant-filled{--card-bg-color:var(--color-warm-grey-1);--card-bg-color-hover:var(--color-primary-bg, var(--color-warm-grey-2));--card-border-width:0;--card-icon-bg-color:var(--color-white);--card-icon-border-color-hover:var(--color-primary-base, var(--card-filled-icon-border-color-hover-legacy));}/*!sc*/
:root .card-variant-outlined{--card-bg-color:transparent;--card-border-color:var(--border-color-secondary);--card-border-color-hover:var(--color-primary-base, var(--card-outlined-border-color-hover-legacy));--card-icon-bg-color:var(--color-warm-grey-1);--card-icon-border-color-hover:var(--card-icon-border-color);--card-icon-bg-color-hover:var(--card-icon-bg-color);}/*!sc*/
:root .card-variant-elevated{--card-bg-color:transparent;--card-border-color:transparent;--card-border-color-hover:transparent;--card-box-shadow:0px 8px 24px -4px #232A6114,0px 6px 12px -6px #232A611F;--card-box-shadow-hover:0px 14px 64px -4px #232A611F,0px 8px 22px -6px #232A611F;--card-icon-border-color-hover:var(--card-icon-border-color);--card-icon-bg-color-hover:var(--card-icon-bg-color);}/*!sc*/
:root .card-variant-ghost{--card-bg-color:transparent;--card-bg-color-hover:var(--layer-color);--card-border-width:0;--card-border-radius:var(--border-radius-xl);--card-padding:var(--spacing-xs) 0;--card-icon-border-color-hover:var(--card-icon-border-color);--card-icon-bg-color-hover:var(--card-icon-bg-color);--card-box-shadow-hover:0 0 0 8px var(--layer-color);--card-hover-transition:initial;}/*!sc*/
:root .card-icon-color-red{--card-icon-color:var(--color-red-7);}/*!sc*/
:root .card-icon-color-green{--card-icon-color:var(--color-green-8);}/*!sc*/
:root .card-icon-color-blue{--card-icon-color:var(--color-blue-7);}/*!sc*/
:root .card-icon-color-grey{--card-icon-color:var(--text-color-secondary);}/*!sc*/
:root .card-icon-color-turquoise{--card-icon-color:var(--color-turquoise-8);}/*!sc*/
:root .card-icon-color-magenta{--card-icon-color:var(--color-magenta-7);}/*!sc*/
:root .card-icon-color-purple{--card-icon-color:var(--color-purple-7);}/*!sc*/
:root .card-icon-color-carrot{--card-icon-color:var(--color-carrot-8);}/*!sc*/
:root .card-icon-color-raspberry{--card-icon-color:var(--color-raspberry-7);}/*!sc*/
:root .card-icon-color-orange{--card-icon-color:var(--color-orange-7);}/*!sc*/
:root .card-icon-color-grass{--card-icon-color:var(--color-grass-8);}/*!sc*/
:root .card-icon-color-persian-green{--card-icon-color:var(--color-persian-green-8);}/*!sc*/
:root .card-icon-color-sky{--card-icon-color:var(--color-sky-8);}/*!sc*/
:root .card-icon-color-blueberry{--card-icon-color:var(--color-blueberry-7);}/*!sc*/
);--input-bg-color:var(--color-white);--input-bg-color-ghost:var(--color-white);--input-font-size:var(--font-size-base);--input-font-family:var(--font-family-base);--input-line-height:1.15em;--input-text-indent:16px;--input-padding:var(--spacing-xs);--input-padding-horizontal:var(--spacing-sm);--input-padding-vertical:var(--spacing-xxs);--input-height:40px;--input-border-radius:var(--border-radius-lg);--input-border-color:var(--color-warm-grey-3);--input-border:1px solid var(--input-border-color);--input-border-color-hover:var(--color-warm-grey-4);--input-border-hover:1px solid var(--input-border-color-hover);--input-border-color-focused:var(--color-info-base);--input-border-focus:1px solid var(--input-border-color-focused);--input-content-color-disabled:var(--color-warm-grey-4);--input-bg-color-disabled:var(--color-warm-grey-1);--input-border-color-disabled:var(--color-warm-grey-3);--input-border-disabled:1px solid var(--input-border-color-disabled);--input-content-color-active:var(--color-warm-grey-11);--input-content-placeholder-color:var(--color-warm-grey-5);--input-t-icon-color:var(--color-warm-grey-5);--checkbox-bg-color:var(--bg-color);--checkbox-checked-bg-color:var(--color-primary-base);--language-picker-dropdown-font-size:var(--dropdown-menu-font-size);--language-picker-dropdown-font-weight:var(--dropdown-menu-font-weight);--language-picker-dropdown-line-height:var(--dropdown-menu-line-height);--language-picker-dropdown-text-color:var(--dropdown-menu-text-color);--language-picker-dropdown-min-width:var(--dropdown-menu-min-width);--language-picker-dropdown-max-width:var(--dropdown-menu-max-width);--language-picker-dropdown-max-height:var(--dropdown-menu-max-height);--language-picker-dropdown-padding:var(--dropdown-menu-padding );--language-picker-dropdown-border-radius:var(--dropdown-menu-border-radius);--language-picker-dropdown-box-shadow:var(--dropdown-menu-box-shadow);--language-picker-dropdown-border-color:var(--dropdown-menu-border-color);--language-picker-dropdown-bg-color:var(--dropdown-menu-bg-color);--language-picker-dropdown-item-padding-horizontal:20px;--language-picker-dropdown-item-padding-vertical:var(--dropdown-menu-item-padding-vertical);--language-picker-dropdown-item-separator-padding-top:var(--dropdown-menu-item-separator-padding-top );--language-picker-dropdown-item-separator-padding-bottom:var(--dropdown-menu-item-separator-padding-bottom );--language-picker-dropdown-item-border-radius:var(--dropdown-menu-item-border-radius);--language-picker-dropdown-item-bg-color-active:var(--dropdown-menu-item-bg-color-active);--language-picker-dropdown-item-bg-color-hover:var(--dropdown-menu-item-bg-color-hover);--language-picker-dropdown-item-bg-color-disabled:var(--dropdown-menu-item-bg-color-disabled);--language-picker-dropdown-item-separator-border-color:var(--dropdown-menu-item-separator-border-color);--language-picker-dropdown-item-color-dangerous:var(--dropdown-menu-item-color-dangerous);--language-picker-dropdown-item-color-disabled:var(--dropdown-menu-item-color-disabled);--language-picker-dropdown-item-color-active:var(--dropdown-menu-item-color-active);--language-picker-dropdown-item-color-hover:var(--dropdown-menu-item-color-hover);--language-picker-dropdown-item-border-color-focused:var(--dropdown-menu-item-border-color-focused);--last-updated-text-color:var(--text-color-secondary);--last-updated-font-size:var(--font-size-sm);--last-updated-font-family:var(--font-family-base);--last-updated-line-height:var(--line-height-base);--link-color-primary:var(--link-color-primary-palette, var(--link-color-primary-legacy));--link-decoration:underline 1px var(--color-primary-base, var(--link-color-primary-legacy));--link-underline-offset:4px;--link-font-weight:var(--font-weight-medium);--link-color-primary-hover:var(--link-color-primary-palette, var(--link-color-primary-hover-legacy));--link-color-primary-pressed:var(--link-color-primary-palette, var(--link-color-primary-pressed-legacy));--link-decoration-hover:underline 2px var(--color-primary-base, var(--link-color-primary-hover-legacy));--link-color-inverse:var(--color-blue-5);--link-color-visited:var(--link-color-primary-palette, var(--link-color-visited-legacy));--link-decoration-visited:underline 1px var(--color-primary-base, var(--link-color-visited-legacy));--load-progress-bar-color:var(--color-info-base);--load-progress-bar-shadow:0 0 10px var(--load-progress-bar-color),0 0 5px var(--load-progress-bar-color);--load-progress-bar-height:2px;--logo-height:2rem;--logo-width:auto;--logo-margin:16px;--logo-max-width:285px;--logo-max-height:285px;--md-content-max-width:688px;--md-content-padding:25px 0px 25px 0px;--md-content-font-size:var(--font-size-base);--md-content-font-family:var(--font-family-base);--md-content-line-height:var(--line-height-base);--md-content-font-weight:var(--font-weight-regular);--md-content-text-color:var(--text-color-secondary);--md-blockquote-margin-vertical:1.5em;--md-blockquote-margin-horizontal:0;--mdLine truncated
:is(.redoc, :where(body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .panel-request-schemas,:is(.redoc, :where(body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .panel-response-schemas,:is(.redoc, :where(body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .panel-callback-schemas,:is(.redoc, :where(body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .panel-security-schemas,:is(.redoc, :where(body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .panel-default{--panel-text-color:var(--text-color-primary);--panel-font-family-local:var(--panel-font-family);--panel-font-size-local:var(--panel-font-size);--panel-font-weight-local:var(--panel-font-weight);--panel-bg-color:var(--bg-color);--panel-border-local:var(--panel-border);--panel-border-radius-local:calc(1px + var(--panel-border-radius));--panel-chevron-icon-color:var(--text-color-primary);--panel-body-text-color:var(--panel-text-color);--panel-body-font-family-local:var(--panel-body-font-family);--panel-body-font-size-local:var(--panel-body-font-size);--panel-body-font-weight-local:var(--panel-body-font-weight);--panel-body-padding-local:var(--panel-body-padding);--panel-body-bg-color:var(--bg-color);--panel-body-border:var(--panel-body-border);--panel-heading-text-color:var(--panel-text-color);--panel-heading-font-family-local:var(--panel-heading-font-family);--panel-heading-font-size-local:var(--panel-heading-font-size);--panel-heading-font-weight-local:var(--panel-heading-font-weight);--panel-heading-line-height-local:var(--panel-heading-line-height);--panel-heading-padding-local:var(--panel-heading-padding);--panel-heading-bg-color:var(--bg-color);--panel-heading-border:var(--panel-heading-border);--panel-heading-white-space-local:var(--panel-heading-white-space);}/*!sc*/
:is(.redoc, :where(body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .panel-samples-default{--text-color:var(--panel-samples-text-color);--panel-text-color:var(--panel-samples-text-color);--panel-font-family-local:var(--panel-samples-font-family);--panel-font-size-local:var(--panel-samples-font-size);--panel-font-weight-local:var(--panel-samples-font-weight);--panel-bg-color:var(--panel-samples-bg-color);--panel-border-local:var(--panel-samples-border);--panel-border-radius-local:calc(1px + var(--panel-border-radius));--panel-chevron-icon-color:var(--panel-samples-chevron-icon-color);--panel-body-text-color:var(--panel-samples-body-text-color);--panel-body-bg-color:var(--panel-samples-body-bg-color);--panel-body-font-family-local:var(--panel-samples-body-font-family);--panel-body-font-size-local:var(--panel-samples-body-font-size);--panel-body-font-weight-local:var(--panel-samples-body-font-weight);--panel-body-padding-local:0;--panel-body-border-local:var(--panel-samples-body-border);--panel-heading-text-color:var(--panel-samples-heading-text-color);--panel-heading-bg-color:var(--panel-samples-heading-bg-color);--panel-heading-font-family-local:var(--panel-samples-heading-font-family);--panel-heading-font-size-local:var(--panel-samples-heading-font-size);--panel-heading-font-weight-local:var(--panel-samples-heading-font-weight);--panel-heading-line-height-local:var(--panel-samples-heading-line-height);--panel-heading-padding-local:var(--panel-samples-heading-padding);--panel-heading-border:var(--panel-samples-heading-border);}/*!sc*/
:is(.redoc, :where(body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .panel-api-docs,:is(.redoc, :where(body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .panel-language-list,:is(.redoc, :where(body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .panel-overview,:is(.redoc, :where(body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .panel-download,:is(.redoc, :where(body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .panel-mcp-overview,:is(.redoc, :where(body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .panel-servers-list,:is(.redoc, :where(body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .panel-request-samples,:is(.redoc, :where(body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .panel-response-samples,:is(.redoc, :where(body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .panel-messages-samples,:is(.redoc, :where(body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .panel-callback-samples{--text-color:var(--panel-samples-text-color);--panel-text-color:var(--panel-samples-text-color);--panel-font-family-local:var(--panel-samples-font-family);--panel-font-size-local:var(--panel-samples-font-size);--panel-font-weight-local:var(--panel-samples-font-weight);--panel-bg-color:var(--panel-samples-bg-color);--panel-border-local:var(--panel-samples-border);--panel-border-radius-local:var(--panel-border-radius);--panel-chevron-icon-color:var(--panel-samples-chevron-icon-color);--panel-body-text-color:var(--panel-samples-body-text-color);--panel-body-bg-color:var(--panel-samples-body-bg-color);--panel-body-font-family-local:var(--panel-samples-body-font-family);--panel-body-font-size-local:var(--panel-samples-body-font-size);--panel-body-font-weight-local:var(--panel-samples-body-font-weight);--panel-body-padding-local:var(--panel-samples-body-padding);--panel-body-border-local:var(--panel-samples-body-border);--panel-heading-text-color:var(--panel-samples-heading-text-color);--panel-heading-bg-color:var(--panel-samples-heading-bg-color);--panel-heading-font-family-local:var(--panel-samples-heading-font-family);--panel-heading-font-size-local:var(--panel-samples-heading-font-size);--panel-heading-font-weight-local:var(--panel-samples-heading-font-weight);--panel-heading-line-height-local:var(--panel-samples-heading-line-height);--panel-heading-padding-local:var(--panel-samples-heading-padding);--panel-heading-border:var(--panel-samples-heading-border);--panel-heading-border-bottom:var(--panel-samples-border);}/*!sc*/
:is(.redoc, :where(body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .panel-container-response-samples{--panel-z-index:0;}/*!sc*/
:is(.redoc, :where(body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .panel-container-request-samples{--panel-z-index:1;}/*!sc*/
:is(.redoc, :where(body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .button-size-small,:is(.redoc, :where(body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .button-group-size-small .button{--button-font-size:var(--font-size-base);--button-border-radius:var(--border-radius-md);--button-padding:1px var(--spacing-sm);--button-line-height:var(--line-height-base);--button-icon-size:14px;--button-icon-padding:5px;--button-icon-left-padding:1px var(--spacing-sm) 1px 10px;--button-icon-right-padding:1px 10px 1px var(--spacing-sm);}/*!sc*/
:is(.redoc, :where(body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .button-size-medium,:is(.redoc, :where(body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .button-group-size-medium .button{--button-font-size:var(--font-size-base);--button-border-radius:var(--border-radius-lg);--button-padding:5px var(--spacing-sm);--button-line-height:var(--line-height-base);--button-icon-size:16px;--button-icon-padding:var(--spacing-xs);--button-icon-left-padding:5px var(--spacing-base) 5px 14px;--button-icon-right-padding:5px 14px 5px var(--spacing-base);}/*!sc*/
:is(.redoc, :where(body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .button-size-large,:is(.redoc, :where(body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .button-group-size-large .button{--button-font-size:var(--font-size-lg);--button-border-radius:var(--border-radius-xl);--button-padding:var(--spacing-xs) var(--spacing-md);--button-line-height:var(--line-height-lg);--button-icon-size:18px;--button-icon-padding:11px;--button-icon-left-padding:var(--spacing-xs) var(--spacing-md) var(--spacing-xs) 18px;--button-icon-right-padding:var(--spacing-xs) 18px var(--spacing-xs) var(--spacing-md);}/*!sc*/
:is(.redoc, :where(body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .button-variant-primary{--button-color:var(--button-content-color-inverse);--button-color-hover:var(--button-content-color-inverse);--button-color-active:var(--button-content-color-inverse);--button-bg-color:var(--button-bg-color-primary);--button-bg-color-hover:var(--button-bg-color-primary-hover);--button-bg-color-active:var(--button-bg-color-primary-pressed);--button-border-color:transparent;--button-bg-color-danger:var(--button-bg-color-primary-danger);--button-bg-color-danger-hover:var(--button-bg-color-primary-danger-hover);--button-bg-color-danger-pressed:var(--button-bg-color-primary-danger-pressed);--button-border-color-danger:transparent;--button-border-color-danger-hover:transparent;--button-border-color-danger-pressed:transparent;--button-content-color-danger:var(--button-content-color-inverse);--button-content-color-danger-hover:var(--button-content-color-inverse);--button-content-color-danger-pressed:var(--button-content-color-inverse);--button-border-width-disabled:0px;}/*!sc*/
:is(.redoc, :where(body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .button-variant-secondary{--button-bg-color:var(--button-bg-color-secondary);--button-bg-color-hover:var(--button-bg-color-secondary-hover);--button-bg-color-active:var(--button-bg-color-secondary-pressed);--button-border-color:transparent;--button-border-color-disabled:transparent;--button-bg-color-danger:var(--button-bg-color-secondary-danger);--button-bg-color-danger-hover:var(--button-bg-color-secondary-danger-hover);--button-bg-color-danger-pressed:var(--button-bg-color-secondary-danger-pressed);--button-border-color-danger:transparent;--button-border-color-danger-hover:transparent;--button-border-color-danger-pressed:transparent;}/*!sc*/
:is(.redoc, :where(body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .button-variant-outlined,:is(.redoc, :where(body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .button-group-outlined .button{--button-color:var(--button-content-color-custom);--button-color-hover:var(--button-content-color-custom-hover);--button-color-active:var(--button-content-color-custom-hover);--button-border-color:var(--border-color-primary);--button-border-color-hover:var(--border-color-primary);--button-border-color-active:var(--border-color-primary);--button-bg-color-disabled:transparent;--button-border-width:1px;--button-border-width-disabled:1px;--button-bg-color:transparent;--button-border-color-disabled:var(--color-warm-grey-2);--button-border-color-danger:var(--color-raspberry-6);--button-border-color-danger-hover:var(--color-raspberry-7);--button-border-color-danger-pressed:var(--color-raspberry-8);}/*!sc*/
:is(.redoc, :where(body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .button-group-outlined .button{--button-bg-color-hover:var(--color-hover-base);}/*!sc*/
:is(.redoc, :where(body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .button-variant-text{--button-bg-color-hover:var(--button-bg-color-secondary);--button-bg-color-active:var(--button-bg-color-secondary-hover);--button-bg-color-disabled:transparent;--button-border-color:transparent;--button-border-color-disabled:transparent;--button-bg-color-danger-hover:var(--button-bg-color-secondary-danger);--button-bg-color-danger-pressed:var(--button-bg-color-secondary-danger-hover);--button-border-color-danger:transparent;--button-border-color-danger-hover:transparent;--button-border-color-danger-pressed:transparent;}/*!sc*/
:is(.redoc, :where(body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .button-variant-link{--button-color:var(--button-content-color-link);--button-color-hover:var(--button-content-color-link-hover);--button-color-active:var(--button-content-color-link-pressed);--button-bg-color-disabled:transparent;--button-border-color:transparent;--button-border-color-disabled:transparent;--button-border-color-danger:transparent;--button-border-color-danger-hover:transparent;--button-border-color-danger-pressed:transparent;--button-padding:0;--button-icon-left-padding:0;--button-icon-right-padding:0;}/*!sc*/
:is(.redoc, :where(body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .button-variant-ghost{--button-color:var(--button-content-color-ghost);--button-color-hover:var(--button-content-color);--button-bg-color-disabled:transparent;--button-border-color:transparent;--button-border-color-disabled:transparent;--button-border-color-danger:transparent;--button-border-color-danger-hover:transparent;--button-border-color-danger-pressed:transparent;--button-padding:0;--button-icon-left-padding:0;--button-icon-right-padding:0;}/*!sc*/
:is(.redoc, :where(body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .button-group-primary .button{--button-group-divider-color:var(--button-color);--button-group-divider-opacity:0.32;}/*!sc*/
:is(.redoc, :where(body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .button-group-secondary .button{--button-group-divider-color:var(--button-color);--button-group-divider-opacity:0.12;}/*!sc*/
:is(.redoc, :where(body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .button-group-outlined .button{--button-group-divider-color:var(--button-border-color);--button-group-divider-opacity:1;}/*!sc*/
:is(.redoc, :where(body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .button-group-primary .button.button-tone-danger,:is(.redoc, :where(body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .button-group-secondary .button.button-tone-danger{--button-group-divider-color:var(--button-content-color-danger);}/*!sc*/
:is(.redoc, :where(body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .button-group-outlined .button.button-tone-danger{--button-group-divider-color:var(--button-border-color-danger);}/*!sc*/
:is(.redoc, :where(body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .card-variant-filled{--card-bg-color:var(--color-warm-grey-1);--card-bg-color-hover:var(--color-primary-bg, var(--color-warm-grey-2));--card-border-width:0;--card-icon-bg-color:var(--color-white);--card-icon-border-color-hover:var(--color-primary-base, var(--card-filled-icon-border-color-hover-legacy));}/*!sc*/
:is(.redoc, :where(body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .card-variant-outlined{--card-bg-color:transparent;--card-border-color:var(--border-color-secondary);--card-border-color-hover:var(--color-primary-base, var(--card-outlined-border-color-hover-legacy));--card-icon-bg-color:var(--color-warm-grey-1);--card-icon-border-color-hover:var(--card-icon-border-color);--card-icon-bg-color-hover:var(--card-icon-bg-color);}/*!sc*/
:is(.redoc, :where(body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .card-variant-elevated{--card-bg-color:transparent;--card-border-color:transparent;--card-border-color-hover:transparent;--card-box-shadow:0px 8px 24px -4px #232A6114,0px 6px 12px -6px #232A611F;--card-box-shadow-hover:0px 14px 64px -4px #232A611F,0px 8px 22px -6px #232A611F;--card-icon-border-color-hover:var(--card-icon-border-color);--card-icon-bg-color-hover:var(--card-icon-bg-color);}/*!sc*/
:is(.redoc, :where(body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .card-variant-ghost{--card-bg-color:transparent;--card-bg-color-hover:var(--layer-color);--card-border-width:0;--card-border-radius:var(--border-radius-xl);--card-padding:var(--spacing-xs) 0;--card-icon-border-color-hover:var(--card-icon-border-color);--card-icon-bg-color-hover:var(--card-icon-bg-color);--card-box-shadow-hover:0 0 0 8px var(--layer-color);--card-hover-transition:initial;}/*!sc*/
:is(.redoc, :where(body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .card-icon-color-red{--card-icon-color:var(--color-red-7);}/*!sc*/
:is(.redoc, :where(body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .card-icon-color-green{--card-icon-color:var(--color-green-8);}/*!sc*/
:is(.redoc, :where(body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .card-icon-color-blue{--card-icon-color:var(--color-blue-7);}/*!sc*/
:is(.redoc, :where(body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .card-icon-color-grey{--card-icon-color:var(--text-color-secondary);}/*!sc*/
:is(.redoc, :where(body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .card-icon-color-turquoise{--card-icon-color:var(--color-turquoise-8);}/*!sc*/
:is(.redoc, :where(body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .card-icon-color-magenta{--card-icon-color:var(--color-magenta-7);}/*!sc*/
:is(.redoc, :where(body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .card-icon-color-purple{--card-icon-color:var(--color-purple-7);}/*!sc*/
:is(.redoc, :where(body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .card-icon-color-carrot{--card-icon-color:var(--color-carrot-8);}/*!sc*/
:is(.redoc, :where(body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .card-icon-color-raspberry{--card-icon-color:var(--color-raspberry-7);}/*!sc*/
:is(.redoc, :where(body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .card-icon-color-orange{--card-icon-color:var(--color-orange-7);}/*!sc*/
:is(.redoc, :where(body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .card-icon-color-grass{--card-icon-color:var(--color-grass-8);}/*!sc*/
:is(.redoc, :where(body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .card-icon-color-persian-green{--card-icon-color:var(--color-persian-green-8);}/*!sc*/
:is(.redoc, :where(body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .card-icon-color-sky{--card-icon-color:var(--color-sky-8);}/*!sc*/
:is(.redoc, :where(body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .card-icon-color-blueberry{--card-icon-color:var(--color-blueberry-7);}/*!sc*/
@font-face{font-family:'TreeViewIcons';src:url('data:application/font-woff;base64,d09GRgABAAAAAAgYAAsAAAAAEGAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAABHU1VCAAABCAAAADsAAABUIIslek9TLzIAAAFEAAAAPwAAAFY1UkH9Y21hcAAAAYQAAAB/AAACCtvO7yxnbHlmAAACBAAAA+MAAAlACm1VqmhlYWQAAAXoAAAAKgAAADZfxj5jaGhlYQAABhQAAAAYAAAAJAFbAMFobXR4AAAGLAAAAA4AAAA0CGQAAGxvY2EAAAY8AAAAHAAAABwM9A9CbWF4cAAABlgAAAAfAAAAIAEgAHZuYW1lAAAGeAAAATcAAAJSfUrk+HBvc3QAAAewAAAAZgAAAIka0DSfeJxjYGRgYOBiMGCwY2BycfMJYeDLSSzJY5BiYGGAAJA8MpsxJzM9kYEDxgPKsYBpDiBmg4gCACY7BUgAeJxjYGRYyjiBgZWBgaGQoRZISkLpUAYOBj0GBiYGVmYGrCAgzTWFweEV4ysehs1ArgDDFgZGIA3CDAB2tQjAAHic7ZHLEcMwCESfLCz/VEoKSEE5parURxMOC4c0Ec283WGFdABgBXrwCAzam4bOK9KWeefM3Hhmjyn3ed+hTRq1pS7Ra/HjYGPniHcXMy4G/zNTP7/KW5HTXArkvdBW3ArN19dCG/NRIN8K5HuB/CiQn4U26VeBfBbML9NEH78AeJyVVc1u20YQ3pn905JcSgr/YsuSDTEg3cR1bFEkYyS1HQcQ2jQF2hot6vYSoECKnnPLA/SWUy9NTr31Bfp+6azsNI0SGiolzu7ODnfn+2Z2lnHG3rxhr9nfLGKbLGesncAYYnUHpsVnMG/uwyzNdFIVd6HI6twp8+R3LpT4TSglLoTHwwJgG2/dFvKrl9yI507/p5CCq4LTxB/PlPjkFaMHnWB/0S9je7RTPS+utnGtom1T2q5pk/e3H0M1S18rsXAL7wgpxQuhAmteGGvNjmcfGXuwnFNOPCXxeOGmnjrBLWNyBeNtVq2Hs03yus1aPS3mzSyNVSfu588iW1Q93x/4fjcHn+5EkS2tMxr4xIRa8ese+4L9uKZnxEqs8+ldyN9atU02a5t5uQ8hZGms1QTKpaKYqnipiNNOAIeIADC0JNEOYY+jtSgFoOchiAjRGFACpUTRje8bwIYWGCDEgENY8MEu9bnCYCdAxftoNg0KiSpUtPaHcanYwzXRu6T4r40b5npal3V7UHWCPJW9niyl1vIHgoujEXZjudBkeWkOeMQBRmbEPhKzij1i52t6/TadL+3q7H0U1eq4E8cG4gIIwQLx8VX7ToPXgPrehVc5QXHR7gMSmwjKfaYAP4KvZV+yn9bE18y2IY37LvtyrSg3i7ZK++B603ndlg/gBJpZRsfpBI6hyiaQ6FjlnThz8lAC3LgBIMnXDOAXxBQ4SIgiEhx2AcGCAwAhwjXRpCQms42bwAUt75BvAwgONzdgOfWEwzk4Ylzj4mz+5YEzzXzWX9aNlk7ot65y5QnBHsNlm6zDTu7sspRqG4V+fgJ1lVBZ07Nm7s5nemo3Lf3PO7iwtnroQ5/YDGwPRUip6fV6L+27p+wCHwSvPs85UnHqId8NAn5IBsKdv95KrL9m31Gsf2a/rluDslk1y1J9GE+LUmmVT/OyOHaFKGnapt2H5XeJTmKd6qYNoVVZOy+pWzr7rMip3ndG/4mQSoUcMbAqG/YNIAdXhkAqTVruXhocSKN0iS4Rwj7vSS4fcF/La07BfeQSuRAcFeW+9igjwPhhYPpGCBCBHhxiKMyFMFT7ziRH7RtfIWdiha+TdW+Rqs7bLHdN2ZJIKl0um0x3op9saYr0REeRdj09pl43pMzz4tjztrY8L4o8bzT+oLY27PR/eFtXs/YY5vtwB5Iqad14eYN0ujveMaGWqkdU3TKbQSC5Uvxaf4fA7SAQ3r2tEfIhd4duld91bwMisjqBw22orthNcroXl7KqO1329HBgAexgoCfGAwiDPoBnriki3lmNojrzvD0tjo6E3vPYP6E2BMIAeJxjYGRgYADiY8t3FsTz23xl4GbYzIAB/v9nWM6wBcjgYGAC8QH+QQhZAAB4nGNgZGBg2MzAACeXMzAyoAJeADPyAh14nGNgAILNpGEA0fgIZQAAAAAAAAA2AHIAvgE+AZgCCAKMAv4DlgPsBEYEoHicY2BkYGDgZchi4GQAASYg5gJCBob/YD4DABTSAZcAeJx9kU1uwjAQhV/4qwpqhdSqi67cTTeVEmBXDgBbhBD7AHYISuLUMSD2PUdP0HNwjp6i676k3qQS9Ujjb968mYUNoI8zPJTHw02Vy9PAFatfbpLuHbfIT47b6MF33KH+6riLF0wc93CHN27wWtdUHvHuuIFbfDhuUv903CKfHbfxgC/HHerfjrtYen3HPTx7ambiIl0YKQ+xPM5ltE9CU9NqxVKaItaZGPqDmj6VmTShlRuxOoniEI2sVUIZnYqJzqxMEi1yo3dybf2ttfk4CJTT/bVOMYNBjAIpFiTJOLCWOGLOHGGPBCE7l32XO0tmw04MjQwCQ7774B//lDmrZkJY3hvOrHBiLuiJMKJqoVgrejQ3CP5Yubt0JwxNJa96Oypr6j621VSOMQKG+uP36eKmHylcb0MAeJxtwdEOgjAMBdBeWEFR/Mdl7bTJtMsygc/nwVfPoYF+QP+tGDAigDFhxgVXLLjhjhUPCtmKTtmLaGN7x6dy/Io5bybqoevRQ3LRObb0sk3HKpn1SFqW6ru26vbpYfcmRCccJhqsAAA=') format('woff');}/*!sc*/
:root *{box-sizing:border-box;}/*!sc*/
:root .menu-item-type-separator{--menu-item-label-chevron-offset:0px;}/*!sc*/
:root .menu-item-type-separator-primary{--menu-item-font-weight:var(--font-weight-bold);--menu-item-text-color:var(--text-color-primary);--menu-item-font-size:var(--sidebar-font-size);--menu-item-line-height:var(--line-height-base);}/*!sc*/
:root .menu-item-type-separator-secondary{--menu-item-font-weight:var(--font-weight-bold);--menu-item-text-color:var(--text-color-secondary);--menu-item-font-size:var(--font-size-sm);--menu-item-line-height:var(--line-height-sm);}/*!sc*/
:root .panel-response-success,:root .panel-response-info,:root .panel-response-error,:root .panel-response-redirect,:root .panel-response-callback{--panel-body-text-color:var(--panel-response-body-text-color);--panel-border-radius-local:calc(1px + var(--panel-border-radius));--panel-heading-text-color:var(--panel-response-heading-text-color);--panel-heading-font-size-local:var(--panel-response-heading-font-size);--panel-heading-font-weight-local:var(--panel-response-heading-font-weight);--panel-heading-line-height-local:var(--panel-response-heading-line-height);--panel-heading-padding-local:var(--panel-response-heading-padding);--panel-heading-border:var(--panel-samples-heading-border);--panel-heading-white-space-local:normal;--panel-body-padding-local:var(--panel-body-padding);}/*!sc*/
:root .panel-response-success{--panel-border-local:1px solid var(--panel-response-success-border-color);--panel-body-text-color:var(--panel-response-success-body-text-color);--panel-body-bg-color:var(--panel-response-success-body-bg-color);--panel-heading-bg-color:var(--panel-response-success-heading-bg-color);--panel-heading-text-color:var(--panel-response-success-heading-text-color);}/*!sc*/
:root .panel-response-info{--panel-border-local:1px solid var(--panel-response-info-border-color);--panel-body-text-color:var(--panel-response-info-body-text-color);--panel-body-bg-color:var(--panel-response-info-body-bg-color);--panel-heading-bg-color:var(--panel-response-info-heading-bg-color);--panel-heading-text-color:var(--panel-response-info-heading-text-color);}/*!sc*/
:root .panel-response-error{--panel-border-local:1px solid var(--panel-response-error-border-color);--panel-body-text-color:var(--panel-response-error-body-text-color);--panel-body-bg-color:var(--panel-response-error-body-bg-color);--panel-heading-bg-color:var(--panel-response-error-heading-bg-color);--panel-heading-text-color:var(--panel-response-error-heading-text-color);}/*!sc*/
:root .panel-response-redirect{--panel-border-local:1px solid var(--panel-response-redirect-border-color);--panel-body-text-color:var(--panel-response-redirect-body-text-color);--panel-body-bg-color:var(--panel-response-redirect-body-bg-color);--panel-heading-bg-color:var(--panel-response-redirect-heading-bg-color);--panel-heading-text-color:var(--panel-response-redirect-heading-text-color);}/*!sc*/
:root .panel-response-callback{--panel-body-text-color:var(--panel-response-callback-body-text-color);--panel-body-bg-color:var(--panel-response-callback-body-bg-color);--panel-heading-text-color:var(--panel-response-callback-heading-text-color);--panel-body-padding-local:0 0 var(--spacing-base) var(--spacing-lg);}/*!sc*/
:root .tag-size-large{--tag-padding:var(--tag-large-padding);--tag-content-padding:var(--tag-large-content-padding);--tag-font-size:var(--font-size-base);}/*!sc*/
:root .tag-grey,:root .tag-draft,:root .tag-schema,:root .tag-default{--tag-color:var(--text-color-secondary);--tag-bg-color:var(--color-warm-grey-2);--tag-border-color:var(--border-color-primary);--tag-bg-color-hover:var(--color-warm-grey-3);--tag-close-button-bg-color-hover:var(--color-warm-grey-3);--tag-border-color-hover:var(--color-warm-grey-5);}/*!sc*/
:root .tag-green{--tag-color:var(--color-green-8);--tag-bg-color:var(--color-green-1);--tag-border-color:var(--color-green-5);--tag-bg-color-hover:var(--color-green-2);--tag-close-button-bg-color-hover:var(--color-green-2);--tag-border-color-hover:var(--color-green-6);}/*!sc*/
:root .tag-success,:root .tag-approved{--tag-color:var(--color-success-active);--tag-bg-color:var(--color-success-bg);--tag-border-color:var(--color-success-border);--tag-close-button-bg-color-hover:var(--color-success-bg-hover);--tag-bg-color-hover:var(--color-success-bg-hover);--tag-border-color-hover:var(--color-success-border-hover);}/*!sc*/
:root .tag-red{--tag-color:var(--color-red-7);--tag-bg-color:var(--color-red-1);--tag-border-color:var(--color-red-3);--tag-close-button-bg-color-hover:var(--color-red-2);--tag-bg-color-hover:var(--color-red-2);--tag-border-color-hover:var(--color-red-5);}/*!sc*/
:root .tag-error,:root .tag-declined{--tag-color:var(--color-error-active);--tag-bg-color:var(--color-error-bg);--tag-border-color:var(--color-error-border);--tag-close-button-bg-color-hover:var(--color-error-hover);--tag-bg-color-hover:var(--color-error-bg-hover);--tag-border-color-hover:var(--color-error-border-hover);}/*!sc*/
:root .tag-warning,:root .tag-deprecated{--tag-color:var(--color-warning-active);--tag-bg-color:var(--color-warning-bg);--tag-border-color:var(--color-warning-border);--tag-close-button-bg-color-hover:var(--color-warning-hover);--tag-bg-color-hover:var(--color-warning-bg-hover);--tag-border-color-hover:var(--color-warning-border-hover);}/*!sc*/
:root .tag-blue{--tag-color:var(--color-blue-7);--tag-bg-color:var(--color-blue-1);--tag-border-color:var(--color-blue-4);--tag-bg-color-hover:var(--color-blue-2);--tag-close-button-bg-color-hover:var(--color-blue-2);--tag-border-color-hover:var(--color-blue-6);}/*!sc*/
:root .tag-processing,:root .tag-info{--tag-color:var(--color-info-active);--tag-bg-color:var(--color-info-bg);--tag-border-color:var(--color-info-border);--tag-close-button-bg-color-hover:var(--color-info-hover);--tag-bg-color-hover:var(--color-info-bg-hover);--tag-border-color-hover:var(--color-info-border-hover);}/*!sc*/
:root .tag-link{--tag-color:var(--color-info-active);--tag-bg-color:var(--color-info-bg);--tag-border-color:var(--color-info-border);--tag-close-button-bg-color-hover:var(--color-info-hover);--tag-bg-color-hover:var(--color-info-bg-hover);--tag-border-color-hover:var(--color-info-border-hover);}/*!sc*/
:root .tag-purple{--tag-color:var(--color-purple-7);--tag-bg-color:var(--color-purple-1);--tag-border-color:var(--color-purple-4);--tag-bg-color-hover:var(--color-purple-2);--tag-close-button-bg-color-hover:var(--color-purple-2);--tag-border-color-hover:var(--color-purple-6);}/*!sc*/
:root .tag-turquoise,:root .tag-option{--tag-color:var(--color-turquoise-8);--tag-bg-color:var(--color-turquoise-1);--tag-border-color:var(--color-turquoise-5);--tag-bg-color-hover:var(--color-turquoise-2);--tag-close-button-bg-color-hover:var(--color-turquoise-2);--tag-border-color-hover:var(--color-turquoise-7);}/*!sc*/
:root .tag-carrot{--tag-color:var(--color-carrot-8);--tag-bg-color:var(--color-carrot-1);--tag-border-color:var(--color-carrot-6);--tag-bg-color-hover:var(--color-carrot-2);--tag-close-button-bg-color-hover:var(--color-carrot-2);--tag-border-color-hover:var(--color-carrot-7);}/*!sc*/
:root .tag-magenta,:root .tag-hook{--tag-color:var(--color-magenta-7);--tag-bg-color:var(--color-magenta-1);--tag-border-color:var(--color-magenta-3);--tag-bg-color-hover:var(--color-magenta-2);--tag-close-button-bg-color-hover:var(--color-magenta-2);--tag-border-color-hover:var(--color-magenta-5);}/*!sc*/
:root .tag-product{--tag-color:var(--color-warm-grey-7);--tag-bg-color:transparent;--tag-border-color:var(--color-warm-grey-4);--tag-close-button-bg-color-hover:var(--color-warm-grey-3);}/*!sc*/
:root .tag-grass{--tag-color:var(--color-grass-8);--tag-bg-color:var(--color-grass-1);--tag-border-color:var(--color-grass-4);--tag-bg-color-hover:var(--color-grass-2);--tag-close-button-bg-color-hover:var(--color-grass-2);--tag-border-color-hover:var(--color-grass-6);}/*!sc*/
:root .tag-sky{--tag-color:var(--color-sky-8);--tag-bg-color:var(--color-sky-1);--tag-border-color:var(--color-sky-5);--tag-bg-color-hover:var(--color-sky-2);--tag-close-button-bg-color-hover:var(--color-sky-2);--tag-border-color-hover:var(--color-sky-7);}/*!sc*/
:root .tag-raspberry{--tag-color:var(--color-raspberry-7);--tag-bg-color:var(--color-raspberry-1);--tag-border-color:var(--color-raspberry-3);--tag-bg-color-hover:var(--color-raspberry-2);--tag-close-button-bg-color-hover:var(--color-raspberry-2);--tag-border-color-hover:var(--color-raspberry-5);}/*!sc*/
:root .tag-orange{--tag-color:var(--color-orange-7);--tag-bg-color:var(--color-orange-1);--tag-border-color:var(--color-orange-4);--tag-bg-color-hover:var(--color-orange-2);--tag-close-button-bg-color-hover:var(--color-orange-2);--tag-border-color-hover:var(--color-orange-5);}/*!sc*/
:root .tag-persian-green{--tag-color:var(--color-persian-green-8);--tag-bg-color:var(--color-persian-green-1);--tag-border-color:var(--color-persian-green-5);--tag-bg-color-hover:var(--color-persian-green-2);--tag-close-button-bg-color-hover:var(--color-persian-green-2);--tag-border-color-hover:var(--color-persian-green-6);}/*!sc*/
:root .tag-blueberry{--tag-color:var(--color-blueberry-7);--tag-bg-color:var(--color-blueberry-1);--tag-border-color:var(--color-blueberry-4);--tag-bg-color-hover:var(--color-blueberry-2);--tag-close-button-bg-color-hover:var(--color-blueberry-2);--tag-border-color-hover:var(--color-blueberry-6);}/*!sc*/
:root .tag-http{--tag-font-family:var(--font-family-monospaced);--tag-text-transform:uppercase;}/*!sc*/
:root .tag-head{--tag-color:#6355CA;--tag-bg-color:#ECEBFA;--tag-bg-color-hover:#E0DEF7;}/*!sc*/
:root .tag-delete{--tag-color:#e70b46;--tag-bg-color:#fee2e9;--tag-bg-color-hover:#fdd3dd;}/*!sc*/
:root .tag-get{--tag-color:#25b869;--tag-bg-color:#e5faef;--tag-bg-color-hover:#D4F7E5;}/*!sc*/
:root .tag-post{--tag-color:#1e65f5;--tag-bg-color:#e2ebfe;--tag-bg-color-hover:#CEDDFD;}/*!sc*/
:root .tag-query{--tag-color:#25b869;--tag-bg-color:#e5faef;--tag-bg-color-hover:#D4F7E5;}/*!sc*/
:root .tag-put{--tag-color:#f5901d;--tag-bg-color:#fef1e2;--tag-bg-color-hover:#FDE2C4;}/*!sc*/
:root .tag-patch{--tag-color:#f5c31d;--tag-bg-color:#fdf6dd;--tag-bg-color-hover:#FCF0C5;}/*!sc*/
:root .tag-http-deprecated{--tag-color:var(--text-color-disabled);}/*!sc*/
:root .tag-http-additional-operation{--tag-color:#F97316;--tag-bg-color:#faede3;--tag-bg-color-hover:#fae2cf;}/*!sc*/
:root .tag-send{--tag-color:#4144F6;--tag-bg-color:#ECECFE;--tag-bg-color-hover:#CFCFFC;}/*!sc*/
:root .tag-pub{--tag-color:#4144F6;--tag-bg-color:#ECECFE;--tag-bg-color-hover:#CFCFFC;}/*!sc*/
:root .tag-publish{--tag-color:#4144F6;--tag-bg-color:#ECECFE;--tag-bg-color-hover:#CFCFFC;}/*!sc*/
:root .tag-receive{--tag-color:#4db144;--tag-bg-color:#E5FDE2;--tag-bg-color-hover:#D4FCCF;}/*!sc*/
:root .tag-sub{--tag-color:#4db144;--tag-bg-color:#E5FDE2;--tag-bg-color-hover:#D4FCCF;}/*!sc*/
:root .tag-cons{--tag-color:#4db144;--tag-bg-color:#E5FDE2;--tag-bg-color-hover:#D4FCCF;}/*!sc*/
:root .tag-channel{--tag-color:#F0870E;--tag-bg-color:#FDF1E2;--tag-bg-color-hover:#FBE1C1;}/*!sc*/
:root .tag-topic{--tag-color:#F0870E;--tag-bg-color:#FDF1E2;--tag-bg-color-hover:#FBE1C1;}/*!sc*/
:root .tag-queue{--tag-color:#D72E81;--tag-bg-color:#FAE5F0;--tag-bg-color-hover:#F7D4E6;}/*!sc*/
:root .tag-exchange{--tag-color:#9B51E0;--tag-bg-color:#F0E5FA;--tag-bg-color-hover:#E6D4F7;}/*!sc*/
:root .tag-exch{--tag-color:#9B51E0;--tag-bg-color:#F0E5FA;--tag-bg-color-hover:#E6D4F7;}/*!sc*/
:root .tag-variant-filled{--tag-border-color:transparent;--tag-border-color-hover:transparent;}/*!sc*/
:root .tag-variant-outline{--tag-bg-color:transparent;--tag-bg-color-hover:transparent;}/*!sc*/
:root .tooltip-copy-button{--tooltip-text-color:var(--color-static-white);--tooltip-bg-color:var(--color-warm-grey-10);--tooltip-arrow-color:var(--tooltip-border-color);}/*!sc*/
:root .tooltip-catalog{--tooltip-text-color:var(--text-color-primary);--tooltip-bg-color:var(--layer-color);--tooltip-arrow-color:var(--border-color-secondary);--tooltip-border-width:2px;--tooltip-border-color:var(--border-color-secondary);--tooltip-padding-vertical:var(--spacing-xs);--tooltip-padding-horizontal:var(--spacing-sm);--tooltip-padding:var(--tooltip-padding-vertical) var(--tooltip-padding-horizontal);--tooltip-max-width:300px;}/*!sc*/
:root *::-webkit-scrollbar{width:6px;height:6px;}/*!sc*/
:root *::-webkit-scrollbar-track{background-color:transparent;}/*!sc*/
:root *::-webkit-scrollbar-thumb{background-color:var(--color-warm-grey-2);border-radius:var(--border-radius-full);}/*!sc*/
:root *::-webkit-scrollbar-thumb:hover{background:var(--bg-color-tonal);}/*!sc*/
:root *::-webkit-scrollbar-corner{background-color:transparent;}/*!sc*/
@media screen and (max-width: 500px){:root{--feedback-comment-max-width:100%;}}/*!sc*/
@media (prefers-reduced-motion: reduce){:root{--replay-transition-duration:0s;}}/*!sc*/
:root.dark{--color-warm-grey-1:#1a1c21;--color-warm-grey-2:#22242b;--color-warm-grey-3:#2a2b33;--color-warm-grey-4:#3b3c45;--color-warm-grey-5:#555761;--color-warm-grey-6:#6e6f7a;--color-warm-grey-7:#9b9ca8;--color-warm-grey-8:#c4c6d1;--color-warm-grey-9:#dcdde5;--color-warm-grey-10:#ededf2;--color-warm-grey-11:#fbfbfc;--color-purple-1:#2c2770;--color-purple-2:#2b2480;--color-purple-3:#291f99;--color-purple-4:#4034ab;--color-purple-5:#5b4ccc;--color-purple-6:#7a69ee;--color-purple-7:#9c8ef4;--color-purple-8:#b3a6f9;--color-purple-9:#cec4fd;--color-purple-10:#e6e1fe;--color-purple-11:#f3f1fe;--color-blueberry-1:#232a61;--color-blueberry-2:#222d7a;--color-blueberry-3:#1e2f93;--color-blueberry-4:#2f46b7;--color-blueberry-5:#4561db;--color-blueberry-6:#5f81ff;--color-blueberry-7:#87a3ff;--color-blueberry-8:#9fb7ff;--color-blueberry-9:#bfd0ff;--color-blueberry-10:#dfe8ff;--color-blueberry-11:#F0F4F;--color-blue-1:#233061;--color-blue-2:#223d7a;--color-blue-3:#1f4399;--color-blue-4:#2558b8;--color-blue-5:#3072db;--color-blue-6:#1f7cff;--color-blue-7:#6eabfa;--color-blue-8:#90c2fc;--color-blue-9:#b3d7fc;--color-blue-10:#d9ebfc;--color-blue-11:#edf5fc;--color-sky-1:#1f3757;--color-sky-2:#1d4479;--color-sky-3:#155393;--color-sky-4:#0569b6;--color-sky-5:#088cda;--color-sky-6:#0bb4fe;--color-sky-7:#47d2fe;--color-sky-8:#6ce5fe;--color-sky-9:#9df4fe;--color-sky-10:#cefdfe;--color-sky-11:#eafefe;--color-turquoise-1:#1c394f;--color-turquoise-2:#1c496b;--color-turquoise-3:#125a81;--color-turquoise-4:#0475a1;--color-turquoise-5:#059cc0;--color-turquoise-6:#08c7e0;--color-turquoise-7:#41e6ec;--color-turquoise-8:#67f5ef;--color-turquoise-9:#9afbef;--color-turquoise-10:#ccfdf3;--color-turquoise-11:#ebfdf9;--color-persian-green-1:#193b47;--color-persian-green-2:#143d4d;--color-persian-green-3:#0f4f5d;--color-persian-green-4:#036a73;--color-persian-green-5:#048a89;--color-persian-green-6:#06a192;--color-persian-green-7:#37c6ab;--color-persian-green-8:#5fe2be;--color-persian-green-9:#97f5d2;--color-persian-green-10:#cafae4;--color-persian-green-11:#ebfaf3;--color-green-1:#1a4d40;--color-green-2:#195848;--color-green-3:#136a4d;--color-green-4:#0e8450;--color-green-5:#149e53;--color-green-6:#1cb854;--color-green-7:#4dd470;--color-green-8:#72e985;--color-green-9:#a3f7a9;--color-green-10:#d2fbd0;--color-green-11:#edfbec;--color-grass-1:#1f4d2d;--color-grass-2:#164f29;--color-grass-3:#135f25;--color-grass-4:#167625;--color-grass-5:#208d28;--color-grass-6:#2fa52d;--color-grass-7:#66c95b;--color-grass-8:#93e380;--color-grass-9:#c2f6ae;--color-grass-10:#e3fad6;--color-grass-11:#f0faeb;--color-carrot-1:#5c3721;--color-carrot-2:#7a4222;--color-carrot-3:#934c1a;--color-carrot-4:#b75f17;--color-carrot-5:#db7e21;--color-carrot-6:#ffa02e;--color-carrot-7:#ffbe62;--color-carrot-8:#ffd181;--color-carrot-9:#ffe4ab;--color-carrot-10:#fff0cc;--color-carrot-11:#fff8e8;--color-orange-1:#612729;--color-orange-2:#7a2024;--color-orange-3:#931e1a;--color-orange-4:#b72111;--color-orange-5:#db3918;--color-orange-6:#ff5722;--color-orange-7:#ff8c59;--color-orange-8:#ffad7a;--color-orange-9:#ffcea6;--color-orange-10:#ffead2;--color-orange-11:#fff4e8;--color-red-1:#612235;--color-red-2:#752137;--color-red-3:#8d1c37;--color-red-4:#af1631;--color-red-5:#d12030;--color-red-6:#f42d2d;--color-red-7:#f86d60;--color-red-8:#fb9580;--color-red-9:#fdc0ab;--color-red-10:#fee3d4;--color-red-11:#fef1ea;--color-raspberry-1:#612241;--color-raspberry-2:#77214c;--color-raspberry-3:#901d56;--color-raspberry-4:#b3185e;--color-raspberry-5:#d6236a;--color-raspberry-6:#f9316d;--color-raspberry-7:#fb6382;--color-raspberry-8:#fd838f;--color-raspberry-9:#feacad;--color-raspberry-10:#fed9d5;--color-raspberry-11:#fef0ef;--color-magenta-1:#5e1a48;--color-magenta-2:#791f5a;--color-magenta-3:#931a65;--color-magenta-4:#b6006a;--color-magenta-5:#da0078;--color-magenta-6:#fe0184;--color-magenta-7:#fe409c;--color-magenta-8:#fe66aa;--color-magenta-9:#fe99c3;--color-magenta-10:#fecce0;--color-magenta-11:#feeff5;--color-black:#ffffff;--color-white:#000000;--color-hover-base:#32343E;--bg-color:var(--color-warm-grey-2);--bg-color-hover:var(--color-warm-grey-3);--bg-color-active:var(--color-warm-grey-4);--bg-color-tonal:var(--color-warm-grey-2);--bg-color-tonal-hover:var(--color-warm-grey-2);--bg-color-tonal-active:var(--color-warm-grey-2);--bg-color-raised:var(--color-warm-grey-2);--bg-color-modal-overlay:rgba(0, 0, 0, 0.4);--bg-raised-gradient:linear-gradient(270deg, var(--color-warm-grey-3) 21.88%, rgba(42, 43, 51, 0) 112.5%);--layer-color:var(--color-warm-grey-3);--layer-color-hover:var(--color-warm-grey-4);--layer-color-active:var(--color-warm-grey-5);--layer-color-ontonal:var(--color-warm-grey-3);--layer-color-ontonal-hover:var(--color-warm-grey-4);--layer-color-ontonal-active:var(--color-warm-grey-5);--layer-color-accent:var(--color-warm-grey-9);--layer-accent-hover:var(--color-warm-grey-10);--layer-color-raised:var(--color-warm-grey-3);--color-brand-subtle-bg:var(--color-brand-subtle-1);--Line truncated
:root.dark .tag-grey,:root.dark .tag-draft,:root.dark .tag-schema,:root.dark .tag-default{--tag-color:var(--color-warm-grey-8);--tag-bg-color:var(--color-warm-grey-4);--tag-bg-color-hover:var(--color-warm-grey-5);--tag-close-button-bg-color-hover:var(--color-warm-grey-5);}/*!sc*/
:root.dark .tag-red{--tag-color:var(--color-red-7);--tag-bg-color:var(--color-red-1);--tag-border-color:var(--color-red-8);--tag-border-color-hover:var(--color-red-6);--tag-close-button-bg-color-hover:var(--color-red-2);}/*!sc*/
:root.dark .tag-green{--tag-color:var(--color-green-7);--tag-bg-color:var(--color-green-1);--tag-border-color:var(--color-green-3);--tag-border-color-hover:var(--color-green-5);--tag-bg-color-hover:var(--color-green-2);--tag-close-button-bg-color-hover:var(--color-green-2);}/*!sc*/
:root.dark .tag-blue{--tag-color:var(--color-blue-7);--tag-bg-color:var(--color-blue-1);--tag-border-color-hover:var(--color-blue-5);--tag-bg-color-hover:var(--color-blue-2);--tag-close-button-bg-color-hover:var(--color-blue-2);}/*!sc*/
:root.dark .tag-magenta,:root.dark .tag-hook{--tag-color:var(--color-magenta-4);--tag-bg-color:var(--color-magenta-1);--tag-bg-color-hover:var(--color-magenta-2);--tag-close-button-bg-color-hover:var(--color-magenta-2);}/*!sc*/
:root.dark .tag-purple{--tag-color:var(--color-purple-7);--tag-bg-color:var(--color-purple-1);--tag-bg-color-hover:var(--color-purple-2);--tag-border-color-hover:var(--color-purple-5);--tag-close-button-bg-color-hover:var(--color-purple-2);}/*!sc*/
:root.dark .tag-carrot{--tag-color:var(--color-carrot-6);--tag-bg-color:var(--color-carrot-1);--tag-bg-color-hover:var(--color-carrot-2);--tag-border-color:var(--color-carrot-4);--tag-close-button-bg-color-hover:var(--color-carrot-2);}/*!sc*/
:root.dark .tag-raspberry{--tag-color:var(--color-raspberry-7);--tag-bg-color:var(--color-raspberry-1);--tag-bg-color-hover:var(--color-raspberry-2);--tag-close-button-bg-color-hover:var(--color-raspberry-2);}/*!sc*/
:root.dark .tag-orange{--tag-color:var(--color-orange-7);--tag-bg-color:var(--color-orange-1);--tag-bg-color-hover:var(--color-orange-2);--tag-border-color:var(--color-orange-3);--tag-border-color-hover:var(--color-orange-5);--tag-close-button-bg-color-hover:var(--color-orange-2);}/*!sc*/
:root.dark .tag-grass{--tag-bg-color:var(--color-grass-1);--tag-bg-color-hover:var(--color-grass-2);--tag-color:var(--color-grass-7);--tag-border-color:var(--color-grass-3);--tag-border-color-hover:var(--color-grass-5);--tag-close-button-bg-color-hover:var(--color-grass-2);}/*!sc*/
:root.dark .tag-persian-green{--tag-color:var(--color-persian-green-7);--tag-bg-color:var(--color-persian-green-1);--tag-bg-color-hover:var(--color-persian-green-2);--tag-border-color:var(--color-persian-green-3);--tag-border-color-hover:var(--color-persian-green-5);--tag-close-button-bg-color-hover:var(--color-persian-green-2);}/*!sc*/
:root.dark .tag-turquoise,:root.dark .tag-option{--tag-color:var(--color-turquoise-6);--tag-bg-color:var(--color-turquoise-1);--tag-bg-color-hover:var(--color-turquoise-2);--tag-border-color:var(--color-turquoise-4);--tag-close-button-bg-color-hover:var(--color-turquoise-2);}/*!sc*/
:root.dark .tag-sky{--tag-color:var(--color-sky-6);--tag-bg-color:var(--color-sky-1);--tag-border-color:var(--color-sky-4);--tag-bg-color-hover:var(--color-sky-2);--tag-close-button-bg-color-hover:var(--color-sky-2);}/*!sc*/
:root.dark .tag-blueberry{--tag-color:var(--color-blueberry-7);--tag-bg-color:var(--color-blueberry-1);--tag-border-color-hover:var(--color-blueberry-5);--tag-bg-color-hover:var(--color-blueberry-2);--tag-close-button-bg-color-hover:var(--color-blueberry-2);}/*!sc*/
:root.dark .tag-warning,:root.dark .tag-deprecated{--tag-color:var(--color-warning-active);--tag-bg-color:var(--color-warning-bg);--tag-bg-color-hover:var(--color-warning-bg-hover);--tag-close-button-bg-color-hover:var(--color-warning-hover);}/*!sc*/
:root.dark .tag-processing,:root.dark .tag-info{--tag-color:var(--color-info-active);--tag-bg-color:var(--color-info-bg);--tag-bg-color-hover:var(--color-info-bg-hover);--tag-close-button-bg-color-hover:var(--color-info-hover);}/*!sc*/
:root.dark .tag-error,:root.dark .tag-declined{--tag-color:var(--color-error-active);--tag-bg-color:var(--color-error-bg);--tag-bg-color-hover:var(--color-error-bg-hover);--tag-close-button-bg-color-hover:var(--color-error-hover);}/*!sc*/
:root.dark .tag-success,:root.dark .tag-approved{--tag-color:var(--color-success-active);--tag-bg-color:var(--color-success-bg);--tag-bg-color-hover:var(--color-success-bg-hover);--tag-close-button-bg-color-hover:var(--color-success-bg-hover);}/*!sc*/
:root.dark .tag-link{--tag-color:var(--color-info-active);--tag-bg-color:var(--color-info-bg);--tag-bg-color-hover:var(--color-info-bg-hover);--tag-close-button-bg-color-hover:var(--color-info-hover);}/*!sc*/
:root.dark .tag-delete{--tag-color:#fa82a2;--tag-bg-color:#47252E;--tag-bg-color-hover:#653441;}/*!sc*/
:root.dark .tag-get{--tag-color:#68cc97;--tag-bg-color:#1F3D2D;--tag-bg-color-hover:#34654B;}/*!sc*/
:root.dark .tag-post{--tag-color:#90b0f0;--tag-bg-color:#2B3447;--tag-bg-color-hover:#3A465F;}/*!sc*/
:root.dark .tag-query{--tag-color:#68cc97;--tag-bg-color:#1F3D2D;--tag-bg-color-hover:#34654B;}/*!sc*/
:root.dark .tag-put{--tag-color:#e0a663;--tag-bg-color:#3D2D1B;--tag-bg-color-hover:#674C2D;}/*!sc*/
:root.dark .tag-patch{--tag-color:#e0c363;--tag-bg-color:#3D351B;--tag-bg-color-hover:#67592D;}/*!sc*/
:root.dark .tag-head{--tag-color:var(--color-purple-7);--tag-bg-color:#312B5A;--tag-bg-color-hover:#413875;}/*!sc*/
:root.dark .tag-channel{--tag-color:#F5AD5B;--tag-bg-color:#4A3721;--tag-bg-color-hover:#64492B;}/*!sc*/
:root.dark .tag-topic{--tag-color:#F5AD5B;--tag-bg-color:#4A3721;--tag-bg-color-hover:#64492B;}/*!sc*/
:root.dark .tag-send{--tag-color:#7779F8;--tag-bg-color:#272754;--tag-bg-color-hover:#373776;}/*!sc*/
:root.dark .tag-pub{--tag-color:#7779F8;--tag-bg-color:#272754;--tag-bg-color-hover:#373776;}/*!sc*/
:root.dark .tag-publish{--tag-color:#7779F8;--tag-bg-color:#272754;--tag-bg-color-hover:#373776;}/*!sc*/
:root.dark .tag-http-additional-operation{--tag-color:#FCAB69;--tag-bg-color:#523926;--tag-bg-color-hover:#694830;}/*!sc*/
:root.dark .tag-receive{--tag-color:#88CF82;--tag-bg-color:#23421F;--tag-bg-color-hover:#305A2A;}/*!sc*/
:root.dark .tag-sub{--tag-color:#88CF82;--tag-bg-color:#23421F;--tag-bg-color-hover:#305A2A;}/*!sc*/
:root.dark .tag-cons{--tag-color:#88CF82;--tag-bg-color:#23421F;--tag-bg-color-hover:#305A2A;}/*!sc*/
:root.dark .tag-exchange{--tag-color:#C79CF2;--tag-bg-color:#472669;--tag-bg-color-hover:#5C3187;}/*!sc*/
:root.dark .tag-exch{--tag-color:#C79CF2;--tag-bg-color:#472669;--tag-bg-color-hover:#5C3187;}/*!sc*/
:root.dark .tag-queue{--tag-color:#F7A7CF;--tag-bg-color:#622242;--tag-bg-color-hover:#882F5C;}/*!sc*/
:root.dark .tag-variant-filled{--tag-border-color:transparent;--tag-border-color-hover:transparent;}/*!sc*/
:root.dark .tag-variant-outline{--tag-bg-color:transparent;--tag-bg-color-hover:transparent;}/*!sc*/
:root.dark .card-variant-filled{--card-bg-color:var(--color-warm-grey-3);--card-bg-color-hover:var(--color-brand-subtle-bg-hover, var(--card-bg-color-hover-legacy));--card-border-width:0;--card-icon-bg-color:var(--card-bg-color);--card-icon-bg-color-hover:transparent;}/*!sc*/
:root.dark .card-variant-outlined{--card-icon-bg-color:var(--color-warm-grey-3);}/*!sc*/
:root.dark .card-variant-elevated{--card-box-shadow:0px 8px 24px -4px #00000014,0px 6px 12px -6px #0000001F;--card-box-shadow-hover:0px 14px 64px -4px #0000001F,0px 8px 22px -6px #0000001F;}/*!sc*/
:root.dark .card-icon-color-green{--card-icon-color:var(--color-green-7);}/*!sc*/
:root.dark .card-icon-color-grey{--card-icon-color:var(--color-warm-grey-8);}/*!sc*/
:root.dark .card-icon-color-turquoise{--card-icon-color:var(--color-turquoise-6);}/*!sc*/
:root.dark .card-icon-color-magenta{--card-icon-color:var(--color-magenta-4);}/*!sc*/
:root.dark .card-icon-color-carrot{--card-icon-color:var(--color-carrot-6);}/*!sc*/
:root.dark .card-icon-color-grass{--card-icon-color:var(--color-grass-7);}/*!sc*/
:root.dark .card-icon-color-persian-green{--card-icon-color:var(--color-persian-green-7);}/*!sc*/
:root.dark .card-icon-color-sky{--card-icon-color:var(--color-sky-6);}/*!sc*/
:root.dark .tooltip-copy-button{--tooltip-bg-color:var(--color-warm-grey-4);}/*!sc*/
:root.dark *::-webkit-scrollbar-thumb{background-color:var(--color-warm-grey-4);}/*!sc*/
@media screen and (min-width: 960px){:root:has([data-component-name='NavigationTabs/NavigationTabs']){--navbar-stack-height:calc(var(--navbar-height) + var(--navigation-tabs-height, 48px));--navbar-row-bottom-border-color:transparent;}}/*!sc*/
:root.notransition *{transition:none!important;}/*!sc*/
button{font-family:var(--font-family-base);font-size:var(--font-size-base);font-weight:var(--font-weight-regular);line-height:var(--line-height-base);background-color:inherit;cursor:pointer;border:none;color:var(--text-color-primary);}/*!sc*/
data-styled.g38[id="sc-global-fFycjn"]{content:"sc-global-fFycjn1,"}/*!sc*/
.kEEIsB{height:var(--menu-item-label-chevron-size);width:var(--menu-item-label-chevron-size);}/*!sc*/
.kEEIsB path{fill:var(--tree-content-color-default);}/*!sc*/
data-styled.g50[id="sc-iUlCMS"]{content:"kEEIsB,"}/*!sc*/
.lksAEE{position:relative;display:flex;}/*!sc*/
data-styled.g59[id="sc-khYboC"]{content:"lksAEE,"}/*!sc*/
.hwcmKf{font-size:var(--md-content-font-size);font-family:var(--md-content-font-family);font-weight:var(--md-content-font-weight);line-height:var(--md-content-line-height);color:var(--md-content-text-color);padding:0;overflow-wrap:break-word;}/*!sc*/
.hwcmKf a:not([role='button']){text-decoration:var(--link-decoration);text-underline-offset:var(--link-underline-offset);color:var(--link-color-primary);font-weight:var(--link-font-weight);}/*!sc*/
.hwcmKf a:not([role='button']):visited{color:var(--link-color-visited);text-decoration:var(--link-decoration-visited);}/*!sc*/
.hwcmKf a:not([role='button']):hover,.hwcmKf a:not([role='button']):focus{text-decoration:var(--link-decoration-hover);color:var(--link-color-primary-hover);}/*!sc*/
.hwcmKf a[name],.hwcmKf [id]{scroll-margin-top:var(--navbar-offset);}/*!sc*/
.hwcmKf img,.hwcmKf iframe{max-width:100%;}/*!sc*/
.hwcmKf .heading-anchor{font-weight:var(--heading-font-weight);font-family:var(--heading-font-family);position:relative;scroll-margin-top:var(--navbar-offset);}/*!sc*/
.hwcmKf .heading-anchor .anchor.before{position:absolute;left:0;transform:translateX(-100%);padding-right:var(--heading-anchor-offset-right);}/*!sc*/
.hwcmKf .heading-anchor .anchor.after{display:inline-block;padding-left:4px;}/*!sc*/
.hwcmKf .heading-anchor .anchor svg{fill:var(--heading-anchor-color);visibility:hidden;background-image:var(--heading-anchor-icon);background-size:contain;background-repeat:no-repeat;background-position:center;}/*!sc*/
.hwcmKf .heading-anchor:hover .anchor svg,.hwcmKf .heading-anchor .anchor:focus svg{visibility:visible;}/*!sc*/
.hwcmKf .heading-anchor h1,.hwcmKf .heading-anchor h2,.hwcmKf .heading-anchor h3,.hwcmKf .heading-anchor h4,.hwcmKf .heading-anchor h5,.hwcmKf .heading-anchor h6{display:flex;align-items:flex-start;gap:var(--spacing-sm);margin:0;font:inherit;color:inherit;}/*!sc*/
.hwcmKf .heading-level-1{font-size:var(--h1-font-size);font-weight:var(--h1-font-weight);font-family:var(--h1-font-family);line-height:var(--h1-line-height);color:var(--h1-text-color);text-transform:var(--h1-text-transform);margin:var(--h1-margin-top) 0 var(--h1-margin-bottom) 0;}/*!sc*/
.hwcmKf .heading-level-1 h1 code{font-size:var(--h1-font-size);line-height:var(--h1-code-line-height);}/*!sc*/
.hwcmKf .heading-level-2{font-size:var(--h2-font-size);font-weight:var(--h2-font-weight);font-family:var(--h2-font-family);line-height:var(--h2-line-height);color:var(--h2-text-color);text-transform:var(--h2-text-transform);margin:var(--h2-margin-top) 0 var(--h2-margin-bottom) 0;}/*!sc*/
.hwcmKf .heading-level-2 h2 code{font-size:var(--h2-font-size);line-height:var(--h2-code-line-height);}/*!sc*/
.hwcmKf .heading-level-3{font-size:var(--h3-font-size);font-weight:var(--h3-font-weight);font-family:var(--h3-font-family);line-height:var(--h3-line-height);color:var(--h3-text-color);text-transform:var(--h3-text-transform);margin:var(--h3-margin-top) 0 var(--h3-margin-bottom) 0;}/*!sc*/
.hwcmKf .heading-level-3 h3 code{font-size:var(--h3-font-size);line-height:var(--h3-code-line-height);}/*!sc*/
.hwcmKf .heading-level-4{font-size:var(--h4-font-size);font-weight:var(--h4-font-weight);font-family:var(--h4-font-family);line-height:var(--h4-line-height);color:var(--h4-text-color);text-transform:var(--h4-text-transform);margin:var(--h4-margin-top) 0 var(--h4-margin-bottom) 0;}/*!sc*/
.hwcmKf .heading-level-4 h4 code{font-size:var(--h4-font-size);line-height:var(--h4-code-line-height);}/*!sc*/
.hwcmKf .heading-level-5{font-size:var(--h5-font-size);font-weight:var(--h5-font-weight);font-family:var(--h5-font-family);line-height:var(--h5-line-height);color:var(--h5-text-color);text-transform:var(--h5-text-transform);margin:var(--h5-margin-top) 0 var(--h5-margin-bottom) 0;}/*!sc*/
.hwcmKf .heading-level-5 h5 code{font-size:var(--h5-font-size);line-height:var(--h5-code-line-height);}/*!sc*/
.hwcmKf .heading-level-6{font-size:var(--h6-font-size);font-weight:var(--h6-font-weight);font-family:var(--h6-font-family);line-height:var(--h6-line-height);color:var(--h6-text-color);text-transform:var(--h6-text-transform);margin:var(--h6-margin-top) 0 var(--h6-margin-bottom) 0;}/*!sc*/
.hwcmKf .heading-level-6 h6 code{font-size:var(--h6-font-size);line-height:var(--h6-code-line-height);}/*!sc*/
.hwcmKf strong{font-weight:var(--font-weight-bold);}/*!sc*/
.hwcmKf hr{margin:var(--md-hr-margin-vertical) 0;border:var(--md-hr-border);}/*!sc*/
.hwcmKf details:not([data-component-name='Markdoc/Accordion/Accordion']){margin:1.125em 0;}/*!sc*/
.hwcmKf details:not([data-component-name='Markdoc/Accordion/Accordion']) summary{color:var(--text-color-secondary);font-weight:var(--font-weight-regular);padding:0;cursor:pointer;}/*!sc*/
.hwcmKf details:not([data-component-name='Markdoc/Accordion/Accordion'])>:not(summary):first-of-type{margin-top:1.25em;}/*!sc*/
.hwcmKf details:not([data-component-name='Markdoc/Accordion/Accordion'])>*:last-child{margin-bottom:1.25em;}/*!sc*/
.hwcmKf details:not([data-component-name='Markdoc/Accordion/Accordion'])>:not(summary){margin-left:2em;}/*!sc*/
.hwcmKf .md-table-wrapper{overflow-x:auto;}/*!sc*/
.hwcmKf table.md{width:100%;overflow:hidden;word-break:keep-all;border-spacing:0;margin:var(--md-table-margin-vertical) 0;border-radius:var(--md-table-border-radius);border:1px solid var(--md-table-border-color);}/*!sc*/
.hwcmKf table.md th,.hwcmKf table.md td{padding:var(--md-table-cell-padding);color:var(--md-table-cell-text-color);font-size:var(--md-table-font-size);line-height:var(--md-table-line-height);vertical-align:var(--md-table-cell-vertical-align);}/*!sc*/
.hwcmKf table.md thead td,.hwcmKf table.md thead th,.hwcmKf table.md tbody td{border-bottom:1px solid var(--md-table-border-color);}/*!sc*/
.hwcmKf table.md tbody tr:last-child td{border-bottom:none;}/*!sc*/
.hwcmKf table.md tfoot td{border-top:1px solid var(--md-table-border-color);padding:var(--md-table-footer-cell-padding);}/*!sc*/
.hwcmKf table.md thead,.hwcmKf table.md tfoot{background-color:var(--md-table-header-bg-color);}/*!sc*/
.hwcmKf table.md thead:has(th:empty){display:none;}/*!sc*/
.hwcmKf table.md thead:has(th:not(:empty)){display:table-header-group;}/*!sc*/
.hwcmKf table.md tr{background-color:var(--md-table-stripe-bg-color);}/*!sc*/
.hwcmKf table.md th{text-align:left;font-weight:var(--md-table-head-font-weight);color:var(--md-table-head-text-color);}/*!sc*/
.hwcmKf table.md th[align='center']{text-align:center;}/*!sc*/
.hwcmKf table.md th[align='right']{text-align:right;}/*!sc*/
.hwcmKf table.md th[valign='top'],.hwcmKf table.md td[valign='top']{vertical-align:top;}/*!sc*/
.hwcmKf table.md th[valign='middle'],.hwcmKf table.md td[valign='middle']{vertical-align:middle;}/*!sc*/
.hwcmKf table.md th[valign='bottom'],.hwcmKf table.md td[valign='bottom']{vertical-align:bottom;}/*!sc*/
.hwcmKf p{margin:var(--md-paragraph-margin);}/*!sc*/
.hwcmKf h1.md{font-size:var(--h1-font-size);font-weight:var(--h1-font-weight);font-family:var(--h1-font-family);line-height:var(--h1-line-height);color:var(--h1-text-color);text-transform:var(--h1-text-transform);margin:var(--h1-margin-top) 0 var(--h1-margin-bottom) 0;}/*!sc*/
.hwcmKf h2.md{font-size:var(--h2-font-size);font-weight:var(--h2-font-weight);font-family:var(--h2-font-family);line-height:var(--h2-line-height);color:var(--h2-text-color);text-transform:var(--h2-text-transform);margin:var(--h2-margin-top) 0 var(--h2-margin-bottom) 0;}/*!sc*/
.hwcmKf h3.md{font-size:var(--h3-font-size);font-weight:var(--h3-font-weight);font-family:var(--h3-font-family);line-height:var(--h3-line-height);color:var(--h3-text-color);text-transform:var(--h3-text-transform);margin:var(--h3-margin-top) 0 var(--h3-margin-bottom) 0;}/*!sc*/
.hwcmKf h4.md{font-size:var(--h4-font-size);font-weight:var(--h4-font-weight);font-family:var(--h4-font-family);line-height:var(--h4-line-height);color:var(--h4-text-color);text-transform:var(--h4-text-transform);margin:var(--h4-margin-top) 0 var(--h4-margin-bottom) 0;}/*!sc*/
.hwcmKf h5.md{font-size:var(--h5-font-size);font-weight:var(--h5-font-weight);font-family:var(--h5-font-family);line-height:var(--h5-line-height);color:var(--h5-text-color);text-transform:var(--h5-text-transform);margin:var(--h5-margin-top) 0 var(--h5-margin-bottom) 0;}/*!sc*/
.hwcmKf h6.md{font-size:var(--h6-font-size);font-weight:var(--h6-font-weight);font-family:var(--h6-font-family);line-height:var(--h6-line-height);color:var(--h6-text-color);text-transform:var(--h6-text-transform);margin:var(--h6-margin-top) 0 var(--h6-margin-bottom) 0;}/*!sc*/
.hwcmKf h1.md code{font-size:var(--h1-font-size);line-height:var(--h1-code-line-height);}/*!sc*/
.hwcmKf h2.md code{font-size:var(--h2-font-size);line-height:var(--h2-code-line-height);}/*!sc*/
.hwcmKf h3.md code{font-size:var(--h3-font-size);line-height:var(--h3-code-line-height);}/*!sc*/
.hwcmKf h4.md code{font-size:var(--h4-font-size);line-height:var(--h4-code-line-height);}/*!sc*/
.hwcmKf h5.md code{font-size:var(--h5-font-size);line-height:var(--h5-code-line-height);}/*!sc*/
.hwcmKf h6.md code{font-size:var(--h6-font-size);line-height:var(--h6-code-line-height);}/*!sc*/
.hwcmKf code{color:var(--inline-code-text-color);background-color:var(--inline-code-bg-color);border-radius:var(--inline-code-border-radius);padding:0.19rem var(--spacing-xxs);font-weight:var(--code-font-weight);word-break:break-word;font-size:var(--font-size-sm);font-family:var(--font-family-monospaced);line-height:var(--line-height-sm);}/*!sc*/
.hwcmKf.hwcmKf pre{white-space:var(--code-wrap, pre);background-color:var(--code-block-bg-color);font-family:var(--code-block-font-family);color:var(--code-block-text-color);padding:var(--code-block-padding);overflow-x:auto;line-height:var(--line-height-base);margin:var(--md-pre-margin);}/*!sc*/
.hwcmKf.hwcmKf pre code{background-color:transparent;color:inherit;font-size:inherit;font-family:inherit;border:none;padding:0;}/*!sc*/
.hwcmKf blockquote{margin:var(--md-blockquote-margin-vertical) var(--md-blockquote-margin-horizontal);padding:var(--md-blockquote-padding-vertical) var(--md-blockquote-padding-horizontal);border-left:var(--md-blockquote-border-left);background-color:var(--md-blockquote-bg-color);color:var(--md-blockquote-text-color);box-shadow:var(--md-blockquote-box-shadow);}/*!sc*/
.hwcmKf blockquote>p:first-child{margin-top:0;}/*!sc*/
.hwcmKf img{max-width:100%;box-sizing:content-box;margin:0 auto;}/*!sc*/
.hwcmKf img:only-child{display:block;}/*!sc*/
.hwcmKf p>img:only-child,.hwcmKf li>img:only-child{display:inline-block;}/*!sc*/
.hwcmKf ul.md,.hwcmKf ol.md{padding-left:var(--md-list-left-padding);margin:var(--md-list-margin);}/*!sc*/
.hwcmKf ul.md ul,.hwcmKf ol.md ul,.hwcmKf ul.md ol,.hwcmKf ol.md ol{margin-bottom:0;margin-top:0;}/*!sc*/
.hwcmKf li{list-style:var(--md-list-item-style);}/*!sc*/
.hwcmKf li:not(:last-child){margin-bottom:calc(var(--spacing-xxs) / 2);}/*!sc*/
.hwcmKf li::marker{padding-left:0.25rem;}/*!sc*/
.hwcmKf ul>li:has(input[type="checkbox"]){list-style:var(--md-checkbox-list-item-style);}/*!sc*/
.hwcmKf ol>li{list-style:var(--md-numbered-list-item-style);}/*!sc*/
.hwcmKf ol>li:before{content:counter(list-item);display:var(--md-numbered-list-number-display);color:var(--md-numbered-list-number-text-color);font-size:var(--md-numbered-list-number-font-size);font-family:var(--md-numbered-list-number-font-family);font-weight:var(--md-numbered-list-number-font-weight);background-color:var(--md-numbered-list-number-bg-color);border-radius:var(--md-numbered-list-number-border-radius);border:var(--md-numbered-list-number-border);margin-right:var(--md-numbered-list-number-margin-right);padding:var(--md-numbered-list-number-padding);}/*!sc*/
data-styled.g131[id="sc-fmkcYi"]{content:"hwcmKf,"}/*!sc*/
.ldIGYl{position:relative;display:inline-flex;align-items:center;vertical-align:middle;}/*!sc*/
data-styled.g135[id="sc-eVYbGh"]{content:"ldIGYl,"}/*!sc*/
.fVoQxm{position:absolute;top:50%;transform:translate(-100%, -50%);z-index:1;display:flex;align-items:center;min-width:24px;min-height:24px;justify-content:flex-end;left:0;padding-right:var(--heading-anchor-offset-right, 4px);}/*!sc*/
.fVoQxm:hover svg{--link-icon-bg-color:var(
--deep-link-anchor-bg-color-hover,
var(--button-bg-color-secondary-hover)
);}/*!sc*/
.fVoQxm:active svg{--link-icon-bg-color:var(
--deep-link-anchor-bg-color-pressed,
var(--button-bg-color-secondary-pressed)
);}/*!sc*/
.fVoQxm.fVoQxm[aria-current] svg,.fVoQxm.fVoQxm[data-tooltip-open] svg{visibility:visible;}/*!sc*/
.fVoQxm.fVoQxm[aria-current] svg{--link-icon-bg-color:var(--deep-link-anchor-bg-color-current, var(--color-blueberry-1));fill:var(--deep-link-anchor-color-current, var(--color-blueberry-6));}/*!sc*/
data-styled.g136[id="sc-kTmAaw"]{content:"fVoQxm,"}/*!sc*/
.dHJrnC{display:flex;}/*!sc*/
data-styled.g137[id="sc-dwkskT"]{content:"dHJrnC,"}/*!sc*/
.gTzQi{height:16px;width:16px;}/*!sc*/
.gTzQi path{fill:currentColor;}/*!sc*/
data-styled.g157[id="sc-cHazmW"]{content:"gTzQi,"}/*!sc*/
.gjVHnn{display:flex;flex-direction:column;justify-content:space-between;width:100%;padding:0;}/*!sc*/
@media screen and (min-width: 1280px){.gjVHnn{flex-direction:row;}}/*!sc*/
@media print{.gjVHnn{flex-direction:column;}}/*!sc*/
data-styled.g388[id="sc-jOfXLR"]{content:"gjVHnn,"}/*!sc*/
.fcbNxa{display:flex;flex-direction:column;width:100%;min-width:0;padding:var(--spacing-sm) var(--panel-gap-horizontal);margin-bottom:var(--spacing-xs);}/*!sc*/
.fcbNxa:empty{display:none;}/*!sc*/
@media screen and (min-width: 1280px){.fcbNxa{width:calc(100% - var(--panel-samples-width));padding-left:calc(var(--panel-gap-horizontal) * 2);padding-right:var(--panel-gap-horizontal);padding-top:0;padding-bottom:0;}}/*!sc*/
@media screen and (max-width: 672px){.fcbNxa{padding-top:0;padding-bottom:var(--spacing-vertical);}}/*!sc*/
@media print{.fcbNxa{width:100%;padding-top:var(--spacing-vertical);padding-bottom:var(--spacing-vertical);}}/*!sc*/
.sc-gmYvYK+.sc-gmYvYK{padding-top:0;}/*!sc*/
.fcbNxa [data-testid="middle-panel"]{padding-left:0;padding-right:0;}/*!sc*/
.fcbNxa [data-testid="right-panel"]{padding-left:0;padding-right:0;}/*!sc*/
data-styled.g389[id="sc-gmYvYK"]{content:"fcbNxa,"}/*!sc*/
.juOWHT{margin-left:auto;--code-block-padding:var(--spacing-xs) 0 var(--spacing-xs) 20px;color:var(--panel-samples-text-color);width:100%;height:fit-content;padding-top:var(--panel-gap-vertical);padding-bottom:var(--panel-gap-vertical);padding-left:var(--panel-gap-horizontal);padding-right:var(--panel-gap-horizontal);margin-bottom:var(--spacing-sm);position:sticky;z-index:var(--panel-z-index);top:calc(var(--navbar-stack-height) + var(--panel-gap-vertical));}/*!sc*/
.juOWHT>[data-component-name='Panel/Panel']:not(:last-child){margin-bottom:var(--spacing-sm);}/*!sc*/
.juOWHT:has([data-component-name='BrokerPanel/BrokerItem']){padding-top:var(--spacing-lg);padding-bottom:0;}/*!sc*/
.juOWHT:has([data-testid='items-navigation-list']){position:static;}/*!sc*/
.juOWHT:empty{display:none;}/*!sc*/
@media screen and (min-width: 1280px){.juOWHT{width:var(--panel-samples-width);padding-top:0;padding-bottom:0;padding-left:var(--panel-gap-horizontal);padding-right:calc(var(--panel-gap-horizontal) * 2);}}/*!sc*/
@media print{.juOWHT{width:100%;padding-top:var(--spacing-vertical);padding-bottom:var(--spacing-vertical);}}/*!sc*/
data-styled.g390[id="sc-jLTNpb"]{content:"juOWHT,"}/*!sc*/
.fLAjrd{position:relative;display:flex;align-items:center;}/*!sc*/
.fLAjrd [data-deep-link-anchor] svg{visibility:hidden;}/*!sc*/
.fLAjrd:hover [data-deep-link-anchor] svg,.fLAjrd [data-deep-link-anchor] a:focus svg{visibility:visible;}/*!sc*/
data-styled.g392[id="sc-jddhbx"]{content:"fLAjrd,"}/*!sc*/
.kbrdNd{margin:0;}/*!sc*/
data-styled.g400[id="sc-jWvXZq"]{content:"kbrdNd,"}/*!sc*/
.lnPvSK{position:relative;display:flex;flex-direction:column;align-items:center;margin:0 auto;width:100%;max-width:100%;}/*!sc*/
@media screen and (min-width: 672px){.lnPvSK{max-width:var(--layout-three-panel-small-max-width);}}/*!sc*/
@media screen and (min-width: 960px){.lnPvSK{max-width:var(--layout-three-panel-medium-max-width);}}/*!sc*/
@media screen and (min-width: 1280px){.lnPvSK{max-width:var(--layout-three-panel-large-max-width);}}/*!sc*/
@media print{.lnPvSK{width:100%;padding:0;}}/*!sc*/
data-styled.g404[id="sc-jlthoW"]{content:"lnPvSK,"}/*!sc*/
.fkuKvu{min-height:var(--lazy-section-min-height, 100px);contain-intrinsic-size:var(--lazy-section-min-height, 100px);content-visibility:auto;}/*!sc*/
data-styled.g405[id="sc-cbLsDt"]{content:"fkuKvu,"}/*!sc*/
.cMfMso{width:100%;content-visibility:auto;contain-intrinsic-size:auto var(--lazy-section-min-height, 100px);}/*!sc*/
data-styled.g406[id="sc-hYZQXI"]{content:"cMfMso,"}/*!sc*/
.rgZNZ{padding:var(--spacing-xs) 0 var(--spacing-xxl) 0;border-bottom:1px solid var(--border-color-secondary);}/*!sc*/
.rgZNZ [data-testid='middle-panel']>:first-child{padding-top:calc(var(--spacing-unit) * 7);}/*!sc*/
.rgZNZ [data-component-name='Markdown/Markdown'] :is(h1, h2, h3, h4, h5, h6){padding-top:var(--spacing-xs);}/*!sc*/
data-styled.g414[id="sc-iEpKbI"]{content:"rgZNZ,"}/*!sc*/
.itvbVV{color:var(--h1-text-color);margin:var(--h1-margin-top) 0 var(--h1-margin-bottom);font-size:var(--h1-font-size);font-weight:var(--h1-font-weight);font-family:var(--h1-font-family);line-height:var(--h1-line-height);color:var(--h1-text-color);text-transform:var(--h1-text-transform);}/*!sc*/
data-styled.g425[id="sc-DxzaO"]{content:"itvbVV,"}/*!sc*/
.eNJzrd{container-type:inline-size;container-name:api-docs-heading;position:relative;display:flex;align-items:flex-start;gap:var(--spacing-base);margin-bottom:var(--spacing-base);flex-wrap:nowrap;gap:var(--spacing-xs);}/*!sc*/
.eNJzrd [data-deep-link-anchor] svg{visibility:hidden;}/*!sc*/
.eNJzrd:hover [data-deep-link-anchor] svg,.eNJzrd [data-deep-link-anchor] a:focus svg{visibility:visible;}/*!sc*/
.eNJzrd:has(h4){margin-bottom:var(--spacing-xs);}/*!sc*/
.eNJzrd:has(+ [data-gql-return-type-details]){margin-bottom:0;}/*!sc*/
@media screen and (max-width: 672px){.eNJzrd{gap:0;}}/*!sc*/
data-styled.g448[id="sc-heCaGu"]{content:"eNJzrd,"}/*!sc*/
.cIpcHf{display:flex;align-items:center;flex-wrap:wrap;row-gap:var(--spacing-xs);flex:1 1 auto;min-width:0;}/*!sc*/
.cIpcHf>*:has(+ [data-response-codes-tablist]){margin-right:var(--spacing-base);}/*!sc*/
data-styled.g449[id="sc-cSXhvd"]{content:"cIpcHf,"}/*!sc*/
.dEGYPm{flex-shrink:0;margin-left:auto;}/*!sc*/
data-styled.g451[id="sc-QvzgP"]{content:"dEGYPm,"}/*!sc*/
.iNjboU{overflow-wrap:anywhere;word-break:break-word;min-width:0;position:relative;margin:0;display:flex;align-items:center;gap:var(--spacing-xs);flex-wrap:wrap;}/*!sc*/
.iNjboU [data-deep-link-anchor]{position:absolute;top:0;left:0;height:1lh;}/*!sc*/
.iNjboU+[data-component-name="Markdown/Markdown"]{margin-top:var(--h1-margin-bottom);}/*!sc*/
data-styled.g454[id="sc-clInUQ"]{content:"iNjboU,"}/*!sc*/
.dffrbr{color:var(--search-trigger-color);line-height:var(--search-trigger-line-height);flex-grow:1;cursor:pointer;}/*!sc*/
.dffrbr svg{width:var(--search-trigger-icon-size);height:var(--search-trigger-icon-size);}/*!sc*/
data-styled.g609[id="sc-cToEys"]{content:"dffrbr,"}/*!sc*/
.ddjiE{display:inline-flex;align-items:center;justify-content:left;gap:var(--search-trigger-gap);width:100%;border:var(--search-trigger-border-width) var(--search-trigger-border-style) var(--search-trigger-border-color);border-radius:var(--search-trigger-border-radius);background:var(--search-trigger-bg-color);padding:var(--search-trigger-padding);}/*!sc*/
.ddjiE:hover{border-color:var(--search-trigger-border-color-hover);}/*!sc*/
data-styled.g611[id="sc-cRcubC"]{content:"ddjiE,"}/*!sc*/
.jjzLa-d{display:flex;margin:var(--sidebar-margin-horizontal, 16px);}/*!sc*/
data-styled.g612[id="sc-jqwVmx"]{content:"jjzLa-d,"}/*!sc*/
.jtaKKl{flex-shrink:0;}/*!sc*/
data-styled.g614[id="sc-jokKPH"]{content:"jtaKKl,"}/*!sc*/
.fGSWQx{display:flex;flex-direction:column;background-color:var(--menu-item-bg-color);font-family:var(--menu-item-font-family);font-size:var(--menu-item-font-size);font-weight:var(--menu-item-font-weight);line-height:var(--menu-item-line-height);color:var(--menu-item-text-color);}/*!sc*/
.fGSWQx .tag-http{align-self:flex-start;margin-left:auto;}/*!sc*/
.fGSWQx.menu-item-type-separator{pointer-events:none;}/*!sc*/
.fGSWQx >a{text-decoration:none;color:var(--menu-item-text-color);}/*!sc*/
data-styled.g615[id="sc-gwVKcS"]{content:"fGSWQx,"}/*!sc*/
.dlNiXx{order:1;position:relative;}/*!sc*/
.dlNiXx:hover:has(.dlNiXx:hover)::before{display:none;}/*!sc*/
.dlNiXx:hover::before{content:'';position:absolute;bottom:var(--spacing-unit);top:0;z-index:var(--z-index-surface);left:calc(
:is(.redoc, :where(body > :has(> [data-component-name='Dropdown/DropdownMenu']))) *{box-sizing:border-box;}/*!sc*/
:is(.redoc, :where(body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .menu-item-type-separator{--menu-item-label-chevron-offset:0px;}/*!sc*/
:is(.redoc, :where(body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .menu-item-type-separator-primary{--menu-item-font-weight:var(--font-weight-bold);--menu-item-text-color:var(--text-color-primary);--menu-item-font-size:var(--sidebar-font-size);--menu-item-line-height:var(--line-height-base);}/*!sc*/
:is(.redoc, :where(body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .menu-item-type-separator-secondary{--menu-item-font-weight:var(--font-weight-bold);--menu-item-text-color:var(--text-color-secondary);--menu-item-font-size:var(--font-size-sm);--menu-item-line-height:var(--line-height-sm);}/*!sc*/
:is(.redoc, :where(body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .panel-response-success,:is(.redoc, :where(body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .panel-response-info,:is(.redoc, :where(body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .panel-response-error,:is(.redoc, :where(body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .panel-response-redirect,:is(.redoc, :where(body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .panel-response-callback{--panel-body-text-color:var(--panel-response-body-text-color);--panel-border-radius-local:calc(1px + var(--panel-border-radius));--panel-heading-text-color:var(--panel-response-heading-text-color);--panel-heading-font-size-local:var(--panel-response-heading-font-size);--panel-heading-font-weight-local:var(--panel-response-heading-font-weight);--panel-heading-line-height-local:var(--panel-response-heading-line-height);--panel-heading-padding-local:var(--panel-response-heading-padding);--panel-heading-border:var(--panel-samples-heading-border);--panel-heading-white-space-local:normal;--panel-body-padding-local:var(--panel-body-padding);}/*!sc*/
:is(.redoc, :where(body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .panel-response-success{--panel-border-local:1px solid var(--panel-response-success-border-color);--panel-body-text-color:var(--panel-response-success-body-text-color);--panel-body-bg-color:var(--panel-response-success-body-bg-color);--panel-heading-bg-color:var(--panel-response-success-heading-bg-color);--panel-heading-text-color:var(--panel-response-success-heading-text-color);}/*!sc*/
:is(.redoc, :where(body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .panel-response-info{--panel-border-local:1px solid var(--panel-response-info-border-color);--panel-body-text-color:var(--panel-response-info-body-text-color);--panel-body-bg-color:var(--panel-response-info-body-bg-color);--panel-heading-bg-color:var(--panel-response-info-heading-bg-color);--panel-heading-text-color:var(--panel-response-info-heading-text-color);}/*!sc*/
:is(.redoc, :where(body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .panel-response-error{--panel-border-local:1px solid var(--panel-response-error-border-color);--panel-body-text-color:var(--panel-response-error-body-text-color);--panel-body-bg-color:var(--panel-response-error-body-bg-color);--panel-heading-bg-color:var(--panel-response-error-heading-bg-color);--panel-heading-text-color:var(--panel-response-error-heading-text-color);}/*!sc*/
:is(.redoc, :where(body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .panel-response-redirect{--panel-border-local:1px solid var(--panel-response-redirect-border-color);--panel-body-text-color:var(--panel-response-redirect-body-text-color);--panel-body-bg-color:var(--panel-response-redirect-body-bg-color);--panel-heading-bg-color:var(--panel-response-redirect-heading-bg-color);--panel-heading-text-color:var(--panel-response-redirect-heading-text-color);}/*!sc*/
:is(.redoc, :where(body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .panel-response-callback{--panel-body-text-color:var(--panel-response-callback-body-text-color);--panel-body-bg-color:var(--panel-response-callback-body-bg-color);--panel-heading-text-color:var(--panel-response-callback-heading-text-color);--panel-body-padding-local:0 0 var(--spacing-base) var(--spacing-lg);}/*!sc*/
:is(.redoc, :where(body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .tag-size-large{--tag-padding:var(--tag-large-padding);--tag-content-padding:var(--tag-large-content-padding);--tag-font-size:var(--font-size-base);}/*!sc*/
:is(.redoc, :where(body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .tag-grey,:is(.redoc, :where(body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .tag-draft,:is(.redoc, :where(body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .tag-schema,:is(.redoc, :where(body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .tag-default{--tag-color:var(--text-color-secondary);--tag-bg-color:var(--color-warm-grey-2);--tag-border-color:var(--border-color-primary);--tag-bg-color-hover:var(--color-warm-grey-3);--tag-close-button-bg-color-hover:var(--color-warm-grey-3);--tag-border-color-hover:var(--color-warm-grey-5);}/*!sc*/
:is(.redoc, :where(body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .tag-green{--tag-color:var(--color-green-8);--tag-bg-color:var(--color-green-1);--tag-border-color:var(--color-green-5);--tag-bg-color-hover:var(--color-green-2);--tag-close-button-bg-color-hover:var(--color-green-2);--tag-border-color-hover:var(--color-green-6);}/*!sc*/
:is(.redoc, :where(body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .tag-success,:is(.redoc, :where(body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .tag-approved{--tag-color:var(--color-success-active);--tag-bg-color:var(--color-success-bg);--tag-border-color:var(--color-success-border);--tag-close-button-bg-color-hover:var(--color-success-bg-hover);--tag-bg-color-hover:var(--color-success-bg-hover);--tag-border-color-hover:var(--color-success-border-hover);}/*!sc*/
:is(.redoc, :where(body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .tag-red{--tag-color:var(--color-red-7);--tag-bg-color:var(--color-red-1);--tag-border-color:var(--color-red-3);--tag-close-button-bg-color-hover:var(--color-red-2);--tag-bg-color-hover:var(--color-red-2);--tag-border-color-hover:var(--color-red-5);}/*!sc*/
:is(.redoc, :where(body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .tag-error,:is(.redoc, :where(body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .tag-declined{--tag-color:var(--color-error-active);--tag-bg-color:var(--color-error-bg);--tag-border-color:var(--color-error-border);--tag-close-button-bg-color-hover:var(--color-error-hover);--tag-bg-color-hover:var(--color-error-bg-hover);--tag-border-color-hover:var(--color-error-border-hover);}/*!sc*/
:is(.redoc, :where(body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .tag-warning,:is(.redoc, :where(body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .tag-deprecated{--tag-color:var(--color-warning-active);--tag-bg-color:var(--color-warning-bg);--tag-border-color:var(--color-warning-border);--tag-close-button-bg-color-hover:var(--color-warning-hover);--tag-bg-color-hover:var(--color-warning-bg-hover);--tag-border-color-hover:var(--color-warning-border-hover);}/*!sc*/
:is(.redoc, :where(body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .tag-blue{--tag-color:var(--color-blue-7);--tag-bg-color:var(--color-blue-1);--tag-border-color:var(--color-blue-4);--tag-bg-color-hover:var(--color-blue-2);--tag-close-button-bg-color-hover:var(--color-blue-2);--tag-border-color-hover:var(--color-blue-6);}/*!sc*/
:is(.redoc, :where(body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .tag-processing,:is(.redoc, :where(body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .tag-info{--tag-color:var(--color-info-active);--tag-bg-color:var(--color-info-bg);--tag-border-color:var(--color-info-border);--tag-close-button-bg-color-hover:var(--color-info-hover);--tag-bg-color-hover:var(--color-info-bg-hover);--tag-border-color-hover:var(--color-info-border-hover);}/*!sc*/
:is(.redoc, :where(body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .tag-link{--tag-color:var(--color-info-active);--tag-bg-color:var(--color-info-bg);--tag-border-color:var(--color-info-border);--tag-close-button-bg-color-hover:var(--color-info-hover);--tag-bg-color-hover:var(--color-info-bg-hover);--tag-border-color-hover:var(--color-info-border-hover);}/*!sc*/
:is(.redoc, :where(body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .tag-purple{--tag-color:var(--color-purple-7);--tag-bg-color:var(--color-purple-1);--tag-border-color:var(--color-purple-4);--tag-bg-color-hover:var(--color-purple-2);--tag-close-button-bg-color-hover:var(--color-purple-2);--tag-border-color-hover:var(--color-purple-6);}/*!sc*/
:is(.redoc, :where(body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .tag-turquoise,:is(.redoc, :where(body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .tag-option{--tag-color:var(--color-turquoise-8);--tag-bg-color:var(--color-turquoise-1);--tag-border-color:var(--color-turquoise-5);--tag-bg-color-hover:var(--color-turquoise-2);--tag-close-button-bg-color-hover:var(--color-turquoise-2);--tag-border-color-hover:var(--color-turquoise-7);}/*!sc*/
:is(.redoc, :where(body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .tag-carrot{--tag-color:var(--color-carrot-8);--tag-bg-color:var(--color-carrot-1);--tag-border-color:var(--color-carrot-6);--tag-bg-color-hover:var(--color-carrot-2);--tag-close-button-bg-color-hover:var(--color-carrot-2);--tag-border-color-hover:var(--color-carrot-7);}/*!sc*/
:is(.redoc, :where(body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .tag-magenta,:is(.redoc, :where(body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .tag-hook{--tag-color:var(--color-magenta-7);--tag-bg-color:var(--color-magenta-1);--tag-border-color:var(--color-magenta-3);--tag-bg-color-hover:var(--color-magenta-2);--tag-close-button-bg-color-hover:var(--color-magenta-2);--tag-border-color-hover:var(--color-magenta-5);}/*!sc*/
:is(.redoc, :where(body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .tag-product{--tag-color:var(--color-warm-grey-7);--tag-bg-color:transparent;--tag-border-color:var(--color-warm-grey-4);--tag-close-button-bg-color-hover:var(--color-warm-grey-3);}/*!sc*/
:is(.redoc, :where(body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .tag-grass{--tag-color:var(--color-grass-8);--tag-bg-color:var(--color-grass-1);--tag-border-color:var(--color-grass-4);--tag-bg-color-hover:var(--color-grass-2);--tag-close-button-bg-color-hover:var(--color-grass-2);--tag-border-color-hover:var(--color-grass-6);}/*!sc*/
:is(.redoc, :where(body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .tag-sky{--tag-color:var(--color-sky-8);--tag-bg-color:var(--color-sky-1);--tag-border-color:var(--color-sky-5);--tag-bg-color-hover:var(--color-sky-2);--tag-close-button-bg-color-hover:var(--color-sky-2);--tag-border-color-hover:var(--color-sky-7);}/*!sc*/
:is(.redoc, :where(body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .tag-raspberry{--tag-color:var(--color-raspberry-7);--tag-bg-color:var(--color-raspberry-1);--tag-border-color:var(--color-raspberry-3);--tag-bg-color-hover:var(--color-raspberry-2);--tag-close-button-bg-color-hover:var(--color-raspberry-2);--tag-border-color-hover:var(--color-raspberry-5);}/*!sc*/
:is(.redoc, :where(body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .tag-orange{--tag-color:var(--color-orange-7);--tag-bg-color:var(--color-orange-1);--tag-border-color:var(--color-orange-4);--tag-bg-color-hover:var(--color-orange-2);--tag-close-button-bg-color-hover:var(--color-orange-2);--tag-border-color-hover:var(--color-orange-5);}/*!sc*/
:is(.redoc, :where(body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .tag-persian-green{--tag-color:var(--color-persian-green-8);--tag-bg-color:var(--color-persian-green-1);--tag-border-color:var(--color-persian-green-5);--tag-bg-color-hover:var(--color-persian-green-2);--tag-close-button-bg-color-hover:var(--color-persian-green-2);--tag-border-color-hover:var(--color-persian-green-6);}/*!sc*/
:is(.redoc, :where(body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .tag-blueberry{--tag-color:var(--color-blueberry-7);--tag-bg-color:var(--color-blueberry-1);--tag-border-color:var(--color-blueberry-4);--tag-bg-color-hover:var(--color-blueberry-2);--tag-close-button-bg-color-hover:var(--color-blueberry-2);--tag-border-color-hover:var(--color-blueberry-6);}/*!sc*/
:is(.redoc, :where(body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .tag-http{--tag-font-family:var(--font-family-monospaced);--tag-text-transform:uppercase;}/*!sc*/
:is(.redoc, :where(body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .tag-head{--tag-color:#6355CA;--tag-bg-color:#ECEBFA;--tag-bg-color-hover:#E0DEF7;}/*!sc*/
:is(.redoc, :where(body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .tag-delete{--tag-color:#e70b46;--tag-bg-color:#fee2e9;--tag-bg-color-hover:#fdd3dd;}/*!sc*/
:is(.redoc, :where(body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .tag-get{--tag-color:#25b869;--tag-bg-color:#e5faef;--tag-bg-color-hover:#D4F7E5;}/*!sc*/
:is(.redoc, :where(body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .tag-post{--tag-color:#1e65f5;--tag-bg-color:#e2ebfe;--tag-bg-color-hover:#CEDDFD;}/*!sc*/
:is(.redoc, :where(body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .tag-query{--tag-color:#25b869;--tag-bg-color:#e5faef;--tag-bg-color-hover:#D4F7E5;}/*!sc*/
:is(.redoc, :where(body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .tag-put{--tag-color:#f5901d;--tag-bg-color:#fef1e2;--tag-bg-color-hover:#FDE2C4;}/*!sc*/
:is(.redoc, :where(body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .tag-patch{--tag-color:#f5c31d;--tag-bg-color:#fdf6dd;--tag-bg-color-hover:#FCF0C5;}/*!sc*/
:is(.redoc, :where(body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .tag-http-deprecated{--tag-color:var(--text-color-disabled);}/*!sc*/
:is(.redoc, :where(body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .tag-http-additional-operation{--tag-color:#F97316;--tag-bg-color:#faede3;--tag-bg-color-hover:#fae2cf;}/*!sc*/
:is(.redoc, :where(body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .tag-send{--tag-color:#4144F6;--tag-bg-color:#ECECFE;--tag-bg-color-hover:#CFCFFC;}/*!sc*/
:is(.redoc, :where(body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .tag-pub{--tag-color:#4144F6;--tag-bg-color:#ECECFE;--tag-bg-color-hover:#CFCFFC;}/*!sc*/
:is(.redoc, :where(body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .tag-publish{--tag-color:#4144F6;--tag-bg-color:#ECECFE;--tag-bg-color-hover:#CFCFFC;}/*!sc*/
:is(.redoc, :where(body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .tag-receive{--tag-color:#4db144;--tag-bg-color:#E5FDE2;--tag-bg-color-hover:#D4FCCF;}/*!sc*/
:is(.redoc, :where(body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .tag-sub{--tag-color:#4db144;--tag-bg-color:#E5FDE2;--tag-bg-color-hover:#D4FCCF;}/*!sc*/
:is(.redoc, :where(body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .tag-cons{--tag-color:#4db144;--tag-bg-color:#E5FDE2;--tag-bg-color-hover:#D4FCCF;}/*!sc*/
:is(.redoc, :where(body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .tag-channel{--tag-color:#F0870E;--tag-bg-color:#FDF1E2;--tag-bg-color-hover:#FBE1C1;}/*!sc*/
:is(.redoc, :where(body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .tag-topic{--tag-color:#F0870E;--tag-bg-color:#FDF1E2;--tag-bg-color-hover:#FBE1C1;}/*!sc*/
:is(.redoc, :where(body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .tag-queue{--tag-color:#D72E81;--tag-bg-color:#FAE5F0;--tag-bg-color-hover:#F7D4E6;}/*!sc*/
:is(.redoc, :where(body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .tag-exchange{--tag-color:#9B51E0;--tag-bg-color:#F0E5FA;--tag-bg-color-hover:#E6D4F7;}/*!sc*/
:is(.redoc, :where(body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .tag-exch{--tag-color:#9B51E0;--tag-bg-color:#F0E5FA;--tag-bg-color-hover:#E6D4F7;}/*!sc*/
:is(.redoc, :where(body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .tag-variant-filled{--tag-border-color:transparent;--tag-border-color-hover:transparent;}/*!sc*/
:is(.redoc, :where(body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .tag-variant-outline{--tag-bg-color:transparent;--tag-bg-color-hover:transparent;}/*!sc*/
:is(.redoc, :where(body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .tooltip-copy-button{--tooltip-text-color:var(--color-static-white);--tooltip-bg-color:var(--color-warm-grey-10);--tooltip-arrow-color:var(--tooltip-border-color);}/*!sc*/
:is(.redoc, :where(body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .tooltip-catalog{--tooltip-text-color:var(--text-color-primary);--tooltip-bg-color:var(--layer-color);--tooltip-arrow-color:var(--border-color-secondary);--tooltip-border-width:2px;--tooltip-border-color:var(--border-color-secondary);--tooltip-padding-vertical:var(--spacing-xs);--tooltip-padding-horizontal:var(--spacing-sm);--tooltip-padding:var(--tooltip-padding-vertical) var(--tooltip-padding-horizontal);--tooltip-max-width:300px;}/*!sc*/
:is(.redoc, :where(body > :has(> [data-component-name='Dropdown/DropdownMenu']))) *::-webkit-scrollbar{width:6px;height:6px;}/*!sc*/
:is(.redoc, :where(body > :has(> [data-component-name='Dropdown/DropdownMenu']))) *::-webkit-scrollbar-track{background-color:transparent;}/*!sc*/
:is(.redoc, :where(body > :has(> [data-component-name='Dropdown/DropdownMenu']))) *::-webkit-scrollbar-thumb{background-color:var(--color-warm-grey-2);border-radius:var(--border-radius-full);}/*!sc*/
:is(.redoc, :where(body > :has(> [data-component-name='Dropdown/DropdownMenu']))) *::-webkit-scrollbar-thumb:hover{background:var(--bg-color-tonal);}/*!sc*/
:is(.redoc, :where(body > :has(> [data-component-name='Dropdown/DropdownMenu']))) *::-webkit-scrollbar-corner{background-color:transparent;}/*!sc*/
@media screen and (max-width: 500px){:where(:root){--feedback-comment-max-width:100%;}}/*!sc*/
@media (prefers-reduced-motion: reduce){:where(:root){--replay-transition-duration:0s;}}/*!sc*/
:where(:root:has(.redoc.dark)){--color-warm-grey-1:#1a1c21;--color-warm-grey-2:#22242b;--color-warm-grey-3:#2a2b33;--color-warm-grey-4:#3b3c45;--color-warm-grey-5:#555761;--color-warm-grey-6:#6e6f7a;--color-warm-grey-7:#9b9ca8;--color-warm-grey-8:#c4c6d1;--color-warm-grey-9:#dcdde5;--color-warm-grey-10:#ededf2;--color-warm-grey-11:#fbfbfc;--color-purple-1:#2c2770;--color-purple-2:#2b2480;--color-purple-3:#291f99;--color-purple-4:#4034ab;--color-purple-5:#5b4ccc;--color-purple-6:#7a69ee;--color-purple-7:#9c8ef4;--color-purple-8:#b3a6f9;--color-purple-9:#cec4fd;--color-purple-10:#e6e1fe;--color-purple-11:#f3f1fe;--color-blueberry-1:#232a61;--color-blueberry-2:#222d7a;--color-blueberry-3:#1e2f93;--color-blueberry-4:#2f46b7;--color-blueberry-5:#4561db;--color-blueberry-6:#5f81ff;--color-blueberry-7:#87a3ff;--color-blueberry-8:#9fb7ff;--color-blueberry-9:#bfd0ff;--color-blueberry-10:#dfe8ff;--color-blueberry-11:#F0F4F;--color-blue-1:#233061;--color-blue-2:#223d7a;--color-blue-3:#1f4399;--color-blue-4:#2558b8;--color-blue-5:#3072db;--color-blue-6:#1f7cff;--color-blue-7:#6eabfa;--color-blue-8:#90c2fc;--color-blue-9:#b3d7fc;--color-blue-10:#d9ebfc;--color-blue-11:#edf5fc;--color-sky-1:#1f3757;--color-sky-2:#1d4479;--color-sky-3:#155393;--color-sky-4:#0569b6;--color-sky-5:#088cda;--color-sky-6:#0bb4fe;--color-sky-7:#47d2fe;--color-sky-8:#6ce5fe;--color-sky-9:#9df4fe;--color-sky-10:#cefdfe;--color-sky-11:#eafefe;--color-turquoise-1:#1c394f;--color-turquoise-2:#1c496b;--color-turquoise-3:#125a81;--color-turquoise-4:#0475a1;--color-turquoise-5:#059cc0;--color-turquoise-6:#08c7e0;--color-turquoise-7:#41e6ec;--color-turquoise-8:#67f5ef;--color-turquoise-9:#9afbef;--color-turquoise-10:#ccfdf3;--color-turquoise-11:#ebfdf9;--color-persian-green-1:#193b47;--color-persian-green-2:#143d4d;--color-persian-green-3:#0f4f5d;--color-persian-green-4:#036a73;--color-persian-green-5:#048a89;--color-persian-green-6:#06a192;--color-persian-green-7:#37c6ab;--color-persian-green-8:#5fe2be;--color-persian-green-9:#97f5d2;--color-persian-green-10:#cafae4;--color-persian-green-11:#ebfaf3;--color-green-1:#1a4d40;--color-green-2:#195848;--color-green-3:#136a4d;--color-green-4:#0e8450;--color-green-5:#149e53;--color-green-6:#1cb854;--color-green-7:#4dd470;--color-green-8:#72e985;--color-green-9:#a3f7a9;--color-green-10:#d2fbd0;--color-green-11:#edfbec;--color-grass-1:#1f4d2d;--color-grass-2:#164f29;--color-grass-3:#135f25;--color-grass-4:#167625;--color-grass-5:#208d28;--color-grass-6:#2fa52d;--color-grass-7:#66c95b;--color-grass-8:#93e380;--color-grass-9:#c2f6ae;--color-grass-10:#e3fad6;--color-grass-11:#f0faeb;--color-carrot-1:#5c3721;--color-carrot-2:#7a4222;--color-carrot-3:#934c1a;--color-carrot-4:#b75f17;--color-carrot-5:#db7e21;--color-carrot-6:#ffa02e;--color-carrot-7:#ffbe62;--color-carrot-8:#ffd181;--color-carrot-9:#ffe4ab;--color-carrot-10:#fff0cc;--color-carrot-11:#fff8e8;--color-orange-1:#612729;--color-orange-2:#7a2024;--color-orange-3:#931e1a;--color-orange-4:#b72111;--color-orange-5:#db3918;--color-orange-6:#ff5722;--color-orange-7:#ff8c59;--color-orange-8:#ffad7a;--color-orange-9:#ffcea6;--color-orange-10:#ffead2;--color-orange-11:#fff4e8;--color-red-1:#612235;--color-red-2:#752137;--color-red-3:#8d1c37;--color-red-4:#af1631;--color-red-5:#d12030;--color-red-6:#f42d2d;--color-red-7:#f86d60;--color-red-8:#fb9580;--color-red-9:#fdc0ab;--color-red-10:#fee3d4;--color-red-11:#fef1ea;--color-raspberry-1:#612241;--color-raspberry-2:#77214c;--color-raspberry-3:#901d56;--color-raspberry-4:#b3185e;--color-raspberry-5:#d6236a;--color-raspberry-6:#f9316d;--color-raspberry-7:#fb6382;--color-raspberry-8:#fd838f;--color-raspberry-9:#feacad;--color-raspberry-10:#fed9d5;--color-raspberry-11:#fef0ef;--color-magenta-1:#5e1a48;--color-magenta-2:#791f5a;--color-magenta-3:#931a65;--color-magenta-4:#b6006a;--color-magenta-5:#da0078;--color-magenta-6:#fe0184;--color-magenta-7:#fe409c;--color-magenta-8:#fe66aa;--color-magenta-9:#fe99c3;--color-magenta-10:#fecce0;--color-magenta-11:#feeff5;--color-black:#ffffff;--color-white:#000000;--color-hover-base:#32343E;--bg-color:var(--color-warm-grey-2);--bg-color-hover:var(--color-warm-grey-3);--bg-color-active:var(--color-warm-grey-4);--bg-color-tonal:var(--color-warm-grey-2);--bg-color-tonal-hover:var(--color-warm-grey-2);--bg-color-tonal-active:var(--color-warm-grey-2);--bg-color-raised:var(--color-warm-grey-2);--bg-color-modal-overlay:rgba(0, 0, 0, 0.4);--bg-raised-gradient:linear-gradient(270deg, var(--color-warm-grey-3) 21.88%, rgba(42, 43, 51, 0) 112.5%);--layer-color:var(--color-warm-grey-3);--layer-color-hover:var(--color-warm-grey-4);--layer-color-active:var(--color-warm-grey-5);--layer-color-ontonal:var(--color-warm-grey-3);--layer-color-ontonal-hover:var(--color-warm-grey-4);--layer-color-ontonal-active:var(--color-warm-grey-5);--layer-color-accent:var(--color-warm-grey-9);--layer-accent-hover:var(--color-warm-grey-10);--layer-color-raised:var(--color-warm-grey-3);--color-brand-subtle-bg:var(--coloLine truncated
:is(.redoc.dark, :where(:root:has(.redoc.dark) body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .tag-grey,:is(.redoc.dark, :where(:root:has(.redoc.dark) body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .tag-draft,:is(.redoc.dark, :where(:root:has(.redoc.dark) body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .tag-schema,:is(.redoc.dark, :where(:root:has(.redoc.dark) body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .tag-default{--tag-color:var(--color-warm-grey-8);--tag-bg-color:var(--color-warm-grey-4);--tag-bg-color-hover:var(--color-warm-grey-5);--tag-close-button-bg-color-hover:var(--color-warm-grey-5);}/*!sc*/
:is(.redoc.dark, :where(:root:has(.redoc.dark) body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .tag-red{--tag-color:var(--color-red-7);--tag-bg-color:var(--color-red-1);--tag-border-color:var(--color-red-8);--tag-border-color-hover:var(--color-red-6);--tag-close-button-bg-color-hover:var(--color-red-2);}/*!sc*/
:is(.redoc.dark, :where(:root:has(.redoc.dark) body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .tag-green{--tag-color:var(--color-green-7);--tag-bg-color:var(--color-green-1);--tag-border-color:var(--color-green-3);--tag-border-color-hover:var(--color-green-5);--tag-bg-color-hover:var(--color-green-2);--tag-close-button-bg-color-hover:var(--color-green-2);}/*!sc*/
:is(.redoc.dark, :where(:root:has(.redoc.dark) body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .tag-blue{--tag-color:var(--color-blue-7);--tag-bg-color:var(--color-blue-1);--tag-border-color-hover:var(--color-blue-5);--tag-bg-color-hover:var(--color-blue-2);--tag-close-button-bg-color-hover:var(--color-blue-2);}/*!sc*/
:is(.redoc.dark, :where(:root:has(.redoc.dark) body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .tag-magenta,:is(.redoc.dark, :where(:root:has(.redoc.dark) body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .tag-hook{--tag-color:var(--color-magenta-4);--tag-bg-color:var(--color-magenta-1);--tag-bg-color-hover:var(--color-magenta-2);--tag-close-button-bg-color-hover:var(--color-magenta-2);}/*!sc*/
:is(.redoc.dark, :where(:root:has(.redoc.dark) body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .tag-purple{--tag-color:var(--color-purple-7);--tag-bg-color:var(--color-purple-1);--tag-bg-color-hover:var(--color-purple-2);--tag-border-color-hover:var(--color-purple-5);--tag-close-button-bg-color-hover:var(--color-purple-2);}/*!sc*/
:is(.redoc.dark, :where(:root:has(.redoc.dark) body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .tag-carrot{--tag-color:var(--color-carrot-6);--tag-bg-color:var(--color-carrot-1);--tag-bg-color-hover:var(--color-carrot-2);--tag-border-color:var(--color-carrot-4);--tag-close-button-bg-color-hover:var(--color-carrot-2);}/*!sc*/
:is(.redoc.dark, :where(:root:has(.redoc.dark) body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .tag-raspberry{--tag-color:var(--color-raspberry-7);--tag-bg-color:var(--color-raspberry-1);--tag-bg-color-hover:var(--color-raspberry-2);--tag-close-button-bg-color-hover:var(--color-raspberry-2);}/*!sc*/
:is(.redoc.dark, :where(:root:has(.redoc.dark) body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .tag-orange{--tag-color:var(--color-orange-7);--tag-bg-color:var(--color-orange-1);--tag-bg-color-hover:var(--color-orange-2);--tag-border-color:var(--color-orange-3);--tag-border-color-hover:var(--color-orange-5);--tag-close-button-bg-color-hover:var(--color-orange-2);}/*!sc*/
:is(.redoc.dark, :where(:root:has(.redoc.dark) body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .tag-grass{--tag-bg-color:var(--color-grass-1);--tag-bg-color-hover:var(--color-grass-2);--tag-color:var(--color-grass-7);--tag-border-color:var(--color-grass-3);--tag-border-color-hover:var(--color-grass-5);--tag-close-button-bg-color-hover:var(--color-grass-2);}/*!sc*/
:is(.redoc.dark, :where(:root:has(.redoc.dark) body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .tag-persian-green{--tag-color:var(--color-persian-green-7);--tag-bg-color:var(--color-persian-green-1);--tag-bg-color-hover:var(--color-persian-green-2);--tag-border-color:var(--color-persian-green-3);--tag-border-color-hover:var(--color-persian-green-5);--tag-close-button-bg-color-hover:var(--color-persian-green-2);}/*!sc*/
:is(.redoc.dark, :where(:root:has(.redoc.dark) body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .tag-turquoise,:is(.redoc.dark, :where(:root:has(.redoc.dark) body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .tag-option{--tag-color:var(--color-turquoise-6);--tag-bg-color:var(--color-turquoise-1);--tag-bg-color-hover:var(--color-turquoise-2);--tag-border-color:var(--color-turquoise-4);--tag-close-button-bg-color-hover:var(--color-turquoise-2);}/*!sc*/
:is(.redoc.dark, :where(:root:has(.redoc.dark) body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .tag-sky{--tag-color:var(--color-sky-6);--tag-bg-color:var(--color-sky-1);--tag-border-color:var(--color-sky-4);--tag-bg-color-hover:var(--color-sky-2);--tag-close-button-bg-color-hover:var(--color-sky-2);}/*!sc*/
:is(.redoc.dark, :where(:root:has(.redoc.dark) body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .tag-blueberry{--tag-color:var(--color-blueberry-7);--tag-bg-color:var(--color-blueberry-1);--tag-border-color-hover:var(--color-blueberry-5);--tag-bg-color-hover:var(--color-blueberry-2);--tag-close-button-bg-color-hover:var(--color-blueberry-2);}/*!sc*/
:is(.redoc.dark, :where(:root:has(.redoc.dark) body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .tag-warning,:is(.redoc.dark, :where(:root:has(.redoc.dark) body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .tag-deprecated{--tag-color:var(--color-warning-active);--tag-bg-color:var(--color-warning-bg);--tag-bg-color-hover:var(--color-warning-bg-hover);--tag-close-button-bg-color-hover:var(--color-warning-hover);}/*!sc*/
:is(.redoc.dark, :where(:root:has(.redoc.dark) body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .tag-processing,:is(.redoc.dark, :where(:root:has(.redoc.dark) body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .tag-info{--tag-color:var(--color-info-active);--tag-bg-color:var(--color-info-bg);--tag-bg-color-hover:var(--color-info-bg-hover);--tag-close-button-bg-color-hover:var(--color-info-hover);}/*!sc*/
:is(.redoc.dark, :where(:root:has(.redoc.dark) body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .tag-error,:is(.redoc.dark, :where(:root:has(.redoc.dark) body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .tag-declined{--tag-color:var(--color-error-active);--tag-bg-color:var(--color-error-bg);--tag-bg-color-hover:var(--color-error-bg-hover);--tag-close-button-bg-color-hover:var(--color-error-hover);}/*!sc*/
:is(.redoc.dark, :where(:root:has(.redoc.dark) body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .tag-success,:is(.redoc.dark, :where(:root:has(.redoc.dark) body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .tag-approved{--tag-color:var(--color-success-active);--tag-bg-color:var(--color-success-bg);--tag-bg-color-hover:var(--color-success-bg-hover);--tag-close-button-bg-color-hover:var(--color-success-bg-hover);}/*!sc*/
:is(.redoc.dark, :where(:root:has(.redoc.dark) body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .tag-link{--tag-color:var(--color-info-active);--tag-bg-color:var(--color-info-bg);--tag-bg-color-hover:var(--color-info-bg-hover);--tag-close-button-bg-color-hover:var(--color-info-hover);}/*!sc*/
:is(.redoc.dark, :where(:root:has(.redoc.dark) body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .tag-delete{--tag-color:#fa82a2;--tag-bg-color:#47252E;--tag-bg-color-hover:#653441;}/*!sc*/
:is(.redoc.dark, :where(:root:has(.redoc.dark) body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .tag-get{--tag-color:#68cc97;--tag-bg-color:#1F3D2D;--tag-bg-color-hover:#34654B;}/*!sc*/
:is(.redoc.dark, :where(:root:has(.redoc.dark) body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .tag-post{--tag-color:#90b0f0;--tag-bg-color:#2B3447;--tag-bg-color-hover:#3A465F;}/*!sc*/
:is(.redoc.dark, :where(:root:has(.redoc.dark) body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .tag-query{--tag-color:#68cc97;--tag-bg-color:#1F3D2D;--tag-bg-color-hover:#34654B;}/*!sc*/
:is(.redoc.dark, :where(:root:has(.redoc.dark) body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .tag-put{--tag-color:#e0a663;--tag-bg-color:#3D2D1B;--tag-bg-color-hover:#674C2D;}/*!sc*/
:is(.redoc.dark, :where(:root:has(.redoc.dark) body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .tag-patch{--tag-color:#e0c363;--tag-bg-color:#3D351B;--tag-bg-color-hover:#67592D;}/*!sc*/
:is(.redoc.dark, :where(:root:has(.redoc.dark) body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .tag-head{--tag-color:var(--color-purple-7);--tag-bg-color:#312B5A;--tag-bg-color-hover:#413875;}/*!sc*/
:is(.redoc.dark, :where(:root:has(.redoc.dark) body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .tag-channel{--tag-color:#F5AD5B;--tag-bg-color:#4A3721;--tag-bg-color-hover:#64492B;}/*!sc*/
:is(.redoc.dark, :where(:root:has(.redoc.dark) body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .tag-topic{--tag-color:#F5AD5B;--tag-bg-color:#4A3721;--tag-bg-color-hover:#64492B;}/*!sc*/
:is(.redoc.dark, :where(:root:has(.redoc.dark) body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .tag-send{--tag-color:#7779F8;--tag-bg-color:#272754;--tag-bg-color-hover:#373776;}/*!sc*/
:is(.redoc.dark, :where(:root:has(.redoc.dark) body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .tag-pub{--tag-color:#7779F8;--tag-bg-color:#272754;--tag-bg-color-hover:#373776;}/*!sc*/
:is(.redoc.dark, :where(:root:has(.redoc.dark) body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .tag-publish{--tag-color:#7779F8;--tag-bg-color:#272754;--tag-bg-color-hover:#373776;}/*!sc*/
:is(.redoc.dark, :where(:root:has(.redoc.dark) body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .tag-http-additional-operation{--tag-color:#FCAB69;--tag-bg-color:#523926;--tag-bg-color-hover:#694830;}/*!sc*/
:is(.redoc.dark, :where(:root:has(.redoc.dark) body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .tag-receive{--tag-color:#88CF82;--tag-bg-color:#23421F;--tag-bg-color-hover:#305A2A;}/*!sc*/
:is(.redoc.dark, :where(:root:has(.redoc.dark) body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .tag-sub{--tag-color:#88CF82;--tag-bg-color:#23421F;--tag-bg-color-hover:#305A2A;}/*!sc*/
:is(.redoc.dark, :where(:root:has(.redoc.dark) body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .tag-cons{--tag-color:#88CF82;--tag-bg-color:#23421F;--tag-bg-color-hover:#305A2A;}/*!sc*/
:is(.redoc.dark, :where(:root:has(.redoc.dark) body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .tag-exchange{--tag-color:#C79CF2;--tag-bg-color:#472669;--tag-bg-color-hover:#5C3187;}/*!sc*/
:is(.redoc.dark, :where(:root:has(.redoc.dark) body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .tag-exch{--tag-color:#C79CF2;--tag-bg-color:#472669;--tag-bg-color-hover:#5C3187;}/*!sc*/
:is(.redoc.dark, :where(:root:has(.redoc.dark) body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .tag-queue{--tag-color:#F7A7CF;--tag-bg-color:#622242;--tag-bg-color-hover:#882F5C;}/*!sc*/
:is(.redoc.dark, :where(:root:has(.redoc.dark) body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .tag-variant-filled{--tag-border-color:transparent;--tag-border-color-hover:transparent;}/*!sc*/
:is(.redoc.dark, :where(:root:has(.redoc.dark) body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .tag-variant-outline{--tag-bg-color:transparent;--tag-bg-color-hover:transparent;}/*!sc*/
:is(.redoc.dark, :where(:root:has(.redoc.dark) body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .card-variant-filled{--card-bg-color:var(--color-warm-grey-3);--card-bg-color-hover:var(--color-brand-subtle-bg-hover, var(--card-bg-color-hover-legacy));--card-border-width:0;--card-icon-bg-color:var(--card-bg-color);--card-icon-bg-color-hover:transparent;}/*!sc*/
:is(.redoc.dark, :where(:root:has(.redoc.dark) body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .card-variant-outlined{--card-icon-bg-color:var(--color-warm-grey-3);}/*!sc*/
:is(.redoc.dark, :where(:root:has(.redoc.dark) body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .card-variant-elevated{--card-box-shadow:0px 8px 24px -4px #00000014,0px 6px 12px -6px #0000001F;--card-box-shadow-hover:0px 14px 64px -4px #0000001F,0px 8px 22px -6px #0000001F;}/*!sc*/
:is(.redoc.dark, :where(:root:has(.redoc.dark) body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .card-icon-color-green{--card-icon-color:var(--color-green-7);}/*!sc*/
:is(.redoc.dark, :where(:root:has(.redoc.dark) body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .card-icon-color-grey{--card-icon-color:var(--color-warm-grey-8);}/*!sc*/
:is(.redoc.dark, :where(:root:has(.redoc.dark) body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .card-icon-color-turquoise{--card-icon-color:var(--color-turquoise-6);}/*!sc*/
:is(.redoc.dark, :where(:root:has(.redoc.dark) body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .card-icon-color-magenta{--card-icon-color:var(--color-magenta-4);}/*!sc*/
:is(.redoc.dark, :where(:root:has(.redoc.dark) body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .card-icon-color-carrot{--card-icon-color:var(--color-carrot-6);}/*!sc*/
:is(.redoc.dark, :where(:root:has(.redoc.dark) body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .card-icon-color-grass{--card-icon-color:var(--color-grass-7);}/*!sc*/
:is(.redoc.dark, :where(:root:has(.redoc.dark) body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .card-icon-color-persian-green{--card-icon-color:var(--color-persian-green-7);}/*!sc*/
:is(.redoc.dark, :where(:root:has(.redoc.dark) body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .card-icon-color-sky{--card-icon-color:var(--color-sky-6);}/*!sc*/
:is(.redoc.dark, :where(:root:has(.redoc.dark) body > :has(> [data-component-name='Dropdown/DropdownMenu']))) .tooltip-copy-button{--tooltip-bg-color:var(--color-warm-grey-4);}/*!sc*/
:is(.redoc.dark, :where(:root:has(.redoc.dark) body > :has(> [data-component-name='Dropdown/DropdownMenu']))) *::-webkit-scrollbar-thumb{background-color:var(--color-warm-grey-4);}/*!sc*/
.redoc{background-color:var(--bg-color);color:var(--text-color-primary);font-family:var(--font-family-base);}/*!sc*/
:where(body > :has(> [data-component-name='Dropdown/DropdownMenu'])){color:var(--text-color-primary);font-family:var(--font-family-base);}/*!sc*/
.redoc.dark,:where(:root:has(.redoc.dark) body > :has(> [data-component-name='Dropdown/DropdownMenu'])){color-scheme:dark;}/*!sc*/
:where(.redoc) svg[display='none']{display:none;}/*!sc*/
:where(.redoc, body > :has(> [data-component-name='Dropdown/DropdownMenu'])) button{font-family:var(--font-family-base);font-size:var(--font-size-base);font-weight:var(--font-weight-regular);line-height:var(--line-height-base);background-color:inherit;cursor:pointer;border:none;color:var(--text-color-primary);}/*!sc*/
:where(.redoc) :is(p, ul, ol, dl, dd, blockquote, figure, pre, hr, h1, h2, h3, h4, h5, h6, table){margin:revert;}/*!sc*/
:where(.redoc) :is(ul, ol, menu, dd){padding:revert;list-style:revert;}/*!sc*/
:where(.redoc) :is(h1, h2, h3, h4, h5, h6){font-size:revert;font-weight:revert;line-height:revert;}/*!sc*/
:where(.redoc) a{color:revert;text-decoration:revert;}/*!sc*/
:where(.redoc) :is(code, kbd, samp, pre){font-family:revert;font-size:revert;color:revert;}/*!sc*/
:where(.redoc) :is(img, svg, video, canvas, iframe){display:revert;vertical-align:revert;}/*!sc*/
:where(.redoc) table{border-collapse:revert;}/*!sc*/
data-styled.g560[id="sc-global-erNKDm"]{content:"sc-global-erNKDm1,"}/*!sc*/
.bStpwo{color:var(--search-trigger-color);line-height:var(--search-trigger-line-height);flex-grow:1;cursor:pointer;}/*!sc*/
.bStpwo svg{width:var(--search-trigger-icon-size);height:var(--search-trigger-icon-size);}/*!sc*/
data-styled.g603[id="sc-eavxOQ"]{content:"bStpwo,"}/*!sc*/
.hRGWCd{display:inline-flex;align-items:center;justify-content:left;gap:var(--search-trigger-gap);width:100%;border:var(--search-trigger-border-width) var(--search-trigger-border-style) var(--search-trigger-border-color);border-radius:var(--search-trigger-border-radius);background:var(--search-trigger-bg-color);padding:var(--search-trigger-padding);}/*!sc*/
.hRGWCd:hover{border-color:var(--search-trigger-border-color-hover);}/*!sc*/
data-styled.g605[id="sc-dGYDtq"]{content:"hRGWCd,"}/*!sc*/
.eXtYur{display:flex;margin:var(--sidebar-margin-horizontal, 16px);}/*!sc*/
data-styled.g606[id="sc-bJapvR"]{content:"eXtYur,"}/*!sc*/
.PCvEP{flex-shrink:0;}/*!sc*/
data-styled.g608[id="sc-dGPMcj"]{content:"PCvEP,"}/*!sc*/
.kJsUv{display:flex;flex-direction:column;background-color:var(--menu-item-bg-color);font-family:var(--menu-item-font-family);font-size:var(--menu-item-font-size);font-weight:var(--menu-item-font-weight);line-height:var(--menu-item-line-height);color:var(--menu-item-text-color);}/*!sc*/
.kJsUv .tag-http{align-self:flex-start;margin-left:auto;}/*!sc*/
.kJsUv.menu-item-type-separator{pointer-events:none;}/*!sc*/
.kJsUv >a{text-decoration:none;color:var(--menu-item-text-color);}/*!sc*/
data-styled.g609[id="sc-jEekwy"]{content:"kJsUv,"}/*!sc*/
.joxUYR{order:1;position:relative;}/*!sc*/
.joxUYR:hover:has(.joxUYR:hover)::before{display:none;}/*!sc*/
.joxUYR:hover::before{content:'';position:absolute;bottom:var(--spacing-unit);top:0;z-index:var(--z-index-surface);left:calc(
var(--menu-item-label-margin-horizontal) +
var(--menu-item-padding-horizontal) +
(var(--menu-item-label-chevron-size) / 2 - 1px) +
var(--menu-item-nested-offset) * 0);border:0.5px solid var(--menu-item-border-color-hover);}/*!sc*/
data-styled.g616[id="sc-hgJgCR"]{content:"dlNiXx,"}/*!sc*/
.fYmDZR{display:flex;position:relative;cursor:pointer;order:1;align-items:var(--menu-item-label-align-items);transition:var(--menu-item-label-transition);word-break:var(--menu-item-label-word-break);border-radius:var(--menu-item-label-border-radius);margin:var(--menu-item-label-margin);padding:var(--menu-item-label-padding);gap:var(--menu-item-label-gap);padding-left:calc(
data-styled.g610[id="sc-fKDFlh"]{content:"joxUYR,"}/*!sc*/
.eWEtkC{display:flex;position:relative;cursor:pointer;order:1;align-items:var(--menu-item-label-align-items);transition:var(--menu-item-label-transition);word-break:var(--menu-item-label-word-break);border-radius:var(--menu-item-label-border-radius);margin:var(--menu-item-label-margin);padding:var(--menu-item-label-padding);gap:var(--menu-item-label-gap);padding-left:calc(
var(--menu-item-padding-horizontal) +
var(--menu-item-label-chevron-offset) + 0px +
0px
);color:var(--menu-item-color-active);background-color:var(--menu-item-bg-color-active);}/*!sc*/
.fYmDZR:hover{color:var(--menu-item-color-hover);background:var(--menu-item-bg-color-hover);}/*!sc*/
.fYmDZR:hover .sc-jePOid path{fill:var(--menu-item-color-hover);}/*!sc*/
.fYmDZR:hover .sc-iUlCMS path{fill:var(--menu-item-color-hover);}/*!sc*/
.fYmDZR .sc-jePOid path{fill:var(--menu-item-color-active);}/*!sc*/
.fYmDZR .sc-iUlCMS path{fill:var(--menu-item-color-active);}/*!sc*/
.fYmDZR:hover{color:var(--menu-item-color-active-hover);background:var(--menu-item-bg-color-active-hover);}/*!sc*/
.fYmDZR:empty{padding:0;}/*!sc*/
.itBMtf{display:flex;position:relative;cursor:pointer;order:1;align-items:var(--menu-item-label-align-items);transition:var(--menu-item-label-transition);word-break:var(--menu-item-label-word-break);border-radius:var(--menu-item-label-border-radius);margin:var(--menu-item-label-margin);padding:var(--menu-item-label-padding);gap:var(--menu-item-label-gap);padding-left:calc(
.eWEtkC:hover{color:var(--menu-item-color-hover);background:var(--menu-item-bg-color-hover);}/*!sc*/
.eWEtkC:hover .sc-bBzIOb path{fill:var(--menu-item-color-hover);}/*!sc*/
.eWEtkC:hover .sc-kLEbxe path{fill:var(--menu-item-color-hover);}/*!sc*/
.eWEtkC .sc-bBzIOb path{fill:var(--menu-item-color-active);}/*!sc*/
.eWEtkC .sc-kLEbxe path{fill:var(--menu-item-color-active);}/*!sc*/
.eWEtkC:hover{color:var(--menu-item-color-active-hover);background:var(--menu-item-bg-color-active-hover);}/*!sc*/
.eWEtkC:empty{padding:0;}/*!sc*/
.iVTRMP{display:flex;position:relative;cursor:pointer;order:1;align-items:var(--menu-item-label-align-items);transition:var(--menu-item-label-transition);word-break:var(--menu-item-label-word-break);border-radius:var(--menu-item-label-border-radius);margin:var(--menu-item-label-margin);padding:var(--menu-item-label-padding);gap:var(--menu-item-label-gap);padding-left:calc(
var(--menu-item-padding-horizontal) +
var(--menu-item-label-chevron-offset) + 0px +
0px
);}/*!sc*/
.itBMtf:hover{color:var(--menu-item-color-hover);background:var(--menu-item-bg-color-hover);}/*!sc*/
.itBMtf:hover .sc-jePOid path{fill:var(--menu-item-color-hover);}/*!sc*/
.itBMtf:hover .sc-iUlCMS path{fill:var(--menu-item-color-hover);}/*!sc*/
.itBMtf:empty{padding:0;}/*!sc*/
.hKPPiy{display:flex;position:relative;cursor:pointer;order:1;align-items:var(--menu-item-label-align-items);transition:var(--menu-item-label-transition);word-break:var(--menu-item-label-word-break);border-radius:var(--menu-item-label-border-radius);margin:var(--menu-item-label-margin);padding:var(--menu-item-label-padding);gap:var(--menu-item-label-gap);padding-left:calc(
.iVTRMP:hover{color:var(--menu-item-color-hover);background:var(--menu-item-bg-color-hover);}/*!sc*/
.iVTRMP:hover .sc-bBzIOb path{fill:var(--menu-item-color-hover);}/*!sc*/
.iVTRMP:hover .sc-kLEbxe path{fill:var(--menu-item-color-hover);}/*!sc*/
.iVTRMP:empty{padding:0;}/*!sc*/
.jYycQW{display:flex;position:relative;cursor:pointer;order:1;align-items:var(--menu-item-label-align-items);transition:var(--menu-item-label-transition);word-break:var(--menu-item-label-word-break);border-radius:var(--menu-item-label-border-radius);margin:var(--menu-item-label-margin);padding:var(--menu-item-label-padding);gap:var(--menu-item-label-gap);padding-left:calc(
var(--menu-item-padding-horizontal) +
0px +
0px
);}/*!sc*/
.hKPPiy:hover{color:var(--menu-item-color-hover);background:var(--menu-item-bg-color-hover);}/*!sc*/
.hKPPiy:hover .sc-jePOid path{fill:var(--menu-item-color-hover);}/*!sc*/
.hKPPiy:hover .sc-iUlCMS path{fill:var(--menu-item-color-hover);}/*!sc*/
.hKPPiy:empty{padding:0;}/*!sc*/
data-styled.g617[id="sc-epufQc"]{content:"fYmDZR,itBMtf,hKPPiy,"}/*!sc*/
.jshptd{display:flex;flex-direction:column;flex-grow:1;}/*!sc*/
data-styled.g618[id="sc-boLXHU"]{content:"jshptd,"}/*!sc*/
.iKLmjR{--icon-width:var(--menu-item-icon-size);--icon-height:var(--menu-item-icon-size);margin:var(--menu-item-icon-margin);flex-shrink:0;overflow:hidden;}/*!sc*/
data-styled.g620[id="sc-bmzNle"]{content:"iKLmjR,"}/*!sc*/
.iBHKJo{order:1;}/*!sc*/
data-styled.g621[id="sc-iJONRL"]{content:"iBHKJo,"}/*!sc*/
.hrIrOF>*{margin-right:var(--spacing-xxs);}/*!sc*/
data-styled.g623[id="sc-bcyEQV"]{content:"hrIrOF,"}/*!sc*/
.iyAVQU{display:inline-block;max-width:100%;font-weight:var(--menu-item-font-weight);font-weight:var(--menu-item-font-weight-active);}/*!sc*/
.iyAVQU::before{content:attr(data-text);display:block;height:0;overflow:hidden;visibility:hidden;font-weight:var(--menu-item-font-weight-active);}/*!sc*/
.liVEkC{display:inline-block;max-width:100%;font-weight:var(--menu-item-font-weight);}/*!sc*/
.liVEkC::before{content:attr(data-text);display:block;height:0;overflow:hidden;visibility:hidden;font-weight:var(--menu-item-font-weight-active);}/*!sc*/
data-styled.g624[id="sc-eStdmu"]{content:"iyAVQU,liVEkC,"}/*!sc*/
.ctPApS{padding:var(--menu-padding);margin:var(--menu-margin);list-style:var(--menu-list-style);color:var(--menu-content-color);position:relative;}/*!sc*/
data-styled.g637[id="sc-kBPbfe"]{content:"ctPApS,"}/*!sc*/
.dtcefb{flex:1;overflow-y:auto;}/*!sc*/
data-styled.g638[id="sc-dNLPWG"]{content:"dtcefb,"}/*!sc*/
.HgsaY{height:16px;width:16px;}/*!sc*/
.HgsaY path{fill:currentColor;}/*!sc*/
data-styled.g640[id="sc-gpABuY"]{content:"HgsaY,"}/*!sc*/
.dHwMlA{box-sizing:border-box;}/*!sc*/
data-styled.g642[id="sc-htFfXW"]{content:"dHwMlA,"}/*!sc*/
.jCBoXz{height:14px;width:14px;}/*!sc*/
.jCBoXz path{fill:var(--segmented-buttons-content-color);}/*!sc*/
data-styled.g643[id="sc-dByxfH"]{content:"jCBoXz,"}/*!sc*/
.hfyGEo{height:14px;width:14px;}/*!sc*/
.hfyGEo path{fill:var(--segmented-buttons-content-color);}/*!sc*/
data-styled.g644[id="sc-jVtRwo"]{content:"hfyGEo,"}/*!sc*/
.cRwIbi{display:flex;position:relative;flex-direction:row;justify-content:center;align-items:center;cursor:pointer;border-radius:var(--border-radius);background:var(--segmented-buttons-bg-color-main);z-index:1;padding:calc(var(--spacing-unit) / 2);}/*!sc*/
data-styled.g645[id="sc-gdniDZ"]{content:"cRwIbi,"}/*!sc*/
.hIBjpD{width:20px;height:20px;border-radius:var(--border-radius);display:flex;align-items:center;justify-content:center;}/*!sc*/
.bklIjO{width:20px;height:20px;border-radius:var(--border-radius);display:flex;align-items:center;justify-content:center;background-color:var(--segmented-buttons-bg-color-inner);}/*!sc*/
data-styled.g646[id="sc-fjRroq"]{content:"hIBjpD,bklIjO,"}/*!sc*/
.hpNlci{display:none;justify-content:flex-end;bottom:0;flex-direction:row;gap:var(--spacing-xs);align-items:center;margin-left:var(--sidebar-controls-margin-left);z-index:2;background:var(--sidebar-bg-color);}/*!sc*/
@media screen and (min-width: 672px){.hpNlci{display:flex;}}/*!sc*/
@media print{.hpNlci{display:none;}}/*!sc*/
data-styled.g647[id="sc-brKIwb"]{content:"hpNlci,"}/*!sc*/
.jIasHw{display:flex;bottom:0;flex-direction:row;gap:var(--spacing-xs);align-items:center;}/*!sc*/
data-styled.g648[id="sc-NPrTd"]{content:"jIasHw,"}/*!sc*/
.klZAKh{height:16px;width:16px;}/*!sc*/
.klZAKh path{fill:currentColor;}/*!sc*/
data-styled.g649[id="sc-jlWaFk"]{content:"klZAKh,"}/*!sc*/
.NwZfv{display:flex;flex-direction:row;align-items:center;justify-content:space-between;gap:var(--spacing-unit);position:sticky;top:calc(100vh);padding:var(--spacing-sm) var(--spacing-md);}/*!sc*/
data-styled.g651[id="sc-jjJQiu"]{content:"NwZfv,"}/*!sc*/
.jODDqk{display:inline-flex;flex-direction:row;align-items:center;gap:var(--spacing-unit);}/*!sc*/
data-styled.g652[id="sc-cXPzfF"]{content:"jODDqk,"}/*!sc*/
.cDtVPi{text-align:center;width:auto;height:auto;display:inline-flex;justify-content:center;bottom:0;background:var(--color-blue-1);padding:2px 8px;margin-bottom:0;border-radius:21px;}/*!sc*/
.cDtVPi:hover{background:var(--color-blue-2);}/*!sc*/
.cDtVPi a,.cDtVPi a:visited,.cDtVPi a:hover{color:var(--sidebar-text-color)!important;text-decoration:none;display:flex;align-items:center;justify-content:center;}/*!sc*/
.cDtVPi img{height:12px;user-select:none;}/*!sc*/
.cDtVPi p{font-size:calc(var(--font-size-xl) / 2);line-height:var(--line-height-sm);margin:0;margin-right:4px;}/*!sc*/
@media screen and (min-width: 672px){.cDtVPi{position:sticky;z-index:auto;}}/*!sc*/
data-styled.g653[id="sc-gAysQ"]{content:"cDtVPi,"}/*!sc*/
.dndlVX{height:24px;width:24px;}/*!sc*/
.dndlVX path{fill:currentColor;}/*!sc*/
data-styled.g654[id="sc-cVDoIP"]{content:"dndlVX,"}/*!sc*/
.bbUcqP{border:none;outline:none;user-select:none;background-color:var(--sidebar-bg-color, #fff);color:var(--color-primary-base, var(--color-blueberry-6));display:none;align-items:center;justify-content:center;cursor:pointer;position:fixed;right:20px;bottom:44px;z-index:100;border-radius:50%;box-shadow:0 0 20px rgba(0, 0, 0, 0.3);width:60px;height:60px;padding:0;}/*!sc*/
.bbUcqP svg path{fill:var(--color-primary-base, var(--color-blueberry-6));}/*!sc*/
@media screen and (max-width: 672px){.bbUcqP{display:flex;}}/*!sc*/
@media print{.bbUcqP{display:none;}}/*!sc*/
data-styled.g655[id="sc-eonWa"]{content:"bbUcqP,"}/*!sc*/
.dGsweu{flex-shrink:0;overflow-y:auto;overflow-x:hidden;top:var(--navbar-stack-height, 0px);height:calc(100vh - var(--navbar-stack-height, 0px));height:calc(100dvh - var(--navbar-stack-height, 0px));flex-direction:column;background:var(--sidebar-bg-color, #fff);border-right:1px solid var(--sidebar-border-color, #e0e0e0);position:fixed;z-index:20;width:100%;display:none;}/*!sc*/
.dGsweu [data-component-name='SidebarLogo/SidebarLogo']{padding:var(--spacing-md, 16px) var(--spacing-sm, 12px);}/*!sc*/
.dGsweu [data-component-name='SidebarLogo/SidebarLogo'] img{max-width:min(140px, var(--sidebar-logo-max-width, 285px));}/*!sc*/
@media screen and (min-width: 672px){.dGsweu{position:sticky;z-index:auto;display:flex;width:var(--sidebar-width, 260px);}.dGsweu [data-component-name='SidebarLogo/SidebarLogo'] img{max-width:var(--sidebar-logo-max-width, 285px);}}/*!sc*/
@media print{.dGsweu{display:none;}}/*!sc*/
data-styled.g656[id="sc-cjUTkN"]{content:"dGsweu,"}/*!sc*/
.fFQMWf{display:flex;position:relative;min-height:100vh;text-align:left;background-color:var(--bg-color);font-family:var(--font-family-base);font-size:var(--font-size-base);line-height:var(--line-height-base);font-weight:var(--font-weight-regular);color:var(--text-color-primary);-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;}/*!sc*/
.fFQMWf [data-section-id]{scroll-margin-top:calc(var(--navbar-stack-height, 0px) + var(--banner-height, 0px));}/*!sc*/
.fFQMWf [id]:not([data-section-id]):has([data-deep-link-anchor]){scroll-margin-top:calc(
.jYycQW:hover{color:var(--menu-item-color-hover);background:var(--menu-item-bg-color-hover);}/*!sc*/
.jYycQW:hover .sc-bBzIOb path{fill:var(--menu-item-color-hover);}/*!sc*/
.jYycQW:hover .sc-kLEbxe path{fill:var(--menu-item-color-hover);}/*!sc*/
.jYycQW:empty{padding:0;}/*!sc*/
data-styled.g611[id="sc-cToEys"]{content:"eWEtkC,iVTRMP,jYycQW,"}/*!sc*/
.dBwuTA{display:flex;flex-direction:column;flex-grow:1;}/*!sc*/
data-styled.g612[id="sc-fIruOr"]{content:"dBwuTA,"}/*!sc*/
.gAnsnE{--icon-width:var(--menu-item-icon-size);--icon-height:var(--menu-item-icon-size);margin:var(--menu-item-icon-margin);flex-shrink:0;overflow:hidden;}/*!sc*/
data-styled.g614[id="sc-jqwVmx"]{content:"gAnsnE,"}/*!sc*/
.kKmXTr{order:1;}/*!sc*/
data-styled.g615[id="sc-gzhUzI"]{content:"kKmXTr,"}/*!sc*/
.efjekG>*{margin-right:var(--spacing-xxs);}/*!sc*/
data-styled.g617[id="sc-gwVKcS"]{content:"efjekG,"}/*!sc*/
.dWgUkd{display:inline-block;max-width:100%;font-weight:var(--menu-item-font-weight);font-weight:var(--menu-item-font-weight-active);}/*!sc*/
.dWgUkd::before{content:attr(data-text);display:block;height:0;overflow:hidden;visibility:hidden;font-weight:var(--menu-item-font-weight-active);}/*!sc*/
.dlVNdD{display:inline-block;max-width:100%;font-weight:var(--menu-item-font-weight);}/*!sc*/
.dlVNdD::before{content:attr(data-text);display:block;height:0;overflow:hidden;visibility:hidden;font-weight:var(--menu-item-font-weight-active);}/*!sc*/
data-styled.g618[id="sc-hgJgCR"]{content:"dWgUkd,dlVNdD,"}/*!sc*/
.hoWuXt{padding:var(--menu-padding);margin:var(--menu-margin);list-style:var(--menu-list-style);color:var(--menu-content-color);position:relative;}/*!sc*/
data-styled.g631[id="sc-jfJzNu"]{content:"hoWuXt,"}/*!sc*/
.kAUzfx{flex:1;overflow-y:auto;}/*!sc*/
data-styled.g632[id="sc-kLsiYl"]{content:"kAUzfx,"}/*!sc*/
.hWOHcP{height:16px;width:16px;}/*!sc*/
.hWOHcP path{fill:currentColor;}/*!sc*/
data-styled.g634[id="sc-Abecn"]{content:"hWOHcP,"}/*!sc*/
.HJDgC{box-sizing:border-box;}/*!sc*/
data-styled.g636[id="sc-dbPPAF"]{content:"HJDgC,"}/*!sc*/
.dbAJDG{height:14px;width:14px;}/*!sc*/
.dbAJDG path{fill:var(--segmented-buttons-content-color);}/*!sc*/
data-styled.g637[id="sc-dXVROg"]{content:"dbAJDG,"}/*!sc*/
.fSOZZc{height:14px;width:14px;}/*!sc*/
.fSOZZc path{fill:var(--segmented-buttons-content-color);}/*!sc*/
data-styled.g638[id="sc-jFIYRD"]{content:"fSOZZc,"}/*!sc*/
.dwCKSU{display:flex;position:relative;flex-direction:row;justify-content:center;align-items:center;cursor:pointer;border-radius:var(--border-radius);background:var(--segmented-buttons-bg-color-main);z-index:1;padding:calc(var(--spacing-unit) / 2);}/*!sc*/
data-styled.g639[id="sc-kBPbfe"]{content:"dwCKSU,"}/*!sc*/
.fJqAYL{width:20px;height:20px;border-radius:var(--border-radius);display:flex;align-items:center;justify-content:center;}/*!sc*/
.bfVPFS{width:20px;height:20px;border-radius:var(--border-radius);display:flex;align-items:center;justify-content:center;background-color:var(--segmented-buttons-bg-color-inner);}/*!sc*/
data-styled.g640[id="sc-dNLPWG"]{content:"fJqAYL,bfVPFS,"}/*!sc*/
.hgBgQv{display:none;justify-content:flex-end;bottom:0;flex-direction:row;gap:var(--spacing-xs);align-items:center;margin-left:var(--sidebar-controls-margin-left);z-index:2;background:var(--sidebar-bg-color);}/*!sc*/
@media screen and (min-width: 672px){.hgBgQv{display:flex;}}/*!sc*/
@media print{.hgBgQv{display:none;}}/*!sc*/
data-styled.g641[id="sc-llaQDn"]{content:"hgBgQv,"}/*!sc*/
.kAHLjQ{display:flex;bottom:0;flex-direction:row;gap:var(--spacing-xs);align-items:center;}/*!sc*/
data-styled.g642[id="sc-gpABuY"]{content:"kAHLjQ,"}/*!sc*/
.iDhGiF{height:16px;width:16px;}/*!sc*/
.iDhGiF path{fill:currentColor;}/*!sc*/
data-styled.g643[id="sc-cxtSCJ"]{content:"iDhGiF,"}/*!sc*/
.cyqpmN{display:flex;flex-direction:row;align-items:center;justify-content:space-between;gap:var(--spacing-unit);position:sticky;top:calc(100vh);padding:var(--spacing-sm) var(--spacing-md);}/*!sc*/
data-styled.g645[id="sc-dByxfH"]{content:"cyqpmN,"}/*!sc*/
.DUVqz{display:inline-flex;flex-direction:row;align-items:center;gap:var(--spacing-unit);}/*!sc*/
data-styled.g646[id="sc-jVtRwo"]{content:"DUVqz,"}/*!sc*/
.ckjMRV{text-align:center;width:auto;height:auto;display:inline-flex;justify-content:center;bottom:0;background:var(--color-blue-1);padding:2px 8px;margin-bottom:0;border-radius:21px;}/*!sc*/
.ckjMRV:hover{background:var(--color-blue-2);}/*!sc*/
.ckjMRV a,.ckjMRV a:visited,.ckjMRV a:hover{color:var(--sidebar-text-color)!important;text-decoration:none;display:flex;align-items:center;justify-content:center;}/*!sc*/
.ckjMRV img{height:12px;user-select:none;}/*!sc*/
.ckjMRV p{font-size:calc(var(--font-size-xl) / 2);line-height:var(--line-height-sm);margin:0;margin-right:4px;}/*!sc*/
@media screen and (min-width: 672px){.ckjMRV{position:sticky;z-index:auto;}}/*!sc*/
data-styled.g647[id="sc-gdniDZ"]{content:"ckjMRV,"}/*!sc*/
.KsnLK{height:24px;width:24px;}/*!sc*/
.KsnLK path{fill:currentColor;}/*!sc*/
data-styled.g648[id="sc-fjRroq"]{content:"KsnLK,"}/*!sc*/
.hSUqRG{border:none;outline:none;user-select:none;background-color:var(--sidebar-bg-color, #fff);color:var(--color-primary-base, var(--color-blueberry-6));display:none;align-items:center;justify-content:center;cursor:pointer;position:fixed;right:20px;bottom:44px;z-index:100;border-radius:50%;box-shadow:0 0 20px rgba(0, 0, 0, 0.3);width:60px;height:60px;padding:0;}/*!sc*/
.hSUqRG svg path{fill:var(--color-primary-base, var(--color-blueberry-6));}/*!sc*/
@media screen and (max-width: 672px){.hSUqRG{display:flex;}}/*!sc*/
@media print{.hSUqRG{display:none;}}/*!sc*/
data-styled.g649[id="sc-brKIwb"]{content:"hSUqRG,"}/*!sc*/
.fpSCXb{flex-shrink:0;overflow-y:auto;overflow-x:hidden;top:var(--navbar-stack-height, 0px);height:calc(100vh - var(--navbar-stack-height, 0px));height:calc(100dvh - var(--navbar-stack-height, 0px));flex-direction:column;background:var(--sidebar-bg-color, #fff);border-right:1px solid var(--sidebar-border-color, #e0e0e0);position:fixed;z-index:20;width:100%;display:none;}/*!sc*/
.fpSCXb [data-component-name='SidebarLogo/SidebarLogo']{padding:var(--spacing-md, 16px) var(--spacing-sm, 12px);}/*!sc*/
.fpSCXb [data-component-name='SidebarLogo/SidebarLogo'] img{max-width:min(140px, var(--sidebar-logo-max-width, 285px));}/*!sc*/
@media screen and (min-width: 672px){.fpSCXb{position:sticky;z-index:auto;display:flex;width:var(--sidebar-width, 260px);}.fpSCXb [data-component-name='SidebarLogo/SidebarLogo'] img{max-width:var(--sidebar-logo-max-width, 285px);}}/*!sc*/
@media print{.fpSCXb{display:none;}}/*!sc*/
data-styled.g650[id="sc-NPrTd"]{content:"fpSCXb,"}/*!sc*/
.kDulYG{display:flex;position:relative;min-height:100vh;text-align:left;background-color:var(--bg-color);font-family:var(--font-family-base);font-size:var(--font-size-base);line-height:var(--line-height-base);font-weight:var(--font-weight-regular);color:var(--text-color-primary);-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;}/*!sc*/
.kDulYG [data-section-id]{scroll-margin-top:calc(var(--navbar-stack-height, 0px) + var(--banner-height, 0px));}/*!sc*/
.kDulYG [id]:not([data-section-id]):has([data-deep-link-anchor]){scroll-margin-top:calc(
var(--navbar-stack-height, 0px) + var(--banner-height, 0px) +
var(--deep-link-scroll-gap, var(--spacing-xl))
);}/*!sc*/
.fFQMWf *{box-sizing:border-box;}/*!sc*/
data-styled.g657[id="sc-kIbBWU"]{content:"fFQMWf,"}/*!sc*/
.dKSNwp{flex:1;min-width:0;}/*!sc*/
data-styled.g658[id="sc-hHttOM"]{content:"dKSNwp,"}/*!sc*/
.kDulYG *{box-sizing:border-box;}/*!sc*/
data-styled.g651[id="sc-jlWaFk"]{content:"kDulYG,"}/*!sc*/
.cirFq{flex:1;min-width:0;}/*!sc*/
data-styled.g652[id="sc-LDhwn"]{content:"cirFq,"}/*!sc*/
</style>
</head>
<body>
<div id="redoc"><div hidden=""><script>try{var m=localStorage.getItem("redoc.colorMode");if(m!=="light"&&m!=="dark")m=matchMedia("(prefers-color-scheme: dark)").matches?"dark":"light";document.documentElement.classList.add(m)}catch(e){}</script></div><div style="--navbar-height:0px;--navbar-stack-height:0px" class="sc-kIbBWU fFQMWf"><div class="sc-cjUTkN dGsweu menu-content"><div class="sc-jqwVmx jjzLa-d"><div data-testid="search-trigger" class="sc-cToEys dffrbr"><div class="sc-cRcubC ddjiE"><svg viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg" data-component-name="icons/SearchIcon/SearchIcon" class="sc-cHazmW gTzQi"><path d="M14.5 13.7931L10.724 10.017C11.6313 8.9277 12.0838 7.5305 11.9872 6.11608C11.8907 4.70165 11.2525 3.37891 10.2055 2.423C9.15857 1.4671 7.78336 0.951637 6.36602 0.983845C4.94867 1.01605 3.5983 1.59345 2.59582 2.59593C1.59335 3.5984 1.01595 4.94877 0.983738 6.36612C0.95153 7.78347 1.46699 9.15867 2.4229 10.2057C3.3788 11.2526 4.70155 11.8908 6.11597 11.9873C7.53039 12.0839 8.92759 11.6314 10.0169 10.7241L13.793 14.5001L14.5 13.7931ZM2.00001 6.50012C2.00001 5.6101 2.26393 4.74007 2.7584 4.00005C3.25286 3.26003 3.95567 2.68325 4.77794 2.34266C5.6002 2.00207 6.505 1.91295 7.37792 2.08658C8.25083 2.26022 9.05266 2.6888 9.68199 3.31814C10.3113 3.94747 10.7399 4.7493 10.9135 5.62221C11.0872 6.49513 10.9981 7.39993 10.6575 8.22219C10.3169 9.04446 9.7401 9.74726 9.00008 10.2417C8.26006 10.7362 7.39003 11.0001 6.50001 11.0001C5.30694 10.9988 4.16312 10.5243 3.31949 9.68064C2.47587 8.83701 2.00133 7.69319 2.00001 6.50012Z"></path></svg><span>Loading...</span></div></div></div><div class="sc-dNLPWG dtcefb"><ul data-component-name="Menu/Menu" class="sc-kBPbfe ctPApS"><div data-component-name="Menu/MenuItem" class="sc-gwVKcS fGSWQx menu-item-type-default" role="listitem"><a class="sc-iJONRL iBHKJo" href="/" data-discover="true"><li role="none" data-testid="menu-item-label" data-active="true" class="sc-epufQc fYmDZR"><div class="sc-boLXHU jshptd"><span class="sc-bcyEQV hrIrOF"><span data-text="WeKan REST API" class="sc-eStdmu iyAVQU">WeKan REST API</span></span></div></li></a></div><div data-component-name="Menu/MenuItem" class="sc-gwVKcS fGSWQx menu-item-type-default" role="listitem"><a class="sc-iJONRL iBHKJo" href="/section/production-security-concerns" data-discover="true"><li role="none" data-testid="menu-item-label" data-active="false" class="sc-epufQc itBMtf"><div class="sc-boLXHU jshptd"><span class="sc-bcyEQV hrIrOF"><span data-text="Production Security Concerns" class="sc-eStdmu liVEkC">Production Security Concerns</span></span></div></li></a></div><div data-component-name="Menu/MenuItem" class="sc-gwVKcS fGSWQx menu-item-type-group" role="listitem"><a class="sc-iJONRL iBHKJo" href="/login" data-discover="true"><li role="none" data-testid="menu-item-label" data-active="false" class="sc-epufQc hKPPiy"><div class="sc-jokKPH jtaKKl"><svg viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg" color="--tree-content-color-default" data-component-name="icons/ChevronRightIcon/ChevronRightIcon" class="sc-iUlCMS kEEIsB"><path d="M11 8L6.00002 13L5.30002 12.3L9.60002 8L5.30002 3.7L6.00002 3L11 8Z"></path></svg></div><div class="sc-boLXHU jshptd"><span class="sc-bcyEQV hrIrOF"><span data-text="Login" class="sc-eStdmu liVEkC">Login</span></span></div></li></a><div style="display:none;height:0px;overflow:hidden" class="sc-hgJgCR dlNiXx"></div></div><div data-component-name="Menu/MenuItem" class="sc-gwVKcS fGSWQx menu-item-type-group" role="listitem"><a class="sc-iJONRL iBHKJo" href="/attachmentstoragesettings" data-discover="true"><li role="none" data-testid="menu-item-label" data-active="false" class="sc-epufQc hKPPiy"><div class="sc-jokKPH jtaKKl"><svg viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg" color="--tree-content-color-default" data-component-name="icons/ChevronRightIcon/ChevronRightIcon" class="sc-iUlCMS kEEIsB"><path d="M11 8L6.00002 13L5.30002 12.3L9.60002 8L5.30002 3.7L6.00002 3L11 8Z"></path></svg></div><div class="sc-boLXHU jshptd"><span class="sc-bcyEQV hrIrOF"><span data-text="AttachmentStorageSettings" class="sc-eStdmu liVEkC">AttachmentStorageSettings</span></span></div></li></a><div style="display:none;height:0px;overflow:hidden" class="sc-hgJgCR dlNiXx"></div></div><div data-component-name="Menu/MenuItem" class="sc-gwVKcS fGSWQx menu-item-type-group" role="listitem"><a class="sc-iJONRL iBHKJo" href="/settings" data-discover="true"><li role="none" data-testid="menu-item-label" data-active="false" class="sc-epufQc hKPPiy"><div class="sc-jokKPH jtaKKl"><svg viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg" color="--tree-content-color-default" data-component-name="icons/ChevronRightIcon/ChevronRightIcon" class="sc-iUlCMS kEEIsB"><path d="M11 8L6.00002 13L5.30002 12.3L9.60002 8L5.30002 3.7L6.00002 3L11 8Z"></path></svg></div><div class="sc-boLXHU jshptd"><span class="sc-bcyEQV hrIrOF"><span data-text="Settings" classLine truncated
<div id="redoc"><div class="sc-jlWaFk kDulYG redoc light" style="--navbar-height:0px;--navbar-stack-height:0px"><div hidden=""><script>try{var r=document.querySelectorAll(".redoc");r=r[r.length-1];var h=document.documentElement,p=h.classList,a=[h.getAttribute("data-bs-theme"),h.getAttribute("data-theme")];var m=p.contains("dark")||a.indexOf("dark")>-1?"dark":p.contains("light")||a.indexOf("light")>-1?"light":localStorage.getItem("redoc.colorMode");if(m!=="light"&&m!=="dark")m=matchMedia("(prefers-color-scheme: dark)").matches?"dark":"light";r.classList.remove("light","dark");r.classList.add(m)}catch(e){}</script></div><div class="sc-NPrTd fpSCXb menu-content"><div class="sc-bJapvR eXtYur"><div data-testid="search-trigger" class="sc-eavxOQ bStpwo"><div class="sc-dGYDtq hRGWCd"><svg viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg" data-component-name="icons/SearchIcon/SearchIcon" class="sc-gJaedx dpfGSp"><path d="M14.5 13.7931L10.724 10.017C11.6313 8.9277 12.0838 7.5305 11.9872 6.11608C11.8907 4.70165 11.2525 3.37891 10.2055 2.423C9.15857 1.4671 7.78336 0.951637 6.36602 0.983845C4.94867 1.01605 3.5983 1.59345 2.59582 2.59593C1.59335 3.5984 1.01595 4.94877 0.983738 6.36612C0.95153 7.78347 1.46699 9.15867 2.4229 10.2057C3.3788 11.2526 4.70155 11.8908 6.11597 11.9873C7.53039 12.0839 8.92759 11.6314 10.0169 10.7241L13.793 14.5001L14.5 13.7931ZM2.00001 6.50012C2.00001 5.6101 2.26393 4.74007 2.7584 4.00005C3.25286 3.26003 3.95567 2.68325 4.77794 2.34266C5.6002 2.00207 6.505 1.91295 7.37792 2.08658C8.25083 2.26022 9.05266 2.6888 9.68199 3.31814C10.3113 3.94747 10.7399 4.7493 10.9135 5.62221C11.0872 6.49513 10.9981 7.39993 10.6575 8.22219C10.3169 9.04446 9.7401 9.74726 9.00008 10.2417C8.26006 10.7362 7.39003 11.0001 6.50001 11.0001C5.30694 10.9988 4.16312 10.5243 3.31949 9.68064C2.47587 8.83701 2.00133 7.69319 2.00001 6.50012Z"></path></svg><span>Loading...</span></div></div></div><div class="sc-kLsiYl kAUzfx"><ul data-component-name="Menu/Menu" class="sc-jfJzNu hoWuXt"><div data-component-name="Menu/MenuItem" class="sc-jEekwy kJsUv menu-item-type-default" role="listitem"><a class="sc-gzhUzI kKmXTr" href="/" data-discover="true"><li role="none" data-testid="menu-item-label" data-active="true" class="sc-cToEys eWEtkC"><div class="sc-fIruOr dBwuTA"><span class="sc-gwVKcS efjekG"><span data-text="WeKan REST API" class="sc-hgJgCR dWgUkd">WeKan REST API</span></span></div></li></a></div><div data-component-name="Menu/MenuItem" class="sc-jEekwy kJsUv menu-item-type-default" role="listitem"><a class="sc-gzhUzI kKmXTr" href="/section/production-security-concerns" data-discover="true"><li role="none" data-testid="menu-item-label" data-active="false" class="sc-cToEys iVTRMP"><div class="sc-fIruOr dBwuTA"><span class="sc-gwVKcS efjekG"><span data-text="Production Security Concerns" class="sc-hgJgCR dlVNdD">Production Security Concerns</span></span></div></li></a></div><div data-component-name="Menu/MenuItem" class="sc-jEekwy kJsUv menu-item-type-group" role="listitem"><a class="sc-gzhUzI kKmXTr" href="/login" data-discover="true"><li role="none" data-testid="menu-item-label" data-active="false" class="sc-cToEys jYycQW"><div class="sc-dGPMcj PCvEP"><svg viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg" color="--tree-content-color-default" data-component-name="icons/ChevronRightIcon/ChevronRightIcon" class="sc-kLEbxe eRqACJ"><path d="M11 8L6.00002 13L5.30002 12.3L9.60002 8L5.30002 3.7L6.00002 3L11 8Z"></path></svg></div><div class="sc-fIruOr dBwuTA"><span class="sc-gwVKcS efjekG"><span data-text="Login" class="sc-hgJgCR dlVNdD">Login</span></span></div></li></a><div style="display:none;height:0px;overflow:hidden" class="sc-fKDFlh joxUYR"></div></div><div data-component-name="Menu/MenuItem" class="sc-jEekwy kJsUv menu-item-type-group" role="listitem"><a class="sc-gzhUzI kKmXTr" href="/attachmentstoragesettings" data-discover="true"><li role="none" data-testid="menu-item-label" data-active="false" class="sc-cToEys jYycQW"><div class="sc-dGPMcj PCvEP"><svg viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg" color="--tree-content-color-default" data-component-name="icons/ChevronRightIcon/ChevronRightIcon" class="sc-kLEbxe eRqACJ"><path d="M11 8L6.00002 13L5.30002 12.3L9.60002 8L5.30002 3.7L6.00002 3L11 8Z"></path></svg></div><div class="sc-fIruOr dBwuTA"><span class="sc-gwVKcS efjekG"><span data-text="AttachmentStorageSettings" class="sc-hgJgCR dlVNdD">AttachmentStorageSettings</span></span></div></li></a><div style="display:none;height:0px;overflow:hidden" class="sc-fKDFlh joxUYR"></div></div><div data-component-name="Menu/MenuItem" class="sc-jEekwy kJsUv menu-item-type-group" role="listitem"><a class="sc-gzhUzI kKmXTr" href="/settings" data-discover="true"><li role="none" data-testid="menu-item-label" data-active="false" class="sc-cToEys jYycQW"><div class="sc-dGPMcj PCvEP"><svg viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg" color="--tree-content-color-defLine truncated
<script type="module">
import { hydrate } from "https://cdn.redocly.com/redoc/v3.0.1/bundles/redoc.standalone.js";
import { hydrate } from "https://cdn.redocly.com/redoc/v3.1.0/bundles/redoc.standalone.js";
const __redoc_definition = {"openapi":"3.2.0","info":{"title":"WeKan REST API","version":"v13.07","description":"The REST API allows you to control and extend WeKan with ease.\n\nIf you are an end-user and not a dev or a tester, [create an issue](https://github.com/wekan/wekan/issues/new) to request new APIs.\n\n> All API calls in the documentation are made using `curl`. However, you are free to use Java / Python / PHP / Golang / Ruby / Swift / Objective-C / Rust / Scala / C# or any other programming languages.\n\n# Production Security Concerns\nWhen calling a production WeKan server, ensure it is running via HTTPS and has a valid SSL Certificate. The login method requires you to post your username and password in plaintext, which is why we highly suggest only calling the REST login api over HTTPS. Also, few things to note:\n\n* Only call via HTTPS\n* Implement a timed authorization token expiration strategy\n* Ensure the calling user only has permissions for what they are calling and no more\n"},"paths":{"/users/login":{"post":{"operationId":"login","summary":"Login with REST API","tags":["Login"],"requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["username","password"],"properties":{"username":{"description":"Your username\n","type":"string"},"password":{"description":"Your password\n","type":"string","format":"password"}}}},"application/x-www-form-urlencoded":{"schema":{"type":"object","required":["username","password"],"properties":{"username":{"description":"Your username\n","type":"string"},"password":{"description":"Your password\n","type":"string","format":"password"}}}}},"description":"Login credentials","required":true},"responses":{"200":{"description":"Successful authentication","content":{"*/*":{"schema":{"type":"object","required":["id","token","tokenExpires"],"properties":{"id":{"type":"string","description":"User ID"},"token":{"type":"string","description":"Authentication token\n"},"tokenExpires":{"type":"string","format":"date-time","description":"Token expiration date\n"}}}}}},"400":{"description":"Error in authentication\n","content":{"*/*":{"schema":{"type":"object","properties":{"error":{"type":"string"},"reason":{"type":"string"}}}}}},"default":{"description":"Error in authentication\n"}}}},"/users/register":{"post":{"operationId":"register","summary":"Register with REST API","description":"Notes:\n - You will need to provide the token for any of the authenticated methods.\n","tags":["Login"],"requestBody":{"content":{"application/x-www-form-urlencoded":{"schema":{"type":"object","properties":{"username":{"description":"Your username\n","type":"string"},"password":{"description":"Your password\n","type":"string","format":"password"},"email":{"description":"Your email\n","type":"string"}},"required":["username","password","email"]}}},"required":true},"responses":{"200":{"description":"Successful registration","content":{"*/*":{"schema":{"items":{"properties":{"id":{"type":"string"},"token":{"type":"string"},"tokenExpires":{"type":"string"}}}}}}},"400":{"description":"Error in registration\n","content":{"*/*":{"schema":{"items":{"properties":{"error":{"type":"number"},"reason":{"type":"string"}}}}}}},"default":{"description":"Error in registration\n"}}}},"/api/admin/attachment-settings":{"get":{"operationId":"get_attachment_settings","summary":"Get the attachment storage / upload-block settings (GlobalAdmin)","description":"Only the global admin can call this. Returns the Admin Panel >\n Attachments settings document, including `limitSettings.attachmentsUploadBlocked`\n and `limitSettings.avatarsUploadBlocked` plus the size limits. Cloud-storage\n secret keys (S3 secretAccessKey, Azure accountKey/connectionString, GCS\n credentials) are masked and never returned; a boolean `\u003cfield>Set` marker\n shows whether a value exists.\n","tags":["AttachmentStorageSettings","Settings"],"security":[{"UserSecurity":[]}],"responses":{"200":{"description":"200 response","content":{"application/json":{"schema":{"type":"Object"}}}}}},"put":{"operationId":"update_attachment_settings","summary":"Update the attachment storage / upload-block settings (GlobalAdmin)","description":"Only the global admin can call this. The request body is a\n partial settings object (for example\n `{\"limitSettings\": {\"avatarsUploadBlocked\": true}}`). It is applied through\n the same update path as the `updateAttachmentStorageSettings` Meteor method,\n so validation and `$set` semantics are identical: cloud-storage secrets left\n blank are preserved. The masked, updated settings document is returned.\n","tags":["AttachmentStorageSettings","Settings"],"security":[{"UserSecurity":[]}],"responses":{"200":{"description":"200 response","content":{"application/json":{"schema":{"type":"Object"}}}}}}},"/api/admin/domains":{"get":{"operationId":"get_admin_domains","summary":"List email domains with their user counts (GlobalAdmin)","description":"Only the global admin can call this. CountsLine truncated
const __redoc_definition = {"openapi":"3.2.0","info":{"title":"WeKan REST API","version":"v13.11","description":"The REST API allows you to control and extend WeKan with ease.\n\nIf you are an end-user and not a dev or a tester, [create an issue](https://github.com/wekan/wekan/issues/new) to request new APIs.\n\n> All API calls in the documentation are made using `curl`. However, you are free to use Java / Python / PHP / Golang / Ruby / Swift / Objective-C / Rust / Scala / C# or any other programming languages.\n\n# Production Security Concerns\nWhen calling a production WeKan server, ensure it is running via HTTPS and has a valid SSL Certificate. The login method requires you to post your username and password in plaintext, which is why we highly suggest only calling the REST login api over HTTPS. Also, few things to note:\n\n* Only call via HTTPS\n* Implement a timed authorization token expiration strategy\n* Ensure the calling user only has permissions for what they are calling and no more\n"},"paths":{"/users/login":{"post":{"operationId":"login","summary":"Login with REST API","tags":["Login"],"requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["username","password"],"properties":{"username":{"description":"Your username\n","type":"string"},"password":{"description":"Your password\n","type":"string","format":"password"}}}},"application/x-www-form-urlencoded":{"schema":{"type":"object","required":["username","password"],"properties":{"username":{"description":"Your username\n","type":"string"},"password":{"description":"Your password\n","type":"string","format":"password"}}}}},"description":"Login credentials","required":true},"responses":{"200":{"description":"Successful authentication","content":{"*/*":{"schema":{"type":"object","required":["id","token","tokenExpires"],"properties":{"id":{"type":"string","description":"User ID"},"token":{"type":"string","description":"Authentication token\n"},"tokenExpires":{"type":"string","format":"date-time","description":"Token expiration date\n"}}}}}},"400":{"description":"Error in authentication\n","content":{"*/*":{"schema":{"type":"object","properties":{"error":{"type":"string"},"reason":{"type":"string"}}}}}},"default":{"description":"Error in authentication\n"}}}},"/users/register":{"post":{"operationId":"register","summary":"Register with REST API","description":"Notes:\n - You will need to provide the token for any of the authenticated methods.\n","tags":["Login"],"requestBody":{"content":{"application/x-www-form-urlencoded":{"schema":{"type":"object","properties":{"username":{"description":"Your username\n","type":"string"},"password":{"description":"Your password\n","type":"string","format":"password"},"email":{"description":"Your email\n","type":"string"}},"required":["username","password","email"]}}},"required":true},"responses":{"200":{"description":"Successful registration","content":{"*/*":{"schema":{"items":{"properties":{"id":{"type":"string"},"token":{"type":"string"},"tokenExpires":{"type":"string"}}}}}}},"400":{"description":"Error in registration\n","content":{"*/*":{"schema":{"items":{"properties":{"error":{"type":"number"},"reason":{"type":"string"}}}}}}},"default":{"description":"Error in registration\n"}}}},"/api/admin/attachment-settings":{"get":{"operationId":"get_attachment_settings","summary":"Get the attachment storage / upload-block settings (GlobalAdmin)","description":"Only the global admin can call this. Returns the Admin Panel >\n Attachments settings document, including `limitSettings.attachmentsUploadBlocked`\n and `limitSettings.avatarsUploadBlocked` plus the size limits. Cloud-storage\n secret keys (S3 secretAccessKey, Azure accountKey/connectionString, GCS\n credentials) are masked and never returned; a boolean `\u003cfield>Set` marker\n shows whether a value exists.\n","tags":["AttachmentStorageSettings","Settings"],"security":[{"UserSecurity":[]}],"responses":{"200":{"description":"200 response","content":{"application/json":{"schema":{"type":"Object"}}}}}},"put":{"operationId":"update_attachment_settings","summary":"Update the attachment storage / upload-block settings (GlobalAdmin)","description":"Only the global admin can call this. The request body is a\n partial settings object (for example\n `{\"limitSettings\": {\"avatarsUploadBlocked\": true}}`). It is applied through\n the same update path as the `updateAttachmentStorageSettings` Meteor method,\n so validation and `$set` semantics are identical: cloud-storage secrets left\n blank are preserved. The masked, updated settings document is returned.\n","tags":["AttachmentStorageSettings","Settings"],"security":[{"UserSecurity":[]}],"responses":{"200":{"description":"200 response","content":{"application/json":{"schema":{"type":"Object"}}}}}}},"/api/admin/domains":{"get":{"operationId":"get_admin_domains","summary":"List email domains with their user counts (GlobalAdmin)","description":"Only the global admin can call this. CountsLine truncated
const __redoc_options = {"skipBundle":true,"disableTelemetry":false};
hydrate(__redoc_definition, __redoc_options, document.getElementById('redoc'));
+6 -1
View File
@@ -1,7 +1,7 @@
swagger: '2.0'
info:
title: WeKan REST API
version: v13.07
version: v13.11
description: |
The REST API allows you to control and extend WeKan with ease.
@@ -2929,6 +2929,11 @@ paths:
- multipart/form-data
- application/json
parameters:
- name: parentId
in: formData
description: the parentId value
type: string
required: true
- name: linkedId
in: formData
description: the linkedId value
+18 -5
View File
@@ -116,8 +116,16 @@ def redact_secrets(alert):
return alert
def write_json(path, value):
with open(path, "w", encoding="utf-8") as fh:
def write_json(path, value, private=False):
"""value as JSON at path; private=True creates it readable by its owner only,
for a file that holds the secrets --include-secret-values kept."""
if private:
fd = os.open(path, os.O_WRONLY | os.O_CREAT | os.O_TRUNC, 0o600)
os.chmod(path, 0o600) # an existing file keeps its old mode otherwise
fh = os.fdopen(fd, "w", encoding="utf-8")
else:
fh = open(path, "w", encoding="utf-8")
with fh:
json.dump(value, fh, indent=2, ensure_ascii=False)
fh.write("\n")
@@ -143,7 +151,11 @@ def describe(kind, alert):
sev = rule.get("security_severity_level") or rule.get("severity") or ""
return sev, f"{rule.get('id', '')}: {rule.get('description', '')}", where
if kind == "secret-scanning":
return "", alert.get("secret_type_display_name") or alert.get("secret_type", ""), ""
# Nothing from the alert itself: SUMMARY.md is never redacted and is the
# file most likely to be pasted somewhere, so it says only what kind of
# finding this is. The secret's type is in alert-<n>.json, beside it
# (CodeQL py/clear-text-storage-sensitive-data, code scanning alert #604).
return "", "Secret scanning alert - details in secret-scanning/", ""
return alert.get("severity", ""), alert.get("summary", ""), ""
@@ -169,14 +181,15 @@ def download_repo(org, repo, run_dir, state, include_secrets, log):
continue
if kind == "secret-scanning" and not include_secrets:
alerts = [redact_secrets(a) for a in alerts]
write_json(os.path.join(kind_dir, "alerts.json"), alerts)
private = kind == "secret-scanning" and include_secrets
write_json(os.path.join(kind_dir, "alerts.json"), alerts, private)
n_open = 0
for alert in alerts:
ident = str(alert.get(key, "")).strip()
if not re.match(r"^[A-Za-z0-9-]+$", ident):
continue
name = f"advisory-{ident}.json" if kind == "security-advisories" else f"alert-{ident}.json"
write_json(os.path.join(kind_dir, name), alert)
write_json(os.path.join(kind_dir, name), alert, private)
if is_open(kind, alert):
n_open += 1
sev, what, where = describe(kind, alert)
+2 -2
View File
@@ -22,10 +22,10 @@ const pkgdef :Spk.PackageDefinition = (
appTitle = (defaultText = "Wekan"),
# The name of the app as it is displayed to the user.
appVersion = 1307,
appVersion = 1311,
# Increment this for every release.
appMarketingVersion = (defaultText = "13.07~2026-11-12"),
appMarketingVersion = (defaultText = "13.11~2026-11-13"),
# Human-readable presentation of the app version.
minUpgradableAppVersion = 0,
+20
View File
@@ -0,0 +1,20 @@
// #6898: forget the file-integrity baselines of files WeKan itself deletes or
// moves, so the daily scan does not report them as missing forever. See
// models/lib/fileIntegrityBaselineIds.cjs.
const { baselineIdsFor, versionPathsOf } = require('/models/lib/fileIntegrityBaselineIds.cjs');
export async function forgetIntegrityBaselines(paths) {
const ids = baselineIdsFor(paths);
if (ids.length === 0) return 0;
const FileIntegrity = require('/models/fileIntegrity').default;
return FileIntegrity.removeAsync({ _id: { $in: ids } });
}
// Fire-and-forget: a delete must never fail because its bookkeeping did.
export function forgetIntegrityBaselinesSoon(paths) {
forgetIntegrityBaselines(paths).catch(() => { /* bookkeeping only */ });
}
export function forgetIntegrityBaselinesOfFiles(fileObjs) {
forgetIntegrityBaselinesSoon(versionPathsOf(fileObjs));
}
+6 -1
View File
@@ -1530,7 +1530,7 @@ WebApp.handlers.post('/api/boards/:boardId/lists/:listId/cards', async function(
await Authentication.checkAdminOrCondition(req.userId, addPermission);
await assertListNotRefused(req.userId, board, req.params.listId); // #4527
const paramListId = req.params.listId;
const paramParentId = req.params.parentId;
const paramParentId = req.body.parentId;
// Issue #5897: create a Linked Card. When linkedId is provided, the new card
// references an existing card (type cardType-linkedCard) instead of holding
@@ -1562,6 +1562,11 @@ WebApp.handlers.post('/api/boards/:boardId/lists/:listId/cards', async function(
return;
}
if (paramParentId !== undefined && paramParentId !== null && typeof paramParentId !== 'string') {
sendJsonResult(res, { code: 400, data: { error: 'Invalid parentId' } });
return;
}
// GHSA-jvv9-498p-hxrg: the same read check the linked-card branch above makes
// for `linkedId`, for the parent card. A parent on another board is only
// allowed when the caller may see that board; otherwise creating one card
+12 -22
View File
@@ -183,29 +183,19 @@ WebApp.handlers.put('/api/boards/:boardId/custom-fields/:customFieldId', async f
boardIds: { $in: [paramBoardId] },
};
if (Object.prototype.hasOwnProperty.call(req.body, 'name')) {
await CustomFields.direct.updateAsync(boardScopedField, { $set: { name: req.body.name } });
// Validate and save the requested fields together so a rejected value cannot
// leave earlier fields persisted from the same request.
const set = {};
for (const field of [
'name', 'type', 'settings', 'showOnCard', 'automaticallyOnCard',
'alwaysOnCard', 'showLabelOnMiniCard', 'showSumAtTopOfList',
]) {
if (Object.prototype.hasOwnProperty.call(req.body, field)) {
set[field] = req.body[field];
}
}
if (Object.prototype.hasOwnProperty.call(req.body, 'type')) {
await CustomFields.direct.updateAsync(boardScopedField, { $set: { type: req.body.type } });
}
if (Object.prototype.hasOwnProperty.call(req.body, 'settings')) {
await CustomFields.direct.updateAsync(boardScopedField, { $set: { settings: req.body.settings } });
}
if (Object.prototype.hasOwnProperty.call(req.body, 'showOnCard')) {
await CustomFields.direct.updateAsync(boardScopedField, { $set: { showOnCard: req.body.showOnCard } });
}
if (Object.prototype.hasOwnProperty.call(req.body, 'automaticallyOnCard')) {
await CustomFields.direct.updateAsync(boardScopedField, { $set: { automaticallyOnCard: req.body.automaticallyOnCard } });
}
if (Object.prototype.hasOwnProperty.call(req.body, 'alwaysOnCard')) {
await CustomFields.direct.updateAsync(boardScopedField, { $set: { alwaysOnCard: req.body.alwaysOnCard } });
}
if (Object.prototype.hasOwnProperty.call(req.body, 'showLabelOnMiniCard')) {
await CustomFields.direct.updateAsync(boardScopedField, { $set: { showLabelOnMiniCard: req.body.showLabelOnMiniCard } });
}
if (Object.prototype.hasOwnProperty.call(req.body, 'showSumAtTopOfList')) {
await CustomFields.direct.updateAsync(boardScopedField, { $set: { showSumAtTopOfList: req.body.showSumAtTopOfList } });
if (Object.keys(set).length > 0) {
await CustomFields.direct.updateAsync(boardScopedField, { $set: set });
}
sendJsonResult(res, {
+18 -12
View File
@@ -151,18 +151,8 @@ WebApp.handlers.put('/api/boards/:boardId/integrations/:intId', async function(r
}
}
if (req.body.hasOwnProperty('enabled')) {
await Integrations.direct.updateAsync(
{ _id: paramIntId, boardId: paramBoardId },
{ $set: { enabled: req.body.enabled } },
);
}
if (req.body.hasOwnProperty('title')) {
await Integrations.direct.updateAsync(
{ _id: paramIntId, boardId: paramBoardId },
{ $set: { title: req.body.title } },
);
}
// Validate the URL before writing any accompanying fields, just as delivery
// settings are validated above. A rejected URL must leave the integration intact.
if (req.body.hasOwnProperty('url')) {
// SSRF protection: this update path uses Integrations.direct, which
// bypasses the schema's url validator, so validate explicitly here.
@@ -176,6 +166,21 @@ WebApp.handlers.put('/api/boards/:boardId/integrations/:intId', async function(r
});
return;
}
}
if (req.body.hasOwnProperty('enabled')) {
await Integrations.direct.updateAsync(
{ _id: paramIntId, boardId: paramBoardId },
{ $set: { enabled: req.body.enabled } },
);
}
if (req.body.hasOwnProperty('title')) {
await Integrations.direct.updateAsync(
{ _id: paramIntId, boardId: paramBoardId },
{ $set: { title: req.body.title } },
);
}
if (req.body.hasOwnProperty('url')) {
await Integrations.direct.updateAsync(
{ _id: paramIntId, boardId: paramBoardId },
{ $set: { url: req.body.url } },
@@ -272,6 +277,7 @@ WebApp.handlers.delete('/api/boards/:boardId/integrations/:intId', async functio
const paramBoardId = req.params.boardId;
const paramIntId = req.params.intId;
await Authentication.checkBoardAdmin(req.userId, paramBoardId);
if (!(await integrationOr404(res, paramBoardId, paramIntId))) return;
await Integrations.direct.removeAsync({ _id: paramIntId, boardId: paramBoardId });
sendJsonResult(res, {
+4 -2
View File
@@ -2725,7 +2725,9 @@ WebApp.handlers.put('/api/users/:userId', async function(req, res) {
}
if (action === 'takeOwnership') {
const boards = await ReactiveCache.getBoards(
{ 'members.userId': id, 'members.isAdmin': true },
// Both conditions must describe the same member; another member's
// admin role does not make this user's ordinary membership ownership.
{ members: { $elemMatch: { userId: id, isAdmin: true } } },
{ sort: { sort: 1 } },
);
data = [];
@@ -2733,7 +2735,7 @@ WebApp.handlers.put('/api/users/:userId', async function(req, res) {
if (board.hasMember(req.userId)) {
await board.removeMember(req.userId);
}
board.changeOwnership(id, req.userId);
await board.changeOwnership(id, req.userId);
data.push({ _id: board._id, title: board.title });
}
} else {
+4 -4
View File
@@ -19,7 +19,7 @@
# base: core24 (not core26) · no build-base: devel · grade: stable
# ─────────────────────────────────────────────────────────────────────────────
name: wekan
version: '13.07'
version: '13.11'
base: core24
confinement: strict
grade: stable
@@ -703,18 +703,18 @@ parts:
armhf) WEKAN_ARCH="armhf" ;;
*) echo "Unsupported architecture: ${CRAFT_ARCH_BUILD_FOR}"; exit 1 ;;
esac
WEKAN_ZIP="wekan-13.07-${WEKAN_ARCH}.zip"
WEKAN_ZIP="wekan-13.11-${WEKAN_ARCH}.zip"
echo "Cleaning environment first"
rm -rf .build
mkdir .build
cd .build
#wget https://github.com/wekan/wekan/releases/download/v13.07/wekan-13.07-amd64.zip
#wget https://github.com/wekan/wekan/releases/download/v13.11/wekan-13.11-amd64.zip
# Retry: a release asset can 404 for a short while right after it is
# uploaded (CDN/propagation lag), and a plain wget treated that as
# fatal. --retry-on-http-error covers the 404/5xx window; a genuinely
# missing bundle still fails after the tries are spent.
wget --tries=20 --waitretry=20 --retry-on-http-error=404,403,500,502,503 \
"https://github.com/wekan/wekan/releases/download/v13.07/${WEKAN_ZIP}"
"https://github.com/wekan/wekan/releases/download/v13.11/${WEKAN_ZIP}"
unzip "${WEKAN_ZIP}"
python3 ../releases/check-telemetry.py --bundle bundle
rm "${WEKAN_ZIP}"
+43
View File
@@ -0,0 +1,43 @@
'use strict';
// #6899: the recurring-checklist reset cron reads the checklists with a
// resetInterval on every tick; with no index that was a full scan of every
// checklist (about 45,000 on the reporting instance), reported on Admin Panel
// -> Problems as db.collection-scan. A partial index holds only the recurring
// checklists, as #6825 did for recurring cards.
// Run: node tests/checklistResetIndex.test.cjs
const assert = require('assert');
const fs = require('fs');
const path = require('path');
const read = rel => fs.readFileSync(path.join(__dirname, '..', rel), 'utf8');
let passed = 0;
const test = (name, fn) => { fn(); passed += 1; console.log(' ok -', name); };
test('checklists have a partial index on resetInterval, ensured at startup', () => {
const src = read('models/checklists.js');
const startup = src.slice(src.indexOf('Meteor.startup(async () => {'));
assert.match(startup,
/await ensureIndex\(Checklists, \{ resetInterval: 1 \},\s+\{ partialFilterExpression: \{ resetInterval: \{ \$exists: true \} \} \}\);/);
});
test('the cron selector asks $exists: true, which lets MongoDB use the partial index', () => {
// negative: a selector without $exists: true could not use a partial index
// and would go back to scanning every checklist
assert.match(read('server/checklistResetSchedule.js'),
/resetInterval: \{ \$exists: true, \$nin: \[null, 'none'\] \}/);
});
test('every server-side resetInterval query carries $exists: true', () => {
const root = path.join(__dirname, '..');
const walk = d => fs.readdirSync(d, { withFileTypes: true }).flatMap(e =>
e.isDirectory() ? walk(path.join(d, e.name)) : e.name.endsWith('.js') ? [path.join(d, e.name)] : []);
for (const file of [...walk(path.join(root, 'server')), ...walk(path.join(root, 'models'))]) {
for (const m of fs.readFileSync(file, 'utf8').matchAll(/resetInterval: \{ \$nin[^}]*\}/g)) {
assert.fail(`${path.relative(root, file)}: ${m[0]} cannot use the partial index`);
}
}
});
console.log(`\n${passed} tests passed`);
+4 -1
View File
@@ -88,7 +88,10 @@ test('CustomFieldBleed: every REST custom-field write is scoped to the URL board
// The PUT route still applies its fields through the scoped selector.
const put = sections.find(s => s.startsWith("WebApp.handlers.put('/api/boards/:boardId/custom-fields/:customFieldId'"));
assert.ok(put, 'the custom-field PUT route exists');
assert.ok(customFieldWrites(put).length >= 8, 'PUT updates each supported field');
// PUT now saves the supported fields together so validation cannot leave a
// partial update. Field coverage lives in restCustomFieldUpdate.test.cjs;
// this guard still checks the selector of every write.
assert.equal(customFieldWrites(put).length, 1, 'PUT saves its fields in one update');
assert.ok(customFieldWrites(put).every(w => w.selector === 'boardScopedField'));
});
@@ -0,0 +1,117 @@
'use strict';
// #6898: the daily file-integrity scan reported every file WeKan itself had
// deleted as a StorageBleed "missing" on every run, because nothing ever removed
// its baseline. On an instance that purges attachments daily one finding was
// folded 1,164 times, burying any real tamper finding.
//
// Every place WeKan deletes or moves a stored local file now forgets that
// file's baseline. A file that disappears any other way keeps its baseline, so
// the scan still reports it.
// Run: node --test tests/integrityBaselineForgottenOnDelete.test.cjs
const assert = require('node:assert/strict');
const fs = require('node:fs');
const path = require('node:path');
const vm = require('node:vm');
const { test } = require('node:test');
const { baselineIdsFor, versionPathsOf } = require('../models/lib/fileIntegrityBaselineIds.cjs');
const root = path.join(__dirname, '..');
const read = rel => fs.readFileSync(path.join(root, rel), 'utf8');
function body(source, signature) {
const start = source.indexOf(signature);
assert.ok(start >= 0, `${signature} not found`);
let depth = 0;
for (let i = source.indexOf('{', start); i < source.length; i++) {
if (source[i] === '{') depth++;
else if (source[i] === '}' && --depth === 0) return source.slice(start, i + 1);
}
throw new Error('unbalanced braces');
}
test('baseline ids are the recorded path and its absolute form', () => {
assert.deepEqual(baselineIdsFor('/data/attachments/a.png'), ['/data/attachments/a.png']);
assert.deepEqual(baselineIdsFor('rel/a.png'), ['rel/a.png', path.resolve('rel/a.png')]);
assert.deepEqual(baselineIdsFor(['/x', '/x']), ['/x']);
});
test('no paths, empty paths and non-strings forget nothing', () => {
assert.deepEqual(baselineIdsFor(undefined), []);
assert.deepEqual(baselineIdsFor(['', null, 5, {}]), []);
});
test('every version path of every removed file is collected', () => {
assert.deepEqual(versionPathsOf([
{ versions: { original: { path: '/a/1' }, thumb: { path: '/a/2' } } },
{ versions: { original: {} } },
null,
{ versions: null },
]), ['/a/1', '/a/2']);
});
test('forgetIntegrityBaselines removes exactly those ids, and nothing when there are none', async () => {
const src = read('server/lib/fileIntegrityForget.js')
.replace(/^export /gm, '')
.concat('\n;({ forgetIntegrityBaselines, forgetIntegrityBaselinesSoon });');
const removed = [];
const FileIntegrity = { async removeAsync(selector) { removed.push(selector); return 1; } };
const api = vm.runInNewContext(src, {
require: id => (id.endsWith('fileIntegrityBaselineIds.cjs')
? require('../models/lib/fileIntegrityBaselineIds.cjs')
: { default: FileIntegrity }),
});
assert.equal(await api.forgetIntegrityBaselines([]), 0);
assert.deepEqual(removed, []);
await api.forgetIntegrityBaselines(['/data/attachments/a.png']);
assert.deepEqual(JSON.parse(JSON.stringify(removed)), [{ _id: { $in: ['/data/attachments/a.png'] } }]);
// A failing removal never surfaces as an error to the delete that caused it.
FileIntegrity.removeAsync = async () => { throw new Error('db down'); };
assert.doesNotThrow(() => api.forgetIntegrityBaselinesSoon(['/x']));
await new Promise(r => setImmediate(r));
});
test('the filesystem strategy forgets the baseline when it deletes or renames a file', () => {
const src = read('models/lib/fileStoreStrategy.js');
const cls = body(src, 'export class FileStoreStrategyFilesystem');
assert.match(body(cls, 'unlink() {'), /fs\.unlink\(filePath[\s\S]*forgetBaselines\(\[filePath/);
assert.match(body(cls, 'rename(newFilePath) {'), /fs\.renameSync\(current, newFilePath\);\s+[\s\S]*forgetBaselines\(\[current/);
const helper = body(src, 'function forgetBaselines(paths)');
assert.match(helper, /if \(!Meteor\.isServer\) return;/);
assert.match(helper, /try \{[\s\S]*\} catch \(e\)/, 'bookkeeping must never break a delete');
});
test('every delete or rename in the filesystem strategy forgets the baseline (negative)', () => {
const cls = body(read('models/lib/fileStoreStrategy.js'), 'export class FileStoreStrategyFilesystem')
.replace(/\/\*[\s\S]*?\*\//g, '').replace(/\/\/.*$/gm, '');
const deletes = cls.match(/fs\.(unlink|unlinkSync|rm|rmSync|renameSync|promises\.unlink|promises\.rename)\(/g) || [];
const forgets = cls.match(/forgetBaselines\(/g) || [];
assert.ok(deletes.length > 0);
assert.equal(forgets.length, deletes.length, 'a delete or rename without forgetBaselines() leaves a stale baseline');
});
test('attachment and avatar removal forget the removed files and keep the default unlink', () => {
for (const [file, sig] of [
['models/attachments.server.js', 'Attachments.onAfterRemove = function'],
['models/avatars.server.js', 'Avatars.onAfterRemove = async function'],
]) {
const src = read(file);
assert.match(src, /import \{ forgetIntegrityBaselinesOfFiles \} from '\/server\/lib\/fileIntegrityForget';/, file);
const hook = body(src, sig);
assert.match(hook, /forgetIntegrityBaselinesOfFiles\(files\);/, file);
// Meteor-Files skips its own unlink of the version paths when this hook
// returns true; the files would then never be deleted at all.
assert.doesNotMatch(hook, /return true/, file);
}
});
test('the scan and the baseline model still never remove a baseline themselves', () => {
// A file that vanishes without going through WeKan - rm, disk corruption,
// tampering - must keep its baseline and still be reported missing.
for (const file of ['server/lib/fileIntegrityScan.js', 'models/fileIntegrity.js']) {
assert.doesNotMatch(read(file), /FileIntegrity\.(remove|removeAsync|deleteMany|deleteOne)\(/, file);
}
assert.match(read('server/lib/fileIntegrityScan.js'), /expected file is missing/);
});
@@ -0,0 +1,54 @@
'use strict';
// #6898: a file WeKan deletes itself must not be reported by the daily
// file-integrity scan as "missing" forever. Deleting an uploaded avatar from
// the avatar popup goes through Avatars.onAfterRemove, which now forgets that
// file's baseline - and only that file's: an unrelated baseline stays, so a
// file that vanishes some other way is still reported.
const { test, expect } = require('../fixtures');
const db = require('../helpers/db');
const { loginWithToken } = require('../helpers/auth');
const BASE_URL = process.env.WEKAN_BASE_URL || 'http://localhost:3000';
// 1x1 transparent PNG.
const PNG = Buffer.from(
'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==',
'base64',
);
test('deleting an avatar forgets its integrity baseline and no other', async ({ page, user }) => {
const unrelated = `/e2e-not-a-real-file-${db.uid('baseline')}`;
db.insertOne('fileIntegrity', { _id: unrelated, path: unrelated, size: 1, at: new Date() });
try {
await loginWithToken(page, user.id, user.token);
await page.goto(BASE_URL, { waitUntil: 'domcontentloaded' });
await page.locator('.js-open-header-member-menu').click();
await page.locator('.js-pop-over .js-change-avatar').click();
await page.locator('.js-pop-over .js-upload-avatar-input')
.setInputFiles({ name: 'avatar.png', mimeType: 'image/png', buffer: PNG });
await expect.poll(() => db.find('avatars', { userId: user.id }).length, { timeout: 30_000 }).toBe(1);
const avatar = db.find('avatars', { userId: user.id })[0];
const filePath = avatar.versions?.original?.path;
test.skip(!filePath, 'avatar storage keeps no local path in this configuration');
// What the scan would have recorded on its first sight of the file.
db.deleteOne('fileIntegrity', { _id: filePath });
db.insertOne('fileIntegrity', { _id: filePath, path: filePath, size: PNG.length, at: new Date() });
const item = page.locator('.js-pop-over .avatar-list-item').filter({ has: page.locator('.js-delete-avatar') });
await expect(item).toHaveCount(1);
await item.locator('.js-delete-avatar').click();
await page.locator('.js-pop-over button.js-confirm').click();
await expect.poll(() => db.countDocuments('avatars', { userId: user.id }), { timeout: 15_000 }).toBe(0);
await expect.poll(() => db.countDocuments('fileIntegrity', { _id: filePath }), { timeout: 15_000 }).toBe(0);
// negative: a baseline for a file WeKan did not delete is kept, so the scan
// still reports a file that disappears out of band.
expect(db.countDocuments('fileIntegrity', { _id: unrelated })).toBe(1);
} finally {
db.deleteOne('fileIntegrity', { _id: unrelated });
db.deleteMany('avatars', { userId: user.id });
}
});
@@ -127,7 +127,10 @@ test('dotted checkbox writes emit false once and removing a valued field emits a
await expect(call(page, 'setCardCustomFieldCheckbox', card._id, 'foreign', true)).rejects.toThrow();
expect(activities()).toHaveLength(1);
await call(page, 'setCardCustomFieldAssigned', card._id, fieldId, false);
expect(activities().map(a => a.activityType)).toEqual(['setCustomField', 'unsetCustomField']);
// One set and one unset. Not in insertion order: an unsorted find walks
// whatever index serves it, and activities' { cardId: 1, createdAt: -1 }
// returns the newest first.
expect(activities().map(a => a.activityType).sort()).toEqual(['setCustomField', 'unsetCustomField']);
await call(page, 'setCardCustomFieldAssigned', card._id, fieldId, true);
expect(activities()).toHaveLength(2);
} finally {
@@ -0,0 +1,84 @@
'use strict';
const { test, expect } = require('../fixtures');
const db = require('../helpers/db');
const headers = token => ({ Authorization: `Bearer ${token}` });
const cardsPath = board => `/api/boards/${board.boardId}/lists/${board.listIds[0]}/cards`;
test('REST creation persists a parent and shows the new card as its subtask', async ({ boardPage: page, request, user, board }) => {
const parent = db.findOne('cards', { boardId: board.boardId, title: 'Alpha Card' });
const response = await request.post(cardsPath(board), {
headers: headers(user.token),
data: { title: 'Created subtask', parentId: parent._id, swimlaneId: board.swimlaneId },
});
expect(response.status()).toBe(200);
const { _id } = await response.json();
expect(db.findOne('cards', { _id }).parentId).toBe(parent._id);
const read = await request.get(`${cardsPath(board)}/${_id}`, { headers: headers(user.token) });
expect(read.status()).toBe(200);
expect((await read.json()).parentId).toBe(parent._id);
await page.goto(`/b/${board.boardId}/${board.slug}/${parent._id}`);
const subtask = page.locator('.card-subtasks-items .js-subtasks').filter({ hasText: 'Created subtask' });
await expect(subtask).toBeVisible();
await page.reload();
await expect(subtask).toBeVisible();
});
test('REST creation accepts an omitted or empty parent', async ({ request, user, board }) => {
for (const body of [{}, { parentId: '' }]) {
const response = await request.post(cardsPath(board), {
headers: headers(user.token),
data: { title: 'Independent card', swimlaneId: board.swimlaneId, ...body },
});
expect(response.status()).toBe(200);
const { _id } = await response.json();
expect(db.findOne('cards', { _id }).parentId).toBe('');
}
});
test('REST creation refuses non-string parents without inserting', async ({ request, user, board }) => {
const parent = db.findOne('cards', { boardId: board.boardId, title: 'Alpha Card' });
const before = db.countDocuments('cards', { boardId: board.boardId });
for (const parentId of [0, false, true, 42, {}, [], { _id: parent._id }]) {
const response = await request.post(cardsPath(board), {
headers: headers(user.token),
data: { title: 'Refused child', parentId, swimlaneId: board.swimlaneId },
});
expect(response.status()).toBe(400);
expect(await response.json()).toEqual({ error: 'Invalid parentId' });
expect(db.countDocuments('cards', { boardId: board.boardId })).toBe(before);
}
});
test('REST creation refuses missing and invisible parents without inserting', async ({ request, user, user2, board }) => {
const foreign = db.seedBoard({ ownerId: user2.id, cardTitlesPerList: [['Private parent']] });
const privateParent = db.findOne('cards', { boardId: foreign.boardId });
const before = db.countDocuments('cards', { boardId: board.boardId });
try {
for (const [parentId, status] of [[db.uid('missing'), 404], [privateParent._id, 403]]) {
const response = await request.post(cardsPath(board), {
headers: headers(user.token),
data: { title: 'Refused child', parentId, swimlaneId: board.swimlaneId },
});
expect(response.status()).toBe(status);
expect(db.countDocuments('cards', { boardId: board.boardId })).toBe(before);
}
} finally {
db.cleanup({ boardIds: [foreign.boardId] });
}
});
test('REST creation with a parent still requires board write access', async ({ request, user2, board }) => {
const parent = db.findOne('cards', { boardId: board.boardId, title: 'Alpha Card' });
const before = db.countDocuments('cards', { boardId: board.boardId });
for (const auth of [{}, headers(user2.token)]) {
const response = await request.post(cardsPath(board), {
headers: auth,
data: { title: 'Refused child', parentId: parent._id, swimlaneId: board.swimlaneId },
});
expect([401, 403]).toContain(response.status());
expect(db.countDocuments('cards', { boardId: board.boardId })).toBe(before);
}
});
@@ -0,0 +1,87 @@
'use strict';
const { test, expect } = require('../fixtures');
const db = require('../helpers/db');
const headers = token => ({ Authorization: `Bearer ${token}` });
async function createField(request, user, board) {
const base = `/api/boards/${board.boardId}/custom-fields`;
const response = await request.post(base, {
headers: headers(user.token),
data: { name: 'Original field', type: 'text', settings: {}, showOnCard: true, showLabelOnMiniCard: true },
});
expect(response.status()).toBe(200);
const { _id } = await response.json();
return { id: _id, url: `${base}/${_id}` };
}
for (const invalid of [{ settings: 'invalid' }, { type: 'invalid' }]) {
test(`rejected custom-field ${Object.keys(invalid)[0]} leaves the entire definition unchanged`, async ({ request, user, board }) => {
const field = await createField(request, user, board);
const before = db.findOne('customFields', { _id: field.id });
const response = await request.put(field.url, {
headers: headers(user.token), data: { name: 'Changed field', ...invalid, showOnCard: false },
});
expect(response.status()).toBe(500);
expect(db.findOne('customFields', { _id: field.id })).toEqual(before);
const read = await request.get(field.url, { headers: headers(user.token) });
expect(read.status()).toBe(200);
expect(await read.json()).toMatchObject({ name: before.name, type: before.type, settings: before.settings, showOnCard: before.showOnCard });
});
}
test('custom-field updates persist together and the board displays the accepted name', async ({ boardPage: page, request, user, board }) => {
const field = await createField(request, user, board);
const card = db.findOne('cards', { boardId: board.boardId, title: 'Alpha Card' });
db.updateOne('boards', { _id: board.boardId }, { $set: { allowsCustomFieldsOnMinicard: true } });
db.updateOne('cards', { _id: card._id }, { $set: { customFields: [{ _id: field.id, value: 'Visible value' }] } });
const minicard = page.locator('.minicard-wrapper').filter({ hasText: 'Alpha Card' });
await expect(minicard).toContainText('Original field');
const body = {
name: 'Updated field', type: 'text', settings: {}, showOnCard: true,
automaticallyOnCard: false, alwaysOnCard: false, showLabelOnMiniCard: true, showSumAtTopOfList: false,
};
const response = await request.put(field.url, { headers: headers(user.token), data: body });
expect(response.status()).toBe(200);
expect(await response.json()).toEqual({ _id: field.id });
expect(db.findOne('customFields', { _id: field.id })).toMatchObject(body);
await expect(minicard).toContainText('Updated field');
await expect(minicard).not.toContainText('Original field');
await expect(minicard).toContainText('Visible value');
const rejected = await request.put(field.url, {
headers: headers(user.token), data: { name: 'Rejected field', settings: 'invalid' },
});
expect(rejected.status()).toBe(500);
expect(db.findOne('customFields', { _id: field.id })).toMatchObject(body);
await page.reload();
await expect(minicard).toContainText('Updated field');
await expect(minicard).not.toContainText('Rejected field');
});
test('custom-field updates preserve authorization, board scope, false values and omitted fields', async ({ request, user, user2, board }) => {
const field = await createField(request, user, board);
const before = db.findOne('customFields', { _id: field.id });
for (const options of [{}, { headers: headers(user2.token) }]) {
const response = await request.put(field.url, { ...options, data: { name: 'Forbidden field' } });
expect([401, 403]).toContain(response.status());
expect(db.findOne('customFields', { _id: field.id })).toEqual(before);
}
const other = db.seedBoard({ ownerId: user.id });
try {
const response = await request.put(`/api/boards/${other.boardId}/custom-fields/${field.id}`, {
headers: headers(user.token), data: { name: 'Wrong board' },
});
expect(response.status()).toBe(200);
expect(db.findOne('customFields', { _id: field.id })).toEqual(before);
} finally { db.cleanup({ boardIds: [other.boardId] }); }
for (const body of [{}, { boardIds: ['other'], adminOnly: true }, { type: 'number', settings: {}, showOnCard: false, showLabelOnMiniCard: false }]) {
const response = await request.put(field.url, { headers: headers(user.token), data: body });
expect(response.status()).toBe(200);
expect(await response.json()).toEqual({ _id: field.id });
}
const after = db.findOne('customFields', { _id: field.id });
expect(after).toEqual({ ...before, modifiedAt: after.modifiedAt, type: 'number', showOnCard: false, showLabelOnMiniCard: false });
});
@@ -73,11 +73,97 @@ test('integration PUT preserves authentication and board-admin requirements', as
expect(db.findOne('integrations', { _id: integration.id })).toEqual(before);
});
test('successful REST updates appear in webhook settings after a wrong-board update is rejected', async ({ request, user, board, integration, boardPage }) => {
test('integration DELETE rejects missing and wrong-board IDs and removes only its own integration', async ({ request, user, board, integration }) => {
const other = db.seedBoard({ ownerId: user.id });
try {
const before = db.findOne('integrations', { _id: integration.id });
for (const path of [
integrationPath(board.boardId, 'missing-integration'),
integrationPath(other.boardId, integration.id),
]) {
const response = await request.delete(path, { headers: headers(user.token) });
expect(response.status()).toBe(404);
expect(response.headers()['content-type']).toContain('application/json');
expect(await response.json()).toEqual({ error: 'Integration not found' });
expect(db.findOne('integrations', { _id: integration.id })).toEqual(before);
}
const response = await request.delete(integration.path, { headers: headers(user.token) });
expect(response.status()).toBe(200);
expect(await response.json()).toEqual({ _id: integration.id });
expect(db.findOne('integrations', { _id: integration.id })).toBeNull();
const read = await request.get(integration.path, { headers: headers(user.token) });
expect(read.status()).toBe(404);
const repeated = await request.delete(integration.path, { headers: headers(user.token) });
expect(repeated.status()).toBe(404);
expect(await repeated.json()).toEqual({ error: 'Integration not found' });
} finally {
db.cleanup({ boardIds: [other.boardId] });
}
});
test('integration DELETE preserves authentication and board-admin requirements', async ({ request, board, integration }) => {
const before = db.findOne('integrations', { _id: integration.id });
for (const id of [integration.id, 'missing-integration']) {
const path = integrationPath(board.boardId, id);
expect((await request.delete(path)).status()).toBe(401);
const member = db.seedUser();
try {
db.updateOne('boards', { _id: board.boardId }, { $push: { members: {
userId: member.id, isActive: true, isAdmin: false, isNoComments: false, isCommentOnly: false,
} } });
expect((await request.delete(path, { headers: headers(member.token) })).status()).toBe(403);
expect(db.findOne('integrations', { _id: integration.id })).toEqual(before);
} finally {
db.cleanup({ userIds: [member.id] });
}
}
});
test('webhook settings retain a rejected deletion and react to a successful REST deletion', async ({ request, user, board, integration, boardPage }) => {
const other = db.seedBoard({ ownerId: user.id });
let siblingId;
try {
const created = await request.post(`/api/boards/${board.boardId}/integrations`, {
headers: headers(user.token), data: { url: 'https://example.com/other-webhook' },
});
expect(created.status()).toBe(200);
siblingId = (await created.json())._id;
const sibling = db.findOne('integrations', { _id: siblingId });
const menu = boardPage.locator('.board-sidebar .js-open-board-menu');
if (!(await menu.isVisible())) await boardPage.locator('.js-toggle-page-sidebar').first().click();
await menu.click();
await boardPage.locator('.js-outgoing-webhooks').click();
const form = boardPage.locator('form.integration-form').filter({
has: boardPage.locator(`input[name="id"][value="${integration.id}"]`),
});
await expect(form).toBeVisible();
const before = db.findOne('integrations', { _id: integration.id });
const rejected = await request.delete(integrationPath(other.boardId, integration.id), {
headers: headers(user.token),
});
expect(rejected.status()).toBe(404);
expect(db.findOne('integrations', { _id: integration.id })).toEqual(before);
await expect(form).toBeVisible();
const response = await request.delete(integration.path, { headers: headers(user.token) });
expect(response.status()).toBe(200);
expect(await response.json()).toEqual({ _id: integration.id });
expect(db.findOne('integrations', { _id: integration.id })).toBeNull();
await expect(form).toHaveCount(0);
expect(db.findOne('integrations', { _id: siblingId })).toEqual(sibling);
await expect(boardPage.locator(`form.integration-form input[name="id"][value="${siblingId}"]`)).toHaveCount(1);
} finally {
if (siblingId) db.deleteOne('integrations', { _id: siblingId });
db.cleanup({ boardIds: [other.boardId] });
}
});
test('successful REST updates appear in webhook settings after invalid updates are rejected', async ({ request, user, board, integration, boardPage }) => {
const other = db.seedBoard({ ownerId: user.id });
try {
const response = await request.put(integration.path, {
headers: headers(user.token), data: { title: 'Retained webhook', enabled: false, activities: [] },
headers: headers(user.token),
data: { title: 'Retained webhook', enabled: false, activities: [], url: 'https://example.com/webhook' },
});
expect(response.status()).toBe(200);
const rejected = await request.put(integrationPath(other.boardId, integration.id), {
@@ -85,6 +171,18 @@ test('successful REST updates appear in webhook settings after a wrong-board upd
});
expect(rejected.status()).toBe(404);
const before = db.findOne('integrations', { _id: integration.id });
const invalid = await request.put(integration.path, {
headers: headers(user.token),
data: { title: 'Invalid URL overwrite', enabled: true, url: 'invalid-url' },
});
expect(invalid.status()).toBe(400);
expect(await invalid.json()).toMatchObject({ error: 'invalid-webhook-url' });
expect(db.findOne('integrations', { _id: integration.id })).toEqual(before);
const read = await request.get(integration.path, { headers: headers(user.token) });
expect(read.status()).toBe(200);
expect(await read.json()).toMatchObject({ title: 'Retained webhook', enabled: false });
const menu = boardPage.locator('.board-sidebar .js-open-board-menu');
if (!(await menu.isVisible())) await boardPage.locator('.js-toggle-page-sidebar').first().click();
await menu.click();
@@ -0,0 +1,47 @@
'use strict';
const { test, expect } = require('../fixtures');
const db = require('../helpers/db');
const { loginWithToken, openBoard } = require('../helpers/auth');
test('ownership transfer leaves ordinary memberships unchanged and transfers administered boards', async ({ page, request, adminUser, user, user2 }) => {
const owned = db.seedBoard({ ownerId: user.id, title: 'Owned board' });
const ordinary = db.seedBoard({ ownerId: user2.id, title: 'Ordinary membership', cardTitlesPerList: [['Retained card']] });
const absent = db.seedBoard({ ownerId: user2.id, title: 'Unrelated board' });
db.addBoardMember({ boardId: ordinary.boardId, userId: user.id });
db.addBoardMember({ boardId: ordinary.boardId, userId: adminUser.id });
const ordinaryBefore = db.findOne('boards', { _id: ordinary.boardId });
const absentBefore = db.findOne('boards', { _id: absent.boardId });
try {
await loginWithToken(page, user.id, user.token);
await openBoard(page, ordinary.boardId, ordinary.slug);
await expect(page.locator('.minicard-title').filter({ hasText: 'Retained card' })).toBeVisible();
const response = await request.put(`/api/users/${user.id}`, {
headers: { Authorization: `Bearer ${adminUser.token}` }, data: { action: 'takeOwnership' },
});
expect(response.status()).toBe(200);
expect(await response.json()).toEqual([{ _id: owned.boardId, title: 'Owned board' }]);
await expect.poll(() => db.findOne('boards', { _id: owned.boardId }).members.some(m => m.userId === adminUser.id && m.isAdmin)).toBe(true);
expect(db.findOne('boards', { _id: owned.boardId }).members.some(m => m.userId === user.id)).toBe(false);
expect(db.findOne('boards', { _id: ordinary.boardId })).toEqual(ordinaryBefore);
expect(db.findOne('boards', { _id: absent.boardId })).toEqual(absentBefore);
const repeated = await request.put(`/api/users/${user.id}`, {
headers: { Authorization: `Bearer ${adminUser.token}` }, data: { action: 'takeOwnership' },
});
expect(repeated.status()).toBe(200);
expect(await repeated.json()).toEqual([]);
expect(db.findOne('boards', { _id: ordinary.boardId })).toEqual(ordinaryBefore);
await page.reload();
await expect(page.locator('.minicard-title').filter({ hasText: 'Retained card' })).toBeVisible();
} finally {
db.cleanup({ boardIds: [owned.boardId, ordinary.boardId, absent.boardId] });
}
});
test('ordinary users cannot request ownership transfer', async ({ request, user, user2, board }) => {
const before = db.findOne('boards', { _id: board.boardId });
for (const headers of [{}, { Authorization: `Bearer ${user2.token}` }]) {
const response = await request.put(`/api/users/${user.id}`, { headers, data: { action: 'takeOwnership' } });
expect([401, 403]).toContain(response.status());
expect(db.findOne('boards', { _id: board.boardId })).toEqual(before);
}
});
+84
View File
@@ -0,0 +1,84 @@
'use strict';
// On the server ReactiveCache.get*() is async and returns a Promise, which is
// always truthy. `!ReactiveCache.getUser(id)` was therefore always false, and
// Boards.removeMember's #5330 branch for members whose account was deleted
// never ran on the server. These tests pin the fix and keep the shape out of
// the whole tree.
const assert = require('node:assert/strict');
const fs = require('node:fs');
const path = require('node:path');
const { test } = require('node:test');
const root = path.join(__dirname, '..');
function walk(dir) {
return fs.readdirSync(dir, { withFileTypes: true }).flatMap(e => {
// rspack output is skipped by prefix (tests/sourceWalkersSkipBuildOutput.test.cjs).
if (e.name === 'node_modules' || e.name.startsWith('.') || e.name.startsWith('_build')) return [];
const p = path.join(dir, e.name);
return e.isDirectory() ? walk(p) : /\.(c|m)?js$/.test(e.name) ? [p] : [];
});
}
function extractMethod(source, signature) {
const start = source.indexOf(signature);
assert.ok(start >= 0, `${signature} not found`);
let depth = 0;
for (let i = source.indexOf('{', start); i < source.length; i++) {
if (source[i] === '{') depth++;
else if (source[i] === '}' && --depth === 0) return source.slice(start, i + 1);
}
throw new Error('unbalanced braces');
}
function removeMemberFixture({ userExists }) {
const source = fs.readFileSync(path.join(root, 'models/boards.js'), 'utf8');
const method = extractMethod(source, 'async removeMember(memberId)');
const updates = [];
const scope = {
ReactiveCache: { async getUser() { return userExists ? { _id: 'gone' } : null; } },
Boards: { async updateAsync(id, modifier) { updates.push(modifier); } },
pullMemberById: (members, id) => members.filter(m => m.userId !== id),
};
const make = new Function(...Object.keys(scope), `return { ${method} };`);
const board = Object.assign(make(...Object.values(scope)), {
_id: 'b1',
members: [
{ userId: 'admin', isAdmin: true, isActive: true },
{ userId: 'gone', isAdmin: false, isActive: true },
],
memberIndex(id) { return this.members.map(x => x.userId).indexOf(id); },
activeAdmins() { return this.members.filter(m => m.isAdmin && m.isActive); },
});
return { board, updates };
}
test('a member whose account was deleted is removed from the board, not just deactivated', async () => {
const { board, updates } = removeMemberFixture({ userExists: false });
await board.removeMember('gone');
assert.deepEqual(JSON.parse(JSON.stringify(updates)), [
{ $set: { members: [{ userId: 'admin', isAdmin: true, isActive: true }] } },
]);
});
test('an existing user is still deactivated by index, keeping the history', async () => {
const { board, updates } = removeMemberFixture({ userExists: true });
await board.removeMember('gone');
assert.equal(updates.length, 1);
assert.deepEqual(updates[0].$set, { 'members.1.isActive': false, 'members.1.isAdmin': false });
});
test('no server-side code negates an un-awaited async ReactiveCache lookup', () => {
// client/ is excluded: ReactiveCache is synchronous in the browser.
const offenders = [];
for (const file of ['server', 'models'].flatMap(d => walk(path.join(root, d)))) {
const lines = fs.readFileSync(file, 'utf8').split('\n');
lines.forEach((line, i) => {
if (/!\s*ReactiveCache\.get[A-Z]\w*\(/.test(line)) {
offenders.push(`${path.relative(root, file)}:${i + 1}: ${line.trim()}`);
}
});
}
assert.deepEqual(offenders, []);
});
+96
View File
@@ -0,0 +1,96 @@
'use strict';
const assert = require('node:assert/strict');
const fs = require('node:fs');
const path = require('node:path');
const vm = require('node:vm');
const { test } = require('node:test');
const route = '/api/boards/:boardId/lists/:listId/cards';
function fixture() {
const inserts = [];
const checkedParents = [];
let handler;
const source = fs.readFileSync(path.join(__dirname, '../server/models/cards.js'), 'utf8');
const start = source.indexOf(`WebApp.handlers.post('${route}'`);
assert.notEqual(start, -1);
const end = source.indexOf('\n});', start);
assert.ok(end > start);
const board = { async getNextCardNumber() { return 1; } };
vm.runInNewContext(source.slice(start, end + '\n});'.length), {
WebApp: { handlers: { post(url, fn) { assert.equal(url, route); handler = fn; } } },
Authentication: {
checkLoggedIn(userId) { if (!userId) throw new Error('Unauthorized'); },
async checkAdminOrCondition(userId) { if (userId !== 'member') throw new Error('Forbidden'); },
},
allowIsBoardMemberWithWriteAccess() { return true; },
async assertListNotRefused() {},
async assertParentCardIsVisible(userId, parentId) {
checkedParents.push(parentId);
if (parentId === 'missing') throw new Error('Parent card not found');
if (parentId === 'private') throw new Error('Forbidden parent');
},
async restPlacementError() { return null; },
ReactiveCache: {
async getBoard() { return board; },
async getCards() { return []; },
async getCustomFields() { return []; },
async getUser() { return { _id: 'member' }; },
async getCard() { return inserts[0]; },
},
Cards: { direct: { async insertAsync(doc) { inserts.push(doc); return 'child'; } } },
sendJsonResult(res, result) { res.result = result; },
noteAuthorSpoof() {},
async cardCreation() {},
});
return {
inserts, checkedParents,
async post(body, userId = 'member') {
const res = {};
await handler({ userId, params: { boardId: 'board', listId: 'list' }, body: { title: 'Child', ...body } }, res);
return JSON.parse(JSON.stringify(res.result));
},
};
}
test('single-card POST validates and stores the parent from the body', async () => {
const f = fixture();
assert.deepEqual(await f.post({ parentId: 'parent' }), { code: 200, data: { _id: 'child' } });
assert.deepEqual(f.checkedParents, ['parent']);
assert.equal(f.inserts[0].parentId, 'parent');
});
for (const parentId of [undefined, '', null]) {
test(`single-card POST still accepts no parent (${JSON.stringify(parentId)})`, async () => {
const f = fixture();
assert.equal((await f.post(parentId === undefined ? {} : { parentId })).code, 200);
assert.equal(f.inserts.length, 1);
assert.equal(f.inserts[0].parentId, parentId);
});
}
for (const parentId of [0, false, true, 42, {}, [], { _id: 'parent' }]) {
test(`single-card POST refuses a non-string parent (${JSON.stringify(parentId)}) before lookup`, async () => {
const f = fixture();
assert.deepEqual(await f.post({ parentId }), { code: 400, data: { error: 'Invalid parentId' } });
assert.equal(f.checkedParents.length, 0);
assert.equal(f.inserts.length, 0);
});
}
for (const [parentId, error] of [['missing', /Parent card not found/], ['private', /Forbidden parent/]]) {
test(`single-card POST refuses a ${parentId} parent before inserting`, async () => {
const f = fixture();
await assert.rejects(f.post({ parentId }), error);
assert.equal(f.inserts.length, 0);
});
}
for (const userId of [null, 'outsider']) {
test(`single-card POST still requires write access (${userId})`, async () => {
const f = fixture();
await assert.rejects(f.post({ parentId: 'parent' }, userId), /Unauthorized|Forbidden/);
assert.equal(f.inserts.length, 0);
});
}
+106
View File
@@ -0,0 +1,106 @@
'use strict';
// Execute the registered REST handler; live schema/database coverage is in Playwright.
const assert = require('node:assert/strict');
const fs = require('node:fs');
const path = require('node:path');
const vm = require('node:vm');
const { test } = require('node:test');
const route = '/api/boards/:boardId/custom-fields/:customFieldId';
const plain = value => JSON.parse(JSON.stringify(value));
function fixture({ deny = false } = {}) {
const calls = [];
const document = { _id: 'field', boardIds: ['board'], name: 'Original', type: 'text', settings: {}, showOnCard: true };
let handler;
const context = {
WebApp: { handlers: Object.fromEntries(['get', 'post', 'put', 'delete'].map(verb => [verb, (url, fn) => {
if (verb === 'put' && url === route) handler = fn;
}])) },
Authentication: { async checkBoardWriteAccess(userId, boardId) {
calls.push(['access', userId, boardId]);
if (deny) throw new Error('Forbidden');
} },
CustomFields: { direct: { async updateAsync(selector, modifier) {
calls.push(['update', plain(selector), plain(modifier)]);
const fields = plain(modifier.$set);
if ('settings' in fields && typeof fields.settings !== 'object') throw new Error('Invalid settings');
if ('type' in fields && !['text', 'number'].includes(fields.type)) throw new Error('Invalid type');
if (selector._id !== document._id || !selector.boardIds.$in.some(id => document.boardIds.includes(id))) return 0;
Object.assign(document, fields);
return 1;
} } },
sendJsonResult(res, result) { res.responses.push(plain(result)); },
};
const source = fs.readFileSync(path.join(__dirname, '../server/models/customFields.js'), 'utf8');
vm.runInNewContext(source.slice(source.indexOf('WebApp.handlers.get(')), context);
assert.equal(typeof handler, 'function');
return {
calls, document,
async put(body, params = {}) {
const res = { responses: [] };
await handler({ userId: 'member', params: { boardId: 'board', customFieldId: 'field', ...params }, body }, res);
assert.equal(res.responses.length, 1);
return res.responses[0];
},
};
}
for (const invalid of [{ settings: 'invalid' }, { type: 'invalid' }]) {
test(`a rejected ${Object.keys(invalid)[0]} update leaves accompanying fields unchanged`, async () => {
const f = fixture();
const before = plain(f.document);
await assert.rejects(f.put({ name: 'Changed', ...invalid }), /Invalid/);
assert.deepEqual(f.document, before);
});
}
test('accepted fields are saved together, including false values, with the existing response', async () => {
const f = fixture();
const body = {
name: 'Changed', type: 'number', settings: {}, showOnCard: false,
automaticallyOnCard: false, alwaysOnCard: false, showLabelOnMiniCard: false, showSumAtTopOfList: false,
};
assert.deepEqual(await f.put(body), { code: 200, data: { _id: 'field' } });
assert.deepEqual(f.document, { _id: 'field', boardIds: ['board'], ...body });
assert.deepEqual(f.calls, [
['access', 'member', 'board'],
['update', { _id: 'field', boardIds: { $in: ['board'] } }, { $set: body }],
]);
});
test('empty or unsupported updates retain the successful response without a write', async () => {
for (const body of [{}, { boardIds: ['other'], adminOnly: true, readOnly: true, unexpected: 'value' }]) {
const f = fixture();
const before = plain(f.document);
assert.deepEqual(await f.put(body), { code: 200, data: { _id: 'field' } });
assert.deepEqual(f.document, before);
assert.deepEqual(f.calls, [['access', 'member', 'board']]);
}
});
test('only own properties are accepted', async () => {
const f = fixture();
const body = Object.assign(Object.create({ name: 'Inherited', type: 'number' }), { showOnCard: false });
await f.put(body);
assert.equal(f.document.name, 'Original');
assert.equal(f.document.type, 'text');
assert.equal(f.document.showOnCard, false);
});
test('the update stays scoped to its board and field', async () => {
for (const params of [{ boardId: 'other' }, { customFieldId: 'missing' }]) {
const f = fixture();
const before = plain(f.document);
await f.put({ name: 'Changed' }, params);
assert.deepEqual(f.document, before);
}
});
test('authorization runs before any write', async () => {
const f = fixture({ deny: true });
await assert.rejects(f.put({ name: 'Changed', settings: 'invalid' }), /Forbidden/);
assert.deepEqual(f.calls, [['access', 'member', 'board']]);
assert.equal(f.document.name, 'Original');
});
+29 -1
View File
@@ -13,7 +13,7 @@ const { publicErrorData } = require('../server/lib/apiResponseHelpers');
const route = '/api/boards/:boardId/integrations/:intId';
const plain = value => JSON.parse(JSON.stringify(value));
function fixture({ deny = false, lookupError } = {}) {
function fixture({ deny = false, lookupError, urlValid = false, internalAllowed = false } = {}) {
const calls = [];
const documents = [
{ _id: 'integration', boardId: 'board', title: 'Webhook', enabled: true, activities: ['all'] },
@@ -43,6 +43,13 @@ function fixture({ deny = false, lookupError } = {}) {
return 1;
} } },
normalizeDeliverySettings,
require(name) {
assert.equal(name, '/server/webhookInternalHosts');
return { async webhookHostIsInternalAllowed() { return internalAllowed; } };
},
async validateAttachmentUrl() {
return { valid: urlValid, reason: 'Invalid URL format' };
},
publicErrorData,
sendJsonResult(res, result) { res.responses.push(plain(result)); },
};
@@ -122,3 +129,24 @@ test('invalid delivery settings still reject an existing integration before writ
assert.equal(f.documents[0].title, 'Webhook');
assert.ok(!f.calls.some(([name]) => name === 'update'));
});
test('an invalid URL rejects all accompanying changes before any write', async () => {
const f = fixture();
const before = plain(f.documents);
const response = await f.put({ title: 'Changed', enabled: false, url: 'invalid-url', token: 'changed', activities: [] });
assert.equal(response.code, 400);
assert.equal(response.data.error, 'invalid-webhook-url');
assert.deepEqual(f.documents, before);
assert.ok(!f.calls.some(([name]) => name === 'update'));
});
for (const options of [{ urlValid: true }, { internalAllowed: true }]) {
test(`an accepted URL still saves accompanying fields ${JSON.stringify(options)}`, async () => {
const f = fixture(options);
const response = await f.put({ title: 'Changed', enabled: false, url: 'https://example.com/webhook' });
assert.equal(response.code, 200);
assert.equal(f.documents[0].title, 'Changed');
assert.equal(f.documents[0].enabled, false);
assert.equal(f.documents[0].url, 'https://example.com/webhook');
});
}
+56 -8
View File
@@ -17,12 +17,12 @@ const { publicErrorData } = require('../server/lib/apiResponseHelpers');
const file = path.join(__dirname, '..', 'server/models/integrations.js');
const source = fs.readFileSync(file, 'utf8');
test('every handler that updates an integration looks it up on that board first', () => {
test('every handler that updates or deletes an integration looks it up on that board first', () => {
const updating = source.split(/\nWebApp\.handlers\./).slice(1)
.filter(block => /Integrations\.direct\.updateAsync/.test(block));
assert.ok(updating.length >= 3, 'PUT and both activities routes are found');
.filter(block => /Integrations\.direct\.(update|remove)Async/.test(block));
assert.ok(updating.length >= 4, 'PUT, both activities routes and DELETE are found');
for (const block of updating) {
const update = block.indexOf('Integrations.direct.updateAsync');
const update = block.search(/Integrations\.direct\.(update|remove)Async/);
const guard = block.indexOf('integrationOr404(');
assert.ok(guard > -1 && guard < update, `${block.split('\n')[0]} checks before it writes`);
// and only after board-admin authorization, so the answer tells nobody anything
@@ -35,33 +35,81 @@ test('negative: the check is not the old "write, then answer 200 with whatever i
assert.match(source, /code: 404, data: \{ error: 'Integration not found' \}/);
});
function run(handlerUrl, verb, integrations) {
function run(handlerUrl, verb, integrations, { deny = false, lookupError, removeError } = {}) {
const calls = [];
const lookups = [];
let handler;
const context = {
WebApp: { handlers: Object.fromEntries(['get', 'post', 'put', 'delete'].map(v => [v, (url, fn) => {
if (v === verb && url === handlerUrl) handler = fn;
}])) },
Authentication: { async checkBoardAdmin() {} },
Authentication: { async checkBoardAdmin() {
if (deny) throw Object.assign(new Error('Forbidden'), { statusCode: 403 });
} },
ReactiveCache: { async getIntegration(selector) {
lookups.push(selector);
if (lookupError) throw lookupError;
return integrations.find(i => i._id === selector._id && i.boardId === selector.boardId);
} },
Integrations: { direct: { async updateAsync(...args) { calls.push(args); return 1; } } },
Integrations: { direct: {
async updateAsync(...args) { calls.push(args); return 1; },
async removeAsync(...args) {
calls.push(args);
if (removeError) throw removeError;
return 1;
},
} },
normalizeDeliverySettings: x => x,
publicErrorData,
sendJsonResult(res, result) { res.responses.push(JSON.parse(JSON.stringify(result))); },
};
vm.runInNewContext(source.slice(source.indexOf('WebApp.handlers.get(')), context);
return {
calls,
calls, lookups,
async call(params, body) {
const res = { responses: [] };
await handler({ userId: 'u', params, body }, res);
assert.equal(res.responses.length, 1, 'respond exactly once');
return res.responses[0];
},
};
}
const deleteUrl = '/api/boards/:boardId/integrations/:intId';
test('DELETE answers 404 without removing anything for missing, foreign and repeated IDs', async () => {
const integrations = [{ _id: 'i1', boardId: 'b1' }, { _id: 'i2', boardId: 'b2' }];
const r = run(deleteUrl, 'delete', integrations);
for (const params of [{ boardId: 'b1', intId: 'missing' }, { boardId: 'b1', intId: 'i2' }]) {
assert.deepEqual(await r.call(params), { code: 404, data: { error: 'Integration not found' } });
}
assert.equal(r.calls.length, 0);
assert.deepEqual(await r.call({ boardId: 'b1', intId: 'i1' }), { code: 200, data: { _id: 'i1' } });
assert.deepEqual(JSON.parse(JSON.stringify(r.calls)), [[{ _id: 'i1', boardId: 'b1' }]]);
integrations.shift();
assert.deepEqual(await r.call({ boardId: 'b1', intId: 'i1' }), { code: 404, data: { error: 'Integration not found' } });
assert.equal(r.calls.length, 1);
});
test('DELETE checks authorization before looking up existing or missing integrations', async () => {
const r = run(deleteUrl, 'delete', [{ _id: 'i1', boardId: 'b1' }], { deny: true });
for (const intId of ['i1', 'missing']) {
assert.equal((await r.call({ boardId: 'b1', intId })).code, 403);
}
assert.equal(r.lookups.length, 0);
assert.equal(r.calls.length, 0);
});
for (const operation of ['lookup', 'remove']) {
test(`DELETE preserves server errors from ${operation} instead of reporting 404 or success`, async () => {
const r = run(deleteUrl, 'delete', [{ _id: 'i1', boardId: 'b1' }], {
[`${operation}Error`]: new Error('database unavailable'),
});
assert.equal((await r.call({ boardId: 'b1', intId: 'i1' })).code, 500);
assert.equal(r.calls.length, operation === 'lookup' ? 0 : 1);
});
}
for (const verb of ['post', 'delete']) {
test(`${verb.toUpperCase()} .../activities answers 404 and writes nothing for a missing or foreign integration`, async () => {
const url = '/api/boards/:boardId/integrations/:intId/activities';
+108
View File
@@ -0,0 +1,108 @@
'use strict';
const assert = require('node:assert/strict');
const fs = require('node:fs');
const path = require('node:path');
const vm = require('node:vm');
const { test } = require('node:test');
const { publicErrorData } = require('../server/lib/apiResponseHelpers');
const source = fs.readFileSync(path.join(__dirname, '../server/models/users.js'), 'utf8');
const start = source.indexOf("WebApp.handlers.put('/api/users/:userId'");
const end = source.indexOf("WebApp.handlers.post('/api/boards/:boardId/members/:userId/add'", start);
assert.ok(start >= 0 && end > start);
const plain = value => JSON.parse(JSON.stringify(value));
function fixture({ missing = false, denied = false, boards = [] } = {}) {
const calls = [];
let handler;
vm.runInNewContext(source.slice(start, end), {
WebApp: { handlers: { put(route, callback) { handler = callback; } } },
Authentication: { async checkUserId(userId) {
calls.push(['authorize', userId]);
if (denied) throw Object.assign(new Error('Forbidden'), { statusCode: 403 });
} },
ReactiveCache: {
async getUser() { calls.push(['user']); return missing ? null : { _id: 'target' }; },
async getBoards(selector, options) {
calls.push(['boards', plain(selector), plain(options)]);
// Require both criteria on ONE member. The live suite exercises this
// query against MongoDB with a different administrator on the board.
assert.deepEqual(plain(selector), { members: { $elemMatch: { userId: 'target', isAdmin: true } } });
return boards;
},
},
publicErrorData,
sendJsonResult(res, result) { res.responses.push(plain(result)); },
});
return { calls, async put() {
const res = { responses: [] };
await handler({ userId: 'admin', params: { userId: 'target' }, body: { action: 'takeOwnership' } }, res);
assert.equal(res.responses.length, 1);
return res.responses[0];
} };
}
test('ownership selection binds the user and administrator flag to the same member', async () => {
const f = fixture();
assert.deepEqual(await f.put(), { code: 200, data: [] });
assert.deepEqual(f.calls, [
['authorize', 'admin'], ['user'],
['boards', { members: { $elemMatch: { userId: 'target', isAdmin: true } } }, { sort: { sort: 1 } }],
]);
});
test('selected boards retain the existing ownership helper and response format', async () => {
const calls = [];
const board = {
_id: 'owned', title: 'Owned board',
hasMember(id) { calls.push(['hasMember', id]); return false; },
async changeOwnership(from, to) { calls.push(['changeOwnership', from, to]); },
};
const f = fixture({ boards: [board] });
assert.deepEqual(await f.put(), { code: 200, data: [{ _id: 'owned', title: 'Owned board' }] });
assert.deepEqual(calls, [['hasMember', 'admin'], ['changeOwnership', 'target', 'admin']]);
});
test('denied callers and missing users do not select boards', async () => {
for (const [options, code] of [[{ denied: true }, 403], [{ missing: true }, 404]]) {
const f = fixture(options);
assert.equal((await f.put()).code, code);
assert.ok(!f.calls.some(([name]) => name === 'boards'));
}
});
test('ownership changes finish before the response, and their failures are reported', async () => {
let finished = false;
const board = {
_id: 'owned', title: 'Owned board',
hasMember() { return false; },
async changeOwnership() { await new Promise(r => setImmediate(r)); finished = true; },
};
const f = fixture({ boards: [board] });
assert.equal((await f.put()).code, 200);
// The response used to be sent before the board update had run.
assert.equal(finished, true);
const failing = fixture({ boards: [{
_id: 'owned', title: 'Owned board', hasMember() { return false; },
async changeOwnership() { throw Object.assign(new Error('update failed'), { statusCode: 500 }); },
}] });
// Previously an unhandled rejection with a 200 reply; now a reported error.
assert.notEqual((await failing.put()).code, 200);
});
test('every board ownership change in the server code is awaited', () => {
const root = path.join(__dirname, '..');
for (const dir of ['server', 'models']) {
const walk = d => fs.readdirSync(d, { withFileTypes: true }).flatMap(e =>
e.isDirectory() ? walk(path.join(d, e.name)) : e.name.endsWith('.js') ? [path.join(d, e.name)] : []);
for (const file of walk(path.join(root, dir))) {
const text = fs.readFileSync(file, 'utf8');
for (const m of text.matchAll(/^.*\.changeOwnership\(.*$/gm)) {
if (/async\s+changeOwnership\(/.test(m[0])) continue;
assert.match(m[0], /\b(await|return)\s+\S*\.changeOwnership\(/, `${path.relative(root, file)}: ${m[0].trim()}`);
}
}
}
});
+27
View File
@@ -126,11 +126,38 @@ test('a secret is redacted unless asked for', { skip: !python }, () => {
const kept = path.join(t.dir, 'kept');
assert.equal(run({}, ['acme', '--repo', 'app', '--out', kept, '--include-secret-values'], t).status, 0);
assert.match(fs.readFileSync(path.join(runDir(kept, 'app'), 'secret-scanning', 'alert-3.json'), 'utf8'), /ghp_SHOULDNOTBEWRITTEN/);
// Kept secrets are readable by their owner only (CWE-312), and the redacted
// download keeps the ordinary mode.
if (process.platform !== 'win32') {
for (const f of ['alerts.json', 'alert-3.json']) {
assert.equal(fs.statSync(path.join(runDir(kept, 'app'), 'secret-scanning', f)).mode & 0o777, 0o600, f);
assert.notEqual(fs.statSync(path.join(app, 'secret-scanning', f)).mode & 0o077, 0, f + ' redacted');
}
}
// SUMMARY.md, which is never redacted, carries no field of a secret
// scanning alert - not the secret, and not its type either (CodeQL
// py/clear-text-storage-sensitive-data, code scanning alert #604).
for (const dir of [app, runDir(kept, 'app')]) {
const summary = fs.readFileSync(path.join(dir, 'SUMMARY.md'), 'utf8');
assert.match(summary, /\| secret-scanning \| 3 \| \| Secret scanning alert - details in secret-scanning\/ \|/);
assert.doesNotMatch(summary, /ghp_SHOULDNOTBEWRITTEN|GitHub Token|github_pat/);
}
} finally {
fs.rmSync(t.dir, { recursive: true, force: true });
}
});
test('negative: no secret scanning field is written to SUMMARY.md, anywhere', () => {
const src = fs.readFileSync(path.join(__dirname, '..', 'releases/sec/download_github_security.py'), 'utf8');
const branch = src.slice(src.indexOf('if kind == "secret-scanning":\n #'), src.indexOf('return alert.get("severity"'));
assert.ok(branch.length > 0, 'the secret scanning branch of describe()');
const code = branch.split('\n').filter(l => !/^\s*#/.test(l)).join('\n');
assert.doesNotMatch(code, /alert\.|alert\[|secret_/, 'describe() reads nothing from a secret scanning alert');
// Every write of a secret scanning download passes the private flag.
assert.match(src, /private = kind == "secret-scanning" and include_secrets/);
assert.equal((src.match(/write_json\(os\.path\.join\(kind_dir, [^)]*\), alerts?, private\)/g) || []).length, 2);
});
test('negative: a name that is not a GitHub name, no gh, or gh signed out, writes nothing', { skip: !python }, () => {
const t = setup();
try {
+16 -1
View File
@@ -816,6 +816,21 @@ test('a 32-bit target is built only by a 32-bit Node.js (negative: win32 from x6
}
const wf = read('.github/workflows/single-file.yml');
assert.match(wf, /contains\(fromJSON\('\["win32","i386","armv7","armhf","armv6"\]'\), matrix\.target\)/);
assert.match(wf, /wekan-\$\{VERSION\}-i386\.zip[\s\S]*?sha256sum -c "\$zip\.sha256sum"/, 'the i386 Node.js is checked before it builds anything');
// The 32-bit host is an i386 Node.js of the TARGET BUNDLE'S exact version,
// verified against its published SHA256 before it builds anything. v13.07
// borrowed the i386 bundle's own Node.js instead: i386 had resolved v26.13.1
// and 32-bit ARM v26.13.0, so armhf, armv6 and armv7 all stopped on the
// version check in build.mjs.
const host = wf.slice(wf.indexOf('id: host32'), wf.indexOf('- name: Build the single file'));
assert.match(host, /NODE_VERSION: \$\{\{ steps\.fetch\.outputs\.node_version \}\}/, 'the host version is the target bundle\'s');
assert.match(host, /resolve-node-source\.sh i386 "\$NODE_VERSION"/, 'resolved at that exact version');
assert.match(host, /sha256sum -c -/, 'the i386 Node.js is checked before it builds anything');
assert.match(host, /"\$host" -p process\.version\)"[\s\S]*?= "\$NODE_VERSION"/, 'and it reports that version');
assert.ok(host.indexOf('sha256sum -c -') < host.indexOf('"$host" -p process.version'), 'checked before it is run');
// Negative: the host is never taken from another platform's bundle, whose
// Node.js may be a different patch release of the same major.
assert.doesNotMatch(host, /wekan-\$\{VERSION\}-i386\.zip|gh release download/);
assert.doesNotMatch(host, /resolve-node-source\.sh i386 "?(2[0-9]|\$NODE_MAJOR)/, 'not the newest of a major');
assert.doesNotMatch(wf, /HOST_LIBS/);
assert.match(wf, /\$\{HOST_NODE:\+--host-node "\$HOST_NODE"\}/);
});
+41
View File
@@ -101,6 +101,47 @@ test('the credential itself is never printed', () => {
assert.ok(/echo "\$out"/.test(job), "only whoami's answer is");
});
test('every snapcraft install in the workflows is retried', () => {
// v13.07: snap-auth-check installed snapcraft once, the Snap Store timed out
// ("Client.Timeout exceeded while awaiting headers") and the whole release
// check failed, while the build job's install already retried five times.
const dir = path.join(__dirname, '..', '.github', 'workflows');
for (const f of fs.readdirSync(dir).filter(n => /\.ya?ml$/.test(n))) {
const lines = fs.readFileSync(path.join(dir, f), 'utf8').split('\n');
lines.forEach((line, i) => {
if (!/snap install snapcraft/.test(line) || /^\s*#/.test(line)) return;
const before = lines.slice(Math.max(0, i - 3), i).join('\n');
assert.ok(/for i in 1 2 3 4 5; do/.test(before),
`${f}:${i + 1} installs snapcraft without the five-attempt retry`);
});
}
assert.ok(/the Snap Store did not answer. That is the store, not SNAP_AUTH/.test(job),
'an outage is not reported as a credential problem');
});
test('every snapcraft install attempt is bounded, and the job has room for all five', () => {
// v13.09: the third attempt hung with no output until snap-auth-check's
// 10-minute timeout cancelled the job - the retry never got its turn.
const dir = path.join(__dirname, '..', '.github', 'workflows');
for (const f of fs.readdirSync(dir).filter(n => /\.ya?ml$/.test(n))) {
const lines = fs.readFileSync(path.join(dir, f), 'utf8').split('\n');
lines.forEach((line, i) => {
if (!/snap install snapcraft/.test(line) || /^\s*#/.test(line)) return;
const m = line.match(/timeout (\d+) sudo snap install snapcraft/);
assert.ok(m, `${f}:${i + 1} installs snapcraft without a time limit per attempt`);
const after = lines.slice(i + 1, i + 3).join('\n');
assert.ok(/snap abort --last=install/.test(after),
`${f}:${i + 1} retries without aborting the attempt that was cut short`);
});
}
// Negative: the job's own limit must not end the retry before five attempts.
const perAttempt = Number((job.match(/timeout (\d+) sudo snap install snapcraft/) || [])[1]);
const minutes = Number((job.match(/timeout-minutes: (\d+)/) || [])[1]);
assert.ok(perAttempt > 0 && minutes > 0, 'both limits are set');
assert.ok(minutes * 60 >= 5 * (perAttempt + 30) + 120,
`snap-auth-check's ${minutes} minutes cannot hold five ${perAttempt}-second attempts`);
});
test('a shell that keeps going long enough to classify the failure', () => {
// `set -e` around the whoami call would kill the step at the failure, before
// the case that says WHICH failure it was.