diff --git a/.dockerignore b/.dockerignore index daa14cbc6..1901f009e 100644 --- a/.dockerignore +++ b/.dockerignore @@ -28,6 +28,12 @@ playwright/.cache playwright.config.ts vitest.config.mts vitest.integration.config.mts +vitest.mutation.config.mts +# The unit setup imports tests/ (excluded above); the image never runs it and +# the build's type check must not see a half-present test harness. +vitest.setup.ts +stryker.config.mjs +stryker.floors.json tests tests/ e2e diff --git a/Dockerfile b/Dockerfile index dde3d43ba..ecc726d6a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -271,7 +271,7 @@ RUN mkdir -p /opt/prisma-cli && \ npm pkg set 'overrides.mysql2=^3.22.0' && \ npm pkg set 'overrides.@hono/node-server=^1.19.15' && \ npm pkg set 'overrides.valibot=^1.4.2' && \ - npm install --omit=dev prisma@7.10.0 @prisma/engines@7.10.0 tsx@4.23.1 dotenv@18.0.6 && \ + npm install --omit=dev prisma@7.10.1 @prisma/engines@7.10.1 tsx@4.23.1 dotenv@18.0.8 && \ ln -sfn /opt/prisma-cli/node_modules/.bin/tsx /usr/local/bin/healthlog-tsx && \ ln -sfn /opt/prisma-cli/node_modules/dotenv /app/node_modules/dotenv && \ ln -sfn /opt/prisma-cli/node_modules/prisma /app/node_modules/prisma && \ diff --git a/docs/api/openapi.yaml b/docs/api/openapi.yaml index 6cc0471ec..a3bddb974 100644 --- a/docs/api/openapi.yaml +++ b/docs/api/openapi.yaml @@ -90,8 +90,10 @@ paths: $ref: "#/components/schemas/ErrorEnvelope" "403": &a2 description: >- - Cycle tracking is not enabled for this account (errorCode `cycle.disabled`). Resolved against the RECORD - being read, not the caller: a delegate inside somebody else's record sees the owner's setting. + `meta.errorCode` = `auth.scope.insufficient` when a Bearer token's scope does not reach this route; the + generic auth gate answers it before the route's own checks run. Otherwise: Cycle tracking is not enabled for + this account (errorCode `cycle.disabled`). Resolved against the RECORD being read, not the caller: a + delegate inside somebody else's record sees the owner's setting. Refused: this request named a record the caller may not act on (`meta.errorCode` = `sharing.access.denied`). @@ -259,8 +261,10 @@ paths: $ref: "#/components/schemas/CycleCustomSymptomUpdatedEnvelope" "401": *a1 "403": &a3 - description: "Cycle tracking is not enabled for this account (errorCode `cycle.disabled`). Resolved against the RECORD - being read, not the caller: a delegate inside somebody else's record sees the owner's setting." + description: "`meta.errorCode` = `auth.scope.insufficient` when a Bearer token's scope does not reach this route; the + generic auth gate answers it before the route's own checks run. Otherwise: Cycle tracking is not enabled for + this account (errorCode `cycle.disabled`). Resolved against the RECORD being read, not the caller: a + delegate inside somebody else's record sees the owner's setting." content: application/json: schema: @@ -395,7 +399,7 @@ paths: "403": *a2 "409": *a7 "422": *a4 - "429": &a12 + "429": &a16 description: "Rate limit exceeded. This route shares one per-account bucket with every other authenticated write — `record-write:`, 300 requests per 60 seconds — keyed on the ACTING account, so a delegate burns their own allowance rather than the record owner's. Only the routes that meter themselves with a dedicated @@ -736,6 +740,15 @@ paths: schema: $ref: "#/components/schemas/CyclePrefsGetEnvelope" "401": *a1 + "403": &a9 + description: "The credential is valid but its scope does not reach this operation. `meta.errorCode` = + `auth.scope.insufficient`: a narrow Bearer token on a route that names no scope it holds, or a + connector-bound token on a write. Retrying with the same token will not help; use a session, or a token + minted with the scope the route names." + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorEnvelope" "422": *a4 "429": *a5 patch: @@ -757,7 +770,7 @@ paths: application/json: schema: $ref: "#/components/schemas/CyclePrefsPatchEnvelope" - "400": &a14 + "400": &a18 description: The request body is not parseable JSON. Nothing was read and nothing was written. `meta.errorCode` names the surface that refused it (`.body.invalid_json`). This is a client-side serialisation fault, not a validation failure — a body that parses but fails the schema is the 422 beside this. @@ -766,6 +779,7 @@ paths: schema: $ref: "#/components/schemas/ErrorEnvelope" "401": *a1 + "403": *a9 "422": *a4 "429": *a5 /api/measurement-categories: @@ -785,6 +799,7 @@ paths: schema: $ref: "#/components/schemas/MeasurementCategoriesEnvelope" "401": *a1 + "403": *a9 "422": *a4 "429": *a5 /api/meta/capabilities: @@ -805,6 +820,7 @@ paths: schema: $ref: "#/components/schemas/CapabilitiesEnvelope" "401": *a1 + "403": *a9 "422": *a4 "429": *a5 /api/version: @@ -877,7 +893,9 @@ paths: $ref: "#/components/schemas/AssistantFlagsEnvelope" "401": *a1 "403": - description: A selector header was attached to an actor surface (`meta.errorCode` = `sharing.not_permitted`). + description: "`meta.errorCode` = `auth.scope.insufficient` when a Bearer token's scope does not reach this route; the + generic auth gate answers it before the route's own checks run. Otherwise: A selector header was attached to + an actor surface (`meta.errorCode` = `sharing.not_permitted`)." content: application/json: schema: @@ -916,6 +934,7 @@ paths: schema: $ref: "#/components/schemas/UpdateCheckEnvelope" "401": *a1 + "403": *a9 "422": *a4 "429": *a5 "502": @@ -964,16 +983,17 @@ paths: type: string format: binary "401": *a1 - "403": &a11 - description: "Refused: this request named a record the caller may not act on (`meta.errorCode` = - `sharing.access.denied`). Byte-identical for an account that does not exist, an account that granted - nothing, a grant whose sections do not reach this surface, and a read grant on a request that writes — the - response carries no reason, so it is not an account-enumeration oracle. The reason reaches the record - owner's activity feed and the operator's audit trail instead. On the cookie transport the same code answers - one further case: a session that has entered a shared record and sends a request without its record-context - assertion, which is a client older than the fence — leave the record and reload. A request that names no - record but the caller's own never reaches this response, so a client that never switches and never sends the - per-request account selector will not see it." + "403": &a15 + description: "`meta.errorCode` = `auth.scope.insufficient` when a Bearer token's scope does not reach this route; the + generic auth gate answers it before the route's own checks run. Otherwise: Refused: this request named a + record the caller may not act on (`meta.errorCode` = `sharing.access.denied`). Byte-identical for an account + that does not exist, an account that granted nothing, a grant whose sections do not reach this surface, and + a read grant on a request that writes — the response carries no reason, so it is not an account-enumeration + oracle. The reason reaches the record owner's activity feed and the operator's audit trail instead. On the + cookie transport the same code answers one further case: a session that has entered a shared record and + sends a request without its record-context assertion, which is a client older than the fence — leave the + record and reload. A request that names no record but the caller's own never reaches this response, so a + client that never switches and never sends the per-request account selector will not see it." content: application/json: schema: @@ -1011,6 +1031,7 @@ paths: schema: $ref: "#/components/schemas/ShareLinkCreated" "401": *a1 + "403": *a9 "422": *a4 "429": *a5 get: @@ -1027,6 +1048,7 @@ paths: schema: $ref: "#/components/schemas/ShareLinkList" "401": *a1 + "403": *a9 "422": *a4 "429": *a5 /api/share-links/{id}: @@ -1050,6 +1072,7 @@ paths: schema: $ref: "#/components/schemas/ShareLinkRevoked" "401": *a1 + "403": *a9 "404": description: Link not found (or owned by another user). content: @@ -1257,6 +1280,7 @@ paths: type: string format: binary "401": *a1 + "403": *a9 "422": *a4 "429": *a5 /api/fhir/Patient: @@ -1285,9 +1309,38 @@ paths: schema: type: string format: binary + "400": &a10 + description: A search parameter the server cannot honour, such as an `_offset` above 9007199254740991. The body is an + `OperationOutcome` (`issue[0].code` = `invalid`, the reason in `diagnostics`) served as + `application/fhir+json`. + content: + application/fhir+json: + schema: + type: string + format: binary "401": *a1 + "403": &a11 + description: "`meta.errorCode` = `auth.scope.insufficient` when a Bearer token's scope does not reach this route; the + generic auth gate answers it before the route's own checks run. Otherwise: Module disabled for this account: + the user (or operator) has the module turned off. `meta.errorCode` = `module.disabled` and `meta.module` + carries the disabled module key (e.g. \"sleep\"). Returned even for a valid Bearer token. Clients hide the + whole module surface end-to-end rather than retry." + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorEnvelope" "422": *a4 - "429": *a5 + "429": &a12 + description: "Rate limit exceeded: the per-account `fhir:` bucket, 120 requests per hour, shared by every + `/api/fhir/*` data route. The body is an `OperationOutcome` (`issue[0].code` = `throttled`) served as + `application/fhir+json`, not the JSON error envelope. The `Retry-After`, `X-RateLimit-Limit`, + `X-RateLimit-Remaining` and `X-RateLimit-Reset` headers describe the bucket; back off on `Retry-After`." + headers: *a8 + content: + application/fhir+json: + schema: + type: string + format: binary /api/fhir/Observation: get: tags: @@ -1331,9 +1384,11 @@ paths: schema: type: string format: binary + "400": *a10 "401": *a1 + "403": *a11 "422": *a4 - "429": *a5 + "429": *a12 /api/fhir/MedicationStatement: get: tags: @@ -1360,9 +1415,11 @@ paths: schema: type: string format: binary + "400": *a10 "401": *a1 + "403": *a11 "422": *a4 - "429": *a5 + "429": *a12 /api/fhir/MedicationAdministration: get: tags: @@ -1389,9 +1446,11 @@ paths: schema: type: string format: binary + "400": *a10 "401": *a1 + "403": *a11 "422": *a4 - "429": *a5 + "429": *a12 /api/fhir/Coverage: get: tags: @@ -1419,9 +1478,11 @@ paths: schema: type: string format: binary + "400": *a10 "401": *a1 + "403": *a11 "422": *a4 - "429": *a5 + "429": *a12 /api/fhir/Patient/{id}: get: tags: @@ -1445,6 +1506,7 @@ paths: type: string format: binary "401": *a1 + "403": *a11 "404": description: OperationOutcome (application/fhir+json). content: @@ -1453,7 +1515,7 @@ paths: type: string format: binary "422": *a4 - "429": *a5 + "429": *a12 /api/fhir/Coverage/{id}: get: tags: @@ -1477,6 +1539,7 @@ paths: type: string format: binary "401": *a1 + "403": *a11 "404": description: OperationOutcome (application/fhir+json). content: @@ -1485,7 +1548,7 @@ paths: type: string format: binary "422": *a4 - "429": *a5 + "429": *a12 /api/fhir/Observation/{id}: get: tags: @@ -1510,6 +1573,7 @@ paths: type: string format: binary "401": *a1 + "403": *a11 "404": description: OperationOutcome (application/fhir+json). content: @@ -1518,7 +1582,7 @@ paths: type: string format: binary "422": *a4 - "429": *a5 + "429": *a12 /api/fhir/MedicationStatement/{id}: get: tags: @@ -1542,6 +1606,7 @@ paths: type: string format: binary "401": *a1 + "403": *a11 "404": description: OperationOutcome (application/fhir+json). content: @@ -1550,7 +1615,7 @@ paths: type: string format: binary "422": *a4 - "429": *a5 + "429": *a12 /api/fhir/MedicationAdministration/{id}: get: tags: @@ -1574,6 +1639,7 @@ paths: type: string format: binary "401": *a1 + "403": *a11 "404": description: OperationOutcome (application/fhir+json). content: @@ -1582,7 +1648,7 @@ paths: type: string format: binary "422": *a4 - "429": *a5 + "429": *a12 /api/fhir/Patient/$everything: get: tags: @@ -1616,9 +1682,11 @@ paths: schema: type: string format: binary + "400": *a10 "401": *a1 + "403": *a11 "422": *a4 - "429": *a5 + "429": *a12 /api/export: get: tags: @@ -1676,6 +1744,7 @@ paths: schema: type: string "401": *a1 + "403": *a9 "422": description: "`format` or `type` carried a value outside its enum. The message names the accepted set; nothing is exported. Note that this route validates by hand rather than through Zod, so the response is the @@ -1719,6 +1788,7 @@ paths: schema: $ref: "#/components/schemas/FullBackupPayload" "401": *a1 + "403": *a9 "429": description: More than 10 exports in the trailing hour for this account (`export:`, shared across the export family). @@ -1779,6 +1849,7 @@ paths: schema: type: string "401": *a1 + "403": *a9 "429": description: More than 10 exports in the trailing hour for this account. content: @@ -1845,6 +1916,7 @@ paths: schema: type: string "401": *a1 + "403": *a9 "404": description: "`medicationId` named a medication this account does not hold. An unknown id and another account's id are indistinguishable." @@ -1900,6 +1972,7 @@ paths: schema: type: string "401": *a1 + "403": *a9 "429": description: More than 10 exports in the trailing hour for this account. content: @@ -1938,8 +2011,10 @@ paths: "401": *a1 "403": description: >- - The Bearer token carries a narrow scope. The encrypted archive needs a cookie session or a full-access - token; the refusal happens before any second-factor proof is looked at. + `meta.errorCode` = `auth.scope.insufficient` when a Bearer token's scope does not reach this route; the + generic auth gate answers it before the route's own checks run. Otherwise: The Bearer token carries a narrow + scope. The encrypted archive needs a cookie session or a full-access token; the refusal happens before any + second-factor proof is looked at. Refused: the request was made while the session is acting on another account (`meta.errorCode` = @@ -2135,7 +2210,7 @@ paths: application/json: schema: $ref: "#/components/schemas/ErrorEnvelope" - "409": &a9 + "409": &a13 description: 'Refused before anything was checked or spent: the request stated `clientTransport.protocol: "http"` on a non-loopback host while this server issues `Secure` session cookies, so the browser would drop the session. `meta.errorCode` = `auth.session.insecure_transport`, `meta.sessionCookieSecure` = `true`. No password was @@ -2170,7 +2245,8 @@ paths: schema: $ref: "#/components/schemas/MfaVerifyResponse" "401": *a1 - "409": *a9 + "403": *a9 + "409": *a13 "422": *a4 "429": *a5 /api/auth/oidc/native/token: @@ -2203,6 +2279,7 @@ paths: schema: $ref: "#/components/schemas/OidcNativeTokenResponse" "401": *a1 + "403": *a9 "422": *a4 "429": *a5 /api/auth/native/login: @@ -2276,6 +2353,7 @@ paths: schema: $ref: "#/components/schemas/NativeHandoffTokenResponse" "401": *a1 + "403": *a9 "422": *a4 "429": *a5 /api/auth/step-up/options: @@ -2302,6 +2380,7 @@ paths: schema: $ref: "#/components/schemas/StepUpOptionsEnvelope" "401": *a1 + "403": *a9 "409": description: No passkey registered on this account. content: @@ -2370,6 +2449,7 @@ paths: schema: $ref: "#/components/schemas/StepUpMintEnvelope" "401": *a1 + "403": *a9 "422": *a4 "429": *a5 /api/auth/reproof: @@ -2414,6 +2494,7 @@ paths: schema: $ref: "#/components/schemas/ReproofEnvelope" "401": *a1 + "403": *a9 "422": *a4 "429": *a5 get: @@ -2432,6 +2513,7 @@ paths: schema: $ref: "#/components/schemas/ReproofStatusEnvelope" "401": *a1 + "403": *a9 "422": *a4 "429": *a5 /api/auth/me/mfa/totp/setup: @@ -2471,13 +2553,14 @@ paths: application/json: schema: $ref: "#/components/schemas/TotpSetupEnvelope" - "401": &a10 + "401": &a14 description: No session or elevation; or, on the cookie path, `auth.reproof.required` (no recent sign-in and no proof in the body) or `auth.reproof.failed` (the proof did not verify). content: application/json: schema: $ref: "#/components/schemas/ErrorEnvelope" + "403": *a9 "409": description: A second factor is already active on this account. content: @@ -2514,6 +2597,7 @@ paths: schema: $ref: "#/components/schemas/TotpConfirmEnvelope" "401": *a1 + "403": *a9 "409": description: A second factor is already active, or enrollment was never started (no pending secret to confirm). content: @@ -2544,6 +2628,7 @@ paths: schema: $ref: "#/components/schemas/MfaDisableEnvelope" "401": *a1 + "403": *a9 "422": *a4 "429": *a5 /api/auth/me/mfa/recovery-codes/regenerate: @@ -2562,6 +2647,7 @@ paths: schema: $ref: "#/components/schemas/MfaRecoveryRegenEnvelope" "401": *a1 + "403": *a9 "422": *a4 "429": *a5 /api/auth/me/mfa: @@ -2581,6 +2667,7 @@ paths: schema: $ref: "#/components/schemas/MfaStatusEnvelope" "401": *a1 + "403": *a9 "422": *a4 "429": *a5 /api/auth/me/mfa/webauthn/register/options: @@ -2619,7 +2706,8 @@ paths: application/json: schema: $ref: "#/components/schemas/MfaWebauthnRegisterOptionsEnvelope" - "401": *a10 + "401": *a14 + "403": *a9 "422": *a4 "429": *a5 /api/auth/me/mfa/webauthn/register/verify: @@ -2650,6 +2738,7 @@ paths: schema: $ref: "#/components/schemas/ErrorEnvelope" "401": *a1 + "403": *a9 "422": *a4 "429": *a5 /api/auth/me/mfa/webauthn/{id}: @@ -2680,6 +2769,7 @@ paths: schema: $ref: "#/components/schemas/MfaWebauthnRenameEnvelope" "401": *a1 + "403": *a9 "404": description: No such security key for this account. content: @@ -2708,6 +2798,7 @@ paths: schema: $ref: "#/components/schemas/MfaWebauthnRemoveEnvelope" "401": *a1 + "403": *a9 "404": description: No such security key for this account. content: @@ -2737,6 +2828,7 @@ paths: schema: $ref: "#/components/schemas/MfaWebauthnLoginOptionsEnvelope" "401": *a1 + "403": *a9 "409": description: No security key is registered on the account — fall back to the TOTP / recovery-code challenge. content: @@ -2767,7 +2859,8 @@ paths: schema: $ref: "#/components/schemas/MfaWebauthnVerifyResponse" "401": *a1 - "409": *a9 + "403": *a9 + "409": *a13 "422": *a4 "429": *a5 /api/auth/passkey/login-verify: @@ -2852,6 +2945,7 @@ paths: - $ref: "#/components/schemas/RefreshResponse" - $ref: "#/components/schemas/RefreshRevokeResponse" "401": *a1 + "403": *a9 "422": *a4 "429": *a5 /api/auth/passkeys: @@ -2870,6 +2964,7 @@ paths: schema: $ref: "#/components/schemas/PasskeyListEnvelope" "401": *a1 + "403": *a9 "422": *a4 "429": *a5 /api/auth/me/sessions: @@ -2888,6 +2983,7 @@ paths: schema: $ref: "#/components/schemas/SessionListEnvelope" "401": *a1 + "403": *a9 "422": *a4 "429": *a5 delete: @@ -2920,6 +3016,7 @@ paths: schema: $ref: "#/components/schemas/SignOutEverywhereEnvelope" "401": *a1 + "403": *a9 "422": *a4 "429": *a5 /api/auth/me/sessions/{id}: @@ -2943,6 +3040,7 @@ paths: schema: $ref: "#/components/schemas/SessionRevokeEnvelope" "401": *a1 + "403": *a9 "404": description: Session not found or not owned by the caller. content: @@ -2966,6 +3064,7 @@ paths: schema: $ref: "#/components/schemas/TrustedDeviceListEnvelope" "401": *a1 + "403": *a9 "422": *a4 "429": *a5 delete: @@ -2981,6 +3080,7 @@ paths: schema: $ref: "#/components/schemas/TrustedDeviceRevokeAllEnvelope" "401": *a1 + "403": *a9 "422": *a4 "429": *a5 /api/auth/me/trusted-devices/{id}: @@ -3003,6 +3103,7 @@ paths: schema: $ref: "#/components/schemas/TrustedDeviceRevokeEnvelope" "401": *a1 + "403": *a9 "404": description: Trusted device not found or not owned by the caller. content: @@ -3036,6 +3137,7 @@ paths: schema: $ref: "#/components/schemas/SecurityActivityEnvelope" "401": *a1 + "403": *a9 "422": *a4 "429": *a5 /api/auth/logout: @@ -3124,7 +3226,7 @@ paths: application/json: schema: $ref: "#/components/schemas/ErrorEnvelope" - "409": *a9 + "409": *a13 "429": *a5 /api/auth/passkeys/{id}: patch: @@ -3166,6 +3268,7 @@ paths: schema: $ref: "#/components/schemas/PasskeyRenameEnvelope" "401": *a1 + "403": *a9 "404": description: No such passkey for this account. content: @@ -3228,6 +3331,7 @@ paths: application/json: schema: $ref: "#/components/schemas/ErrorEnvelope" + "403": *a9 "404": description: No such passkey for this account. content: @@ -3264,6 +3368,7 @@ paths: schema: $ref: "#/components/schemas/ApiTokenListEnvelope" "401": *a1 + "403": *a9 "422": *a4 "429": *a5 /api/tokens/measurements: @@ -3322,8 +3427,10 @@ paths: $ref: "#/components/schemas/ErrorEnvelope" "401": *a1 "403": - description: The operator has switched the API off instance-wide (`AppSettings.apiGlobal`). Nothing was minted. A Bearer - caller does not reach this arm — it is refused 401 before the switch is read. + description: "`meta.errorCode` = `auth.scope.insufficient` when a Bearer token's scope does not reach this route; the + generic auth gate answers it before the route's own checks run. Otherwise: The operator has switched the API + off instance-wide (`AppSettings.apiGlobal`). Nothing was minted. A Bearer caller does not reach this arm — + it is refused 401 before the switch is read." content: application/json: schema: @@ -3412,8 +3519,9 @@ paths: $ref: "#/components/schemas/ErrorEnvelope" "401": *a1 "403": - description: The operator has switched the API off instance-wide. Nothing was minted. A Bearer caller is refused 401 - before the switch is read. + description: "`meta.errorCode` = `auth.scope.insufficient` when a Bearer token's scope does not reach this route; the + generic auth gate answers it before the route's own checks run. Otherwise: The operator has switched the API + off instance-wide. Nothing was minted. A Bearer caller is refused 401 before the switch is read." content: application/json: schema: @@ -3499,8 +3607,9 @@ paths: $ref: "#/components/schemas/ErrorEnvelope" "401": *a1 "403": - description: The operator has switched the API off instance-wide. Nothing was minted. A Bearer caller is refused 401 - before the switch is read. + description: "`meta.errorCode` = `auth.scope.insufficient` when a Bearer token's scope does not reach this route; the + generic auth gate answers it before the route's own checks run. Otherwise: The operator has switched the API + off instance-wide. Nothing was minted. A Bearer caller is refused 401 before the switch is read." content: application/json: schema: @@ -3564,10 +3673,11 @@ paths: $ref: "#/components/schemas/ApiTokenRevokeEnvelope" "401": *a1 "403": - description: The operator has switched the API off instance-wide (`AppSettings.apiGlobal`). `meta.errorCode` is the - status default `http.forbidden`; nothing was revoked. The sibling list is deliberately not gated this way — - a token stays live while the switch is off, so its owner can still see it here even though this call will - not kill it. + description: "`meta.errorCode` = `auth.scope.insufficient` when a Bearer token's scope does not reach this route; the + generic auth gate answers it before the route's own checks run. Otherwise: The operator has switched the API + off instance-wide (`AppSettings.apiGlobal`). `meta.errorCode` is the status default `http.forbidden`; + nothing was revoked. The sibling list is deliberately not gated this way — a token stays live while the + switch is off, so its owner can still see it here even though this call will not kill it." content: application/json: schema: @@ -3685,6 +3795,7 @@ paths: application/json: schema: $ref: "#/components/schemas/ErrorEnvelope" + "403": *a9 "422": *a4 "429": *a5 /api/auth/profile: @@ -3721,6 +3832,7 @@ paths: schema: $ref: "#/components/schemas/AuthProfileUpdateEnvelope" "401": *a1 + "403": *a9 "409": description: The requested email belongs to another account (`meta.errorCode` = `profile.update.emailInUse`). Nothing was written. @@ -3736,7 +3848,7 @@ paths: application/json: schema: $ref: "#/components/schemas/ErrorEnvelope" - "429": &a16 + "429": &a20 description: "Too many email-address changes, and the request asked for nothing else. The address change shares one per-account bucket across both profile routes — `profile-email:`, 10 requests per 3600 seconds — keyed on the ACTING account, so an account cannot collect a fresh allowance by probing a different address. @@ -3933,6 +4045,7 @@ paths: application/json: schema: $ref: "#/components/schemas/ErrorEnvelope" + "403": *a9 "409": description: The re-proof arm asked for a method the account has no credential for — fall back to another factor. content: @@ -3993,6 +4106,7 @@ paths: application/json: schema: $ref: "#/components/schemas/ErrorEnvelope" + "403": *a9 "422": *a4 "429": *a5 /api/auth/codex/device-start: @@ -4020,6 +4134,7 @@ paths: schema: $ref: "#/components/schemas/CodexDeviceStartEnvelope" "401": *a1 + "403": *a9 "422": *a4 "429": *a5 /api/auth/codex/device-poll: @@ -4050,6 +4165,7 @@ paths: schema: $ref: "#/components/schemas/ErrorEnvelope" "401": *a1 + "403": *a9 "422": *a4 "429": *a5 "502": @@ -4080,6 +4196,7 @@ paths: schema: $ref: "#/components/schemas/CodexDisconnectEnvelope" "401": *a1 + "403": *a9 "422": *a4 "429": *a5 /api/sync/state: @@ -4098,6 +4215,7 @@ paths: schema: $ref: "#/components/schemas/SyncStateEnvelope" "401": *a1 + "403": *a9 "422": *a4 "429": *a5 /api/sync/changes: @@ -4137,6 +4255,7 @@ paths: schema: $ref: "#/components/schemas/SyncChangesEnvelope" "401": *a1 + "403": *a9 "422": *a4 "429": *a5 /api/measurements/series-batch: @@ -4186,7 +4305,7 @@ paths: schema: $ref: "#/components/schemas/GetMeasurementsSeriesBatchResponse" "401": *a1 - "403": *a11 + "403": *a15 "422": *a4 "429": *a5 /api/measurements/series: @@ -4241,7 +4360,7 @@ paths: schema: $ref: "#/components/schemas/GetMeasurementsSeriesResponse" "401": *a1 - "403": *a11 + "403": *a15 "422": *a4 "429": *a5 /api/measurements: @@ -4462,7 +4581,7 @@ paths: schema: $ref: "#/components/schemas/ListMeasurementsResponse" "401": *a1 - "403": *a11 + "403": *a15 "422": *a4 "429": *a5 post: @@ -4518,7 +4637,7 @@ paths: schema: $ref: "#/components/schemas/CreateMeasurementResponse" "401": *a1 - "403": *a11 + "403": *a15 "409": description: A measurement with this data already exists — the `(userId, type, source, externalId)` dedup index rejected the write. Never for a named sleep-stage body, which answers a re-post with 200 instead. @@ -4534,7 +4653,7 @@ paths: application/json: schema: $ref: "#/components/schemas/ErrorEnvelope" - "429": *a12 + "429": *a16 /api/measurements/{id}: get: tags: @@ -4555,7 +4674,7 @@ paths: schema: $ref: "#/components/schemas/GetMeasurementResponse" "401": *a1 - "403": *a11 + "403": *a15 "404": description: Measurement not found (or owned by another user). content: @@ -4603,7 +4722,7 @@ paths: schema: $ref: "#/components/schemas/UpdateMeasurementResponse" "401": *a1 - "403": *a11 + "403": *a15 "404": description: Measurement not found (or owned by another user). content: @@ -4639,7 +4758,7 @@ paths: schema: $ref: "#/components/schemas/DeleteMeasurementResponse" "401": *a1 - "403": *a11 + "403": *a15 "404": description: Measurement not found (or owned by another user). content: @@ -4683,6 +4802,7 @@ paths: schema: $ref: "#/components/schemas/BatchMeasurementsResponse" "401": *a1 + "403": *a9 "409": *a7 "422": description: "The batch exceeded the 500-entry limit (`measurement.batch.too_large`), failed validation @@ -4723,6 +4843,7 @@ paths: schema: $ref: "#/components/schemas/MeasurementsDeleteByExternalIdsResponse" "401": *a1 + "403": *a9 "422": *a4 "429": *a5 /api/measurements/bulk-delete: @@ -4750,7 +4871,7 @@ paths: schema: $ref: "#/components/schemas/MeasurementsBulkDeleteResponse" "401": *a1 - "403": *a11 + "403": *a15 "409": *a7 "422": *a4 "429": *a5 @@ -4779,7 +4900,7 @@ paths: schema: $ref: "#/components/schemas/MeasurementsRestoreResponse" "401": *a1 - "403": *a11 + "403": *a15 "409": *a7 "422": *a4 "429": *a5 @@ -4812,11 +4933,13 @@ paths: schema: $ref: "#/components/schemas/SleepNightResponse" "401": *a1 - "403": &a13 + "403": &a17 description: >- - Module disabled for this account: the user (or operator) has the module turned off. `meta.errorCode` = - `module.disabled` and `meta.module` carries the disabled module key (e.g. "sleep"). Returned even for a - valid Bearer token. Clients hide the whole module surface end-to-end rather than retry. + `meta.errorCode` = `auth.scope.insufficient` when a Bearer token's scope does not reach this route; the + generic auth gate answers it before the route's own checks run. Otherwise: Module disabled for this account: + the user (or operator) has the module turned off. `meta.errorCode` = `module.disabled` and `meta.module` + carries the disabled module key (e.g. "sleep"). Returned even for a valid Bearer token. Clients hide the + whole module surface end-to-end rather than retry. Refused: this request named a record the caller may not act on (`meta.errorCode` = `sharing.access.denied`). @@ -4853,7 +4976,7 @@ paths: schema: $ref: "#/components/schemas/SleepRhythmResponse" "401": *a1 - "403": *a13 + "403": *a17 "422": *a4 "429": *a5 /api/workouts: @@ -4902,7 +5025,7 @@ paths: schema: $ref: "#/components/schemas/ListWorkoutsResponse" "401": *a1 - "403": *a13 + "403": *a17 "422": *a4 "429": *a5 /api/workouts/{id}: @@ -4938,14 +5061,7 @@ paths: schema: $ref: "#/components/schemas/GetWorkoutResponse" "401": *a1 - "403": &a19 - description: 'Module disabled for this account: the user (or operator) has the module turned off. `meta.errorCode` = - `module.disabled` and `meta.module` carries the disabled module key (e.g. "sleep"). Returned even for a - valid Bearer token. Clients hide the whole module surface end-to-end rather than retry.' - content: - application/json: - schema: - $ref: "#/components/schemas/ErrorEnvelope" + "403": *a11 "404": description: Workout not found (or owned by another user). content: @@ -4976,6 +5092,7 @@ paths: schema: $ref: "#/components/schemas/DeleteWorkoutResponse" "401": *a1 + "403": *a9 "404": description: Workout not found (or owned by another user). content: @@ -5038,6 +5155,7 @@ paths: schema: $ref: "#/components/schemas/ErrorEnvelope" "401": *a1 + "403": *a9 "409": *a7 "413": description: Request body exceeds the 5 MB ceiling. @@ -5095,7 +5213,7 @@ paths: schema: $ref: "#/components/schemas/ActivityCalendarEnvelope" "401": *a1 - "403": *a13 + "403": *a17 "422": *a4 "429": *a5 /api/activity/goals: @@ -5113,6 +5231,7 @@ paths: schema: $ref: "#/components/schemas/ActivityGoalsEnvelope" "401": *a1 + "403": *a9 "422": *a4 "429": *a5 patch: @@ -5135,8 +5254,9 @@ paths: schema: $ref: "#/components/schemas/ActivityGoalsPatchEnvelope" "401": *a1 + "403": *a9 "422": *a4 - "429": *a12 + "429": *a16 /api/measurements/provenance: get: tags: @@ -5261,7 +5381,7 @@ paths: schema: $ref: "#/components/schemas/DayProvenanceEnvelope" "401": *a1 - "403": *a11 + "403": *a15 "422": *a4 "429": *a5 /api/measurements/provenance/compare: @@ -5439,7 +5559,7 @@ paths: schema: $ref: "#/components/schemas/SourceComparisonEnvelope" "401": *a1 - "403": *a11 + "403": *a15 "422": *a4 "429": *a5 /api/devices: @@ -5463,6 +5583,7 @@ paths: schema: $ref: "#/components/schemas/DeviceRegisterResponse" "401": *a1 + "403": *a9 "409": description: Device or APNs token already registered to another user. content: @@ -5497,6 +5618,7 @@ paths: schema: $ref: "#/components/schemas/DeviceRevokeEnvelope" "401": *a1 + "403": *a9 "404": description: No such device for this caller — also the answer for one already revoked, so a retry after a successful revoke is a 404 rather than an idempotent 200. @@ -5549,7 +5671,7 @@ paths: schema: $ref: "#/components/schemas/MedicationIntakeAggregateEnvelope" "401": *a1 - "403": *a11 + "403": *a15 "422": description: The query failed validation — a missing or unknown `scope`, or a `days` outside 1..365. Multi-issue envelope; an audit breadcrumb is filed under the RECORD as `medications.intake.list.validation-failed` with @@ -5639,9 +5761,11 @@ paths: "401": *a1 "403": description: >- - Refused: a delegate tried to snooze, or to change a dose the owner had already recorded as taken or skipped - (`meta.errorCode` = `sharing.not_permitted`). Both stay open to the owner, and the attempt is filed on the - owner's activity trail. + `meta.errorCode` = `auth.scope.insufficient` when a Bearer token's scope does not reach this route; the + generic auth gate answers it before the route's own checks run. Otherwise: Refused: a delegate tried to + snooze, or to change a dose the owner had already recorded as taken or skipped (`meta.errorCode` = + `sharing.not_permitted`). Both stay open to the owner, and the attempt is filed on the owner's activity + trail. Refused: this request named a record the caller may not act on (`meta.errorCode` = `sharing.access.denied`). @@ -5677,7 +5801,7 @@ paths: application/json: schema: $ref: "#/components/schemas/ErrorEnvelope" - "429": *a12 + "429": *a16 /api/medications/{id}/glp1: get: tags: @@ -5702,7 +5826,7 @@ paths: schema: $ref: "#/components/schemas/Glp1DetailResponseEnvelope" "401": *a1 - "403": *a11 + "403": *a15 "404": description: Medication not found (or owned by another account). content: @@ -5786,7 +5910,7 @@ paths: schema: $ref: "#/components/schemas/Glp1PostResponseEnvelope" "401": *a1 - "403": *a11 + "403": *a15 "404": description: Medication not found (or owned by another account). content: @@ -5821,7 +5945,9 @@ paths: $ref: "#/components/schemas/MedicationApiEndpointStateEnvelope" "401": *a1 "403": - description: The operator has disabled the API globally. + description: "`meta.errorCode` = `auth.scope.insufficient` when a Bearer token's scope does not reach this route; the + generic auth gate answers it before the route's own checks run. Otherwise: The operator has disabled the API + globally." content: application/json: schema: @@ -5875,7 +6001,9 @@ paths: $ref: "#/components/schemas/MedicationApiEndpointCreatedEnvelope" "401": *a1 "403": - description: The operator has disabled the API globally. + description: "`meta.errorCode` = `auth.scope.insufficient` when a Bearer token's scope does not reach this route; the + generic auth gate answers it before the route's own checks run. Otherwise: The operator has disabled the API + globally." content: application/json: schema: @@ -5921,7 +6049,7 @@ paths: schema: $ref: "#/components/schemas/ReminderPhaseConfigEnvelope" "401": *a1 - "403": *a11 + "403": *a15 "404": description: Medication not found (or owned by another account). content: @@ -5959,6 +6087,7 @@ paths: schema: $ref: "#/components/schemas/ReminderPhaseConfigSavedEnvelope" "401": *a1 + "403": *a9 "404": description: Medication not found (or owned by another account). content: @@ -5987,6 +6116,7 @@ paths: schema: $ref: "#/components/schemas/ReminderPhaseConfigResetEnvelope" "401": *a1 + "403": *a9 "404": description: Medication not found (or owned by another account). content: @@ -6037,7 +6167,7 @@ paths: schema: $ref: "#/components/schemas/ListMedicationSideEffectsEnvelope" "401": *a1 - "403": *a11 + "403": *a15 "404": description: Medication not found (or owned by another account). content: @@ -6076,7 +6206,7 @@ paths: schema: $ref: "#/components/schemas/CreateMedicationSideEffectEnvelope" "401": *a1 - "403": *a11 + "403": *a15 "404": description: Medication not found (or owned by another account). content: @@ -6121,7 +6251,7 @@ paths: schema: $ref: "#/components/schemas/DeleteMedicationSideEffectEnvelope" "401": *a1 - "403": *a11 + "403": *a15 "404": description: No such entry for this medication and this record. An unknown id, another medication's entry and another record's entry are indistinguishable. @@ -6169,7 +6299,7 @@ paths: schema: $ref: "#/components/schemas/UpdateMedicationIntakeEventEnvelope" "401": *a1 - "403": *a11 + "403": *a15 "404": description: No live event with that id under this medication and record. An unknown id and an already-tombstoned one are indistinguishable. @@ -6217,7 +6347,7 @@ paths: schema: $ref: "#/components/schemas/DeleteMedicationIntakeEventEnvelope" "401": *a1 - "403": *a11 + "403": *a15 "404": description: No event with that id under this medication and record. content: @@ -6260,7 +6390,7 @@ paths: schema: $ref: "#/components/schemas/BulkDeleteMedicationIntakeEnvelope" "401": *a1 - "403": *a11 + "403": *a15 "404": description: Medication not found (or owned by another account). content: @@ -6302,7 +6432,7 @@ paths: schema: $ref: "#/components/schemas/PurgeMedicationIntakeEnvelope" "401": *a1 - "403": *a11 + "403": *a15 "404": description: Medication not found (or owned by another account). content: @@ -6347,7 +6477,7 @@ paths: schema: $ref: "#/components/schemas/MedicationIntakeImportAcceptedEnvelope" "401": *a1 - "403": *a11 + "403": *a15 "404": description: Medication not found (or owned by another account). content: @@ -6407,6 +6537,7 @@ paths: schema: $ref: "#/components/schemas/BulkMedicationIntakeResponseEnvelope" "401": *a1 + "403": *a9 "409": *a7 "422": *a4 "429": *a5 @@ -6427,7 +6558,7 @@ paths: schema: $ref: "#/components/schemas/ListMedicationsResponse" "401": *a1 - "403": *a11 + "403": *a15 "422": *a4 "429": *a5 post: @@ -6459,7 +6590,7 @@ paths: schema: $ref: "#/components/schemas/CreateMedicationResponse" "401": *a1 - "403": *a11 + "403": *a15 "409": *a7 "422": *a4 "429": *a5 @@ -6479,7 +6610,7 @@ paths: schema: $ref: "#/components/schemas/ListMedicationCategoriesResponse" "401": *a1 - "403": *a11 + "403": *a15 "422": *a4 "429": *a5 post: @@ -6506,7 +6637,7 @@ paths: schema: $ref: "#/components/schemas/CreateMedicationCategoryResponse" "401": *a1 - "403": *a11 + "403": *a15 "409": *a7 "422": *a4 "429": *a5 @@ -6539,6 +6670,7 @@ paths: schema: $ref: "#/components/schemas/UpdateMedicationCategoryResponse" "401": *a1 + "403": *a9 "404": description: Not one of the record's own categories. content: @@ -6570,6 +6702,7 @@ paths: schema: $ref: "#/components/schemas/DeleteMedicationCategoryResponse" "401": *a1 + "403": *a9 "404": description: Not one of the record's own categories. content: @@ -6598,6 +6731,7 @@ paths: schema: $ref: "#/components/schemas/MedicationCoursesResponse" "401": *a1 + "403": *a9 "404": description: Medication not found (or another user's). content: @@ -6635,6 +6769,7 @@ paths: schema: $ref: "#/components/schemas/CreateMedicationCourseResponse" "401": *a1 + "403": *a9 "404": description: Medication not found (or another user's). content: @@ -6676,6 +6811,7 @@ paths: schema: $ref: "#/components/schemas/UpdateMedicationCourseResponse" "401": *a1 + "403": *a9 "404": description: Course not found (`medications.course.notFound`), or another user's. content: @@ -6709,6 +6845,7 @@ paths: schema: $ref: "#/components/schemas/DeleteMedicationCourseResponse" "401": *a1 + "403": *a9 "404": description: Course not found (`medications.course.notFound`), or another user's. content: @@ -6733,7 +6870,7 @@ paths: schema: $ref: "#/components/schemas/MedicationListLayoutResponse" "401": *a1 - "403": *a11 + "403": *a15 "422": *a4 "429": *a5 put: @@ -6759,6 +6896,7 @@ paths: schema: $ref: "#/components/schemas/MedicationListLayoutSaved" "401": *a1 + "403": *a9 "409": description: Medications layout changed since it was loaded (optimistic-concurrency conflict). No write happened. Re-read the resource, re-apply the user's change against the fresh state, and resend with the new token. @@ -6767,7 +6905,7 @@ paths: application/json: schema: $ref: "#/components/schemas/ErrorEnvelope" - "422": &a15 + "422": &a19 description: Request validation failed. When the body carried a `baseUpdatedAt` that could not be parsed as an ISO-8601 timestamp — including an explicit `null` — `meta.errorCode` = `invalid_base_updated_at` and nothing was written. Omit the key entirely for the unconditional write; do not send `null` for it. @@ -6789,6 +6927,7 @@ paths: schema: $ref: "#/components/schemas/MedicationListLayoutReset" "401": *a1 + "403": *a9 "422": *a4 "429": *a5 /api/medications/{id}: @@ -6812,7 +6951,7 @@ paths: schema: $ref: "#/components/schemas/GetMedicationResponse" "401": *a1 - "403": *a11 + "403": *a15 "404": description: Medication not found (or owned by another user). content: @@ -6853,7 +6992,7 @@ paths: schema: $ref: "#/components/schemas/UpdateMedicationResponse" "401": *a1 - "403": *a11 + "403": *a15 "404": description: Medication not found (or owned by another user). content: @@ -6882,6 +7021,7 @@ paths: schema: $ref: "#/components/schemas/DeleteMedicationResponse" "401": *a1 + "403": *a9 "404": description: Medication not found (or owned by another user). content: @@ -6926,7 +7066,7 @@ paths: schema: $ref: "#/components/schemas/CreateMedicationIntakeResponse" "401": *a1 - "403": *a11 + "403": *a15 "404": description: Medication not found (or owned by another user). content: @@ -6944,7 +7084,7 @@ paths: application/json: schema: $ref: "#/components/schemas/ErrorEnvelope" - "429": *a12 + "429": *a16 get: tags: - Medications @@ -7009,7 +7149,7 @@ paths: schema: $ref: "#/components/schemas/ListMedicationIntakeEventsResponse" "401": *a1 - "403": *a11 + "403": *a15 "404": description: Medication not found (or owned by another user). content: @@ -7042,7 +7182,7 @@ paths: schema: $ref: "#/components/schemas/ListMedicationInventoryResponse" "401": *a1 - "403": *a11 + "403": *a15 "404": description: Medication not found (or owned by another user). content: @@ -7078,7 +7218,7 @@ paths: schema: $ref: "#/components/schemas/CreateMedicationInventoryItemResponse" "401": *a1 - "403": *a11 + "403": *a15 "404": description: Medication not found (or owned by another user). content: @@ -7122,7 +7262,7 @@ paths: schema: $ref: "#/components/schemas/UpdateMedicationInventoryItemResponse" "401": *a1 - "403": *a11 + "403": *a15 "404": description: Inventory item not found (or owned by another user / medication). content: @@ -7157,7 +7297,7 @@ paths: schema: $ref: "#/components/schemas/DeleteMedicationInventoryItemResponse" "401": *a1 - "403": *a11 + "403": *a15 "404": description: Inventory item not found (or owned by another user / medication). content: @@ -7193,14 +7333,16 @@ paths: $ref: "#/components/schemas/MedicationExtractResponse" "401": *a1 "403": - description: "AI refusal: the `medicationExtract` capability is unavailable for this record. The envelope carries - `meta.errorCode`, `meta.capability` and `meta.reason` (one of `AiUnavailableReason`). - `assistant.disabled.documentAi` — the operator turned off reading documents, or every AI feature (the master - switch). `ai.record.notPermitted` — AI work is not admitted inside this record (a delegate acting in - somebody else's record). `consent.ai.required` — the provider this request would reach leaves the machine - and the record holds no active `ai_extraction` or `ai_full` receipt; only a self-hosted model is exempt. The - question is answered for the provider actually picked, so it can differ from the published capability when a - text-mode read takes a different provider than a vision read would." + description: "`meta.errorCode` = `auth.scope.insufficient` when a Bearer token's scope does not reach this route; the + generic auth gate answers it before the route's own checks run. Otherwise: AI refusal: the + `medicationExtract` capability is unavailable for this record. The envelope carries `meta.errorCode`, + `meta.capability` and `meta.reason` (one of `AiUnavailableReason`). `assistant.disabled.documentAi` — the + operator turned off reading documents, or every AI feature (the master switch). `ai.record.notPermitted` — + AI work is not admitted inside this record (a delegate acting in somebody else's record). + `consent.ai.required` — the provider this request would reach leaves the machine and the record holds no + active `ai_extraction` or `ai_full` receipt; only a self-hosted model is exempt. The question is answered + for the provider actually picked, so it can differ from the published capability when a text-mode read takes + a different provider than a vision read would." content: application/json: schema: @@ -7253,7 +7395,7 @@ paths: schema: $ref: "#/components/schemas/GetMedicationCadenceResponse" "401": *a1 - "403": *a11 + "403": *a15 "404": description: Medication not found (or owned by another user). content: @@ -7302,7 +7444,7 @@ paths: schema: $ref: "#/components/schemas/ListMedicationComplianceResponse" "401": *a1 - "403": *a11 + "403": *a15 "422": *a4 "429": *a5 /api/medications/{id}/compliance: @@ -7329,6 +7471,7 @@ paths: schema: $ref: "#/components/schemas/GetMedicationComplianceResponse" "401": *a1 + "403": *a9 "404": description: Medication not found (or owned by another user). content: @@ -7361,6 +7504,7 @@ paths: schema: $ref: "#/components/schemas/GetMedicationEfficacyResponse" "401": *a1 + "403": *a9 "404": description: Medication not found (or owned by another user). content: @@ -7397,6 +7541,7 @@ paths: schema: $ref: "#/components/schemas/SetMedicationEfficacyTargetResponse" "401": *a1 + "403": *a9 "404": description: Medication or biomarker not found. content: @@ -7453,6 +7598,7 @@ paths: schema: $ref: "#/components/schemas/GetDoseHistoryResponse" "401": *a1 + "403": *a9 "404": description: Medication not found (or owned by another user). content: @@ -7483,7 +7629,7 @@ paths: schema: $ref: "#/components/schemas/ListScheduleRevisionsResponse" "401": *a1 - "403": *a11 + "403": *a15 "404": description: Medication not found (or owned by another user). content: @@ -7521,7 +7667,7 @@ paths: schema: $ref: "#/components/schemas/CreateScheduleRevisionResponse" "401": *a1 - "403": *a11 + "403": *a15 "404": description: Medication not found (or owned by another user). content: @@ -7565,7 +7711,7 @@ paths: schema: $ref: "#/components/schemas/UpdateScheduleRevisionResponse" "401": *a1 - "403": *a11 + "403": *a15 "404": description: Medication or revision not found (or owned by another user). content: @@ -7606,7 +7752,7 @@ paths: schema: $ref: "#/components/schemas/DeleteScheduleRevisionResponse" "401": *a1 - "403": *a11 + "403": *a15 "404": description: Medication or revision not found (or owned by another user). content: @@ -7669,7 +7815,7 @@ paths: schema: $ref: "#/components/schemas/MedicationDoseHistoryImportQueuedEnvelope" "401": *a1 - "403": *a11 + "403": *a15 "409": description: A dose-history import is already in progress. content: @@ -7731,7 +7877,7 @@ paths: schema: $ref: "#/components/schemas/MedicationIntakeImportJobStatusEnvelope" "401": *a1 - "403": *a11 + "403": *a15 "404": description: No such job for this medication (or owned by another user). content: @@ -7765,7 +7911,7 @@ paths: schema: $ref: "#/components/schemas/MedicationIntakeImportJobStatusEnvelope" "401": *a1 - "403": *a11 + "403": *a15 "404": description: No such job for this account (or owned by another user). content: @@ -7795,7 +7941,7 @@ paths: schema: $ref: "#/components/schemas/ProfileSummaryEnvelope" "401": *a1 - "403": *a11 + "403": *a15 "422": *a4 "429": *a5 /api/ai/test: @@ -7844,8 +7990,10 @@ paths: $ref: "#/components/schemas/AiTestEnvelope" "401": *a1 "403": - description: The operator's master AI switch is off (`meta.errorCode = "assistant.disabled.enabled"`), or the switches - could not be read (`ai.unavailable`). Nothing was sent. + description: "`meta.errorCode` = `auth.scope.insufficient` when a Bearer token's scope does not reach this route; the + generic auth gate answers it before the route's own checks run. Otherwise: The operator's master AI switch + is off (`meta.errorCode = \"assistant.disabled.enabled\"`), or the switches could not be read + (`ai.unavailable`). Nothing was sent." content: application/json: schema: @@ -7896,6 +8044,7 @@ paths: schema: $ref: "#/components/schemas/GetAiProviderResponse" "401": *a1 + "403": *a9 "422": *a4 "429": *a5 patch: @@ -7939,6 +8088,7 @@ paths: schema: $ref: "#/components/schemas/PatchAiProviderResponse" "401": *a1 + "403": *a9 "413": description: Body exceeds 64 KiB. content: @@ -7978,6 +8128,7 @@ paths: schema: $ref: "#/components/schemas/GetCoachPrefsResponse" "401": *a1 + "403": *a9 "422": *a4 "429": *a5 put: @@ -8000,8 +8151,9 @@ paths: application/json: schema: $ref: "#/components/schemas/PutCoachPrefsResponse" - "400": *a14 + "400": *a18 "401": *a1 + "403": *a9 "409": description: Coach preferences changed since it was loaded (optimistic-concurrency conflict). No write happened. Re-read the resource, re-apply the user's change against the fresh state, and resend with the new token. @@ -8010,7 +8162,7 @@ paths: application/json: schema: $ref: "#/components/schemas/ErrorEnvelope" - "422": *a15 + "422": *a19 "429": *a5 /api/auth/me/disable-coach: get: @@ -8027,6 +8179,7 @@ paths: schema: $ref: "#/components/schemas/GetDisableCoachResponse" "401": *a1 + "403": *a9 "422": *a4 "429": *a5 patch: @@ -8048,8 +8201,9 @@ paths: application/json: schema: $ref: "#/components/schemas/PatchDisableCoachResponse" - "400": *a14 + "400": *a18 "401": *a1 + "403": *a9 "422": *a4 "429": *a5 /api/auth/me/diabetes: @@ -8069,6 +8223,7 @@ paths: schema: $ref: "#/components/schemas/GetDiabetesResponse" "401": *a1 + "403": *a9 "422": *a4 "429": *a5 patch: @@ -8092,6 +8247,7 @@ paths: schema: $ref: "#/components/schemas/PatchDiabetesResponse" "401": *a1 + "403": *a9 "422": *a4 "429": *a5 /api/auth/me/health-score-config: @@ -8111,6 +8267,7 @@ paths: schema: $ref: "#/components/schemas/HealthScoreConfigEnvelope" "401": *a1 + "403": *a9 "422": *a4 "429": *a5 patch: @@ -8139,6 +8296,7 @@ paths: schema: $ref: "#/components/schemas/PatchHealthScoreConfigEnvelope" "401": *a1 + "403": *a9 "409": description: Score selection changed since it was loaded (optimistic-concurrency conflict). No write happened. Re-read the resource, re-apply the user's change against the fresh state, and resend with the new token. @@ -8179,6 +8337,7 @@ paths: schema: $ref: "#/components/schemas/GetModulesResponse" "401": *a1 + "403": *a9 "422": *a4 "429": *a5 patch: @@ -8203,8 +8362,9 @@ paths: application/json: schema: $ref: "#/components/schemas/PatchModulesResponse" - "400": *a14 + "400": *a18 "401": *a1 + "403": *a9 "409": description: Module preferences changed since it was loaded (optimistic-concurrency conflict). No write happened. Re-read the resource, re-apply the user's change against the fresh state, and resend with the new token. @@ -8213,7 +8373,7 @@ paths: application/json: schema: $ref: "#/components/schemas/ErrorEnvelope" - "422": *a15 + "422": *a19 "429": *a5 /api/auth/me/source-priority: get: @@ -8230,6 +8390,7 @@ paths: schema: $ref: "#/components/schemas/GetSourcePriorityResponse" "401": *a1 + "403": *a9 "422": *a4 "429": *a5 put: @@ -9164,8 +9325,9 @@ paths: application/json: schema: $ref: "#/components/schemas/PutSourcePriorityResponse" - "400": *a14 + "400": *a18 "401": *a1 + "403": *a9 "422": *a4 "429": *a5 /api/user/avatar: @@ -9198,6 +9360,7 @@ paths: schema: $ref: "#/components/schemas/UploadAvatarResponse" "401": *a1 + "403": *a9 "413": description: Upload exceeds the 2 MiB byte limit or the 2048×2048 dimension limit. content: @@ -9222,6 +9385,7 @@ paths: "204": description: Avatar cleared (or already empty). "401": *a1 + "403": *a9 "422": *a4 "429": *a5 /api/user/avatar/{id}: @@ -9258,7 +9422,9 @@ paths: description: Raw WebP bytes. "401": *a1 "403": - description: Caller is not the avatar's owner. + description: "`meta.errorCode` = `auth.scope.insufficient` when a Bearer token's scope does not reach this route; the + generic auth gate answers it before the route's own checks run. Otherwise: Caller is not the avatar's + owner." content: application/json: schema: @@ -9286,6 +9452,7 @@ paths: schema: $ref: "#/components/schemas/GetProfileResponse" "401": *a1 + "403": *a9 "422": *a4 "429": *a5 patch: @@ -9309,6 +9476,7 @@ paths: schema: $ref: "#/components/schemas/PatchProfileResponse" "401": *a1 + "403": *a9 "409": description: The requested email belongs to another account (`meta.errorCode` = `profile.update.emailInUse`). Nothing was written. Shared with PUT /api/auth/profile, which runs the same handler. @@ -9317,7 +9485,7 @@ paths: schema: $ref: "#/components/schemas/ErrorEnvelope" "422": *a4 - "429": *a16 + "429": *a20 /api/auth/me/notification-prefs: get: tags: @@ -9334,6 +9502,7 @@ paths: schema: $ref: "#/components/schemas/GetNotificationPrefsResponse" "401": *a1 + "403": *a9 "422": *a4 "429": *a5 patch: @@ -9359,8 +9528,9 @@ paths: application/json: schema: $ref: "#/components/schemas/PatchNotificationPrefsResponse" - "400": *a14 + "400": *a18 "401": *a1 + "403": *a9 "409": description: Notification preferences changed since it was loaded (optimistic-concurrency conflict). No write happened. Re-read the resource, re-apply the user's change against the fresh state, and resend with the new token. @@ -9369,7 +9539,7 @@ paths: application/json: schema: $ref: "#/components/schemas/ErrorEnvelope" - "422": *a15 + "422": *a19 "429": *a5 /api/auth/me/report-selection: get: @@ -9387,6 +9557,7 @@ paths: schema: $ref: "#/components/schemas/GetReportSelectionResponseEnvelope" "401": *a1 + "403": *a9 "422": *a4 "429": *a5 put: @@ -9411,8 +9582,9 @@ paths: application/json: schema: $ref: "#/components/schemas/PutReportSelectionResponseEnvelope" - "400": *a14 + "400": *a18 "401": *a1 + "403": *a9 "422": *a4 "429": *a5 /api/auth/me/unit-preference: @@ -9432,6 +9604,7 @@ paths: schema: $ref: "#/components/schemas/GetUnitPreferenceEnvelope" "401": *a1 + "403": *a9 "422": *a4 "429": *a5 patch: @@ -9458,13 +9631,26 @@ paths: application/json: schema: $ref: "#/components/schemas/PatchUnitPreferenceEnvelope" + "400": &a21 + description: The request body is not parseable JSON. Nothing was written. + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorEnvelope" "401": *a1 - "413": + "403": *a9 + "413": &a22 description: Request body exceeds 1024 bytes. Nothing was written. content: application/json: schema: $ref: "#/components/schemas/ErrorEnvelope" + "415": &a23 + description: "The request did not carry `Content-Type: application/json`. Nothing was read and nothing was written." + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorEnvelope" "422": *a4 "429": *a5 /api/auth/me/glucose-unit: @@ -9484,6 +9670,7 @@ paths: schema: $ref: "#/components/schemas/GetGlucoseUnitEnvelope" "401": *a1 + "403": *a9 "422": *a4 "429": *a5 patch: @@ -9514,13 +9701,11 @@ paths: application/json: schema: $ref: "#/components/schemas/PatchGlucoseUnitEnvelope" + "400": *a21 "401": *a1 - "413": - description: Request body exceeds 1024 bytes. Nothing was written. - content: - application/json: - schema: - $ref: "#/components/schemas/ErrorEnvelope" + "403": *a9 + "413": *a22 + "415": *a23 "422": *a4 "429": *a5 /api/auth/me/documents-auto-ai-read: @@ -9539,6 +9724,7 @@ paths: schema: $ref: "#/components/schemas/GetDocumentsAutoAiReadEnvelope" "401": *a1 + "403": *a9 "422": *a4 "429": *a5 patch: @@ -9575,13 +9761,11 @@ paths: application/json: schema: $ref: "#/components/schemas/PatchDocumentsAutoAiReadEnvelope" + "400": *a21 "401": *a1 - "413": - description: Request body exceeds 1024 bytes. Nothing was written. - content: - application/json: - schema: - $ref: "#/components/schemas/ErrorEnvelope" + "403": *a9 + "413": *a22 + "415": *a23 "422": *a4 "429": *a5 /api/auth/me/injection-site-prefs: @@ -9600,6 +9784,7 @@ paths: schema: $ref: "#/components/schemas/GetInjectionSitePrefsEnvelope" "401": *a1 + "403": *a9 "422": *a4 "429": *a5 patch: @@ -9628,6 +9813,7 @@ paths: schema: $ref: "#/components/schemas/PatchInjectionSitePrefsEnvelope" "401": *a1 + "403": *a9 "422": *a4 "429": *a5 /api/user/thresholds: @@ -9652,6 +9838,7 @@ paths: schema: $ref: "#/components/schemas/GetThresholdsEnvelope" "401": *a1 + "403": *a9 "404": description: The authenticated user row could not be loaded. In practice unreachable — the session that resolved the caller was validated against that row. @@ -9689,6 +9876,7 @@ paths: schema: $ref: "#/components/schemas/PutThresholdsEnvelope" "401": *a1 + "403": *a9 "422": *a4 "429": *a5 delete: @@ -9732,6 +9920,7 @@ paths: schema: $ref: "#/components/schemas/ErrorEnvelope" "401": *a1 + "403": *a9 "422": description: '`metric` was omitted and the body did not carry `{ "confirm": "RESET_THRESHOLDS" }`. Nothing was reset.' content: @@ -9760,6 +9949,7 @@ paths: schema: $ref: "#/components/schemas/DeviceListEnvelope" "401": *a1 + "403": *a9 "422": *a4 "429": *a5 /api/auth/me/devices/{id}: @@ -9790,6 +9980,7 @@ paths: schema: $ref: "#/components/schemas/DevicePatchEnvelope" "401": *a1 + "403": *a9 "404": description: No such device for this account. content: @@ -9828,6 +10019,7 @@ paths: schema: $ref: "#/components/schemas/AccountDeviceRevokeEnvelope" "401": *a1 + "403": *a9 "404": description: No such device for this account. content: @@ -9854,6 +10046,7 @@ paths: schema: $ref: "#/components/schemas/GetLabsLocalOcrEnvelope" "401": *a1 + "403": *a9 "422": *a4 "429": *a5 patch: @@ -9874,13 +10067,11 @@ paths: application/json: schema: $ref: "#/components/schemas/PatchLabsLocalOcrEnvelope" + "400": *a21 "401": *a1 - "413": - description: Request body exceeds 1024 bytes. Nothing was written. - content: - application/json: - schema: - $ref: "#/components/schemas/ErrorEnvelope" + "403": *a9 + "413": *a22 + "415": *a23 "422": *a4 "429": *a5 /api/auth/me/workout-routes: @@ -9899,6 +10090,7 @@ paths: schema: $ref: "#/components/schemas/GetWorkoutRoutesEnvelope" "401": *a1 + "403": *a9 "422": *a4 "429": *a5 patch: @@ -9920,13 +10112,11 @@ paths: application/json: schema: $ref: "#/components/schemas/PatchWorkoutRoutesEnvelope" + "400": *a21 "401": *a1 - "413": - description: Request body exceeds 1024 bytes. Nothing was written. - content: - application/json: - schema: - $ref: "#/components/schemas/ErrorEnvelope" + "403": *a9 + "413": *a22 + "415": *a23 "422": *a4 "429": *a5 /api/auth/me/use-central-codex: @@ -9944,6 +10134,7 @@ paths: schema: $ref: "#/components/schemas/GetUseCentralCodexEnvelope" "401": *a1 + "403": *a9 "422": *a4 "429": *a5 patch: @@ -9971,13 +10162,11 @@ paths: application/json: schema: $ref: "#/components/schemas/PatchUseCentralCodexEnvelope" + "400": *a21 "401": *a1 - "413": - description: Request body exceeds 1024 bytes. Nothing was written. - content: - application/json: - schema: - $ref: "#/components/schemas/ErrorEnvelope" + "403": *a9 + "413": *a22 + "415": *a23 "422": *a4 "429": *a5 /api/auth/me/locale: @@ -10017,7 +10206,9 @@ paths: $ref: "#/components/schemas/LocalePutEnvelope" "401": *a1 "403": - description: A selector header was attached to an actor surface (`meta.errorCode` = `sharing.not_permitted`). + description: "`meta.errorCode` = `auth.scope.insufficient` when a Bearer token's scope does not reach this route; the + generic auth gate answers it before the route's own checks run. Otherwise: A selector header was attached to + an actor surface (`meta.errorCode` = `sharing.not_permitted`)." content: application/json: schema: @@ -10053,6 +10244,7 @@ paths: schema: $ref: "#/components/schemas/TimezonePutEnvelope" "401": *a1 + "403": *a9 "422": *a4 "429": *a5 /api/auth/me/passkey-nudge/dismiss: @@ -10070,6 +10262,7 @@ paths: schema: $ref: "#/components/schemas/PasskeyNudgeDismissEnvelope" "401": *a1 + "403": *a9 "422": *a4 "429": *a5 /api/insights/chat: @@ -10116,6 +10309,7 @@ paths: schema: $ref: "#/components/schemas/CoachConversationsPageResponse" "401": *a1 + "403": *a9 "422": *a4 "429": *a5 post: @@ -10160,11 +10354,13 @@ paths: $ref: "#/components/schemas/CoachStreamEvent" "401": *a1 "403": - description: "The `coach` AI capability is unavailable for this record. `meta.capability` = `coach` and `meta.reason` - says which layer refused: the operator's switch (`assistant.disabled.coach`, or `assistant.disabled.enabled` - for the master switch), an owning module or the person's own AI opt-out (`module.disabled` with - `meta.module`), a delegate inside somebody else's record (`ai.record.notPermitted`), or a missing consent - receipt (`consent.ai.required`). Nothing was generated, read from a stored model answer, or enqueued." + description: "`meta.errorCode` = `auth.scope.insufficient` when a Bearer token's scope does not reach this route; the + generic auth gate answers it before the route's own checks run. Otherwise: The `coach` AI capability is + unavailable for this record. `meta.capability` = `coach` and `meta.reason` says which layer refused: the + operator's switch (`assistant.disabled.coach`, or `assistant.disabled.enabled` for the master switch), an + owning module or the person's own AI opt-out (`module.disabled` with `meta.module`), a delegate inside + somebody else's record (`ai.record.notPermitted`), or a missing consent receipt (`consent.ai.required`). + Nothing was generated, read from a stored model answer, or enqueued." content: application/json: schema: @@ -10177,7 +10373,7 @@ paths: $ref: "#/components/schemas/ErrorEnvelope" "422": *a4 "429": *a5 - "503": &a18 + "503": &a26 description: "The AI capability could not be resolved (its inputs could not be read) and the answer failed closed: `meta.errorCode` = `ai.unavailable`, `meta.reason` = `check_failed`. Retry later; no data depends on it." content: @@ -10217,6 +10413,7 @@ paths: schema: $ref: "#/components/schemas/CoachMessageResultsResponse" "401": *a1 + "403": *a9 "404": description: Conversation or message not found, or not owned by the caller. content: @@ -10260,6 +10457,7 @@ paths: schema: $ref: "#/components/schemas/CoachMessageTrailResponse" "401": *a1 + "403": *a9 "404": description: Conversation or message not found, or not owned by the caller. content: @@ -10292,6 +10490,7 @@ paths: schema: $ref: "#/components/schemas/CoachConversationDetailResponse" "401": *a1 + "403": *a9 "404": description: Conversation not found or not owned by the caller. content: @@ -10331,6 +10530,7 @@ paths: schema: $ref: "#/components/schemas/CoachConversationRenamed" "401": *a1 + "403": *a9 "404": description: Conversation not found or not owned by the caller. content: @@ -10366,6 +10566,7 @@ paths: schema: $ref: "#/components/schemas/CoachConversationDeleted" "401": *a1 + "403": *a9 "404": description: Conversation not found or not owned by the caller. content: @@ -10408,20 +10609,21 @@ paths: description: "SSE frames: `data: \\n\\n`. See the operation description for the per-`type` frame shapes." "401": *a1 "403": - description: "AI refusal: the `documentAi` capability is unavailable for this record. The envelope carries - `meta.errorCode`, `meta.capability` and `meta.reason` (one of `AiUnavailableReason`). - `assistant.disabled.documentAi` — the operator turned off reading documents, or every AI feature (the master - switch). `module.disabled` with `meta.module = \"inboundDocuments\"` — the module is off for this record. - `ai.record.notPermitted` — AI work is not admitted inside this record (a delegate acting in somebody else's - record). `consent.ai.required` — the provider this request would reach leaves the machine and the record - holds no active `ai_extraction` or `ai_full` receipt; only a self-hosted model is exempt. The question is - answered for the provider actually picked, so it can differ from the published capability when a text-mode - read takes a different provider than a vision read would. The route also answers under `coach`, with the - same envelope and that capability's own codes (`assistant.disabled.coach`, `module.disabled` with - `meta.module = \"coach\"` when the Coach is off for the record). A 422 (`stdResponses`) covers an un-indexed - / unavailable attachment (`coach.fenced.attachmentUnavailable`), the attachment cap - (`coach.fenced.attachmentLimit`), or `attachmentIds` sent with a `conversationId` - (`coach.fenced.attachmentConflict`)." + description: "`meta.errorCode` = `auth.scope.insufficient` when a Bearer token's scope does not reach this route; the + generic auth gate answers it before the route's own checks run. Otherwise: AI refusal: the `documentAi` + capability is unavailable for this record. The envelope carries `meta.errorCode`, `meta.capability` and + `meta.reason` (one of `AiUnavailableReason`). `assistant.disabled.documentAi` — the operator turned off + reading documents, or every AI feature (the master switch). `module.disabled` with `meta.module = + \"inboundDocuments\"` — the module is off for this record. `ai.record.notPermitted` — AI work is not + admitted inside this record (a delegate acting in somebody else's record). `consent.ai.required` — the + provider this request would reach leaves the machine and the record holds no active `ai_extraction` or + `ai_full` receipt; only a self-hosted model is exempt. The question is answered for the provider actually + picked, so it can differ from the published capability when a text-mode read takes a different provider than + a vision read would. The route also answers under `coach`, with the same envelope and that capability's own + codes (`assistant.disabled.coach`, `module.disabled` with `meta.module = \"coach\"` when the Coach is off + for the record). A 422 (`stdResponses`) covers an un-indexed / unavailable attachment + (`coach.fenced.attachmentUnavailable`), the attachment cap (`coach.fenced.attachmentLimit`), or + `attachmentIds` sent with a `conversationId` (`coach.fenced.attachmentConflict`)." content: application/json: schema: @@ -10469,17 +10671,19 @@ paths: $ref: "#/components/schemas/CoachAttachmentsResponse" "401": *a1 "403": - description: "AI refusal: the `documentAi` capability is unavailable for this record. The envelope carries - `meta.errorCode`, `meta.capability` and `meta.reason` (one of `AiUnavailableReason`). - `assistant.disabled.documentAi` — the operator turned off reading documents, or every AI feature (the master - switch). `module.disabled` with `meta.module = \"inboundDocuments\"` — the module is off for this record. - `ai.record.notPermitted` — AI work is not admitted inside this record (a delegate acting in somebody else's - record). `consent.ai.required` — the provider this request would reach leaves the machine and the record - holds no active `ai_extraction` or `ai_full` receipt; only a self-hosted model is exempt. The question is - answered for the provider actually picked, so it can differ from the published capability when a text-mode - read takes a different provider than a vision read would. The route also answers under `coach`, with the - same envelope and that capability's own codes (`assistant.disabled.coach`, `module.disabled` with - `meta.module = \"coach\"` when the Coach is off for the record)." + description: "`meta.errorCode` = `auth.scope.insufficient` when a Bearer token's scope does not reach this route; the + generic auth gate answers it before the route's own checks run. Otherwise: AI refusal: the `documentAi` + capability is unavailable for this record. The envelope carries `meta.errorCode`, `meta.capability` and + `meta.reason` (one of `AiUnavailableReason`). `assistant.disabled.documentAi` — the operator turned off + reading documents, or every AI feature (the master switch). `module.disabled` with `meta.module = + \"inboundDocuments\"` — the module is off for this record. `ai.record.notPermitted` — AI work is not + admitted inside this record (a delegate acting in somebody else's record). `consent.ai.required` — the + provider this request would reach leaves the machine and the record holds no active `ai_extraction` or + `ai_full` receipt; only a self-hosted model is exempt. The question is answered for the provider actually + picked, so it can differ from the published capability when a text-mode read takes a different provider than + a vision read would. The route also answers under `coach`, with the same envelope and that capability's own + codes (`assistant.disabled.coach`, `module.disabled` with `meta.module = \"coach\"` when the Coach is off + for the record)." content: application/json: schema: @@ -10529,6 +10733,7 @@ paths: schema: $ref: "#/components/schemas/CoachAttachmentsResponseDetach" "401": *a1 + "403": *a9 "404": description: Attachment or conversation not found / not owned. content: @@ -10553,6 +10758,7 @@ paths: schema: $ref: "#/components/schemas/InsightsLayout" "401": *a1 + "403": *a9 "422": *a4 "429": *a5 put: @@ -10577,6 +10783,7 @@ paths: schema: $ref: "#/components/schemas/InsightsLayoutSaved" "401": *a1 + "403": *a9 "409": description: Insights layout changed since it was loaded (optimistic-concurrency conflict). No write happened. Re-read the resource, re-apply the user's change against the fresh state, and resend with the new token. @@ -10585,7 +10792,7 @@ paths: application/json: schema: $ref: "#/components/schemas/ErrorEnvelope" - "422": *a15 + "422": *a19 "429": *a5 delete: tags: @@ -10600,6 +10807,7 @@ paths: schema: $ref: "#/components/schemas/InsightsLayoutReset" "401": *a1 + "403": *a9 "422": *a4 "429": *a5 /api/insights/coach/facts: @@ -10621,6 +10829,7 @@ paths: schema: $ref: "#/components/schemas/CoachFactsList" "401": *a1 + "403": *a9 "422": *a4 "429": *a5 post: @@ -10653,9 +10862,11 @@ paths: $ref: "#/components/schemas/CoachFactCreated" "401": *a1 "403": - description: 'Module disabled for this account: the user (or operator) has the module turned off. `meta.errorCode` = - `module.disabled` and `meta.module` carries the disabled module key (e.g. "sleep"). Returned even for a - valid Bearer token. Clients hide the whole module surface end-to-end rather than retry.' + description: "`meta.errorCode` = `auth.scope.insufficient` when a Bearer token's scope does not reach this route; the + generic auth gate answers it before the route's own checks run. Otherwise: Module disabled for this account: + the user (or operator) has the module turned off. `meta.errorCode` = `module.disabled` and `meta.module` + carries the disabled module key (e.g. \"sleep\"). Returned even for a valid Bearer token. Clients hide the + whole module surface end-to-end rather than retry." content: application/json: schema: @@ -10682,6 +10893,7 @@ paths: schema: $ref: "#/components/schemas/CoachFactsCleared" "401": *a1 + "403": *a9 "422": *a4 "429": *a5 /api/insights/coach/facts/{id}: @@ -10715,6 +10927,7 @@ paths: schema: $ref: "#/components/schemas/CoachFactUpdated" "401": *a1 + "403": *a9 "404": description: Fact not found or not owned by the caller. content: @@ -10738,6 +10951,7 @@ paths: schema: $ref: "#/components/schemas/CoachFactDeleted" "401": *a1 + "403": *a9 "422": *a4 "429": *a5 /api/insights/coach/nudge-status: @@ -10760,7 +10974,7 @@ paths: schema: $ref: "#/components/schemas/CoachNudgeStatus" "401": *a1 - "403": *a11 + "403": *a15 "422": *a4 "429": *a5 /api/insights/coach/seen: @@ -10781,6 +10995,7 @@ paths: schema: $ref: "#/components/schemas/CoachSeenResponse" "401": *a1 + "403": *a9 "422": *a4 "429": *a5 /api/coach/about-me: @@ -10803,6 +11018,7 @@ paths: schema: $ref: "#/components/schemas/GetCoachAboutMeResponse" "401": *a1 + "403": *a9 "422": *a4 "429": *a5 put: @@ -10870,6 +11086,7 @@ paths: schema: $ref: "#/components/schemas/PutCoachAboutMeResponse" "401": *a1 + "403": *a9 "409": description: Self-context changed since it was loaded (optimistic-concurrency conflict). No write happened. Re-read the resource, re-apply the user's change against the fresh state, and resend with the new token. @@ -10878,7 +11095,7 @@ paths: application/json: schema: $ref: "#/components/schemas/ErrorEnvelope" - "422": *a15 + "422": *a19 "429": *a5 /api/anamnesis/facts: get: @@ -10897,7 +11114,7 @@ paths: schema: $ref: "#/components/schemas/GetAnamnesisFactsResponse" "401": *a1 - "403": *a11 + "403": *a15 "422": *a4 "429": *a5 post: @@ -11315,6 +11532,7 @@ paths: schema: $ref: "#/components/schemas/CreateAnamnesisFactResponse" "401": *a1 + "403": *a9 "409": description: A current revision already exists for this fact kind. `meta.errorCode` = `anamnesis.fact.currentExists`. content: @@ -11370,7 +11588,8 @@ paths: schema: $ref: "#/components/schemas/CorrectAnamnesisFactResponse" "401": *a1 - "404": &a17 + "403": *a9 + "404": &a24 description: No current revision with this id exists for the authenticated account. content: application/json: @@ -11406,7 +11625,8 @@ paths: schema: $ref: "#/components/schemas/RemoveAnamnesisFactResponse" "401": *a1 - "404": *a17 + "403": *a9 + "404": *a24 "409": description: Anamnesis fact changed since it was loaded (optimistic-concurrency conflict). No write happened. Re-read the resource, re-apply the user's change against the fresh state, and resend with the new token. @@ -11433,6 +11653,7 @@ paths: schema: $ref: "#/components/schemas/GetEmergencyProfileResponse" "401": *a1 + "403": *a9 "422": *a4 "429": *a5 patch: @@ -11455,6 +11676,7 @@ paths: schema: $ref: "#/components/schemas/UpdateEmergencyProfileResponse" "401": *a1 + "403": *a9 "422": *a4 "429": *a5 /api/coach/about-me/questions: @@ -11473,6 +11695,7 @@ paths: schema: $ref: "#/components/schemas/GetCoachAboutMeQuestionsResponse" "401": *a1 + "403": *a9 "422": *a4 "429": *a5 delete: @@ -11499,6 +11722,7 @@ paths: schema: $ref: "#/components/schemas/DeleteCoachAboutMeQuestionsResponse" "401": *a1 + "403": *a9 "422": *a4 "429": *a5 /api/coach/about-me/adopt: @@ -11548,9 +11772,11 @@ paths: $ref: "#/components/schemas/CoachAboutMeAdoptResponse" "401": *a1 "403": - description: 'Module disabled for this account: the user (or operator) has the module turned off. `meta.errorCode` = - `module.disabled` and `meta.module` carries the disabled module key (e.g. "sleep"). Returned even for a - valid Bearer token. Clients hide the whole module surface end-to-end rather than retry.' + description: "`meta.errorCode` = `auth.scope.insufficient` when a Bearer token's scope does not reach this route; the + generic auth gate answers it before the route's own checks run. Otherwise: Module disabled for this account: + the user (or operator) has the module turned off. `meta.errorCode` = `module.disabled` and `meta.module` + carries the disabled module key (e.g. \"sleep\"). Returned even for a valid Bearer token. Clients hide the + whole module surface end-to-end rather than retry." content: application/json: schema: @@ -11623,6 +11849,7 @@ paths: schema: $ref: "#/components/schemas/CoachPlansList" "401": *a1 + "403": *a9 "422": *a4 "429": *a5 /api/coach/plans/{id}: @@ -11657,7 +11884,8 @@ paths: $ref: "#/components/schemas/CoachPlanUpdated" "401": *a1 "403": - description: Coach surface disabled. + description: "`meta.errorCode` = `auth.scope.insufficient` when a Bearer token's scope does not reach this route; the + generic auth gate answers it before the route's own checks run. Otherwise: Coach surface disabled." content: application/json: schema: @@ -11694,7 +11922,8 @@ paths: $ref: "#/components/schemas/CoachPlanDeleted" "401": *a1 "403": - description: Coach surface disabled. + description: "`meta.errorCode` = `auth.scope.insufficient` when a Bearer token's scope does not reach this route; the + generic auth gate answers it before the route's own checks run. Otherwise: Coach surface disabled." content: application/json: schema: @@ -11733,7 +11962,9 @@ paths: $ref: "#/components/schemas/CoachReminderSuggestionResultCreated" "401": *a1 "403": - description: Coach surface (or the cadence's module) disabled. + description: "`meta.errorCode` = `auth.scope.insufficient` when a Bearer token's scope does not reach this route; the + generic auth gate answers it before the route's own checks run. Otherwise: Coach surface (or the cadence's + module) disabled." content: application/json: schema: @@ -11766,7 +11997,7 @@ paths: schema: $ref: "#/components/schemas/CoachRemindersList" "401": *a1 - "403": *a11 + "403": *a15 "422": *a4 "429": *a5 post: @@ -11792,7 +12023,8 @@ paths: $ref: "#/components/schemas/CoachReminderCreated" "401": *a1 "403": - description: Coach surface disabled. + description: "`meta.errorCode` = `auth.scope.insufficient` when a Bearer token's scope does not reach this route; the + generic auth gate answers it before the route's own checks run. Otherwise: Coach surface disabled." content: application/json: schema: @@ -11836,7 +12068,8 @@ paths: $ref: "#/components/schemas/CoachReminderUpdated" "401": *a1 "403": - description: Coach surface disabled. + description: "`meta.errorCode` = `auth.scope.insufficient` when a Bearer token's scope does not reach this route; the + generic auth gate answers it before the route's own checks run. Otherwise: Coach surface disabled." content: application/json: schema: @@ -11872,6 +12105,7 @@ paths: schema: $ref: "#/components/schemas/CoachReminderDeleted" "401": *a1 + "403": *a9 "422": *a4 "429": *a5 /api/coach/suggested-actions: @@ -11900,7 +12134,8 @@ paths: $ref: "#/components/schemas/CoachSuggestedActionResultCreated" "401": *a1 "403": - description: Coach surface disabled. + description: "`meta.errorCode` = `auth.scope.insufficient` when a Bearer token's scope does not reach this route; the + generic auth gate answers it before the route's own checks run. Otherwise: Coach surface disabled." content: application/json: schema: @@ -11930,6 +12165,12 @@ paths: schema: $ref: "#/components/schemas/AdminInviteList" "401": *a1 + "403": &a25 + description: The session is valid but its account is not an administrator. `meta.errorCode` = `auth.admin.required`. + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorEnvelope" "422": *a4 "429": *a5 post: @@ -11965,6 +12206,7 @@ paths: schema: $ref: "#/components/schemas/AdminInviteCreated" "401": *a1 + "403": *a25 "422": *a4 "429": *a5 /api/admin/invites/{id}: @@ -11989,6 +12231,7 @@ paths: schema: $ref: "#/components/schemas/AdminInviteRevoked" "401": *a1 + "403": *a25 "422": *a4 "429": *a5 /api/insights/chat/messages/{id}/feedback: @@ -12013,6 +12256,7 @@ paths: schema: $ref: "#/components/schemas/CoachMessageFeedbackResponse" "401": *a1 + "403": *a9 "404": description: Message not found or not owned by the caller. content: @@ -12043,6 +12287,7 @@ paths: schema: $ref: "#/components/schemas/AdminMeasurementMaintenanceStatusResponse" "401": *a1 + "403": *a25 "422": *a4 "429": *a5 post: @@ -12068,7 +12313,8 @@ paths: $ref: "#/components/schemas/AdminMeasurementMaintenanceResponse" "401": *a1 "403": - description: Caller is not an admin. + description: "`meta.errorCode` = `auth.admin.required` when the session's account is not an administrator; the admin + gate answers it before the route's own checks run. Otherwise: Caller is not an admin." content: application/json: schema: @@ -12118,7 +12364,8 @@ paths: $ref: "#/components/schemas/AdminReminderCheckResponse" "401": *a1 "403": - description: Caller is not an admin. + description: "`meta.errorCode` = `auth.admin.required` when the session's account is not an administrator; the admin + gate answers it before the route's own checks run. Otherwise: Caller is not an admin." content: application/json: schema: @@ -12142,7 +12389,8 @@ paths: $ref: "#/components/schemas/AdminDiagnosticResponse" "401": *a1 "403": - description: Caller is not an admin. + description: "`meta.errorCode` = `auth.admin.required` when the session's account is not an administrator; the admin + gate answers it before the route's own checks run. Otherwise: Caller is not an admin." content: application/json: schema: @@ -12169,7 +12417,7 @@ paths: schema: $ref: "#/components/schemas/DashboardSnapshotResponseEnvelope" "401": *a1 - "403": *a11 + "403": *a15 "422": *a4 "429": *a5 /api/insights/comprehensive: @@ -12191,7 +12439,7 @@ paths: schema: $ref: "#/components/schemas/InsightsComprehensiveResponseEnvelope" "401": *a1 - "403": *a11 + "403": *a15 "422": *a4 "429": *a5 /api/insights/cards: @@ -12212,7 +12460,7 @@ paths: schema: $ref: "#/components/schemas/InsightsCardsEnvelope" "401": *a1 - "403": *a11 + "403": *a15 "422": *a4 "429": *a5 /api/insights/pregenerate: @@ -12244,6 +12492,7 @@ paths: schema: $ref: "#/components/schemas/InsightsPregenerateResponseEnvelope" "401": *a1 + "403": *a9 "422": *a4 "429": *a5 /api/analytics: @@ -12294,6 +12543,7 @@ paths: schema: $ref: "#/components/schemas/AnalyticsEnvelope" "401": *a1 + "403": *a9 "422": description: "`slice` carried a value outside the closed set. `meta.errorCode` = `analytics.invalid_query`, with the offending path under `details.issues`." @@ -12329,12 +12579,13 @@ paths: $ref: "#/components/schemas/InsightsGenerateReadEnvelope" "401": *a1 "403": - description: "The `briefing` AI capability is unavailable for this record. `meta.capability` = `briefing` and - `meta.reason` says which layer refused: the operator's switch (`assistant.disabled.briefing`, or - `assistant.disabled.enabled` for the master switch), an owning module or the person's own AI opt-out - (`module.disabled` with `meta.module`), a delegate inside somebody else's record (`ai.record.notPermitted`), - or a missing consent receipt (`consent.ai.required`). Nothing was generated, read from a stored model - answer, or enqueued." + description: "`meta.errorCode` = `auth.scope.insufficient` when a Bearer token's scope does not reach this route; the + generic auth gate answers it before the route's own checks run. Otherwise: The `briefing` AI capability is + unavailable for this record. `meta.capability` = `briefing` and `meta.reason` says which layer refused: the + operator's switch (`assistant.disabled.briefing`, or `assistant.disabled.enabled` for the master switch), an + owning module or the person's own AI opt-out (`module.disabled` with `meta.module`), a delegate inside + somebody else's record (`ai.record.notPermitted`), or a missing consent receipt (`consent.ai.required`). + Nothing was generated, read from a stored model answer, or enqueued." content: application/json: schema: @@ -12347,7 +12598,7 @@ paths: schema: $ref: "#/components/schemas/ErrorEnvelope" "429": *a5 - "503": *a18 + "503": *a26 post: tags: - Insights @@ -12382,12 +12633,13 @@ paths: $ref: "#/components/schemas/InsightsGeneratePostEnvelope" "401": *a1 "403": - description: "The `briefing` AI capability is unavailable for this record. `meta.capability` = `briefing` and - `meta.reason` says which layer refused: the operator's switch (`assistant.disabled.briefing`, or - `assistant.disabled.enabled` for the master switch), an owning module or the person's own AI opt-out - (`module.disabled` with `meta.module`), a delegate inside somebody else's record (`ai.record.notPermitted`), - or a missing consent receipt (`consent.ai.required`). Nothing was generated, read from a stored model - answer, or enqueued." + description: "`meta.errorCode` = `auth.scope.insufficient` when a Bearer token's scope does not reach this route; the + generic auth gate answers it before the route's own checks run. Otherwise: The `briefing` AI capability is + unavailable for this record. `meta.capability` = `briefing` and `meta.reason` says which layer refused: the + operator's switch (`assistant.disabled.briefing`, or `assistant.disabled.enabled` for the master switch), an + owning module or the person's own AI opt-out (`module.disabled` with `meta.module`), a delegate inside + somebody else's record (`ai.record.notPermitted`), or a missing consent receipt (`consent.ai.required`). + Nothing was generated, read from a stored model answer, or enqueued." content: application/json: schema: @@ -12445,6 +12697,7 @@ paths: schema: $ref: "#/components/schemas/ProviderChainEnvelope" "401": *a1 + "403": *a9 "422": *a4 "429": *a5 put: @@ -12471,6 +12724,7 @@ paths: schema: $ref: "#/components/schemas/ProviderChainSavedEnvelope" "401": *a1 + "403": *a9 "422": description: The body was not a valid chain — an empty array, more entries than there are provider types, an unknown provider type, or the same type twice. A single-message envelope with no issue list. @@ -12506,6 +12760,7 @@ paths: schema: $ref: "#/components/schemas/InsightsFeedbackCreatedEnvelope" "401": *a1 + "403": *a9 "409": *a7 "422": description: The body failed validation. A single-message envelope (`Invalid feedback payload`) with no issue list — @@ -12549,7 +12804,7 @@ paths: schema: $ref: "#/components/schemas/CoachReadStripEnvelope" "401": *a1 - "403": *a11 + "403": *a15 "422": *a4 "429": *a5 /api/insights/narrative: @@ -12598,7 +12853,7 @@ paths: schema: $ref: "#/components/schemas/NarrativeEnvelope" "401": *a1 - "403": *a11 + "403": *a15 "422": *a4 "429": *a5 /api/insights/glp1-timeline: @@ -12635,7 +12890,7 @@ paths: schema: $ref: "#/components/schemas/Glp1TimelineEnvelope" "401": *a1 - "403": *a11 + "403": *a15 "422": *a4 "429": *a5 /api/insights/pulse/intraday: @@ -12671,7 +12926,7 @@ paths: schema: $ref: "#/components/schemas/IntradayPulseEnvelope" "401": *a1 - "403": *a11 + "403": *a15 "422": *a4 "429": *a5 /api/insights/settings: @@ -12690,6 +12945,7 @@ paths: schema: $ref: "#/components/schemas/InsightsSettingsEnvelope" "401": *a1 + "403": *a9 "422": *a4 "429": *a5 put: @@ -12715,6 +12971,7 @@ paths: schema: $ref: "#/components/schemas/InsightsSettingsUpdatedEnvelope" "401": *a1 + "403": *a9 "422": description: "`Invalid privacy mode` when `privacyMode` was a string outside the two accepted values, or `No changes` when the body carried nothing this endpoint writes. Single-message envelope with no issue list — this route @@ -12745,7 +13002,7 @@ paths: schema: $ref: "#/components/schemas/TargetsEnvelope" "401": *a1 - "403": *a11 + "403": *a15 "422": *a4 "429": *a5 /api/analytics/range: @@ -12787,6 +13044,7 @@ paths: schema: $ref: "#/components/schemas/AnalyticsRangeResponseEnvelope" "401": *a1 + "403": *a9 "422": *a4 "429": *a5 /api/insights/blood-pressure-status: @@ -12818,7 +13076,7 @@ paths: schema: $ref: "#/components/schemas/BloodPressureStatusResponseEnvelope" "401": *a1 - "403": *a11 + "403": *a15 "422": *a4 "429": *a5 /api/insights/pulse-status: @@ -12850,7 +13108,7 @@ paths: schema: $ref: "#/components/schemas/PulseStatusResponseEnvelope" "401": *a1 - "403": *a11 + "403": *a15 "422": *a4 "429": *a5 /api/insights/weight-status: @@ -12882,7 +13140,7 @@ paths: schema: $ref: "#/components/schemas/WeightStatusResponseEnvelope" "401": *a1 - "403": *a11 + "403": *a15 "422": *a4 "429": *a5 /api/insights/bmi-status: @@ -12914,7 +13172,7 @@ paths: schema: $ref: "#/components/schemas/BmiStatusResponseEnvelope" "401": *a1 - "403": *a11 + "403": *a15 "422": *a4 "429": *a5 /api/insights/mood-status: @@ -12946,7 +13204,7 @@ paths: schema: $ref: "#/components/schemas/MoodStatusResponseEnvelope" "401": *a1 - "403": *a11 + "403": *a15 "422": *a4 "429": *a5 /api/insights/medication-compliance-status: @@ -12979,7 +13237,7 @@ paths: schema: $ref: "#/components/schemas/MedicationComplianceStatusResponseEnvelope" "401": *a1 - "403": *a11 + "403": *a15 "422": *a4 "429": *a5 /api/insights/metric-status: @@ -13074,7 +13332,7 @@ paths: schema: $ref: "#/components/schemas/MetricStatusResponseEnvelope" "401": *a1 - "403": *a11 + "403": *a15 "422": *a4 "429": *a5 /api/insights/biomarker-assessment: @@ -13119,7 +13377,7 @@ paths: schema: $ref: "#/components/schemas/BiomarkerAssessmentResponseEnvelope" "401": *a1 - "403": *a11 + "403": *a15 "422": *a4 "429": *a5 /api/insights/derived: @@ -13231,7 +13489,7 @@ paths: schema: $ref: "#/components/schemas/DerivedMetricResponseEnvelope" "401": *a1 - "403": *a11 + "403": *a15 "422": *a4 "429": *a5 /api/insights/derived/batch: @@ -13271,7 +13529,7 @@ paths: schema: $ref: "#/components/schemas/DerivedBatchResponseEnvelope" "401": *a1 - "403": *a11 + "403": *a15 "422": *a4 "429": *a5 /api/insights/score-history: @@ -13321,7 +13579,7 @@ paths: schema: $ref: "#/components/schemas/ScoreHistoryResponseEnvelope" "401": *a1 - "403": *a11 + "403": *a15 "422": *a4 "429": *a5 /api/insights/glp1-plateau: @@ -13341,7 +13599,7 @@ paths: schema: $ref: "#/components/schemas/InsightsGlp1PlateauResponseEnvelope" "401": *a1 - "403": *a11 + "403": *a15 "422": *a4 "429": *a5 /api/insights/ecg: @@ -13363,7 +13621,7 @@ paths: schema: $ref: "#/components/schemas/EcgListResponseEnvelope" "401": *a1 - "403": *a11 + "403": *a15 "422": *a4 "429": *a5 post: @@ -13401,6 +13659,7 @@ paths: schema: $ref: "#/components/schemas/EcgIngestCreatedEnvelope" "401": *a1 + "403": *a9 "422": *a4 "429": *a5 /api/insights/ecg/{id}: @@ -13440,7 +13699,7 @@ paths: schema: $ref: "#/components/schemas/EcgDetailResponseEnvelope" "401": *a1 - "403": *a11 + "403": *a15 "404": description: No ECG recording with that id for the authenticated user (existence sealed — a foreign id is indistinguishable from a missing one). @@ -13472,7 +13731,7 @@ paths: schema: $ref: "#/components/schemas/RhythmEventsResponseEnvelope" "401": *a1 - "403": *a11 + "403": *a15 "422": *a4 "429": *a5 /api/insights/correlations: @@ -13494,7 +13753,7 @@ paths: schema: $ref: "#/components/schemas/CorrelationDiscoveryResponseEnvelope" "401": *a1 - "403": *a11 + "403": *a15 "422": *a4 "429": *a5 /api/insights/patterns: @@ -13512,7 +13771,7 @@ paths: schema: $ref: "#/components/schemas/CorrelationPatternListResponseEnvelope" "401": *a1 - "403": *a11 + "403": *a15 "422": *a4 "429": *a5 /api/insights/patterns/{id}: @@ -13542,7 +13801,7 @@ paths: schema: $ref: "#/components/schemas/UpdateCorrelationPatternResponseEnvelope" "401": *a1 - "403": *a11 + "403": *a15 "404": description: No current pattern with that id for this account. content: @@ -13567,7 +13826,7 @@ paths: schema: $ref: "#/components/schemas/InsightsHealthStatusEnvelope" "401": *a1 - "403": *a11 + "403": *a15 "422": *a4 "429": *a5 /api/insights/breathing-screening: @@ -13586,7 +13845,7 @@ paths: schema: $ref: "#/components/schemas/InsightsBreathingScreeningEnvelope" "401": *a1 - "403": *a11 + "403": *a15 "422": *a4 "429": *a5 /api/insights/labs-changes: @@ -13605,7 +13864,7 @@ paths: schema: $ref: "#/components/schemas/InsightsLabsChangesEnvelope" "401": *a1 - "403": *a11 + "403": *a15 "422": *a4 "429": *a5 /api/mood/analytics: @@ -13628,7 +13887,7 @@ paths: schema: $ref: "#/components/schemas/MoodDailySeriesEnvelope" "401": *a1 - "403": *a19 + "403": *a11 "422": *a4 "429": *a5 /api/mood/insights: @@ -13663,7 +13922,7 @@ paths: schema: $ref: "#/components/schemas/MoodAggregatesEnvelope" "401": *a1 - "403": *a19 + "403": *a11 "422": *a4 "429": *a5 /api/mood/prognosis: @@ -13686,7 +13945,7 @@ paths: schema: $ref: "#/components/schemas/MoodPrognosisEnvelope" "401": *a1 - "403": *a13 + "403": *a17 "422": *a4 "429": *a5 /api/mood-entries/bulk: @@ -13715,6 +13974,7 @@ paths: schema: $ref: "#/components/schemas/BulkMoodResponseEnvelope" "401": *a1 + "403": *a9 "409": *a7 "422": *a4 "429": *a5 @@ -13743,6 +14003,7 @@ paths: schema: $ref: "#/components/schemas/MoodTagTreeEnvelope" "401": *a1 + "403": *a9 "422": *a4 "429": *a5 /api/mood/tags/custom: @@ -13767,6 +14028,7 @@ paths: schema: $ref: "#/components/schemas/MoodCustomTagCreatedEnvelope" "401": *a1 + "403": *a9 "422": *a4 "429": *a5 /api/mood/tags/custom/{key}: @@ -13796,7 +14058,8 @@ paths: schema: $ref: "#/components/schemas/MoodCustomTagPatchEnvelope" "401": *a1 - "404": &a20 + "403": *a9 + "404": &a27 description: Not found / not owned by the caller. content: application/json: @@ -13831,7 +14094,8 @@ paths: schema: $ref: "#/components/schemas/MoodCustomTagDeleteEnvelope" "401": *a1 - "404": *a20 + "403": *a9 + "404": *a27 "422": *a4 "429": *a5 /api/mood/tags/{key}/hidden: @@ -13867,7 +14131,8 @@ paths: schema: $ref: "#/components/schemas/ErrorEnvelope" "401": *a1 - "404": *a20 + "403": *a9 + "404": *a27 "422": *a4 "429": *a5 /api/mood/tags/groups: @@ -13891,6 +14156,7 @@ paths: schema: $ref: "#/components/schemas/MoodTagGroupCreatedEnvelope" "401": *a1 + "403": *a9 "422": *a4 "429": *a5 /api/mood/tags/groups/{key}: @@ -13920,7 +14186,8 @@ paths: schema: $ref: "#/components/schemas/MoodTagGroupPatchEnvelope" "401": *a1 - "404": *a20 + "403": *a9 + "404": *a27 "422": *a4 "429": *a5 delete: @@ -13951,7 +14218,8 @@ paths: schema: $ref: "#/components/schemas/MoodTagGroupDeleteEnvelope" "401": *a1 - "404": *a20 + "403": *a9 + "404": *a27 "422": *a4 "429": *a5 /api/mood/tags/layout: @@ -13969,6 +14237,7 @@ paths: schema: $ref: "#/components/schemas/MoodTagLayoutGetEnvelope" "401": *a1 + "403": *a9 "422": *a4 "429": *a5 put: @@ -13991,6 +14260,7 @@ paths: schema: $ref: "#/components/schemas/MoodTagLayoutPutEnvelope" "401": *a1 + "403": *a9 "409": description: Mood-tag layout changed since it was loaded (optimistic-concurrency conflict). No write happened. Re-read the resource, re-apply the user's change against the fresh state, and resend with the new token. @@ -13999,7 +14269,7 @@ paths: application/json: schema: $ref: "#/components/schemas/ErrorEnvelope" - "422": *a15 + "422": *a19 "429": *a5 /api/mood-entries: get: @@ -14083,7 +14353,7 @@ paths: schema: $ref: "#/components/schemas/MoodEntryListEnvelope" "401": *a1 - "403": *a11 + "403": *a15 "422": *a4 "429": *a5 post: @@ -14116,8 +14386,8 @@ paths: schema: $ref: "#/components/schemas/MoodEntryEnvelope" "401": *a1 - "403": *a11 - "409": &a21 + "403": *a15 + "409": &a28 description: A mood entry with the same `(date, moodLoggedAt)` instant already exists. `meta.errorCode` = `mood.duplicate_timestamp`. Only a request without `externalId` can answer this — with one, the write upserts in place. @@ -14126,7 +14396,7 @@ paths: schema: $ref: "#/components/schemas/ErrorEnvelope" "422": *a4 - "429": *a12 + "429": *a16 /api/mood-entries/{id}: get: tags: @@ -14148,8 +14418,8 @@ paths: schema: $ref: "#/components/schemas/MoodEntryEnvelope" "401": *a1 - "403": *a11 - "404": *a20 + "403": *a15 + "404": *a27 "422": *a4 "429": *a5 put: @@ -14183,9 +14453,9 @@ paths: schema: $ref: "#/components/schemas/MoodEntryEnvelope" "401": *a1 - "403": *a11 - "404": *a20 - "409": *a21 + "403": *a15 + "404": *a27 + "409": *a28 "422": *a4 "429": *a5 delete: @@ -14210,8 +14480,8 @@ paths: schema: $ref: "#/components/schemas/MoodEntryDeleteEnvelope" "401": *a1 - "403": *a11 - "404": *a20 + "403": *a15 + "404": *a27 "422": *a4 "429": *a5 /api/mood-entries/restore: @@ -14240,7 +14510,7 @@ paths: schema: $ref: "#/components/schemas/MoodEntriesRestoreEnvelope" "401": *a1 - "403": *a11 + "403": *a15 "409": *a7 "422": *a4 "429": *a5 @@ -14269,7 +14539,7 @@ paths: schema: $ref: "#/components/schemas/MoodEntriesBulkDeleteEnvelope" "401": *a1 - "403": *a11 + "403": *a15 "409": *a7 "422": *a4 "429": *a5 @@ -14313,7 +14583,7 @@ paths: schema: $ref: "#/components/schemas/MoodLinkedContextEnvelope" "401": *a1 - "403": *a13 + "403": *a17 "422": *a4 "429": *a5 /api/settings/ntfy: @@ -14332,6 +14602,7 @@ paths: schema: $ref: "#/components/schemas/GetNtfySettings" "401": *a1 + "403": *a9 "422": *a4 "429": *a5 put: @@ -14361,7 +14632,7 @@ paths: application/json: schema: $ref: "#/components/schemas/PutNtfySettingsResponse" - "400": &a22 + "400": &a29 description: Body is not parseable JSON. content: application/json: @@ -14369,18 +14640,20 @@ paths: $ref: "#/components/schemas/ErrorEnvelope" "401": *a1 "403": - description: The operator switched ntfy off for this instance and the body asked to enable it. Nothing was stored. + description: "`meta.errorCode` = `auth.scope.insufficient` when a Bearer token's scope does not reach this route; the + generic auth gate answers it before the route's own checks run. Otherwise: The operator switched ntfy off + for this instance and the body asked to enable it. Nothing was stored." content: application/json: schema: $ref: "#/components/schemas/ErrorEnvelope" - "413": &a23 + "413": &a30 description: Body exceeds 64 KiB. content: application/json: schema: $ref: "#/components/schemas/ErrorEnvelope" - "415": &a24 + "415": &a31 description: Content-Type is not `application/json`. content: application/json: @@ -14411,6 +14684,7 @@ paths: schema: $ref: "#/components/schemas/GetTelegramSettings" "401": *a1 + "403": *a9 "422": *a4 "429": *a5 put: @@ -14440,11 +14714,12 @@ paths: application/json: schema: $ref: "#/components/schemas/PutTelegramSettingsResponse" - "400": *a22 + "400": *a29 "401": *a1 "403": - description: The operator switched Telegram off for this instance and the body asked to enable it. No webhook was - registered and nothing was stored. + description: "`meta.errorCode` = `auth.scope.insufficient` when a Bearer token's scope does not reach this route; the + generic auth gate answers it before the route's own checks run. Otherwise: The operator switched Telegram + off for this instance and the body asked to enable it. No webhook was registered and nothing was stored." content: application/json: schema: @@ -14455,8 +14730,8 @@ paths: application/json: schema: $ref: "#/components/schemas/ErrorEnvelope" - "413": *a23 - "415": *a24 + "413": *a30 + "415": *a31 "422": description: Body matched neither accepted shape; or `enabled` is true without both a bot token and a chat id; or the deployment's `APP_URL` is not a public HTTPS host on port 80/88/443/8443; or Telegram refused the webhook @@ -14489,6 +14764,7 @@ paths: schema: $ref: "#/components/schemas/GetWebhookSettings" "401": *a1 + "403": *a9 "422": *a4 "429": *a5 put: @@ -14510,10 +14786,11 @@ paths: application/json: schema: $ref: "#/components/schemas/PutWebhookSettingsResponse" - "400": *a22 + "400": *a29 "401": *a1 - "413": *a23 - "415": *a24 + "403": *a9 + "413": *a30 + "415": *a31 "422": description: Body matched neither accepted shape, or `enabled` is true while no webhook URL is stored; or the URL is on a private network the operator has not listed in `NOTIFICATION_PRIVATE_ORIGINS` (`meta.errorCode` = @@ -14540,7 +14817,7 @@ paths: schema: $ref: "#/components/schemas/GetReminderThresholdsResponse" "401": *a1 - "403": *a11 + "403": *a15 "422": *a4 "429": *a5 /api/settings/global-services: @@ -14564,6 +14841,7 @@ paths: schema: $ref: "#/components/schemas/GlobalServiceAvailabilityEnvelope" "401": *a1 + "403": *a9 "422": *a4 "429": *a5 /api/settings/account: @@ -14628,8 +14906,10 @@ paths: $ref: "#/components/schemas/ErrorEnvelope" "403": description: >- - The Bearer token carries a narrow scope. Erasure needs a cookie session or a full-access token; the refusal - happens before any second-factor proof is looked at. + `meta.errorCode` = `auth.scope.insufficient` when a Bearer token's scope does not reach this route; the + generic auth gate answers it before the route's own checks run. Otherwise: The Bearer token carries a narrow + scope. Erasure needs a cookie session or a full-access token; the refusal happens before any second-factor + proof is looked at. Refused: the request was made while the session is acting on another account (`meta.errorCode` = @@ -14677,6 +14957,7 @@ paths: schema: $ref: "#/components/schemas/GetEmailChannelEnvelope" "401": *a1 + "403": *a9 "422": *a4 "429": *a5 put: @@ -14711,6 +14992,7 @@ paths: schema: $ref: "#/components/schemas/ErrorEnvelope" "401": *a1 + "403": *a9 "422": *a4 "429": *a5 /api/settings/email/test: @@ -14734,6 +15016,7 @@ paths: schema: $ref: "#/components/schemas/EmailTestEnvelope" "401": *a1 + "403": *a9 "422": *a4 "429": *a5 "500": @@ -14771,6 +15054,7 @@ paths: schema: $ref: "#/components/schemas/NtfyTestEnvelope" "401": *a1 + "403": *a9 "422": description: The saved server is on a private network the operator has not approved (`meta.errorCode` = `private_origin_not_approved`), or is a link-local, metadata or unspecified address that no grant can open @@ -14820,6 +15104,7 @@ paths: schema: $ref: "#/components/schemas/TelegramTestEnvelope" "401": *a1 + "403": *a9 "422": *a4 "429": *a5 /api/settings/webhook/test: @@ -14842,6 +15127,7 @@ paths: schema: $ref: "#/components/schemas/WebhookTestEnvelope" "401": *a1 + "403": *a9 "422": description: The saved URL is on a private network the operator has not approved (`meta.errorCode` = `private_origin_not_approved`), or is a link-local, metadata or unspecified address that no grant can open @@ -14933,8 +15219,10 @@ paths: $ref: "#/components/schemas/ErrorEnvelope" "403": description: >- - The Bearer token carries a narrow scope. Erasure needs a cookie session or a full-access token; the refusal - happens before any second-factor proof is looked at. + `meta.errorCode` = `auth.scope.insufficient` when a Bearer token's scope does not reach this route; the + generic auth gate answers it before the route's own checks run. Otherwise: The Bearer token carries a narrow + scope. Erasure needs a cookie session or a full-access token; the refusal happens before any second-factor + proof is looked at. Refused: the request was made while the session is acting on another account (`meta.errorCode` = @@ -14976,6 +15264,7 @@ paths: schema: $ref: "#/components/schemas/ModuleListLayoutEnvelope" "401": *a1 + "403": *a9 "422": *a4 "429": *a5 put: @@ -14998,6 +15287,7 @@ paths: schema: $ref: "#/components/schemas/ModuleListLayoutSavedEnvelope" "401": *a1 + "403": *a9 "422": *a4 "429": *a5 /api/settings/preview-features: @@ -15014,6 +15304,7 @@ paths: schema: $ref: "#/components/schemas/PreviewFeaturesEnvelope" "401": *a1 + "403": *a9 "422": *a4 "429": *a5 patch: @@ -15035,6 +15326,7 @@ paths: schema: $ref: "#/components/schemas/PreviewFeaturesSavedEnvelope" "401": *a1 + "403": *a9 "422": *a4 "429": *a5 /api/settings/privacy-summary: @@ -15053,6 +15345,7 @@ paths: schema: $ref: "#/components/schemas/PrivacySummaryEnvelope" "401": *a1 + "403": *a9 "422": *a4 "429": *a5 /api/consent/ai: @@ -15091,6 +15384,7 @@ paths: schema: $ref: "#/components/schemas/ConsentValidationErrorEnvelope" "401": *a1 + "403": *a9 "422": *a4 "429": *a5 /api/consent/ai/web: @@ -15123,6 +15417,7 @@ paths: schema: $ref: "#/components/schemas/ErrorEnvelope" "401": *a1 + "403": *a9 "422": *a4 "429": *a5 /api/consent/ai/latest: @@ -15160,7 +15455,7 @@ paths: anyOf: - $ref: "#/components/schemas/ConsentLatestForKindEnvelope" - $ref: "#/components/schemas/ConsentLatestByKindEnvelope" - "400": &a25 + "400": &a32 description: "`kind` was present but is not one of `ai_full`, `ai_insights_only`, `ai_coach`, `ai_extraction`. Multi-issue envelope; nothing was read or revoked. The consent family answers 400 here where the rest of the API answers 422." @@ -15169,6 +15464,7 @@ paths: schema: $ref: "#/components/schemas/ErrorEnvelope" "401": *a1 + "403": *a9 "422": *a4 "429": *a5 delete: @@ -15220,8 +15516,9 @@ paths: anyOf: - $ref: "#/components/schemas/ConsentRevokeForKindEnvelope" - $ref: "#/components/schemas/ConsentRevokeAllEnvelope" - "400": *a25 + "400": *a32 "401": *a1 + "403": *a9 "422": *a4 "429": *a5 /api/import/health-connect-export: @@ -15260,6 +15557,7 @@ paths: schema: $ref: "#/components/schemas/ErrorEnvelope" "401": *a1 + "403": *a9 "409": description: An import for this account (Health Connect or Apple Health) is already queued or running. The upload was discarded. `errorCode` is `import.apple_health.busy` for either kind, `jobId` names the running job. @@ -15302,6 +15600,7 @@ paths: schema: $ref: "#/components/schemas/HealthConnectImportStatusEnvelope" "401": *a1 + "403": *a9 "422": *a4 "429": *a5 /api/import/csv: @@ -15354,6 +15653,7 @@ paths: schema: $ref: "#/components/schemas/CsvImportResponseEnvelope" "401": *a1 + "403": *a9 "413": description: CSV exceeds the 16 MB limit. content: @@ -15549,7 +15849,9 @@ paths: $ref: "#/components/schemas/LabsCsvImportEnvelope" "401": *a1 "403": - description: "`module.disabled`: the module the import writes to is switched off." + description: "`meta.errorCode` = `auth.scope.insufficient` when a Bearer token's scope does not reach this route; the + generic auth gate answers it before the route's own checks run. Otherwise: `module.disabled`: the module the + import writes to is switched off." content: application/json: schema: @@ -15739,7 +16041,9 @@ paths: $ref: "#/components/schemas/MedicationsCsvImportEnvelope" "401": *a1 "403": - description: "`module.disabled`: the module the import writes to is switched off." + description: "`meta.errorCode` = `auth.scope.insufficient` when a Bearer token's scope does not reach this route; the + generic auth gate answers it before the route's own checks run. Otherwise: `module.disabled`: the module the + import writes to is switched off." content: application/json: schema: @@ -15787,6 +16091,7 @@ paths: schema: $ref: "#/components/schemas/JsonImportStatsEnvelope" "401": *a1 + "403": *a9 "413": description: Body exceeds the 16 MB limit. content: @@ -15856,6 +16161,7 @@ paths: schema: $ref: "#/components/schemas/ErrorEnvelope" "401": *a1 + "403": *a9 "409": description: An import for this account is already queued or running, and these bytes are not the ones it carries (the same bytes resolve to that job as a 202). `meta.errorCode` is `import.apple_health.busy` and `meta.jobId` @@ -15924,6 +16230,7 @@ paths: schema: $ref: "#/components/schemas/AppleHealthImportStatusEnvelope" "401": *a1 + "403": *a9 "404": description: No such job, or one the caller may not see. The two are indistinguishable on purpose. content: @@ -15952,7 +16259,7 @@ paths: schema: $ref: "#/components/schemas/MeasurementReminderListEnvelope" "401": *a1 - "403": *a11 + "403": *a15 "422": *a4 "429": *a5 post: @@ -15977,7 +16284,7 @@ paths: schema: $ref: "#/components/schemas/MeasurementReminderCreatedEnvelope" "401": *a1 - "403": *a11 + "403": *a15 "409": *a7 "422": *a4 "429": *a5 @@ -16001,8 +16308,8 @@ paths: schema: $ref: "#/components/schemas/MeasurementReminderEnvelope" "401": *a1 - "403": *a11 - "404": &a26 + "403": *a15 + "404": &a33 description: Measurement reminder not found / not owned. content: application/json: @@ -16039,8 +16346,8 @@ paths: schema: $ref: "#/components/schemas/MeasurementReminderPatchEnvelope" "401": *a1 - "403": *a11 - "404": *a26 + "403": *a15 + "404": *a33 "422": *a4 "429": *a5 delete: @@ -16062,8 +16369,8 @@ paths: schema: $ref: "#/components/schemas/MeasurementReminderDeleteEnvelope" "401": *a1 - "403": *a11 - "404": *a26 + "403": *a15 + "404": *a33 "422": *a4 "429": *a5 /api/measurement-reminders/{id}/satisfy: @@ -16087,8 +16394,8 @@ paths: schema: $ref: "#/components/schemas/MeasurementReminderSatisfyEnvelope" "401": *a1 - "403": *a11 - "404": *a26 + "403": *a15 + "404": *a33 "422": *a4 "429": *a5 /api/measurement-reminders/{id}/complete: @@ -16115,8 +16422,8 @@ paths: schema: $ref: "#/components/schemas/MeasurementReminderCompleteEnvelope" "401": *a1 - "403": *a11 - "404": *a26 + "403": *a15 + "404": *a33 "422": *a4 "429": *a5 /api/measurement-reminders/{id}/skip: @@ -16145,8 +16452,8 @@ paths: schema: $ref: "#/components/schemas/MeasurementReminderSkipEnvelope" "401": *a1 - "403": *a11 - "404": *a26 + "403": *a15 + "404": *a33 "422": *a4 "429": *a5 /api/measurement-reminders/{id}/snooze: @@ -16179,8 +16486,8 @@ paths: schema: $ref: "#/components/schemas/MeasurementReminderSnoozeEnvelope" "401": *a1 - "403": *a11 - "404": *a26 + "403": *a15 + "404": *a33 "422": *a4 "429": *a5 /api/measurement-reminders/{id}/history: @@ -16220,8 +16527,8 @@ paths: schema: $ref: "#/components/schemas/MeasurementReminderHistoryEnvelope" "401": *a1 - "403": *a11 - "404": *a26 + "403": *a15 + "404": *a33 "422": *a4 "429": *a5 /api/labs: @@ -16299,7 +16606,7 @@ paths: schema: $ref: "#/components/schemas/ListLabResultsEnvelope" "401": *a1 - "403": *a11 + "403": *a15 "422": *a4 "429": *a5 post: @@ -16326,7 +16633,7 @@ paths: schema: $ref: "#/components/schemas/CreateLabResultResponse" "401": *a1 - "403": *a11 + "403": *a15 "409": *a7 "422": description: "Nothing was written. `meta.errorCode` = `labs.create.invalid` when the body failed validation (every issue @@ -16341,7 +16648,7 @@ paths: application/json: schema: $ref: "#/components/schemas/ErrorEnvelope" - "429": *a12 + "429": *a16 /api/labs/overview: get: tags: @@ -16359,7 +16666,7 @@ paths: schema: $ref: "#/components/schemas/LabOverviewEnvelope" "401": *a1 - "403": *a11 + "403": *a15 "422": *a4 "429": *a5 /api/labs/{id}: @@ -16382,8 +16689,8 @@ paths: schema: $ref: "#/components/schemas/GetLabResultResponse" "401": *a1 - "403": *a11 - "404": &a27 + "403": *a15 + "404": &a34 description: Lab result not found (or owned by another user). content: application/json: @@ -16417,8 +16724,8 @@ paths: schema: $ref: "#/components/schemas/UpdateLabResultResponse" "401": *a1 - "403": *a11 - "404": *a27 + "403": *a15 + "404": *a34 "422": *a4 "429": *a5 delete: @@ -16440,8 +16747,8 @@ paths: schema: $ref: "#/components/schemas/DeleteLabResultResponse" "401": *a1 - "403": *a11 - "404": *a27 + "403": *a15 + "404": *a34 "422": *a4 "429": *a5 /api/labs/restore: @@ -16467,7 +16774,7 @@ paths: schema: $ref: "#/components/schemas/RestoreLabResultsResponse" "401": *a1 - "403": *a11 + "403": *a15 "409": *a7 "422": *a4 "429": *a5 @@ -16486,6 +16793,7 @@ paths: schema: $ref: "#/components/schemas/OcrCapabilityEnvelope" "401": *a1 + "403": *a9 "422": *a4 "429": *a5 /api/labs/ocr/extract: @@ -16528,7 +16836,7 @@ paths: schema: $ref: "#/components/schemas/OcrTextExtractRequest" responses: - "202": &a37 + "202": &a44 description: Queued for the background worker. Every refusal the route can answer quickly (capability, provider, rate limit, budget, validation) has already been answered; anything that goes wrong from here is reported on the run. @@ -16552,15 +16860,16 @@ paths: $ref: "#/components/schemas/AiRunAcceptedEnvelope" "401": *a1 "403": - description: "AI refusal: the `labsOcr` capability is unavailable for this record. The envelope carries - `meta.errorCode`, `meta.capability` and `meta.reason` (one of `AiUnavailableReason`). - `assistant.disabled.documentAi` — the operator turned off reading documents, or every AI feature (the master - switch). `module.disabled` with `meta.module = \"labs\"` — the module is off for this record. - `ai.record.notPermitted` — AI work is not admitted inside this record (a delegate acting in somebody else's - record). `consent.ai.required` — the provider this request would reach leaves the machine and the record - holds no active `ai_extraction` or `ai_full` receipt; only a self-hosted model is exempt. The question is - answered for the provider actually picked, so it can differ from the published capability when a text-mode - read takes a different provider than a vision read would." + description: "`meta.errorCode` = `auth.scope.insufficient` when a Bearer token's scope does not reach this route; the + generic auth gate answers it before the route's own checks run. Otherwise: AI refusal: the `labsOcr` + capability is unavailable for this record. The envelope carries `meta.errorCode`, `meta.capability` and + `meta.reason` (one of `AiUnavailableReason`). `assistant.disabled.documentAi` — the operator turned off + reading documents, or every AI feature (the master switch). `module.disabled` with `meta.module = \"labs\"` + — the module is off for this record. `ai.record.notPermitted` — AI work is not admitted inside this record + (a delegate acting in somebody else's record). `consent.ai.required` — the provider this request would reach + leaves the machine and the record holds no active `ai_extraction` or `ai_full` receipt; only a self-hosted + model is exempt. The question is answered for the provider actually picked, so it can differ from the + published capability when a text-mode read takes a different provider than a vision read would." content: application/json: schema: @@ -16599,6 +16908,7 @@ paths: schema: $ref: "#/components/schemas/OcrCommitEnvelope" "401": *a1 + "403": *a9 "409": *a7 "422": *a4 "429": *a5 @@ -16626,6 +16936,7 @@ paths: schema: $ref: "#/components/schemas/AiRunEnvelope" "401": *a1 + "403": *a9 "404": description: "`aiRuns.notFound`." content: @@ -16649,7 +16960,7 @@ paths: schema: $ref: "#/components/schemas/ListBiomarkersEnvelope" "401": *a1 - "403": *a11 + "403": *a15 "422": *a4 "429": *a5 post: @@ -16674,7 +16985,7 @@ paths: schema: $ref: "#/components/schemas/CreateBiomarkerResponse" "401": *a1 - "403": *a11 + "403": *a15 "409": description: A biomarker with this name already exists for the caller. content: @@ -16682,7 +16993,7 @@ paths: schema: $ref: "#/components/schemas/ErrorEnvelope" "422": *a4 - "429": *a12 + "429": *a16 /api/biomarkers/{id}: get: tags: @@ -16705,8 +17016,8 @@ paths: schema: $ref: "#/components/schemas/GetBiomarkerResponse" "401": *a1 - "403": *a11 - "404": &a28 + "403": *a15 + "404": &a35 description: "Biomarker not found (or owned by another user). `meta.errorCode` = `labs.biomarker.notFound`. Since v1.43 a marker that was merged into another answers with `meta.mergedInto` naming the marker it went into: drop the cached marker and open that one." @@ -16747,8 +17058,8 @@ paths: schema: $ref: "#/components/schemas/UpdateBiomarkerResponse" "401": *a1 - "403": *a11 - "404": *a28 + "403": *a15 + "404": *a35 "409": description: "`labs.biomarker.duplicate`: a rename collides with another of the caller's markers. `labs.biomarker.alias.taken` (v1.43): one of `aliases` is already the name or an alias of another marker; @@ -16781,8 +17092,8 @@ paths: schema: $ref: "#/components/schemas/DeleteBiomarkerResponse" "401": *a1 - "403": *a11 - "404": *a28 + "403": *a15 + "404": *a35 "422": *a4 "429": *a5 /api/biomarkers/{id}/merge: @@ -16819,8 +17130,8 @@ paths: schema: $ref: "#/components/schemas/MergeBiomarkersResponse" "401": *a1 - "403": *a11 - "404": *a28 + "403": *a15 + "404": *a35 "409": *a7 "422": description: "`labs.biomarker.merge.invalid`: a malformed body, or the target among the sources (Zod issues in @@ -16831,7 +17142,7 @@ paths: application/json: schema: $ref: "#/components/schemas/ErrorEnvelope" - "429": *a12 + "429": *a16 /api/encounters: get: tags: @@ -16894,7 +17205,7 @@ paths: schema: $ref: "#/components/schemas/ListEncountersEnvelope" "401": *a1 - "403": *a11 + "403": *a15 "422": *a4 "429": *a5 post: @@ -16920,10 +17231,10 @@ paths: schema: $ref: "#/components/schemas/CreateEncounterEnvelope" "401": *a1 - "403": *a11 + "403": *a15 "409": *a7 "422": *a4 - "429": *a12 + "429": *a16 /api/encounters/procedures: get: tags: @@ -16954,7 +17265,7 @@ paths: schema: $ref: "#/components/schemas/ListProceduresEnvelope" "401": *a1 - "403": *a11 + "403": *a15 "422": *a4 "429": *a5 /api/body-sites: @@ -16991,7 +17302,7 @@ paths: schema: $ref: "#/components/schemas/ListBodySitesEnvelope" "401": *a1 - "403": *a11 + "403": *a15 "422": *a4 "429": *a5 /api/encounters/suggest: @@ -17021,7 +17332,7 @@ paths: schema: $ref: "#/components/schemas/SuggestEncounterEnvelope" "401": *a1 - "403": *a11 + "403": *a15 "422": *a4 "429": *a5 /api/encounters/{id}: @@ -17044,8 +17355,8 @@ paths: schema: $ref: "#/components/schemas/GetEncounterEnvelope" "401": *a1 - "403": *a11 - "404": &a29 + "403": *a15 + "404": &a36 description: Visit not found (or owned by another user). content: application/json: @@ -17078,8 +17389,8 @@ paths: schema: $ref: "#/components/schemas/UpdateEncounterEnvelope" "401": *a1 - "403": *a11 - "404": *a29 + "403": *a15 + "404": *a36 "422": *a4 "429": *a5 delete: @@ -17103,8 +17414,8 @@ paths: schema: $ref: "#/components/schemas/DeleteEncounterEnvelope" "401": *a1 - "403": *a11 - "404": *a29 + "403": *a15 + "404": *a36 "422": *a4 "429": *a5 /api/encounters/{id}/restore: @@ -17129,8 +17440,8 @@ paths: schema: $ref: "#/components/schemas/RestoreEncounterEnvelope" "401": *a1 - "403": *a11 - "404": *a29 + "403": *a15 + "404": *a36 "422": *a4 "429": *a5 /api/encounters/{id}/links: @@ -17161,7 +17472,8 @@ paths: schema: $ref: "#/components/schemas/LinkEncounterEnvelope" "401": *a1 - "404": *a29 + "403": *a9 + "404": *a36 "422": *a4 "429": *a5 delete: @@ -17190,7 +17502,8 @@ paths: schema: $ref: "#/components/schemas/UnlinkEncounterEnvelope" "401": *a1 - "404": *a29 + "403": *a9 + "404": *a36 "422": *a4 "429": *a5 /api/practitioners: @@ -17230,7 +17543,7 @@ paths: schema: $ref: "#/components/schemas/ListPractitionersEnvelope" "401": *a1 - "403": *a11 + "403": *a15 "422": *a4 "429": *a5 post: @@ -17254,7 +17567,7 @@ paths: schema: $ref: "#/components/schemas/CreatePractitionerEnvelope" "401": *a1 - "403": *a11 + "403": *a15 "409": *a7 "422": *a4 "429": *a5 @@ -17278,8 +17591,8 @@ paths: schema: $ref: "#/components/schemas/GetPractitionerEnvelope" "401": *a1 - "403": *a11 - "404": &a30 + "403": *a15 + "404": &a37 description: Practitioner not found (or owned by another user). content: application/json: @@ -17312,8 +17625,8 @@ paths: schema: $ref: "#/components/schemas/UpdatePractitionerEnvelope" "401": *a1 - "403": *a11 - "404": *a30 + "403": *a15 + "404": *a37 "422": *a4 "429": *a5 delete: @@ -17337,8 +17650,8 @@ paths: schema: $ref: "#/components/schemas/DeletePractitionerEnvelope" "401": *a1 - "403": *a11 - "404": *a30 + "403": *a15 + "404": *a37 "422": *a4 "429": *a5 /api/practitioners/{id}/restore: @@ -17363,8 +17676,8 @@ paths: schema: $ref: "#/components/schemas/RestorePractitionerEnvelope" "401": *a1 - "403": *a11 - "404": *a30 + "403": *a15 + "404": *a37 "422": *a4 "429": *a5 /api/vaccinations/custom: @@ -17382,7 +17695,7 @@ paths: schema: $ref: "#/components/schemas/ListCustomVaccinesEnvelope" "401": *a1 - "403": *a11 + "403": *a15 "422": *a4 "429": *a5 post: @@ -17407,8 +17720,8 @@ paths: schema: $ref: "#/components/schemas/CreateCustomVaccineEnvelope" "401": *a1 - "403": *a11 - "409": &a31 + "403": *a15 + "409": &a38 description: A live definition on the record already carries this name, compared without case (`vaccination.custom.name-taken`). content: @@ -17416,7 +17729,7 @@ paths: schema: $ref: "#/components/schemas/ErrorEnvelope" "422": *a4 - "429": *a12 + "429": *a16 /api/vaccinations/custom/{id}: patch: tags: @@ -17444,14 +17757,14 @@ paths: schema: $ref: "#/components/schemas/UpdateCustomVaccineEnvelope" "401": *a1 - "403": *a11 - "404": &a32 + "403": *a15 + "404": &a39 description: No live definition with this id on the record (`vaccination.custom.not-found`). content: application/json: schema: $ref: "#/components/schemas/ErrorEnvelope" - "409": *a31 + "409": *a38 "422": *a4 "429": *a5 delete: @@ -17475,8 +17788,8 @@ paths: schema: $ref: "#/components/schemas/DeleteCustomVaccineEnvelope" "401": *a1 - "403": *a11 - "404": *a32 + "403": *a15 + "404": *a39 "422": *a4 "429": *a5 /api/vaccinations: @@ -17529,7 +17842,7 @@ paths: schema: $ref: "#/components/schemas/ListVaccinationsEnvelope" "401": *a1 - "403": *a11 + "403": *a15 "422": *a4 "429": *a5 post: @@ -17556,7 +17869,7 @@ paths: schema: $ref: "#/components/schemas/CreateVaccinationEnvelope" "401": *a1 - "403": *a11 + "403": *a15 "404": description: A practice, visit or own vaccine the body names is not the record's (`vaccination.practitioner-not-found`, `vaccination.encounter-not-found`, `vaccination.custom-vaccine-not-found`). @@ -17566,7 +17879,7 @@ paths: $ref: "#/components/schemas/ErrorEnvelope" "409": *a7 "422": *a4 - "429": *a12 + "429": *a16 /api/vaccinations/{id}: get: tags: @@ -17587,8 +17900,8 @@ paths: schema: $ref: "#/components/schemas/GetVaccinationEnvelope" "401": *a1 - "403": *a11 - "404": &a33 + "403": *a15 + "404": &a40 description: Vaccination not found (or owned by another user). content: application/json: @@ -17622,8 +17935,8 @@ paths: schema: $ref: "#/components/schemas/UpdateVaccinationEnvelope" "401": *a1 - "403": *a11 - "404": *a33 + "403": *a15 + "404": *a40 "422": *a4 "429": *a5 delete: @@ -17647,8 +17960,8 @@ paths: schema: $ref: "#/components/schemas/DeleteVaccinationEnvelope" "401": *a1 - "403": *a11 - "404": *a33 + "403": *a15 + "404": *a40 "422": *a4 "429": *a5 /api/vaccinations/{id}/restore: @@ -17672,8 +17985,8 @@ paths: schema: $ref: "#/components/schemas/RestoreVaccinationEnvelope" "401": *a1 - "403": *a11 - "404": *a33 + "403": *a15 + "404": *a40 "422": *a4 "429": *a5 /api/vaccinations/{id}/booster: @@ -17722,8 +18035,10 @@ paths: "401": *a1 "403": description: >- - Refused: the caller's grant does not open this record's measurements, and the booster reminder lives there - (`meta.errorCode` = `vaccination.booster-out-of-scope`). Nothing was written. + `meta.errorCode` = `auth.scope.insufficient` when a Bearer token's scope does not reach this route; the + generic auth gate answers it before the route's own checks run. Otherwise: Refused: the caller's grant does + not open this record's measurements, and the booster reminder lives there (`meta.errorCode` = + `vaccination.booster-out-of-scope`). Nothing was written. Refused: this request named a record the caller may not act on (`meta.errorCode` = `sharing.access.denied`). @@ -17739,7 +18054,7 @@ paths: application/json: schema: $ref: "#/components/schemas/ErrorEnvelope" - "404": *a33 + "404": *a40 "422": *a4 "429": *a5 /api/vaccinations/suggest: @@ -17767,7 +18082,7 @@ paths: schema: $ref: "#/components/schemas/VaccinationSuggestEnvelope" "401": *a1 - "403": *a11 + "403": *a15 "422": *a4 "429": *a5 /api/vaccinations/{id}/links: @@ -17799,7 +18114,8 @@ paths: schema: $ref: "#/components/schemas/LinkVaccinationEnvelope" "401": *a1 - "404": *a33 + "403": *a9 + "404": *a40 "422": *a4 "429": *a5 delete: @@ -17828,7 +18144,8 @@ paths: schema: $ref: "#/components/schemas/UnlinkVaccinationEnvelope" "401": *a1 - "404": *a33 + "403": *a9 + "404": *a40 "422": *a4 "429": *a5 /api/illness/episodes: @@ -17892,7 +18209,7 @@ paths: schema: $ref: "#/components/schemas/ListIllnessEpisodesEnvelope" "401": *a1 - "403": *a11 + "403": *a15 "422": *a4 "429": *a5 post: @@ -17918,7 +18235,7 @@ paths: schema: $ref: "#/components/schemas/CreateIllnessEpisodeEnvelope" "401": *a1 - "403": *a11 + "403": *a15 "409": *a7 "422": *a4 "429": *a5 @@ -17942,8 +18259,8 @@ paths: schema: $ref: "#/components/schemas/GetIllnessEpisodeEnvelope" "401": *a1 - "403": *a11 - "404": &a34 + "403": *a15 + "404": &a41 description: Illness episode not found (or owned by another user). content: application/json: @@ -17977,8 +18294,8 @@ paths: schema: $ref: "#/components/schemas/UpdateIllnessEpisodeEnvelope" "401": *a1 - "403": *a11 - "404": *a34 + "403": *a15 + "404": *a41 "422": *a4 "429": *a5 delete: @@ -18001,8 +18318,8 @@ paths: schema: $ref: "#/components/schemas/DeleteIllnessEpisodeEnvelope" "401": *a1 - "403": *a11 - "404": *a34 + "403": *a15 + "404": *a41 "422": *a4 "429": *a5 /api/illness/episodes/{id}/restore: @@ -18029,8 +18346,8 @@ paths: schema: $ref: "#/components/schemas/RestoreIllnessEpisodeEnvelope" "401": *a1 - "403": *a11 - "404": *a34 + "403": *a15 + "404": *a41 "409": *a7 "422": *a4 "429": *a5 @@ -18062,8 +18379,8 @@ paths: schema: $ref: "#/components/schemas/ResolveIllnessEpisodeEnvelope" "401": *a1 - "403": *a11 - "404": *a34 + "403": *a15 + "404": *a41 "422": *a4 "429": *a5 /api/illness/episodes/{id}/day-logs: @@ -18113,8 +18430,8 @@ paths: schema: $ref: "#/components/schemas/GetIllnessDayLogEnvelope" "401": *a1 - "403": *a11 - "404": *a34 + "403": *a15 + "404": *a41 "422": *a4 "429": *a5 post: @@ -18150,8 +18467,8 @@ paths: schema: $ref: "#/components/schemas/UpsertIllnessDayLogCreatedEnvelope" "401": *a1 - "403": *a11 - "404": *a34 + "403": *a15 + "404": *a41 "422": *a4 "429": *a5 /api/illness/episodes/{id}/correlation: @@ -18178,7 +18495,8 @@ paths: schema: $ref: "#/components/schemas/IllnessCorrelationEnvelope" "401": *a1 - "404": *a34 + "403": *a9 + "404": *a41 "422": *a4 "429": *a5 /api/illness/insights: @@ -18212,6 +18530,7 @@ paths: schema: $ref: "#/components/schemas/IllnessInsightsEnvelope" "401": *a1 + "403": *a9 "422": *a4 "429": *a5 /api/mental-health/assessments: @@ -18251,7 +18570,7 @@ paths: schema: $ref: "#/components/schemas/ListMentalHealthAssessmentsEnvelope" "401": *a1 - "403": *a11 + "403": *a15 "422": *a4 "429": *a5 post: @@ -18284,7 +18603,7 @@ paths: schema: $ref: "#/components/schemas/CreateMentalHealthAssessmentEnvelope" "401": *a1 - "403": *a11 + "403": *a15 "409": *a7 "422": *a4 "429": *a5 @@ -18311,7 +18630,7 @@ paths: schema: $ref: "#/components/schemas/GetMentalHealthAssessmentEnvelope" "401": *a1 - "403": *a11 + "403": *a15 "422": *a4 "429": *a5 /api/allergies: @@ -18344,7 +18663,7 @@ paths: schema: $ref: "#/components/schemas/ListAllergiesEnvelope" "401": *a1 - "403": *a11 + "403": *a15 "422": *a4 "429": *a5 post: @@ -18369,10 +18688,10 @@ paths: schema: $ref: "#/components/schemas/CreateAllergyEnvelope" "401": *a1 - "403": *a11 + "403": *a15 "409": *a7 "422": *a4 - "429": *a12 + "429": *a16 /api/allergies/{id}: get: tags: @@ -18393,8 +18712,8 @@ paths: schema: $ref: "#/components/schemas/GetAllergyEnvelope" "401": *a1 - "403": *a11 - "404": &a35 + "403": *a15 + "404": &a42 description: Allergy not found (or owned by another user). content: application/json: @@ -18427,8 +18746,8 @@ paths: schema: $ref: "#/components/schemas/UpdateAllergyEnvelope" "401": *a1 - "403": *a11 - "404": *a35 + "403": *a15 + "404": *a42 "422": *a4 "429": *a5 delete: @@ -18451,8 +18770,8 @@ paths: schema: $ref: "#/components/schemas/DeleteAllergyEnvelope" "401": *a1 - "403": *a11 - "404": *a35 + "403": *a15 + "404": *a42 "422": *a4 "429": *a5 /api/family-history: @@ -18476,7 +18795,7 @@ paths: schema: $ref: "#/components/schemas/ListFamilyHistoryEnvelope" "401": *a1 - "403": *a11 + "403": *a15 "422": *a4 "429": *a5 post: @@ -18501,7 +18820,7 @@ paths: schema: $ref: "#/components/schemas/CreateFamilyHistoryEnvelope" "401": *a1 - "403": *a11 + "403": *a15 "409": *a7 "422": *a4 "429": *a5 @@ -18525,8 +18844,8 @@ paths: schema: $ref: "#/components/schemas/GetFamilyHistoryEnvelope" "401": *a1 - "403": *a11 - "404": &a36 + "403": *a15 + "404": &a43 description: Family history entry not found (or owned by another user). content: application/json: @@ -18559,8 +18878,8 @@ paths: schema: $ref: "#/components/schemas/UpdateFamilyHistoryEnvelope" "401": *a1 - "403": *a11 - "404": *a36 + "403": *a15 + "404": *a43 "422": *a4 "429": *a5 delete: @@ -18583,8 +18902,8 @@ paths: schema: $ref: "#/components/schemas/DeleteFamilyHistoryEnvelope" "401": *a1 - "403": *a11 - "404": *a36 + "403": *a15 + "404": *a43 "422": *a4 "429": *a5 /api/documents/inbound: @@ -18699,7 +19018,7 @@ paths: schema: $ref: "#/components/schemas/InboundDocumentListEnvelope" "401": *a1 - "403": *a11 + "403": *a15 "422": *a4 "429": *a5 post: @@ -18866,6 +19185,7 @@ paths: schema: $ref: "#/components/schemas/DocumentUploadEnvelope" "401": *a1 + "403": *a9 "409": description: The same bytes are already stored under 20 other import source ids (`documents.inbound.sourceAliasLimit`). Nothing was stored or remembered. @@ -18935,6 +19255,7 @@ paths: schema: $ref: "#/components/schemas/DocumentSourceLookupEnvelope" "401": *a1 + "403": *a9 "422": *a4 "429": *a5 /api/documents/inbound/capability: @@ -18960,6 +19281,7 @@ paths: schema: $ref: "#/components/schemas/DocumentAiCapabilityEnvelope" "401": *a1 + "403": *a9 "422": *a4 "429": *a5 /api/documents/inbound/usage: @@ -18982,7 +19304,7 @@ paths: schema: $ref: "#/components/schemas/DocumentUsageEnvelope" "401": *a1 - "403": *a11 + "403": *a15 "422": *a4 "429": *a5 /api/documents/inbound/layout: @@ -19000,7 +19322,7 @@ paths: schema: $ref: "#/components/schemas/DocumentsLayoutEnvelope" "401": *a1 - "403": *a11 + "403": *a15 "422": *a4 "429": *a5 put: @@ -19023,6 +19345,7 @@ paths: schema: $ref: "#/components/schemas/DocumentsLayoutSavedEnvelope" "401": *a1 + "403": *a9 "422": *a4 "429": *a5 /api/documents/inbound/bulk: @@ -19047,6 +19370,7 @@ paths: schema: $ref: "#/components/schemas/DocumentBulkEnvelope" "401": *a1 + "403": *a9 "422": *a4 "429": *a5 /api/documents/inbound/{id}: @@ -19069,7 +19393,7 @@ paths: schema: $ref: "#/components/schemas/InboundDocumentDetailEnvelope" "401": *a1 - "403": *a11 + "403": *a15 "422": *a4 "429": *a5 patch: @@ -19098,6 +19422,7 @@ paths: schema: $ref: "#/components/schemas/InboundDocumentUpdateEnvelope" "401": *a1 + "403": *a9 "422": *a4 "429": *a5 delete: @@ -19120,6 +19445,7 @@ paths: schema: $ref: "#/components/schemas/InboundDiscardEnvelope" "401": *a1 + "403": *a9 "422": *a4 "429": *a5 /api/documents/inbound/{id}/restore: @@ -19144,6 +19470,7 @@ paths: schema: $ref: "#/components/schemas/InboundRestoreEnvelope" "401": *a1 + "403": *a9 "409": description: "Not restorable: purged past the grace window, or an identical live document exists." content: @@ -19188,7 +19515,7 @@ paths: type: string format: binary "401": *a1 - "403": *a11 + "403": *a15 "422": *a4 "429": *a5 /api/documents/inbound/{id}/thumbnail: @@ -19217,7 +19544,7 @@ paths: type: string format: binary "401": *a1 - "403": *a11 + "403": *a15 "422": *a4 "429": *a5 /api/documents/inbound/{id}/extract: @@ -19249,7 +19576,7 @@ paths: required: true schema: type: string - - &a38 + - &a45 name: Prefer in: header required: false @@ -19274,18 +19601,20 @@ paths: application/json: schema: $ref: "#/components/schemas/InboundExtractEnvelope" - "202": *a37 + "202": *a44 "401": *a1 - "403": &a39 - description: "AI refusal: the `documentAi` capability is unavailable for this record. The envelope carries - `meta.errorCode`, `meta.capability` and `meta.reason` (one of `AiUnavailableReason`). - `assistant.disabled.documentAi` — the operator turned off reading documents, or every AI feature (the master - switch). `module.disabled` with `meta.module = \"inboundDocuments\"` — the module is off for this record. - `ai.record.notPermitted` — AI work is not admitted inside this record (a delegate acting in somebody else's - record). `consent.ai.required` — the provider this request would reach leaves the machine and the record - holds no active `ai_extraction` or `ai_full` receipt; only a self-hosted model is exempt. The question is - answered for the provider actually picked, so it can differ from the published capability when a text-mode - read takes a different provider than a vision read would." + "403": &a46 + description: "`meta.errorCode` = `auth.scope.insufficient` when a Bearer token's scope does not reach this route; the + generic auth gate answers it before the route's own checks run. Otherwise: AI refusal: the `documentAi` + capability is unavailable for this record. The envelope carries `meta.errorCode`, `meta.capability` and + `meta.reason` (one of `AiUnavailableReason`). `assistant.disabled.documentAi` — the operator turned off + reading documents, or every AI feature (the master switch). `module.disabled` with `meta.module = + \"inboundDocuments\"` — the module is off for this record. `ai.record.notPermitted` — AI work is not + admitted inside this record (a delegate acting in somebody else's record). `consent.ai.required` — the + provider this request would reach leaves the machine and the record holds no active `ai_extraction` or + `ai_full` receipt; only a self-hosted model is exempt. The question is answered for the provider actually + picked, so it can differ from the published capability when a text-mode read takes a different provider than + a vision read would." content: application/json: schema: @@ -19300,7 +19629,7 @@ paths: $ref: "#/components/schemas/ErrorEnvelope" "422": *a4 "429": *a5 - "503": &a40 + "503": &a47 description: "`ai.unavailable` — the inputs behind the AI capability could not be read, and the answer failed closed. Nothing was sent. Retry later." content: @@ -19330,7 +19659,7 @@ paths: required: true schema: type: string - - *a38 + - *a45 requestBody: required: false content: @@ -19366,12 +19695,12 @@ paths: application/json: schema: $ref: "#/components/schemas/DocumentSuggestEnvelope" - "202": *a37 + "202": *a44 "401": *a1 - "403": *a39 + "403": *a46 "422": *a4 "429": *a5 - "503": *a40 + "503": *a47 /api/documents/inbound/{id}/summary: post: tags: @@ -19421,7 +19750,7 @@ paths: type: boolean default: false description: With `persist=true`, explicitly replace an existing stored summary. - - *a38 + - *a45 requestBody: required: false content: @@ -19457,12 +19786,12 @@ paths: application/json: schema: $ref: "#/components/schemas/DocumentSummaryEnvelope" - "202": *a37 + "202": *a44 "401": *a1 - "403": *a39 + "403": *a46 "422": *a4 "429": *a5 - "503": *a40 + "503": *a47 /api/documents/inbound/{id}/index: post: tags: @@ -19488,7 +19817,7 @@ paths: required: true schema: type: string - - *a38 + - *a45 requestBody: required: false content: @@ -19524,12 +19853,12 @@ paths: application/json: schema: $ref: "#/components/schemas/DocumentIndexEnvelope" - "202": *a37 + "202": *a44 "401": *a1 - "403": *a39 + "403": *a46 "422": *a4 "429": *a5 - "503": *a40 + "503": *a47 /api/documents/inbound/{id}/chat: post: tags: @@ -19569,10 +19898,10 @@ paths: type: string description: "SSE frames: `data: \\n\\n`. See the operation description for the per-`type` frame shapes." "401": *a1 - "403": *a39 + "403": *a46 "422": *a4 "429": *a5 - "503": *a40 + "503": *a47 get: tags: - Documents @@ -19613,6 +19942,7 @@ paths: schema: $ref: "#/components/schemas/DocumentChatHistoryEnvelope" "401": *a1 + "403": *a9 "422": *a4 "429": *a5 /api/documents/inbound/reindex: @@ -19635,10 +19965,10 @@ paths: schema: $ref: "#/components/schemas/DocumentReindexEnvelope" "401": *a1 - "403": *a39 + "403": *a46 "422": *a4 "429": *a5 - "503": *a40 + "503": *a47 /api/documents/inbound/{id}/facts/{factId}: patch: tags: @@ -19671,6 +20001,7 @@ paths: schema: $ref: "#/components/schemas/ExtractedFactEnvelope" "401": *a1 + "403": *a9 "422": *a4 "429": *a5 /api/documents/inbound/{id}/confirm: @@ -19702,6 +20033,7 @@ paths: schema: $ref: "#/components/schemas/InboundConfirmEnvelope" "401": *a1 + "403": *a9 "409": *a7 "422": *a4 "429": *a5 @@ -19720,7 +20052,7 @@ paths: schema: $ref: "#/components/schemas/ListCustomMetricsEnvelope" "401": *a1 - "403": *a11 + "403": *a15 "422": *a4 "429": *a5 post: @@ -19745,7 +20077,8 @@ paths: schema: $ref: "#/components/schemas/CreateCustomMetricResponse" "401": *a1 - "409": &a42 + "403": *a9 + "409": &a49 description: A custom metric with this name already exists for the caller. content: application/json: @@ -19773,8 +20106,8 @@ paths: schema: $ref: "#/components/schemas/GetCustomMetricResponse" "401": *a1 - "403": *a11 - "404": &a41 + "403": *a15 + "404": &a48 description: Custom metric not found (or owned by another user). content: application/json: @@ -19808,8 +20141,9 @@ paths: schema: $ref: "#/components/schemas/UpdateCustomMetricResponse" "401": *a1 - "404": *a41 - "409": *a42 + "403": *a9 + "404": *a48 + "409": *a49 "422": *a4 "429": *a5 delete: @@ -19832,7 +20166,8 @@ paths: schema: $ref: "#/components/schemas/DeleteCustomMetricResponse" "401": *a1 - "404": *a41 + "403": *a9 + "404": *a48 "422": *a4 "429": *a5 /api/custom-metrics/{id}/entries: @@ -19875,8 +20210,8 @@ paths: schema: $ref: "#/components/schemas/ListCustomMetricEntriesEnvelope" "401": *a1 - "403": *a11 - "404": *a41 + "403": *a15 + "404": *a48 "422": *a4 "429": *a5 post: @@ -19905,10 +20240,11 @@ paths: schema: $ref: "#/components/schemas/CreateCustomMetricEntryResponse" "401": *a1 - "404": *a41 + "403": *a9 + "404": *a48 "409": *a7 "422": *a4 - "429": *a12 + "429": *a16 /api/custom-metrics/{id}/entries/{entryId}: patch: tags: @@ -19941,7 +20277,8 @@ paths: schema: $ref: "#/components/schemas/UpdateCustomMetricEntryResponse" "401": *a1 - "404": &a43 + "403": *a9 + "404": &a50 description: Custom metric entry not found (or owned by another user). content: application/json: @@ -19974,7 +20311,8 @@ paths: schema: $ref: "#/components/schemas/DeleteCustomMetricEntryResponse" "401": *a1 - "404": *a43 + "403": *a9 + "404": *a50 "422": *a4 "429": *a5 /api/custom-metrics/{id}/entries/restore: @@ -20005,6 +20343,7 @@ paths: schema: $ref: "#/components/schemas/RestoreCustomMetricEntriesResponse" "401": *a1 + "403": *a9 "422": *a4 "429": *a5 /api/onboarding/answers: @@ -20045,6 +20384,7 @@ paths: schema: $ref: "#/components/schemas/OnboardingStateEnvelope" "401": *a1 + "403": *a9 "413": description: Body exceeds 16 KiB. content: @@ -20102,6 +20442,7 @@ paths: schema: $ref: "#/components/schemas/OnboardingStateEnvelope" "401": *a1 + "403": *a9 "413": description: Body exceeds 4 KiB. content: @@ -20183,6 +20524,7 @@ paths: schema: $ref: "#/components/schemas/OnboardingCompleteEnvelope" "401": *a1 + "403": *a9 "404": description: "`managedRecordId` names no managed profile the caller actively guards — missing, not a managed record, or not theirs; one answer for all three. `meta.errorCode` = `onboarding.complete.recordNotFound`. Nothing was @@ -20235,6 +20577,7 @@ paths: schema: $ref: "#/components/schemas/DisclaimerAckEnvelope" "401": *a1 + "403": *a9 "422": *a4 "429": *a5 /api/onboarding/tour: @@ -20259,6 +20602,7 @@ paths: schema: $ref: "#/components/schemas/TourUpdateEnvelope" "401": *a1 + "403": *a9 "422": *a4 "429": *a5 /api/nutrients/catalog: @@ -20276,11 +20620,12 @@ paths: schema: $ref: "#/components/schemas/NutrientCatalogEnvelope" "401": *a1 - "403": &a44 + "403": &a51 description: >- - The opt-in `nutrients` module is off for this account (errorCode `module.disabled`). Stop syncing and do not - retry; offer the enable-in-settings hint. Only run the HealthKit read-authorization flow while the module is - on. + `meta.errorCode` = `auth.scope.insufficient` when a Bearer token's scope does not reach this route; the + generic auth gate answers it before the route's own checks run. Otherwise: The opt-in `nutrients` module is + off for this account (errorCode `module.disabled`). Stop syncing and do not retry; offer the + enable-in-settings hint. Only run the HealthKit read-authorization flow while the module is on. Refused: this request named a record the caller may not act on (`meta.errorCode` = `sharing.access.denied`). @@ -20389,9 +20734,10 @@ paths: $ref: "#/components/schemas/NutrientBatchEnvelope" "401": *a1 "403": - description: The opt-in `nutrients` module is off for this account (errorCode `module.disabled`). Stop syncing and do - not retry; offer the enable-in-settings hint. Only run the HealthKit read-authorization flow while the - module is on. + description: "`meta.errorCode` = `auth.scope.insufficient` when a Bearer token's scope does not reach this route; the + generic auth gate answers it before the route's own checks run. Otherwise: The opt-in `nutrients` module is + off for this account (errorCode `module.disabled`). Stop syncing and do not retry; offer the + enable-in-settings hint. Only run the HealthKit read-authorization flow while the module is on." content: application/json: schema: @@ -20423,7 +20769,7 @@ paths: schema: $ref: "#/components/schemas/NutrientOverviewEnvelope" "401": *a1 - "403": *a44 + "403": *a51 "422": *a4 "429": *a5 /api/nutrients/daily: @@ -20460,7 +20806,7 @@ paths: schema: $ref: "#/components/schemas/NutrientDailySeriesEnvelope" "401": *a1 - "403": *a44 + "403": *a51 "422": *a4 "429": *a5 /api/nutrients/water: @@ -20489,7 +20835,7 @@ paths: schema: $ref: "#/components/schemas/NutrientWaterWriteEnvelope" "401": *a1 - "403": *a44 + "403": *a51 "409": *a7 "422": *a4 "429": *a5 @@ -20508,6 +20854,7 @@ paths: schema: $ref: "#/components/schemas/HealthKitConfigEnvelope" "401": *a1 + "403": *a9 "422": *a4 "429": *a5 patch: @@ -20531,6 +20878,7 @@ paths: schema: $ref: "#/components/schemas/HealthKitConfigPatchEnvelope" "401": *a1 + "403": *a9 "422": *a4 "429": *a5 /api/oura/sync: @@ -20548,6 +20896,7 @@ paths: schema: $ref: "#/components/schemas/OuraSyncEnvelope" "401": *a1 + "403": *a9 "422": *a4 "429": *a5 "502": @@ -20573,6 +20922,7 @@ paths: schema: $ref: "#/components/schemas/PolarSyncEnvelope" "401": *a1 + "403": *a9 "422": *a4 "429": *a5 "502": @@ -20600,6 +20950,7 @@ paths: schema: $ref: "#/components/schemas/StravaSyncEnvelope" "401": *a1 + "403": *a9 "422": *a4 "429": *a5 "502": @@ -20626,6 +20977,7 @@ paths: schema: $ref: "#/components/schemas/NightscoutSyncEnvelope" "401": *a1 + "403": *a9 "422": description: The configured Nightscout origin is private (RFC 1918 / loopback / link-local) and the server operator has not approved private-origin access for this deployment. @@ -20659,6 +21011,7 @@ paths: schema: $ref: "#/components/schemas/GoogleHealthSyncStatusEnvelope" "401": *a1 + "403": *a9 "422": *a4 "429": *a5 /api/nightscout/connect: @@ -20691,6 +21044,7 @@ paths: schema: $ref: "#/components/schemas/ErrorEnvelope" "401": *a1 + "403": *a9 "413": description: Body exceeds 16 KiB. content: @@ -20728,6 +21082,7 @@ paths: schema: $ref: "#/components/schemas/NightscoutStatusEnvelope" "401": *a1 + "403": *a9 "422": *a4 "429": *a5 /api/nightscout/test: @@ -20746,6 +21101,7 @@ paths: schema: $ref: "#/components/schemas/NightscoutTestEnvelope" "401": *a1 + "403": *a9 "422": description: No Nightscout instance is configured for this user. `meta.errorCode` = `not_configured`. content: @@ -20786,6 +21142,7 @@ paths: schema: $ref: "#/components/schemas/NightscoutDisconnectEnvelope" "401": *a1 + "403": *a9 "404": description: "No Nightscout connection to clear. Note that this is NOT an idempotent no-op: a second disconnect answers 404 rather than 200." @@ -20810,6 +21167,7 @@ paths: schema: $ref: "#/components/schemas/WhoopCredentialsStatusEnvelope" "401": *a1 + "403": *a9 "422": *a4 "429": *a5 put: @@ -20838,6 +21196,7 @@ paths: schema: $ref: "#/components/schemas/ErrorEnvelope" "401": *a1 + "403": *a9 "413": description: Body exceeds 64 KiB. content: @@ -20867,6 +21226,7 @@ paths: schema: $ref: "#/components/schemas/WhoopCredentialsDeleteEnvelope" "401": *a1 + "403": *a9 "422": *a4 "429": *a5 /api/whoop/status: @@ -20885,6 +21245,7 @@ paths: schema: $ref: "#/components/schemas/WhoopStatusEnvelope" "401": *a1 + "403": *a9 "422": *a4 "429": *a5 /api/whoop/disconnect: @@ -20905,6 +21266,7 @@ paths: schema: $ref: "#/components/schemas/WhoopDisconnectEnvelope" "401": *a1 + "403": *a9 "404": description: "No WHOOP connection to remove. NOT an idempotent no-op: a second disconnect answers 404 rather than 200." content: @@ -20930,6 +21292,7 @@ paths: schema: $ref: "#/components/schemas/WithingsStatusEnvelope" "401": *a1 + "403": *a9 "422": *a4 "429": *a5 /api/withings/disconnect: @@ -20948,6 +21311,7 @@ paths: schema: $ref: "#/components/schemas/WithingsDisconnectEnvelope" "401": *a1 + "403": *a9 "404": description: "No Withings connection to remove. Note that this is NOT an idempotent no-op: a second disconnect answers 404 rather than 200." @@ -20971,6 +21335,7 @@ paths: schema: $ref: "#/components/schemas/WithingsCredentialsStatusEnvelope" "401": *a1 + "403": *a9 "422": *a4 "429": *a5 put: @@ -20991,26 +21356,27 @@ paths: application/json: schema: $ref: "#/components/schemas/WithingsCredentialsUpdateEnvelope" - "400": &a45 + "400": &a52 description: Body is not parseable JSON. content: application/json: schema: $ref: "#/components/schemas/ErrorEnvelope" "401": *a1 - "413": &a46 + "403": *a9 + "413": &a53 description: Body exceeds 64 KiB. content: application/json: schema: $ref: "#/components/schemas/ErrorEnvelope" - "415": &a47 + "415": &a54 description: Content-Type is not `application/json`. content: application/json: schema: $ref: "#/components/schemas/ErrorEnvelope" - "422": &a48 + "422": &a55 description: "`clientId` and `clientSecret` are both required and each must be 1–200 characters. The envelope carries a single fixed message, not per-field issues." content: @@ -21033,6 +21399,7 @@ paths: schema: $ref: "#/components/schemas/WithingsCredentialsDeleteEnvelope" "401": *a1 + "403": *a9 "422": *a4 "429": *a5 /api/withings/sync: @@ -21063,6 +21430,7 @@ paths: schema: $ref: "#/components/schemas/ErrorEnvelope" "401": *a1 + "403": *a9 "413": description: Body exceeds 64 KiB. content: @@ -21111,6 +21479,7 @@ paths: schema: $ref: "#/components/schemas/ErrorEnvelope" "401": *a1 + "403": *a9 "413": description: Body exceeds 64 KiB. content: @@ -21155,6 +21524,7 @@ paths: schema: $ref: "#/components/schemas/ErrorEnvelope" "401": *a1 + "403": *a9 "422": *a4 "429": description: More than 10 ticket mints in 60 s from this user. @@ -21176,6 +21546,7 @@ paths: schema: $ref: "#/components/schemas/FitbitCredentialsStatusEnvelope" "401": *a1 + "403": *a9 "422": *a4 "429": *a5 put: @@ -21196,11 +21567,12 @@ paths: application/json: schema: $ref: "#/components/schemas/FitbitCredentialsUpdateEnvelope" - "400": *a45 + "400": *a52 "401": *a1 - "413": *a46 - "415": *a47 - "422": *a48 + "403": *a9 + "413": *a53 + "415": *a54 + "422": *a55 "429": *a5 delete: tags: @@ -21217,6 +21589,7 @@ paths: schema: $ref: "#/components/schemas/FitbitCredentialsDeleteEnvelope" "401": *a1 + "403": *a9 "422": *a4 "429": *a5 /api/fitbit/status: @@ -21234,6 +21607,7 @@ paths: schema: $ref: "#/components/schemas/FitbitStatusEnvelope" "401": *a1 + "403": *a9 "422": *a4 "429": *a5 /api/fitbit/disconnect: @@ -21253,6 +21627,7 @@ paths: schema: $ref: "#/components/schemas/FitbitDisconnectEnvelope" "401": *a1 + "403": *a9 "404": description: "No Fitbit connection to remove. NOT an idempotent no-op: a second disconnect answers 404 rather than 200." content: @@ -21289,6 +21664,7 @@ paths: schema: $ref: "#/components/schemas/ErrorEnvelope" "401": *a1 + "403": *a9 "410": description: Google turned the legacy Fitbit Web API off on 2026-10-30; nothing is fetched. `meta.errorCode` = `provider_retired`. The data already imported stays; Google Health reads the same devices. @@ -21330,6 +21706,7 @@ paths: schema: $ref: "#/components/schemas/GoogleHealthCredentialsStatusEnvelope" "401": *a1 + "403": *a9 "422": *a4 "429": *a5 put: @@ -21350,11 +21727,12 @@ paths: application/json: schema: $ref: "#/components/schemas/GoogleHealthCredentialsUpdateEnvelope" - "400": *a45 + "400": *a52 "401": *a1 - "413": *a46 - "415": *a47 - "422": *a48 + "403": *a9 + "413": *a53 + "415": *a54 + "422": *a55 "429": *a5 delete: tags: @@ -21371,6 +21749,7 @@ paths: schema: $ref: "#/components/schemas/GoogleHealthCredentialsDeleteEnvelope" "401": *a1 + "403": *a9 "422": *a4 "429": *a5 /api/google-health/status: @@ -21389,6 +21768,7 @@ paths: schema: $ref: "#/components/schemas/GoogleHealthStatusEnvelope" "401": *a1 + "403": *a9 "422": *a4 "429": *a5 /api/google-health/disconnect: @@ -21407,6 +21787,7 @@ paths: schema: $ref: "#/components/schemas/GoogleHealthDisconnectEnvelope" "401": *a1 + "403": *a9 "404": description: "No Google Health connection to remove. NOT an idempotent no-op: a second disconnect answers 404 rather than 200." @@ -21446,6 +21827,7 @@ paths: schema: $ref: "#/components/schemas/ErrorEnvelope" "401": *a1 + "403": *a9 "409": description: Another sync of this account is already running (the history backfill after a reconnect, or the hourly poll), so none was started. `meta.errorCode` = `sync_in_progress`. `GET /api/google-health/sync/status` @@ -21495,6 +21877,7 @@ paths: schema: $ref: "#/components/schemas/OuraCredentialsStatusEnvelope" "401": *a1 + "403": *a9 "422": *a4 "429": *a5 put: @@ -21515,11 +21898,12 @@ paths: application/json: schema: $ref: "#/components/schemas/OuraCredentialsUpdateEnvelope" - "400": *a45 + "400": *a52 "401": *a1 - "413": *a46 - "415": *a47 - "422": *a48 + "403": *a9 + "413": *a53 + "415": *a54 + "422": *a55 "429": *a5 delete: tags: @@ -21536,6 +21920,7 @@ paths: schema: $ref: "#/components/schemas/OuraCredentialsDeleteEnvelope" "401": *a1 + "403": *a9 "422": *a4 "429": *a5 /api/oura/status: @@ -21554,6 +21939,7 @@ paths: schema: $ref: "#/components/schemas/OuraStatusEnvelope" "401": *a1 + "403": *a9 "422": *a4 "429": *a5 /api/oura/disconnect: @@ -21572,6 +21958,7 @@ paths: schema: $ref: "#/components/schemas/OuraDisconnectEnvelope" "401": *a1 + "403": *a9 "404": description: "No Oura token stored. NOT an idempotent no-op: a second disconnect answers 404 rather than 200." content: @@ -21595,21 +21982,22 @@ paths: schema: $ref: "#/components/schemas/OuraTestEnvelope" "401": *a1 - "422": &a49 + "403": *a9 + "422": &a56 description: No usable connection for this provider — nothing stored, or the stored grant could no longer be resolved into an access token. `meta.errorCode` = `not_configured`. content: application/json: schema: $ref: "#/components/schemas/ErrorEnvelope" - "429": &a50 + "429": &a57 description: More than 5 requests in 60 s from this user. `meta.errorCode` = `rate_limited_self` — this is the local limiter, distinct from a `rate_limited` 502 which is the provider's. content: application/json: schema: $ref: "#/components/schemas/ErrorEnvelope" - "502": &a51 + "502": &a58 description: "The probe did not complete. `meta.errorCode` names the class: `credentials_rejected` (the provider answered 401/403 — the stored grant is gone), `rate_limited` (the PROVIDER answered 429, which is not this endpoint's own limiter), `upstream_error` (5xx), `timeout` (no answer within 5 s), `connection_failed` @@ -21633,6 +22021,7 @@ paths: schema: $ref: "#/components/schemas/PolarCredentialsStatusEnvelope" "401": *a1 + "403": *a9 "422": *a4 "429": *a5 put: @@ -21654,11 +22043,12 @@ paths: application/json: schema: $ref: "#/components/schemas/PolarCredentialsUpdateEnvelope" - "400": *a45 + "400": *a52 "401": *a1 - "413": *a46 - "415": *a47 - "422": *a48 + "403": *a9 + "413": *a53 + "415": *a54 + "422": *a55 "429": *a5 delete: tags: @@ -21675,6 +22065,7 @@ paths: schema: $ref: "#/components/schemas/PolarCredentialsDeleteEnvelope" "401": *a1 + "403": *a9 "422": *a4 "429": *a5 /api/polar/status: @@ -21693,6 +22084,7 @@ paths: schema: $ref: "#/components/schemas/PolarStatusEnvelope" "401": *a1 + "403": *a9 "422": *a4 "429": *a5 /api/polar/disconnect: @@ -21711,6 +22103,7 @@ paths: schema: $ref: "#/components/schemas/PolarDisconnectEnvelope" "401": *a1 + "403": *a9 "404": description: "No Polar token stored. NOT an idempotent no-op: a second disconnect answers 404 rather than 200." content: @@ -21734,9 +22127,10 @@ paths: schema: $ref: "#/components/schemas/PolarTestEnvelope" "401": *a1 - "422": *a49 - "429": *a50 - "502": *a51 + "403": *a9 + "422": *a56 + "429": *a57 + "502": *a58 /api/strava/credentials: get: tags: @@ -21752,6 +22146,7 @@ paths: schema: $ref: "#/components/schemas/StravaCredentialsStatusEnvelope" "401": *a1 + "403": *a9 "422": *a4 "429": *a5 put: @@ -21772,11 +22167,12 @@ paths: application/json: schema: $ref: "#/components/schemas/StravaCredentialsUpdateEnvelope" - "400": *a45 + "400": *a52 "401": *a1 - "413": *a46 - "415": *a47 - "422": *a48 + "403": *a9 + "413": *a53 + "415": *a54 + "422": *a55 "429": *a5 delete: tags: @@ -21794,6 +22190,7 @@ paths: schema: $ref: "#/components/schemas/StravaCredentialsDeleteEnvelope" "401": *a1 + "403": *a9 "422": *a4 "429": *a5 /api/strava/status: @@ -21812,6 +22209,7 @@ paths: schema: $ref: "#/components/schemas/StravaStatusEnvelope" "401": *a1 + "403": *a9 "422": *a4 "429": *a5 /api/strava/disconnect: @@ -21831,6 +22229,7 @@ paths: schema: $ref: "#/components/schemas/StravaDisconnectEnvelope" "401": *a1 + "403": *a9 "404": description: "No Strava connection. NOT an idempotent no-op: a second disconnect answers 404 rather than 200." content: @@ -21854,9 +22253,10 @@ paths: schema: $ref: "#/components/schemas/StravaTestEnvelope" "401": *a1 - "422": *a49 - "429": *a50 - "502": *a51 + "403": *a9 + "422": *a56 + "429": *a57 + "502": *a58 /api/integrations/status: get: tags: @@ -21873,6 +22273,7 @@ paths: schema: $ref: "#/components/schemas/IntegrationStatusEnvelopeResponse" "401": *a1 + "403": *a9 "422": *a4 "429": *a5 /api/integrations/fitbit/resume: @@ -21891,8 +22292,9 @@ paths: schema: $ref: "#/components/schemas/FitbitResumeEnvelope" "401": *a1 + "403": *a9 "422": *a4 - "429": *a50 + "429": *a57 /api/integrations/fitbit/test: post: tags: @@ -21910,6 +22312,7 @@ paths: schema: $ref: "#/components/schemas/FitbitIntegrationTestEnvelope" "401": *a1 + "403": *a9 "410": description: Google turned the legacy Fitbit Web API off on 2026-10-30; nothing is probed. `meta.errorCode` = `provider_retired`. @@ -21917,9 +22320,9 @@ paths: application/json: schema: $ref: "#/components/schemas/ErrorEnvelope" - "422": *a49 - "429": *a50 - "502": *a51 + "422": *a56 + "429": *a57 + "502": *a58 /api/integrations/google-health/resume: post: tags: @@ -21935,8 +22338,9 @@ paths: schema: $ref: "#/components/schemas/GoogleHealthResumeEnvelope" "401": *a1 + "403": *a9 "422": *a4 - "429": *a50 + "429": *a57 /api/integrations/google-health/test: post: tags: @@ -21963,15 +22367,16 @@ paths: schema: $ref: "#/components/schemas/GoogleHealthIntegrationTestEnvelope" "401": *a1 + "403": *a9 "413": description: Body exceeds 64 KiB. content: application/json: schema: $ref: "#/components/schemas/ErrorEnvelope" - "422": *a49 - "429": *a50 - "502": *a51 + "422": *a56 + "429": *a57 + "502": *a58 /api/integrations/whoop/resume: post: tags: @@ -21987,8 +22392,9 @@ paths: schema: $ref: "#/components/schemas/WhoopResumeEnvelope" "401": *a1 + "403": *a9 "422": *a4 - "429": *a50 + "429": *a57 /api/integrations/whoop/test: post: tags: @@ -22005,9 +22411,10 @@ paths: schema: $ref: "#/components/schemas/WhoopIntegrationTestEnvelope" "401": *a1 - "422": *a49 - "429": *a50 - "502": *a51 + "403": *a9 + "422": *a56 + "429": *a57 + "502": *a58 /api/integrations/withings/resume: post: tags: @@ -22024,8 +22431,9 @@ paths: schema: $ref: "#/components/schemas/WithingsResumeEnvelope" "401": *a1 + "403": *a9 "422": *a4 - "429": *a50 + "429": *a57 /api/integrations/withings/test: post: tags: @@ -22043,8 +22451,9 @@ paths: schema: $ref: "#/components/schemas/WithingsIntegrationTestEnvelope" "401": *a1 - "422": *a49 - "429": *a50 + "403": *a9 + "422": *a56 + "429": *a57 "502": description: "The probe did not complete. The shared codes apply (`credentials_rejected`, `rate_limited`, `upstream_error`, `timeout`, `connection_failed`) and this path adds one of its own: @@ -22077,7 +22486,7 @@ paths: schema: $ref: "#/components/schemas/DailyDigestEnvelope" "401": *a1 - "403": *a11 + "403": *a15 "422": *a4 "429": *a5 /api/daily/digest/dismiss: @@ -22112,6 +22521,7 @@ paths: schema: $ref: "#/components/schemas/DismissPriorityItemEnvelope" "401": *a1 + "403": *a9 "422": *a4 "429": *a5 /api/dashboard/chart-overlay-prefs: @@ -22146,6 +22556,7 @@ paths: schema: $ref: "#/components/schemas/ChartOverlayPrefsSavedEnvelope" "401": *a1 + "403": *a9 "422": *a4 "429": *a5 /api/dashboard/summary: @@ -22168,7 +22579,7 @@ paths: schema: $ref: "#/components/schemas/DashboardSummaryEnvelope" "401": *a1 - "403": *a11 + "403": *a15 "422": *a4 "429": *a5 /api/dashboard/widgets: @@ -22187,7 +22598,7 @@ paths: schema: $ref: "#/components/schemas/DashboardLayout" "401": *a1 - "403": *a11 + "403": *a15 "422": *a4 "429": *a5 put: @@ -22213,6 +22624,7 @@ paths: schema: $ref: "#/components/schemas/DashboardLayoutSaved" "401": *a1 + "403": *a9 "409": description: Dashboard layout changed since it was loaded (optimistic-concurrency conflict). No write happened. Re-read the resource, re-apply the user's change against the fresh state, and resend with the new token. @@ -22221,7 +22633,7 @@ paths: application/json: schema: $ref: "#/components/schemas/ErrorEnvelope" - "422": *a15 + "422": *a19 "429": *a5 delete: tags: @@ -22237,6 +22649,7 @@ paths: schema: $ref: "#/components/schemas/DashboardLayoutReset" "401": *a1 + "403": *a9 "422": *a4 "429": *a5 /api/managed-profiles/{id}/handover: @@ -22260,6 +22673,7 @@ paths: schema: $ref: "#/components/schemas/ManagedProfileHandoverStatusEnvelope" "401": *a1 + "403": *a9 "404": description: "No such managed profile, or the caller is not one of its Guardians (`meta.errorCode: managed_profile.not_found`)." @@ -22300,9 +22714,11 @@ paths: $ref: "#/components/schemas/ManagedProfileHandoverEnvelope" "401": *a1 "403": - description: "Neither a password nor single sign-on could redeem a link on this instance (`meta.errorCode: - profile_claim.oidc_only_unsupported`). Since v1.43 a single-sign-on-only instance hands over through the - identity provider, so a working configuration never answers this." + description: "`meta.errorCode` = `auth.scope.insufficient` when a Bearer token's scope does not reach this route; the + generic auth gate answers it before the route's own checks run. Otherwise: Neither a password nor single + sign-on could redeem a link on this instance (`meta.errorCode: profile_claim.oidc_only_unsupported`). Since + v1.43 a single-sign-on-only instance hands over through the identity provider, so a working configuration + never answers this." content: application/json: schema: @@ -22342,6 +22758,7 @@ paths: schema: $ref: "#/components/schemas/RevokeManagedProfileHandoverEnvelope" "401": *a1 + "403": *a9 "404": description: "No such managed profile, or the caller is not one of its Guardians (`meta.errorCode: managed_profile.not_found`)." @@ -22366,6 +22783,7 @@ paths: schema: $ref: "#/components/schemas/PendingHandoverDecisionEnvelope" "401": *a1 + "403": *a9 "422": *a4 "429": *a5 post: @@ -22389,6 +22807,7 @@ paths: schema: $ref: "#/components/schemas/HandoverDecisionEnvelope" "401": *a1 + "403": *a9 "409": description: "No decision is waiting (`meta.errorCode: managed_profile.handover.no_pending`)." content: @@ -22423,7 +22842,9 @@ paths: $ref: "#/components/schemas/AccountPayloadEnvelope" "401": *a1 "403": - description: "A selector header was attached to an actor surface (`meta.errorCode: sharing.not_permitted`)." + description: "`meta.errorCode` = `auth.scope.insufficient` when a Bearer token's scope does not reach this route; the + generic auth gate answers it before the route's own checks run. Otherwise: A selector header was attached to + an actor surface (`meta.errorCode: sharing.not_permitted`)." content: application/json: schema: @@ -22450,11 +22871,13 @@ paths: schema: $ref: "#/components/schemas/RecordActivityEnvelope" "401": *a1 - "403": &a52 - description: Refused. `meta.errorCode` is `sharing.not_permitted` when the request was made while acting on another - account (grant management is never delegable), or `sharing.access.denied` when the caller may not act as the - account they named. The latter is byte-identical for an account that does not exist and one that granted - nothing — the refusal is not an enumeration oracle. + "403": &a59 + description: "`meta.errorCode` = `auth.scope.insufficient` when a Bearer token's scope does not reach this route; the + generic auth gate answers it before the route's own checks run. Otherwise: Refused. `meta.errorCode` is + `sharing.not_permitted` when the request was made while acting on another account (grant management is never + delegable), or `sharing.access.denied` when the caller may not act as the account they named. The latter is + byte-identical for an account that does not exist and one that granted nothing — the refusal is not an + enumeration oracle." content: application/json: schema: @@ -22475,7 +22898,7 @@ paths: schema: $ref: "#/components/schemas/AccountGrantListEnvelope" "401": *a1 - "403": *a52 + "403": *a59 "422": *a4 "429": *a5 post: @@ -22519,10 +22942,12 @@ paths: schema: $ref: "#/components/schemas/ErrorEnvelope" "403": - description: Refused. `meta.errorCode` is `sharing.not_permitted` when the request was made while acting on another - account (grant management is never delegable), or `sharing.invite.manage_browser_only` when a Bearer caller - tried to offer MANAGE — the step-up that gates it is cookie-only by construction, so the native client - cannot mint it and should route the person to a browser rather than surface an error. + description: "`meta.errorCode` = `auth.scope.insufficient` when a Bearer token's scope does not reach this route; the + generic auth gate answers it before the route's own checks run. Otherwise: Refused. `meta.errorCode` is + `sharing.not_permitted` when the request was made while acting on another account (grant management is never + delegable), or `sharing.invite.manage_browser_only` when a Bearer caller tried to offer MANAGE — the step-up + that gates it is cookie-only by construction, so the native client cannot mint it and should route the + person to a browser rather than surface an error." content: application/json: schema: @@ -22577,7 +23002,7 @@ paths: schema: $ref: "#/components/schemas/AcceptedAccountGrantEnvelope" "401": *a1 - "403": *a52 + "403": *a59 "404": description: No such invitation for this account. content: @@ -22621,7 +23046,7 @@ paths: schema: $ref: "#/components/schemas/RevokedAccountGrantEnvelope" "401": *a1 - "403": *a52 + "403": *a59 "404": description: No such grant on this account's record. content: @@ -22662,7 +23087,7 @@ paths: schema: $ref: "#/components/schemas/RenouncedAccountGrantEnvelope" "401": *a1 - "403": *a52 + "403": *a59 "404": description: No such grant held by this account. content: @@ -22717,6 +23142,7 @@ paths: application/json: schema: $ref: "#/components/schemas/ErrorEnvelope" + "403": *a9 "422": *a4 "429": description: "More than ten profiles from one caller within the hour (`meta.errorCode: http.rate_limited`, the status @@ -22750,6 +23176,7 @@ paths: schema: $ref: "#/components/schemas/ManagedProfileEnvelope" "401": *a1 + "403": *a9 "404": description: "No such managed profile, or the caller is not one of its Guardians (`meta.errorCode: managed_profile.not_found`). The two are byte-identical, so the refusal is not an enumeration oracle." @@ -22794,6 +23221,7 @@ paths: application/json: schema: $ref: "#/components/schemas/ErrorEnvelope" + "403": *a9 "404": description: "No such managed profile, or the caller is not one of its Guardians (`meta.errorCode: managed_profile.not_found`). The two are byte-identical." @@ -22837,6 +23265,7 @@ paths: application/json: schema: $ref: "#/components/schemas/ErrorEnvelope" + "403": *a9 "404": description: "No such managed profile, or the caller is not one of its Guardians (`meta.errorCode: managed_profile.not_found`). The two are byte-identical, so the refusal is not an enumeration oracle — and @@ -22875,6 +23304,7 @@ paths: schema: $ref: "#/components/schemas/ManagedProfileGuardianListEnvelope" "401": *a1 + "403": *a9 "404": description: "No such managed profile, or the caller is not one of its Guardians (`meta.errorCode: managed_profile.not_found`). The two are byte-identical." @@ -22919,6 +23349,7 @@ paths: application/json: schema: $ref: "#/components/schemas/ErrorEnvelope" + "403": *a9 "404": description: "Two refusals share this status, each with its own code. `meta.errorCode: sharing.invite.no_account`: no account on this instance carries that identifier — the same deliberate disclosure to an authenticated caller @@ -22989,6 +23420,7 @@ paths: application/json: schema: $ref: "#/components/schemas/ErrorEnvelope" + "403": *a9 "404": description: "`meta.errorCode: managed_profile.not_found`, for four situations the caller cannot tell apart: no such profile, no such grant on it, the caller is not one of its Guardians, or the target grant is the caller's @@ -23047,7 +23479,7 @@ paths: schema: $ref: "#/components/schemas/ErrorEnvelope" "401": *a1 - "403": *a52 + "403": *a59 "409": description: "The record this browser session is on is not the record the request asserted. `meta.errorCode` is `sharing.session.changed`. It is not a refusal of access — the caller may well be entitled to both records — @@ -23127,7 +23559,9 @@ paths: $ref: "#/components/schemas/WebPushSubscribeEnvelope" "401": *a1 "403": - description: The operator switched Web Push off for this instance. Nothing was stored. + description: "`meta.errorCode` = `auth.scope.insufficient` when a Bearer token's scope does not reach this route; the + generic auth gate answers it before the route's own checks run. Otherwise: The operator switched Web Push + off for this instance. Nothing was stored." content: application/json: schema: @@ -23166,6 +23600,7 @@ paths: schema: $ref: "#/components/schemas/WebPushUnsubscribeEnvelope" "401": *a1 + "403": *a9 "413": description: Request body exceeds 65536 bytes. content: @@ -23197,6 +23632,7 @@ paths: schema: $ref: "#/components/schemas/WebPushTestEnvelope" "401": *a1 + "403": *a9 "422": description: "Nothing to test against: the account has no Push subscription (`meta.errorCode` = `not_configured`), or the operator has configured no VAPID keys (`vapid_not_configured`)." @@ -23238,6 +23674,7 @@ paths: schema: $ref: "#/components/schemas/ApnsTestEnvelope" "401": *a1 + "403": *a9 "422": *a4 "429": description: More than 5 tests in a minute (`meta.errorCode` = `rate_limited_self`). @@ -23312,6 +23749,7 @@ paths: schema: $ref: "#/components/schemas/GetNotificationPreferences" "401": *a1 + "403": *a9 "422": *a4 "429": *a5 put: @@ -23334,6 +23772,7 @@ paths: schema: $ref: "#/components/schemas/PutNotificationPreferenceResponse" "401": *a1 + "403": *a9 "404": description: No such channel for this caller. content: @@ -23369,6 +23808,7 @@ paths: schema: $ref: "#/components/schemas/GetNotificationStatus" "401": *a1 + "403": *a9 "422": *a4 "429": *a5 post: @@ -23393,6 +23833,7 @@ paths: schema: $ref: "#/components/schemas/ReEnableNotificationChannelResponse" "401": *a1 + "403": *a9 "404": description: No such channel for this caller. content: @@ -23427,10 +23868,11 @@ paths: schema: $ref: "#/components/schemas/McpConnectionListEnvelope" "401": *a1 - "403": &a53 - description: The operator has disabled the API instance-wide. Every route in this module refuses while that setting is - off, for a cookie session as well as for a Bearer token; it is not a per-user permission and retrying will - not help. + "403": &a60 + description: "`meta.errorCode` = `auth.scope.insufficient` when a Bearer token's scope does not reach this route; the + generic auth gate answers it before the route's own checks run. Otherwise: The operator has disabled the API + instance-wide. Every route in this module refuses while that setting is off, for a cookie session as well as + for a Bearer token; it is not a per-user permission and retrying will not help." content: application/json: schema: @@ -23460,7 +23902,7 @@ paths: schema: $ref: "#/components/schemas/McpConnectionRevokeEnvelope" "401": *a1 - "403": *a53 + "403": *a60 "404": description: No live connection with this id for this caller — also the answer for an already-revoked one, so a repeat revoke is 404 rather than an idempotent 200. @@ -23487,7 +23929,7 @@ paths: schema: $ref: "#/components/schemas/McpConnectorTokenListEnvelope" "401": *a1 - "403": *a53 + "403": *a60 "422": *a4 "429": *a5 post: @@ -23520,7 +23962,7 @@ paths: schema: $ref: "#/components/schemas/ErrorEnvelope" "401": *a1 - "403": *a53 + "403": *a60 "413": description: Body exceeds 16 KiB. content: @@ -23563,7 +24005,7 @@ paths: schema: $ref: "#/components/schemas/McpConnectorTokenRevokeEnvelope" "401": *a1 - "403": *a53 + "403": *a60 "404": description: No `health:read` token with this id for this caller. An already-revoked token is still found, so a repeat revoke answers 200. @@ -23590,7 +24032,7 @@ paths: schema: $ref: "#/components/schemas/RecordSettingsEnvelope" "401": *a1 - "403": *a11 + "403": *a15 "422": *a4 "429": *a5 /api/record-settings/integrations: @@ -23609,7 +24051,7 @@ paths: schema: $ref: "#/components/schemas/RecordSettingsIntegrationsEnvelope" "401": *a1 - "403": *a11 + "403": *a15 "422": *a4 "429": *a5 /api/record-settings/integrations/{provider}/connect: @@ -23625,7 +24067,7 @@ paths: record (audited). Fitbit is not offered: its classic API is retired. Cookie session, while acting on a managed record the caller guards." parameters: - - &a54 + - &a61 name: provider in: path required: true @@ -23647,7 +24089,7 @@ paths: schema: $ref: "#/components/schemas/ManagedConnectStartEnvelope" "401": *a1 - "403": *a11 + "403": *a15 "404": description: The path named a provider outside the closed set. Answered before the caller is authenticated. content: @@ -23672,7 +24114,7 @@ paths: Audited against the record with the Guardian as the actor. Cookie session or Bearer, while acting on a managed record the caller guards." parameters: - - *a54 + - *a61 responses: "200": description: The provider is disconnected. @@ -23681,7 +24123,7 @@ paths: schema: $ref: "#/components/schemas/ManagedDisconnectEnvelope" "401": *a1 - "403": *a11 + "403": *a15 "404": description: The path named a provider outside the closed set, or the record has no connection to it. content: @@ -23698,7 +24140,7 @@ paths: description: Returns the named family's resolved settings for the record the session is acting on. Cookie session or Bearer, while acting on a managed record the caller guards. parameters: - - &a55 + - &a62 name: family in: path required: true @@ -23722,7 +24164,7 @@ paths: schema: $ref: "#/components/schemas/GetRecordSettingsFamilyEnvelope" "401": *a1 - "403": *a11 + "403": *a15 "404": description: Either the path named a family outside the closed set, or the record row itself is gone. The two are not distinguished, and the first is answered before the caller is authenticated at all. @@ -23742,7 +24184,7 @@ paths: ride the audit row. Body capped at 64 KiB. Cookie session or Bearer, while acting on a managed record the caller guards. parameters: - - *a55 + - *a62 requestBody: required: true content: @@ -23763,7 +24205,7 @@ paths: schema: $ref: "#/components/schemas/ErrorEnvelope" "401": *a1 - "403": *a11 + "403": *a15 "404": description: The path named a family outside the closed set. Answered before authentication. content: @@ -23877,7 +24319,7 @@ paths: schema: $ref: "#/components/schemas/ListPersonalRecordsEnvelope" "401": *a1 - "403": *a11 + "403": *a15 "422": description: "`metricType` or `limit` carried an unacceptable value. `meta.errorCode` = `personal_records.invalid_query`, with the offending path under `details.issues`." @@ -23930,7 +24372,7 @@ paths: schema: $ref: "#/components/schemas/AchievementsEnvelope" "401": *a1 - "403": *a13 + "403": *a17 "422": *a4 "429": *a5 /api/environment/preferences: @@ -23956,7 +24398,7 @@ paths: schema: $ref: "#/components/schemas/EnvironmentPreferencesEnvelope" "401": *a1 - "403": *a19 + "403": *a11 "422": *a4 "429": *a5 /api/environment: @@ -23974,7 +24416,7 @@ paths: schema: $ref: "#/components/schemas/EnvironmentOverviewEnvelope" "401": *a1 - "403": *a19 + "403": *a11 "422": *a4 "429": *a5 /api/environment/home: @@ -24011,7 +24453,7 @@ paths: schema: $ref: "#/components/schemas/EnvironmentHomeEnvelope" "401": *a1 - "403": *a19 + "403": *a11 "413": description: Body exceeds 4 KiB. content: @@ -24071,7 +24513,7 @@ paths: schema: $ref: "#/components/schemas/EnvironmentBackfillEnvelope" "401": *a1 - "403": *a19 + "403": *a11 "409": description: No home location is set, so there is nothing to resolve the days against (`meta.errorCode` = `environment.no_home`), or a backfill for this account is already queued in the current twenty-minute slot @@ -24131,7 +24573,7 @@ paths: schema: $ref: "#/components/schemas/EnvironmentGeocodeEnvelope" "401": *a1 - "403": *a19 + "403": *a11 "422": description: "`q` was missing, empty after trimming, or longer than 120 characters. `meta.errorCode` = `environment.invalid`." @@ -24170,7 +24612,7 @@ paths: schema: $ref: "#/components/schemas/CreateEnvironmentTravelEnvelope" "401": *a1 - "403": *a19 + "403": *a11 "413": description: Body exceeds 4 KiB. content: @@ -24223,7 +24665,7 @@ paths: schema: $ref: "#/components/schemas/DeleteEnvironmentTravelEnvelope" "401": *a1 - "403": *a19 + "403": *a11 "404": description: No such override for this account. `meta.errorCode` = `environment.travel.not_found`. Also the answer for an id owned by somebody else — existence is not leaked. @@ -24343,6 +24785,93 @@ paths: application/json: schema: $ref: "#/components/schemas/ErrorEnvelope" + /api/admin/backups/upload: + post: + tags: + - Admin + summary: Store an uploaded backup file as a new copy + description: "Reads a backup file, checks it, and stores it encrypted as a new copy beside the weekly one; it does NOT + restore anything. Restoring is the separate `POST /api/admin/backups/{id}/restore`, so an operator can review + what was uploaded first. The file goes up as the raw request body, plain JSON or gzip-compressed JSON (sniffed + from the bytes, not the content type), up to 512 MB as sent; or, for a file up to 10 MB, as + `multipart/form-data` with one `file` field. It is read as a stream, checked as it arrives, and stored in the + same pass; a file that fails any check leaves nothing behind. It is refused when it is not valid JSON, fails the + backup schema, was written by a newer schema version than this server reads, needs an encryption key this server + does not hold (`meta.errorCode` = `backup.key.missing`, `meta.keyIds` naming them), or belongs to an account + that does not exist here. Three uploads a minute per administrator. Admin session cookie required; Bearer tokens + cannot reach admin endpoints. A browser session needs a fresh proof as well as a live one: a sign-in or `POST + /api/auth/reproof` within the last five minutes (with a second factor, on an account that has one). Without it + the answer is 401 `auth.reproof.required`, with `meta.methods` naming the proofs the account can give." + requestBody: + required: true + content: + application/json: + schema: + type: object + propertyNames: + type: string + additionalProperties: {} + description: The backup document, as `GET /api/admin/backups/{id}/download` or the account export wrote it. + application/gzip: + schema: + type: string + format: binary + multipart/form-data: + schema: + type: object + properties: + file: + type: string + format: binary + description: The backup file, plain or gzip-compressed, up to 10 MB. + required: + - file + responses: + "201": + description: The file was stored as a new copy. + content: + application/json: + schema: + $ref: "#/components/schemas/AdminBackupUploadEnvelope" + "400": + description: A `multipart/form-data` body that could not be parsed as a form. Nothing was stored. + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorEnvelope" + "401": + description: "No session (`auth.missing`), or a session without a recent proof (`auth.reproof.required`, with + `meta.methods`). A Bearer token lands here too: the admin gate reads only the session cookie." + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorEnvelope" + "403": *a25 + "413": + description: "Too large: over 512 MB as sent (10 MB in a form), a gzip file that expands past what any backup this + server wrote does, or larger than the stored-copy limit once encrypted (`meta.errorCode` = + `backup.upload.too_large`, the numbers and the setting in the message). Nothing was stored." + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorEnvelope" + "422": + description: "The file was refused and nothing was stored: no file in the request, not valid JSON or not readable as + gzip, failed the backup schema (`meta.issues`), an unsupported schema version, a key this server does not + hold (`meta.errorCode` = `backup.key.missing`, `meta.keyIds`), or an owner account that does not exist + here." + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorEnvelope" + "429": + description: More than three uploads in a minute by this administrator. Nothing was stored. The `Retry-After` and + `X-RateLimit-*` headers describe the bucket. + headers: *a8 + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorEnvelope" /api/admin/backups/{id}/download: get: tags: @@ -24369,13 +24898,13 @@ paths: application/json: schema: $ref: "#/components/schemas/AdminBackupDocument" - "403": &a56 + "403": &a63 description: Caller is not an admin. `requireAdmin()` is cookie-only, so a Bearer token lands here whatever its scope. content: application/json: schema: $ref: "#/components/schemas/ErrorEnvelope" - "404": &a57 + "404": &a64 description: No stored copy with this id. content: application/json: @@ -24449,8 +24978,8 @@ paths: application/json: schema: $ref: "#/components/schemas/ErrorEnvelope" - "403": *a56 - "404": *a57 + "403": *a63 + "404": *a64 "409": description: A restore of the same account is already queued or running (`meta.errorCode` = `backup.restore.active`, `meta.jobId` names it), the stored copy was replaced by a newer one while it was being checked @@ -24494,7 +25023,7 @@ paths: application/json: schema: $ref: "#/components/schemas/AdminBackupRestoreJobListResponse" - "403": *a56 + "403": *a63 /api/admin/backups/restores/{jobId}: get: tags: @@ -24517,7 +25046,7 @@ paths: application/json: schema: $ref: "#/components/schemas/AdminBackupRestoreJobResponse" - "403": *a56 + "403": *a63 "404": description: No restore job with this id. content: @@ -24536,7 +25065,7 @@ paths: request leaves the server. Needs neither the Documents module nor the list: with the list unset, `available` is false and any connection still stored is listed (with `originAllowed: false`) so it can be removed. Cookie session only; not delegable." - security: &a58 + security: &a65 - cookieAuth: [] responses: "200": @@ -24546,10 +25075,12 @@ paths: schema: $ref: "#/components/schemas/DocumentSourcesStatusEnvelope" "401": *a1 - "403": &a59 - description: "Refused before any request to the source: `documents.sources.browserOnly` for a Bearer caller, - `sharing.not_permitted` while acting on another person's record (a delegate or guardian never uses the - owner's archive), or `module.disabled` when the Documents module is off." + "403": &a66 + description: "`meta.errorCode` = `auth.scope.insufficient` when a Bearer token's scope does not reach this route; the + generic auth gate answers it before the route's own checks run. Otherwise: Refused before any request to the + source: `documents.sources.browserOnly` for a Bearer caller, `sharing.not_permitted` while acting on another + person's record (a delegate or guardian never uses the owner's archive), or `module.disabled` when the + Documents module is off." content: application/json: schema: @@ -24569,9 +25100,9 @@ paths: `documents.sources.tokenRequired`), so a stored token is never sent to an address it was not saved for. A Papra organization id is letters, digits, `_` and `-`. The token is stored encrypted (AES-256-GCM) and never returned. Ten saves and tests a minute per person. Cookie session only; not delegable." - security: *a58 + security: *a65 parameters: - - &a60 + - &a67 name: system in: path required: true @@ -24610,8 +25141,8 @@ paths: schema: $ref: "#/components/schemas/DocumentSourceConnectionEnvelope" "401": *a1 - "403": *a59 - "404": &a61 + "403": *a66 + "404": &a68 description: "`documents.sources.unavailable`: the operator has not listed any origin in `DOCUMENT_SOURCE_ORIGINS`, so the picker is off. `documents.sources.notConnected`: no connection is saved for this system. `documents.sources.notFound`: the source has no such document, or (Papra) no such organization, including @@ -24630,7 +25161,7 @@ paths: schema: $ref: "#/components/schemas/ErrorEnvelope" "429": *a5 - "502": &a62 + "502": &a69 description: "The source could not answer usefully, with the reason in `meta.errorCode` and, when the source answered at all, its HTTP status in `meta.upstreamStatus` (its body is never passed on): `documents.sources.unreachable` (no connection, timeout, or a destination refused at dial time), `documents.sources.redirected` (the source @@ -24649,9 +25180,9 @@ paths: description: Deletes the caller's connection for this system, and its token with it. Works even after the operator removed the origin from the list, and with the Documents module switched off. `disconnected` is false when there was nothing to delete. Cookie session only; not delegable. - security: *a58 + security: *a65 parameters: - - *a60 + - *a67 responses: "200": description: Removed, or there was nothing to remove. @@ -24660,7 +25191,7 @@ paths: schema: $ref: "#/components/schemas/DocumentSourceDisconnectEnvelope" "401": *a1 - "403": *a59 + "403": *a66 "422": *a4 "429": *a5 /api/documents/sources/{system}/test: @@ -24671,9 +25202,9 @@ paths: description: One request to the source with the stored token. Answers `ok` with the round-trip time and stamps `lastVerifiedAt`, or the reason in `meta.errorCode`. Shares the save's allowance of ten a minute. Cookie session only; not delegable. - security: *a58 + security: *a65 parameters: - - *a60 + - *a67 responses: "200": description: The source answered. @@ -24682,9 +25213,9 @@ paths: schema: $ref: "#/components/schemas/DocumentSourceTestEnvelope" "401": *a1 - "403": *a59 - "404": *a61 - "422": &a63 + "403": *a66 + "404": *a68 + "422": &a70 description: "`documents.sources.originNotAllowed`: the base address's origin is not on the operator's list (checked on every request, so a connection saved earlier is cut off once its origin is removed), or another validation failure." @@ -24693,7 +25224,7 @@ paths: schema: $ref: "#/components/schemas/ErrorEnvelope" "429": *a5 - "502": *a62 + "502": *a69 /api/documents/sources/{system}/tags: get: tags: @@ -24701,9 +25232,9 @@ paths: summary: Tags of a connected archive description: The source's tags for the picker's filter, the first 250 by name, in the source's own ids. Shares the search allowance of sixty a minute. Cookie session only; not delegable. - security: *a58 + security: *a65 parameters: - - *a60 + - *a67 responses: "200": description: The tags. @@ -24712,11 +25243,11 @@ paths: schema: $ref: "#/components/schemas/DocumentSourceTagsEnvelope" "401": *a1 - "403": *a59 - "404": *a61 - "422": *a63 + "403": *a66 + "404": *a68 + "422": *a70 "429": *a5 - "502": *a62 + "502": *a69 /api/documents/sources/{system}/search: get: tags: @@ -24730,9 +25261,9 @@ paths: under that source key: `new`, `imported` with the vault document's `documentId`, or `deleted` (deleted in HealthLog; importing it again stores nothing). Nothing is stored. Sixty searches a minute, shared with the tag read. Cookie session only; not delegable." - security: *a58 + security: *a65 parameters: - - *a60 + - *a67 - name: q in: query required: false @@ -24773,11 +25304,11 @@ paths: schema: $ref: "#/components/schemas/DocumentSourceSearchEnvelope" "401": *a1 - "403": *a59 - "404": *a61 - "422": *a63 + "403": *a66 + "404": *a68 + "422": *a70 "429": *a5 - "502": *a62 + "502": *a69 /api/documents/sources/{system}/import: post: tags: @@ -24796,9 +25327,9 @@ paths: fetched), including a duplicate. `kind` sets the document type (default OTHER). Automatic AI reading follows the person's own setting. Counts against the person's upload allowance (60 an hour) and storage quota. 201 for a new document, 200 otherwise. Cookie session only; not delegable." - security: *a58 + security: *a65 parameters: - - *a60 + - *a67 requestBody: required: true content: @@ -24856,7 +25387,7 @@ paths: schema: $ref: "#/components/schemas/DocumentSourceImportEnvelope" "401": *a1 - "403": *a59 + "403": *a66 "404": description: "`documents.sources.unavailable`: the operator has not listed any origin in `DOCUMENT_SOURCE_ORIGINS`, so the picker is off. `documents.sources.notConnected`: no connection is saved for this system. @@ -24887,7 +25418,7 @@ paths: application/json: schema: $ref: "#/components/schemas/ErrorEnvelope" - "422": *a63 + "422": *a70 "429": description: "`documents.inbound.rateLimited` (the person's hourly upload allowance, with the rate-limit headers) or `documents.inbound.uploadBusy` (too many uploads in progress; `Retry-After: 1`)." @@ -24895,7 +25426,7 @@ paths: application/json: schema: $ref: "#/components/schemas/ErrorEnvelope" - "502": *a62 + "502": *a69 /api/symptoms/definitions: get: tags: @@ -24919,10 +25450,11 @@ paths: schema: $ref: "#/components/schemas/ListSymptomDefinitionsEnvelope" "401": *a1 - "403": &a64 + "403": &a71 description: >- - `illness.disabled`: the record has the illness module switched off (or the operator turned it off - server-wide); symptoms ride that module. + `meta.errorCode` = `auth.scope.insufficient` when a Bearer token's scope does not reach this route; the + generic auth gate answers it before the route's own checks run. Otherwise: `illness.disabled`: the record + has the illness module switched off (or the operator turned it off server-wide); symptoms ride that module. Refused: this request named a record the caller may not act on (`meta.errorCode` = `sharing.access.denied`). @@ -24962,7 +25494,7 @@ paths: schema: $ref: "#/components/schemas/CreateSymptomDefinitionEnvelope" "401": *a1 - "403": *a64 + "403": *a71 "409": *a7 "422": *a4 "429": *a5 @@ -24992,8 +25524,8 @@ paths: schema: $ref: "#/components/schemas/UpdateSymptomDefinitionEnvelope" "401": *a1 - "403": *a64 - "404": &a65 + "403": *a71 + "404": &a72 description: "`symptoms.definition.notFound`: no definition with this id on the record." content: application/json: @@ -25027,8 +25559,8 @@ paths: schema: $ref: "#/components/schemas/DeleteSymptomDefinitionEnvelope" "401": *a1 - "403": *a64 - "404": *a65 + "403": *a71 + "404": *a72 "422": *a4 "429": *a5 /api/symptoms/events: @@ -25077,7 +25609,7 @@ paths: schema: $ref: "#/components/schemas/ListSymptomEventsEnvelope" "401": *a1 - "403": *a64 + "403": *a71 "422": *a4 "429": *a5 post: @@ -25103,8 +25635,8 @@ paths: schema: $ref: "#/components/schemas/CreateSymptomEventEnvelope" "401": *a1 - "403": *a64 - "404": *a65 + "403": *a71 + "404": *a72 "409": *a7 "422": *a4 "429": *a5 @@ -25134,8 +25666,8 @@ paths: schema: $ref: "#/components/schemas/UpdateSymptomEventEnvelope" "401": *a1 - "403": *a64 - "404": &a66 + "403": *a71 + "404": &a73 description: "`symptoms.event.notFound`: no occurrence with this id on the record." content: application/json: @@ -25162,8 +25694,8 @@ paths: schema: $ref: "#/components/schemas/DeleteSymptomEventEnvelope" "401": *a1 - "403": *a64 - "404": *a66 + "403": *a71 + "404": *a73 "422": *a4 "429": *a5 /api/day/index: @@ -25196,7 +25728,7 @@ paths: schema: $ref: "#/components/schemas/DayIndexEnvelope" "401": *a1 - "403": *a11 + "403": *a15 "422": *a4 "429": *a5 /api/day/notable: @@ -25231,7 +25763,7 @@ paths: schema: $ref: "#/components/schemas/DayNotableWindowEnvelope" "401": *a1 - "403": *a11 + "403": *a15 "422": *a4 "429": *a5 /api/day/{date}: @@ -25261,7 +25793,7 @@ paths: schema: $ref: "#/components/schemas/DayEnvelope" "401": *a1 - "403": *a11 + "403": *a15 "422": *a4 "429": *a5 /api/timeline: @@ -25314,10 +25846,12 @@ paths: schema: $ref: "#/components/schemas/TimelineEnvelope" "401": *a1 - "403": &a67 + "403": &a74 description: >- - `module.disabled` with `meta.module = timeline`: the record has the timeline switched off (it is opt-in), or - the operator turned it off server-wide. + `meta.errorCode` = `auth.scope.insufficient` when a Bearer token's scope does not reach this route; the + generic auth gate answers it before the route's own checks run. Otherwise: `module.disabled` with + `meta.module = timeline`: the record has the timeline switched off (it is opt-in), or the operator turned it + off server-wide. Refused: this request named a record the caller may not act on (`meta.errorCode` = `sharing.access.denied`). @@ -25351,7 +25885,7 @@ paths: schema: $ref: "#/components/schemas/TimelineReadinessEnvelope" "401": *a1 - "403": *a67 + "403": *a74 "422": *a4 "429": *a5 /api/life-events: @@ -25369,10 +25903,12 @@ paths: schema: $ref: "#/components/schemas/LifeEventListEnvelope" "401": *a1 - "403": &a68 + "403": &a75 description: >- - Owner-only in v1.42: no share reaches life events, not a `profile` share, a whole-record share or MANAGE, - and not a guardian acting for a managed profile. + `meta.errorCode` = `auth.scope.insufficient` when a Bearer token's scope does not reach this route; the + generic auth gate answers it before the route's own checks run. Otherwise: Owner-only in v1.42: no share + reaches life events, not a `profile` share, a whole-record share or MANAGE, and not a guardian acting for a + managed profile. Refused: the request was made while the session is acting on another account (`meta.errorCode` = @@ -25409,10 +25945,10 @@ paths: schema: $ref: "#/components/schemas/CreateLifeEventEnvelope" "401": *a1 - "403": *a68 + "403": *a75 "409": *a7 "422": *a4 - "429": *a12 + "429": *a16 /api/life-events/{id}: patch: tags: @@ -25440,8 +25976,8 @@ paths: schema: $ref: "#/components/schemas/UpdateLifeEventEnvelope" "401": *a1 - "403": *a68 - "404": &a69 + "403": *a75 + "404": &a76 description: No such life event on this account. content: application/json: @@ -25468,8 +26004,8 @@ paths: schema: $ref: "#/components/schemas/DeleteLifeEventEnvelope" "401": *a1 - "403": *a68 - "404": *a69 + "403": *a75 + "404": *a76 "422": *a4 "429": *a5 /api/search: @@ -25515,7 +26051,7 @@ paths: schema: $ref: "#/components/schemas/SearchEnvelope" "401": *a1 - "403": *a11 + "403": *a15 "422": *a4 "429": *a5 /api/auth/me/ai-usage: @@ -25540,6 +26076,7 @@ paths: schema: $ref: "#/components/schemas/PutAiUsageResponse" "401": *a1 + "403": *a9 "422": description: "`ai.usage.invalid` (body) or `ai.usage.aboveCeiling` (`meta.maxLevel` names the ceiling)." content: @@ -25582,8 +26119,9 @@ paths: "401": *a1 "403": description: >- - The opt-in `nutrients` module is off for this account: 403 `module.disabled` with `meta.module: - "nutrients"`. + `meta.errorCode` = `auth.scope.insufficient` when a Bearer token's scope does not reach this route; the + generic auth gate answers it before the route's own checks run. Otherwise: The opt-in `nutrients` module is + off for this account: 403 `module.disabled` with `meta.module: "nutrients"`. Refused: this request named a record the caller may not act on (`meta.errorCode` = `sharing.access.denied`). @@ -25642,16 +26180,17 @@ paths: required: - file responses: - "202": *a37 + "202": *a44 "401": *a1 "403": - description: "The opt-in `nutrients` module is off for this account: 403 `module.disabled` with `meta.module: - \"nutrients\"`. The `supplementLabel` AI capability is unavailable for this record. `meta.capability` = - `supplementLabel` and `meta.reason` says which layer refused: the operator's switch - (`assistant.disabled.documentAi`, or `assistant.disabled.enabled` for the master switch), an owning module - or the person's own AI opt-out (`module.disabled` with `meta.module`), a delegate inside somebody else's - record (`ai.record.notPermitted`), or a missing consent receipt (`consent.ai.required`). Nothing was - generated, read from a stored model answer, or enqueued." + description: "`meta.errorCode` = `auth.scope.insufficient` when a Bearer token's scope does not reach this route; the + generic auth gate answers it before the route's own checks run. Otherwise: The opt-in `nutrients` module is + off for this account: 403 `module.disabled` with `meta.module: \"nutrients\"`. The `supplementLabel` AI + capability is unavailable for this record. `meta.capability` = `supplementLabel` and `meta.reason` says + which layer refused: the operator's switch (`assistant.disabled.documentAi`, or `assistant.disabled.enabled` + for the master switch), an owning module or the person's own AI opt-out (`module.disabled` with + `meta.module`), a delegate inside somebody else's record (`ai.record.notPermitted`), or a missing consent + receipt (`consent.ai.required`). Nothing was generated, read from a stored model answer, or enqueued." content: application/json: schema: @@ -25677,7 +26216,7 @@ paths: schema: $ref: "#/components/schemas/ErrorEnvelope" "429": *a5 - "503": *a18 + "503": *a26 /api/anamnesis/life-circumstances: get: tags: @@ -25692,7 +26231,7 @@ paths: schema: $ref: "#/components/schemas/LifeCircumstancesEnvelope" "401": *a1 - "403": *a11 + "403": *a15 "422": *a4 "429": *a5 /api/anamnesis/life-circumstances/ai-sections: @@ -25716,6 +26255,7 @@ paths: schema: $ref: "#/components/schemas/LifeAiSectionsEnvelope" "401": *a1 + "403": *a9 "409": description: "`about_me_conflict`." content: @@ -25745,6 +26285,7 @@ paths: schema: $ref: "#/components/schemas/LifeConfirmEnvelope" "401": *a1 + "403": *a9 "422": *a4 "429": *a5 /api/anamnesis/audit-c: @@ -25779,7 +26320,7 @@ paths: schema: $ref: "#/components/schemas/AuditCCreatedEnvelope" "401": *a1 - "403": *a11 + "403": *a15 "409": *a7 "422": *a4 "429": *a5 @@ -25803,7 +26344,7 @@ paths: schema: $ref: "#/components/schemas/AuditCRemovedEnvelope" "401": *a1 - "403": *a11 + "403": *a15 "404": description: "`anamnesis.auditC.notFound`." content: @@ -25834,7 +26375,7 @@ paths: schema: $ref: "#/components/schemas/DawarichConnectEnvelope" "401": *a1 - "403": *a19 + "403": *a11 "422": *a4 "429": *a5 /api/dawarich/status: @@ -25850,7 +26391,7 @@ paths: schema: $ref: "#/components/schemas/DawarichStatusEnvelope" "401": *a1 - "403": *a19 + "403": *a11 "422": *a4 "429": *a5 /api/dawarich/places: @@ -25874,7 +26415,7 @@ paths: schema: $ref: "#/components/schemas/DawarichPlacesEnvelope" "401": *a1 - "403": *a19 + "403": *a11 "409": description: "`dawarich.not_connected`." content: @@ -25912,7 +26453,7 @@ paths: schema: $ref: "#/components/schemas/DawarichSettingsEnvelope" "401": *a1 - "403": *a19 + "403": *a11 "409": description: "`dawarich.not_connected`." content: @@ -25944,7 +26485,7 @@ paths: schema: $ref: "#/components/schemas/DawarichProbeEnvelope" "401": *a1 - "403": *a19 + "403": *a11 "422": *a4 "429": *a5 "502": @@ -25980,7 +26521,7 @@ paths: schema: $ref: "#/components/schemas/DawarichSyncEnvelope" "401": *a1 - "403": *a19 + "403": *a11 "409": description: "`dawarich.not_connected`." content: @@ -26015,7 +26556,7 @@ paths: schema: $ref: "#/components/schemas/DawarichDisconnectEnvelope" "401": *a1 - "403": *a19 + "403": *a11 "422": *a4 "429": *a5 /api/environment/travel/suggestions: @@ -26033,7 +26574,7 @@ paths: schema: $ref: "#/components/schemas/TravelSuggestionsEnvelope" "401": *a1 - "403": *a19 + "403": *a11 "422": *a4 "429": *a5 /api/environment/travel/suggestions/{id}/dismiss: @@ -26056,7 +26597,7 @@ paths: schema: $ref: "#/components/schemas/TravelSuggestionDismissEnvelope" "401": *a1 - "403": *a19 + "403": *a11 "404": description: "`environment.travel_suggestion.not_found`." content: @@ -26093,6 +26634,7 @@ paths: schema: $ref: "#/components/schemas/MedicationLinkResponse" "401": *a1 + "403": *a9 "404": description: The medication was not found. content: @@ -26128,6 +26670,7 @@ paths: schema: $ref: "#/components/schemas/MedicationUnlinkResponse" "401": *a1 + "403": *a9 "404": description: The medication was not found. content: @@ -26164,6 +26707,7 @@ paths: schema: $ref: "#/components/schemas/LabResultLinkResponse" "401": *a1 + "403": *a9 "404": description: The lab result was not found. content: @@ -26199,6 +26743,7 @@ paths: schema: $ref: "#/components/schemas/LabResultUnlinkResponse" "401": *a1 + "403": *a9 "404": description: The lab result was not found. content: @@ -88223,6 +88768,61 @@ components: - data - error additionalProperties: false + AdminBackupUploadEnvelope: + type: object + properties: + data: + $ref: "#/components/schemas/AdminBackupUploadResponse" + error: + type: "null" + meta: + type: object + properties: + requestId: + type: string + additionalProperties: false + required: + - data + - error + additionalProperties: false + AdminBackupUploadResponse: + type: object + properties: + id: + type: string + description: "`DataBackup.id` of the stored copy; pass it to `POST /api/admin/backups/{id}/restore`." + valid: + type: boolean + const: true + summary: + $ref: "#/components/schemas/AdminBackupUploadSummary" + required: + - id + - valid + - summary + additionalProperties: false + description: The file was read, checked and stored as a new copy. Nothing was restored. + AdminBackupUploadSummary: + type: object + properties: + schemaVersion: + type: string + userId: + type: string + exportedAt: + type: string + required: + - schemaVersion + - userId + - exportedAt + additionalProperties: + type: integer + minimum: -9007199254740991 + maximum: 9007199254740991 + description: "What the stored file holds: its `schemaVersion`, the account it was exported from (`userId`) and when + (`exportedAt`), and one count per serialised class (`measurements`, `medications`, `intakeEvents`, + `moodEntries`, …). The set of counts grows with the backup format; read the ones you know and show the rest + generically." AdminBackupDocument: type: object propertyNames: diff --git a/docs/contributing/testing.md b/docs/contributing/testing.md index fddd0c974..100e9a043 100644 --- a/docs/contributing/testing.md +++ b/docs/contributing/testing.md @@ -89,10 +89,12 @@ Every call is also a contract check. The status must be one the registry (`src/lib/openapi/routes/`) declares for that path and method, a JSON body must parse with the registered schema, and a key the schema does not declare fails the call: a client generated from the contract cannot see that field, so -nobody promised to keep it. The one generic answer accepted without its own -entry is the scope gate's 403 `auth.scope.insufficient`, which the shared 401 -documents for every authenticated operation. A test that calls an unregistered -route on purpose passes `contract: { skip: "" }`. +nobody promised to keep it. There is no generic answer the kit accepts on an +operation's behalf: the scope gate's 403 `auth.scope.insufficient` is declared +on every operation that passes the gate (`withScopeRefusal` in +`src/lib/openapi/routes/shared.ts`), so it is checked like any other status. A +test that calls an unregistered route on purpose passes +`contract: { skip: "" }`. `tests/integration/routes/kit-contract.test.ts` proves the check can fail. Assert what a person or a client would notice: the stored row, the response, @@ -119,7 +121,9 @@ Each Vitest worker clones it on its first file (`CREATE DATABASE … TEMPLATE`, see `tests/integration/worker-database.ts`), so files run in parallel without seeing each other's rows, advisory locks or queue schema. The worker count follows the host (half the cores, at most eight) unless -`HEALTHLOG_INTEGRATION_WORKERS` pins it; CI uses three. +`HEALTHLOG_INTEGRATION_WORKERS` pins it; CI uses three. To reproduce a CI runner's +contention on a larger machine, cap the database container with +`HEALTHLOG_INTEGRATION_PG_CPUS=2`. `truncateAllTables` empties every table the migrations created and puts back the rows they seed (the mood-tag and symptom catalogues), so a test starts diff --git a/package.json b/package.json index 5d19a1764..06671fd29 100644 --- a/package.json +++ b/package.json @@ -53,8 +53,8 @@ "mcp:stdio": "npx tsx src/cli/mcp-stdio.ts" }, "dependencies": { - "@aws-sdk/client-s3": "^3.1150.0", - "@aws-sdk/lib-storage": "3.1150.0", + "@aws-sdk/client-s3": "^3.1152.0", + "@aws-sdk/lib-storage": "3.1152.0", "@dnd-kit/core": "^6.3.1", "@dnd-kit/sortable": "^10.0.0", "@dnd-kit/utilities": "^3.2.2", @@ -62,42 +62,42 @@ "@napi-rs/canvas": "1.0.11", "@node-rs/argon2": "^2.2.2", "@parse/node-apn": "^8.1.0", - "@prisma/adapter-pg": "^7.10.0", - "@prisma/client": "^7.10.0", + "@prisma/adapter-pg": "^7.10.1", + "@prisma/client": "^7.10.1", "@simplewebauthn/browser": "^14.0.0", "@simplewebauthn/server": "^14.0.3", - "@tanstack/react-query": "5.104.2", - "@tanstack/react-virtual": "^3.14.17", + "@tanstack/react-query": "5.104.3", + "@tanstack/react-virtual": "^3.14.21", "@types/sax": "^1.2.7", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "date-fns": "^4.4.0", - "dotenv": "^18.0.7", + "dotenv": "^18.0.8", "fflate": "^0.8.3", "jose": "^6.2.12", "jspdf": "^4.2.1", - "jspdf-autotable": "^5.0.8", - "lucide-react": "^1.56.0", + "jspdf-autotable": "^5.0.9", + "lucide-react": "^1.59.0", "mmdb-lib": "^3.0.3", "next": "16.3.8", - "nodemailer": "10.1.0", + "nodemailer": "10.1.7", "otpauth": "^9.5.2", "p-limit": "^7.3.3", "pg": "^8.23.1", - "pg-boss": "^12.37.1", - "prisma": "^7.10.0", + "pg-boss": "^12.38.0", + "prisma": "^7.10.1", "qrcode": "^1.5.4", "radix-ui": "^1.8.1", - "react": "19.3.0", + "react": "19.3.1", "react-day-picker": "^10.0.2", - "react-dom": "19.3.0", + "react-dom": "19.3.1", "recharts": "^3.10.1", "rrule": "^2.8.1", "sax": "^1.6.1", "sonner": "^2.0.8", "tailwind-merge": "^3.7.0", "tesseract.js": "^7.0.0", - "undici": "^8.11.2", + "undici": "^8.12.2", "web-push": "^3.6.7", "yaml": "^2.9.1", "zod": "^4.6.7", @@ -105,9 +105,9 @@ "zxcvbn-typescript": "^5.0.1" }, "devDependencies": { - "@axe-core/playwright": "^4.13.0", + "@axe-core/playwright": "^4.14.0", "@next/bundle-analyzer": "^16.3.8", - "@playwright/test": "^1.64.0", + "@playwright/test": "^1.64.1", "@stryker-mutator/core": "10.0.0", "@stryker-mutator/vitest-runner": "10.0.0", "@tailwindcss/postcss": "^4", @@ -120,7 +120,7 @@ "@types/react": "^19.3.0", "@types/react-dom": "^19.3.0", "@types/web-push": "^3.6.4", - "@vitest/coverage-v8": "^5.0.4", + "@vitest/coverage-v8": "^5.0.5", "ajv": "^8.20.0", "eslint": "^9", "eslint-config-next": "16.3.8", @@ -131,11 +131,11 @@ "pdfjs-dist": "6.4.299", "prettier": "^3.9.10", "prettier-plugin-tailwindcss": "^0.8.1", - "shadcn": "^4.21.4", + "shadcn": "^4.22.0", "tailwindcss": "^4", "testcontainers": "^12.2.1", "tw-animate-css": "^1.4.0", "typescript": "^6", - "vitest": "^5.0.4" + "vitest": "^5.0.5" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index aee6822ad..579276b80 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -36,6 +36,7 @@ overrides: brace-expansion@>=2.0.0 <2.1.7: ^2.1.7 brace-expansion@>=4.0.0 <5.0.12: ^5.0.12 undici@>=7.0.0 <7.29.1: ^7.29.1 + vite@>=8.0.0 <8.1.6: ^8.1.6 patchedDependencies: '@stryker-mutator/vitest-runner@10.0.0': 9ad3e9855fd9781fd36e872ed42c5f934f91b962d6830e6cc56d56bf16e1dabd @@ -45,20 +46,20 @@ importers: .: dependencies: '@aws-sdk/client-s3': - specifier: ^3.1150.0 - version: 3.1150.0 + specifier: ^3.1152.0 + version: 3.1152.0 '@aws-sdk/lib-storage': - specifier: 3.1150.0 - version: 3.1150.0(@aws-sdk/client-s3@3.1150.0) + specifier: 3.1152.0 + version: 3.1152.0(@aws-sdk/client-s3@3.1152.0) '@dnd-kit/core': specifier: ^6.3.1 - version: 6.3.1(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + version: 6.3.1(react-dom@19.3.1(react@19.3.1))(react@19.3.1) '@dnd-kit/sortable': specifier: ^10.0.0 - version: 10.0.0(@dnd-kit/core@6.3.1(react-dom@19.3.0(react@19.3.0))(react@19.3.0))(react@19.3.0) + version: 10.0.0(@dnd-kit/core@6.3.1(react-dom@19.3.1(react@19.3.1))(react@19.3.1))(react@19.3.1) '@dnd-kit/utilities': specifier: ^3.2.2 - version: 3.2.2(react@19.3.0) + version: 3.2.2(react@19.3.1) '@modelcontextprotocol/sdk': specifier: ^1.32.1 version: 1.32.1(@cfworker/json-schema@4.1.1)(supports-color@7.2.0)(zod@4.6.7) @@ -72,11 +73,11 @@ importers: specifier: ^8.1.0 version: 8.1.0(supports-color@7.2.0) '@prisma/adapter-pg': - specifier: ^7.10.0 - version: 7.10.0 + specifier: ^7.10.1 + version: 7.10.1 '@prisma/client': - specifier: ^7.10.0 - version: 7.10.0(prisma@7.10.0(@types/node@26.6.5)(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(magicast@0.5.5)(react-dom@19.3.0(react@19.3.0))(react@19.3.0)(typescript@6.0.3))(typescript@6.0.3) + specifier: ^7.10.1 + version: 7.10.1(prisma@7.10.1(@types/node@26.6.5)(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(magicast@0.5.5)(react-dom@19.3.1(react@19.3.1))(react@19.3.1)(typescript@6.0.3))(typescript@6.0.3) '@simplewebauthn/browser': specifier: ^14.0.0 version: 14.0.0 @@ -84,11 +85,11 @@ importers: specifier: ^14.0.3 version: 14.0.3 '@tanstack/react-query': - specifier: 5.104.2 - version: 5.104.2(react@19.3.0) + specifier: 5.104.3 + version: 5.104.3(react@19.3.1) '@tanstack/react-virtual': - specifier: ^3.14.17 - version: 3.14.17(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + specifier: ^3.14.21 + version: 3.14.21(react-dom@19.3.1(react@19.3.1))(react@19.3.1) '@types/sax': specifier: ^1.2.7 version: 1.2.7 @@ -102,8 +103,8 @@ importers: specifier: ^4.4.0 version: 4.4.0 dotenv: - specifier: ^18.0.7 - version: 18.0.7 + specifier: ^18.0.8 + version: 18.0.8 fflate: specifier: ^0.8.3 version: 0.8.3 @@ -114,20 +115,20 @@ importers: specifier: ^4.2.1 version: 4.2.1 jspdf-autotable: - specifier: ^5.0.8 - version: 5.0.8(jspdf@4.2.1) + specifier: ^5.0.9 + version: 5.0.9(jspdf@4.2.1) lucide-react: - specifier: ^1.56.0 - version: 1.56.0(@types/react@19.3.0)(react@19.3.0) + specifier: ^1.59.0 + version: 1.59.0(@types/react@19.3.0)(react@19.3.1) mmdb-lib: specifier: ^3.0.3 version: 3.0.3 next: specifier: 16.3.8 - version: 16.3.8(@babel/core@7.29.7(supports-color@7.2.0))(@opentelemetry/api@1.9.1)(@playwright/test@1.64.0)(@types/node@26.6.5)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + version: 16.3.8(@babel/core@7.29.7(supports-color@7.2.0))(@opentelemetry/api@1.9.1)(@playwright/test@1.64.1)(@types/node@26.6.5)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) nodemailer: - specifier: 10.1.0 - version: 10.1.0 + specifier: 10.1.7 + version: 10.1.7 otpauth: specifier: ^9.5.2 version: 9.5.2 @@ -138,29 +139,29 @@ importers: specifier: ^8.23.1 version: 8.23.1 pg-boss: - specifier: ^12.37.1 - version: 12.37.1(@opentelemetry/api@1.9.1) + specifier: ^12.38.0 + version: 12.38.0(@opentelemetry/api@1.9.1) prisma: - specifier: ^7.10.0 - version: 7.10.0(@types/node@26.6.5)(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(magicast@0.5.5)(react-dom@19.3.0(react@19.3.0))(react@19.3.0)(typescript@6.0.3) + specifier: ^7.10.1 + version: 7.10.1(@types/node@26.6.5)(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(magicast@0.5.5)(react-dom@19.3.1(react@19.3.1))(react@19.3.1)(typescript@6.0.3) qrcode: specifier: ^1.5.4 version: 1.5.4 radix-ui: specifier: ^1.8.1 - version: 1.8.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + version: 1.8.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) react: - specifier: 19.3.0 - version: 19.3.0 + specifier: 19.3.1 + version: 19.3.1 react-day-picker: specifier: ^10.0.2 - version: 10.0.2(@types/react@19.3.0)(react@19.3.0) + version: 10.0.2(@types/react@19.3.0)(react@19.3.1) react-dom: - specifier: 19.3.0 - version: 19.3.0(react@19.3.0) + specifier: 19.3.1 + version: 19.3.1(react@19.3.1) recharts: specifier: ^3.10.1 - version: 3.10.1(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react-is@16.13.1)(react@19.3.0)(redux@5.0.1) + version: 3.10.1(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react-is@16.13.1)(react@19.3.1)(redux@5.0.1) rrule: specifier: ^2.8.1 version: 2.8.1 @@ -169,7 +170,7 @@ importers: version: 1.6.1 sonner: specifier: ^2.0.8 - version: 2.0.8(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + version: 2.0.8(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) tailwind-merge: specifier: ^3.7.0 version: 3.7.0 @@ -177,8 +178,8 @@ importers: specifier: ^7.0.0 version: 7.0.0 undici: - specifier: ^8.11.2 - version: 8.11.2 + specifier: ^8.12.2 + version: 8.12.3 web-push: specifier: ^3.6.7 version: 3.6.7(supports-color@7.2.0) @@ -196,20 +197,20 @@ importers: version: 5.0.1 devDependencies: '@axe-core/playwright': - specifier: ^4.13.0 - version: 4.13.0(playwright-core@1.64.0) + specifier: ^4.14.0 + version: 4.14.0(playwright-core@1.64.1) '@next/bundle-analyzer': specifier: ^16.3.8 version: 16.3.8 '@playwright/test': - specifier: ^1.64.0 - version: 1.64.0 + specifier: ^1.64.1 + version: 1.64.1 '@stryker-mutator/core': specifier: 10.0.0 version: 10.0.0(@types/node@26.6.5) '@stryker-mutator/vitest-runner': specifier: 10.0.0 - version: 10.0.0(patch_hash=9ad3e9855fd9781fd36e872ed42c5f934f91b962d6830e6cc56d56bf16e1dabd)(@stryker-mutator/core@10.0.0(@types/node@26.6.5))(vitest@5.0.4) + version: 10.0.0(patch_hash=9ad3e9855fd9781fd36e872ed42c5f934f91b962d6830e6cc56d56bf16e1dabd)(@stryker-mutator/core@10.0.0(@types/node@26.6.5))(vitest@5.0.5) '@tailwindcss/postcss': specifier: ^4 version: 4.3.3 @@ -241,8 +242,8 @@ importers: specifier: ^3.6.4 version: 3.6.4 '@vitest/coverage-v8': - specifier: ^5.0.4 - version: 5.0.4(vitest@5.0.4) + specifier: ^5.0.5 + version: 5.0.5(vitest@5.0.5) ajv: specifier: ^8.20.0 version: 8.20.0 @@ -274,8 +275,8 @@ importers: specifier: ^0.8.1 version: 0.8.1(prettier@3.9.10) shadcn: - specifier: ^4.21.4 - version: 4.21.4(@cfworker/json-schema@4.1.1)(supports-color@7.2.0) + specifier: ^4.22.0 + version: 4.22.0(@cfworker/json-schema@4.1.1)(supports-color@7.2.0) tailwindcss: specifier: ^4 version: 4.3.3 @@ -289,8 +290,8 @@ importers: specifier: ^6 version: 6.0.3 vitest: - specifier: ^5.0.4 - version: 5.0.4(@opentelemetry/api@1.9.1)(@types/node@26.6.5)(@vitest/coverage-v8@5.0.4)(msw@2.14.6(@types/node@26.6.5)(typescript@6.0.3))(vite@8.1.5(@types/node@26.6.5)(jiti@2.7.0)(yaml@2.9.1)) + specifier: ^5.0.5 + version: 5.0.5(@opentelemetry/api@1.9.1)(@types/node@26.6.5)(@vitest/coverage-v8@5.0.5)(msw@2.14.6(@types/node@26.6.5)(typescript@6.0.3))(vite@8.1.6(@types/node@26.6.5)(jiti@2.7.0)(yaml@2.9.1)) packages: @@ -298,70 +299,70 @@ packages: resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==} engines: {node: '>=10'} - '@aws-sdk/checksums@3.1001.3': - resolution: {integrity: sha512-CcAkVyEUKGngbEqJdVdSc53Iy52+XzVL5NIcTen2BlK8UByNC1DThVtf5VKSrNzyA+SqyTvs+3S+knVBbOUizQ==} + '@aws-sdk/checksums@3.1001.4': + resolution: {integrity: sha512-H8BiG/2DnfwydVxD1uDPLa1s1sfhRnVTbD2OwzfTr3WlJzgbIzICDXxBTlTMjbWNNCe4VS9X55fco+JI34L9kw==} engines: {node: '>=20.0.0'} - '@aws-sdk/client-s3@3.1150.0': - resolution: {integrity: sha512-tv7KzK7PY7GqBwA6nzAVAsazIY/tAagDjlqvmCwlIYpH2SMfUuD6pJklg++LGcMaNy5p0VMvz+F+/es1BpoyRw==} + '@aws-sdk/client-s3@3.1152.0': + resolution: {integrity: sha512-oI1+ZaM6pSOwZazu4nAb7rL8ug7LSjg31tDoJqRB9MooRqqWCf/qvkIZqne1mRA3xK3slg6K0RNWXaJYHbWLvg==} engines: {node: '>=20.0.0'} - '@aws-sdk/core@3.978.3': - resolution: {integrity: sha512-nslpzUCTI9b2IQMWSd6mSAXldBexPc8rr0d3dXUutXYFLFaTdsHa2GD7sJdsn3PhyjK0O5K8ypd6zi+RXXPVlg==} + '@aws-sdk/core@3.978.4': + resolution: {integrity: sha512-JvGp6y9sPX8FSs2K/EmFMZcUfdkHxqGE45+oxwGC75OIh50j2e4cbx7zcR3BwNs2DSGqJdBja5h6Vy89iIDmRQ==} engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-env@3.972.74': - resolution: {integrity: sha512-rsRJcW2iFUhCgclyD6OSbMEaPl8Eu0qSeRZb2uuCK1ViZ66hl5wOe/p6Vr6MO3b+ydyKgFeqza2p6tZThvILlw==} + '@aws-sdk/credential-provider-env@3.972.75': + resolution: {integrity: sha512-nc8Epg98PcYoKNx0VFlwtmZSCP+tuJ1FLWqpslET/gp4h/WYuULSJqtxppM6ywKKDdzNJPvLD7ceRfmxm3Qdxw==} engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-http@3.972.76': - resolution: {integrity: sha512-pnKdh0Br7OJPj+7qKjzgTfDTFW0RhEMKXNDR2jv9CAM4oHuG/4z3hRh8KtutLfpmItuRtDbKBmwWkDqHwwAWXw==} + '@aws-sdk/credential-provider-http@3.972.77': + resolution: {integrity: sha512-KkYFdIQwuw9IOHfEzK78slxmVWuhcm1w6YgE3V3WVxAeRRj9q5J9jlwzpY/oszWxd4SD96FTFkDdRGBdMFF2Mw==} engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-ini@3.973.19': - resolution: {integrity: sha512-kcjvAxqQEV8yuwkX58RelNn3ne65o7JgRWf3Z/gVvbi7Xuw1qSDLJlatrLD76+40cmRxmNkLxBoyp/vAQChQ5g==} + '@aws-sdk/credential-provider-ini@3.973.20': + resolution: {integrity: sha512-OSl2R/0zLkoeKEIy6oP2tu/bsSdY+DGgro2eOffMD2CcP8POv7rS14FY5Jzc5bjnqB1G9wv/aM1akHC2mdlhZw==} engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-login@3.972.81': - resolution: {integrity: sha512-CeQHSbE2CnVdaHIJ/NayRaCecIER+Pm9faUPkmRATO54rCoMTEPSbVieGHzEwvm1dZM2HIs6qQZCznhayNTWJQ==} + '@aws-sdk/credential-provider-login@3.972.82': + resolution: {integrity: sha512-8jyDPtf1WPxeQ/5xlNpyxAD6P7YKwkYeNk8B2vFEDwkPWDvb7c3tQJG5/jbNWbF2yTMIH9tDTueBkdfovYIAPw==} engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-node@3.972.86': - resolution: {integrity: sha512-WNttSyr6e8innPtBSty//CdnqJcgZN6eAbtw3v/CR2IObz6MYZEo8eusdtuCHJ2ujwFqC2bSPHVyhXGHx8HruQ==} + '@aws-sdk/credential-provider-node@3.972.87': + resolution: {integrity: sha512-TKj2C0x4IMPSSOAlmeryX8fCPvvPS2pFJz2ikV9IGqyh6Ehsg4leMalCTVfV2rvMTrlGAe7X7nK0HZ3P0KZR9g==} engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-process@3.972.74': - resolution: {integrity: sha512-Yj6kZFJQdbR+PygTkqdVLPHHpdDpOS9GCYVcDCuweCRRig9LF24ELQBMfyUw1vspvrZJUfnOGVyMjhaL094ahw==} + '@aws-sdk/credential-provider-process@3.972.75': + resolution: {integrity: sha512-ZNm8T/HJNhpS8MikDVAsym8QyUSUuOUGiCkmI3TTya7G6xhOgfRLd2iK91HhQQyvRM36eo6Xm845hHVyfToA2w==} engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-sso@3.973.18': - resolution: {integrity: sha512-FnpPhdY4qUGtX/8X6eKxh+uD+9Y19i9FYibgk4SBe0M935s9vocHkHzaxaMNk+YkHmvzgdu8cHA5SCoeU+kkew==} + '@aws-sdk/credential-provider-sso@3.973.19': + resolution: {integrity: sha512-qHGDAGesyWtRw3BapyFkYSUzVNrC1CWPS6+S2lf/MLMEkWB2gR3Bl6imVHy1nhImXKS+gcuUDzbu39BnsHJc1Q==} engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-web-identity@3.972.80': - resolution: {integrity: sha512-TJF25Ndwy2HxcVj6oxMgp8qVlJAawlExHYBXqhuC6Zt2m+A7WpdkjBD6QglNbiArlteYljZHEY3R7KKmno6ISA==} + '@aws-sdk/credential-provider-web-identity@3.972.81': + resolution: {integrity: sha512-nHkgkMIC6NHgqfI6UAQq5cCS4bUkNxkT+r9fXCq/X4IKfX/DHGZ8UM3T4b78OXpWK5OlzEzN4kxwyax0W/MUCA==} engines: {node: '>=20.0.0'} - '@aws-sdk/lib-storage@3.1150.0': - resolution: {integrity: sha512-ucHABAJADcOkNEmQtaHkqq7kSBe7yZ5+gudhEfiN1NxDsz2WpON1N2NpbENsu4oWxH4Ueq4wqEIbBUt8zbEFbw==} + '@aws-sdk/lib-storage@3.1152.0': + resolution: {integrity: sha512-UU1fDLKBd8az+4AmNx30ggnA4D4HolUd1MxT/J6ka76qKcckI0IxjAy6FpX/vwiUf2KDNxxN2YX0cfwjMqzWoQ==} engines: {node: '>=20.0.0'} peerDependencies: - '@aws-sdk/client-s3': ^3.1150.0 + '@aws-sdk/client-s3': ^3.1152.0 - '@aws-sdk/middleware-sdk-s3@3.972.79': - resolution: {integrity: sha512-UfgAQosf4IEXldMsyntVuHsJnaQzlH6pmkOr6dQy7CaOYSlGz3mWiW4Di7XfQ/kPJP6xE7oMshz1V0E8RqGdRg==} + '@aws-sdk/middleware-sdk-s3@3.972.80': + resolution: {integrity: sha512-zX3lXN/9XOUAuGpxgt3wXwZrXhTjQYEgRsqa856PbZWpOtleUWmCMbhlkLfusPuhW24cOC/8JYR8hnRalrrIkQ==} engines: {node: '>=20.0.0'} - '@aws-sdk/nested-clients@3.997.48': - resolution: {integrity: sha512-B01jdXJtAW/XOiUzGdI2mz67WyEHbGEc9X6Euzw3bjLAa+txTqdZDER4X2FsIEcXzV2ABqudVZ5thali8tCCzQ==} + '@aws-sdk/nested-clients@3.997.49': + resolution: {integrity: sha512-zjqIqzTYLRKhT7TPJDUreK9vG9PjhmnfLCaTaqArkRT7j+Ogl0tuXB7MpSd8rlRfRT+xTOeyNFWaOfyNcf51hg==} engines: {node: '>=20.0.0'} '@aws-sdk/signature-v4-multi-region@3.996.48': resolution: {integrity: sha512-cFDXfEbXaGwbnpvR/DyqfxfjxpjXVRVthQLgkwvcfDS5u3+8G9WSj1X1Ag/pTRg/QOeOno1Tq3ycEdrKsCUcOA==} engines: {node: '>=20.0.0'} - '@aws-sdk/token-providers@3.1150.0': - resolution: {integrity: sha512-wDU36ofhabZYu+d2KSriyDbwzxNoaOp+4dG0p3qkabk7HeALR5nIWyQbjoyJZLn3iqWkv/xNdyUfbfevDVDzzg==} + '@aws-sdk/token-providers@3.1153.0': + resolution: {integrity: sha512-6bKJ2OfLboxhzeJ3hBDeB8L6emCYf9DfdrjBAharLrY42J1RV/6jA6G8jvfNghzID8Yb40BdSY7jQMtWx9Ot0Q==} engines: {node: '>=20.0.0'} '@aws-sdk/types@3.974.6': @@ -376,8 +377,9 @@ packages: resolution: {integrity: sha512-sl4Bm6yiMNYrZKkqqDFWN0UfnWhlS8ivKxrYl+6t0gCLrqr8y3B2IqZZbFRkfaVVp7C/baApyh71P+LeE1A2sQ==} engines: {node: '>=18.0.0'} - '@axe-core/playwright@4.13.0': - resolution: {integrity: sha512-6YLx+kxXu5GJceG4ozFg+33a2EMTdjYwWGloJ3sb9Kta5pp+ZNS53uxGVog5JetIY8s++P5UrtX+cri+u0VAVg==} + '@axe-core/playwright@4.14.0': + resolution: {integrity: sha512-SGUhG5PIHd2eMdpgBF2PyQLOhX+qyDR2PT4cmoS4Qk2ak+jX/rrSvst9l0PGv5WTHolKpUMvVmoHpr4cZgf1RQ==} + engines: {node: '>=22.12.0'} peerDependencies: playwright-core: '>= 1.0.0' @@ -1869,22 +1871,22 @@ packages: resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} engines: {node: '>=14'} - '@playwright/test@1.64.0': - resolution: {integrity: sha512-oTN9FNYuN6bqQMwiho6jJKLCG2/QOYCixGXuCjRmzOq23eODeukuY2XiAVoVpAECbuezMt1MHQjAiQxK8F+Y+g==} + '@playwright/test@1.64.1': + resolution: {integrity: sha512-IaYGml/nSOiZWROfPtuIL7neuu9qDOZ3TNvNljUIrhDMxiGTgPjzj/g9p9RxpS2tSepPDRY+UwAaEeiKtUvIAg==} engines: {node: '>=20'} hasBin: true '@polka/url@1.0.0-next.29': resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==} - '@prisma/adapter-pg@7.10.0': - resolution: {integrity: sha512-N7nwSor0HO1Kz6xBv0TPAjAPysKK0fac6p4fVN3ensLOuzc/83Fgmln5k92eK/cvzqdkSR/2kkAqlbcdwVrwpw==} + '@prisma/adapter-pg@7.10.1': + resolution: {integrity: sha512-EP9ruPYHjKP80WZlb5qVaEXKgZjJieBFjAxcBtQnDAHD6+16UN4keVmBh6L/RUpuDQT2FQRbvKNgq5d4Ix+IkQ==} - '@prisma/client-runtime-utils@7.10.0': - resolution: {integrity: sha512-cnCy7lUV8/CctgKVEmqAbSLAmwqJdE/qAlqTBk/0NDk59zEb2cZ0M0M0E4vVPnqbSEYudRroQDvOWfUZH6RIfw==} + '@prisma/client-runtime-utils@7.10.1': + resolution: {integrity: sha512-AzoATVIgmO3lcZfM443+9SI7+cyLMW2+Psw4op8WQHpHvHCX//CKxv8fwhiL0yGWF8DjdQrFewxrZo5McBdB8g==} - '@prisma/client@7.10.0': - resolution: {integrity: sha512-Ubw/QS9JGIBSBUsyxAUQuK/Jcu0Tsva7le7QbLd91Kix9yJvYDdj5QkwgEbbZniH80dd+sziQcALPc+HnvQC8Q==} + '@prisma/client@7.10.1': + resolution: {integrity: sha512-5tgAbFGh//zVOC6WPNVgziOErJHYkUE+wmqCwbIyq6IrTXUYtv9y2iBFGh/Owo99Mx0+cr0j1FOrxkYBMuyhaQ==} engines: {node: ^20.19 || ^22.12 || >=24.0} peerDependencies: prisma: '*' @@ -1895,11 +1897,11 @@ packages: typescript: optional: true - '@prisma/config@7.10.0': - resolution: {integrity: sha512-Rcg828gIRE3HOQ3pOATFjV5d/P0U9OIobxhd/IMxlfWjA4vru0eGwb0AIwFw0rmcLMVShohZYWPixVxkBHsxUA==} + '@prisma/config@7.10.1': + resolution: {integrity: sha512-+0swF5ELUa3P/9cIR26+gpYjjCbGE0qvEBZhV4zMDkYsrtHjZ884MQbOnWFWXj600N0sSJNnHbtBqe12yDngWg==} - '@prisma/debug@7.10.0': - resolution: {integrity: sha512-caygJKtltmRIgdJ3jRpkOr7yM4DW6zxo5uOmojKWFb3asnxWoRkQOwZmXBgD8FZp4htrX+nMpcWqDwzlQ1+Y4g==} + '@prisma/debug@7.10.1': + resolution: {integrity: sha512-eFCAkGZ13D/qPg48JBlG5IWi9cSBBOC7oG3abDPcAkYioDWlIjh0YJJLK2J7qo5S+xWWD8I6egAN/8KbCaiMCA==} '@prisma/debug@7.2.0': resolution: {integrity: sha512-YSGTiSlBAVJPzX4ONZmMotL+ozJwQjRmZweQNIq/ER0tQJKJynNkRB3kyvt37eOfsbMCXk3gnLF6J9OJ4QWftw==} @@ -1907,20 +1909,20 @@ packages: '@prisma/dev@0.24.17': resolution: {integrity: sha512-UvdZzmpFwknnfreh6Jije84ekkYGPYEJhXG1tFzCsCfQyzJifrOo38eZc0qajzvaC6OLUOrN9ML5XfCnEZL9DA==} - '@prisma/driver-adapter-utils@7.10.0': - resolution: {integrity: sha512-u8zkcRLlaryO652T4qavBg0HmzNW5tSKdsCn6hc1PhWAp/J6k0vrxLuUs+b9o+HcjsK7Dfa01o4OFSn0frauJA==} + '@prisma/driver-adapter-utils@7.10.1': + resolution: {integrity: sha512-DJ4c6xvVnm3s9ZUyql9nCUicADSqyvH+CjXRCM9L6m5zJxCNmebCElG6X4irICq3rosBwlX+S19Dxq/4IrZk3A==} - '@prisma/engines-version@7.10.0-4.0edf323efd1d98336f3f0a68684b56f689b900d3': - resolution: {integrity: sha512-8OJ6RuZTZ06eFUOtBwxVmv8XMmOW6HWN5F+uxUbZkGxR0Bfab1dfAdXaHPmR5mb59E+fmUo8IOzXlbLY1SClbw==} + '@prisma/engines-version@8.1.0-1.d1b3f377ca835d73fd50954a5e22128b9ebdd6b8': + resolution: {integrity: sha512-XjXmwjN2NE340RpXoXv8ZF8aNAvr0FE+7gNfkKeW6vyjXs7F8uxvEbWQoVrhioXDQoIsnNmv7r+wxvzfdEZaWA==} - '@prisma/engines@7.10.0': - resolution: {integrity: sha512-KNumN6NHFwybvfdYzTee9pqwx5PvknpWAaHn6L5NsbrKdl+SQrsVZs9opKs6U6SAsvB26HDt3WybRjOhgoWOYQ==} + '@prisma/engines@7.10.1': + resolution: {integrity: sha512-R6/ghv1Bu36FhCdQLFof1pNtyoWoeDzJLYeTKjgqb4eGtOAJx7ZqJtcm4pzkahLfZ9rYz8AkmsMDwkyiYbXPCQ==} - '@prisma/fetch-engine@7.10.0': - resolution: {integrity: sha512-Zqyu8DY14t6W/xwmAxUYWCXtHrvQnSvT644EAZSsdM8NSmCS74vJJbBKdVsK3ucFpnUWkEpbO1a0CxJXrg130g==} + '@prisma/fetch-engine@7.10.1': + resolution: {integrity: sha512-qZezeaExLWG2ptbHOrMxdiWluKHgqEclTdJbvY6pk6+Wg7EiVkeq5U3cXz9ti8nd0bngNvsDgledLHVRg5LF1w==} - '@prisma/get-platform@7.10.0': - resolution: {integrity: sha512-0bra1LFYi8xNw0yqV62bHJNQk4BKleOngZiqPWIQc7a3+9q6rqsnqJ15BuepP8943PFMvtmgnP52juZsyYkA6w==} + '@prisma/get-platform@7.10.1': + resolution: {integrity: sha512-iVnghHne5vcaobBjPVCMEjh5r/1UCj394EIGTUclasFRatpR/MQwcM2veLv5kgoFSyYP54nch/lNFCQRYcDxvg==} '@prisma/get-platform@7.2.0': resolution: {integrity: sha512-k1V0l0Td1732EHpAfi2eySTezyllok9dXb6UQanajkJQzPUGi3vO2z7jdkz67SypFTdmbnyGYxvEvYZdZsMAVA==} @@ -2846,8 +2848,8 @@ packages: '@sec-ant/readable-stream@0.4.1': resolution: {integrity: sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==} - '@shadcn/registry@0.1.3': - resolution: {integrity: sha512-82/OmKAVM9pChDHFVAsDP9PcimPGSKZGdbcdwdGGGcSI69ImpN4ZnqsfZ4hkOFEMal92t3y9djYJHMjO4PcVZA==} + '@shadcn/registry@0.2.0': + resolution: {integrity: sha512-m3TeqU/+av2kgOLfESHkWzsmhuQuY4NXZd5Y1Rd4lzy4IX3JJ5+k2mYfkq6OT8oMuk2W4GQPAkO7+CC7KlpSbw==} engines: {node: '>=20.18.1'} '@simplewebauthn/browser@14.0.0': @@ -2865,6 +2867,10 @@ packages: resolution: {integrity: sha512-ejTukBuP/Pg3t9N8Ux+0zKsXeHWm7p6W3HbXFOyltKasSxZwCFmvTut7kBdZSCF3m9y1bXXyVu6p2xuMFcsTyQ==} engines: {node: '>=18.0.0'} + '@smithy/core@3.36.1': + resolution: {integrity: sha512-WC5W1Jrsfxu1y6LeqXMie7N14qmcdafij/Haye4feVlyMMXLGpYxBeOX6Ee1iRJbDzGG5LVman8U1rrt9MYFHA==} + engines: {node: '>=18.0.0'} + '@smithy/credential-provider-imds@4.5.2': resolution: {integrity: sha512-A9uSdn72ozbRUSit0eib0TW7nXuNPlaeM0zcGkJ+nE6tFcSDbnmtwoxbTCFBukVQcszDAyvsd7+rTduPTXpygg==} engines: {node: '>=18.0.0'} @@ -3009,22 +3015,22 @@ packages: '@tailwindcss/postcss@4.3.3': resolution: {integrity: sha512-JTSZZGQi1AyKirbLN3azmjVzef92tcX7h+iSqPdaeStyFpGpDlKvvpxeOE8njhbUanbRwr3z8DyzhICWnMtQeg==} - '@tanstack/query-core@5.104.2': - resolution: {integrity: sha512-/qbsxJK5c3Ve1aFIULWYNcT2jeRUAiD4HME6Eu2ZdpofHoVgAjpkQhLg5xHuOoBkTPQUJLfP7zOxy0qsBcqROA==} + '@tanstack/query-core@5.104.3': + resolution: {integrity: sha512-DZY9TuJAOgU3UyHUhXb4CcTYEHNZszlwkPg4VViIwTxOKzLprPUU+rbaPZQd3srqkGOtpaDdKpW972UV/LKlcg==} - '@tanstack/react-query@5.104.2': - resolution: {integrity: sha512-xKmzWGhadbE5wGS/ZmXShRITnI9oItox5GO14KvFZco3+vhTAyXS1erYzJWdiY280zapAGJXFUL9nHgwP6SV/g==} + '@tanstack/react-query@5.104.3': + resolution: {integrity: sha512-5Ts1ImwJKqoJgPkLxl9WrPQy1JEoEmti3MaaODCfBamHUGFk57IFrc+AIksPq+BAm10EsroX56qm398Z42LG7w==} peerDependencies: react: ^18 || ^19 - '@tanstack/react-virtual@3.14.17': - resolution: {integrity: sha512-b7b5LwydgQAWcsAWEZyd3iZkN5/QcC6S0K5E1zNheyFoSZl7E78foepGeDHlX2+JuXilA+VIGGFsUYPFddOrig==} + '@tanstack/react-virtual@3.14.21': + resolution: {integrity: sha512-wWE/ZJDNNDp18JHWy7n5PQooqerHePVkEEu6Op0twKQ2InF9DPhYMQptQY0syDEviMd3/r8D0OUvKJibDGm9HQ==} peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - '@tanstack/virtual-core@3.19.2': - resolution: {integrity: sha512-Ch+e8t097FdTfuihfRwBMqz/OfRZKHOH/C213dbM1lXDZ8bv6r330Bcc9k9XCrP6NkoMYy0/ZCHcddOAPihW4w==} + '@tanstack/virtual-core@3.19.6': + resolution: {integrity: sha512-CsTW892z23gy7IyxYTTorVu1uZWB+0N349rSMgKz4rFLPpLza7F7TpcCKeI5iUAwYQCt8QUlOU67J+KXCxtpGw==} '@tesseract.js-data/deu@1.0.0': resolution: {integrity: sha512-3ter1p2gFAiCd5CMC+PodZ9YY2UDomkKj+ytlKeeZqOUmhLx1SDnYPf5zog0g5MBbxYveW+8omI3PUflqHfZ4Q==} @@ -3404,11 +3410,11 @@ packages: '@visx/vendor@4.0.0-alpha.0': resolution: {integrity: sha512-6I+MuqXBcv9jnlcVowHoHKSdk9gXTWkHLKyqBwRWg7LY6A3Ei8SHfubpqGV5rBUSppxMq2RszPJUS6w+H0YgmQ==} - '@vitest/coverage-v8@5.0.4': - resolution: {integrity: sha512-P5YcQY8Y6RmoE9xRXI38rT8pssbcubEzqtYTM2XQoxOXMDn04nRaTI1SM8qCl9/NfW3ag9GlzdSXu7IwO5Ft8A==} + '@vitest/coverage-v8@5.0.5': + resolution: {integrity: sha512-4KpkGdIrSqBihx/JyP/e+S+IKB98etg1XDQiLAxksLfcShnwi1VReGeOT9NI43hLHbknosKp/1CCLn9Zgcs8mg==} peerDependencies: - '@vitest/browser': 5.0.4 - vitest: 5.0.4 + '@vitest/browser': 5.0.5 + vitest: 5.0.5 peerDependenciesMeta: '@vitest/browser': optional: true @@ -3421,19 +3427,19 @@ packages: resolution: {integrity: sha512-gUsfXZJbzPamoIY5TvHFiMMoXESBrUMo+xqaj+rYrWI69+EnvRlYBlP96ZnHPY4vX8kyUpgAnFUCg5wZG/HkDQ==} engines: {node: '>=22'} - '@vitest/mocker@5.0.4': - resolution: {integrity: sha512-tqAlrYWce4iUxhr3ncNohejtCTOcHeE8XWrSNZY3OhPoDn01yeGv+gnaBy64ATp7o6pb03eIbBAjzmibpc9vIQ==} + '@vitest/mocker@5.0.5': + resolution: {integrity: sha512-xLL3lkDegJZw+MGoGaCCsFayDZthGgpAtr3Vziyfc4p7xH89K7KRPcRBBh/kEYm0Aqkt5DtTmkYpBtRV/FrHZg==} peerDependencies: msw: ^2.4.9 || ^3.0.0 - vite: ^6.0.0 || ^7.0.0 || ^8.0.0 + vite: ^8.1.6 peerDependenciesMeta: msw: optional: true vite: optional: true - '@vitest/spy@5.0.4': - resolution: {integrity: sha512-MZKk/gSkm2q+ICobMxCpH1mA+cCAWm8WV7UhxlqGjq/sJ3AGc1BqFOs5CRVXxH78wQtme9q/NAAgBquj4AcyMw==} + '@vitest/spy@5.0.5': + resolution: {integrity: sha512-lwJRExuH417257Q5wv9QYugN9IMJkoxcrKrYxGPQkBzl7wlNpTUxD/5yNX4ESKVUIShKPgkGchYPFMWOOk0wzw==} abort-controller@3.0.0: resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==} @@ -3617,6 +3623,10 @@ packages: resolution: {integrity: sha512-UzGt8zg7Ny8djbYMhxl2zuEevVa7r2gJjYY5Lwr1xM7+XU2nd6CkIWFTVcCIbAP63vSz71NaVyyuSk9lHKcy0A==} engines: {node: '>=4'} + axe-core@4.14.0: + resolution: {integrity: sha512-9WTZxEjsZ7b13TH8JPmbV2z8CHbl80/2hm3XPEG4JgNdQLK81IBRXmSxHfMAOkSqQeRxT/0dwNDz2GOm3zzpcQ==} + engines: {node: '>=4'} + axobject-query@4.1.0: resolution: {integrity: sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==} engines: {node: '>= 0.4'} @@ -3952,8 +3962,8 @@ packages: resolution: {integrity: sha512-piICUB6ei4IlTv1+653yq5+KoqfBYmj9bw6LqXoOneTMDXk5nM1qt12mFW1caG3LlJXEKW1Bp0WggEmIfQB34g==} engines: {node: '>= 14'} - cron-parser@5.10.1: - resolution: {integrity: sha512-pKRrRagItwk9rGIStcUyMxFX34x56zoX3KDf9HJ4ttwkXIK1qxK63EvXvEDmlxI9AdPJ+Y7TEKRftRlW5eSS7A==} + cron-parser@5.10.2: + resolution: {integrity: sha512-JfAYMY0LZaWQTuict8PhtF/ksSnq8xefSDmQUnnoqvkPYkU/y3UP0DTWzJKFBz0P1IXsDG8q0vY3zHkoFZOhvw==} engines: {node: '>=18'} cross-spawn@7.0.6: @@ -4183,8 +4193,8 @@ packages: resolution: {integrity: sha512-nI4U3TottKAcAD9LLud4Cb7b2QztQMUEfHbvhTH09bqXTxnSie8WnjPALV/WMCrJZ6UV/qHJ6L03OqO3LcdYZw==} engines: {node: '>=12'} - dotenv@18.0.7: - resolution: {integrity: sha512-QEvMEF8ca3GNZJJnAbOgxpj5ULhBvnMcURp+XKrtvqFNPgjBvYoswQ0Jl2cO7JzCjxZjJhr9ZuIEhlrRxMft/Q==} + dotenv@18.0.8: + resolution: {integrity: sha512-2KDeoFoK/zRtcnbunCDlLG5VWVMcyqeQc6iK65o231nfNAcuGrKoqUQfJFUtBI+QlShk5HKIHxwebBtUl3rEfA==} engines: {node: '>=12'} hasBin: true @@ -5153,8 +5163,8 @@ packages: resolution: {integrity: sha512-MT/xP0CrubFRNLNKvxJ2BYfy53Zkm++5bX9dtuPbqAeQpTVe0MQTFhao8+Cp//EmJp244xt6Drw/GVEGCUj40g==} engines: {node: '>=12', npm: '>=6'} - jspdf-autotable@5.0.8: - resolution: {integrity: sha512-Hy05N86yBO7CXBrnSLOge7i1ZYpKH2DjQ94iybaP7vBhSInjvRBgDc99ngKzSbSO8Jc98ZCally8I6n0tj2RJQ==} + jspdf-autotable@5.0.9: + resolution: {integrity: sha512-wIOG2ggjG6Y7qvqjgFACZZeu+oHxoxrhwCn1lTMxjPhojLVVvu2U2yS2srVCYl0CXe6pqiG/jQMIIS0CFn1ssw==} peerDependencies: jspdf: ^2 || ^3 || ^4 @@ -5267,30 +5277,60 @@ packages: cpu: [arm64] os: [android] + lightningcss-android-arm64@1.33.0: + resolution: {integrity: sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [android] + lightningcss-darwin-arm64@1.32.0: resolution: {integrity: sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [darwin] + lightningcss-darwin-arm64@1.33.0: + resolution: {integrity: sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [darwin] + lightningcss-darwin-x64@1.32.0: resolution: {integrity: sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [darwin] + lightningcss-darwin-x64@1.33.0: + resolution: {integrity: sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [darwin] + lightningcss-freebsd-x64@1.32.0: resolution: {integrity: sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [freebsd] + lightningcss-freebsd-x64@1.33.0: + resolution: {integrity: sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [freebsd] + lightningcss-linux-arm-gnueabihf@1.32.0: resolution: {integrity: sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==} engines: {node: '>= 12.0.0'} cpu: [arm] os: [linux] + lightningcss-linux-arm-gnueabihf@1.33.0: + resolution: {integrity: sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm] + os: [linux] + lightningcss-linux-arm64-gnu@1.32.0: resolution: {integrity: sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==} engines: {node: '>= 12.0.0'} @@ -5298,6 +5338,13 @@ packages: os: [linux] libc: [glibc] + lightningcss-linux-arm64-gnu@1.33.0: + resolution: {integrity: sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + libc: [glibc] + lightningcss-linux-arm64-musl@1.32.0: resolution: {integrity: sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==} engines: {node: '>= 12.0.0'} @@ -5305,6 +5352,13 @@ packages: os: [linux] libc: [musl] + lightningcss-linux-arm64-musl@1.33.0: + resolution: {integrity: sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + libc: [musl] + lightningcss-linux-x64-gnu@1.32.0: resolution: {integrity: sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==} engines: {node: '>= 12.0.0'} @@ -5312,6 +5366,13 @@ packages: os: [linux] libc: [glibc] + lightningcss-linux-x64-gnu@1.33.0: + resolution: {integrity: sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + libc: [glibc] + lightningcss-linux-x64-musl@1.32.0: resolution: {integrity: sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==} engines: {node: '>= 12.0.0'} @@ -5319,22 +5380,45 @@ packages: os: [linux] libc: [musl] + lightningcss-linux-x64-musl@1.33.0: + resolution: {integrity: sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + libc: [musl] + lightningcss-win32-arm64-msvc@1.32.0: resolution: {integrity: sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [win32] + lightningcss-win32-arm64-msvc@1.33.0: + resolution: {integrity: sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [win32] + lightningcss-win32-x64-msvc@1.32.0: resolution: {integrity: sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [win32] + lightningcss-win32-x64-msvc@1.33.0: + resolution: {integrity: sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [win32] + lightningcss@1.32.0: resolution: {integrity: sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==} engines: {node: '>= 12.0.0'} + lightningcss@1.33.0: + resolution: {integrity: sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==} + engines: {node: '>= 12.0.0'} + locate-path@3.0.0: resolution: {integrity: sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==} engines: {node: '>=6'} @@ -5401,8 +5485,8 @@ packages: resolution: {integrity: sha512-5J9ysMYUpYIg9RF2vJpy9SinEmSviFSe0GyPpCQ4L5QSkLAgeLXlTAOu2ZwWUU5m+0SBl6gUU1R1ZQB3aKypfA==} engines: {bun: '>=1.0.0', deno: '>=1.30.0', node: '>=8.0.0'} - lucide-react@1.56.0: - resolution: {integrity: sha512-b+hqj30/trHBma5r1zlKb6yLewQp6tUtnz9loc3xdrYerPmvLYhlEbyPVCJ8DOg2/blBf6lwPLznftFVACka6A==} + lucide-react@1.59.0: + resolution: {integrity: sha512-5qgMyyCPU6OvnKpLS2t693+ZtiqA8oaSweWoUows0BLFKFf6V75m4+CptpmohZHpD5IuOVIBXHBBiv2vpLJmtw==} peerDependencies: '@types/react': '*' react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0 @@ -5619,8 +5703,8 @@ packages: resolution: {integrity: sha512-BdiKAgbfRlvfMq/XzO/AF9NPmaA8a+YpLAyAFTVFBj3gARrAIfWvNq7Q7NE4se3KgFu0KkrvQMxTS0XVnsGzHg==} engines: {node: '>=18'} - nodemailer@10.1.0: - resolution: {integrity: sha512-fXW8AulB25aOTuwfRYV6lqHyfz2VM4rr4SGXH03mE7x5znEUpzL5mu6anARUpZN7744Ziwkmr4AxSLPJsBQspg==} + nodemailer@10.1.7: + resolution: {integrity: sha512-OQa6FDln5fLttmqSbjDiRYvFDOFNrBqfiOwHPlEbURil3b+vPB1v8xpi+Sp46drzNDlsTnNsS502vthmz56r4w==} engines: {node: '>=20.0.0'} non-error@0.1.0: @@ -5841,8 +5925,8 @@ packages: performance-now@2.1.0: resolution: {integrity: sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==} - pg-boss@12.37.1: - resolution: {integrity: sha512-LghWeOIbMCiLcTY3+8MInXaJ0wk0AOkbeUZ8oE+cGO0uoheGSW3GK57eDqEbrDGpwTIpX9BVphvdvWEDYTcJ9A==} + pg-boss@12.38.0: + resolution: {integrity: sha512-felA3fyPSJwWk5cb/VwAgnudCAzWEdgBjjf+eukXm8pExlwkhjdBbOMukMyG5s8uZHO6fZZZcaIaTa3oKpfSCg==} engines: {node: '>=22.12.0'} hasBin: true peerDependencies: @@ -5903,13 +5987,13 @@ packages: resolution: {integrity: sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==} engines: {node: '>=8'} - playwright-core@1.64.0: - resolution: {integrity: sha512-T9r+MZkTECl2+oUcZ26YgZzTrYidTsIhYYkJix+6iDOymOb1FB2RLLSOxRGnZGv5DStt+aduIVbt+E8WygXYpg==} + playwright-core@1.64.1: + resolution: {integrity: sha512-BC4s8y6SEVA6Q1rSxZTto8hI1KUI3xXlHGZgvGqP4IqPdI/a6FX9H29IDNz7vKo9l21b6LfYosScPL6M3xxyDQ==} engines: {node: '>=20'} hasBin: true - playwright@1.64.0: - resolution: {integrity: sha512-kVzTnYFYEyQ9RGzBjNCd2+yEqWmVbxl3AHPynv3qQ6JTjv4a+kwRcnqQwIpuP3NnrnIrE/Q6wWAIAvMfIE2/Yg==} + playwright@1.64.1: + resolution: {integrity: sha512-HCrVhHr5QQuwYoswEUGTb7gGp522yflbv/ST8nF4sFWGuLjQCt17Q/EubpGGSNKYn1tsTCPZxqHhHCfVURu7UA==} engines: {node: '>=20'} hasBin: true @@ -6033,8 +6117,8 @@ packages: resolution: {integrity: sha512-HzMy3Geq23nVALD/M2LliU+F+M+gVNsvkQWWqeBZ8HDiCgzo6YPJ/Omrmtq24EFrIsk0a3EkQGEd7bDOo+IhGA==} engines: {node: '>=18'} - prisma@7.10.0: - resolution: {integrity: sha512-o0ornyJOWgygVAzGCpr8PdXV8EJLHyVGDDUr/voBQt8Azzw8cYTByzzPGcA/m4tCkPcnJA8raEOv2CslsKhPEw==} + prisma@7.10.1: + resolution: {integrity: sha512-GoVeErHGGtsSjsflQ1r2BA3RKTXtxNraQJz8OLkLhoLr7OgCscdx4qw4WMM4HnERYc+a1cq2VnMNbep+vOjNcQ==} engines: {node: ^20.19 || ^22.12 || >=24.0} hasBin: true peerDependencies: @@ -6148,10 +6232,10 @@ packages: '@types/react': optional: true - react-dom@19.3.0: - resolution: {integrity: sha512-JDk8dgif51OjFoDE70+OT9ICyYr+69HlmihNwp1+Nsfbna3t5sIiCa9ZJktDmQ4/1b/rn26hIAR2uYXDMr5r0Q==} + react-dom@19.3.1: + resolution: {integrity: sha512-Y0bARz53lhEC1NMCASs0/j+faB/zqt3R8hpvg1e8+61ZVJ4UL0UdC4cBbXfZMcIuhrfgeILTLe+ZeBCavQPtKQ==} peerDependencies: - react: ^19.3.0 + react: ^19.3.1 react-is@16.13.1: resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} @@ -6198,8 +6282,8 @@ packages: '@types/react': optional: true - react@19.3.0: - resolution: {integrity: sha512-E8LUcbtBWt20bbl2YoHfx4ZDBdxVTfOKtCZn9cDSJ4l6/nuoApcpIBcj47t2wZoVX8g2ZHuMHbiShgCR1T5Sog==} + react@19.3.1: + resolution: {integrity: sha512-QNhWdt7nrgVWWQG58QuGsAR3jv3Hbe4mb4vz7w2csnHavciZJgda1CPNaldF6eVxv90ud3jUXpKlHL69gO38yw==} engines: {node: '>=0.10.0'} readable-stream@2.3.8: @@ -6416,8 +6500,8 @@ packages: setprototypeof@1.2.0: resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} - shadcn@4.21.4: - resolution: {integrity: sha512-YYfn/hsHbRH2mGwnKBx9tk+DjTQH+RUMS5iCxjKzwzuJ5uoHPqLOFUCJSeX0sXfrcBSfgQaXiOLGKNLjlIQxxw==} + shadcn@4.22.0: + resolution: {integrity: sha512-QKR3XUnatWlgo8IbZqGHrpsxjgdsfpPSAvJvLntQqAxPEf5U2sNsjDYLAS2xmFrOkC9lpj5UpZbrh/BuHrE2CQ==} engines: {node: '>=20.18.1'} hasBin: true @@ -6869,8 +6953,8 @@ packages: resolution: {integrity: sha512-dkrQXeHSaoamnItlYbmzG0wFYrM0ZwDxCIg0A7aKjTyyhh9svRzCNFEzV+Vm05/yehjCzjDZ31KXfGEjYSztDQ==} engines: {node: '>=20.18.1'} - undici@8.11.2: - resolution: {integrity: sha512-u4UB2/IrKdU6lFxumHmmo1a3fCQO5tzQllRorfoRS63txhrB7xTpSn1PftwC4qEHkOaqP95fCWW4lJzwErwzhQ==} + undici@8.12.3: + resolution: {integrity: sha512-+fdndUTwBP86cVjihTp21hZHo7d6oj1c/47XPA3lL8mM0XHs+C4EkcjmgRsFnFk2f/UONGDy9ItBItmXsLOQHw==} engines: {node: '>=22.19.0'} unicorn-magic@0.3.0: @@ -6958,8 +7042,8 @@ packages: victory-vendor@37.3.6: resolution: {integrity: sha512-SbPDPdDBYp+5MJHhBCAyI7wKM3d5ivekigc2Dk2s7pgbZ9wIgIBYGVw4zGHBml/qTFbexrofXW6Gu4noGxrOwQ==} - vite@8.1.5: - resolution: {integrity: sha512-7ULLwsCdYx/nRyrpiEwvqb5TFHrMVZyBt+rg/OAXT7rgj/z+DtTDyKFeLAdDkubDVDKD8jOsndmy7m55XcfUsw==} + vite@8.1.6: + resolution: {integrity: sha512-aRAHIxx5SW5zc1sMqFwJU/t5qaFfXb5Hj0J7W0VXihu/X99+346tz6080tO1T1xVIJORX/b3zVTXGFqUXh35uw==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: @@ -7001,23 +7085,23 @@ packages: yaml: optional: true - vitest@5.0.4: - resolution: {integrity: sha512-z0WxZRV2Sg48wF0FSgMq3TFG8JS38/Qr96vZVZdX6+Em9raQTvnXQvdTFJe2m3rfyJnEHeHQ/snceIdi8D1pVg==} + vitest@5.0.5: + resolution: {integrity: sha512-cPODOMF8YnkiiYtDdHo6n4wWmBrOBBbOIYH2d33M4l3CdNn72h+qMmO6uXffUN6A5AZFXNND/d+krS9mgkXRfg==} engines: {node: ^22.12.0 || ^24.0.0 || >=26.0.0} hasBin: true peerDependencies: '@edge-runtime/vm': '*' '@opentelemetry/api': ^1.9.0 '@types/node': ^22.0.0 || >=24.0.0 - '@vitest/browser-playwright': 5.0.4 - '@vitest/browser-preview': 5.0.4 + '@vitest/browser-playwright': 5.0.5 + '@vitest/browser-preview': 5.0.5 '@vitest/browser-webdriverio': ^5.0.0-beta.5 || >=5.0.0 - '@vitest/coverage-istanbul': 5.0.4 - '@vitest/coverage-v8': 5.0.4 - '@vitest/ui': 5.0.4 + '@vitest/coverage-istanbul': 5.0.5 + '@vitest/coverage-v8': 5.0.5 + '@vitest/ui': 5.0.5 happy-dom: '*' jsdom: '*' - vite: ^6.4.0 || ^7.0.0 || ^8.0.0 + vite: ^8.1.6 peerDependenciesMeta: '@edge-runtime/vm': optional: true @@ -7215,9 +7299,6 @@ packages: peerDependencies: zod: ^3.25.0 || ^4.0.0 - zod@3.25.76: - resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} - zod@4.6.7: resolution: {integrity: sha512-g8W2D2kU6a8untc053fX1uVBKzM7yqAxW/3zwk83+6RyF2+SKOuV+c0wBDhUwzV1PaGapmZH+rmcUzPf+3CGwg==} @@ -7228,126 +7309,126 @@ snapshots: '@alloc/quick-lru@5.2.0': {} - '@aws-sdk/checksums@3.1001.3': + '@aws-sdk/checksums@3.1001.4': dependencies: - '@aws-sdk/core': 3.978.3 + '@aws-sdk/core': 3.978.4 '@aws-sdk/types': 3.974.6 - '@smithy/core': 3.35.2 + '@smithy/core': 3.36.1 '@smithy/types': 4.19.0 tslib: 2.8.1 - '@aws-sdk/client-s3@3.1150.0': + '@aws-sdk/client-s3@3.1152.0': dependencies: - '@aws-sdk/checksums': 3.1001.3 - '@aws-sdk/core': 3.978.3 - '@aws-sdk/credential-provider-node': 3.972.86 - '@aws-sdk/middleware-sdk-s3': 3.972.79 + '@aws-sdk/checksums': 3.1001.4 + '@aws-sdk/core': 3.978.4 + '@aws-sdk/credential-provider-node': 3.972.87 + '@aws-sdk/middleware-sdk-s3': 3.972.80 '@aws-sdk/signature-v4-multi-region': 3.996.48 '@aws-sdk/types': 3.974.6 - '@smithy/core': 3.35.2 + '@smithy/core': 3.36.1 '@smithy/fetch-http-handler': 5.8.0 '@smithy/node-http-handler': 4.12.1 '@smithy/types': 4.19.0 tslib: 2.8.1 - '@aws-sdk/core@3.978.3': + '@aws-sdk/core@3.978.4': dependencies: '@aws-sdk/types': 3.974.6 '@aws-sdk/xml-builder': 3.972.41 '@aws/lambda-invoke-store': 0.3.0 - '@smithy/core': 3.35.2 + '@smithy/core': 3.36.1 '@smithy/signature-v4': 5.7.4 '@smithy/types': 4.19.0 bowser: 2.14.1 tslib: 2.8.1 - '@aws-sdk/credential-provider-env@3.972.74': + '@aws-sdk/credential-provider-env@3.972.75': dependencies: - '@aws-sdk/core': 3.978.3 + '@aws-sdk/core': 3.978.4 '@aws-sdk/types': 3.974.6 - '@smithy/core': 3.35.2 + '@smithy/core': 3.36.1 '@smithy/types': 4.19.0 tslib: 2.8.1 - '@aws-sdk/credential-provider-http@3.972.76': + '@aws-sdk/credential-provider-http@3.972.77': dependencies: - '@aws-sdk/core': 3.978.3 + '@aws-sdk/core': 3.978.4 '@aws-sdk/types': 3.974.6 - '@smithy/core': 3.35.2 + '@smithy/core': 3.36.1 '@smithy/fetch-http-handler': 5.8.0 '@smithy/node-http-handler': 4.12.1 '@smithy/types': 4.19.0 tslib: 2.8.1 - '@aws-sdk/credential-provider-ini@3.973.19': + '@aws-sdk/credential-provider-ini@3.973.20': dependencies: - '@aws-sdk/core': 3.978.3 - '@aws-sdk/credential-provider-env': 3.972.74 - '@aws-sdk/credential-provider-http': 3.972.76 - '@aws-sdk/credential-provider-login': 3.972.81 - '@aws-sdk/credential-provider-process': 3.972.74 - '@aws-sdk/credential-provider-sso': 3.973.18 - '@aws-sdk/credential-provider-web-identity': 3.972.80 - '@aws-sdk/nested-clients': 3.997.48 + '@aws-sdk/core': 3.978.4 + '@aws-sdk/credential-provider-env': 3.972.75 + '@aws-sdk/credential-provider-http': 3.972.77 + '@aws-sdk/credential-provider-login': 3.972.82 + '@aws-sdk/credential-provider-process': 3.972.75 + '@aws-sdk/credential-provider-sso': 3.973.19 + '@aws-sdk/credential-provider-web-identity': 3.972.81 + '@aws-sdk/nested-clients': 3.997.49 '@aws-sdk/types': 3.974.6 - '@smithy/core': 3.35.2 + '@smithy/core': 3.36.1 '@smithy/credential-provider-imds': 4.5.2 '@smithy/types': 4.19.0 tslib: 2.8.1 - '@aws-sdk/credential-provider-login@3.972.81': + '@aws-sdk/credential-provider-login@3.972.82': dependencies: - '@aws-sdk/core': 3.978.3 - '@aws-sdk/nested-clients': 3.997.48 + '@aws-sdk/core': 3.978.4 + '@aws-sdk/nested-clients': 3.997.49 '@aws-sdk/types': 3.974.6 - '@smithy/core': 3.35.2 + '@smithy/core': 3.36.1 '@smithy/types': 4.19.0 tslib: 2.8.1 - '@aws-sdk/credential-provider-node@3.972.86': + '@aws-sdk/credential-provider-node@3.972.87': dependencies: - '@aws-sdk/credential-provider-env': 3.972.74 - '@aws-sdk/credential-provider-http': 3.972.76 - '@aws-sdk/credential-provider-ini': 3.973.19 - '@aws-sdk/credential-provider-process': 3.972.74 - '@aws-sdk/credential-provider-sso': 3.973.18 - '@aws-sdk/credential-provider-web-identity': 3.972.80 + '@aws-sdk/credential-provider-env': 3.972.75 + '@aws-sdk/credential-provider-http': 3.972.77 + '@aws-sdk/credential-provider-ini': 3.973.20 + '@aws-sdk/credential-provider-process': 3.972.75 + '@aws-sdk/credential-provider-sso': 3.973.19 + '@aws-sdk/credential-provider-web-identity': 3.972.81 '@aws-sdk/types': 3.974.6 - '@smithy/core': 3.35.2 + '@smithy/core': 3.36.1 '@smithy/credential-provider-imds': 4.5.2 '@smithy/types': 4.19.0 tslib: 2.8.1 - '@aws-sdk/credential-provider-process@3.972.74': + '@aws-sdk/credential-provider-process@3.972.75': dependencies: - '@aws-sdk/core': 3.978.3 + '@aws-sdk/core': 3.978.4 '@aws-sdk/types': 3.974.6 - '@smithy/core': 3.35.2 + '@smithy/core': 3.36.1 '@smithy/types': 4.19.0 tslib: 2.8.1 - '@aws-sdk/credential-provider-sso@3.973.18': + '@aws-sdk/credential-provider-sso@3.973.19': dependencies: - '@aws-sdk/core': 3.978.3 - '@aws-sdk/nested-clients': 3.997.48 - '@aws-sdk/token-providers': 3.1150.0 + '@aws-sdk/core': 3.978.4 + '@aws-sdk/nested-clients': 3.997.49 + '@aws-sdk/token-providers': 3.1153.0 '@aws-sdk/types': 3.974.6 - '@smithy/core': 3.35.2 + '@smithy/core': 3.36.1 '@smithy/types': 4.19.0 tslib: 2.8.1 - '@aws-sdk/credential-provider-web-identity@3.972.80': + '@aws-sdk/credential-provider-web-identity@3.972.81': dependencies: - '@aws-sdk/core': 3.978.3 - '@aws-sdk/nested-clients': 3.997.48 + '@aws-sdk/core': 3.978.4 + '@aws-sdk/nested-clients': 3.997.49 '@aws-sdk/types': 3.974.6 - '@smithy/core': 3.35.2 + '@smithy/core': 3.36.1 '@smithy/types': 4.19.0 tslib: 2.8.1 - '@aws-sdk/lib-storage@3.1150.0(@aws-sdk/client-s3@3.1150.0)': + '@aws-sdk/lib-storage@3.1152.0(@aws-sdk/client-s3@3.1152.0)': dependencies: - '@aws-sdk/client-s3': 3.1150.0 + '@aws-sdk/client-s3': 3.1152.0 '@smithy/core': 3.35.2 '@smithy/types': 4.19.0 buffer: 5.6.0 @@ -7355,21 +7436,21 @@ snapshots: stream-browserify: 3.0.0 tslib: 2.8.1 - '@aws-sdk/middleware-sdk-s3@3.972.79': + '@aws-sdk/middleware-sdk-s3@3.972.80': dependencies: - '@aws-sdk/core': 3.978.3 + '@aws-sdk/core': 3.978.4 '@aws-sdk/signature-v4-multi-region': 3.996.48 '@aws-sdk/types': 3.974.6 - '@smithy/core': 3.35.2 + '@smithy/core': 3.36.1 '@smithy/types': 4.19.0 tslib: 2.8.1 - '@aws-sdk/nested-clients@3.997.48': + '@aws-sdk/nested-clients@3.997.49': dependencies: - '@aws-sdk/core': 3.978.3 + '@aws-sdk/core': 3.978.4 '@aws-sdk/signature-v4-multi-region': 3.996.48 '@aws-sdk/types': 3.974.6 - '@smithy/core': 3.35.2 + '@smithy/core': 3.36.1 '@smithy/fetch-http-handler': 5.8.0 '@smithy/node-http-handler': 4.12.1 '@smithy/types': 4.19.0 @@ -7382,12 +7463,12 @@ snapshots: '@smithy/types': 4.19.0 tslib: 2.8.1 - '@aws-sdk/token-providers@3.1150.0': + '@aws-sdk/token-providers@3.1153.0': dependencies: - '@aws-sdk/core': 3.978.3 - '@aws-sdk/nested-clients': 3.997.48 + '@aws-sdk/core': 3.978.4 + '@aws-sdk/nested-clients': 3.997.49 '@aws-sdk/types': 3.974.6 - '@smithy/core': 3.35.2 + '@smithy/core': 3.36.1 '@smithy/types': 4.19.0 tslib: 2.8.1 @@ -7403,10 +7484,10 @@ snapshots: '@aws/lambda-invoke-store@0.3.0': {} - '@axe-core/playwright@4.13.0(playwright-core@1.64.0)': + '@axe-core/playwright@4.14.0(playwright-core@1.64.1)': dependencies: - axe-core: 4.13.0 - playwright-core: 1.64.0 + axe-core: 4.14.0 + playwright-core: 1.64.1 '@babel/code-frame@7.29.7': dependencies: @@ -7831,29 +7912,29 @@ snapshots: '@discoveryjs/json-ext@0.5.7': {} - '@dnd-kit/accessibility@3.1.1(react@19.3.0)': + '@dnd-kit/accessibility@3.1.1(react@19.3.1)': dependencies: - react: 19.3.0 + react: 19.3.1 tslib: 2.8.1 - '@dnd-kit/core@6.3.1(react-dom@19.3.0(react@19.3.0))(react@19.3.0)': + '@dnd-kit/core@6.3.1(react-dom@19.3.1(react@19.3.1))(react@19.3.1)': dependencies: - '@dnd-kit/accessibility': 3.1.1(react@19.3.0) - '@dnd-kit/utilities': 3.2.2(react@19.3.0) - react: 19.3.0 - react-dom: 19.3.0(react@19.3.0) + '@dnd-kit/accessibility': 3.1.1(react@19.3.1) + '@dnd-kit/utilities': 3.2.2(react@19.3.1) + react: 19.3.1 + react-dom: 19.3.1(react@19.3.1) tslib: 2.8.1 - '@dnd-kit/sortable@10.0.0(@dnd-kit/core@6.3.1(react-dom@19.3.0(react@19.3.0))(react@19.3.0))(react@19.3.0)': + '@dnd-kit/sortable@10.0.0(@dnd-kit/core@6.3.1(react-dom@19.3.1(react@19.3.1))(react@19.3.1))(react@19.3.1)': dependencies: - '@dnd-kit/core': 6.3.1(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - '@dnd-kit/utilities': 3.2.2(react@19.3.0) - react: 19.3.0 + '@dnd-kit/core': 6.3.1(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + '@dnd-kit/utilities': 3.2.2(react@19.3.1) + react: 19.3.1 tslib: 2.8.1 - '@dnd-kit/utilities@3.2.2(react@19.3.0)': + '@dnd-kit/utilities@3.2.2(react@19.3.1)': dependencies: - react: 19.3.0 + react: 19.3.1 tslib: 2.8.1 '@dotenvx/dotenvx@1.75.1': @@ -7995,11 +8076,11 @@ snapshots: '@floating-ui/core': 1.8.0 '@floating-ui/utils': 0.2.12 - '@floating-ui/react-dom@2.1.9(react-dom@19.3.0(react@19.3.0))(react@19.3.0)': + '@floating-ui/react-dom@2.1.9(react-dom@19.3.1(react@19.3.1))(react@19.3.1)': dependencies: '@floating-ui/dom': 1.8.0 - react: 19.3.0 - react-dom: 19.3.0(react@19.3.0) + react: 19.3.1 + react-dom: 19.3.1(react@19.3.1) '@floating-ui/utils@0.2.12': {} @@ -8313,30 +8394,6 @@ snapshots: '@levischuck/tiny-cbor@0.2.11': {} - '@modelcontextprotocol/sdk@1.32.1(@cfworker/json-schema@4.1.1)(supports-color@7.2.0)(zod@3.25.76)': - dependencies: - '@hono/node-server': 1.19.17(hono@4.13.13) - ajv: 8.20.0 - ajv-formats: 3.0.1(ajv@8.20.0) - content-type: 1.0.5 - cors: 2.8.6 - cross-spawn: 7.0.6 - eventsource: 3.0.7 - eventsource-parser: 3.1.1 - express: 5.2.1(supports-color@7.2.0) - express-rate-limit: 8.6.2(express@5.2.1(supports-color@7.2.0))(supports-color@7.2.0) - hono: 4.13.13 - jose: 6.2.12 - json-schema-typed: 8.0.2 - pkce-challenge: 5.0.1 - raw-body: 3.0.2 - zod: 3.25.76 - zod-to-json-schema: 3.25.2(zod@3.25.76) - optionalDependencies: - '@cfworker/json-schema': 4.1.1 - transitivePeerDependencies: - - supports-color - '@modelcontextprotocol/sdk@1.32.1(@cfworker/json-schema@4.1.1)(supports-color@7.2.0)(zod@4.6.7)': dependencies: '@hono/node-server': 1.19.17(hono@4.13.13) @@ -8860,31 +8917,31 @@ snapshots: '@pkgjs/parseargs@0.11.0': optional: true - '@playwright/test@1.64.0': + '@playwright/test@1.64.1': dependencies: - playwright: 1.64.0 + playwright: 1.64.1 '@polka/url@1.0.0-next.29': {} - '@prisma/adapter-pg@7.10.0': + '@prisma/adapter-pg@7.10.1': dependencies: - '@prisma/driver-adapter-utils': 7.10.0 + '@prisma/driver-adapter-utils': 7.10.1 '@types/pg': 8.23.1 pg: 8.23.1 postgres-array: 3.0.4 transitivePeerDependencies: - pg-native - '@prisma/client-runtime-utils@7.10.0': {} + '@prisma/client-runtime-utils@7.10.1': {} - '@prisma/client@7.10.0(prisma@7.10.0(@types/node@26.6.5)(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(magicast@0.5.5)(react-dom@19.3.0(react@19.3.0))(react@19.3.0)(typescript@6.0.3))(typescript@6.0.3)': + '@prisma/client@7.10.1(prisma@7.10.1(@types/node@26.6.5)(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(magicast@0.5.5)(react-dom@19.3.1(react@19.3.1))(react@19.3.1)(typescript@6.0.3))(typescript@6.0.3)': dependencies: - '@prisma/client-runtime-utils': 7.10.0 + '@prisma/client-runtime-utils': 7.10.1 optionalDependencies: - prisma: 7.10.0(@types/node@26.6.5)(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(magicast@0.5.5)(react-dom@19.3.0(react@19.3.0))(react@19.3.0)(typescript@6.0.3) + prisma: 7.10.1(@types/node@26.6.5)(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(magicast@0.5.5)(react-dom@19.3.1(react@19.3.1))(react@19.3.1)(typescript@6.0.3) typescript: 6.0.3 - '@prisma/config@7.10.0(magicast@0.5.5)': + '@prisma/config@7.10.1(magicast@0.5.5)': dependencies: c12: 3.3.4(magicast@0.5.5) deepmerge-ts: 8.0.2 @@ -8893,7 +8950,7 @@ snapshots: transitivePeerDependencies: - magicast - '@prisma/debug@7.10.0': {} + '@prisma/debug@7.10.1': {} '@prisma/debug@7.2.0': {} @@ -8917,28 +8974,28 @@ snapshots: transitivePeerDependencies: - typescript - '@prisma/driver-adapter-utils@7.10.0': + '@prisma/driver-adapter-utils@7.10.1': dependencies: - '@prisma/debug': 7.10.0 + '@prisma/debug': 7.10.1 - '@prisma/engines-version@7.10.0-4.0edf323efd1d98336f3f0a68684b56f689b900d3': {} + '@prisma/engines-version@8.1.0-1.d1b3f377ca835d73fd50954a5e22128b9ebdd6b8': {} - '@prisma/engines@7.10.0': + '@prisma/engines@7.10.1': dependencies: - '@prisma/debug': 7.10.0 - '@prisma/engines-version': 7.10.0-4.0edf323efd1d98336f3f0a68684b56f689b900d3 - '@prisma/fetch-engine': 7.10.0 - '@prisma/get-platform': 7.10.0 + '@prisma/debug': 7.10.1 + '@prisma/engines-version': 8.1.0-1.d1b3f377ca835d73fd50954a5e22128b9ebdd6b8 + '@prisma/fetch-engine': 7.10.1 + '@prisma/get-platform': 7.10.1 - '@prisma/fetch-engine@7.10.0': + '@prisma/fetch-engine@7.10.1': dependencies: - '@prisma/debug': 7.10.0 - '@prisma/engines-version': 7.10.0-4.0edf323efd1d98336f3f0a68684b56f689b900d3 - '@prisma/get-platform': 7.10.0 + '@prisma/debug': 7.10.1 + '@prisma/engines-version': 8.1.0-1.d1b3f377ca835d73fd50954a5e22128b9ebdd6b8 + '@prisma/get-platform': 7.10.1 - '@prisma/get-platform@7.10.0': + '@prisma/get-platform@7.10.1': dependencies: - '@prisma/debug': 7.10.0 + '@prisma/debug': 7.10.1 '@prisma/get-platform@7.2.0': dependencies: @@ -8953,22 +9010,22 @@ snapshots: env-paths: 3.0.0 proper-lockfile: 4.1.2 - '@prisma/studio-core@0.33.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0)': + '@prisma/studio-core@0.33.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1)': dependencies: - '@radix-ui/react-toggle': 1.1.10(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-toggle': 1.1.10(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) '@types/react': 19.3.0 '@visx/curve': 4.0.1-alpha.0 '@visx/event': 4.0.1-alpha.0 - '@visx/grid': 4.0.1-alpha.0(react@19.3.0) - '@visx/group': 4.0.1-alpha.0(react@19.3.0) - '@visx/responsive': 4.0.1-alpha.0(react@19.3.0) + '@visx/grid': 4.0.1-alpha.0(react@19.3.1) + '@visx/group': 4.0.1-alpha.0(react@19.3.1) + '@visx/responsive': 4.0.1-alpha.0(react@19.3.1) '@visx/scale': 4.0.1-alpha.0 - '@visx/shape': 4.0.1-alpha.0(react@19.3.0) + '@visx/shape': 4.0.1-alpha.0(react@19.3.1) d3-array: 3.2.4 d3-shape: 3.2.0 elkjs: 0.11.1 - react: 19.3.0 - react-dom: 19.3.0(react@19.3.0) + react: 19.3.1 + react-dom: 19.3.1(react@19.3.1) transitivePeerDependencies: - '@types/react-dom' @@ -8998,809 +9055,809 @@ snapshots: '@radix-ui/primitive@1.2.0': {} - '@radix-ui/react-accessible-icon@1.2.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0)': + '@radix-ui/react-accessible-icon@1.2.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1)': dependencies: - '@radix-ui/react-visually-hidden': 1.2.13(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - react: 19.3.0 - react-dom: 19.3.0(react@19.3.0) + '@radix-ui/react-visually-hidden': 1.2.13(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + react: 19.3.1 + react-dom: 19.3.1(react@19.3.1) optionalDependencies: '@types/react': 19.3.0 '@types/react-dom': 19.3.0(@types/react@19.3.0) - '@radix-ui/react-accordion@1.3.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0)': + '@radix-ui/react-accordion@1.3.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1)': dependencies: '@radix-ui/primitive': 1.2.0 - '@radix-ui/react-collapsible': 1.2.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - '@radix-ui/react-collection': 1.2.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.3.0)(react@19.3.0) - '@radix-ui/react-context': 1.3.0(@types/react@19.3.0)(react@19.3.0) - '@radix-ui/react-direction': 1.2.0(@types/react@19.3.0)(react@19.3.0) - '@radix-ui/react-id': 1.1.4(@types/react@19.3.0)(react@19.3.0) - '@radix-ui/react-primitive': 2.2.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - '@radix-ui/react-use-controllable-state': 1.3.0(@types/react@19.3.0)(react@19.3.0) - react: 19.3.0 - react-dom: 19.3.0(react@19.3.0) + '@radix-ui/react-collapsible': 1.2.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + '@radix-ui/react-collection': 1.2.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.3.0)(react@19.3.1) + '@radix-ui/react-context': 1.3.0(@types/react@19.3.0)(react@19.3.1) + '@radix-ui/react-direction': 1.2.0(@types/react@19.3.0)(react@19.3.1) + '@radix-ui/react-id': 1.1.4(@types/react@19.3.0)(react@19.3.1) + '@radix-ui/react-primitive': 2.2.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + '@radix-ui/react-use-controllable-state': 1.3.0(@types/react@19.3.0)(react@19.3.1) + react: 19.3.1 + react-dom: 19.3.1(react@19.3.1) optionalDependencies: '@types/react': 19.3.0 '@types/react-dom': 19.3.0(@types/react@19.3.0) - '@radix-ui/react-alert-dialog@1.2.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0)': + '@radix-ui/react-alert-dialog@1.2.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1)': dependencies: '@radix-ui/primitive': 1.2.0 - '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.3.0)(react@19.3.0) - '@radix-ui/react-context': 1.3.0(@types/react@19.3.0)(react@19.3.0) - '@radix-ui/react-dialog': 1.3.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - '@radix-ui/react-primitive': 2.2.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - react: 19.3.0 - react-dom: 19.3.0(react@19.3.0) + '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.3.0)(react@19.3.1) + '@radix-ui/react-context': 1.3.0(@types/react@19.3.0)(react@19.3.1) + '@radix-ui/react-dialog': 1.3.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + '@radix-ui/react-primitive': 2.2.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + react: 19.3.1 + react-dom: 19.3.1(react@19.3.1) optionalDependencies: '@types/react': 19.3.0 '@types/react-dom': 19.3.0(@types/react@19.3.0) - '@radix-ui/react-arrow@1.1.17(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0)': + '@radix-ui/react-arrow@1.1.17(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1)': dependencies: - '@radix-ui/react-primitive': 2.2.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - react: 19.3.0 - react-dom: 19.3.0(react@19.3.0) + '@radix-ui/react-primitive': 2.2.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + react: 19.3.1 + react-dom: 19.3.1(react@19.3.1) optionalDependencies: '@types/react': 19.3.0 '@types/react-dom': 19.3.0(@types/react@19.3.0) - '@radix-ui/react-aspect-ratio@1.2.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0)': + '@radix-ui/react-aspect-ratio@1.2.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1)': dependencies: - '@radix-ui/react-primitive': 2.2.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - react: 19.3.0 - react-dom: 19.3.0(react@19.3.0) + '@radix-ui/react-primitive': 2.2.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + react: 19.3.1 + react-dom: 19.3.1(react@19.3.1) optionalDependencies: '@types/react': 19.3.0 '@types/react-dom': 19.3.0(@types/react@19.3.0) - '@radix-ui/react-avatar@1.3.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0)': + '@radix-ui/react-avatar@1.3.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1)': dependencies: '@radix-ui/primitive': 1.2.0 - '@radix-ui/react-context': 1.3.0(@types/react@19.3.0)(react@19.3.0) - '@radix-ui/react-primitive': 2.2.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - '@radix-ui/react-use-callback-ref': 1.1.5(@types/react@19.3.0)(react@19.3.0) - '@radix-ui/react-use-is-hydrated': 0.1.3(@types/react@19.3.0)(react@19.3.0) - '@radix-ui/react-use-layout-effect': 1.1.4(@types/react@19.3.0)(react@19.3.0) - react: 19.3.0 - react-dom: 19.3.0(react@19.3.0) + '@radix-ui/react-context': 1.3.0(@types/react@19.3.0)(react@19.3.1) + '@radix-ui/react-primitive': 2.2.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + '@radix-ui/react-use-callback-ref': 1.1.5(@types/react@19.3.0)(react@19.3.1) + '@radix-ui/react-use-is-hydrated': 0.1.3(@types/react@19.3.0)(react@19.3.1) + '@radix-ui/react-use-layout-effect': 1.1.4(@types/react@19.3.0)(react@19.3.1) + react: 19.3.1 + react-dom: 19.3.1(react@19.3.1) optionalDependencies: '@types/react': 19.3.0 '@types/react-dom': 19.3.0(@types/react@19.3.0) - '@radix-ui/react-checkbox@1.4.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0)': + '@radix-ui/react-checkbox@1.4.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1)': dependencies: '@radix-ui/primitive': 1.2.0 - '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.3.0)(react@19.3.0) - '@radix-ui/react-context': 1.3.0(@types/react@19.3.0)(react@19.3.0) - '@radix-ui/react-presence': 1.1.11(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - '@radix-ui/react-primitive': 2.2.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - '@radix-ui/react-use-controllable-state': 1.3.0(@types/react@19.3.0)(react@19.3.0) - '@radix-ui/react-use-size': 1.1.5(@types/react@19.3.0)(react@19.3.0) - react: 19.3.0 - react-dom: 19.3.0(react@19.3.0) + '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.3.0)(react@19.3.1) + '@radix-ui/react-context': 1.3.0(@types/react@19.3.0)(react@19.3.1) + '@radix-ui/react-presence': 1.1.11(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + '@radix-ui/react-primitive': 2.2.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + '@radix-ui/react-use-controllable-state': 1.3.0(@types/react@19.3.0)(react@19.3.1) + '@radix-ui/react-use-size': 1.1.5(@types/react@19.3.0)(react@19.3.1) + react: 19.3.1 + react-dom: 19.3.1(react@19.3.1) optionalDependencies: '@types/react': 19.3.0 '@types/react-dom': 19.3.0(@types/react@19.3.0) - '@radix-ui/react-collapsible@1.2.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0)': + '@radix-ui/react-collapsible@1.2.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1)': dependencies: '@radix-ui/primitive': 1.2.0 - '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.3.0)(react@19.3.0) - '@radix-ui/react-context': 1.3.0(@types/react@19.3.0)(react@19.3.0) - '@radix-ui/react-id': 1.1.4(@types/react@19.3.0)(react@19.3.0) - '@radix-ui/react-presence': 1.1.11(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - '@radix-ui/react-primitive': 2.2.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - '@radix-ui/react-use-controllable-state': 1.3.0(@types/react@19.3.0)(react@19.3.0) - '@radix-ui/react-use-layout-effect': 1.1.4(@types/react@19.3.0)(react@19.3.0) - react: 19.3.0 - react-dom: 19.3.0(react@19.3.0) + '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.3.0)(react@19.3.1) + '@radix-ui/react-context': 1.3.0(@types/react@19.3.0)(react@19.3.1) + '@radix-ui/react-id': 1.1.4(@types/react@19.3.0)(react@19.3.1) + '@radix-ui/react-presence': 1.1.11(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + '@radix-ui/react-primitive': 2.2.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + '@radix-ui/react-use-controllable-state': 1.3.0(@types/react@19.3.0)(react@19.3.1) + '@radix-ui/react-use-layout-effect': 1.1.4(@types/react@19.3.0)(react@19.3.1) + react: 19.3.1 + react-dom: 19.3.1(react@19.3.1) optionalDependencies: '@types/react': 19.3.0 '@types/react-dom': 19.3.0(@types/react@19.3.0) - '@radix-ui/react-collection@1.2.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0)': + '@radix-ui/react-collection@1.2.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1)': dependencies: - '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.3.0)(react@19.3.0) - '@radix-ui/react-context': 1.3.0(@types/react@19.3.0)(react@19.3.0) - '@radix-ui/react-primitive': 2.2.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - '@radix-ui/react-slot': 1.5.0(@types/react@19.3.0)(react@19.3.0) - '@radix-ui/react-use-layout-effect': 1.1.4(@types/react@19.3.0)(react@19.3.0) - react: 19.3.0 - react-dom: 19.3.0(react@19.3.0) + '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.3.0)(react@19.3.1) + '@radix-ui/react-context': 1.3.0(@types/react@19.3.0)(react@19.3.1) + '@radix-ui/react-primitive': 2.2.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + '@radix-ui/react-slot': 1.5.0(@types/react@19.3.0)(react@19.3.1) + '@radix-ui/react-use-layout-effect': 1.1.4(@types/react@19.3.0)(react@19.3.1) + react: 19.3.1 + react-dom: 19.3.1(react@19.3.1) optionalDependencies: '@types/react': 19.3.0 '@types/react-dom': 19.3.0(@types/react@19.3.0) - '@radix-ui/react-compose-refs@1.1.2(@types/react@19.3.0)(react@19.3.0)': + '@radix-ui/react-compose-refs@1.1.2(@types/react@19.3.0)(react@19.3.1)': dependencies: - react: 19.3.0 + react: 19.3.1 optionalDependencies: '@types/react': 19.3.0 - '@radix-ui/react-compose-refs@1.1.5(@types/react@19.3.0)(react@19.3.0)': + '@radix-ui/react-compose-refs@1.1.5(@types/react@19.3.0)(react@19.3.1)': dependencies: - react: 19.3.0 + react: 19.3.1 optionalDependencies: '@types/react': 19.3.0 - '@radix-ui/react-context-menu@2.4.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0)': + '@radix-ui/react-context-menu@2.4.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1)': dependencies: '@radix-ui/primitive': 1.2.0 - '@radix-ui/react-context': 1.3.0(@types/react@19.3.0)(react@19.3.0) - '@radix-ui/react-menu': 2.2.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - '@radix-ui/react-primitive': 2.2.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - '@radix-ui/react-use-controllable-state': 1.3.0(@types/react@19.3.0)(react@19.3.0) - react: 19.3.0 - react-dom: 19.3.0(react@19.3.0) + '@radix-ui/react-context': 1.3.0(@types/react@19.3.0)(react@19.3.1) + '@radix-ui/react-menu': 2.2.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + '@radix-ui/react-primitive': 2.2.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + '@radix-ui/react-use-controllable-state': 1.3.0(@types/react@19.3.0)(react@19.3.1) + react: 19.3.1 + react-dom: 19.3.1(react@19.3.1) optionalDependencies: '@types/react': 19.3.0 '@types/react-dom': 19.3.0(@types/react@19.3.0) - '@radix-ui/react-context@1.3.0(@types/react@19.3.0)(react@19.3.0)': + '@radix-ui/react-context@1.3.0(@types/react@19.3.0)(react@19.3.1)': dependencies: - react: 19.3.0 + react: 19.3.1 optionalDependencies: '@types/react': 19.3.0 - '@radix-ui/react-dialog@1.3.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0)': + '@radix-ui/react-dialog@1.3.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1)': dependencies: '@radix-ui/primitive': 1.2.0 - '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.3.0)(react@19.3.0) - '@radix-ui/react-context': 1.3.0(@types/react@19.3.0)(react@19.3.0) - '@radix-ui/react-dismissable-layer': 1.2.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - '@radix-ui/react-focus-guards': 1.2.0(@types/react@19.3.0)(react@19.3.0) - '@radix-ui/react-focus-scope': 1.3.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - '@radix-ui/react-id': 1.1.4(@types/react@19.3.0)(react@19.3.0) - '@radix-ui/react-portal': 1.2.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - '@radix-ui/react-presence': 1.1.11(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - '@radix-ui/react-primitive': 2.2.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - '@radix-ui/react-slot': 1.5.0(@types/react@19.3.0)(react@19.3.0) - '@radix-ui/react-use-controllable-state': 1.3.0(@types/react@19.3.0)(react@19.3.0) - '@radix-ui/react-use-layout-effect': 1.1.4(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.3.0)(react@19.3.1) + '@radix-ui/react-context': 1.3.0(@types/react@19.3.0)(react@19.3.1) + '@radix-ui/react-dismissable-layer': 1.2.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + '@radix-ui/react-focus-guards': 1.2.0(@types/react@19.3.0)(react@19.3.1) + '@radix-ui/react-focus-scope': 1.3.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + '@radix-ui/react-id': 1.1.4(@types/react@19.3.0)(react@19.3.1) + '@radix-ui/react-portal': 1.2.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + '@radix-ui/react-presence': 1.1.11(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + '@radix-ui/react-primitive': 2.2.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + '@radix-ui/react-slot': 1.5.0(@types/react@19.3.0)(react@19.3.1) + '@radix-ui/react-use-controllable-state': 1.3.0(@types/react@19.3.0)(react@19.3.1) + '@radix-ui/react-use-layout-effect': 1.1.4(@types/react@19.3.0)(react@19.3.1) aria-hidden: 1.2.6 - react: 19.3.0 - react-dom: 19.3.0(react@19.3.0) - react-remove-scroll: 2.7.2(@types/react@19.3.0)(react@19.3.0) + react: 19.3.1 + react-dom: 19.3.1(react@19.3.1) + react-remove-scroll: 2.7.2(@types/react@19.3.0)(react@19.3.1) optionalDependencies: '@types/react': 19.3.0 '@types/react-dom': 19.3.0(@types/react@19.3.0) - '@radix-ui/react-direction@1.2.0(@types/react@19.3.0)(react@19.3.0)': + '@radix-ui/react-direction@1.2.0(@types/react@19.3.0)(react@19.3.1)': dependencies: - react: 19.3.0 + react: 19.3.1 optionalDependencies: '@types/react': 19.3.0 - '@radix-ui/react-dismissable-layer@1.2.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0)': + '@radix-ui/react-dismissable-layer@1.2.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1)': dependencies: '@radix-ui/primitive': 1.2.0 - '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.3.0)(react@19.3.0) - '@radix-ui/react-primitive': 2.2.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - '@radix-ui/react-use-callback-ref': 1.1.5(@types/react@19.3.0)(react@19.3.0) - '@radix-ui/react-use-effect-event': 0.0.5(@types/react@19.3.0)(react@19.3.0) - react: 19.3.0 - react-dom: 19.3.0(react@19.3.0) + '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.3.0)(react@19.3.1) + '@radix-ui/react-primitive': 2.2.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + '@radix-ui/react-use-callback-ref': 1.1.5(@types/react@19.3.0)(react@19.3.1) + '@radix-ui/react-use-effect-event': 0.0.5(@types/react@19.3.0)(react@19.3.1) + react: 19.3.1 + react-dom: 19.3.1(react@19.3.1) optionalDependencies: '@types/react': 19.3.0 '@types/react-dom': 19.3.0(@types/react@19.3.0) - '@radix-ui/react-dropdown-menu@2.2.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0)': + '@radix-ui/react-dropdown-menu@2.2.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1)': dependencies: '@radix-ui/primitive': 1.2.0 - '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.3.0)(react@19.3.0) - '@radix-ui/react-context': 1.3.0(@types/react@19.3.0)(react@19.3.0) - '@radix-ui/react-id': 1.1.4(@types/react@19.3.0)(react@19.3.0) - '@radix-ui/react-menu': 2.2.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - '@radix-ui/react-primitive': 2.2.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - '@radix-ui/react-use-controllable-state': 1.3.0(@types/react@19.3.0)(react@19.3.0) - react: 19.3.0 - react-dom: 19.3.0(react@19.3.0) + '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.3.0)(react@19.3.1) + '@radix-ui/react-context': 1.3.0(@types/react@19.3.0)(react@19.3.1) + '@radix-ui/react-id': 1.1.4(@types/react@19.3.0)(react@19.3.1) + '@radix-ui/react-menu': 2.2.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + '@radix-ui/react-primitive': 2.2.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + '@radix-ui/react-use-controllable-state': 1.3.0(@types/react@19.3.0)(react@19.3.1) + react: 19.3.1 + react-dom: 19.3.1(react@19.3.1) optionalDependencies: '@types/react': 19.3.0 '@types/react-dom': 19.3.0(@types/react@19.3.0) - '@radix-ui/react-focus-guards@1.2.0(@types/react@19.3.0)(react@19.3.0)': + '@radix-ui/react-focus-guards@1.2.0(@types/react@19.3.0)(react@19.3.1)': dependencies: - react: 19.3.0 + react: 19.3.1 optionalDependencies: '@types/react': 19.3.0 - '@radix-ui/react-focus-scope@1.3.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0)': + '@radix-ui/react-focus-scope@1.3.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1)': dependencies: - '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.3.0)(react@19.3.0) - '@radix-ui/react-primitive': 2.2.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - '@radix-ui/react-use-callback-ref': 1.1.5(@types/react@19.3.0)(react@19.3.0) - react: 19.3.0 - react-dom: 19.3.0(react@19.3.0) + '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.3.0)(react@19.3.1) + '@radix-ui/react-primitive': 2.2.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + '@radix-ui/react-use-callback-ref': 1.1.5(@types/react@19.3.0)(react@19.3.1) + react: 19.3.1 + react-dom: 19.3.1(react@19.3.1) optionalDependencies: '@types/react': 19.3.0 '@types/react-dom': 19.3.0(@types/react@19.3.0) - '@radix-ui/react-form@0.3.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0)': + '@radix-ui/react-form@0.3.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1)': dependencies: '@radix-ui/primitive': 1.2.0 - '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.3.0)(react@19.3.0) - '@radix-ui/react-context': 1.3.0(@types/react@19.3.0)(react@19.3.0) - '@radix-ui/react-id': 1.1.4(@types/react@19.3.0)(react@19.3.0) - '@radix-ui/react-label': 2.1.17(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - '@radix-ui/react-primitive': 2.2.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - react: 19.3.0 - react-dom: 19.3.0(react@19.3.0) + '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.3.0)(react@19.3.1) + '@radix-ui/react-context': 1.3.0(@types/react@19.3.0)(react@19.3.1) + '@radix-ui/react-id': 1.1.4(@types/react@19.3.0)(react@19.3.1) + '@radix-ui/react-label': 2.1.17(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + '@radix-ui/react-primitive': 2.2.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + react: 19.3.1 + react-dom: 19.3.1(react@19.3.1) optionalDependencies: '@types/react': 19.3.0 '@types/react-dom': 19.3.0(@types/react@19.3.0) - '@radix-ui/react-hover-card@1.2.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0)': + '@radix-ui/react-hover-card@1.2.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1)': dependencies: '@radix-ui/primitive': 1.2.0 - '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.3.0)(react@19.3.0) - '@radix-ui/react-context': 1.3.0(@types/react@19.3.0)(react@19.3.0) - '@radix-ui/react-dismissable-layer': 1.2.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - '@radix-ui/react-focus-scope': 1.3.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - '@radix-ui/react-popper': 1.4.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - '@radix-ui/react-portal': 1.2.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - '@radix-ui/react-presence': 1.1.11(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - '@radix-ui/react-primitive': 2.2.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - '@radix-ui/react-use-controllable-state': 1.3.0(@types/react@19.3.0)(react@19.3.0) - react: 19.3.0 - react-dom: 19.3.0(react@19.3.0) + '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.3.0)(react@19.3.1) + '@radix-ui/react-context': 1.3.0(@types/react@19.3.0)(react@19.3.1) + '@radix-ui/react-dismissable-layer': 1.2.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + '@radix-ui/react-focus-scope': 1.3.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + '@radix-ui/react-popper': 1.4.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + '@radix-ui/react-portal': 1.2.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + '@radix-ui/react-presence': 1.1.11(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + '@radix-ui/react-primitive': 2.2.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + '@radix-ui/react-use-controllable-state': 1.3.0(@types/react@19.3.0)(react@19.3.1) + react: 19.3.1 + react-dom: 19.3.1(react@19.3.1) optionalDependencies: '@types/react': 19.3.0 '@types/react-dom': 19.3.0(@types/react@19.3.0) - '@radix-ui/react-id@1.1.4(@types/react@19.3.0)(react@19.3.0)': + '@radix-ui/react-id@1.1.4(@types/react@19.3.0)(react@19.3.1)': dependencies: - '@radix-ui/react-use-layout-effect': 1.1.4(@types/react@19.3.0)(react@19.3.0) - react: 19.3.0 + '@radix-ui/react-use-layout-effect': 1.1.4(@types/react@19.3.0)(react@19.3.1) + react: 19.3.1 optionalDependencies: '@types/react': 19.3.0 - '@radix-ui/react-label@2.1.17(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0)': + '@radix-ui/react-label@2.1.17(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1)': dependencies: - '@radix-ui/react-primitive': 2.2.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - react: 19.3.0 - react-dom: 19.3.0(react@19.3.0) + '@radix-ui/react-primitive': 2.2.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + react: 19.3.1 + react-dom: 19.3.1(react@19.3.1) optionalDependencies: '@types/react': 19.3.0 '@types/react-dom': 19.3.0(@types/react@19.3.0) - '@radix-ui/react-menu@2.2.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0)': + '@radix-ui/react-menu@2.2.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1)': dependencies: '@radix-ui/primitive': 1.2.0 - '@radix-ui/react-collection': 1.2.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.3.0)(react@19.3.0) - '@radix-ui/react-context': 1.3.0(@types/react@19.3.0)(react@19.3.0) - '@radix-ui/react-direction': 1.2.0(@types/react@19.3.0)(react@19.3.0) - '@radix-ui/react-dismissable-layer': 1.2.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - '@radix-ui/react-focus-guards': 1.2.0(@types/react@19.3.0)(react@19.3.0) - '@radix-ui/react-focus-scope': 1.3.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - '@radix-ui/react-id': 1.1.4(@types/react@19.3.0)(react@19.3.0) - '@radix-ui/react-popper': 1.4.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - '@radix-ui/react-portal': 1.2.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - '@radix-ui/react-presence': 1.1.11(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - '@radix-ui/react-primitive': 2.2.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - '@radix-ui/react-roving-focus': 1.2.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - '@radix-ui/react-slot': 1.5.0(@types/react@19.3.0)(react@19.3.0) - '@radix-ui/react-use-callback-ref': 1.1.5(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-collection': 1.2.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.3.0)(react@19.3.1) + '@radix-ui/react-context': 1.3.0(@types/react@19.3.0)(react@19.3.1) + '@radix-ui/react-direction': 1.2.0(@types/react@19.3.0)(react@19.3.1) + '@radix-ui/react-dismissable-layer': 1.2.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + '@radix-ui/react-focus-guards': 1.2.0(@types/react@19.3.0)(react@19.3.1) + '@radix-ui/react-focus-scope': 1.3.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + '@radix-ui/react-id': 1.1.4(@types/react@19.3.0)(react@19.3.1) + '@radix-ui/react-popper': 1.4.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + '@radix-ui/react-portal': 1.2.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + '@radix-ui/react-presence': 1.1.11(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + '@radix-ui/react-primitive': 2.2.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + '@radix-ui/react-roving-focus': 1.2.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + '@radix-ui/react-slot': 1.5.0(@types/react@19.3.0)(react@19.3.1) + '@radix-ui/react-use-callback-ref': 1.1.5(@types/react@19.3.0)(react@19.3.1) aria-hidden: 1.2.6 - react: 19.3.0 - react-dom: 19.3.0(react@19.3.0) - react-remove-scroll: 2.7.2(@types/react@19.3.0)(react@19.3.0) + react: 19.3.1 + react-dom: 19.3.1(react@19.3.1) + react-remove-scroll: 2.7.2(@types/react@19.3.0)(react@19.3.1) optionalDependencies: '@types/react': 19.3.0 '@types/react-dom': 19.3.0(@types/react@19.3.0) - '@radix-ui/react-menubar@1.2.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0)': + '@radix-ui/react-menubar@1.2.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1)': dependencies: '@radix-ui/primitive': 1.2.0 - '@radix-ui/react-collection': 1.2.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.3.0)(react@19.3.0) - '@radix-ui/react-context': 1.3.0(@types/react@19.3.0)(react@19.3.0) - '@radix-ui/react-direction': 1.2.0(@types/react@19.3.0)(react@19.3.0) - '@radix-ui/react-id': 1.1.4(@types/react@19.3.0)(react@19.3.0) - '@radix-ui/react-menu': 2.2.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - '@radix-ui/react-primitive': 2.2.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - '@radix-ui/react-roving-focus': 1.2.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - '@radix-ui/react-use-controllable-state': 1.3.0(@types/react@19.3.0)(react@19.3.0) - react: 19.3.0 - react-dom: 19.3.0(react@19.3.0) + '@radix-ui/react-collection': 1.2.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.3.0)(react@19.3.1) + '@radix-ui/react-context': 1.3.0(@types/react@19.3.0)(react@19.3.1) + '@radix-ui/react-direction': 1.2.0(@types/react@19.3.0)(react@19.3.1) + '@radix-ui/react-id': 1.1.4(@types/react@19.3.0)(react@19.3.1) + '@radix-ui/react-menu': 2.2.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + '@radix-ui/react-primitive': 2.2.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + '@radix-ui/react-roving-focus': 1.2.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + '@radix-ui/react-use-controllable-state': 1.3.0(@types/react@19.3.0)(react@19.3.1) + react: 19.3.1 + react-dom: 19.3.1(react@19.3.1) optionalDependencies: '@types/react': 19.3.0 '@types/react-dom': 19.3.0(@types/react@19.3.0) - '@radix-ui/react-navigation-menu@1.4.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0)': + '@radix-ui/react-navigation-menu@1.4.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1)': dependencies: '@radix-ui/primitive': 1.2.0 - '@radix-ui/react-collection': 1.2.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.3.0)(react@19.3.0) - '@radix-ui/react-context': 1.3.0(@types/react@19.3.0)(react@19.3.0) - '@radix-ui/react-direction': 1.2.0(@types/react@19.3.0)(react@19.3.0) - '@radix-ui/react-dismissable-layer': 1.2.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - '@radix-ui/react-id': 1.1.4(@types/react@19.3.0)(react@19.3.0) - '@radix-ui/react-presence': 1.1.11(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - '@radix-ui/react-primitive': 2.2.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - '@radix-ui/react-slot': 1.5.0(@types/react@19.3.0)(react@19.3.0) - '@radix-ui/react-use-callback-ref': 1.1.5(@types/react@19.3.0)(react@19.3.0) - '@radix-ui/react-use-controllable-state': 1.3.0(@types/react@19.3.0)(react@19.3.0) - '@radix-ui/react-use-layout-effect': 1.1.4(@types/react@19.3.0)(react@19.3.0) - '@radix-ui/react-use-previous': 1.1.4(@types/react@19.3.0)(react@19.3.0) - '@radix-ui/react-visually-hidden': 1.2.13(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - react: 19.3.0 - react-dom: 19.3.0(react@19.3.0) + '@radix-ui/react-collection': 1.2.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.3.0)(react@19.3.1) + '@radix-ui/react-context': 1.3.0(@types/react@19.3.0)(react@19.3.1) + '@radix-ui/react-direction': 1.2.0(@types/react@19.3.0)(react@19.3.1) + '@radix-ui/react-dismissable-layer': 1.2.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + '@radix-ui/react-id': 1.1.4(@types/react@19.3.0)(react@19.3.1) + '@radix-ui/react-presence': 1.1.11(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + '@radix-ui/react-primitive': 2.2.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + '@radix-ui/react-slot': 1.5.0(@types/react@19.3.0)(react@19.3.1) + '@radix-ui/react-use-callback-ref': 1.1.5(@types/react@19.3.0)(react@19.3.1) + '@radix-ui/react-use-controllable-state': 1.3.0(@types/react@19.3.0)(react@19.3.1) + '@radix-ui/react-use-layout-effect': 1.1.4(@types/react@19.3.0)(react@19.3.1) + '@radix-ui/react-use-previous': 1.1.4(@types/react@19.3.0)(react@19.3.1) + '@radix-ui/react-visually-hidden': 1.2.13(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + react: 19.3.1 + react-dom: 19.3.1(react@19.3.1) optionalDependencies: '@types/react': 19.3.0 '@types/react-dom': 19.3.0(@types/react@19.3.0) - '@radix-ui/react-one-time-password-field@0.2.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0)': + '@radix-ui/react-one-time-password-field@0.2.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1)': dependencies: '@radix-ui/number': 1.1.3 '@radix-ui/primitive': 1.2.0 - '@radix-ui/react-collection': 1.2.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.3.0)(react@19.3.0) - '@radix-ui/react-context': 1.3.0(@types/react@19.3.0)(react@19.3.0) - '@radix-ui/react-direction': 1.2.0(@types/react@19.3.0)(react@19.3.0) - '@radix-ui/react-primitive': 2.2.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - '@radix-ui/react-roving-focus': 1.2.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - '@radix-ui/react-use-controllable-state': 1.3.0(@types/react@19.3.0)(react@19.3.0) - '@radix-ui/react-use-effect-event': 0.0.5(@types/react@19.3.0)(react@19.3.0) - '@radix-ui/react-use-is-hydrated': 0.1.3(@types/react@19.3.0)(react@19.3.0) - '@radix-ui/react-use-layout-effect': 1.1.4(@types/react@19.3.0)(react@19.3.0) - react: 19.3.0 - react-dom: 19.3.0(react@19.3.0) + '@radix-ui/react-collection': 1.2.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.3.0)(react@19.3.1) + '@radix-ui/react-context': 1.3.0(@types/react@19.3.0)(react@19.3.1) + '@radix-ui/react-direction': 1.2.0(@types/react@19.3.0)(react@19.3.1) + '@radix-ui/react-primitive': 2.2.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + '@radix-ui/react-roving-focus': 1.2.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + '@radix-ui/react-use-controllable-state': 1.3.0(@types/react@19.3.0)(react@19.3.1) + '@radix-ui/react-use-effect-event': 0.0.5(@types/react@19.3.0)(react@19.3.1) + '@radix-ui/react-use-is-hydrated': 0.1.3(@types/react@19.3.0)(react@19.3.1) + '@radix-ui/react-use-layout-effect': 1.1.4(@types/react@19.3.0)(react@19.3.1) + react: 19.3.1 + react-dom: 19.3.1(react@19.3.1) optionalDependencies: '@types/react': 19.3.0 '@types/react-dom': 19.3.0(@types/react@19.3.0) - '@radix-ui/react-password-toggle-field@0.2.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0)': + '@radix-ui/react-password-toggle-field@0.2.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1)': dependencies: '@radix-ui/primitive': 1.2.0 - '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.3.0)(react@19.3.0) - '@radix-ui/react-context': 1.3.0(@types/react@19.3.0)(react@19.3.0) - '@radix-ui/react-id': 1.1.4(@types/react@19.3.0)(react@19.3.0) - '@radix-ui/react-primitive': 2.2.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - '@radix-ui/react-use-controllable-state': 1.3.0(@types/react@19.3.0)(react@19.3.0) - '@radix-ui/react-use-effect-event': 0.0.5(@types/react@19.3.0)(react@19.3.0) - '@radix-ui/react-use-is-hydrated': 0.1.3(@types/react@19.3.0)(react@19.3.0) - react: 19.3.0 - react-dom: 19.3.0(react@19.3.0) + '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.3.0)(react@19.3.1) + '@radix-ui/react-context': 1.3.0(@types/react@19.3.0)(react@19.3.1) + '@radix-ui/react-id': 1.1.4(@types/react@19.3.0)(react@19.3.1) + '@radix-ui/react-primitive': 2.2.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + '@radix-ui/react-use-controllable-state': 1.3.0(@types/react@19.3.0)(react@19.3.1) + '@radix-ui/react-use-effect-event': 0.0.5(@types/react@19.3.0)(react@19.3.1) + '@radix-ui/react-use-is-hydrated': 0.1.3(@types/react@19.3.0)(react@19.3.1) + react: 19.3.1 + react-dom: 19.3.1(react@19.3.1) optionalDependencies: '@types/react': 19.3.0 '@types/react-dom': 19.3.0(@types/react@19.3.0) - '@radix-ui/react-popover@1.3.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0)': + '@radix-ui/react-popover@1.3.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1)': dependencies: '@radix-ui/primitive': 1.2.0 - '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.3.0)(react@19.3.0) - '@radix-ui/react-context': 1.3.0(@types/react@19.3.0)(react@19.3.0) - '@radix-ui/react-dismissable-layer': 1.2.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - '@radix-ui/react-focus-guards': 1.2.0(@types/react@19.3.0)(react@19.3.0) - '@radix-ui/react-focus-scope': 1.3.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - '@radix-ui/react-id': 1.1.4(@types/react@19.3.0)(react@19.3.0) - '@radix-ui/react-popper': 1.4.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - '@radix-ui/react-portal': 1.2.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - '@radix-ui/react-presence': 1.1.11(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - '@radix-ui/react-primitive': 2.2.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - '@radix-ui/react-slot': 1.5.0(@types/react@19.3.0)(react@19.3.0) - '@radix-ui/react-use-controllable-state': 1.3.0(@types/react@19.3.0)(react@19.3.0) - '@radix-ui/react-use-layout-effect': 1.1.4(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.3.0)(react@19.3.1) + '@radix-ui/react-context': 1.3.0(@types/react@19.3.0)(react@19.3.1) + '@radix-ui/react-dismissable-layer': 1.2.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + '@radix-ui/react-focus-guards': 1.2.0(@types/react@19.3.0)(react@19.3.1) + '@radix-ui/react-focus-scope': 1.3.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + '@radix-ui/react-id': 1.1.4(@types/react@19.3.0)(react@19.3.1) + '@radix-ui/react-popper': 1.4.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + '@radix-ui/react-portal': 1.2.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + '@radix-ui/react-presence': 1.1.11(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + '@radix-ui/react-primitive': 2.2.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + '@radix-ui/react-slot': 1.5.0(@types/react@19.3.0)(react@19.3.1) + '@radix-ui/react-use-controllable-state': 1.3.0(@types/react@19.3.0)(react@19.3.1) + '@radix-ui/react-use-layout-effect': 1.1.4(@types/react@19.3.0)(react@19.3.1) aria-hidden: 1.2.6 - react: 19.3.0 - react-dom: 19.3.0(react@19.3.0) - react-remove-scroll: 2.7.2(@types/react@19.3.0)(react@19.3.0) + react: 19.3.1 + react-dom: 19.3.1(react@19.3.1) + react-remove-scroll: 2.7.2(@types/react@19.3.0)(react@19.3.1) optionalDependencies: '@types/react': 19.3.0 '@types/react-dom': 19.3.0(@types/react@19.3.0) - '@radix-ui/react-popper@1.4.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0)': + '@radix-ui/react-popper@1.4.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1)': dependencies: - '@floating-ui/react-dom': 2.1.9(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - '@radix-ui/react-arrow': 1.1.17(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.3.0)(react@19.3.0) - '@radix-ui/react-context': 1.3.0(@types/react@19.3.0)(react@19.3.0) - '@radix-ui/react-primitive': 2.2.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - '@radix-ui/react-use-callback-ref': 1.1.5(@types/react@19.3.0)(react@19.3.0) - '@radix-ui/react-use-layout-effect': 1.1.4(@types/react@19.3.0)(react@19.3.0) - '@radix-ui/react-use-rect': 1.1.4(@types/react@19.3.0)(react@19.3.0) - '@radix-ui/react-use-size': 1.1.5(@types/react@19.3.0)(react@19.3.0) + '@floating-ui/react-dom': 2.1.9(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + '@radix-ui/react-arrow': 1.1.17(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.3.0)(react@19.3.1) + '@radix-ui/react-context': 1.3.0(@types/react@19.3.0)(react@19.3.1) + '@radix-ui/react-primitive': 2.2.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + '@radix-ui/react-use-callback-ref': 1.1.5(@types/react@19.3.0)(react@19.3.1) + '@radix-ui/react-use-layout-effect': 1.1.4(@types/react@19.3.0)(react@19.3.1) + '@radix-ui/react-use-rect': 1.1.4(@types/react@19.3.0)(react@19.3.1) + '@radix-ui/react-use-size': 1.1.5(@types/react@19.3.0)(react@19.3.1) '@radix-ui/rect': 1.1.3 - react: 19.3.0 - react-dom: 19.3.0(react@19.3.0) + react: 19.3.1 + react-dom: 19.3.1(react@19.3.1) optionalDependencies: '@types/react': 19.3.0 '@types/react-dom': 19.3.0(@types/react@19.3.0) - '@radix-ui/react-portal@1.2.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0)': + '@radix-ui/react-portal@1.2.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1)': dependencies: - '@radix-ui/react-primitive': 2.2.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - '@radix-ui/react-use-layout-effect': 1.1.4(@types/react@19.3.0)(react@19.3.0) - react: 19.3.0 - react-dom: 19.3.0(react@19.3.0) + '@radix-ui/react-primitive': 2.2.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + '@radix-ui/react-use-layout-effect': 1.1.4(@types/react@19.3.0)(react@19.3.1) + react: 19.3.1 + react-dom: 19.3.1(react@19.3.1) optionalDependencies: '@types/react': 19.3.0 '@types/react-dom': 19.3.0(@types/react@19.3.0) - '@radix-ui/react-presence@1.1.11(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0)': + '@radix-ui/react-presence@1.1.11(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1)': dependencies: - '@radix-ui/react-use-layout-effect': 1.1.4(@types/react@19.3.0)(react@19.3.0) - react: 19.3.0 - react-dom: 19.3.0(react@19.3.0) + '@radix-ui/react-use-layout-effect': 1.1.4(@types/react@19.3.0)(react@19.3.1) + react: 19.3.1 + react-dom: 19.3.1(react@19.3.1) optionalDependencies: '@types/react': 19.3.0 '@types/react-dom': 19.3.0(@types/react@19.3.0) - '@radix-ui/react-primitive@2.1.3(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0)': + '@radix-ui/react-primitive@2.1.3(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1)': dependencies: - '@radix-ui/react-slot': 1.2.3(@types/react@19.3.0)(react@19.3.0) - react: 19.3.0 - react-dom: 19.3.0(react@19.3.0) + '@radix-ui/react-slot': 1.2.3(@types/react@19.3.0)(react@19.3.1) + react: 19.3.1 + react-dom: 19.3.1(react@19.3.1) optionalDependencies: '@types/react': 19.3.0 '@types/react-dom': 19.3.0(@types/react@19.3.0) - '@radix-ui/react-primitive@2.2.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0)': + '@radix-ui/react-primitive@2.2.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1)': dependencies: - '@radix-ui/react-slot': 1.5.0(@types/react@19.3.0)(react@19.3.0) - react: 19.3.0 - react-dom: 19.3.0(react@19.3.0) + '@radix-ui/react-slot': 1.5.0(@types/react@19.3.0)(react@19.3.1) + react: 19.3.1 + react-dom: 19.3.1(react@19.3.1) optionalDependencies: '@types/react': 19.3.0 '@types/react-dom': 19.3.0(@types/react@19.3.0) - '@radix-ui/react-progress@1.2.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0)': + '@radix-ui/react-progress@1.2.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1)': dependencies: - '@radix-ui/react-context': 1.3.0(@types/react@19.3.0)(react@19.3.0) - '@radix-ui/react-primitive': 2.2.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - react: 19.3.0 - react-dom: 19.3.0(react@19.3.0) + '@radix-ui/react-context': 1.3.0(@types/react@19.3.0)(react@19.3.1) + '@radix-ui/react-primitive': 2.2.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + react: 19.3.1 + react-dom: 19.3.1(react@19.3.1) optionalDependencies: '@types/react': 19.3.0 '@types/react-dom': 19.3.0(@types/react@19.3.0) - '@radix-ui/react-radio-group@1.5.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0)': + '@radix-ui/react-radio-group@1.5.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1)': dependencies: '@radix-ui/primitive': 1.2.0 - '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.3.0)(react@19.3.0) - '@radix-ui/react-context': 1.3.0(@types/react@19.3.0)(react@19.3.0) - '@radix-ui/react-direction': 1.2.0(@types/react@19.3.0)(react@19.3.0) - '@radix-ui/react-presence': 1.1.11(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - '@radix-ui/react-primitive': 2.2.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - '@radix-ui/react-roving-focus': 1.2.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - '@radix-ui/react-use-controllable-state': 1.3.0(@types/react@19.3.0)(react@19.3.0) - '@radix-ui/react-use-size': 1.1.5(@types/react@19.3.0)(react@19.3.0) - react: 19.3.0 - react-dom: 19.3.0(react@19.3.0) + '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.3.0)(react@19.3.1) + '@radix-ui/react-context': 1.3.0(@types/react@19.3.0)(react@19.3.1) + '@radix-ui/react-direction': 1.2.0(@types/react@19.3.0)(react@19.3.1) + '@radix-ui/react-presence': 1.1.11(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + '@radix-ui/react-primitive': 2.2.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + '@radix-ui/react-roving-focus': 1.2.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + '@radix-ui/react-use-controllable-state': 1.3.0(@types/react@19.3.0)(react@19.3.1) + '@radix-ui/react-use-size': 1.1.5(@types/react@19.3.0)(react@19.3.1) + react: 19.3.1 + react-dom: 19.3.1(react@19.3.1) optionalDependencies: '@types/react': 19.3.0 '@types/react-dom': 19.3.0(@types/react@19.3.0) - '@radix-ui/react-roving-focus@1.2.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0)': + '@radix-ui/react-roving-focus@1.2.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1)': dependencies: '@radix-ui/primitive': 1.2.0 - '@radix-ui/react-collection': 1.2.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.3.0)(react@19.3.0) - '@radix-ui/react-context': 1.3.0(@types/react@19.3.0)(react@19.3.0) - '@radix-ui/react-direction': 1.2.0(@types/react@19.3.0)(react@19.3.0) - '@radix-ui/react-id': 1.1.4(@types/react@19.3.0)(react@19.3.0) - '@radix-ui/react-primitive': 2.2.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - '@radix-ui/react-use-callback-ref': 1.1.5(@types/react@19.3.0)(react@19.3.0) - '@radix-ui/react-use-controllable-state': 1.3.0(@types/react@19.3.0)(react@19.3.0) - '@radix-ui/react-use-is-hydrated': 0.1.3(@types/react@19.3.0)(react@19.3.0) - '@radix-ui/react-use-layout-effect': 1.1.4(@types/react@19.3.0)(react@19.3.0) - react: 19.3.0 - react-dom: 19.3.0(react@19.3.0) + '@radix-ui/react-collection': 1.2.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.3.0)(react@19.3.1) + '@radix-ui/react-context': 1.3.0(@types/react@19.3.0)(react@19.3.1) + '@radix-ui/react-direction': 1.2.0(@types/react@19.3.0)(react@19.3.1) + '@radix-ui/react-id': 1.1.4(@types/react@19.3.0)(react@19.3.1) + '@radix-ui/react-primitive': 2.2.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + '@radix-ui/react-use-callback-ref': 1.1.5(@types/react@19.3.0)(react@19.3.1) + '@radix-ui/react-use-controllable-state': 1.3.0(@types/react@19.3.0)(react@19.3.1) + '@radix-ui/react-use-is-hydrated': 0.1.3(@types/react@19.3.0)(react@19.3.1) + '@radix-ui/react-use-layout-effect': 1.1.4(@types/react@19.3.0)(react@19.3.1) + react: 19.3.1 + react-dom: 19.3.1(react@19.3.1) optionalDependencies: '@types/react': 19.3.0 '@types/react-dom': 19.3.0(@types/react@19.3.0) - '@radix-ui/react-scroll-area@1.4.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0)': + '@radix-ui/react-scroll-area@1.4.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1)': dependencies: '@radix-ui/number': 1.1.3 '@radix-ui/primitive': 1.2.0 - '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.3.0)(react@19.3.0) - '@radix-ui/react-context': 1.3.0(@types/react@19.3.0)(react@19.3.0) - '@radix-ui/react-direction': 1.2.0(@types/react@19.3.0)(react@19.3.0) - '@radix-ui/react-presence': 1.1.11(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - '@radix-ui/react-primitive': 2.2.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - '@radix-ui/react-slot': 1.5.0(@types/react@19.3.0)(react@19.3.0) - '@radix-ui/react-use-callback-ref': 1.1.5(@types/react@19.3.0)(react@19.3.0) - '@radix-ui/react-use-layout-effect': 1.1.4(@types/react@19.3.0)(react@19.3.0) - react: 19.3.0 - react-dom: 19.3.0(react@19.3.0) + '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.3.0)(react@19.3.1) + '@radix-ui/react-context': 1.3.0(@types/react@19.3.0)(react@19.3.1) + '@radix-ui/react-direction': 1.2.0(@types/react@19.3.0)(react@19.3.1) + '@radix-ui/react-presence': 1.1.11(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + '@radix-ui/react-primitive': 2.2.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + '@radix-ui/react-slot': 1.5.0(@types/react@19.3.0)(react@19.3.1) + '@radix-ui/react-use-callback-ref': 1.1.5(@types/react@19.3.0)(react@19.3.1) + '@radix-ui/react-use-layout-effect': 1.1.4(@types/react@19.3.0)(react@19.3.1) + react: 19.3.1 + react-dom: 19.3.1(react@19.3.1) optionalDependencies: '@types/react': 19.3.0 '@types/react-dom': 19.3.0(@types/react@19.3.0) - '@radix-ui/react-select@2.4.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0)': + '@radix-ui/react-select@2.4.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1)': dependencies: '@radix-ui/number': 1.1.3 '@radix-ui/primitive': 1.2.0 - '@radix-ui/react-collection': 1.2.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.3.0)(react@19.3.0) - '@radix-ui/react-context': 1.3.0(@types/react@19.3.0)(react@19.3.0) - '@radix-ui/react-direction': 1.2.0(@types/react@19.3.0)(react@19.3.0) - '@radix-ui/react-dismissable-layer': 1.2.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - '@radix-ui/react-focus-guards': 1.2.0(@types/react@19.3.0)(react@19.3.0) - '@radix-ui/react-focus-scope': 1.3.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - '@radix-ui/react-id': 1.1.4(@types/react@19.3.0)(react@19.3.0) - '@radix-ui/react-popper': 1.4.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - '@radix-ui/react-portal': 1.2.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - '@radix-ui/react-presence': 1.1.11(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - '@radix-ui/react-primitive': 2.2.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - '@radix-ui/react-slot': 1.5.0(@types/react@19.3.0)(react@19.3.0) - '@radix-ui/react-use-callback-ref': 1.1.5(@types/react@19.3.0)(react@19.3.0) - '@radix-ui/react-use-controllable-state': 1.3.0(@types/react@19.3.0)(react@19.3.0) - '@radix-ui/react-use-layout-effect': 1.1.4(@types/react@19.3.0)(react@19.3.0) - '@radix-ui/react-visually-hidden': 1.2.13(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-collection': 1.2.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.3.0)(react@19.3.1) + '@radix-ui/react-context': 1.3.0(@types/react@19.3.0)(react@19.3.1) + '@radix-ui/react-direction': 1.2.0(@types/react@19.3.0)(react@19.3.1) + '@radix-ui/react-dismissable-layer': 1.2.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + '@radix-ui/react-focus-guards': 1.2.0(@types/react@19.3.0)(react@19.3.1) + '@radix-ui/react-focus-scope': 1.3.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + '@radix-ui/react-id': 1.1.4(@types/react@19.3.0)(react@19.3.1) + '@radix-ui/react-popper': 1.4.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + '@radix-ui/react-portal': 1.2.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + '@radix-ui/react-presence': 1.1.11(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + '@radix-ui/react-primitive': 2.2.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + '@radix-ui/react-slot': 1.5.0(@types/react@19.3.0)(react@19.3.1) + '@radix-ui/react-use-callback-ref': 1.1.5(@types/react@19.3.0)(react@19.3.1) + '@radix-ui/react-use-controllable-state': 1.3.0(@types/react@19.3.0)(react@19.3.1) + '@radix-ui/react-use-layout-effect': 1.1.4(@types/react@19.3.0)(react@19.3.1) + '@radix-ui/react-visually-hidden': 1.2.13(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) aria-hidden: 1.2.6 - react: 19.3.0 - react-dom: 19.3.0(react@19.3.0) - react-remove-scroll: 2.7.2(@types/react@19.3.0)(react@19.3.0) + react: 19.3.1 + react-dom: 19.3.1(react@19.3.1) + react-remove-scroll: 2.7.2(@types/react@19.3.0)(react@19.3.1) optionalDependencies: '@types/react': 19.3.0 '@types/react-dom': 19.3.0(@types/react@19.3.0) - '@radix-ui/react-separator@1.2.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0)': + '@radix-ui/react-separator@1.2.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1)': dependencies: - '@radix-ui/react-primitive': 2.2.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - react: 19.3.0 - react-dom: 19.3.0(react@19.3.0) + '@radix-ui/react-primitive': 2.2.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + react: 19.3.1 + react-dom: 19.3.1(react@19.3.1) optionalDependencies: '@types/react': 19.3.0 '@types/react-dom': 19.3.0(@types/react@19.3.0) - '@radix-ui/react-slider@1.6.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0)': + '@radix-ui/react-slider@1.6.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1)': dependencies: '@radix-ui/number': 1.1.3 '@radix-ui/primitive': 1.2.0 - '@radix-ui/react-collection': 1.2.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.3.0)(react@19.3.0) - '@radix-ui/react-context': 1.3.0(@types/react@19.3.0)(react@19.3.0) - '@radix-ui/react-direction': 1.2.0(@types/react@19.3.0)(react@19.3.0) - '@radix-ui/react-primitive': 2.2.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - '@radix-ui/react-use-controllable-state': 1.3.0(@types/react@19.3.0)(react@19.3.0) - '@radix-ui/react-use-layout-effect': 1.1.4(@types/react@19.3.0)(react@19.3.0) - '@radix-ui/react-use-size': 1.1.5(@types/react@19.3.0)(react@19.3.0) - react: 19.3.0 - react-dom: 19.3.0(react@19.3.0) + '@radix-ui/react-collection': 1.2.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.3.0)(react@19.3.1) + '@radix-ui/react-context': 1.3.0(@types/react@19.3.0)(react@19.3.1) + '@radix-ui/react-direction': 1.2.0(@types/react@19.3.0)(react@19.3.1) + '@radix-ui/react-primitive': 2.2.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + '@radix-ui/react-use-controllable-state': 1.3.0(@types/react@19.3.0)(react@19.3.1) + '@radix-ui/react-use-layout-effect': 1.1.4(@types/react@19.3.0)(react@19.3.1) + '@radix-ui/react-use-size': 1.1.5(@types/react@19.3.0)(react@19.3.1) + react: 19.3.1 + react-dom: 19.3.1(react@19.3.1) optionalDependencies: '@types/react': 19.3.0 '@types/react-dom': 19.3.0(@types/react@19.3.0) - '@radix-ui/react-slot@1.2.3(@types/react@19.3.0)(react@19.3.0)': + '@radix-ui/react-slot@1.2.3(@types/react@19.3.0)(react@19.3.1)': dependencies: - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.3.0)(react@19.3.0) - react: 19.3.0 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.3.0)(react@19.3.1) + react: 19.3.1 optionalDependencies: '@types/react': 19.3.0 - '@radix-ui/react-slot@1.5.0(@types/react@19.3.0)(react@19.3.0)': + '@radix-ui/react-slot@1.5.0(@types/react@19.3.0)(react@19.3.1)': dependencies: - '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.3.0)(react@19.3.0) - react: 19.3.0 + '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.3.0)(react@19.3.1) + react: 19.3.1 optionalDependencies: '@types/react': 19.3.0 - '@radix-ui/react-switch@1.4.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0)': + '@radix-ui/react-switch@1.4.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1)': dependencies: '@radix-ui/primitive': 1.2.0 - '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.3.0)(react@19.3.0) - '@radix-ui/react-context': 1.3.0(@types/react@19.3.0)(react@19.3.0) - '@radix-ui/react-primitive': 2.2.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - '@radix-ui/react-use-controllable-state': 1.3.0(@types/react@19.3.0)(react@19.3.0) - '@radix-ui/react-use-size': 1.1.5(@types/react@19.3.0)(react@19.3.0) - react: 19.3.0 - react-dom: 19.3.0(react@19.3.0) + '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.3.0)(react@19.3.1) + '@radix-ui/react-context': 1.3.0(@types/react@19.3.0)(react@19.3.1) + '@radix-ui/react-primitive': 2.2.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + '@radix-ui/react-use-controllable-state': 1.3.0(@types/react@19.3.0)(react@19.3.1) + '@radix-ui/react-use-size': 1.1.5(@types/react@19.3.0)(react@19.3.1) + react: 19.3.1 + react-dom: 19.3.1(react@19.3.1) optionalDependencies: '@types/react': 19.3.0 '@types/react-dom': 19.3.0(@types/react@19.3.0) - '@radix-ui/react-tabs@1.2.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0)': + '@radix-ui/react-tabs@1.2.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1)': dependencies: '@radix-ui/primitive': 1.2.0 - '@radix-ui/react-context': 1.3.0(@types/react@19.3.0)(react@19.3.0) - '@radix-ui/react-direction': 1.2.0(@types/react@19.3.0)(react@19.3.0) - '@radix-ui/react-id': 1.1.4(@types/react@19.3.0)(react@19.3.0) - '@radix-ui/react-presence': 1.1.11(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - '@radix-ui/react-primitive': 2.2.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - '@radix-ui/react-roving-focus': 1.2.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - '@radix-ui/react-use-controllable-state': 1.3.0(@types/react@19.3.0)(react@19.3.0) - react: 19.3.0 - react-dom: 19.3.0(react@19.3.0) + '@radix-ui/react-context': 1.3.0(@types/react@19.3.0)(react@19.3.1) + '@radix-ui/react-direction': 1.2.0(@types/react@19.3.0)(react@19.3.1) + '@radix-ui/react-id': 1.1.4(@types/react@19.3.0)(react@19.3.1) + '@radix-ui/react-presence': 1.1.11(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + '@radix-ui/react-primitive': 2.2.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + '@radix-ui/react-roving-focus': 1.2.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + '@radix-ui/react-use-controllable-state': 1.3.0(@types/react@19.3.0)(react@19.3.1) + react: 19.3.1 + react-dom: 19.3.1(react@19.3.1) optionalDependencies: '@types/react': 19.3.0 '@types/react-dom': 19.3.0(@types/react@19.3.0) - '@radix-ui/react-toast@1.3.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0)': + '@radix-ui/react-toast@1.3.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1)': dependencies: '@radix-ui/primitive': 1.2.0 - '@radix-ui/react-collection': 1.2.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.3.0)(react@19.3.0) - '@radix-ui/react-context': 1.3.0(@types/react@19.3.0)(react@19.3.0) - '@radix-ui/react-dismissable-layer': 1.2.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - '@radix-ui/react-portal': 1.2.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - '@radix-ui/react-presence': 1.1.11(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - '@radix-ui/react-primitive': 2.2.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - '@radix-ui/react-use-callback-ref': 1.1.5(@types/react@19.3.0)(react@19.3.0) - '@radix-ui/react-use-controllable-state': 1.3.0(@types/react@19.3.0)(react@19.3.0) - '@radix-ui/react-use-layout-effect': 1.1.4(@types/react@19.3.0)(react@19.3.0) - '@radix-ui/react-visually-hidden': 1.2.13(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - react: 19.3.0 - react-dom: 19.3.0(react@19.3.0) + '@radix-ui/react-collection': 1.2.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.3.0)(react@19.3.1) + '@radix-ui/react-context': 1.3.0(@types/react@19.3.0)(react@19.3.1) + '@radix-ui/react-dismissable-layer': 1.2.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + '@radix-ui/react-portal': 1.2.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + '@radix-ui/react-presence': 1.1.11(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + '@radix-ui/react-primitive': 2.2.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + '@radix-ui/react-use-callback-ref': 1.1.5(@types/react@19.3.0)(react@19.3.1) + '@radix-ui/react-use-controllable-state': 1.3.0(@types/react@19.3.0)(react@19.3.1) + '@radix-ui/react-use-layout-effect': 1.1.4(@types/react@19.3.0)(react@19.3.1) + '@radix-ui/react-visually-hidden': 1.2.13(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + react: 19.3.1 + react-dom: 19.3.1(react@19.3.1) optionalDependencies: '@types/react': 19.3.0 '@types/react-dom': 19.3.0(@types/react@19.3.0) - '@radix-ui/react-toggle-group@1.2.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0)': + '@radix-ui/react-toggle-group@1.2.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1)': dependencies: '@radix-ui/primitive': 1.2.0 - '@radix-ui/react-context': 1.3.0(@types/react@19.3.0)(react@19.3.0) - '@radix-ui/react-direction': 1.2.0(@types/react@19.3.0)(react@19.3.0) - '@radix-ui/react-primitive': 2.2.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - '@radix-ui/react-roving-focus': 1.2.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - '@radix-ui/react-toggle': 1.2.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - '@radix-ui/react-use-controllable-state': 1.3.0(@types/react@19.3.0)(react@19.3.0) - react: 19.3.0 - react-dom: 19.3.0(react@19.3.0) + '@radix-ui/react-context': 1.3.0(@types/react@19.3.0)(react@19.3.1) + '@radix-ui/react-direction': 1.2.0(@types/react@19.3.0)(react@19.3.1) + '@radix-ui/react-primitive': 2.2.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + '@radix-ui/react-roving-focus': 1.2.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + '@radix-ui/react-toggle': 1.2.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + '@radix-ui/react-use-controllable-state': 1.3.0(@types/react@19.3.0)(react@19.3.1) + react: 19.3.1 + react-dom: 19.3.1(react@19.3.1) optionalDependencies: '@types/react': 19.3.0 '@types/react-dom': 19.3.0(@types/react@19.3.0) - '@radix-ui/react-toggle@1.1.10(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0)': + '@radix-ui/react-toggle@1.1.10(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1)': dependencies: '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.3.0)(react@19.3.0) - react: 19.3.0 - react-dom: 19.3.0(react@19.3.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.3.0)(react@19.3.1) + react: 19.3.1 + react-dom: 19.3.1(react@19.3.1) optionalDependencies: '@types/react': 19.3.0 '@types/react-dom': 19.3.0(@types/react@19.3.0) - '@radix-ui/react-toggle@1.2.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0)': + '@radix-ui/react-toggle@1.2.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1)': dependencies: '@radix-ui/primitive': 1.2.0 - '@radix-ui/react-primitive': 2.2.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - '@radix-ui/react-use-controllable-state': 1.3.0(@types/react@19.3.0)(react@19.3.0) - react: 19.3.0 - react-dom: 19.3.0(react@19.3.0) + '@radix-ui/react-primitive': 2.2.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + '@radix-ui/react-use-controllable-state': 1.3.0(@types/react@19.3.0)(react@19.3.1) + react: 19.3.1 + react-dom: 19.3.1(react@19.3.1) optionalDependencies: '@types/react': 19.3.0 '@types/react-dom': 19.3.0(@types/react@19.3.0) - '@radix-ui/react-toolbar@1.2.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0)': + '@radix-ui/react-toolbar@1.2.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1)': dependencies: '@radix-ui/primitive': 1.2.0 - '@radix-ui/react-context': 1.3.0(@types/react@19.3.0)(react@19.3.0) - '@radix-ui/react-direction': 1.2.0(@types/react@19.3.0)(react@19.3.0) - '@radix-ui/react-primitive': 2.2.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - '@radix-ui/react-roving-focus': 1.2.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - '@radix-ui/react-separator': 1.2.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - '@radix-ui/react-toggle-group': 1.2.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - react: 19.3.0 - react-dom: 19.3.0(react@19.3.0) + '@radix-ui/react-context': 1.3.0(@types/react@19.3.0)(react@19.3.1) + '@radix-ui/react-direction': 1.2.0(@types/react@19.3.0)(react@19.3.1) + '@radix-ui/react-primitive': 2.2.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + '@radix-ui/react-roving-focus': 1.2.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + '@radix-ui/react-separator': 1.2.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + '@radix-ui/react-toggle-group': 1.2.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + react: 19.3.1 + react-dom: 19.3.1(react@19.3.1) optionalDependencies: '@types/react': 19.3.0 '@types/react-dom': 19.3.0(@types/react@19.3.0) - '@radix-ui/react-tooltip@1.4.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0)': + '@radix-ui/react-tooltip@1.4.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1)': dependencies: '@radix-ui/primitive': 1.2.0 - '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.3.0)(react@19.3.0) - '@radix-ui/react-context': 1.3.0(@types/react@19.3.0)(react@19.3.0) - '@radix-ui/react-dismissable-layer': 1.2.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - '@radix-ui/react-focus-scope': 1.3.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - '@radix-ui/react-id': 1.1.4(@types/react@19.3.0)(react@19.3.0) - '@radix-ui/react-popper': 1.4.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - '@radix-ui/react-portal': 1.2.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - '@radix-ui/react-presence': 1.1.11(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - '@radix-ui/react-primitive': 2.2.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - '@radix-ui/react-slot': 1.5.0(@types/react@19.3.0)(react@19.3.0) - '@radix-ui/react-use-controllable-state': 1.3.0(@types/react@19.3.0)(react@19.3.0) - '@radix-ui/react-use-layout-effect': 1.1.4(@types/react@19.3.0)(react@19.3.0) - '@radix-ui/react-visually-hidden': 1.2.13(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - react: 19.3.0 - react-dom: 19.3.0(react@19.3.0) + '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.3.0)(react@19.3.1) + '@radix-ui/react-context': 1.3.0(@types/react@19.3.0)(react@19.3.1) + '@radix-ui/react-dismissable-layer': 1.2.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + '@radix-ui/react-focus-scope': 1.3.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + '@radix-ui/react-id': 1.1.4(@types/react@19.3.0)(react@19.3.1) + '@radix-ui/react-popper': 1.4.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + '@radix-ui/react-portal': 1.2.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + '@radix-ui/react-presence': 1.1.11(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + '@radix-ui/react-primitive': 2.2.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + '@radix-ui/react-slot': 1.5.0(@types/react@19.3.0)(react@19.3.1) + '@radix-ui/react-use-controllable-state': 1.3.0(@types/react@19.3.0)(react@19.3.1) + '@radix-ui/react-use-layout-effect': 1.1.4(@types/react@19.3.0)(react@19.3.1) + '@radix-ui/react-visually-hidden': 1.2.13(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + react: 19.3.1 + react-dom: 19.3.1(react@19.3.1) optionalDependencies: '@types/react': 19.3.0 '@types/react-dom': 19.3.0(@types/react@19.3.0) - '@radix-ui/react-use-callback-ref@1.1.5(@types/react@19.3.0)(react@19.3.0)': + '@radix-ui/react-use-callback-ref@1.1.5(@types/react@19.3.0)(react@19.3.1)': dependencies: - '@radix-ui/react-use-layout-effect': 1.1.4(@types/react@19.3.0)(react@19.3.0) - react: 19.3.0 + '@radix-ui/react-use-layout-effect': 1.1.4(@types/react@19.3.0)(react@19.3.1) + react: 19.3.1 optionalDependencies: '@types/react': 19.3.0 - '@radix-ui/react-use-controllable-state@1.2.2(@types/react@19.3.0)(react@19.3.0)': + '@radix-ui/react-use-controllable-state@1.2.2(@types/react@19.3.0)(react@19.3.1)': dependencies: - '@radix-ui/react-use-effect-event': 0.0.2(@types/react@19.3.0)(react@19.3.0) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.3.0)(react@19.3.0) - react: 19.3.0 + '@radix-ui/react-use-effect-event': 0.0.2(@types/react@19.3.0)(react@19.3.1) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.3.0)(react@19.3.1) + react: 19.3.1 optionalDependencies: '@types/react': 19.3.0 - '@radix-ui/react-use-controllable-state@1.3.0(@types/react@19.3.0)(react@19.3.0)': + '@radix-ui/react-use-controllable-state@1.3.0(@types/react@19.3.0)(react@19.3.1)': dependencies: '@radix-ui/primitive': 1.2.0 - '@radix-ui/react-use-effect-event': 0.0.5(@types/react@19.3.0)(react@19.3.0) - '@radix-ui/react-use-layout-effect': 1.1.4(@types/react@19.3.0)(react@19.3.0) - react: 19.3.0 + '@radix-ui/react-use-effect-event': 0.0.5(@types/react@19.3.0)(react@19.3.1) + '@radix-ui/react-use-layout-effect': 1.1.4(@types/react@19.3.0)(react@19.3.1) + react: 19.3.1 optionalDependencies: '@types/react': 19.3.0 - '@radix-ui/react-use-effect-event@0.0.2(@types/react@19.3.0)(react@19.3.0)': + '@radix-ui/react-use-effect-event@0.0.2(@types/react@19.3.0)(react@19.3.1)': dependencies: - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.3.0)(react@19.3.0) - react: 19.3.0 + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.3.0)(react@19.3.1) + react: 19.3.1 optionalDependencies: '@types/react': 19.3.0 - '@radix-ui/react-use-effect-event@0.0.5(@types/react@19.3.0)(react@19.3.0)': + '@radix-ui/react-use-effect-event@0.0.5(@types/react@19.3.0)(react@19.3.1)': dependencies: - '@radix-ui/react-use-layout-effect': 1.1.4(@types/react@19.3.0)(react@19.3.0) - react: 19.3.0 + '@radix-ui/react-use-layout-effect': 1.1.4(@types/react@19.3.0)(react@19.3.1) + react: 19.3.1 optionalDependencies: '@types/react': 19.3.0 - '@radix-ui/react-use-escape-keydown@1.1.6(@types/react@19.3.0)(react@19.3.0)': + '@radix-ui/react-use-escape-keydown@1.1.6(@types/react@19.3.0)(react@19.3.1)': dependencies: - '@radix-ui/react-use-callback-ref': 1.1.5(@types/react@19.3.0)(react@19.3.0) - react: 19.3.0 + '@radix-ui/react-use-callback-ref': 1.1.5(@types/react@19.3.0)(react@19.3.1) + react: 19.3.1 optionalDependencies: '@types/react': 19.3.0 - '@radix-ui/react-use-is-hydrated@0.1.3(@types/react@19.3.0)(react@19.3.0)': + '@radix-ui/react-use-is-hydrated@0.1.3(@types/react@19.3.0)(react@19.3.1)': dependencies: - react: 19.3.0 + react: 19.3.1 optionalDependencies: '@types/react': 19.3.0 - '@radix-ui/react-use-layout-effect@1.1.1(@types/react@19.3.0)(react@19.3.0)': + '@radix-ui/react-use-layout-effect@1.1.1(@types/react@19.3.0)(react@19.3.1)': dependencies: - react: 19.3.0 + react: 19.3.1 optionalDependencies: '@types/react': 19.3.0 - '@radix-ui/react-use-layout-effect@1.1.4(@types/react@19.3.0)(react@19.3.0)': + '@radix-ui/react-use-layout-effect@1.1.4(@types/react@19.3.0)(react@19.3.1)': dependencies: - react: 19.3.0 + react: 19.3.1 optionalDependencies: '@types/react': 19.3.0 - '@radix-ui/react-use-previous@1.1.4(@types/react@19.3.0)(react@19.3.0)': + '@radix-ui/react-use-previous@1.1.4(@types/react@19.3.0)(react@19.3.1)': dependencies: - react: 19.3.0 + react: 19.3.1 optionalDependencies: '@types/react': 19.3.0 - '@radix-ui/react-use-rect@1.1.4(@types/react@19.3.0)(react@19.3.0)': + '@radix-ui/react-use-rect@1.1.4(@types/react@19.3.0)(react@19.3.1)': dependencies: '@radix-ui/rect': 1.1.3 - react: 19.3.0 + react: 19.3.1 optionalDependencies: '@types/react': 19.3.0 - '@radix-ui/react-use-size@1.1.5(@types/react@19.3.0)(react@19.3.0)': + '@radix-ui/react-use-size@1.1.5(@types/react@19.3.0)(react@19.3.1)': dependencies: - '@radix-ui/react-use-layout-effect': 1.1.4(@types/react@19.3.0)(react@19.3.0) - react: 19.3.0 + '@radix-ui/react-use-layout-effect': 1.1.4(@types/react@19.3.0)(react@19.3.1) + react: 19.3.1 optionalDependencies: '@types/react': 19.3.0 - '@radix-ui/react-visually-hidden@1.2.13(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0)': + '@radix-ui/react-visually-hidden@1.2.13(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1)': dependencies: - '@radix-ui/react-primitive': 2.2.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - react: 19.3.0 - react-dom: 19.3.0(react@19.3.0) + '@radix-ui/react-primitive': 2.2.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + react: 19.3.1 + react-dom: 19.3.1(react@19.3.1) optionalDependencies: '@types/react': 19.3.0 '@types/react-dom': 19.3.0(@types/react@19.3.0) '@radix-ui/rect@1.1.3': {} - '@reduxjs/toolkit@2.11.2(react-redux@9.2.0(@types/react@19.3.0)(react@19.3.0)(redux@5.0.1))(react@19.3.0)': + '@reduxjs/toolkit@2.11.2(react-redux@9.2.0(@types/react@19.3.0)(react@19.3.1)(redux@5.0.1))(react@19.3.1)': dependencies: '@standard-schema/spec': 1.1.0 '@standard-schema/utils': 0.3.0 @@ -9809,8 +9866,8 @@ snapshots: redux-thunk: 3.1.0(redux@5.0.1) reselect: 5.1.1 optionalDependencies: - react: 19.3.0 - react-redux: 9.2.0(@types/react@19.3.0)(react@19.3.0)(redux@5.0.1) + react: 19.3.1 + react-redux: 9.2.0(@types/react@19.3.0)(react@19.3.1)(redux@5.0.1) '@rolldown/binding-android-arm64@1.1.5': optional: true @@ -9867,7 +9924,7 @@ snapshots: '@sec-ant/readable-stream@0.4.1': {} - '@shadcn/registry@0.1.3(supports-color@7.2.0)': + '@shadcn/registry@0.2.0(supports-color@7.2.0)': dependencies: '@babel/core': 7.29.7(supports-color@7.2.0) '@babel/parser': 7.29.9 @@ -9891,7 +9948,7 @@ snapshots: tsconfig-paths: 4.2.0 type-fest: 4.41.0 undici: 7.30.0 - zod: 3.25.76 + zod: 4.6.7 transitivePeerDependencies: - supports-color @@ -9917,27 +9974,32 @@ snapshots: '@smithy/types': 4.19.0 tslib: 2.8.1 + '@smithy/core@3.36.1': + dependencies: + '@smithy/types': 4.19.0 + tslib: 2.8.1 + '@smithy/credential-provider-imds@4.5.2': dependencies: - '@smithy/core': 3.35.2 + '@smithy/core': 3.36.1 '@smithy/types': 4.19.0 tslib: 2.8.1 '@smithy/fetch-http-handler@5.8.0': dependencies: - '@smithy/core': 3.35.2 + '@smithy/core': 3.36.1 '@smithy/types': 4.19.0 tslib: 2.8.1 '@smithy/node-http-handler@4.12.1': dependencies: - '@smithy/core': 3.35.2 + '@smithy/core': 3.36.1 '@smithy/types': 4.19.0 tslib: 2.8.1 '@smithy/signature-v4@5.7.4': dependencies: - '@smithy/core': 3.35.2 + '@smithy/core': 3.36.1 '@smithy/types': 4.19.0 tslib: 2.8.1 @@ -10006,14 +10068,14 @@ snapshots: '@stryker-mutator/util@10.0.0': {} - '@stryker-mutator/vitest-runner@10.0.0(patch_hash=9ad3e9855fd9781fd36e872ed42c5f934f91b962d6830e6cc56d56bf16e1dabd)(@stryker-mutator/core@10.0.0(@types/node@26.6.5))(vitest@5.0.4)': + '@stryker-mutator/vitest-runner@10.0.0(patch_hash=9ad3e9855fd9781fd36e872ed42c5f934f91b962d6830e6cc56d56bf16e1dabd)(@stryker-mutator/core@10.0.0(@types/node@26.6.5))(vitest@5.0.5)': dependencies: '@stryker-mutator/api': 10.0.0 '@stryker-mutator/core': 10.0.0(@types/node@26.6.5) '@stryker-mutator/util': 10.0.0 semver: 7.8.5 tslib: 2.8.1 - vitest: 5.0.4(@opentelemetry/api@1.9.1)(@types/node@26.6.5)(@vitest/coverage-v8@5.0.4)(msw@2.14.6(@types/node@26.6.5)(typescript@6.0.3))(vite@8.1.5(@types/node@26.6.5)(jiti@2.7.0)(yaml@2.9.1)) + vitest: 5.0.5(@opentelemetry/api@1.9.1)(@types/node@26.6.5)(@vitest/coverage-v8@5.0.5)(msw@2.14.6(@types/node@26.6.5)(typescript@6.0.3))(vite@8.1.6(@types/node@26.6.5)(jiti@2.7.0)(yaml@2.9.1)) '@swc/helpers@0.5.23': dependencies: @@ -10088,20 +10150,20 @@ snapshots: postcss: 8.5.26 tailwindcss: 4.3.3 - '@tanstack/query-core@5.104.2': {} + '@tanstack/query-core@5.104.3': {} - '@tanstack/react-query@5.104.2(react@19.3.0)': + '@tanstack/react-query@5.104.3(react@19.3.1)': dependencies: - '@tanstack/query-core': 5.104.2 - react: 19.3.0 + '@tanstack/query-core': 5.104.3 + react: 19.3.1 - '@tanstack/react-virtual@3.14.17(react-dom@19.3.0(react@19.3.0))(react@19.3.0)': + '@tanstack/react-virtual@3.14.21(react-dom@19.3.1(react@19.3.1))(react@19.3.1)': dependencies: - '@tanstack/virtual-core': 3.19.2 - react: 19.3.0 - react-dom: 19.3.0(react@19.3.0) + '@tanstack/virtual-core': 3.19.6 + react: 19.3.1 + react-dom: 19.3.1(react@19.3.1) - '@tanstack/virtual-core@3.19.2': {} + '@tanstack/virtual-core@3.19.6': {} '@tesseract.js-data/deu@1.0.0': {} @@ -10450,47 +10512,47 @@ snapshots: '@types/react': 19.3.0 '@visx/point': 4.0.1-alpha.0 - '@visx/grid@4.0.1-alpha.0(react@19.3.0)': + '@visx/grid@4.0.1-alpha.0(react@19.3.1)': dependencies: '@types/react': 19.3.0 '@visx/curve': 4.0.1-alpha.0 - '@visx/group': 4.0.1-alpha.0(react@19.3.0) + '@visx/group': 4.0.1-alpha.0(react@19.3.1) '@visx/point': 4.0.1-alpha.0 '@visx/scale': 4.0.1-alpha.0 - '@visx/shape': 4.0.1-alpha.0(react@19.3.0) + '@visx/shape': 4.0.1-alpha.0(react@19.3.1) classnames: 2.5.1 - react: 19.3.0 + react: 19.3.1 - '@visx/group@4.0.1-alpha.0(react@19.3.0)': + '@visx/group@4.0.1-alpha.0(react@19.3.1)': dependencies: '@types/react': 19.3.0 classnames: 2.5.1 - react: 19.3.0 + react: 19.3.1 '@visx/point@4.0.1-alpha.0': {} - '@visx/responsive@4.0.1-alpha.0(react@19.3.0)': + '@visx/responsive@4.0.1-alpha.0(react@19.3.1)': dependencies: '@types/lodash': 4.17.25 '@types/react': 19.3.0 lodash: 4.18.1 - react: 19.3.0 + react: 19.3.1 '@visx/scale@4.0.1-alpha.0': dependencies: '@visx/vendor': 4.0.0-alpha.0 - '@visx/shape@4.0.1-alpha.0(react@19.3.0)': + '@visx/shape@4.0.1-alpha.0(react@19.3.1)': dependencies: '@types/lodash': 4.17.25 '@types/react': 19.3.0 '@visx/curve': 4.0.1-alpha.0 - '@visx/group': 4.0.1-alpha.0(react@19.3.0) + '@visx/group': 4.0.1-alpha.0(react@19.3.1) '@visx/scale': 4.0.1-alpha.0 '@visx/vendor': 4.0.0-alpha.0 classnames: 2.5.1 lodash: 4.18.1 - react: 19.3.0 + react: 19.3.1 '@visx/vendor@4.0.0-alpha.0': dependencies: @@ -10518,7 +10580,7 @@ snapshots: d3-time-format: 4.1.0 internmap: 2.0.3 - '@vitest/coverage-v8@5.0.4(vitest@5.0.4)': + '@vitest/coverage-v8@5.0.5(vitest@5.0.5)': dependencies: '@bcoe/v8-coverage': 1.0.2 '@vitest/istanbul-lib-coverage': 1.0.2 @@ -10528,7 +10590,7 @@ snapshots: obug: 3.0.0 std-env: 4.3.0 tinyrainbow: 3.2.0 - vitest: 5.0.4(@opentelemetry/api@1.9.1)(@types/node@26.6.5)(@vitest/coverage-v8@5.0.4)(msw@2.14.6(@types/node@26.6.5)(typescript@6.0.3))(vite@8.1.5(@types/node@26.6.5)(jiti@2.7.0)(yaml@2.9.1)) + vitest: 5.0.5(@opentelemetry/api@1.9.1)(@types/node@26.6.5)(@vitest/coverage-v8@5.0.5)(msw@2.14.6(@types/node@26.6.5)(typescript@6.0.3))(vite@8.1.6(@types/node@26.6.5)(jiti@2.7.0)(yaml@2.9.1)) '@vitest/istanbul-lib-coverage@1.0.2': {} @@ -10536,17 +10598,17 @@ snapshots: dependencies: '@vitest/istanbul-lib-coverage': 1.0.2 - '@vitest/mocker@5.0.4(msw@2.14.6(@types/node@26.6.5)(typescript@6.0.3))(vite@8.1.5(@types/node@26.6.5)(jiti@2.7.0)(yaml@2.9.1))': + '@vitest/mocker@5.0.5(msw@2.14.6(@types/node@26.6.5)(typescript@6.0.3))(vite@8.1.6(@types/node@26.6.5)(jiti@2.7.0)(yaml@2.9.1))': dependencies: '@jridgewell/trace-mapping': 0.3.31 - '@vitest/spy': 5.0.4 + '@vitest/spy': 5.0.5 estree-walker: 3.0.3 magic-string: 1.4.3 optionalDependencies: msw: 2.14.6(@types/node@26.6.5)(typescript@6.0.3) - vite: 8.1.5(@types/node@26.6.5)(jiti@2.7.0)(yaml@2.9.1) + vite: 8.1.6(@types/node@26.6.5)(jiti@2.7.0)(yaml@2.9.1) - '@vitest/spy@5.0.4': {} + '@vitest/spy@5.0.5': {} abort-controller@3.0.0: dependencies: @@ -10755,6 +10817,8 @@ snapshots: axe-core@4.13.0: {} + axe-core@4.14.0: {} + axobject-query@4.1.0: {} b4a@1.9.0: {} @@ -11072,7 +11136,7 @@ snapshots: crc-32: 1.2.2 readable-stream: 4.8.0 - cron-parser@5.10.1: + cron-parser@5.10.2: dependencies: luxon: 3.7.2 @@ -11282,7 +11346,7 @@ snapshots: dotenv@17.4.2: {} - dotenv@18.0.7: {} + dotenv@18.0.8: {} dunder-proto@1.0.1: dependencies: @@ -12383,7 +12447,7 @@ snapshots: ms: 2.1.3 semver: 7.8.0 - jspdf-autotable@5.0.8(jspdf@4.2.1): + jspdf-autotable@5.0.9(jspdf@4.2.1): dependencies: jspdf: 4.2.1 @@ -12504,36 +12568,69 @@ snapshots: lightningcss-android-arm64@1.32.0: optional: true + lightningcss-android-arm64@1.33.0: + optional: true + lightningcss-darwin-arm64@1.32.0: optional: true + lightningcss-darwin-arm64@1.33.0: + optional: true + lightningcss-darwin-x64@1.32.0: optional: true + lightningcss-darwin-x64@1.33.0: + optional: true + lightningcss-freebsd-x64@1.32.0: optional: true + lightningcss-freebsd-x64@1.33.0: + optional: true + lightningcss-linux-arm-gnueabihf@1.32.0: optional: true + lightningcss-linux-arm-gnueabihf@1.33.0: + optional: true + lightningcss-linux-arm64-gnu@1.32.0: optional: true + lightningcss-linux-arm64-gnu@1.33.0: + optional: true + lightningcss-linux-arm64-musl@1.32.0: optional: true + lightningcss-linux-arm64-musl@1.33.0: + optional: true + lightningcss-linux-x64-gnu@1.32.0: optional: true + lightningcss-linux-x64-gnu@1.33.0: + optional: true + lightningcss-linux-x64-musl@1.32.0: optional: true + lightningcss-linux-x64-musl@1.33.0: + optional: true + lightningcss-win32-arm64-msvc@1.32.0: optional: true + lightningcss-win32-arm64-msvc@1.33.0: + optional: true + lightningcss-win32-x64-msvc@1.32.0: optional: true + lightningcss-win32-x64-msvc@1.33.0: + optional: true + lightningcss@1.32.0: dependencies: detect-libc: 2.1.2 @@ -12550,6 +12647,22 @@ snapshots: lightningcss-win32-arm64-msvc: 1.32.0 lightningcss-win32-x64-msvc: 1.32.0 + lightningcss@1.33.0: + dependencies: + detect-libc: 2.1.2 + optionalDependencies: + lightningcss-android-arm64: 1.33.0 + lightningcss-darwin-arm64: 1.33.0 + lightningcss-darwin-x64: 1.33.0 + lightningcss-freebsd-x64: 1.33.0 + lightningcss-linux-arm-gnueabihf: 1.33.0 + lightningcss-linux-arm64-gnu: 1.33.0 + lightningcss-linux-arm64-musl: 1.33.0 + lightningcss-linux-x64-gnu: 1.33.0 + lightningcss-linux-x64-musl: 1.33.0 + lightningcss-win32-arm64-msvc: 1.33.0 + lightningcss-win32-x64-msvc: 1.33.0 + locate-path@3.0.0: dependencies: p-locate: 3.0.0 @@ -12604,9 +12717,9 @@ snapshots: lru.min@1.1.5: {} - lucide-react@1.56.0(@types/react@19.3.0)(react@19.3.0): + lucide-react@1.59.0(@types/react@19.3.0)(react@19.3.1): dependencies: - react: 19.3.0 + react: 19.3.1 optionalDependencies: '@types/react': 19.3.0 @@ -12755,16 +12868,16 @@ snapshots: negotiator@1.0.0: {} - next@16.3.8(@babel/core@7.29.7(supports-color@7.2.0))(@opentelemetry/api@1.9.1)(@playwright/test@1.64.0)(@types/node@26.6.5)(react-dom@19.3.0(react@19.3.0))(react@19.3.0): + next@16.3.8(@babel/core@7.29.7(supports-color@7.2.0))(@opentelemetry/api@1.9.1)(@playwright/test@1.64.1)(@types/node@26.6.5)(react-dom@19.3.1(react@19.3.1))(react@19.3.1): dependencies: '@next/env': 16.3.8 '@swc/helpers': 0.5.23 baseline-browser-mapping: 2.11.26 caniuse-lite: 1.0.30001814 postcss: 8.5.23 - react: 19.3.0 - react-dom: 19.3.0(react@19.3.0) - styled-jsx: 5.1.6(@babel/core@7.29.7(supports-color@7.2.0))(react@19.3.0) + react: 19.3.1 + react-dom: 19.3.1(react@19.3.1) + styled-jsx: 5.1.6(@babel/core@7.29.7(supports-color@7.2.0))(react@19.3.1) optionalDependencies: '@next/swc-darwin-arm64': 16.3.8 '@next/swc-darwin-x64': 16.3.8 @@ -12775,7 +12888,7 @@ snapshots: '@next/swc-win32-arm64-msvc': 16.3.8 '@next/swc-win32-x64-msvc': 16.3.8 '@opentelemetry/api': 1.9.1 - '@playwright/test': 1.64.0 + '@playwright/test': 1.64.1 sharp: 0.35.5(@types/node@26.6.5) transitivePeerDependencies: - '@babel/core' @@ -12797,7 +12910,7 @@ snapshots: node-releases@2.0.58: {} - nodemailer@10.1.0: {} + nodemailer@10.1.7: {} non-error@0.1.0: {} @@ -13058,10 +13171,10 @@ snapshots: performance-now@2.1.0: optional: true - pg-boss@12.37.1(@opentelemetry/api@1.9.1): + pg-boss@12.38.0(@opentelemetry/api@1.9.1): dependencies: '@opentelemetry/api': 1.9.1 - cron-parser: 5.10.1 + cron-parser: 5.10.2 pg: 8.23.1 rrule-temporal: 2.2.8 serialize-error: 13.0.2 @@ -13121,11 +13234,11 @@ snapshots: dependencies: find-up: 3.0.0 - playwright-core@1.64.0: {} + playwright-core@1.64.1: {} - playwright@1.64.0: + playwright@1.64.1: dependencies: - playwright-core: 1.64.0 + playwright-core: 1.64.1 pngjs@5.0.0: {} @@ -13182,12 +13295,12 @@ snapshots: dependencies: parse-ms: 4.0.0 - prisma@7.10.0(@types/node@26.6.5)(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(magicast@0.5.5)(react-dom@19.3.0(react@19.3.0))(react@19.3.0)(typescript@6.0.3): + prisma@7.10.1(@types/node@26.6.5)(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(magicast@0.5.5)(react-dom@19.3.1(react@19.3.1))(react@19.3.1)(typescript@6.0.3): dependencies: - '@prisma/config': 7.10.0(magicast@0.5.5) + '@prisma/config': 7.10.1(magicast@0.5.5) '@prisma/dev': 0.24.17(typescript@6.0.3) - '@prisma/engines': 7.10.0 - '@prisma/studio-core': 0.33.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@prisma/engines': 7.10.1 + '@prisma/studio-core': 0.33.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) mysql2: 3.24.4(@types/node@26.6.5) postgres: 3.4.7 optionalDependencies: @@ -13279,65 +13392,65 @@ snapshots: queue-microtask@1.2.3: {} - radix-ui@1.8.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0): + radix-ui@1.8.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1): dependencies: '@radix-ui/primitive': 1.2.0 - '@radix-ui/react-accessible-icon': 1.2.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - '@radix-ui/react-accordion': 1.3.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - '@radix-ui/react-alert-dialog': 1.2.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - '@radix-ui/react-arrow': 1.1.17(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - '@radix-ui/react-aspect-ratio': 1.2.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - '@radix-ui/react-avatar': 1.3.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - '@radix-ui/react-checkbox': 1.4.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - '@radix-ui/react-collapsible': 1.2.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - '@radix-ui/react-collection': 1.2.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.3.0)(react@19.3.0) - '@radix-ui/react-context': 1.3.0(@types/react@19.3.0)(react@19.3.0) - '@radix-ui/react-context-menu': 2.4.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - '@radix-ui/react-dialog': 1.3.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - '@radix-ui/react-direction': 1.2.0(@types/react@19.3.0)(react@19.3.0) - '@radix-ui/react-dismissable-layer': 1.2.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - '@radix-ui/react-dropdown-menu': 2.2.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - '@radix-ui/react-focus-guards': 1.2.0(@types/react@19.3.0)(react@19.3.0) - '@radix-ui/react-focus-scope': 1.3.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - '@radix-ui/react-form': 0.3.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - '@radix-ui/react-hover-card': 1.2.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - '@radix-ui/react-label': 2.1.17(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - '@radix-ui/react-menu': 2.2.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - '@radix-ui/react-menubar': 1.2.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - '@radix-ui/react-navigation-menu': 1.4.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - '@radix-ui/react-one-time-password-field': 0.2.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - '@radix-ui/react-password-toggle-field': 0.2.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - '@radix-ui/react-popover': 1.3.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - '@radix-ui/react-popper': 1.4.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - '@radix-ui/react-portal': 1.2.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - '@radix-ui/react-presence': 1.1.11(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - '@radix-ui/react-primitive': 2.2.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - '@radix-ui/react-progress': 1.2.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - '@radix-ui/react-radio-group': 1.5.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - '@radix-ui/react-roving-focus': 1.2.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - '@radix-ui/react-scroll-area': 1.4.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - '@radix-ui/react-select': 2.4.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - '@radix-ui/react-separator': 1.2.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - '@radix-ui/react-slider': 1.6.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - '@radix-ui/react-slot': 1.5.0(@types/react@19.3.0)(react@19.3.0) - '@radix-ui/react-switch': 1.4.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - '@radix-ui/react-tabs': 1.2.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - '@radix-ui/react-toast': 1.3.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - '@radix-ui/react-toggle': 1.2.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - '@radix-ui/react-toggle-group': 1.2.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - '@radix-ui/react-toolbar': 1.2.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - '@radix-ui/react-tooltip': 1.4.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - '@radix-ui/react-use-callback-ref': 1.1.5(@types/react@19.3.0)(react@19.3.0) - '@radix-ui/react-use-controllable-state': 1.3.0(@types/react@19.3.0)(react@19.3.0) - '@radix-ui/react-use-effect-event': 0.0.5(@types/react@19.3.0)(react@19.3.0) - '@radix-ui/react-use-escape-keydown': 1.1.6(@types/react@19.3.0)(react@19.3.0) - '@radix-ui/react-use-is-hydrated': 0.1.3(@types/react@19.3.0)(react@19.3.0) - '@radix-ui/react-use-layout-effect': 1.1.4(@types/react@19.3.0)(react@19.3.0) - '@radix-ui/react-use-size': 1.1.5(@types/react@19.3.0)(react@19.3.0) - '@radix-ui/react-visually-hidden': 1.2.13(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - react: 19.3.0 - react-dom: 19.3.0(react@19.3.0) + '@radix-ui/react-accessible-icon': 1.2.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + '@radix-ui/react-accordion': 1.3.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + '@radix-ui/react-alert-dialog': 1.2.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + '@radix-ui/react-arrow': 1.1.17(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + '@radix-ui/react-aspect-ratio': 1.2.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + '@radix-ui/react-avatar': 1.3.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + '@radix-ui/react-checkbox': 1.4.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + '@radix-ui/react-collapsible': 1.2.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + '@radix-ui/react-collection': 1.2.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.3.0)(react@19.3.1) + '@radix-ui/react-context': 1.3.0(@types/react@19.3.0)(react@19.3.1) + '@radix-ui/react-context-menu': 2.4.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + '@radix-ui/react-dialog': 1.3.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + '@radix-ui/react-direction': 1.2.0(@types/react@19.3.0)(react@19.3.1) + '@radix-ui/react-dismissable-layer': 1.2.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + '@radix-ui/react-dropdown-menu': 2.2.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + '@radix-ui/react-focus-guards': 1.2.0(@types/react@19.3.0)(react@19.3.1) + '@radix-ui/react-focus-scope': 1.3.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + '@radix-ui/react-form': 0.3.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + '@radix-ui/react-hover-card': 1.2.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + '@radix-ui/react-label': 2.1.17(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + '@radix-ui/react-menu': 2.2.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + '@radix-ui/react-menubar': 1.2.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + '@radix-ui/react-navigation-menu': 1.4.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + '@radix-ui/react-one-time-password-field': 0.2.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + '@radix-ui/react-password-toggle-field': 0.2.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + '@radix-ui/react-popover': 1.3.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + '@radix-ui/react-popper': 1.4.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + '@radix-ui/react-portal': 1.2.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + '@radix-ui/react-presence': 1.1.11(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + '@radix-ui/react-primitive': 2.2.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + '@radix-ui/react-progress': 1.2.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + '@radix-ui/react-radio-group': 1.5.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + '@radix-ui/react-roving-focus': 1.2.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + '@radix-ui/react-scroll-area': 1.4.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + '@radix-ui/react-select': 2.4.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + '@radix-ui/react-separator': 1.2.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + '@radix-ui/react-slider': 1.6.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + '@radix-ui/react-slot': 1.5.0(@types/react@19.3.0)(react@19.3.1) + '@radix-ui/react-switch': 1.4.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + '@radix-ui/react-tabs': 1.2.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + '@radix-ui/react-toast': 1.3.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + '@radix-ui/react-toggle': 1.2.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + '@radix-ui/react-toggle-group': 1.2.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + '@radix-ui/react-toolbar': 1.2.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + '@radix-ui/react-tooltip': 1.4.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + '@radix-ui/react-use-callback-ref': 1.1.5(@types/react@19.3.0)(react@19.3.1) + '@radix-ui/react-use-controllable-state': 1.3.0(@types/react@19.3.0)(react@19.3.1) + '@radix-ui/react-use-effect-event': 0.0.5(@types/react@19.3.0)(react@19.3.1) + '@radix-ui/react-use-escape-keydown': 1.1.6(@types/react@19.3.0)(react@19.3.1) + '@radix-ui/react-use-is-hydrated': 0.1.3(@types/react@19.3.0)(react@19.3.1) + '@radix-ui/react-use-layout-effect': 1.1.4(@types/react@19.3.0)(react@19.3.1) + '@radix-ui/react-use-size': 1.1.5(@types/react@19.3.0)(react@19.3.1) + '@radix-ui/react-visually-hidden': 1.2.13(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + react: 19.3.1 + react-dom: 19.3.1(react@19.3.1) optionalDependencies: '@types/react': 19.3.0 '@types/react-dom': 19.3.0(@types/react@19.3.0) @@ -13361,58 +13474,58 @@ snapshots: defu: 6.1.7 destr: 2.0.5 - react-day-picker@10.0.2(@types/react@19.3.0)(react@19.3.0): + react-day-picker@10.0.2(@types/react@19.3.0)(react@19.3.1): dependencies: '@date-fns/tz': 1.5.0 date-fns: 4.4.0 - react: 19.3.0 + react: 19.3.1 optionalDependencies: '@types/react': 19.3.0 - react-dom@19.3.0(react@19.3.0): + react-dom@19.3.1(react@19.3.1): dependencies: - react: 19.3.0 + react: 19.3.1 scheduler: 0.28.0 react-is@16.13.1: {} - react-redux@9.2.0(@types/react@19.3.0)(react@19.3.0)(redux@5.0.1): + react-redux@9.2.0(@types/react@19.3.0)(react@19.3.1)(redux@5.0.1): dependencies: '@types/use-sync-external-store': 0.0.6 - react: 19.3.0 - use-sync-external-store: 1.6.0(react@19.3.0) + react: 19.3.1 + use-sync-external-store: 1.6.0(react@19.3.1) optionalDependencies: '@types/react': 19.3.0 redux: 5.0.1 - react-remove-scroll-bar@2.3.8(@types/react@19.3.0)(react@19.3.0): + react-remove-scroll-bar@2.3.8(@types/react@19.3.0)(react@19.3.1): dependencies: - react: 19.3.0 - react-style-singleton: 2.2.3(@types/react@19.3.0)(react@19.3.0) + react: 19.3.1 + react-style-singleton: 2.2.3(@types/react@19.3.0)(react@19.3.1) tslib: 2.8.1 optionalDependencies: '@types/react': 19.3.0 - react-remove-scroll@2.7.2(@types/react@19.3.0)(react@19.3.0): + react-remove-scroll@2.7.2(@types/react@19.3.0)(react@19.3.1): dependencies: - react: 19.3.0 - react-remove-scroll-bar: 2.3.8(@types/react@19.3.0)(react@19.3.0) - react-style-singleton: 2.2.3(@types/react@19.3.0)(react@19.3.0) + react: 19.3.1 + react-remove-scroll-bar: 2.3.8(@types/react@19.3.0)(react@19.3.1) + react-style-singleton: 2.2.3(@types/react@19.3.0)(react@19.3.1) tslib: 2.8.1 - use-callback-ref: 1.3.3(@types/react@19.3.0)(react@19.3.0) - use-sidecar: 1.1.3(@types/react@19.3.0)(react@19.3.0) + use-callback-ref: 1.3.3(@types/react@19.3.0)(react@19.3.1) + use-sidecar: 1.1.3(@types/react@19.3.0)(react@19.3.1) optionalDependencies: '@types/react': 19.3.0 - react-style-singleton@2.2.3(@types/react@19.3.0)(react@19.3.0): + react-style-singleton@2.2.3(@types/react@19.3.0)(react@19.3.1): dependencies: get-nonce: 1.0.1 - react: 19.3.0 + react: 19.3.1 tslib: 2.8.1 optionalDependencies: '@types/react': 19.3.0 - react@19.3.0: {} + react@19.3.1: {} readable-stream@2.3.8: dependencies: @@ -13452,21 +13565,21 @@ snapshots: tiny-invariant: 1.3.3 tslib: 2.8.1 - recharts@3.10.1(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react-is@16.13.1)(react@19.3.0)(redux@5.0.1): + recharts@3.10.1(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react-is@16.13.1)(react@19.3.1)(redux@5.0.1): dependencies: - '@reduxjs/toolkit': 2.11.2(react-redux@9.2.0(@types/react@19.3.0)(react@19.3.0)(redux@5.0.1))(react@19.3.0) + '@reduxjs/toolkit': 2.11.2(react-redux@9.2.0(@types/react@19.3.0)(react@19.3.1)(redux@5.0.1))(react@19.3.1) clsx: 2.1.1 decimal.js-light: 2.5.1 es-toolkit: 1.45.1 eventemitter3: 5.0.4 immer: 11.1.15 - react: 19.3.0 - react-dom: 19.3.0(react@19.3.0) + react: 19.3.1 + react-dom: 19.3.1(react@19.3.1) react-is: 16.13.1 - react-redux: 9.2.0(@types/react@19.3.0)(react@19.3.0)(redux@5.0.1) + react-redux: 9.2.0(@types/react@19.3.0)(react@19.3.1)(redux@5.0.1) reselect: 5.2.0 tiny-invariant: 1.3.3 - use-sync-external-store: 1.6.0(react@19.3.0) + use-sync-external-store: 1.6.0(react@19.3.1) victory-vendor: 37.3.6 transitivePeerDependencies: - '@types/react' @@ -13693,15 +13806,15 @@ snapshots: setprototypeof@1.2.0: {} - shadcn@4.21.4(@cfworker/json-schema@4.1.1)(supports-color@7.2.0): + shadcn@4.22.0(@cfworker/json-schema@4.1.1)(supports-color@7.2.0): dependencies: '@babel/core': 7.29.7(supports-color@7.2.0) '@babel/parser': 7.29.9 '@babel/plugin-transform-typescript': 7.29.9(@babel/core@7.29.7(supports-color@7.2.0))(supports-color@7.2.0) '@babel/preset-typescript': 7.29.7(@babel/core@7.29.7(supports-color@7.2.0))(supports-color@7.2.0) '@dotenvx/dotenvx': 1.75.1 - '@modelcontextprotocol/sdk': 1.32.1(@cfworker/json-schema@4.1.1)(supports-color@7.2.0)(zod@3.25.76) - '@shadcn/registry': 0.1.3(supports-color@7.2.0) + '@modelcontextprotocol/sdk': 1.32.1(@cfworker/json-schema@4.1.1)(supports-color@7.2.0)(zod@4.6.7) + '@shadcn/registry': 0.2.0(supports-color@7.2.0) '@types/validate-npm-package-name': 4.0.2 browserslist: 4.29.3 cn: 0.2.6 @@ -13726,8 +13839,7 @@ snapshots: tsconfig-paths: 4.2.0 undici: 7.30.0 validate-npm-package-name: 7.0.2 - zod: 3.25.76 - zod-to-json-schema: 3.25.2(zod@3.25.76) + zod: 4.6.7 transitivePeerDependencies: - '@cfworker/json-schema' - babel-plugin-macros @@ -13822,10 +13934,10 @@ snapshots: ip-address: 10.7.3 smart-buffer: 4.2.0 - sonner@2.0.8(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0): + sonner@2.0.8(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1): dependencies: - react: 19.3.0 - react-dom: 19.3.0(react@19.3.0) + react: 19.3.1 + react-dom: 19.3.1(react@19.3.1) optionalDependencies: '@types/react': 19.3.0 @@ -13990,10 +14102,10 @@ snapshots: strip-json-comments@5.0.3: {} - styled-jsx@5.1.6(@babel/core@7.29.7(supports-color@7.2.0))(react@19.3.0): + styled-jsx@5.1.6(@babel/core@7.29.7(supports-color@7.2.0))(react@19.3.1): dependencies: client-only: 0.0.1 - react: 19.3.0 + react: 19.3.1 optionalDependencies: '@babel/core': 7.29.7(supports-color@7.2.0) @@ -14095,7 +14207,7 @@ snapshots: ssh-remote-port-forward: 1.0.4 tar-fs: 3.1.3 tmp: 0.2.7 - undici: 8.11.2 + undici: 8.12.3 transitivePeerDependencies: - bare-abort-controller - bare-buffer @@ -14277,7 +14389,7 @@ snapshots: undici@7.30.0: {} - undici@8.11.2: {} + undici@8.12.3: {} unicorn-magic@0.3.0: {} @@ -14325,24 +14437,24 @@ snapshots: dependencies: punycode: 2.3.1 - use-callback-ref@1.3.3(@types/react@19.3.0)(react@19.3.0): + use-callback-ref@1.3.3(@types/react@19.3.0)(react@19.3.1): dependencies: - react: 19.3.0 + react: 19.3.1 tslib: 2.8.1 optionalDependencies: '@types/react': 19.3.0 - use-sidecar@1.1.3(@types/react@19.3.0)(react@19.3.0): + use-sidecar@1.1.3(@types/react@19.3.0)(react@19.3.1): dependencies: detect-node-es: 1.1.0 - react: 19.3.0 + react: 19.3.1 tslib: 2.8.1 optionalDependencies: '@types/react': 19.3.0 - use-sync-external-store@1.6.0(react@19.3.0): + use-sync-external-store@1.6.0(react@19.3.1): dependencies: - react: 19.3.0 + react: 19.3.1 util-deprecate@1.0.2: {} @@ -14384,9 +14496,9 @@ snapshots: d3-time: 3.1.0 d3-timer: 3.0.1 - vite@8.1.5(@types/node@26.6.5)(jiti@2.7.0)(yaml@2.9.1): + vite@8.1.6(@types/node@26.6.5)(jiti@2.7.0)(yaml@2.9.1): dependencies: - lightningcss: 1.32.0 + lightningcss: 1.33.0 picomatch: 4.0.7 postcss: 8.5.29 rolldown: 1.1.5 @@ -14397,10 +14509,10 @@ snapshots: jiti: 2.7.0 yaml: 2.9.1 - vitest@5.0.4(@opentelemetry/api@1.9.1)(@types/node@26.6.5)(@vitest/coverage-v8@5.0.4)(msw@2.14.6(@types/node@26.6.5)(typescript@6.0.3))(vite@8.1.5(@types/node@26.6.5)(jiti@2.7.0)(yaml@2.9.1)): + vitest@5.0.5(@opentelemetry/api@1.9.1)(@types/node@26.6.5)(@vitest/coverage-v8@5.0.5)(msw@2.14.6(@types/node@26.6.5)(typescript@6.0.3))(vite@8.1.6(@types/node@26.6.5)(jiti@2.7.0)(yaml@2.9.1)): dependencies: '@types/chai': 5.2.3 - '@vitest/mocker': 5.0.4(msw@2.14.6(@types/node@26.6.5)(typescript@6.0.3))(vite@8.1.5(@types/node@26.6.5)(jiti@2.7.0)(yaml@2.9.1)) + '@vitest/mocker': 5.0.5(msw@2.14.6(@types/node@26.6.5)(typescript@6.0.3))(vite@8.1.6(@types/node@26.6.5)(jiti@2.7.0)(yaml@2.9.1)) chai: 6.3.0 es-module-lexer: 2.3.2 expect-type: 1.4.0 @@ -14411,12 +14523,12 @@ snapshots: tinybench: 6.2.2 tinyexec: 1.3.1 tinyglobby: 0.2.17 - vite: 8.1.5(@types/node@26.6.5)(jiti@2.7.0)(yaml@2.9.1) + vite: 8.1.6(@types/node@26.6.5)(jiti@2.7.0)(yaml@2.9.1) why-is-node-running: 3.2.1 optionalDependencies: '@opentelemetry/api': 1.9.1 '@types/node': 26.6.5 - '@vitest/coverage-v8': 5.0.4(vitest@5.0.4) + '@vitest/coverage-v8': 5.0.5(vitest@5.0.5) transitivePeerDependencies: - msw @@ -14612,10 +14724,6 @@ snapshots: dependencies: zod: 4.6.7 - zod-to-json-schema@3.25.2(zod@3.25.76): - dependencies: - zod: 3.25.76 - zod-to-json-schema@3.25.2(zod@4.6.7): dependencies: zod: 4.6.7 @@ -14624,8 +14732,6 @@ snapshots: dependencies: zod: 4.6.7 - zod@3.25.76: {} - zod@4.6.7: {} zxcvbn-typescript@5.0.1: {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 5f13e535e..5d93cae2c 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -103,6 +103,13 @@ overrides: "brace-expansion@>=4.0.0 <5.0.12": "^5.0.12" "undici@>=7.0.0 <7.29.1": "^7.29.1" + # GHSA-587h-7wmj-h5pm (HIGH), GHSA-rq7h-c2jc-7f22, GHSA-r825-w8mm-5pxw, + # GHSA-698v-4r3x-3vcv and GHSA-9jrq-w75r-8gcw: dev-server file serving, + # cross-origin middleware and the bundled launch-editor. Vite arrives only + # under vitest, never in the image; vitest asks for ^8, so a floor on the + # same major moves it and nothing else. + "vite@>=8.0.0 <8.1.6": "^8.1.6" + allowBuilds: "@prisma/engines": true prisma: true diff --git a/scripts/__tests__/release-workflows.test.ts b/scripts/__tests__/release-workflows.test.ts index 79e27408a..c402102b8 100644 --- a/scripts/__tests__/release-workflows.test.ts +++ b/scripts/__tests__/release-workflows.test.ts @@ -236,6 +236,7 @@ describe("release container inputs", () => { const dockerfile = readRepoFile("Dockerfile"); const packageJson = JSON.parse(readRepoFile("package.json")) as { packageManager: string; + dependencies: Record; }; expect(packageJson.packageManager).toBe("pnpm@11.15.1"); expect(dockerfile).not.toContain("pnpm@latest"); @@ -249,7 +250,15 @@ describe("release container inputs", () => { expect(dockerfile).toContain( "ln -sfn /opt/prisma-cli/node_modules/.bin/tsx /usr/local/bin/healthlog-tsx", ); - expect(dockerfile).toContain("dotenv@18.0.6"); + // The image's npm-installed Prisma CLI, its engines and dotenv are a second + // install of packages the app tree already declares; they move together or + // the image runs a CLI the tests never did. Drift is how the dotenv pin + // here trailed the app's by a patch. + const floor = (name: string) => + packageJson.dependencies[name]!.replace(/^[\^~]/, ""); + expect(dockerfile).toContain(`prisma@${floor("prisma")} `); + expect(dockerfile).toContain(`@prisma/engines@${floor("prisma")} `); + expect(dockerfile).toContain(`dotenv@${floor("dotenv")} `); expect(dockerfile).toContain( "ln -sfn /opt/prisma-cli/node_modules/dotenv /app/node_modules/dotenv", ); diff --git a/src/__tests__/openapi-route-coverage-guard.test.ts b/src/__tests__/openapi-route-coverage-guard.test.ts index a1210c215..cf7bf490b 100644 --- a/src/__tests__/openapi-route-coverage-guard.test.ts +++ b/src/__tests__/openapi-route-coverage-guard.test.ts @@ -216,11 +216,13 @@ const UNPUBLISHED: Readonly> = { "/api/admin/backup/test": { kind: "adminConsole", methods: ["POST"] }, "/api/admin/backups": { kind: "adminConsole", methods: ["GET"] }, "/api/admin/backups/run": { kind: "adminConsole", methods: ["POST"] }, - "/api/admin/backups/upload": { kind: "adminConsole", methods: ["POST"] }, - // `/api/admin/backups/{id}/download` and `/api/admin/backups/{id}/restore` - // are published rather than exempted: both open the encrypted stored copy, - // and the refusal they answer when it will not open is a promise an operator - // needs to be able to look up. See `src/lib/openapi/routes/admin-backups.ts`. + // `/api/admin/backups/{id}/download`, `/api/admin/backups/{id}/restore` and + // `/api/admin/backups/upload` are published rather than exempted: the first + // two open the encrypted stored copy, and the refusal they answer when it + // will not open is a promise an operator needs to be able to look up; the + // upload refuses a file for reasons (a missing key, an unknown owner, a + // newer schema) an operator has to be able to look up the same way. See + // `src/lib/openapi/routes/admin-backups.ts`. "/api/admin/backups/{id}/summary": { kind: "adminConsole", methods: ["GET"] }, "/api/admin/central-codex": { kind: "adminConsole", diff --git a/src/__tests__/self-hosted-font-guard.test.ts b/src/__tests__/self-hosted-font-guard.test.ts new file mode 100644 index 000000000..5ee54cf6a --- /dev/null +++ b/src/__tests__/self-hosted-font-guard.test.ts @@ -0,0 +1,58 @@ +/** + * The build fetches no font from the network. + * + * `next/font/google` downloads its files from fonts.gstatic.com while the app + * builds, and that download failing is what broke image builds. Inter is + * committed under `src/app/fonts/inter/` instead, under its licence, and + * declared through `next/font/local`. This guard keeps it that way: no source + * file imports the Google loader, every file the local declarations name is + * in the tree, and each subset carries the four weights the app was built + * with. + * + * Mutation check: importing `Inter` from the Google loader anywhere under + * `src/` fails the first case; deleting a `.woff2` or dropping a weight from a + * subset fails the second. + */ +import { existsSync } from "node:fs"; +import { dirname, join } from "node:path"; + +import { describe, expect, it } from "vitest"; + +import { readSource, readSourceFolder } from "./helpers/source-files"; + +const ROOT = process.cwd(); +const FONT_MODULE = "src/app/fonts/inter.ts"; + +describe("self-hosted fonts", () => { + it("imports no font loader that downloads at build time", () => { + const importing = readSourceFolder("src", { + floor: 3000, + include: (rel) => !rel.startsWith("generated/"), + extensions: [".ts", ".tsx", ".mts", ".js", ".mjs"], + }) + .filter(([, text]) => /from\s+["']next\/font\/google["']/.test(text)) + .map(([rel]) => rel); + expect(importing).toEqual([]); + }); + + it("ships every file the declarations name, with its licence and four weights", () => { + const source = readSource(FONT_MODULE, ["next/font/local", "localFont("]); + const entries = [ + ...source.matchAll(/path:\s*"([^"]+)",\s*weight:\s*"(\d+)"/g), + ].map(([, path, weight]) => ({ path: path!, weight: weight! })); + expect(entries.length).toBe(28); + + const byFile = new Map(); + for (const { path, weight } of entries) { + byFile.set(path, [...(byFile.get(path) ?? []), weight]); + } + expect(byFile.size).toBe(7); + for (const [path, weights] of byFile) { + expect(existsSync(join(ROOT, dirname(FONT_MODULE), path)), path).toBe( + true, + ); + expect(weights, path).toEqual(["400", "500", "600", "700"]); + } + expect(existsSync(join(ROOT, "src/app/fonts/inter/OFL.txt"))).toBe(true); + }); +}); diff --git a/src/app/fonts/inter.ts b/src/app/fonts/inter.ts new file mode 100644 index 000000000..3816878bb --- /dev/null +++ b/src/app/fonts/inter.ts @@ -0,0 +1,179 @@ +/** + * Inter, self-hosted. The build fetches nothing from the network for it. + * + * These are the files `next/font/google` used to download at build time for + * `Inter({ subsets: ["latin"], weight: ["400", "500", "600", "700"] })`, + * byte for byte: Google serves Inter 4.001 as one variable file per script + * subset, the same file for every weight. That download failed often enough + * to break image builds, so the files live here under their licence + * (`./inter/OFL.txt`) and the declarations below rebuild the stylesheet + * Google answered: twenty-eight faces, the four weights for each of the seven + * subsets with Google's `unicode-range`s, in Google's order (where two ranges + * overlap the face declared later wins, so the order is part of the result), + * `font-display: swap`, and only the Latin file preloaded. + * + * `next/font/local` emits one `unicode-range` per call, hence one call per + * subset, all declaring the family `Inter`. The fallback face and the + * `--font-sans` stack are in `globals.css`: the local loader would derive the + * Arial metric overrides from the font file, and they come out a little + * different from the ones next/font/google shipped, which are kept as they + * were. + * + * Imported by the root layout for its stylesheet only. + */ +import localFont from "next/font/local"; + +// `next/font/local` is evaluated at build time from the literal arguments of +// each call, so every option below has to be written out in place. +// The bindings are never read: importing this module is what attaches the +// faces, so each name carries the leading underscore of an intended unused. +const _interCyrillicExt = localFont({ + src: [ + { + path: "./inter/inter-cyrillic-ext.woff2", + weight: "400", + style: "normal", + }, + { + path: "./inter/inter-cyrillic-ext.woff2", + weight: "500", + style: "normal", + }, + { + path: "./inter/inter-cyrillic-ext.woff2", + weight: "600", + style: "normal", + }, + { + path: "./inter/inter-cyrillic-ext.woff2", + weight: "700", + style: "normal", + }, + ], + display: "swap", + preload: false, + adjustFontFallback: false, + declarations: [ + { prop: "font-family", value: "Inter" }, + { + prop: "unicode-range", + value: + "U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F", + }, + ], +}); + +const _interCyrillic = localFont({ + src: [ + { path: "./inter/inter-cyrillic.woff2", weight: "400", style: "normal" }, + { path: "./inter/inter-cyrillic.woff2", weight: "500", style: "normal" }, + { path: "./inter/inter-cyrillic.woff2", weight: "600", style: "normal" }, + { path: "./inter/inter-cyrillic.woff2", weight: "700", style: "normal" }, + ], + display: "swap", + preload: false, + adjustFontFallback: false, + declarations: [ + { prop: "font-family", value: "Inter" }, + { + prop: "unicode-range", + value: "U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116", + }, + ], +}); + +const _interGreekExt = localFont({ + src: [ + { path: "./inter/inter-greek-ext.woff2", weight: "400", style: "normal" }, + { path: "./inter/inter-greek-ext.woff2", weight: "500", style: "normal" }, + { path: "./inter/inter-greek-ext.woff2", weight: "600", style: "normal" }, + { path: "./inter/inter-greek-ext.woff2", weight: "700", style: "normal" }, + ], + display: "swap", + preload: false, + adjustFontFallback: false, + declarations: [ + { prop: "font-family", value: "Inter" }, + { prop: "unicode-range", value: "U+1F00-1FFF" }, + ], +}); + +const _interGreek = localFont({ + src: [ + { path: "./inter/inter-greek.woff2", weight: "400", style: "normal" }, + { path: "./inter/inter-greek.woff2", weight: "500", style: "normal" }, + { path: "./inter/inter-greek.woff2", weight: "600", style: "normal" }, + { path: "./inter/inter-greek.woff2", weight: "700", style: "normal" }, + ], + display: "swap", + preload: false, + adjustFontFallback: false, + declarations: [ + { prop: "font-family", value: "Inter" }, + { + prop: "unicode-range", + value: + "U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF", + }, + ], +}); + +const _interVietnamese = localFont({ + src: [ + { path: "./inter/inter-vietnamese.woff2", weight: "400", style: "normal" }, + { path: "./inter/inter-vietnamese.woff2", weight: "500", style: "normal" }, + { path: "./inter/inter-vietnamese.woff2", weight: "600", style: "normal" }, + { path: "./inter/inter-vietnamese.woff2", weight: "700", style: "normal" }, + ], + display: "swap", + preload: false, + adjustFontFallback: false, + declarations: [ + { prop: "font-family", value: "Inter" }, + { + prop: "unicode-range", + value: + "U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB", + }, + ], +}); + +const _interLatinExt = localFont({ + src: [ + { path: "./inter/inter-latin-ext.woff2", weight: "400", style: "normal" }, + { path: "./inter/inter-latin-ext.woff2", weight: "500", style: "normal" }, + { path: "./inter/inter-latin-ext.woff2", weight: "600", style: "normal" }, + { path: "./inter/inter-latin-ext.woff2", weight: "700", style: "normal" }, + ], + display: "swap", + preload: false, + adjustFontFallback: false, + declarations: [ + { prop: "font-family", value: "Inter" }, + { + prop: "unicode-range", + value: + "U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C4, U+2113, U+2C60-2C7F, U+A720-A7FF", + }, + ], +}); + +const _interLatin = localFont({ + src: [ + { path: "./inter/inter-latin.woff2", weight: "400", style: "normal" }, + { path: "./inter/inter-latin.woff2", weight: "500", style: "normal" }, + { path: "./inter/inter-latin.woff2", weight: "600", style: "normal" }, + { path: "./inter/inter-latin.woff2", weight: "700", style: "normal" }, + ], + display: "swap", + preload: true, + adjustFontFallback: false, + declarations: [ + { prop: "font-family", value: "Inter" }, + { + prop: "unicode-range", + value: + "U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD", + }, + ], +}); diff --git a/src/app/fonts/inter/OFL.txt b/src/app/fonts/inter/OFL.txt new file mode 100644 index 000000000..21f6aff96 --- /dev/null +++ b/src/app/fonts/inter/OFL.txt @@ -0,0 +1,93 @@ +Copyright 2020 The Inter Project Authors (https://github.com/rsms/inter) + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +https://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/src/app/fonts/inter/inter-cyrillic-ext.woff2 b/src/app/fonts/inter/inter-cyrillic-ext.woff2 new file mode 100644 index 000000000..2cd45edf4 Binary files /dev/null and b/src/app/fonts/inter/inter-cyrillic-ext.woff2 differ diff --git a/src/app/fonts/inter/inter-cyrillic.woff2 b/src/app/fonts/inter/inter-cyrillic.woff2 new file mode 100644 index 000000000..bc0e0ab26 Binary files /dev/null and b/src/app/fonts/inter/inter-cyrillic.woff2 differ diff --git a/src/app/fonts/inter/inter-greek-ext.woff2 b/src/app/fonts/inter/inter-greek-ext.woff2 new file mode 100644 index 000000000..b6dd1facb Binary files /dev/null and b/src/app/fonts/inter/inter-greek-ext.woff2 differ diff --git a/src/app/fonts/inter/inter-greek.woff2 b/src/app/fonts/inter/inter-greek.woff2 new file mode 100644 index 000000000..9c71603a5 Binary files /dev/null and b/src/app/fonts/inter/inter-greek.woff2 differ diff --git a/src/app/fonts/inter/inter-latin-ext.woff2 b/src/app/fonts/inter/inter-latin-ext.woff2 new file mode 100644 index 000000000..57da6f8d4 Binary files /dev/null and b/src/app/fonts/inter/inter-latin-ext.woff2 differ diff --git a/src/app/fonts/inter/inter-latin.woff2 b/src/app/fonts/inter/inter-latin.woff2 new file mode 100644 index 000000000..91dc3e852 Binary files /dev/null and b/src/app/fonts/inter/inter-latin.woff2 differ diff --git a/src/app/fonts/inter/inter-vietnamese.woff2 b/src/app/fonts/inter/inter-vietnamese.woff2 new file mode 100644 index 000000000..072229b87 Binary files /dev/null and b/src/app/fonts/inter/inter-vietnamese.woff2 differ diff --git a/src/app/globals.css b/src/app/globals.css index ef7ed6d0e..651538b37 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -30,6 +30,28 @@ @source not "../../.planning"; @source not "../../docs"; +/* + * Inter's fallback and the font-sans stack. The Inter faces themselves come + * from `./fonts/inter.ts`. This is what next/font/google generated for Inter + * when it still fetched the font at build time: Arial, scaled with the metric + * overrides it shipped for Inter, so text keeps its box while the web font + * loads. They are written out here because next/font/local would compute its + * own, slightly different overrides from the font file. + */ +@font-face { + font-family: "Inter Fallback"; + src: local("Arial"); + ascent-override: 90.44%; + descent-override: 22.52%; + line-gap-override: 0%; + size-adjust: 107.12%; +} + +/* On body, where the next/font variable class used to set it. */ +body { + --font-sans: "Inter", "Inter Fallback"; +} + @custom-variant dark (&:is(.dark *)); /* diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 71de7f39c..2e41e3b20 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -1,7 +1,9 @@ import type { Metadata, Viewport } from "next"; -import { Inter } from "next/font/google"; import { headers } from "next/headers"; import Script from "next/script"; +// Inter's faces, self-hosted; see the module. Before globals.css, where the +// font-sans stack that names them lives, as the next/font/google import was. +import "./fonts/inter"; import "./globals.css"; import { Providers } from "@/components/providers"; import { AuthShell } from "@/components/layout/auth-shell"; @@ -12,12 +14,6 @@ import { isKeyMismatch } from "@/lib/boot/key-mismatch-state"; import { THEME_BOOT_SCRIPT, THEME_COLOR } from "@/lib/pwa/theme-color"; import { KeyMismatchPage } from "./key-mismatch-page"; -const inter = Inter({ - variable: "--font-sans", - subsets: ["latin"], - weight: ["400", "500", "600", "700"], -}); - export const metadata: Metadata = { title: "HealthLog", description: @@ -136,7 +132,7 @@ export default async function RootLayout({ src={`/i18n/${initialLocale}.js?v=${process.env.NEXT_PUBLIC_I18N_CATALOG_VERSION || "dev"}`} /> - + {isKeyMismatch() ? ( // The boot key check refused this process: no app shell, no data // reads, one page that names the fix. diff --git a/src/components/insights/__tests__/health-score-card-geometry.test.tsx b/src/components/insights/__tests__/health-score-card-geometry.test.tsx index 500824677..a76aaba67 100644 --- a/src/components/insights/__tests__/health-score-card-geometry.test.tsx +++ b/src/components/insights/__tests__/health-score-card-geometry.test.tsx @@ -23,9 +23,10 @@ import { primeLocales } from "@/__tests__/helpers/prime-locales"; * REAL compiled stylesheet, lays it out in a browser at the real column * width, and reads the boxes back. * - * What it does NOT do is pretend to know the shipped typeface. The app loads - * Inter through `next/font/google` at build time; no font file is committed, - * so a headless browser here resolves the `font-sans` fallback chain instead. + * What it does NOT do is pretend to know the shipped typeface. The app's Inter + * faces are declared through `next/font/local` (`src/app/fonts/inter.ts`) and + * only exist in a Next build, so a headless browser here resolves the + * `font-sans` fallback chain instead. * Every assertion below is therefore written to be font-independent — a * relative comparison inside one run, or a structural property (does the row * push, does the label ellipsise, is the bar still there) that holds in any diff --git a/src/lib/jobs/__tests__/job-failures-retention.test.ts b/src/lib/jobs/__tests__/job-failures-retention.test.ts index 1e03effe1..6219eb253 100644 --- a/src/lib/jobs/__tests__/job-failures-retention.test.ts +++ b/src/lib/jobs/__tests__/job-failures-retention.test.ts @@ -18,7 +18,7 @@ describe("pg-boss failed-row ledger retention", () => { ) as { version: string }; const plans = readFileSync(join(PG_BOSS_ROOT, "dist/plans.js"), "utf8"); - expect(packageJson.version).toBe("12.37.1"); + expect(packageJson.version).toBe("12.38.0"); expect(plans).toContain("retention_seconds: FORTEEN_DAYS"); expect(plans).toContain("deletion_seconds: SEVEN_DAYS"); // 12.37 reads the clock through job_now(), which is pg_catalog.now() diff --git a/src/lib/openapi/__tests__/gate-refusal-publication.test.ts b/src/lib/openapi/__tests__/gate-refusal-publication.test.ts new file mode 100644 index 000000000..3a70c7c04 --- /dev/null +++ b/src/lib/openapi/__tests__/gate-refusal-publication.test.ts @@ -0,0 +1,134 @@ +/** + * The generic auth gate's 403, declared on every operation that passes it. + * + * `requireAuth` answers 403 `auth.scope.insufficient` to a narrow Bearer token + * on a route that names no scope it holds, and `requireAdmin` answers 403 + * `auth.admin.required` to a session that is not an administrator's. Both run + * before a route's own body, so every operation behind them can send the + * status; `withScopeRefusal` declares it on the assembled table. These checks + * hold the published document to that, and hold the helper to not trampling + * the 403s operations already publish for reasons of their own. + */ +import { describe, expect, it } from "vitest"; + +import { openApiPaths } from "../routes"; +import { + adminRefusalResponse, + errorEnvelope, + scopeRefusalResponse, + stdResponses, + withScopeRefusal, +} from "../routes/shared"; + +const METHODS = ["get", "put", "post", "patch", "delete"] as const; + +interface Op { + security?: unknown[]; + responses?: Record; +} + +function operations(): Array<{ path: string; method: string; op: Op }> { + const out: Array<{ path: string; method: string; op: Op }> = []; + for (const [path, item] of Object.entries(openApiPaths)) { + for (const method of METHODS) { + const op = (item as Record)[method]; + if (op?.responses) out.push({ path, method, op }); + } + } + return out; +} + +const URL_CREDENTIAL_OR_OWN_GATE = new Set([ + "/api/c/{token}/unlock", + "/api/notifications/action/{token}", + "/api/ingest/medication", +]); + +describe("the gate's 403 in the published contract", () => { + it("is declared, naming its code, on every operation behind the gate", () => { + const behind = operations().filter( + ({ path, op }) => + op.responses?.["401"] && + !(Array.isArray(op.security) && op.security.length === 0) && + !URL_CREDENTIAL_OR_OWN_GATE.has(path), + ); + expect(behind.length).toBeGreaterThan(500); + + const silent = behind.filter(({ path, op }) => { + const code = path.startsWith("/api/admin/") + ? "auth.admin.required" + : "auth.scope.insufficient"; + return !op.responses?.["403"]?.description?.includes(code); + }); + expect(silent.map(({ path, method }) => `${method} ${path}`)).toEqual([]); + }); + + it("is not declared on an operation that takes no credential", () => { + const open = operations().filter( + ({ op }) => Array.isArray(op.security) && op.security.length === 0, + ); + expect(open.length).toBeGreaterThan(0); + const claimed = open.filter(({ op }) => + op.responses?.["403"]?.description?.includes("auth.scope.insufficient"), + ); + expect(claimed.map(({ path, method }) => `${method} ${path}`)).toEqual([]); + }); + + it("keeps an operation's own 403 and its wording", () => { + const calendar = ( + openApiPaths["/api/cycle/calendar"] as unknown as { + get: Op; + } + ).get; + const description = calendar.responses?.["403"]?.description ?? ""; + expect(description).toContain("cycle.disabled"); + expect(description).toContain("auth.scope.insufficient"); + }); +}); + +describe("withScopeRefusal", () => { + const ownForbidden = { + description: "Module off. `meta.errorCode` = `module.disabled`.", + content: { "application/json": { schema: errorEnvelope } }, + }; + const table = withScopeRefusal({ + "/api/a": { + get: { responses: { "200": {}, ...stdResponses } }, + post: { responses: { "200": {}, ...stdResponses, "403": ownForbidden } }, + }, + "/api/b": { + get: { responses: { "200": {}, "403": ownForbidden, ...stdResponses } }, + }, + "/api/admin/x": { get: { responses: { "200": {}, ...stdResponses } } }, + "/api/open": { + get: { security: [], responses: { "200": {}, ...stdResponses } }, + }, + "/api/no-auth-declared": { get: { responses: { "200": {} } } }, + }) as unknown as Record>; + + it("adds the shared response where an operation has none", () => { + expect(table["/api/a"].get.responses?.["403"]).toBe(scopeRefusalResponse); + expect(table["/api/admin/x"].get.responses?.["403"]).toBe( + adminRefusalResponse, + ); + }); + + it("prefixes an own 403, once per shared object, and leaves its text", () => { + const a = table["/api/a"].post.responses?.["403"]; + const b = table["/api/b"].get.responses?.["403"]; + expect(a).toBe(b); + expect(a?.description?.startsWith("`meta.errorCode` = `auth.scope")).toBe( + true, + ); + expect(a?.description?.endsWith(ownForbidden.description)).toBe(true); + // The input object is not mutated. + expect(ownForbidden.description).not.toContain("auth.scope"); + }); + + it("leaves an operation that takes no credential or declares no 401 alone", () => { + expect(table["/api/open"].get.responses?.["403"]).toBeUndefined(); + expect( + table["/api/no-auth-declared"].get.responses?.["403"], + ).toBeUndefined(); + }); +}); diff --git a/src/lib/openapi/routes/admin-backups.ts b/src/lib/openapi/routes/admin-backups.ts index 673eb6912..686c2adf5 100644 --- a/src/lib/openapi/routes/admin-backups.ts +++ b/src/lib/openapi/routes/admin-backups.ts @@ -1,13 +1,13 @@ /** * OpenAPI route table — the backups-console routes that read a stored copy, and - * the restore job they start. + * the restore job they start, and the upload that stores a copy to restore. * * Part of the OpenAPI route table; aggregated in `./index.ts`. * * Most of `/api/admin/backups/*` is deliberately unpublished: it is a * cookie-only console, `requireAdmin()` refuses every Bearer caller, and a - * contract nobody can call against is a contract nobody reads. These two are - * here for the opposite reason. Both open the encrypted stored copy (pieces in + * contract nobody can call against is a contract nobody reads. The two readers + * are here for the opposite reason. Both open the encrypted stored copy (pieces in * `data_backup_chunks`, or one value in `data_backups.data` for a copy written * before v1.39.2), and both therefore have a refusal an operator meets on * the day a key rotation went one step too far — the case @@ -16,6 +16,11 @@ * the handler happens to answer, and a promise belongs somewhere it can be * quoted from. * + * The upload is here for the same reason: it refuses a file the operator has + * in hand for reasons they have to be able to look up (a key this server does + * not hold, an owner that does not exist here, a newer schema), and its answer + * is the copy id the restore takes. + * * The restore answers 202 and runs as a background job, so the two routes that * report on that job are published with it: a 202 without its status route is * half a contract. @@ -23,7 +28,12 @@ import { z } from "zod/v4"; import type { ZodOpenApiObject } from "zod-openapi"; -import { dataEnvelope, errorEnvelope, idempotencyKeyParameter } from "./shared"; +import { + dataEnvelope, + errorEnvelope, + idempotencyKeyParameter, + stdResponses, +} from "./shared"; const restoreRequest = z .object({ @@ -168,7 +178,101 @@ const notFoundResponse = { content: { "application/json": { schema: errorEnvelope } }, }; +const uploadSummary = z + .object({ + schemaVersion: z.string(), + userId: z.string(), + exportedAt: z.string(), + }) + .catchall(z.number().int()) + .meta({ + id: "AdminBackupUploadSummary", + description: + "What the stored file holds: its `schemaVersion`, the account it was exported from (`userId`) and when (`exportedAt`), and one count per serialised class (`measurements`, `medications`, `intakeEvents`, `moodEntries`, …). The set of counts grows with the backup format; read the ones you know and show the rest generically.", + }); + +const uploadStored = z + .object({ + id: z.string().meta({ + description: + "`DataBackup.id` of the stored copy; pass it to `POST /api/admin/backups/{id}/restore`.", + }), + valid: z.literal(true), + summary: uploadSummary, + }) + .meta({ + id: "AdminBackupUploadResponse", + description: + "The file was read, checked and stored as a new copy. Nothing was restored.", + }); + export const adminBackupPaths: NonNullable = { + "/api/admin/backups/upload": { + post: { + tags: ["Admin"], + summary: "Store an uploaded backup file as a new copy", + description: + "Reads a backup file, checks it, and stores it encrypted as a new copy beside the weekly one; it does NOT restore anything. Restoring is the separate `POST /api/admin/backups/{id}/restore`, so an operator can review what was uploaded first. The file goes up as the raw request body, plain JSON or gzip-compressed JSON (sniffed from the bytes, not the content type), up to 512 MB as sent; or, for a file up to 10 MB, as `multipart/form-data` with one `file` field. It is read as a stream, checked as it arrives, and stored in the same pass; a file that fails any check leaves nothing behind. It is refused when it is not valid JSON, fails the backup schema, was written by a newer schema version than this server reads, needs an encryption key this server does not hold (`meta.errorCode` = `backup.key.missing`, `meta.keyIds` naming them), or belongs to an account that does not exist here. Three uploads a minute per administrator. Admin session cookie required; Bearer tokens cannot reach admin endpoints. A browser session needs a fresh proof as well as a live one: a sign-in or `POST /api/auth/reproof` within the last five minutes (with a second factor, on an account that has one). Without it the answer is 401 `auth.reproof.required`, with `meta.methods` naming the proofs the account can give.", + requestBody: { + required: true, + content: { + "application/json": { + schema: z.record(z.string(), z.unknown()).meta({ + description: + "The backup document, as `GET /api/admin/backups/{id}/download` or the account export wrote it.", + }), + }, + "application/gzip": { + schema: z.string().meta({ format: "binary" }), + }, + "multipart/form-data": { + schema: z.object({ + file: z.string().meta({ + format: "binary", + description: + "The backup file, plain or gzip-compressed, up to 10 MB.", + }), + }), + }, + }, + }, + responses: { + "201": { + description: "The file was stored as a new copy.", + content: { + "application/json": { + schema: dataEnvelope(uploadStored, "AdminBackupUploadEnvelope"), + }, + }, + }, + "400": { + description: + "A `multipart/form-data` body that could not be parsed as a form. Nothing was stored.", + content: { "application/json": { schema: errorEnvelope } }, + }, + "401": { + description: + "No session (`auth.missing`), or a session without a recent proof (`auth.reproof.required`, with `meta.methods`). A Bearer token lands here too: the admin gate reads only the session cookie.", + content: { "application/json": { schema: errorEnvelope } }, + }, + "413": { + description: + "Too large: over 512 MB as sent (10 MB in a form), a gzip file that expands past what any backup this server wrote does, or larger than the stored-copy limit once encrypted (`meta.errorCode` = `backup.upload.too_large`, the numbers and the setting in the message). Nothing was stored.", + content: { "application/json": { schema: errorEnvelope } }, + }, + "422": { + description: + "The file was refused and nothing was stored: no file in the request, not valid JSON or not readable as gzip, failed the backup schema (`meta.issues`), an unsupported schema version, a key this server does not hold (`meta.errorCode` = `backup.key.missing`, `meta.keyIds`), or an owner account that does not exist here.", + content: { "application/json": { schema: errorEnvelope } }, + }, + "429": { + ...stdResponses["429"], + description: + "More than three uploads in a minute by this administrator. Nothing was stored. The `Retry-After` and `X-RateLimit-*` headers describe the bucket.", + }, + }, + }, + }, "/api/admin/backups/{id}/download": { get: { tags: ["Admin"], diff --git a/src/lib/openapi/routes/health-record.ts b/src/lib/openapi/routes/health-record.ts index 303055b89..ca515a797 100644 --- a/src/lib/openapi/routes/health-record.ts +++ b/src/lib/openapi/routes/health-record.ts @@ -15,10 +15,44 @@ import { import { dataEnvelope, errorEnvelope, + moduleDisabledResponse, recordRefusal, stdResponses, } from "./shared"; +// The FHIR face answers its own refusals as FHIR: a 400, 404 or 429 raised +// by a `/api/fhir/*` handler is an `OperationOutcome` served as +// `application/fhir+json` (`operationOutcome` in `@/lib/fhir/rest`), which is +// what a FHIR client parses on any non-2xx. The refusals that run before the +// handler's own body are the API's: the generic 401 / 403 envelopes and the +// 403 `module.disabled`, which keeps the errorCode every client already +// branches on for a disabled module. Declared here after `...stdResponses`, +// so the FHIR shapes replace the generic JSON ones. +const fhirOutcomeContent = { + "application/fhir+json": { + schema: z.string().meta({ format: "binary" }), + }, +}; + +const fhirDataRefusals = { + ...moduleDisabledResponse, + "429": { + description: + "Rate limit exceeded: the per-account `fhir:` bucket, 120 requests per hour, shared by every `/api/fhir/*` data route. The body is an `OperationOutcome` (`issue[0].code` = `throttled`) served as `application/fhir+json`, not the JSON error envelope. The `Retry-After`, `X-RateLimit-Limit`, `X-RateLimit-Remaining` and `X-RateLimit-Reset` headers describe the bucket; back off on `Retry-After`.", + content: fhirOutcomeContent, + headers: stdResponses["429"].headers, + }, +}; + +const fhirSearchRefusals = { + ...fhirDataRefusals, + "400": { + description: + "A search parameter the server cannot honour, such as an `_offset` above 9007199254740991. The body is an `OperationOutcome` (`issue[0].code` = `invalid`, the reason in `diagnostics`) served as `application/fhir+json`.", + content: fhirOutcomeContent, + }, +}; + // The two request bodies this module publishes. `.meta()` CLONES in Zod 4 // rather than annotating in place, so the returned schema has to be captured // and referenced: a bare `schema.meta({...})` statement registers nothing and @@ -394,6 +428,7 @@ export const healthRecordPaths: NonNullable = { }, }, ...stdResponses, + ...fhirSearchRefusals, }, }, }, @@ -426,6 +461,7 @@ export const healthRecordPaths: NonNullable = { }, }, ...stdResponses, + ...fhirSearchRefusals, }, }, }, @@ -456,6 +492,7 @@ export const healthRecordPaths: NonNullable = { }, }, ...stdResponses, + ...fhirSearchRefusals, }, }, }, @@ -486,6 +523,7 @@ export const healthRecordPaths: NonNullable = { }, }, ...stdResponses, + ...fhirSearchRefusals, }, }, }, @@ -516,6 +554,7 @@ export const healthRecordPaths: NonNullable = { }, }, ...stdResponses, + ...fhirSearchRefusals, }, }, }, @@ -546,6 +585,7 @@ export const healthRecordPaths: NonNullable = { }, }, ...stdResponses, + ...fhirDataRefusals, }, }, }, @@ -576,6 +616,7 @@ export const healthRecordPaths: NonNullable = { }, }, ...stdResponses, + ...fhirDataRefusals, }, }, }, @@ -606,6 +647,7 @@ export const healthRecordPaths: NonNullable = { }, }, ...stdResponses, + ...fhirDataRefusals, }, }, }, @@ -636,6 +678,7 @@ export const healthRecordPaths: NonNullable = { }, }, ...stdResponses, + ...fhirDataRefusals, }, }, }, @@ -666,6 +709,7 @@ export const healthRecordPaths: NonNullable = { }, }, ...stdResponses, + ...fhirDataRefusals, }, }, }, @@ -696,6 +740,7 @@ export const healthRecordPaths: NonNullable = { }, }, ...stdResponses, + ...fhirSearchRefusals, }, }, }, diff --git a/src/lib/openapi/routes/index.ts b/src/lib/openapi/routes/index.ts index e72f9655a..31da5dec6 100644 --- a/src/lib/openapi/routes/index.ts +++ b/src/lib/openapi/routes/index.ts @@ -96,9 +96,9 @@ import { settingsPaths } from "./settings"; import { symptomPaths } from "./symptoms"; import { syncPaths } from "./sync"; import { workoutPaths } from "./workouts"; -import { coachPrefsSchema } from "./shared"; +import { coachPrefsSchema, withScopeRefusal } from "./shared"; -export const openApiPaths: NonNullable = { +const registeredPaths: NonNullable = { ...cyclePaths, ...metaPaths, ...healthRecordPaths, @@ -214,6 +214,11 @@ export const openApiPaths: NonNullable = { ...retiredPaths, }; +// The generic auth gate's 403 is declared on the assembled table, not spread +// from `stdResponses`; `scopeRefusalResponse` in `./shared` says why. +export const openApiPaths: NonNullable = + withScopeRefusal(registeredPaths); + export const openApiComponents: NonNullable = { // Schemas listed here are forced into `components.schemas` even when no // route references them directly. `Medication` lives in this slot diff --git a/src/lib/openapi/routes/profile.ts b/src/lib/openapi/routes/profile.ts index a510c4fad..a19f2eb14 100644 --- a/src/lib/openapi/routes/profile.ts +++ b/src/lib/openapi/routes/profile.ts @@ -1141,6 +1141,29 @@ const profileSummaryResponse = z "The bounded profile view a record viewer gets: allergies, family history and current lifestyle facts. Structurally incapable of becoming a Settings or AI surface.", }); +/** + * What the single-value `/api/auth/me/*` writes answer for a body they refuse + * to read. Each reads through `safeJson` with a 1 KB cap, so the three + * refusals are the cap's, not the route's: a wrong content type is 415, a body + * over the cap 413, and one that does not parse 400. All three are answered + * before validation, and nothing is written. + */ +const cappedScalarBodyRefusals = { + "400": { + description: "The request body is not parseable JSON. Nothing was written.", + content: { "application/json": { schema: errorEnvelope } }, + }, + "413": { + description: "Request body exceeds 1024 bytes. Nothing was written.", + content: { "application/json": { schema: errorEnvelope } }, + }, + "415": { + description: + "The request did not carry `Content-Type: application/json`. Nothing was read and nothing was written.", + content: { "application/json": { schema: errorEnvelope } }, + }, +}; + export const profilePaths: NonNullable = { "/api/profile/summary": { get: { @@ -1863,10 +1886,7 @@ export const profilePaths: NonNullable = { }, }, }, - "413": { - description: "Request body exceeds 1024 bytes. Nothing was written.", - content: { "application/json": { schema: errorEnvelope } }, - }, + ...cappedScalarBodyRefusals, ...stdResponses, }, }, @@ -1917,10 +1937,7 @@ export const profilePaths: NonNullable = { }, }, }, - "413": { - description: "Request body exceeds 1024 bytes. Nothing was written.", - content: { "application/json": { schema: errorEnvelope } }, - }, + ...cappedScalarBodyRefusals, ...stdResponses, }, }, @@ -1971,10 +1988,7 @@ export const profilePaths: NonNullable = { }, }, }, - "413": { - description: "Request body exceeds 1024 bytes. Nothing was written.", - content: { "application/json": { schema: errorEnvelope } }, - }, + ...cappedScalarBodyRefusals, ...stdResponses, }, }, @@ -2257,10 +2271,7 @@ export const profilePaths: NonNullable = { }, }, }, - "413": { - description: "Request body exceeds 1024 bytes. Nothing was written.", - content: { "application/json": { schema: errorEnvelope } }, - }, + ...cappedScalarBodyRefusals, ...stdResponses, }, }, @@ -2307,10 +2318,7 @@ export const profilePaths: NonNullable = { }, }, }, - "413": { - description: "Request body exceeds 1024 bytes. Nothing was written.", - content: { "application/json": { schema: errorEnvelope } }, - }, + ...cappedScalarBodyRefusals, ...stdResponses, }, }, @@ -2360,10 +2368,7 @@ export const profilePaths: NonNullable = { }, }, }, - "413": { - description: "Request body exceeds 1024 bytes. Nothing was written.", - content: { "application/json": { schema: errorEnvelope } }, - }, + ...cappedScalarBodyRefusals, ...stdResponses, }, }, diff --git a/src/lib/openapi/routes/shared.ts b/src/lib/openapi/routes/shared.ts index 1e929b18e..8dfe8b32b 100644 --- a/src/lib/openapi/routes/shared.ts +++ b/src/lib/openapi/routes/shared.ts @@ -351,6 +351,128 @@ export const stdResponses = { }, }; +// ── The generic 403 ────────────────────────────────────────────────── + +/** + * The 403 the generic auth gates answer before a route's own body runs. + * + * `requireAuth` refuses a narrow Bearer token on a route that names no scope + * the token holds (`auth.scope.insufficient`), and `requireAdmin` refuses a + * session that is not an administrator's (`auth.admin.required`). The shared + * 401 described both in its prose and no operation declared the status, so a + * decoder generated from the contract had no typed branch for a refusal any + * authenticated route can send. + * + * Neither is a member of `stdResponses`, on purpose. That set is spread into + * several hundred operations, about three hundred of them declare a 403 of + * their own, and most spread the set AFTER their own entries — a shared 403 + * in it would silently overwrite every module-off and sharing refusal in the + * document. `withScopeRefusal` adds them to the assembled table instead, where + * the order of a spread cannot decide it. + */ +export const scopeRefusalResponse = { + description: + "The credential is valid but its scope does not reach this operation. `meta.errorCode` = `auth.scope.insufficient`: a narrow Bearer token on a route that names no scope it holds, or a connector-bound token on a write. Retrying with the same token will not help; use a session, or a token minted with the scope the route names.", + content: { "application/json": { schema: errorEnvelope } }, +}; + +/** + * The admin surfaces' variant. `requireAdmin` reads only the session cookie, + * so a Bearer caller never gets this far (it is a 401 there); the 403 is a + * signed-in account that is not an administrator. + */ +export const adminRefusalResponse = { + description: + "The session is valid but its account is not an administrator. `meta.errorCode` = `auth.admin.required`.", + content: { "application/json": { schema: errorEnvelope } }, +}; + +const GATE_SENTENCE = { + scope: + "`meta.errorCode` = `auth.scope.insufficient` when a Bearer token's scope does not reach this route; the generic auth gate answers it before the route's own checks run. Otherwise: ", + admin: + "`meta.errorCode` = `auth.admin.required` when the session's account is not an administrator; the admin gate answers it before the route's own checks run. Otherwise: ", +} as const; + +const GATE_CODE = { + scope: "auth.scope.insufficient", + admin: "auth.admin.required", +} as const; + +/** + * Surfaces that authenticate by a credential of their own and never pass the + * generic gate, so they cannot answer its 403: a URL credential (the + * clinician share link, the notification quick answer) or the external ingest + * token, which runs its own scope check and documents its own 403. + */ +const OUTSIDE_THE_GENERIC_GATE = new Set([ + "/api/c/{token}/unlock", + "/api/notifications/action/{token}", + "/api/ingest/medication", +]); + +/** + * Declare the gate's 403 on every operation that passes the generic auth + * gate: one that declares a 401 and does not opt out of authentication with + * `security: []`. Under `/api/admin/` the gate is `requireAdmin`, everywhere + * else `requireAuth`. An operation without a 403 of its own gets the shared + * response; one with its own keeps it, and its description is prefixed with + * the gate's code, because a client branching on that 403 has to tell a + * module-off refusal from a gate refusal. Prefixed, not appended: the sharing + * refusal has to stay the description's closing sentence, byte for byte + * (`openapi-sharing-denial.test.ts`). + * + * Response objects shared between operations stay shared (one augmented copy + * per original), so the emitted document keeps its anchors. + */ +export function withScopeRefusal>( + paths: T, +): T { + const augmented = { + scope: new WeakMap(), + admin: new WeakMap(), + }; + const out: Record = {}; + for (const [path, item] of Object.entries(paths)) { + if (!isPlainRecord(item) || OUTSIDE_THE_GENERIC_GATE.has(path)) { + out[path] = item; + continue; + } + const gate = path.startsWith("/api/admin/") ? "admin" : "scope"; + const nextItem: Record = { ...item }; + for (const method of ["get", "put", "post", "patch", "delete"] as const) { + const op = item[method]; + if (!isPlainRecord(op) || !isPlainRecord(op.responses)) continue; + if (Array.isArray(op.security) && op.security.length === 0) continue; + if (!op.responses["401"]) continue; + const own = op.responses["403"]; + let forbidden: unknown = + gate === "admin" ? adminRefusalResponse : scopeRefusalResponse; + if (isPlainRecord(own)) { + forbidden = augmented[gate].get(own); + if (!forbidden) { + const description = + typeof own.description === "string" ? own.description : ""; + forbidden = description.includes(GATE_CODE[gate]) + ? own + : { ...own, description: GATE_SENTENCE[gate] + description }; + augmented[gate].set(own, forbidden as object); + } + } + nextItem[method] = { + ...op, + responses: { ...op.responses, "403": forbidden }, + }; + } + out[path] = nextItem; + } + return out as T; +} + +function isPlainRecord(value: unknown): value is Record { + return typeof value === "object" && value !== null && !Array.isArray(value); +} + /** * The 429 the shared single-record write bucket answers with. * diff --git a/src/lib/strava/__tests__/token-refusal.test.ts b/src/lib/strava/__tests__/token-refusal.test.ts new file mode 100644 index 000000000..207f99f4a --- /dev/null +++ b/src/lib/strava/__tests__/token-refusal.test.ts @@ -0,0 +1,140 @@ +/** + * What Strava's token endpoint answers when it refuses, and what the sync + * makes of it. + * + * Strava does not answer RFC 6749 errors. A refused refresh is the API's + * generic `Fault`; the bodies below are the ones the endpoint sends, as + * captured from live traffic by several unrelated client libraries (Strava + * documents the `Fault` / `Error` model, not these particular values). The + * HTTP layer is the only thing replaced: `postToken`, the error it throws and + * the classifier all run as they do in production. + */ +import { beforeEach, describe, expect, it, vi } from "vitest"; + +vi.mock("@/lib/safe-fetch", () => ({ safeFetch: vi.fn() })); + +import { safeFetch } from "@/lib/safe-fetch"; + +import { exchangeCode, refreshAccessToken } from "../client"; +import { + classifyStravaError, + stravaFaultSummary, + StravaApiError, +} from "../response-classifier"; +import { classifyStravaFailure } from "../sync"; + +const creds = { clientId: "12345", clientSecret: "not-a-real-secret" }; + +const DEAD_REFRESH_TOKEN = { + message: "Bad Request", + errors: [ + { resource: "RefreshToken", field: "refresh_token", code: "invalid" }, + ], +}; +const BAD_CLIENT_ID = { + message: "Bad Request", + errors: [{ resource: "Application", field: "client_id", code: "invalid" }], +}; +const BAD_CLIENT = { + message: "Authorization Error", + errors: [{ resource: "Application", field: "", code: "invalid" }], +}; + +function answer(status: number, body: unknown) { + vi.mocked(safeFetch).mockResolvedValueOnce( + new Response(JSON.stringify(body), { + status, + headers: { "content-type": "application/json" }, + }), + ); +} + +async function refusal(run: () => Promise): Promise { + const err = await run().then( + () => null, + (e: unknown) => e, + ); + expect(err).toBeInstanceOf(StravaApiError); + return err as StravaApiError; +} + +beforeEach(() => { + vi.mocked(safeFetch).mockReset(); +}); + +describe("a refused Strava token request", () => { + it("asks for a reconnect when the refresh token is dead", async () => { + answer(400, DEAD_REFRESH_TOKEN); + const err = await refusal(() => refreshAccessToken("rt-dead", creds)); + + expect(err.httpStatus).toBe(400); + expect(err.upstreamError).toBe("RefreshToken.refresh_token:invalid"); + expect(classifyStravaError(err)).toBe("reauth_required"); + expect(classifyStravaFailure(err)).toBe("reauth_required"); + }); + + it("still asks for a reconnect when the error lost its prototype on a retry", async () => { + answer(400, DEAD_REFRESH_TOKEN); + const err = await refusal(() => refreshAccessToken("rt-dead", creds)); + const replayed = new Error(err.message); + + expect(replayed.message).toBe( + "Strava refreshAccessToken error: 400 - RefreshToken.refresh_token:invalid", + ); + expect(classifyStravaError(replayed)).toBe("reauth_required"); + }); + + it("leaves a fault that names the application to the status, as before", async () => { + // A client id or secret the instance holds wrongly. Whether Strava ever + // answers a per-person condition this way is not documented, so the + // status decides, as it always has: 400 persistent, 401 reconnect. + answer(400, BAD_CLIENT_ID); + const bad = await refusal(() => refreshAccessToken("rt-live", creds)); + expect(bad.upstreamError).toBe("Application.client_id:invalid"); + expect(classifyStravaError(bad)).toBe("persistent"); + + answer(401, BAD_CLIENT); + const unauthorised = await refusal(() => + refreshAccessToken("rt-live", creds), + ); + expect(unauthorised.upstreamError).toBe("Application:invalid"); + expect(classifyStravaError(unauthorised)).toBe("reauth_required"); + }); + + it("leaves a 400 that names neither as persistent and a 5xx as transient", async () => { + answer(400, { message: "Bad Request", errors: [] }); + const plain = await refusal(() => exchangeCode("code", creds)); + expect(plain.upstreamError).toBe("Bad Request"); + expect(classifyStravaError(plain)).toBe("persistent"); + + answer(503, { message: "Service Unavailable" }); + const outage = await refusal(() => refreshAccessToken("rt-live", creds)); + expect(classifyStravaError(outage)).toBe("transient"); + }); + + it("still honours a standards-shaped invalid_grant", async () => { + answer(400, { error: "invalid_grant" }); + const err = await refusal(() => refreshAccessToken("rt-dead", creds)); + expect(err.upstreamError).toBe("invalid_grant"); + expect(classifyStravaError(err)).toBe("reauth_required"); + }); +}); + +describe("stravaFaultSummary", () => { + it("names only the resource, field and code, and drops anything else", () => { + expect( + stravaFaultSummary({ + message: "Bad Request", + errors: [ + { + resource: "RefreshToken", + field: "refresh_token