@@ -50,9 +50,9 @@ function RegisterPage() {
);
diff --git a/bun.lock b/bun.lock
index 2687db6..76884e2 100644
--- a/bun.lock
+++ b/bun.lock
@@ -15,7 +15,7 @@
"@vitest/browser-playwright": "catalog:",
"@vitest/coverage-v8": "catalog:",
"eslint-plugin-drizzle": "0.2.3",
- "eslint-plugin-lingui": "0.11.0",
+ "eslint-plugin-lingui": "0.12.0",
"eslint-plugin-react-hooks": "7.0.1",
"oxfmt": "0.42.0",
"oxlint": "1.57.0",
@@ -27,7 +27,7 @@
},
"apps/native": {
"name": "@sofa/native",
- "version": "0.1.2",
+ "version": "0.1.3",
"dependencies": {
"@better-auth/expo": "catalog:",
"@expo/metro-runtime": "55.0.6",
@@ -46,7 +46,7 @@
"@react-native-menu/menu": "2.0.0",
"@react-navigation/elements": "2.9.11",
"@react-navigation/native": "7.1.34",
- "@sentry/react-native": "8.5.0",
+ "@sentry/react-native": "8.6.0",
"@shopify/flash-list": "2.0.2",
"@sofa/api": "workspace:*",
"@sofa/i18n": "workspace:*",
@@ -98,7 +98,7 @@
"react-native-worklets": "0.7.2",
"tailwind-merge": "catalog:",
"tailwindcss": "catalog:",
- "uniwind": "1.6.0",
+ "uniwind": "1.6.1",
"zeego": "3.0.6",
"zod": "catalog:",
},
@@ -112,7 +112,7 @@
},
"apps/public-api": {
"name": "@sofa/public-api",
- "version": "0.1.2",
+ "version": "0.1.3",
"dependencies": {
"@hono/zod-validator": "0.7.6",
"@vercel/firewall": "1.1.2",
@@ -127,7 +127,7 @@
},
"apps/server": {
"name": "@sofa/server",
- "version": "0.1.2",
+ "version": "0.1.3",
"dependencies": {
"@orpc/contract": "catalog:",
"@orpc/json-schema": "catalog:",
@@ -153,7 +153,7 @@
},
"apps/web": {
"name": "@sofa/web",
- "version": "0.1.2",
+ "version": "0.1.3",
"dependencies": {
"@base-ui/react": "1.3.0",
"@fontsource-variable/dm-sans": "5.2.8",
@@ -170,9 +170,9 @@
"@sofa/i18n": "workspace:*",
"@tabler/icons-react": "3.40.0",
"@tanstack/react-form": "catalog:",
- "@tanstack/react-hotkeys": "0.6.0",
+ "@tanstack/react-hotkeys": "0.8.1",
"@tanstack/react-query": "catalog:",
- "@tanstack/react-router": "1.168.3",
+ "@tanstack/react-router": "1.168.5",
"@tanstack/zod-adapter": "1.166.9",
"better-auth": "catalog:",
"class-variance-authority": "0.7.1",
@@ -198,7 +198,7 @@
"@tanstack/react-devtools": "0.10.0",
"@tanstack/react-query-devtools": "5.95.2",
"@tanstack/react-router-devtools": "1.166.11",
- "@tanstack/router-plugin": "1.167.4",
+ "@tanstack/router-plugin": "1.167.6",
"@types/bun": "catalog:",
"@types/node": "catalog:",
"@types/react": "catalog:",
@@ -209,13 +209,13 @@
"babel-plugin-react-compiler": "1.0.0",
"tailwindcss": "catalog:",
"typescript": "catalog:",
- "vite": "8.0.2",
+ "vite": "8.0.3",
"vitest": "catalog:",
},
},
"packages/api": {
"name": "@sofa/api",
- "version": "0.1.2",
+ "version": "0.1.3",
"dependencies": {
"@orpc/contract": "catalog:",
"zod": "catalog:",
@@ -227,7 +227,7 @@
},
"packages/auth": {
"name": "@sofa/auth",
- "version": "0.1.2",
+ "version": "0.1.3",
"dependencies": {
"@better-auth/drizzle-adapter": "1.5.6",
"@better-auth/expo": "catalog:",
@@ -244,7 +244,7 @@
},
"packages/config": {
"name": "@sofa/config",
- "version": "0.1.2",
+ "version": "0.1.3",
"devDependencies": {
"@sofa/tsconfig": "workspace:*",
"@types/bun": "catalog:",
@@ -253,7 +253,7 @@
},
"packages/core": {
"name": "@sofa/core",
- "version": "0.1.2",
+ "version": "0.1.3",
"dependencies": {
"@orpc/server": "catalog:",
"@sofa/api": "workspace:*",
@@ -277,7 +277,7 @@
},
"packages/db": {
"name": "@sofa/db",
- "version": "0.1.2",
+ "version": "0.1.3",
"dependencies": {
"@sofa/config": "workspace:*",
"@sofa/logger": "workspace:*",
@@ -292,7 +292,7 @@
},
"packages/i18n": {
"name": "@sofa/i18n",
- "version": "0.1.2",
+ "version": "0.1.3",
"dependencies": {
"@lingui/core": "catalog:",
"@lingui/react": "catalog:",
@@ -310,7 +310,7 @@
},
"packages/logger": {
"name": "@sofa/logger",
- "version": "0.1.2",
+ "version": "0.1.3",
"dependencies": {
"pino": "10.3.1",
"pino-pretty": "13.1.3",
@@ -323,7 +323,7 @@
},
"packages/test": {
"name": "@sofa/test",
- "version": "0.1.2",
+ "version": "0.1.3",
"dependencies": {
"@sofa/db": "workspace:*",
"better-sqlite3": "12.8.0",
@@ -338,7 +338,7 @@
},
"packages/tmdb": {
"name": "@sofa/tmdb",
- "version": "0.1.2",
+ "version": "0.1.3",
"dependencies": {
"@sofa/logger": "workspace:*",
"openapi-fetch": "0.17.0",
@@ -351,7 +351,7 @@
},
"packages/tsconfig": {
"name": "@sofa/tsconfig",
- "version": "0.1.2",
+ "version": "0.1.3",
},
},
"trustedDependencies": [
@@ -371,21 +371,21 @@
"@lingui/metro-transformer": "5.9.3",
"@lingui/react": "5.9.3",
"@lingui/vite-plugin": "5.9.3",
- "@orpc/client": "1.13.10",
- "@orpc/contract": "1.13.10",
- "@orpc/json-schema": "1.13.10",
- "@orpc/openapi": "1.13.10",
- "@orpc/server": "1.13.10",
- "@orpc/tanstack-query": "1.13.10",
- "@orpc/zod": "1.13.10",
+ "@orpc/client": "1.13.13",
+ "@orpc/contract": "1.13.13",
+ "@orpc/json-schema": "1.13.13",
+ "@orpc/openapi": "1.13.13",
+ "@orpc/server": "1.13.13",
+ "@orpc/tanstack-query": "1.13.13",
+ "@orpc/zod": "1.13.13",
"@tanstack/react-form": "1.28.5",
"@tanstack/react-query": "5.95.2",
"@types/bun": "1.3.11",
"@types/node": "25.5.0",
"@types/react": "19.2.14",
- "@vitest/browser": "4.1.1",
- "@vitest/browser-playwright": "4.1.1",
- "@vitest/coverage-v8": "4.1.1",
+ "@vitest/browser": "4.1.2",
+ "@vitest/browser-playwright": "4.1.2",
+ "@vitest/coverage-v8": "4.1.2",
"better-auth": "1.5.6",
"drizzle-kit": "1.0.0-beta.19",
"drizzle-orm": "1.0.0-beta.19",
@@ -395,7 +395,7 @@
"tailwindcss": "4.2.2",
"thumbhash": "0.1.1",
"typescript": "5.9.3",
- "vitest": "4.1.1",
+ "vitest": "4.1.2",
"zod": "4.3.6",
},
"packages": {
@@ -985,7 +985,7 @@
"@messageformat/parser": ["@messageformat/parser@5.1.1", "", { "dependencies": { "moo": "^0.5.1" } }, "sha512-3p0YRGCcTUCYvBKLIxtDDyrJ0YijGIwrTRu1DT8gIviIDZru8H23+FkY6MJBzM1n9n20CiM4VeDYuBsrrwnLjg=="],
- "@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.27.1", "", { "dependencies": { "@hono/node-server": "^1.19.9", "ajv": "^8.17.1", "ajv-formats": "^3.0.1", "content-type": "^1.0.5", "cors": "^2.8.5", "cross-spawn": "^7.0.5", "eventsource": "^3.0.2", "eventsource-parser": "^3.0.0", "express": "^5.2.1", "express-rate-limit": "^8.2.1", "hono": "^4.11.4", "jose": "^6.1.3", "json-schema-typed": "^8.0.2", "pkce-challenge": "^5.0.0", "raw-body": "^3.0.0", "zod": "^3.25 || ^4.0", "zod-to-json-schema": "^3.25.1" }, "peerDependencies": { "@cfworker/json-schema": "^4.1.1" }, "optionalPeers": ["@cfworker/json-schema"] }, "sha512-sr6GbP+4edBwFndLbM60gf07z0FQ79gaExpnsjMGePXqFcSSb7t6iscpjk9DhFhwd+mTEQrzNafGP8/iGGFYaA=="],
+ "@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.28.0", "", { "dependencies": { "@hono/node-server": "^1.19.9", "ajv": "^8.17.1", "ajv-formats": "^3.0.1", "content-type": "^1.0.5", "cors": "^2.8.5", "cross-spawn": "^7.0.5", "eventsource": "^3.0.2", "eventsource-parser": "^3.0.0", "express": "^5.2.1", "express-rate-limit": "^8.2.1", "hono": "^4.11.4", "jose": "^6.1.3", "json-schema-typed": "^8.0.2", "pkce-challenge": "^5.0.0", "raw-body": "^3.0.0", "zod": "^3.25 || ^4.0", "zod-to-json-schema": "^3.25.1" }, "peerDependencies": { "@cfworker/json-schema": "^4.1.1" }, "optionalPeers": ["@cfworker/json-schema"] }, "sha512-gmloF+i+flI8ouQK7MWW4mOwuMh4RePBuPFAEPC6+pdqyWOUMDOixb6qZ69owLJpz6XmyllCouc4t8YWO+E2Nw=="],
"@mswjs/interceptors": ["@mswjs/interceptors@0.41.3", "", { "dependencies": { "@open-draft/deferred-promise": "^2.2.0", "@open-draft/logger": "^0.3.0", "@open-draft/until": "^2.0.0", "is-node-process": "^1.2.0", "outvariant": "^1.4.3", "strict-event-emitter": "^0.5.1" } }, "sha512-cXu86tF4VQVfwz8W1SPbhoRyHJkti6mjH/XJIxp40jhO4j2k1m4KYrEykxqWPkFF3vrK4rgQppBh//AwyGSXPA=="],
@@ -1009,41 +1009,41 @@
"@open-draft/until": ["@open-draft/until@2.1.0", "", {}, "sha512-U69T3ItWHvLwGg5eJ0n3I62nWuE6ilHlmz7zM0npLBRvPRd7e6NYmg54vvRtP5mZG7kZqZCFVdsTWo7BPtBujg=="],
- "@opentelemetry/api": ["@opentelemetry/api@1.9.0", "", {}, "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg=="],
+ "@opentelemetry/api": ["@opentelemetry/api@1.9.1", "", {}, "sha512-gLyJlPHPZYdAk1JENA9LeHejZe1Ti77/pTeFm/nMXmQH/HFZlcS/O2XJB+L8fkbrNSqhdtlvjBVjxwUYanNH5Q=="],
"@opentelemetry/semantic-conventions": ["@opentelemetry/semantic-conventions@1.40.0", "", {}, "sha512-cifvXDhcqMwwTlTK04GBNeIe7yyo28Mfby85QXFe1Yk8nmi36Ab/5UQwptOx84SsoGNRg+EVSjwzfSZMy6pmlw=="],
- "@orpc/client": ["@orpc/client@1.13.10", "", { "dependencies": { "@orpc/shared": "1.13.10", "@orpc/standard-server": "1.13.10", "@orpc/standard-server-fetch": "1.13.10", "@orpc/standard-server-peer": "1.13.10" } }, "sha512-G16KsNeKn+5+CKyY47VvAGCn04AQ5Wt+2oqqjU56lAJcNvII+Xn3cWwtva/dv6u2lNXVG4JOO1TT1cVgCAc9Aw=="],
+ "@orpc/client": ["@orpc/client@1.13.13", "", { "dependencies": { "@orpc/shared": "1.13.13", "@orpc/standard-server": "1.13.13", "@orpc/standard-server-fetch": "1.13.13", "@orpc/standard-server-peer": "1.13.13" } }, "sha512-jagx/Sa+9K4HEC5lBrUlMSrmR/06hvZctWh93/sKZc8GBk4zM0+71oT1kXQVw1oRYFV2XAq3xy3m6NdM6gfKYA=="],
- "@orpc/contract": ["@orpc/contract@1.13.10", "", { "dependencies": { "@orpc/client": "1.13.10", "@orpc/shared": "1.13.10", "@standard-schema/spec": "^1.1.0", "openapi-types": "^12.1.3" } }, "sha512-5kSW34UZ3UTxcP0uw4COQd7wb2qFT2M4RXo/HahdAxa9U+uuLGSBC4Q6E0und45Vk5DkT8LnVESixK18uK9mCg=="],
+ "@orpc/contract": ["@orpc/contract@1.13.13", "", { "dependencies": { "@orpc/client": "1.13.13", "@orpc/shared": "1.13.13", "@standard-schema/spec": "^1.1.0", "openapi-types": "^12.1.3" } }, "sha512-md6iyrYkePBSJNs1VnVEEnAUORMDPHIf3JGRSHxyssIcNakev/iOjP0HvpH0Sx0MlTBhihAJo6uFL8Vpth58Nw=="],
- "@orpc/interop": ["@orpc/interop@1.13.10", "", {}, "sha512-h9mHeYkXkZJidRFAtx/jSzWbQBR0nTVo6iYg66siXyDTz7r4uimOBFAFPn1TYAl3jY1JHW8EXNDr7pNOaeNv/g=="],
+ "@orpc/interop": ["@orpc/interop@1.13.13", "", {}, "sha512-S8edIRA7ekD3/gHx5MrsO9CxZ0QvCaVDkVbNf42SZbLYPXuvxtdH0CYwaB1q9uQg2Jk0PBP8AquLH6WFwjMSbQ=="],
- "@orpc/json-schema": ["@orpc/json-schema@1.13.10", "", { "dependencies": { "@orpc/contract": "1.13.10", "@orpc/interop": "1.13.10", "@orpc/openapi": "1.13.10", "@orpc/server": "1.13.10", "@orpc/shared": "1.13.10", "json-schema-typed": "^8.0.2" } }, "sha512-6mG8KKOw3ctzG8nVyObU0OFxSkCM41Q814Ol5nOmHA00+QCam03ZTZXsnYywijonUaSfJmW4KsjKJ9ID7txXzQ=="],
+ "@orpc/json-schema": ["@orpc/json-schema@1.13.13", "", { "dependencies": { "@orpc/contract": "1.13.13", "@orpc/interop": "1.13.13", "@orpc/openapi": "1.13.13", "@orpc/server": "1.13.13", "@orpc/shared": "1.13.13", "json-schema-typed": "^8.0.2" } }, "sha512-48CgMrWIKG/t8l4OBlFRcYOj2ZCrIXfw1LA2Q6qRGdyBPzSLmQCMhh3Y4eoFXfz/wKvQIfyoUmBIdd91BxSyLg=="],
- "@orpc/openapi": ["@orpc/openapi@1.13.10", "", { "dependencies": { "@orpc/client": "1.13.10", "@orpc/contract": "1.13.10", "@orpc/interop": "1.13.10", "@orpc/openapi-client": "1.13.10", "@orpc/server": "1.13.10", "@orpc/shared": "1.13.10", "@orpc/standard-server": "1.13.10", "json-schema-typed": "^8.0.2", "rou3": "^0.7.12" } }, "sha512-U9E+vb24p1TYRK9/l+/4EzzI1khuTBsffVByDn1/dCpMQTvrFytzbr88LDHsXnpGz6fKnc2x+5RLVwSMWVzyGg=="],
+ "@orpc/openapi": ["@orpc/openapi@1.13.13", "", { "dependencies": { "@orpc/client": "1.13.13", "@orpc/contract": "1.13.13", "@orpc/interop": "1.13.13", "@orpc/openapi-client": "1.13.13", "@orpc/server": "1.13.13", "@orpc/shared": "1.13.13", "@orpc/standard-server": "1.13.13", "json-schema-typed": "^8.0.2", "rou3": "^0.7.12" } }, "sha512-lm3cAR1KJ1xp5WyDKCIqvhzYlDC914oevwYPWC7GFvTU1cTEmFP4lkQRdzLfvo06BprCg+UtHjzdLUDrBOF/zQ=="],
- "@orpc/openapi-client": ["@orpc/openapi-client@1.13.10", "", { "dependencies": { "@orpc/client": "1.13.10", "@orpc/contract": "1.13.10", "@orpc/shared": "1.13.10", "@orpc/standard-server": "1.13.10" } }, "sha512-EKnmm4lwVhqVt5jW8wtn4eyx3Kl5Va3sEoMqqmUZHjv2NzYj+qZMBTDzatZoN001MGABkrrIwtdYrOqTNPD/qQ=="],
+ "@orpc/openapi-client": ["@orpc/openapi-client@1.13.13", "", { "dependencies": { "@orpc/client": "1.13.13", "@orpc/contract": "1.13.13", "@orpc/shared": "1.13.13", "@orpc/standard-server": "1.13.13" } }, "sha512-k8od+bD7MqysKPPybAkxgfaNIaNseFPXtbidWkZAdCZ5w34SnDc7QPZJ0PQbyt9n9B+jOXSADNwQSTWSuGpjyA=="],
- "@orpc/server": ["@orpc/server@1.13.10", "", { "dependencies": { "@orpc/client": "1.13.10", "@orpc/contract": "1.13.10", "@orpc/interop": "1.13.10", "@orpc/shared": "1.13.10", "@orpc/standard-server": "1.13.10", "@orpc/standard-server-aws-lambda": "1.13.10", "@orpc/standard-server-fastify": "1.13.10", "@orpc/standard-server-fetch": "1.13.10", "@orpc/standard-server-node": "1.13.10", "@orpc/standard-server-peer": "1.13.10", "cookie": "^1.1.1" }, "peerDependencies": { "crossws": ">=0.3.4", "ws": ">=8.18.1" }, "optionalPeers": ["crossws", "ws"] }, "sha512-SVSyqMGILZ81a0aiCAeR740JRaiCHScK1e+rDYP9dyp1KpOQ+gy8qZbR4QwK7MpsSH58SRZPPxWgEqzYPGUKLg=="],
+ "@orpc/server": ["@orpc/server@1.13.13", "", { "dependencies": { "@orpc/client": "1.13.13", "@orpc/contract": "1.13.13", "@orpc/interop": "1.13.13", "@orpc/shared": "1.13.13", "@orpc/standard-server": "1.13.13", "@orpc/standard-server-aws-lambda": "1.13.13", "@orpc/standard-server-fastify": "1.13.13", "@orpc/standard-server-fetch": "1.13.13", "@orpc/standard-server-node": "1.13.13", "@orpc/standard-server-peer": "1.13.13", "cookie": "^1.1.1" }, "peerDependencies": { "crossws": ">=0.3.4", "ws": ">=8.18.1" }, "optionalPeers": ["crossws", "ws"] }, "sha512-l3OfiaqfLiLQqamXufKiEzT8K9Zq8VHvijADV5eDJOaz6sFzK1Mg6q2D8l9wcmuyG7ga0HfMT+cf3JQsh5WDIA=="],
- "@orpc/shared": ["@orpc/shared@1.13.10", "", { "dependencies": { "radash": "^12.1.1", "type-fest": "^5.4.4" }, "peerDependencies": { "@opentelemetry/api": ">=1.9.0" }, "optionalPeers": ["@opentelemetry/api"] }, "sha512-EvlsgN2Ll7mgrgL51UqoWcu8jJXLZeuqL2f5q/OfUTFk5VJ+wwmNep8Ig2s5655sHoFydhnLGHi216GqNkqwRw=="],
+ "@orpc/shared": ["@orpc/shared@1.13.13", "", { "dependencies": { "radash": "^12.1.1", "type-fest": "^5.4.4" }, "peerDependencies": { "@opentelemetry/api": ">=1.9.0" }, "optionalPeers": ["@opentelemetry/api"] }, "sha512-kNpYOBjHvmgKHla6munWOaEeA0utEfAvoiZpXjiRjjt1RxTibdwQvVHgxRIBNMXfQsb+ON3Q/wDkoaUhvvSnIw=="],
- "@orpc/standard-server": ["@orpc/standard-server@1.13.10", "", { "dependencies": { "@orpc/shared": "1.13.10" } }, "sha512-AUKKAqXMb3X1s7oYSe7ok1AAHc+S/n43+QMOko+9McUKo62StMp5SAbLMgBN1BUaC0RVzAmgV9nnFkqjwNpUKA=="],
+ "@orpc/standard-server": ["@orpc/standard-server@1.13.13", "", { "dependencies": { "@orpc/shared": "1.13.13" } }, "sha512-9pgS8XvauuRQElkyuD8F3om+nN0KBEnTkhblDHCBzkZERjWkmfirJmshQrWHoFaDTk+nnXHIaY6d7TBTxXdPRw=="],
- "@orpc/standard-server-aws-lambda": ["@orpc/standard-server-aws-lambda@1.13.10", "", { "dependencies": { "@orpc/shared": "1.13.10", "@orpc/standard-server": "1.13.10", "@orpc/standard-server-fetch": "1.13.10", "@orpc/standard-server-node": "1.13.10" } }, "sha512-8TtB4+Sc8plNKlGYUOSXkyidl2BrGiXQNqQl/pUXEwvgw6zxcX5JsHJhHFZdlP7UyfQnFrOgpEMz8Uds+kZ8Yw=="],
+ "@orpc/standard-server-aws-lambda": ["@orpc/standard-server-aws-lambda@1.13.13", "", { "dependencies": { "@orpc/shared": "1.13.13", "@orpc/standard-server": "1.13.13", "@orpc/standard-server-fetch": "1.13.13", "@orpc/standard-server-node": "1.13.13" } }, "sha512-e7310DnN6FSpRgR5gQpwvwg2B3uR+NoT7qlnyQWsjxQGr95L4GST3hRNSsQdQyXmFx1jvCCp0i3kAE9SJyvU6A=="],
- "@orpc/standard-server-fastify": ["@orpc/standard-server-fastify@1.13.10", "", { "dependencies": { "@orpc/shared": "1.13.10", "@orpc/standard-server": "1.13.10", "@orpc/standard-server-node": "1.13.10" }, "peerDependencies": { "fastify": ">=5.6.1" }, "optionalPeers": ["fastify"] }, "sha512-tFujlelDF6S+SR6XzG7H62uro9uuPXYLVN4HgOhk75cNp/Bdn6/KatE3IQZB90nu3LgO6EmVwtIZR8jcjhtjmA=="],
+ "@orpc/standard-server-fastify": ["@orpc/standard-server-fastify@1.13.13", "", { "dependencies": { "@orpc/shared": "1.13.13", "@orpc/standard-server": "1.13.13", "@orpc/standard-server-node": "1.13.13" }, "peerDependencies": { "fastify": ">=5.6.1" }, "optionalPeers": ["fastify"] }, "sha512-G5Sq+rZDPwHvWJHsAA6bYWoJ7LcGyBby+SjjEGwz+viRdBk/inZnZafKpM+pL89aDdq6iw99Bcbug5WFPP6KdA=="],
- "@orpc/standard-server-fetch": ["@orpc/standard-server-fetch@1.13.10", "", { "dependencies": { "@orpc/shared": "1.13.10", "@orpc/standard-server": "1.13.10" } }, "sha512-zEb67B/CPvMw1ZsDx4iwi0zSZ/gHuy4oo2icnteiw6U0RMHzbyYSeSu7fTEUDYqkQnsRDRyw28GCyyZl7ePRxQ=="],
+ "@orpc/standard-server-fetch": ["@orpc/standard-server-fetch@1.13.13", "", { "dependencies": { "@orpc/shared": "1.13.13", "@orpc/standard-server": "1.13.13" } }, "sha512-Lffy26+WtCQkwOUacsrdyeJF1GNzrhm75O3LXKVFXqmSdyVVdyI6zuqLn/YKGODU2L9IqGxZ2CwsV2tE298SSA=="],
- "@orpc/standard-server-node": ["@orpc/standard-server-node@1.13.10", "", { "dependencies": { "@orpc/shared": "1.13.10", "@orpc/standard-server": "1.13.10", "@orpc/standard-server-fetch": "1.13.10" } }, "sha512-nZ41/yx112/FfUgUBXh88N/yEFxen6mxde14zgr5+WjZA7MAifdj9HNd3fqkep06sWNiMgXzQj6rgCglFpQuWQ=="],
+ "@orpc/standard-server-node": ["@orpc/standard-server-node@1.13.13", "", { "dependencies": { "@orpc/shared": "1.13.13", "@orpc/standard-server": "1.13.13", "@orpc/standard-server-fetch": "1.13.13" } }, "sha512-wI45h/bVWn7lgvqqm5aM7vj5BblmmBdFG2wbEhmpY85DslbCtMNJrIw/x+zxm1XgYAzsKLExLp6L01hzjeHmEA=="],
- "@orpc/standard-server-peer": ["@orpc/standard-server-peer@1.13.10", "", { "dependencies": { "@orpc/shared": "1.13.10", "@orpc/standard-server": "1.13.10" } }, "sha512-/jHacwIeeNm0l5cPG1CfVZEa69TZIv4Pgxd7PmLqFdjUwHnX0AOb+3klaOOOj7V6UxNFyPbB8EXOcQDm7KuIXQ=="],
+ "@orpc/standard-server-peer": ["@orpc/standard-server-peer@1.13.13", "", { "dependencies": { "@orpc/shared": "1.13.13", "@orpc/standard-server": "1.13.13" } }, "sha512-FeWAbXfnZDPYQRajM0hD6GJvHeC3DZILngAjdcLHy5zt3riu6nL2lLPSWDv5yNWWscmYU+CfKmXWd0Z01BOeWA=="],
- "@orpc/tanstack-query": ["@orpc/tanstack-query@1.13.10", "", { "dependencies": { "@orpc/shared": "1.13.10" }, "peerDependencies": { "@orpc/client": "1.13.10", "@tanstack/query-core": ">=5.80.2" } }, "sha512-K8SFPmlpnz/LY6uUX5UPERHYmcytibRRubtA6ZvQ09vv+ZwhAHBtzon0ExJtzifRQEPPQhX1Uf+vWUSKwHxhhw=="],
+ "@orpc/tanstack-query": ["@orpc/tanstack-query@1.13.13", "", { "dependencies": { "@orpc/shared": "1.13.13" }, "peerDependencies": { "@orpc/client": "1.13.13", "@tanstack/query-core": ">=5.80.2" } }, "sha512-6+Cheaiu+RDPdszdeRKoBINrF8MQp64zSeZB+L3gqgF43zlYDhLOgELZMzYa6U3U6bLk4rmIeubpk+i1kACfRg=="],
- "@orpc/zod": ["@orpc/zod@1.13.10", "", { "dependencies": { "@orpc/json-schema": "1.13.10", "@orpc/openapi": "1.13.10", "@orpc/shared": "1.13.10", "escape-string-regexp": "^5.0.0", "wildcard-match": "^5.1.4" }, "peerDependencies": { "@orpc/contract": "1.13.10", "@orpc/server": "1.13.10", "zod": ">=3.25.0" } }, "sha512-1SizAwPVc2gh+ajlOSVCjOyBV1vEKrc4mHqrcYXuzLcCdZ1vCQAU89dEpps/C+5/RbPSJn8JBbsF1pdg4LUfqQ=="],
+ "@orpc/zod": ["@orpc/zod@1.13.13", "", { "dependencies": { "@orpc/json-schema": "1.13.13", "@orpc/openapi": "1.13.13", "@orpc/shared": "1.13.13", "escape-string-regexp": "^5.0.0", "wildcard-match": "^5.1.4" }, "peerDependencies": { "@orpc/contract": "1.13.13", "@orpc/server": "1.13.13", "zod": ">=3.25.0" } }, "sha512-K2iWGHopi3apExqNVb6EWJJqmq7cgytY3PjJyJ/Uy98UfJJ17Kwz7rAZKffNc7E967rNk9IH8MIcUcGkT7dmzQ=="],
"@oxc-project/types": ["@oxc-project/types@0.122.0", "", {}, "sha512-oLAl5kBpV4w69UtFZ9xqcmTi+GENWOcPF7FCrczTiBbmC0ibXxCwyvZGbO39rCVEuLGAZM84DH0pUIyyv/YJzA=="],
@@ -1215,47 +1215,47 @@
"@react-native/virtualized-lists": ["@react-native/virtualized-lists@0.83.2", "", { "dependencies": { "invariant": "^2.2.4", "nullthrows": "^1.1.1" }, "peerDependencies": { "@types/react": "^19.2.0", "react": "*", "react-native": "*" }, "optionalPeers": ["@types/react"] }, "sha512-N7mRjHLW/+KWxMp9IHRWyE3VIkeG1m3PnZJAGEFLCN8VFb7e4VfI567o7tE/HYcdcXCylw+Eqhlciz8gDeQ71g=="],
- "@react-navigation/bottom-tabs": ["@react-navigation/bottom-tabs@7.15.7", "", { "dependencies": { "@react-navigation/elements": "^2.9.12", "color": "^4.2.3", "sf-symbols-typescript": "^2.1.0" }, "peerDependencies": { "@react-navigation/native": "^7.2.0", "react": ">= 18.2.0", "react-native": "*", "react-native-safe-area-context": ">= 4.0.0", "react-native-screens": ">= 4.0.0" } }, "sha512-OXNvU0esvyZf//KpaIsFh2GD1otxqG+Lv48VfkNIh+3DEbOnqni8pOrKdICgoC4T0R8oVln7pnVeHl89Gipv8w=="],
+ "@react-navigation/bottom-tabs": ["@react-navigation/bottom-tabs@7.15.8", "", { "dependencies": { "@react-navigation/elements": "^2.9.13", "color": "^4.2.3", "sf-symbols-typescript": "^2.1.0" }, "peerDependencies": { "@react-navigation/native": "^7.2.1", "react": ">= 18.2.0", "react-native": "*", "react-native-safe-area-context": ">= 4.0.0", "react-native-screens": ">= 4.0.0" } }, "sha512-Fz/AAPE6Be0CimOXvon75RNgpFCbZvzF2RPcNeZOdOxIYyHDGxDdtsfTxLHB0tOp9HHXkT0xXOX8Rk001jdpbg=="],
- "@react-navigation/core": ["@react-navigation/core@7.17.0", "", { "dependencies": { "@react-navigation/routers": "^7.5.3", "escape-string-regexp": "^4.0.0", "fast-deep-equal": "^3.1.3", "nanoid": "^3.3.11", "query-string": "^7.1.3", "react-is": "^19.1.0", "use-latest-callback": "^0.2.4", "use-sync-external-store": "^1.5.0" }, "peerDependencies": { "react": ">= 18.2.0" } }, "sha512-E4Kr1PRrhKiVn1RdMdPIG1rCfrKh+HiVJ2smdLsh9D95Q2z0a9dGE9yHpRQ2pAUiiwOfgloLqegkPb8g+TcCBA=="],
+ "@react-navigation/core": ["@react-navigation/core@7.17.1", "", { "dependencies": { "@react-navigation/routers": "^7.5.3", "escape-string-regexp": "^4.0.0", "fast-deep-equal": "^3.1.3", "nanoid": "^3.3.11", "query-string": "^7.1.3", "react-is": "^19.1.0", "use-latest-callback": "^0.2.4", "use-sync-external-store": "^1.5.0" }, "peerDependencies": { "react": ">= 18.2.0" } }, "sha512-P1kL4FVTVYEf9Cvmb+WFxQ2UkbaXc9psj6OE0BsZ+hutPqZVbmiN6v/TI5QPf4qtg40a02yYo3vo+Mob9vJKtg=="],
"@react-navigation/elements": ["@react-navigation/elements@2.9.11", "", { "dependencies": { "color": "^4.2.3", "use-latest-callback": "^0.2.4", "use-sync-external-store": "^1.5.0" }, "peerDependencies": { "@react-native-masked-view/masked-view": ">= 0.2.0", "@react-navigation/native": "^7.1.34", "react": ">= 18.2.0", "react-native": "*", "react-native-safe-area-context": ">= 4.0.0" }, "optionalPeers": ["@react-native-masked-view/masked-view"] }, "sha512-O5KiwaVCcEVuqZgQ77xiBFSl1sha77rNMTFlLWYnom33ZHPDarV3bM9WNyVnMZxU8ZVTi02X3+ZhO0fSn5QYyg=="],
"@react-navigation/native": ["@react-navigation/native@7.1.34", "", { "dependencies": { "@react-navigation/core": "^7.16.2", "escape-string-regexp": "^4.0.0", "fast-deep-equal": "^3.1.3", "nanoid": "^3.3.11", "use-latest-callback": "^0.2.4" }, "peerDependencies": { "react": ">= 18.2.0", "react-native": "*" } }, "sha512-zzQ0mKAhLsjTIsaoLfILKZVMObJzE0F+bOi0hl2Glt+1Rd2GtaWJ1Z024c3yLmX+Oc79pqoCQLBXpyxtrZu9NQ=="],
- "@react-navigation/native-stack": ["@react-navigation/native-stack@7.14.7", "", { "dependencies": { "@react-navigation/elements": "^2.9.12", "color": "^4.2.3", "sf-symbols-typescript": "^2.1.0", "warn-once": "^0.1.1" }, "peerDependencies": { "@react-navigation/native": "^7.2.0", "react": ">= 18.2.0", "react-native": "*", "react-native-safe-area-context": ">= 4.0.0", "react-native-screens": ">= 4.0.0" } }, "sha512-MxKdKS817YK7iirlyW+XZnXJ339eRE7aA3E55zHVDS+R+bqro+PwRwNGqL1Y9e3w0KjAKZVsOfn5erJRWrO4iQ=="],
+ "@react-navigation/native-stack": ["@react-navigation/native-stack@7.14.9", "", { "dependencies": { "@react-navigation/elements": "^2.9.13", "color": "^4.2.3", "sf-symbols-typescript": "^2.1.0", "warn-once": "^0.1.1" }, "peerDependencies": { "@react-navigation/native": "^7.2.1", "react": ">= 18.2.0", "react-native": "*", "react-native-safe-area-context": ">= 4.0.0", "react-native-screens": ">= 4.0.0" } }, "sha512-s76NyRr/VSPRqXaLtaKUj9Q1qZ5ym0831QZFFXJcRyom6QYpo9eESB9/dfeN+tTEnH7kP77CwoCuR0THKMuk3w=="],
"@react-navigation/routers": ["@react-navigation/routers@7.5.3", "", { "dependencies": { "nanoid": "^3.3.11" } }, "sha512-1tJHg4KKRJuQ1/EvJxatrMef3NZXEPzwUIUZ3n1yJ2t7Q97siwRtbynRpQG9/69ebbtiZ8W3ScOZF/OmhvM4Rg=="],
- "@rolldown/binding-android-arm64": ["@rolldown/binding-android-arm64@1.0.0-rc.11", "", { "os": "android", "cpu": "arm64" }, "sha512-SJ+/g+xNnOh6NqYxD0V3uVN4W3VfnrGsC9/hoglicgTNfABFG9JjISvkkU0dNY84MNHLWyOgxP9v9Y9pX4S7+A=="],
+ "@rolldown/binding-android-arm64": ["@rolldown/binding-android-arm64@1.0.0-rc.12", "", { "os": "android", "cpu": "arm64" }, "sha512-pv1y2Fv0JybcykuiiD3qBOBdz6RteYojRFY1d+b95WVuzx211CRh+ytI/+9iVyWQ6koTh5dawe4S/yRfOFjgaA=="],
- "@rolldown/binding-darwin-arm64": ["@rolldown/binding-darwin-arm64@1.0.0-rc.11", "", { "os": "darwin", "cpu": "arm64" }, "sha512-7WQgR8SfOPwmDZGFkThUvsmd/nwAWv91oCO4I5LS7RKrssPZmOt7jONN0cW17ydGC1n/+puol1IpoieKqQidmg=="],
+ "@rolldown/binding-darwin-arm64": ["@rolldown/binding-darwin-arm64@1.0.0-rc.12", "", { "os": "darwin", "cpu": "arm64" }, "sha512-cFYr6zTG/3PXXF3pUO+umXxt1wkRK/0AYT8lDwuqvRC+LuKYWSAQAQZjCWDQpAH172ZV6ieYrNnFzVVcnSflAg=="],
- "@rolldown/binding-darwin-x64": ["@rolldown/binding-darwin-x64@1.0.0-rc.11", "", { "os": "darwin", "cpu": "x64" }, "sha512-39Ks6UvIHq4rEogIfQBoBRusj0Q0nPVWIvqmwBLaT6aqQGIakHdESBVOPRRLacy4WwUPIx4ZKzfZ9PMW+IeyUQ=="],
+ "@rolldown/binding-darwin-x64": ["@rolldown/binding-darwin-x64@1.0.0-rc.12", "", { "os": "darwin", "cpu": "x64" }, "sha512-ZCsYknnHzeXYps0lGBz8JrF37GpE9bFVefrlmDrAQhOEi4IOIlcoU1+FwHEtyXGx2VkYAvhu7dyBf75EJQffBw=="],
- "@rolldown/binding-freebsd-x64": ["@rolldown/binding-freebsd-x64@1.0.0-rc.11", "", { "os": "freebsd", "cpu": "x64" }, "sha512-jfsm0ZHfhiqrvWjJAmzsqiIFPz5e7mAoCOPBNTcNgkiid/LaFKiq92+0ojH+nmJmKYkre4t71BWXUZDNp7vsag=="],
+ "@rolldown/binding-freebsd-x64": ["@rolldown/binding-freebsd-x64@1.0.0-rc.12", "", { "os": "freebsd", "cpu": "x64" }, "sha512-dMLeprcVsyJsKolRXyoTH3NL6qtsT0Y2xeuEA8WQJquWFXkEC4bcu1rLZZSnZRMtAqwtrF/Ib9Ddtpa/Gkge9Q=="],
- "@rolldown/binding-linux-arm-gnueabihf": ["@rolldown/binding-linux-arm-gnueabihf@1.0.0-rc.11", "", { "os": "linux", "cpu": "arm" }, "sha512-zjQaUtSyq1nVe3nxmlSCuR96T1LPlpvmJ0SZy0WJFEsV4kFbXcq2u68L4E6O0XeFj4aex9bEauqjW8UQBeAvfQ=="],
+ "@rolldown/binding-linux-arm-gnueabihf": ["@rolldown/binding-linux-arm-gnueabihf@1.0.0-rc.12", "", { "os": "linux", "cpu": "arm" }, "sha512-YqWjAgGC/9M1lz3GR1r1rP79nMgo3mQiiA+Hfo+pvKFK1fAJ1bCi0ZQVh8noOqNacuY1qIcfyVfP6HoyBRZ85Q=="],
- "@rolldown/binding-linux-arm64-gnu": ["@rolldown/binding-linux-arm64-gnu@1.0.0-rc.11", "", { "os": "linux", "cpu": "arm64" }, "sha512-WMW1yE6IOnehTcFE9eipFkm3XN63zypWlrJQ2iF7NrQ9b2LDRjumFoOGJE8RJJTJCTBAdmLMnJ8uVitACUUo1Q=="],
+ "@rolldown/binding-linux-arm64-gnu": ["@rolldown/binding-linux-arm64-gnu@1.0.0-rc.12", "", { "os": "linux", "cpu": "arm64" }, "sha512-/I5AS4cIroLpslsmzXfwbe5OmWvSsrFuEw3mwvbQ1kDxJ822hFHIx+vsN/TAzNVyepI/j/GSzrtCIwQPeKCLIg=="],
- "@rolldown/binding-linux-arm64-musl": ["@rolldown/binding-linux-arm64-musl@1.0.0-rc.11", "", { "os": "linux", "cpu": "arm64" }, "sha512-jfndI9tsfm4APzjNt6QdBkYwre5lRPUgHeDHoI7ydKUuJvz3lZeCfMsI56BZj+7BYqiKsJm7cfd/6KYV7ubrBg=="],
+ "@rolldown/binding-linux-arm64-musl": ["@rolldown/binding-linux-arm64-musl@1.0.0-rc.12", "", { "os": "linux", "cpu": "arm64" }, "sha512-V6/wZztnBqlx5hJQqNWwFdxIKN0m38p8Jas+VoSfgH54HSj9tKTt1dZvG6JRHcjh6D7TvrJPWFGaY9UBVOaWPw=="],
- "@rolldown/binding-linux-ppc64-gnu": ["@rolldown/binding-linux-ppc64-gnu@1.0.0-rc.11", "", { "os": "linux", "cpu": "ppc64" }, "sha512-ZlFgw46NOAGMgcdvdYwAGu2Q+SLFA9LzbJLW+iyMOJyhj5wk6P3KEE9Gct4xWwSzFoPI7JCdYmYMzVtlgQ+zfw=="],
+ "@rolldown/binding-linux-ppc64-gnu": ["@rolldown/binding-linux-ppc64-gnu@1.0.0-rc.12", "", { "os": "linux", "cpu": "ppc64" }, "sha512-AP3E9BpcUYliZCxa3w5Kwj9OtEVDYK6sVoUzy4vTOJsjPOgdaJZKFmN4oOlX0Wp0RPV2ETfmIra9x1xuayFB7g=="],
- "@rolldown/binding-linux-s390x-gnu": ["@rolldown/binding-linux-s390x-gnu@1.0.0-rc.11", "", { "os": "linux", "cpu": "s390x" }, "sha512-hIOYmuT6ofM4K04XAZd3OzMySEO4K0/nc9+jmNcxNAxRi6c5UWpqfw3KMFV4MVFWL+jQsSh+bGw2VqmaPMTLyw=="],
+ "@rolldown/binding-linux-s390x-gnu": ["@rolldown/binding-linux-s390x-gnu@1.0.0-rc.12", "", { "os": "linux", "cpu": "s390x" }, "sha512-nWwpvUSPkoFmZo0kQazZYOrT7J5DGOJ/+QHHzjvNlooDZED8oH82Yg67HvehPPLAg5fUff7TfWFHQS8IV1n3og=="],
- "@rolldown/binding-linux-x64-gnu": ["@rolldown/binding-linux-x64-gnu@1.0.0-rc.11", "", { "os": "linux", "cpu": "x64" }, "sha512-qXBQQO9OvkjjQPLdUVr7Nr2t3QTZI7s4KZtfw7HzBgjbmAPSFwSv4rmET9lLSgq3rH/ndA3ngv3Qb8l2njoPNA=="],
+ "@rolldown/binding-linux-x64-gnu": ["@rolldown/binding-linux-x64-gnu@1.0.0-rc.12", "", { "os": "linux", "cpu": "x64" }, "sha512-RNrafz5bcwRy+O9e6P8Z/OCAJW/A+qtBczIqVYwTs14pf4iV1/+eKEjdOUta93q2TsT/FI0XYDP3TCky38LMAg=="],
- "@rolldown/binding-linux-x64-musl": ["@rolldown/binding-linux-x64-musl@1.0.0-rc.11", "", { "os": "linux", "cpu": "x64" }, "sha512-/tpFfoSTzUkH9LPY+cYbqZBDyyX62w5fICq9qzsHLL8uTI6BHip3Q9Uzft0wylk/i8OOwKik8OxW+QAhDmzwmg=="],
+ "@rolldown/binding-linux-x64-musl": ["@rolldown/binding-linux-x64-musl@1.0.0-rc.12", "", { "os": "linux", "cpu": "x64" }, "sha512-Jpw/0iwoKWx3LJ2rc1yjFrj+T7iHZn2JDg1Yny1ma0luviFS4mhAIcd1LFNxK3EYu3DHWCps0ydXQ5i/rrJ2ig=="],
- "@rolldown/binding-openharmony-arm64": ["@rolldown/binding-openharmony-arm64@1.0.0-rc.11", "", { "os": "none", "cpu": "arm64" }, "sha512-mcp3Rio2w72IvdZG0oQ4bM2c2oumtwHfUfKncUM6zGgz0KgPz4YmDPQfnXEiY5t3+KD/i8HG2rOB/LxdmieK2g=="],
+ "@rolldown/binding-openharmony-arm64": ["@rolldown/binding-openharmony-arm64@1.0.0-rc.12", "", { "os": "none", "cpu": "arm64" }, "sha512-vRugONE4yMfVn0+7lUKdKvN4D5YusEiPilaoO2sgUWpCvrncvWgPMzK00ZFFJuiPgLwgFNP5eSiUlv2tfc+lpA=="],
- "@rolldown/binding-wasm32-wasi": ["@rolldown/binding-wasm32-wasi@1.0.0-rc.11", "", { "dependencies": { "@napi-rs/wasm-runtime": "^1.1.1" }, "cpu": "none" }, "sha512-LXk5Hii1Ph9asuGRjBuz8TUxdc1lWzB7nyfdoRgI0WGPZKmCxvlKk8KfYysqtr4MfGElu/f/pEQRh8fcEgkrWw=="],
+ "@rolldown/binding-wasm32-wasi": ["@rolldown/binding-wasm32-wasi@1.0.0-rc.12", "", { "dependencies": { "@napi-rs/wasm-runtime": "^1.1.1" }, "cpu": "none" }, "sha512-ykGiLr/6kkiHc0XnBfmFJuCjr5ZYKKofkx+chJWDjitX+KsJuAmrzWhwyOMSHzPhzOHOy7u9HlFoa5MoAOJ/Zg=="],
- "@rolldown/binding-win32-arm64-msvc": ["@rolldown/binding-win32-arm64-msvc@1.0.0-rc.11", "", { "os": "win32", "cpu": "arm64" }, "sha512-dDwf5otnx0XgRY1yqxOC4ITizcdzS/8cQ3goOWv3jFAo4F+xQYni+hnMuO6+LssHHdJW7+OCVL3CoU4ycnh35Q=="],
+ "@rolldown/binding-win32-arm64-msvc": ["@rolldown/binding-win32-arm64-msvc@1.0.0-rc.12", "", { "os": "win32", "cpu": "arm64" }, "sha512-5eOND4duWkwx1AzCxadcOrNeighiLwMInEADT0YM7xeEOOFcovWZCq8dadXgcRHSf3Ulh1kFo/qvzoFiCLOL1Q=="],
- "@rolldown/binding-win32-x64-msvc": ["@rolldown/binding-win32-x64-msvc@1.0.0-rc.11", "", { "os": "win32", "cpu": "x64" }, "sha512-LN4/skhSggybX71ews7dAj6r2geaMJfm3kMbK2KhFMg9B10AZXnKoLCVVgzhMHL0S+aKtr4p8QbAW8k+w95bAA=="],
+ "@rolldown/binding-win32-x64-msvc": ["@rolldown/binding-win32-x64-msvc@1.0.0-rc.12", "", { "os": "win32", "cpu": "x64" }, "sha512-PyqoipaswDLAZtot351MLhrlrh6lcZPo2LSYE+VDxbVk24LVKAGOuE4hb8xZQmrPAuEtTZW8E6D2zc5EUZX4Lw=="],
"@rolldown/plugin-babel": ["@rolldown/plugin-babel@0.2.2", "", { "dependencies": { "picomatch": "^4.0.3" }, "peerDependencies": { "@babel/core": "^7.29.0 || ^8.0.0-rc.1", "@babel/plugin-transform-runtime": "^7.29.0 || ^8.0.0-rc.1", "@babel/runtime": "^7.27.0 || ^8.0.0-rc.1", "rolldown": "^1.0.0-rc.5", "vite": "^8.0.0" }, "optionalPeers": ["@babel/plugin-transform-runtime", "@babel/runtime", "vite"] }, "sha512-q9pE8+47bQNHb5eWVcE6oXppA+JTSwvnrhH53m0ZuHuK5MLvwsLoWrWzBTFQqQ06BVxz1gp0HblLsch8o6pvZw=="],
@@ -1263,43 +1263,43 @@
"@sec-ant/readable-stream": ["@sec-ant/readable-stream@0.4.1", "", {}, "sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg=="],
- "@sentry-internal/browser-utils": ["@sentry-internal/browser-utils@10.44.0", "", { "dependencies": { "@sentry/core": "10.44.0" } }, "sha512-z9xz3T/v+MnfHY6kdUCmOZI8CiAl3LlKYtGH2p3rAsrxhwX+BTnUp01VhMVnEZIDgUXNt3AhJac+4kcDIPu1Hg=="],
+ "@sentry-internal/browser-utils": ["@sentry-internal/browser-utils@10.46.0", "", { "dependencies": { "@sentry/core": "10.46.0" } }, "sha512-WB1gBT9G13V02ekZ6NpUhoI1aGHV2eNfjEPthkU2bGBvFpQKnstwzjg7waIRGR7cu+YSW2Q6UI6aQLgBeOPD1g=="],
- "@sentry-internal/feedback": ["@sentry-internal/feedback@10.44.0", "", { "dependencies": { "@sentry/core": "10.44.0" } }, "sha512-yNS2EGK1bNm8YUI+Orzpa7yr05Da+b1VEe/9x7dl7gTjw/+tfutoXlG6Y+iFZBB3gQ9QU+nxZAhU+KcxiPEURw=="],
+ "@sentry-internal/feedback": ["@sentry-internal/feedback@10.46.0", "", { "dependencies": { "@sentry/core": "10.46.0" } }, "sha512-c4pI/z9nZCQXe9GYEw/hE/YTY9AxGBp8/wgKI+T8zylrN35SGHaXv63szzE1WbI8lacBY8lBF7rstq9bQVCaHw=="],
- "@sentry-internal/replay": ["@sentry-internal/replay@10.44.0", "", { "dependencies": { "@sentry-internal/browser-utils": "10.44.0", "@sentry/core": "10.44.0" } }, "sha512-KDmoqBsRmkaoc+eKLR2CbScd2eBmLcw+1+D441lLttAO3WWhvYyCaYdu/HIGGUoybuSgt+IcpCJdi7hFuCvYqw=="],
+ "@sentry-internal/replay": ["@sentry-internal/replay@10.46.0", "", { "dependencies": { "@sentry-internal/browser-utils": "10.46.0", "@sentry/core": "10.46.0" } }, "sha512-JBsWeXG6bRbxBFK8GzWymWGOB9QE7Kl57BeF3jzgdHTuHSWZ2mRnAmb1K05T4LU+gVygk6yW0KmdC8Py9Qzg9A=="],
- "@sentry-internal/replay-canvas": ["@sentry-internal/replay-canvas@10.44.0", "", { "dependencies": { "@sentry-internal/replay": "10.44.0", "@sentry/core": "10.44.0" } }, "sha512-RA7XgYZWHY7M+vaHvuMxDFT51wCs4puS2smElM5oh+j3YqbFXY7P16fOCwIAGoyI4gVsj8aTeBgVqUmrmzhAXQ=="],
+ "@sentry-internal/replay-canvas": ["@sentry-internal/replay-canvas@10.46.0", "", { "dependencies": { "@sentry-internal/replay": "10.46.0", "@sentry/core": "10.46.0" } }, "sha512-ub314MWUsekVCuoH0/HJbbimlI24SkV745UW2pj9xRbxOAEf1wjkmIzxKrMDbTgJGuEunug02XZVdJFJUzOcDw=="],
"@sentry/babel-plugin-component-annotate": ["@sentry/babel-plugin-component-annotate@5.1.1", "", {}, "sha512-x2wEpBHwsTyTF2rWsLKJlzrRF1TTIGOfX+ngdE+Yd5DBkoS58HwQv824QOviPGQRla4/ypISqAXzjdDPL/zalg=="],
- "@sentry/browser": ["@sentry/browser@10.44.0", "", { "dependencies": { "@sentry-internal/browser-utils": "10.44.0", "@sentry-internal/feedback": "10.44.0", "@sentry-internal/replay": "10.44.0", "@sentry-internal/replay-canvas": "10.44.0", "@sentry/core": "10.44.0" } }, "sha512-UpMx5forbVKieNULma3gT2SsLYqsYT4nLXa6s1io/Y8BFej9sH2dD5ExA8TrkQThQwAWFI3qKsQzYnF+EX/Bfg=="],
+ "@sentry/browser": ["@sentry/browser@10.46.0", "", { "dependencies": { "@sentry-internal/browser-utils": "10.46.0", "@sentry-internal/feedback": "10.46.0", "@sentry-internal/replay": "10.46.0", "@sentry-internal/replay-canvas": "10.46.0", "@sentry/core": "10.46.0" } }, "sha512-80DmGlTk5Z2/OxVOzLNxwolMyouuAYKqG8KUcoyintZqHbF6kO1RulI610HmyUt3OagKeBCqt9S7w0VIfCRL+Q=="],
- "@sentry/cli": ["@sentry/cli@3.3.3", "", { "dependencies": { "progress": "^2.0.3", "proxy-from-env": "^1.1.0", "undici": "^6.22.0", "which": "^2.0.2" }, "optionalDependencies": { "@sentry/cli-darwin": "3.3.3", "@sentry/cli-linux-arm": "3.3.3", "@sentry/cli-linux-arm64": "3.3.3", "@sentry/cli-linux-i686": "3.3.3", "@sentry/cli-linux-x64": "3.3.3", "@sentry/cli-win32-arm64": "3.3.3", "@sentry/cli-win32-i686": "3.3.3", "@sentry/cli-win32-x64": "3.3.3" }, "bin": { "sentry-cli": "bin/sentry-cli" } }, "sha512-4CZtfgiOraX+BntMjYQhfLDArXwpqt3sEo5Zdj2pqWSZSd4yI3ncfQ21CsxLcI/sUQrjmD5Vzidu4/1OShyxtA=="],
+ "@sentry/cli": ["@sentry/cli@3.3.4", "", { "dependencies": { "progress": "^2.0.3", "proxy-from-env": "^1.1.0", "undici": "^6.22.0", "which": "^2.0.2" }, "optionalDependencies": { "@sentry/cli-darwin": "3.3.4", "@sentry/cli-linux-arm": "3.3.4", "@sentry/cli-linux-arm64": "3.3.4", "@sentry/cli-linux-i686": "3.3.4", "@sentry/cli-linux-x64": "3.3.4", "@sentry/cli-win32-arm64": "3.3.4", "@sentry/cli-win32-i686": "3.3.4", "@sentry/cli-win32-x64": "3.3.4" }, "bin": { "sentry-cli": "bin/sentry-cli" } }, "sha512-r97H1GTdaRs1qhTvbzyomclPesrt4vpjY2W7KGtgSOa5ynQsXKajsM5oJOtNW99O1pNNMZFlR1mmGDMHOxYm4g=="],
- "@sentry/cli-darwin": ["@sentry/cli-darwin@3.3.3", "", { "os": "darwin" }, "sha512-P8DoL79eX5fhKCfBHHl7xwwTShDPOb2drJC8lizZ3v1iS1JLPrNweM1KEzDefR30zH1wghbLSwsYv/svWdM3wA=="],
+ "@sentry/cli-darwin": ["@sentry/cli-darwin@3.3.4", "", { "os": "darwin" }, "sha512-1cFHgwJq0yJ4lAvxQISag2R5R/wRtY7e4YX54Da4n+Isw1WHdF2CLmdT0ufOyT04iiF406UD2d7qsPEVDniAmw=="],
- "@sentry/cli-linux-arm": ["@sentry/cli-linux-arm@3.3.3", "", { "os": [ "linux", "android", "freebsd", ], "cpu": "arm" }, "sha512-a7o/huozveLIImXHe0HDwEMVhvDopOP2tLcopvV7sQsVE8f/QOShR5FudKjmiaZz2opdLzPJO9pv5WuF9jAZPg=="],
+ "@sentry/cli-linux-arm": ["@sentry/cli-linux-arm@3.3.4", "", { "os": [ "linux", "android", "freebsd", ], "cpu": "arm" }, "sha512-8XDDmUZ/4X7Dw2hoSU6T9tpD8qMwtVKHYLQjcY+xNBQujPrSq+YCrNXK/iIN9UgX8Rza2q4IftsIkJADOxLFow=="],
- "@sentry/cli-linux-arm64": ["@sentry/cli-linux-arm64@3.3.3", "", { "os": [ "linux", "android", "freebsd", ], "cpu": "arm64" }, "sha512-9jaX9RGyTpjo9u2urNi5ciBDpRdTt107YJpFXev+BFHJ6Lwz/owgRuYzPRfAen8hKkOOFheZ3iy07kl576eZzw=="],
+ "@sentry/cli-linux-arm64": ["@sentry/cli-linux-arm64@3.3.4", "", { "os": [ "linux", "android", "freebsd", ], "cpu": "arm64" }, "sha512-SD9YQjUPXjIBkt4q41lHMopeL9lKskaxc7qpt1ZuQpoHOszDOUNP3WPvxpeiaMjFMmgMkGojyDBk2XY9eyfGNQ=="],
- "@sentry/cli-linux-i686": ["@sentry/cli-linux-i686@3.3.3", "", { "os": [ "linux", "android", "freebsd", ], "cpu": "ia32" }, "sha512-VngQYzR2kDm2oojCuYF20ebLTK8HKvEwxe785J6gxob8Ef9JvZkERyUqENYppBa9aVgN0pandqPAqOECWykTMA=="],
+ "@sentry/cli-linux-i686": ["@sentry/cli-linux-i686@3.3.4", "", { "os": [ "linux", "android", "freebsd", ], "cpu": "ia32" }, "sha512-LNQlRDPrHLTDgxxJsAzT+1+sJ8Kv/Lq8E4Ob8RjqkwuZxl/wR6QJ4O83cxYGJPPnmjEAT+lOUQt1pTPXAwIwLQ=="],
- "@sentry/cli-linux-x64": ["@sentry/cli-linux-x64@3.3.3", "", { "os": [ "linux", "android", "freebsd", ], "cpu": "x64" }, "sha512-rBxXQeIYGefUNI2cXHxEr0y3bhxDQjOD4G6j/gqLz/Dj+l8gJ/iKP64kTudnoViNIpn0pdYccG69th7zmzM/Fg=="],
+ "@sentry/cli-linux-x64": ["@sentry/cli-linux-x64@3.3.4", "", { "os": [ "linux", "android", "freebsd", ], "cpu": "x64" }, "sha512-yEOVI4a0RTBYcHJBEaiFU2s4GzcfkXDToMUeLlUg4B3Bgz8AX76163RTEJH5dKavKkoMLKzOrKgVylXPxo1JLQ=="],
- "@sentry/cli-win32-arm64": ["@sentry/cli-win32-arm64@3.3.3", "", { "os": "win32", "cpu": "arm64" }, "sha512-c52g+YS6BO0rzH8AEHqQPmpqZrw0GJjMWqy0tQ5jcqaGdaLVnxk0mMEubv8R6Dv5MR2LShoKjiNsaeVfrWIMUg=="],
+ "@sentry/cli-win32-arm64": ["@sentry/cli-win32-arm64@3.3.4", "", { "os": "win32", "cpu": "arm64" }, "sha512-aLGgnIf7FHK+yRsemXGQ1yF0Q4R3D/jwCf/20k1miUgFP9fn5mZt+fArGDHr5k3vFfh3bUTf22Ga4CUwXqwkvQ=="],
- "@sentry/cli-win32-i686": ["@sentry/cli-win32-i686@3.3.3", "", { "os": "win32", "cpu": "ia32" }, "sha512-DygYzSY/+tS7oFj/mfeg/yzYxsQx3fO8cI+IWc2pns/at+JcJ9O5xyM/x/q55wOxpnwla7RL1D3rsqK2mqkYfg=="],
+ "@sentry/cli-win32-i686": ["@sentry/cli-win32-i686@3.3.4", "", { "os": "win32", "cpu": "ia32" }, "sha512-mLD5NpgI3G3+f1iBWGqTTC1kvdQ0CzmkvM9aIRiXUYWXZiaZVd4YuqhoDvTU6zNFEUXI+9jUEp84VF171B0Pqg=="],
- "@sentry/cli-win32-x64": ["@sentry/cli-win32-x64@3.3.3", "", { "os": "win32", "cpu": "x64" }, "sha512-i0glPcHwkqbVA2Y+0Yz7CD/l8TSkfft1a+lTU9yk/+DDU8WGkyArEAxAji9bGo4p+k5HIFC8OC2MwpKdcdFM4Q=="],
+ "@sentry/cli-win32-x64": ["@sentry/cli-win32-x64@3.3.4", "", { "os": "win32", "cpu": "x64" }, "sha512-dNWifGo3VLx7n3N3/m+7+rLGNZEb7JmnLwLLAHoz11DneCa6OTBSMCKFABArxqLinlzTbiSOYc8QbvCTcLk5FA=="],
- "@sentry/core": ["@sentry/core@10.44.0", "", {}, "sha512-aa7CiDaNFZvHpqd97LJhuskolfJ/4IH5xyuVVLnv7l6B0v9KTwskPUxb0tH1ej3FxuzfH+i8iTiTFuqpfHS3QA=="],
+ "@sentry/core": ["@sentry/core@10.46.0", "", {}, "sha512-N3fj4zqBQOhXliS1Ne9euqIKuciHCGOJfPGQLwBoW9DNz03jF+NB8+dUKtrJ79YLoftjVgf8nbgwtADK7NR+2Q=="],
- "@sentry/react": ["@sentry/react@10.44.0", "", { "dependencies": { "@sentry/browser": "10.44.0", "@sentry/core": "10.44.0" }, "peerDependencies": { "react": "^16.14.0 || 17.x || 18.x || 19.x" } }, "sha512-blaYoLk/UgFZXj9ieKZeY1JIiqzeL2VegQt22S9IQk8gHpunDZux5XC4CdcPdavcVusddaB/SmHAmhy2RCBdPQ=="],
+ "@sentry/react": ["@sentry/react@10.46.0", "", { "dependencies": { "@sentry/browser": "10.46.0", "@sentry/core": "10.46.0" }, "peerDependencies": { "react": "^16.14.0 || 17.x || 18.x || 19.x" } }, "sha512-Rb1S+9OuUPVwsz7GWnQ6Kgf3azbsseUymIegg3JZHNcW/fM1nPpaljzTBnuineia113DH0pgMBcdrrZDLaosFQ=="],
- "@sentry/react-native": ["@sentry/react-native@8.5.0", "", { "dependencies": { "@sentry/babel-plugin-component-annotate": "5.1.1", "@sentry/browser": "10.44.0", "@sentry/cli": "3.3.3", "@sentry/core": "10.44.0", "@sentry/react": "10.44.0", "@sentry/types": "10.44.0" }, "peerDependencies": { "expo": ">=49.0.0", "react": ">=17.0.0", "react-native": ">=0.65.0" }, "optionalPeers": ["expo"], "bin": { "sentry-eas-build-on-complete": "scripts/eas-build-hook.js", "sentry-eas-build-on-error": "scripts/eas-build-hook.js", "sentry-eas-build-on-success": "scripts/eas-build-hook.js", "sentry-expo-upload-sourcemaps": "scripts/expo-upload-sourcemaps.js" } }, "sha512-ZMemYiEIIXV7p4PcpRmDf9+dxWTbWgfSTgJ/cVIB3xiU3PtfLefupjpaNuwNNDK7pTA0amHzPgFDY5mdm/KE5w=="],
+ "@sentry/react-native": ["@sentry/react-native@8.6.0", "", { "dependencies": { "@sentry/babel-plugin-component-annotate": "5.1.1", "@sentry/browser": "10.46.0", "@sentry/cli": "3.3.4", "@sentry/core": "10.46.0", "@sentry/react": "10.46.0", "@sentry/types": "10.46.0" }, "peerDependencies": { "expo": ">=49.0.0", "react": ">=17.0.0", "react-native": ">=0.65.0" }, "optionalPeers": ["expo"], "bin": { "sentry-eas-build-on-complete": "scripts/eas-build-hook.js", "sentry-eas-build-on-error": "scripts/eas-build-hook.js", "sentry-eas-build-on-success": "scripts/eas-build-hook.js", "sentry-expo-upload-sourcemaps": "scripts/expo-upload-sourcemaps.js" } }, "sha512-sT44HU09BCxlFndTJaYJjxkd6OGAiTLd85rgXkShlllur5ITDg7GZmVgTv/ja4bPOqSMHAGMI9Tx3/VTDq3dgQ=="],
- "@sentry/types": ["@sentry/types@10.44.0", "", { "dependencies": { "@sentry/core": "10.44.0" } }, "sha512-eON+xfpPVZr246cPqByW2yoRiZsURTjNnXJ9qPiDf64lCUxAj1IZE4v14Ns+ZcrhUPadGMUVBwmDNnrfmEAy3A=="],
+ "@sentry/types": ["@sentry/types@10.46.0", "", { "dependencies": { "@sentry/core": "10.46.0" } }, "sha512-ZJWEuevCTzxA2z+C5NQ+bXKxA+oVnZM5QacBZ+RX6NHItwgYOcp4GNM6Wc9xTymOUvVmBC0Vcj1wbx7TA2JX0Q=="],
"@shopify/flash-list": ["@shopify/flash-list@2.0.2", "", { "dependencies": { "tslib": "2.8.1" }, "peerDependencies": { "@babel/runtime": "*", "react": "*", "react-native": "*" } }, "sha512-zhlrhA9eiuEzja4wxVvotgXHtqd3qsYbXkQ3rsBfOgbFA9BVeErpDE/yEwtlIviRGEqpuFj/oU5owD6ByaNX+w=="],
@@ -1409,7 +1409,7 @@
"@tanstack/history": ["@tanstack/history@1.161.6", "", {}, "sha512-NaOGLRrddszbQj9upGat6HG/4TKvXLvu+osAIgfxPYA+eIvYKv8GKDJOrY2D3/U9MRnKfMWD7bU4jeD4xmqyIg=="],
- "@tanstack/hotkeys": ["@tanstack/hotkeys@0.5.0", "", { "dependencies": { "@tanstack/store": "^0.9.2" } }, "sha512-OByddYW3N7Y9yHOWzf5cR7mqq1OKa9S1DMGLciUAlmZrt1xw31GpbauabcO0FicAkaDa7AqFm7+gXX93PVmxrA=="],
+ "@tanstack/hotkeys": ["@tanstack/hotkeys@0.6.1", "", { "dependencies": { "@tanstack/store": "^0.9.3" } }, "sha512-9JSqG0Go2r0Hj2z7UfK05YOGsWK2bwfoa880emVe340o0Vp4hrDHSDNV2bmR72wHmuf9dLVms9Gkx2ZCDjEpkA=="],
"@tanstack/pacer-lite": ["@tanstack/pacer-lite@0.1.1", "", {}, "sha512-y/xtNPNt/YeyoVxE/JCx+T7yjEzpezmbb+toK8DDD1P4m7Kzs5YR956+7OKexG3f8aXgC3rLZl7b1V+yNUSy5w=="],
@@ -1425,7 +1425,7 @@
"@tanstack/react-form": ["@tanstack/react-form@1.28.5", "", { "dependencies": { "@tanstack/form-core": "1.28.5", "@tanstack/react-store": "^0.9.1" }, "peerDependencies": { "react": "^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-CL8IeWkeXnEEDsHt5wBuIOZvSYrKiLRtsC9ca0LzfJJ22SYCma9cBmh1UX1EBX0o3gH2U21PmUf+y5f9OJNoEQ=="],
- "@tanstack/react-hotkeys": ["@tanstack/react-hotkeys@0.6.0", "", { "dependencies": { "@tanstack/hotkeys": "0.5.0", "@tanstack/react-store": "^0.9.2" }, "peerDependencies": { "react": ">=16.8", "react-dom": ">=16.8" } }, "sha512-9c/31SV5VmuhPKUJdSSseDBSlmBzqK7Lae9ig9SQI8nvsZDEDZ48L/k2IkWaYsxrPQPVDL1ZIuQjlw/xhz4LZg=="],
+ "@tanstack/react-hotkeys": ["@tanstack/react-hotkeys@0.8.1", "", { "dependencies": { "@tanstack/hotkeys": "0.6.1", "@tanstack/react-store": "^0.9.3" }, "peerDependencies": { "react": ">=16.8", "react-dom": ">=16.8" } }, "sha512-U5j2ulCAJ6FaEctYt5loNOFicrzYrESQXctx/tgPbgysR/YZSvI9UdBIRuvyHW0unwlnei8MEgYkNLGZFufDCw=="],
"@tanstack/react-query": ["@tanstack/react-query@5.95.2", "", { "dependencies": { "@tanstack/query-core": "5.95.2" }, "peerDependencies": { "react": "^18 || ^19" } }, "sha512-/wGkvLj/st5Ud1Q76KF1uFxScV7WeqN1slQx5280ycwAyYkIPGaRZAEgHxe3bjirSd5Zpwkj6zNcR4cqYni/ZA=="],
@@ -1433,23 +1433,23 @@
"@tanstack/react-query-persist-client": ["@tanstack/react-query-persist-client@5.95.2", "", { "dependencies": { "@tanstack/query-persist-client-core": "5.95.2" }, "peerDependencies": { "@tanstack/react-query": "^5.95.2", "react": "^18 || ^19" } }, "sha512-i3fvzD8gaLgQyFvRc/+iSUr60aL31tMN+5QM11zdPRg0K9CirIQjHD7WgXFBnD29KJDvcjcv7OrIBaPwZ+H9xw=="],
- "@tanstack/react-router": ["@tanstack/react-router@1.168.3", "", { "dependencies": { "@tanstack/history": "1.161.6", "@tanstack/react-store": "^0.9.2", "@tanstack/router-core": "1.168.3", "isbot": "^5.1.22" }, "peerDependencies": { "react": ">=18.0.0 || >=19.0.0", "react-dom": ">=18.0.0 || >=19.0.0" } }, "sha512-hMWXhckeaSvjepHT5x9tUYJVXMvT/kUjaVHOUDmCfyOBtjxJNYJKbEWClXoopGwWlHjRTAzhsndhnQQRbIiKmA=="],
+ "@tanstack/react-router": ["@tanstack/react-router@1.168.5", "", { "dependencies": { "@tanstack/history": "1.161.6", "@tanstack/react-store": "^0.9.3", "@tanstack/router-core": "1.168.5", "isbot": "^5.1.22" }, "peerDependencies": { "react": ">=18.0.0 || >=19.0.0", "react-dom": ">=18.0.0 || >=19.0.0" } }, "sha512-jh+S5bqvHxH7ng4KKsN3wTYegJIP4V68dGrgUUwmYSmipb4cJyq+09TqCWVRXQ9f9xVeESRAqotAgK7GOS1uFQ=="],
"@tanstack/react-router-devtools": ["@tanstack/react-router-devtools@1.166.11", "", { "dependencies": { "@tanstack/router-devtools-core": "1.167.1" }, "peerDependencies": { "@tanstack/react-router": "^1.168.2", "@tanstack/router-core": "^1.168.2", "react": ">=18.0.0 || >=19.0.0", "react-dom": ">=18.0.0 || >=19.0.0" }, "optionalPeers": ["@tanstack/router-core"] }, "sha512-WYR3q4Xui5yPT/5PXtQh8i03iUA7q8dONBjWpV3nsGdM8Cs1FxpfhLstW0wZO1dOvSyElscwTRCJ6nO5N8r3Lg=="],
- "@tanstack/react-store": ["@tanstack/react-store@0.9.2", "", { "dependencies": { "@tanstack/store": "0.9.2", "use-sync-external-store": "^1.6.0" }, "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" } }, "sha512-Vt5usJE5sHG/cMechQfmwvwne6ktGCELe89Lmvoxe3LKRoFrhPa8OCKWs0NliG8HTJElEIj7PLtaBQIcux5pAQ=="],
+ "@tanstack/react-store": ["@tanstack/react-store@0.9.3", "", { "dependencies": { "@tanstack/store": "0.9.3", "use-sync-external-store": "^1.6.0" }, "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" } }, "sha512-y2iHd/N9OkoQbFJLUX1T9vbc2O9tjH0pQRgTcx1/Nz4IlwLvkgpuglXUx+mXt0g5ZDFrEeDnONPqkbfxXJKwRg=="],
- "@tanstack/router-core": ["@tanstack/router-core@1.168.3", "", { "dependencies": { "@tanstack/history": "1.161.6", "cookie-es": "^2.0.0", "seroval": "^1.4.2", "seroval-plugins": "^1.4.2" }, "bin": { "intent": "bin/intent.js" } }, "sha512-qcjArls3v12UQQkEpU0+todc0/MCyrEZeXxhtgZZ0e5gxZDG25BUe/HlNcIjzyb7NZaw0TQAUBXbTClmFaHZiw=="],
+ "@tanstack/router-core": ["@tanstack/router-core@1.168.5", "", { "dependencies": { "@tanstack/history": "1.161.6", "cookie-es": "^2.0.0", "seroval": "^1.4.2", "seroval-plugins": "^1.4.2" }, "bin": { "intent": "bin/intent.js" } }, "sha512-k378TPlJBM1x+7ixgQL+gC+v6omN6vF31QuhOBzcISQ1oW/oyHsJf8D4OWnZ4wJD63vD2P5kXLb8QcHM419oeg=="],
"@tanstack/router-devtools-core": ["@tanstack/router-devtools-core@1.167.1", "", { "dependencies": { "clsx": "^2.1.1", "goober": "^2.1.16" }, "peerDependencies": { "@tanstack/router-core": "^1.168.2", "csstype": "^3.0.10" }, "optionalPeers": ["csstype"] }, "sha512-ECMM47J4KmifUvJguGituSiBpfN8SyCUEoxQks5RY09hpIBfR2eswCv2e6cJimjkKwBQXOVTPkTUk/yRvER+9w=="],
- "@tanstack/router-generator": ["@tanstack/router-generator@1.166.17", "", { "dependencies": { "@tanstack/router-core": "1.168.3", "@tanstack/router-utils": "1.161.6", "@tanstack/virtual-file-routes": "1.161.7", "prettier": "^3.5.0", "recast": "^0.23.11", "source-map": "^0.7.4", "tsx": "^4.19.2", "zod": "^3.24.2" } }, "sha512-sBs6lyvA+B51hpUWYLx0KdaAIO/m9Ml2bsAdfVYyvs5DZXiAZZEbVD0myndyIkWaPR5x+kzuBakkrgTxJ9/m9Q=="],
+ "@tanstack/router-generator": ["@tanstack/router-generator@1.166.19", "", { "dependencies": { "@tanstack/router-core": "1.168.5", "@tanstack/router-utils": "1.161.6", "@tanstack/virtual-file-routes": "1.161.7", "prettier": "^3.5.0", "recast": "^0.23.11", "source-map": "^0.7.4", "tsx": "^4.19.2", "zod": "^3.24.2" } }, "sha512-fSmeX99DOZrhL0fcc5S0VL1RNyImzTKzrP2SPXoee9dOGypWSln3tDce7LfbZS2JdwIXpyE+jQZfe67nXYUWig=="],
- "@tanstack/router-plugin": ["@tanstack/router-plugin@1.167.4", "", { "dependencies": { "@babel/core": "^7.28.5", "@babel/plugin-syntax-jsx": "^7.27.1", "@babel/plugin-syntax-typescript": "^7.27.1", "@babel/template": "^7.27.2", "@babel/traverse": "^7.28.5", "@babel/types": "^7.28.5", "@tanstack/router-core": "1.168.3", "@tanstack/router-generator": "1.166.17", "@tanstack/router-utils": "1.161.6", "@tanstack/virtual-file-routes": "1.161.7", "chokidar": "^3.6.0", "unplugin": "^2.1.2", "zod": "^3.24.2" }, "peerDependencies": { "@rsbuild/core": ">=1.0.2", "@tanstack/react-router": "^1.168.3", "vite": ">=5.0.0 || >=6.0.0 || >=7.0.0", "vite-plugin-solid": "^2.11.10", "webpack": ">=5.92.0" }, "optionalPeers": ["@rsbuild/core", "@tanstack/react-router", "vite", "vite-plugin-solid", "webpack"], "bin": { "intent": "bin/intent.js" } }, "sha512-VChByI+CHdHMW350E6winbgqdX4tzmZIHovys8vXidRZkxGAhlygj/zhbnepF/TGX88rubj+SXDwSHY25qEcpQ=="],
+ "@tanstack/router-plugin": ["@tanstack/router-plugin@1.167.6", "", { "dependencies": { "@babel/core": "^7.28.5", "@babel/plugin-syntax-jsx": "^7.27.1", "@babel/plugin-syntax-typescript": "^7.27.1", "@babel/template": "^7.27.2", "@babel/traverse": "^7.28.5", "@babel/types": "^7.28.5", "@tanstack/router-core": "1.168.5", "@tanstack/router-generator": "1.166.19", "@tanstack/router-utils": "1.161.6", "@tanstack/virtual-file-routes": "1.161.7", "chokidar": "^3.6.0", "unplugin": "^2.1.2", "zod": "^3.24.2" }, "peerDependencies": { "@rsbuild/core": ">=1.0.2", "@tanstack/react-router": "^1.168.5", "vite": ">=5.0.0 || >=6.0.0 || >=7.0.0", "vite-plugin-solid": "^2.11.10", "webpack": ">=5.92.0" }, "optionalPeers": ["@rsbuild/core", "@tanstack/react-router", "vite", "vite-plugin-solid", "webpack"], "bin": { "intent": "bin/intent.js" } }, "sha512-ktl4ErXjY1a08EAcvIwddnDTjbx7wmFbcYFtSLAx09ZPOxpMbK5wiHu7dlzw9Acv0hc176OzV1o4rrGV6t4RqQ=="],
"@tanstack/router-utils": ["@tanstack/router-utils@1.161.6", "", { "dependencies": { "@babel/core": "^7.28.5", "@babel/generator": "^7.28.5", "@babel/parser": "^7.28.5", "@babel/types": "^7.28.5", "ansis": "^4.1.0", "babel-dead-code-elimination": "^1.0.12", "diff": "^8.0.2", "pathe": "^2.0.3", "tinyglobby": "^0.2.15" } }, "sha512-nRcYw+w2OEgK6VfjirYvGyPLOK+tZQz1jkYcmH5AjMamQ9PycnlxZF2aEZtPpNoUsaceX2bHptn6Ub5hGXqNvw=="],
- "@tanstack/store": ["@tanstack/store@0.9.2", "", {}, "sha512-K013lUJEFJK2ofFQ/hZKJUmCnpcV00ebLyOyFOWQvyQHUOZp/iYO84BM6aOGiV81JzwbX0APTVmW8YI7yiG5oA=="],
+ "@tanstack/store": ["@tanstack/store@0.9.3", "", {}, "sha512-8reSzl/qGWGGVKhBoxXPMWzATSbZLZFWhwBAFO9NAyp0TxzfBP0mIrGb8CP8KrQTmvzXlR/vFPPUrHTLBGyFyw=="],
"@tanstack/virtual-file-routes": ["@tanstack/virtual-file-routes@1.161.7", "", { "bin": { "intent": "bin/intent.js" } }, "sha512-olW33+Cn+bsCsZKPwEGhlkqS6w3M2slFv11JIobdnCFKMLG97oAI2kWKdx5/zsywTL8flpnoIgaZZPlQTFYhdQ=="],
@@ -1507,7 +1507,7 @@
"@types/json-schema": ["@types/json-schema@7.0.15", "", {}, "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="],
- "@types/mssql": ["@types/mssql@9.1.10", "", { "dependencies": { "@types/node": "*", "tarn": "^3.0.1", "tedious": "*" } }, "sha512-h3ImCiKdO7SM+2P1ToAZ+O0Ip23iISUHplH5GgMGnM9AwjmusDv8QqoNztoNj6vXpFbX29axsIybte7J2Q3XNg=="],
+ "@types/mssql": ["@types/mssql@9.1.11", "", { "dependencies": { "@types/node": "*", "tarn": "^3.0.1", "tedious": "*" } }, "sha512-vcujgrDbDezCxNDO4KY6gjwduLYOKfrexpRUwhoysRvcXZ3+IgZ/PMYFDgh8c3cQIxZ6skAwYo+H6ibMrBWPjQ=="],
"@types/node": ["@types/node@25.5.0", "", { "dependencies": { "undici-types": "~7.18.0" } }, "sha512-jp2P3tQMSxWugkCUKLRPVUpGaL5MVFwF8RDuSRztfwgN1wmqJeMSbKlnEtQqU8UrhTmzEmZdu2I6v2dpp7XIxw=="],
@@ -1571,25 +1571,25 @@
"@vitejs/plugin-react": ["@vitejs/plugin-react@6.0.1", "", { "dependencies": { "@rolldown/pluginutils": "1.0.0-rc.7" }, "peerDependencies": { "@rolldown/plugin-babel": "^0.1.7 || ^0.2.0", "babel-plugin-react-compiler": "^1.0.0", "vite": "^8.0.0" }, "optionalPeers": ["@rolldown/plugin-babel", "babel-plugin-react-compiler"] }, "sha512-l9X/E3cDb+xY3SWzlG1MOGt2usfEHGMNIaegaUGFsLkb3RCn/k8/TOXBcab+OndDI4TBtktT8/9BwwW8Vi9KUQ=="],
- "@vitest/browser": ["@vitest/browser@4.1.1", "", { "dependencies": { "@blazediff/core": "1.9.1", "@vitest/mocker": "4.1.1", "@vitest/utils": "4.1.1", "magic-string": "^0.30.21", "pngjs": "^7.0.0", "sirv": "^3.0.2", "tinyrainbow": "^3.0.3", "ws": "^8.19.0" }, "peerDependencies": { "vitest": "4.1.1" } }, "sha512-gjjrFC4+kPVK/fN9URDJWrssU5Gqh8Az8pKG/NSfQ2V+ky8b/y1BgBg0Ug13+hOGp5pzInonmGRPn7vOgSLgzA=="],
+ "@vitest/browser": ["@vitest/browser@4.1.2", "", { "dependencies": { "@blazediff/core": "1.9.1", "@vitest/mocker": "4.1.2", "@vitest/utils": "4.1.2", "magic-string": "^0.30.21", "pngjs": "^7.0.0", "sirv": "^3.0.2", "tinyrainbow": "^3.1.0", "ws": "^8.19.0" }, "peerDependencies": { "vitest": "4.1.2" } }, "sha512-CwdIf90LNf1Zitgqy63ciMAzmyb4oIGs8WZ40VGYrWkssQKeEKr32EzO8MKUrDPPcPVHFI9oQ5ni2Hp24NaNRQ=="],
- "@vitest/browser-playwright": ["@vitest/browser-playwright@4.1.1", "", { "dependencies": { "@vitest/browser": "4.1.1", "@vitest/mocker": "4.1.1", "tinyrainbow": "^3.0.3" }, "peerDependencies": { "playwright": "*", "vitest": "4.1.1" } }, "sha512-dtVSBZZha2k/7P7EAXXrEAoxuIKl8Yv9f2Dk4GN/DGfmhf4DQvkvu+57okR2wq/gan1xppKjL/aBxK/kbYrbGw=="],
+ "@vitest/browser-playwright": ["@vitest/browser-playwright@4.1.2", "", { "dependencies": { "@vitest/browser": "4.1.2", "@vitest/mocker": "4.1.2", "tinyrainbow": "^3.1.0" }, "peerDependencies": { "playwright": "*", "vitest": "4.1.2" } }, "sha512-N0Z2HzMLvMR6k/tWPTS6Q/DaRscrkax/f2f9DIbNQr+Cd1l4W4wTf/I6S983PAMr0tNqqoTL+xNkLh9M5vbkLg=="],
- "@vitest/coverage-v8": ["@vitest/coverage-v8@4.1.1", "", { "dependencies": { "@bcoe/v8-coverage": "^1.0.2", "@vitest/utils": "4.1.1", "ast-v8-to-istanbul": "^1.0.0", "istanbul-lib-coverage": "^3.2.2", "istanbul-lib-report": "^3.0.1", "istanbul-reports": "^3.2.0", "magicast": "^0.5.2", "obug": "^2.1.1", "std-env": "^4.0.0-rc.1", "tinyrainbow": "^3.0.3" }, "peerDependencies": { "@vitest/browser": "4.1.1", "vitest": "4.1.1" }, "optionalPeers": ["@vitest/browser"] }, "sha512-nZ4RWwGCoGOQRMmU/Q9wlUY540RVRxJZ9lxFsFfy0QV7Zmo5VVBhB6Sl9Xa0KIp2iIs3zWfPlo9LcY1iqbpzCw=="],
+ "@vitest/coverage-v8": ["@vitest/coverage-v8@4.1.2", "", { "dependencies": { "@bcoe/v8-coverage": "^1.0.2", "@vitest/utils": "4.1.2", "ast-v8-to-istanbul": "^1.0.0", "istanbul-lib-coverage": "^3.2.2", "istanbul-lib-report": "^3.0.1", "istanbul-reports": "^3.2.0", "magicast": "^0.5.2", "obug": "^2.1.1", "std-env": "^4.0.0-rc.1", "tinyrainbow": "^3.1.0" }, "peerDependencies": { "@vitest/browser": "4.1.2", "vitest": "4.1.2" }, "optionalPeers": ["@vitest/browser"] }, "sha512-sPK//PHO+kAkScb8XITeB1bf7fsk85Km7+rt4eeuRR3VS1/crD47cmV5wicisJmjNdfeokTZwjMk4Mj2d58Mgg=="],
- "@vitest/expect": ["@vitest/expect@4.1.1", "", { "dependencies": { "@standard-schema/spec": "^1.1.0", "@types/chai": "^5.2.2", "@vitest/spy": "4.1.1", "@vitest/utils": "4.1.1", "chai": "^6.2.2", "tinyrainbow": "^3.0.3" } }, "sha512-xAV0fqBTk44Rn6SjJReEQkHP3RrqbJo6JQ4zZ7/uVOiJZRarBtblzrOfFIZeYUrukp2YD6snZG6IBqhOoHTm+A=="],
+ "@vitest/expect": ["@vitest/expect@4.1.2", "", { "dependencies": { "@standard-schema/spec": "^1.1.0", "@types/chai": "^5.2.2", "@vitest/spy": "4.1.2", "@vitest/utils": "4.1.2", "chai": "^6.2.2", "tinyrainbow": "^3.1.0" } }, "sha512-gbu+7B0YgUJ2nkdsRJrFFW6X7NTP44WlhiclHniUhxADQJH5Szt9mZ9hWnJPJ8YwOK5zUOSSlSvyzRf0u1DSBQ=="],
- "@vitest/mocker": ["@vitest/mocker@4.1.1", "", { "dependencies": { "@vitest/spy": "4.1.1", "estree-walker": "^3.0.3", "magic-string": "^0.30.21" }, "peerDependencies": { "msw": "^2.4.9", "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" }, "optionalPeers": ["msw", "vite"] }, "sha512-h3BOylsfsCLPeceuCPAAJ+BvNwSENgJa4hXoXu4im0bs9Lyp4URc4JYK4pWLZ4pG/UQn7AT92K6IByi6rE6g3A=="],
+ "@vitest/mocker": ["@vitest/mocker@4.1.2", "", { "dependencies": { "@vitest/spy": "4.1.2", "estree-walker": "^3.0.3", "magic-string": "^0.30.21" }, "peerDependencies": { "msw": "^2.4.9", "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" }, "optionalPeers": ["msw", "vite"] }, "sha512-Ize4iQtEALHDttPRCmN+FKqOl2vxTiNUhzobQFFt/BM1lRUTG7zRCLOykG/6Vo4E4hnUdfVLo5/eqKPukcWW7Q=="],
- "@vitest/pretty-format": ["@vitest/pretty-format@4.1.1", "", { "dependencies": { "tinyrainbow": "^3.0.3" } }, "sha512-GM+TEQN5WhOygr1lp7skeVjdLPqqWMHsfzXrcHAqZJi/lIVh63H0kaRCY8MDhNWikx19zBUK8ceaLB7X5AH9NQ=="],
+ "@vitest/pretty-format": ["@vitest/pretty-format@4.1.2", "", { "dependencies": { "tinyrainbow": "^3.1.0" } }, "sha512-dwQga8aejqeuB+TvXCMzSQemvV9hNEtDDpgUKDzOmNQayl2OG241PSWeJwKRH3CiC+sESrmoFd49rfnq7T4RnA=="],
- "@vitest/runner": ["@vitest/runner@4.1.1", "", { "dependencies": { "@vitest/utils": "4.1.1", "pathe": "^2.0.3" } }, "sha512-f7+FPy75vN91QGWsITueq0gedwUZy1fLtHOCMeQpjs8jTekAHeKP80zfDEnhrleviLHzVSDXIWuCIOFn3D3f8A=="],
+ "@vitest/runner": ["@vitest/runner@4.1.2", "", { "dependencies": { "@vitest/utils": "4.1.2", "pathe": "^2.0.3" } }, "sha512-Gr+FQan34CdiYAwpGJmQG8PgkyFVmARK8/xSijia3eTFgVfpcpztWLuP6FttGNfPLJhaZVP/euvujeNYar36OQ=="],
- "@vitest/snapshot": ["@vitest/snapshot@4.1.1", "", { "dependencies": { "@vitest/pretty-format": "4.1.1", "@vitest/utils": "4.1.1", "magic-string": "^0.30.21", "pathe": "^2.0.3" } }, "sha512-kMVSgcegWV2FibXEx9p9WIKgje58lcTbXgnJixfcg15iK8nzCXhmalL0ZLtTWLW9PH1+1NEDShiFFedB3tEgWg=="],
+ "@vitest/snapshot": ["@vitest/snapshot@4.1.2", "", { "dependencies": { "@vitest/pretty-format": "4.1.2", "@vitest/utils": "4.1.2", "magic-string": "^0.30.21", "pathe": "^2.0.3" } }, "sha512-g7yfUmxYS4mNxk31qbOYsSt2F4m1E02LFqO53Xpzg3zKMhLAPZAjjfyl9e6z7HrW6LvUdTwAQR3HHfLjpko16A=="],
- "@vitest/spy": ["@vitest/spy@4.1.1", "", {}, "sha512-6Ti/KT5OVaiupdIZEuZN7l3CZcR0cxnxt70Z0//3CtwgObwA6jZhmVBA3yrXSVN3gmwjgd7oDNLlsXz526gpRA=="],
+ "@vitest/spy": ["@vitest/spy@4.1.2", "", {}, "sha512-DU4fBnbVCJGNBwVA6xSToNXrkZNSiw59H8tcuUspVMsBDBST4nfvsPsEHDHGtWRRnqBERBQu7TrTKskmjqTXKA=="],
- "@vitest/utils": ["@vitest/utils@4.1.1", "", { "dependencies": { "@vitest/pretty-format": "4.1.1", "convert-source-map": "^2.0.0", "tinyrainbow": "^3.0.3" } }, "sha512-cNxAlaB3sHoCdL6pj6yyUXv9Gry1NHNg0kFTXdvSIZXLHsqKH7chiWOkwJ5s5+d/oMwcoG9T0bKU38JZWKusrQ=="],
+ "@vitest/utils": ["@vitest/utils@4.1.2", "", { "dependencies": { "@vitest/pretty-format": "4.1.2", "convert-source-map": "^2.0.0", "tinyrainbow": "^3.1.0" } }, "sha512-xw2/TiX82lQHA06cgbqRKFb5lCAy3axQ4H4SoUFhUsg+wztiet+co86IAMDtF6Vm1hc7J6j09oh/rgDn+JdKIQ=="],
"@xmldom/xmldom": ["@xmldom/xmldom@0.8.11", "", {}, "sha512-cQzWCtO6C8TQiYl1ruKNn2U6Ao4o4WBBcbL61yJl84x+j5sOWWFU9X7DpND8XZG3daDppSsigMdfAIl2upQBRw=="],
@@ -1667,11 +1667,11 @@
"babel-preset-jest": ["babel-preset-jest@29.6.3", "", { "dependencies": { "babel-plugin-jest-hoist": "^29.6.3", "babel-preset-current-node-syntax": "^1.0.0" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA=="],
- "balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="],
+ "balanced-match": ["balanced-match@4.0.4", "", {}, "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA=="],
"base64-js": ["base64-js@1.5.1", "", {}, "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="],
- "baseline-browser-mapping": ["baseline-browser-mapping@2.10.10", "", { "bin": { "baseline-browser-mapping": "dist/cli.cjs" } }, "sha512-sUoJ3IMxx4AyRqO4MLeHlnGDkyXRoUG0/AI9fjK+vS72ekpV0yWVY7O0BVjmBcRtkNcsAO2QDZ4tdKKGoI6YaQ=="],
+ "baseline-browser-mapping": ["baseline-browser-mapping@2.10.11", "", { "bin": { "baseline-browser-mapping": "dist/cli.cjs" } }, "sha512-DAKrHphkJyiGuau/cFieRYhcTFeK/lBuD++C7cZ6KZHbMhBrisoi+EvhQ5RZrIfV5qwsW8kgQ07JIC+MDJRAhg=="],
"better-auth": ["better-auth@1.5.6", "", { "dependencies": { "@better-auth/core": "1.5.6", "@better-auth/drizzle-adapter": "1.5.6", "@better-auth/kysely-adapter": "1.5.6", "@better-auth/memory-adapter": "1.5.6", "@better-auth/mongo-adapter": "1.5.6", "@better-auth/prisma-adapter": "1.5.6", "@better-auth/telemetry": "1.5.6", "@better-auth/utils": "0.3.1", "@better-fetch/fetch": "1.1.21", "@noble/ciphers": "^2.1.1", "@noble/hashes": "^2.0.1", "better-call": "1.3.2", "defu": "^6.1.4", "jose": "^6.1.3", "kysely": "^0.28.12", "nanostores": "^1.1.1", "zod": "^4.3.6" }, "peerDependencies": { "@lynx-js/react": "*", "@prisma/client": "^5.0.0 || ^6.0.0 || ^7.0.0", "@sveltejs/kit": "^2.0.0", "@tanstack/react-start": "^1.0.0", "@tanstack/solid-start": "^1.0.0", "better-sqlite3": "^12.0.0", "drizzle-kit": ">=0.31.4", "drizzle-orm": ">=0.41.0", "mongodb": "^6.0.0 || ^7.0.0", "mysql2": "^3.0.0", "next": "^14.0.0 || ^15.0.0 || ^16.0.0", "pg": "^8.0.0", "prisma": "^5.0.0 || ^6.0.0 || ^7.0.0", "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0", "solid-js": "^1.0.0", "svelte": "^4.0.0 || ^5.0.0", "vitest": "^2.0.0 || ^3.0.0 || ^4.0.0", "vue": "^3.0.0" }, "optionalPeers": ["@lynx-js/react", "@prisma/client", "@sveltejs/kit", "@tanstack/react-start", "@tanstack/solid-start", "better-sqlite3", "drizzle-kit", "drizzle-orm", "mongodb", "mysql2", "next", "pg", "prisma", "react", "react-dom", "solid-js", "svelte", "vitest", "vue"] }, "sha512-QSpJTqaT1XVfWRQe/fm3PgeuwOIlz1nWX/Dx7nsHStJ382bLzmDbQk2u7IT0IJ6wS5SRxfqEE1Ev9TXontgyAQ=="],
@@ -1699,7 +1699,7 @@
"bplist-parser": ["bplist-parser@0.3.2", "", { "dependencies": { "big-integer": "1.6.x" } }, "sha512-apC2+fspHGI3mMKj+dGevkGo/tCqVB8jMb6i+OX+E29p0Iposz07fABkRIfVUPNd5A5VbuOz1bZbnmkKLYF+wQ=="],
- "brace-expansion": ["brace-expansion@1.1.12", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="],
+ "brace-expansion": ["brace-expansion@5.0.5", "", { "dependencies": { "balanced-match": "^4.0.2" } }, "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ=="],
"braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="],
@@ -1891,7 +1891,7 @@
"ee-first": ["ee-first@1.1.1", "", {}, "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="],
- "electron-to-chromium": ["electron-to-chromium@1.5.322", "", {}, "sha512-vFU34OcrvMcH66T+dYC3G4nURmgfDVewMIu6Q2urXpumAPSMmzvcn04KVVV8Opikq8Vs5nUbO/8laNhNRqSzYw=="],
+ "electron-to-chromium": ["electron-to-chromium@1.5.325", "", {}, "sha512-PwfIw7WQSt3xX7yOf5OE/unLzsK9CaN2f/FvV3WjPR1Knoc1T9vePRVV4W1EM301JzzysK51K7FNKcusCr0zYA=="],
"emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="],
@@ -1933,7 +1933,7 @@
"eslint-plugin-drizzle": ["eslint-plugin-drizzle@0.2.3", "", { "peerDependencies": { "eslint": ">=8.0.0" } }, "sha512-BO+ymHo33IUNoJlC0rbd7HP9EwwpW4VIp49R/tWQF/d2E1K2kgTf0tCXT0v9MSiBr6gGR1LtPwMLapTKEWSg9A=="],
- "eslint-plugin-lingui": ["eslint-plugin-lingui@0.11.0", "", { "dependencies": { "@typescript-eslint/utils": "^8.0.0", "micromatch": "^4.0.0" }, "peerDependencies": { "eslint": "^8.37.0 || ^9.0.0" } }, "sha512-O2Ixoapt5fa4VKZJgXhVwb6BHnzByIUDNMfZOhHWGMYk40GfGCho4MUfspLVrHAFLimgBPKXtCcJ8GC4YNZmfg=="],
+ "eslint-plugin-lingui": ["eslint-plugin-lingui@0.12.0", "", { "dependencies": { "@typescript-eslint/utils": "^8.57.2", "micromatch": "^4.0.8" }, "peerDependencies": { "eslint": "^8.37.0 || ^9.0.0", "typescript": "^5.0.0 || ^6.0.0" } }, "sha512-2+9P3thudGIBI10sDWYUIrGs3HIP09gv0XF98RDzZs34GAsAsGTUoSgrttKS1knAU5dwbrFKhNKu2LMrjRECag=="],
"eslint-plugin-react-hooks": ["eslint-plugin-react-hooks@7.0.1", "", { "dependencies": { "@babel/core": "^7.24.4", "@babel/parser": "^7.24.4", "hermes-parser": "^0.25.1", "zod": "^3.25.0 || ^4.0.0", "zod-validation-error": "^3.5.0 || ^4.0.0" }, "peerDependencies": { "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0" } }, "sha512-O0d0m04evaNzEPoSW+59Mezf8Qt0InfgGIBJnpC0h3NH/WjUAR7BIKUfysC6todmtiZ/A0oUVS8Gce0WhBrHsA=="],
@@ -2085,7 +2085,7 @@
"fetch-blob": ["fetch-blob@3.2.0", "", { "dependencies": { "node-domexception": "^1.0.0", "web-streams-polyfill": "^3.0.3" } }, "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ=="],
- "fetch-nodeshim": ["fetch-nodeshim@0.4.9", "", {}, "sha512-XIQWlB2A4RZ7NebXWGxS0uDMdvRHkiUDTghBVJKFg9yEOd45w/PP8cZANuPf2H08W6Cor3+2n7Q6TTZgAS3Fkw=="],
+ "fetch-nodeshim": ["fetch-nodeshim@0.4.10", "", {}, "sha512-m6I8ALe4L4XpdETy7MJZWs6L1IVMbjs99bwbpIKphxX+0CTns4IKDWJY0LWfr4YsFjfg+z1TjzTMU8lKl8rG0w=="],
"figures": ["figures@6.1.0", "", { "dependencies": { "is-unicode-supported": "^2.0.0" } }, "sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg=="],
@@ -2161,7 +2161,7 @@
"glob": ["glob@11.1.0", "", { "dependencies": { "foreground-child": "^3.3.1", "jackspeak": "^4.1.1", "minimatch": "^10.1.1", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", "path-scurry": "^2.0.0" }, "bin": { "glob": "dist/esm/bin.mjs" } }, "sha512-vuNwKSaKiqm7g0THUBu2x7ckSs3XJLXE+2ssL7/MfTGPLLcrJQ/4Uq1CjPTtO5cCIiRxqvN6Twy1qOwhL0Xjcw=="],
- "glob-parent": ["glob-parent@6.0.2", "", { "dependencies": { "is-glob": "^4.0.3" } }, "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A=="],
+ "glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="],
"globals": ["globals@14.0.0", "", {}, "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ=="],
@@ -2435,7 +2435,7 @@
"mdn-data": ["mdn-data@2.0.14", "", {}, "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow=="],
- "media-chrome": ["media-chrome@4.18.2", "", { "dependencies": { "ce-la-react": "^0.3.2" } }, "sha512-Ywy34ZalIXr/Ss1efv3KU9I02hb8PF7l+62kKwhxDkUjwcWM8Ashl0P7Ya6YcuxENaJLycsRgikG3hz52GhpIA=="],
+ "media-chrome": ["media-chrome@4.18.3", "", { "dependencies": { "ce-la-react": "^0.3.2" } }, "sha512-YuS2wY0Fn+2nXGijJYn4+IE0n9wFe3v6SvOZHGNkoxh32T/cCcrXHUWskA+9tyYTONa6JKwKAOJJeO6QOlJLKw=="],
"media-played-ranges-mixin": ["media-played-ranges-mixin@0.1.0", "", {}, "sha512-zTsvkleu5sAyTsPVxDI+KUbCwy/lXwHgOPi3ER9S3lhtAWhGTQH6qxvfrVMym1cvoLU36SPbVr6Qe8Zxyc0WpA=="],
@@ -2491,7 +2491,7 @@
"mimic-response": ["mimic-response@3.1.0", "", {}, "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ=="],
- "minimatch": ["minimatch@3.1.5", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w=="],
+ "minimatch": ["minimatch@10.2.4", "", { "dependencies": { "brace-expansion": "^5.0.2" } }, "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg=="],
"minimist": ["minimist@1.2.8", "", {}, "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA=="],
@@ -2541,7 +2541,7 @@
"node-fetch": ["node-fetch@3.3.2", "", { "dependencies": { "data-uri-to-buffer": "^4.0.0", "fetch-blob": "^3.1.4", "formdata-polyfill": "^4.0.10" } }, "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA=="],
- "node-forge": ["node-forge@1.3.3", "", {}, "sha512-rLvcdSyRCyouf6jcOIPe/BgwG/d7hKjzMKOas33/pHEr6gbq18IK9zV7DiPvzsz0oBJPme6qr6H6kGZuI9/DZg=="],
+ "node-forge": ["node-forge@1.4.0", "", {}, "sha512-LarFH0+6VfriEhqMMcLX2F7SwSXeWwnEAJEsYm5QKWchiVYVvJyV9v7UDvUv+w5HO23ZpQTXDv/GxdDdMyOuoQ=="],
"node-int64": ["node-int64@0.4.0", "", {}, "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw=="],
@@ -2819,7 +2819,7 @@
"rimraf": ["rimraf@3.0.2", "", { "dependencies": { "glob": "^7.1.3" }, "bin": { "rimraf": "bin.js" } }, "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA=="],
- "rolldown": ["rolldown@1.0.0-rc.11", "", { "dependencies": { "@oxc-project/types": "=0.122.0", "@rolldown/pluginutils": "1.0.0-rc.11" }, "optionalDependencies": { "@rolldown/binding-android-arm64": "1.0.0-rc.11", "@rolldown/binding-darwin-arm64": "1.0.0-rc.11", "@rolldown/binding-darwin-x64": "1.0.0-rc.11", "@rolldown/binding-freebsd-x64": "1.0.0-rc.11", "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-rc.11", "@rolldown/binding-linux-arm64-gnu": "1.0.0-rc.11", "@rolldown/binding-linux-arm64-musl": "1.0.0-rc.11", "@rolldown/binding-linux-ppc64-gnu": "1.0.0-rc.11", "@rolldown/binding-linux-s390x-gnu": "1.0.0-rc.11", "@rolldown/binding-linux-x64-gnu": "1.0.0-rc.11", "@rolldown/binding-linux-x64-musl": "1.0.0-rc.11", "@rolldown/binding-openharmony-arm64": "1.0.0-rc.11", "@rolldown/binding-wasm32-wasi": "1.0.0-rc.11", "@rolldown/binding-win32-arm64-msvc": "1.0.0-rc.11", "@rolldown/binding-win32-x64-msvc": "1.0.0-rc.11" }, "bin": { "rolldown": "bin/cli.mjs" } }, "sha512-NRjoKMusSjfRbSYiH3VSumlkgFe7kYAa3pzVOsVYVFY3zb5d7nS+a3KGQ7hJKXuYWbzJKPVQ9Wxq2UvyK+ENpw=="],
+ "rolldown": ["rolldown@1.0.0-rc.12", "", { "dependencies": { "@oxc-project/types": "=0.122.0", "@rolldown/pluginutils": "1.0.0-rc.12" }, "optionalDependencies": { "@rolldown/binding-android-arm64": "1.0.0-rc.12", "@rolldown/binding-darwin-arm64": "1.0.0-rc.12", "@rolldown/binding-darwin-x64": "1.0.0-rc.12", "@rolldown/binding-freebsd-x64": "1.0.0-rc.12", "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-rc.12", "@rolldown/binding-linux-arm64-gnu": "1.0.0-rc.12", "@rolldown/binding-linux-arm64-musl": "1.0.0-rc.12", "@rolldown/binding-linux-ppc64-gnu": "1.0.0-rc.12", "@rolldown/binding-linux-s390x-gnu": "1.0.0-rc.12", "@rolldown/binding-linux-x64-gnu": "1.0.0-rc.12", "@rolldown/binding-linux-x64-musl": "1.0.0-rc.12", "@rolldown/binding-openharmony-arm64": "1.0.0-rc.12", "@rolldown/binding-wasm32-wasi": "1.0.0-rc.12", "@rolldown/binding-win32-arm64-msvc": "1.0.0-rc.12", "@rolldown/binding-win32-x64-msvc": "1.0.0-rc.12" }, "bin": { "rolldown": "bin/cli.mjs" } }, "sha512-yP4USLIMYrwpPHEFB5JGH1uxhcslv6/hL0OyvTuY+3qlOSJvZ7ntYnoWpehBxufkgN0cvXxppuTu5hHa/zPh+A=="],
"rou3": ["rou3@0.7.12", "", {}, "sha512-iFE4hLDuloSWcD7mjdCDhx2bKcIsYbtOTpfH5MHHLSKMOUyjqQXTeZVa289uuwEGEKFoE/BAPbhaU4B774nceg=="],
@@ -2903,7 +2903,7 @@
"slugify": ["slugify@1.6.8", "", {}, "sha512-HVk9X1E0gz3mSpoi60h/saazLKXKaZThMLU3u/aNwoYn8/xQyX2MGxL0ui2eaokkD7tF+Zo+cKTHUbe1mmmGzA=="],
- "solid-js": ["solid-js@1.9.11", "", { "dependencies": { "csstype": "^3.1.0", "seroval": "~1.5.0", "seroval-plugins": "~1.5.0" } }, "sha512-WEJtcc5mkh/BnHA6Yrg4whlF8g6QwpmXXRg4P2ztPmcKeHHlH4+djYecBLhSpecZY2RRECXYUwIc/C2r3yzQ4Q=="],
+ "solid-js": ["solid-js@1.9.12", "", { "dependencies": { "csstype": "^3.1.0", "seroval": "~1.5.0", "seroval-plugins": "~1.5.0" } }, "sha512-QzKaSJq2/iDrWR1As6MHZQ8fQkdOBf8GReYb7L5iKwMGceg7HxDcaOHk0at66tNgn9U2U7dXo8ZZpLIAmGMzgw=="],
"sonic-boom": ["sonic-boom@4.2.1", "", { "dependencies": { "atomic-sleep": "^1.0.0" } }, "sha512-w6AxtubXa2wTXAUsZMMWERrsIRAdrK0Sc+FUytWvYAhBJLyuI4llrMIC1DtlNSdI99EI86KZum2MMq3EAZlF9Q=="],
@@ -3079,7 +3079,7 @@
"universalify": ["universalify@2.0.1", "", {}, "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw=="],
- "uniwind": ["uniwind@1.6.0", "", { "dependencies": { "@tailwindcss/node": "4.2.1", "@tailwindcss/oxide": "4.2.1", "culori": "4.0.2", "lightningcss": "1.30.1" }, "peerDependencies": { "react": ">=19.0.0", "react-native": ">=0.81.0", "tailwindcss": ">=4" } }, "sha512-D4jX0594cDnjeFCd0qDXJNQ5xT09W+zWkzq9yjUngNQQ4lCc7sN7Ht5a3+S752iT2D1r/Sul1eYPvjrTwmgSfg=="],
+ "uniwind": ["uniwind@1.6.1", "", { "dependencies": { "@tailwindcss/node": "4.2.1", "@tailwindcss/oxide": "4.2.1", "culori": "4.0.2", "lightningcss": "1.30.1" }, "peerDependencies": { "react": ">=19.0.0", "react-native": ">=0.81.0", "tailwindcss": ">=4" } }, "sha512-aR7vEGccEHDCvjyNM5BE5ZVdbdVTVL6yIffYe65mgjedzOj82TcdWrvae+A3PSHwQTuC1E3/3HbOX/Y5GqMvRQ=="],
"unpipe": ["unpipe@1.0.0", "", {}, "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ=="],
@@ -3113,9 +3113,9 @@
"vaul": ["vaul@1.1.2", "", { "dependencies": { "@radix-ui/react-dialog": "^1.1.1" }, "peerDependencies": { "react": "^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc" } }, "sha512-ZFkClGpWyI2WUQjdLJ/BaGuV6AVQiJ3uELGk3OYtP+B6yCO7Cmn9vPFXVJkRaGkOJu3m8bQMgtyzNHixULceQA=="],
- "vite": ["vite@8.0.2", "", { "dependencies": { "lightningcss": "^1.32.0", "picomatch": "^4.0.3", "postcss": "^8.5.8", "rolldown": "1.0.0-rc.11", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "@vitejs/devtools": "^0.1.0", "esbuild": "^0.27.0", "jiti": ">=1.21.0", "less": "^4.0.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "@vitejs/devtools", "esbuild", "jiti", "less", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-1gFhNi+bHhRE/qKZOJXACm6tX4bA3Isy9KuKF15AgSRuRazNBOJfdDemPBU16/mpMxApDPrWvZ08DcLPEoRnuA=="],
+ "vite": ["vite@8.0.3", "", { "dependencies": { "lightningcss": "^1.32.0", "picomatch": "^4.0.4", "postcss": "^8.5.8", "rolldown": "1.0.0-rc.12", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "@vitejs/devtools": "^0.1.0", "esbuild": "^0.27.0", "jiti": ">=1.21.0", "less": "^4.0.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "@vitejs/devtools", "esbuild", "jiti", "less", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-B9ifbFudT1TFhfltfaIPgjo9Z3mDynBTJSUYxTjOQruf/zHH+ezCQKcoqO+h7a9Pw9Nm/OtlXAiGT1axBgwqrQ=="],
- "vitest": ["vitest@4.1.1", "", { "dependencies": { "@vitest/expect": "4.1.1", "@vitest/mocker": "4.1.1", "@vitest/pretty-format": "4.1.1", "@vitest/runner": "4.1.1", "@vitest/snapshot": "4.1.1", "@vitest/spy": "4.1.1", "@vitest/utils": "4.1.1", "es-module-lexer": "^2.0.0", "expect-type": "^1.3.0", "magic-string": "^0.30.21", "obug": "^2.1.1", "pathe": "^2.0.3", "picomatch": "^4.0.3", "std-env": "^4.0.0-rc.1", "tinybench": "^2.9.0", "tinyexec": "^1.0.2", "tinyglobby": "^0.2.15", "tinyrainbow": "^3.0.3", "vite": "^6.0.0 || ^7.0.0 || ^8.0.0", "why-is-node-running": "^2.3.0" }, "peerDependencies": { "@edge-runtime/vm": "*", "@opentelemetry/api": "^1.9.0", "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", "@vitest/browser-playwright": "4.1.1", "@vitest/browser-preview": "4.1.1", "@vitest/browser-webdriverio": "4.1.1", "@vitest/ui": "4.1.1", "happy-dom": "*", "jsdom": "*" }, "optionalPeers": ["@edge-runtime/vm", "@opentelemetry/api", "@types/node", "@vitest/browser-playwright", "@vitest/browser-preview", "@vitest/browser-webdriverio", "@vitest/ui", "happy-dom", "jsdom"], "bin": { "vitest": "vitest.mjs" } }, "sha512-yF+o4POL41rpAzj5KVILUxm1GCjKnELvaqmU9TLLUbMfDzuN0UpUR9uaDs+mCtjPe+uYPksXDRLQGGPvj1cTmA=="],
+ "vitest": ["vitest@4.1.2", "", { "dependencies": { "@vitest/expect": "4.1.2", "@vitest/mocker": "4.1.2", "@vitest/pretty-format": "4.1.2", "@vitest/runner": "4.1.2", "@vitest/snapshot": "4.1.2", "@vitest/spy": "4.1.2", "@vitest/utils": "4.1.2", "es-module-lexer": "^2.0.0", "expect-type": "^1.3.0", "magic-string": "^0.30.21", "obug": "^2.1.1", "pathe": "^2.0.3", "picomatch": "^4.0.3", "std-env": "^4.0.0-rc.1", "tinybench": "^2.9.0", "tinyexec": "^1.0.2", "tinyglobby": "^0.2.15", "tinyrainbow": "^3.1.0", "vite": "^6.0.0 || ^7.0.0 || ^8.0.0", "why-is-node-running": "^2.3.0" }, "peerDependencies": { "@edge-runtime/vm": "*", "@opentelemetry/api": "^1.9.0", "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", "@vitest/browser-playwright": "4.1.2", "@vitest/browser-preview": "4.1.2", "@vitest/browser-webdriverio": "4.1.2", "@vitest/ui": "4.1.2", "happy-dom": "*", "jsdom": "*" }, "optionalPeers": ["@edge-runtime/vm", "@opentelemetry/api", "@types/node", "@vitest/browser-playwright", "@vitest/browser-preview", "@vitest/browser-webdriverio", "@vitest/ui", "happy-dom", "jsdom"], "bin": { "vitest": "vitest.mjs" } }, "sha512-xjR1dMTVHlFLh98JE3i/f/WePqJsah4A0FK9cc8Ehp9Udk0AZk6ccpIZhh1qJ/yxVWRZ+Q54ocnD8TXmkhspGg=="],
"vlq": ["vlq@1.0.1", "", {}, "sha512-gQpnTgkubC6hQgdIcRdYGDSDc+SaujOdyesZQMv6JlfQee/9Mp0Qhnys6WxDWvQnL5WZdT7o2Ul187aSt0Rq+w=="],
@@ -3203,8 +3203,6 @@
"@babel/plugin-transform-runtime/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="],
- "@better-auth/drizzle-adapter/drizzle-orm": ["drizzle-orm@1.0.0-beta.18-7eb39f0", "", { "peerDependencies": { "@aws-sdk/client-rds-data": ">=3", "@cloudflare/workers-types": ">=4", "@effect/sql": "^0.48.5", "@effect/sql-pg": "^0.49.7", "@electric-sql/pglite": ">=0.2.0", "@libsql/client": ">=0.10.0", "@libsql/client-wasm": ">=0.10.0", "@neondatabase/serverless": ">=0.10.0", "@op-engineering/op-sqlite": ">=2", "@opentelemetry/api": "^1.4.1", "@planetscale/database": ">=1.13", "@sinclair/typebox": ">=0.34.8", "@sqlitecloud/drivers": ">=1.0.653", "@tidbcloud/serverless": "*", "@tursodatabase/database": ">=0.2.1", "@tursodatabase/database-common": ">=0.2.1", "@tursodatabase/database-wasm": ">=0.2.1", "@types/better-sqlite3": "*", "@types/mssql": "^9.1.4", "@types/pg": "*", "@types/sql.js": "*", "@upstash/redis": ">=1.34.7", "@vercel/postgres": ">=0.8.0", "@xata.io/client": "*", "arktype": ">=2.0.0", "better-sqlite3": ">=9.3.0", "bun-types": "*", "expo-sqlite": ">=14.0.0", "gel": ">=2", "mssql": "^11.0.1", "mysql2": ">=2", "pg": ">=8", "postgres": ">=3", "sql.js": ">=1", "sqlite3": ">=5", "typebox": ">=1.0.0", "valibot": ">=1.0.0-beta.7", "zod": "^3.25.0 || ^4.0.0" }, "optionalPeers": ["@aws-sdk/client-rds-data", "@cloudflare/workers-types", "@effect/sql", "@effect/sql-pg", "@electric-sql/pglite", "@libsql/client", "@libsql/client-wasm", "@neondatabase/serverless", "@op-engineering/op-sqlite", "@opentelemetry/api", "@planetscale/database", "@sinclair/typebox", "@sqlitecloud/drivers", "@tidbcloud/serverless", "@tursodatabase/database", "@tursodatabase/database-common", "@tursodatabase/database-wasm", "@types/better-sqlite3", "@types/pg", "@types/sql.js", "@upstash/redis", "@vercel/postgres", "@xata.io/client", "arktype", "better-sqlite3", "bun-types", "expo-sqlite", "gel", "mysql2", "pg", "postgres", "sql.js", "sqlite3", "typebox", "valibot", "zod"] }, "sha512-piQ+esfm3krAiwXw3d8U559VdBjzA178jqazAkb0tSo7lJPZUL4SbB5uqg77ZUuCvY37hJuQ/o/gQoMX4m0E6Q=="],
-
"@dotenvx/dotenvx/commander": ["commander@11.1.0", "", {}, "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ=="],
"@dotenvx/dotenvx/execa": ["execa@5.1.1", "", { "dependencies": { "cross-spawn": "^7.0.3", "get-stream": "^6.0.0", "human-signals": "^2.1.0", "is-stream": "^2.0.0", "merge-stream": "^2.0.0", "npm-run-path": "^4.0.1", "onetime": "^5.1.2", "signal-exit": "^3.0.3", "strip-final-newline": "^2.0.0" } }, "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg=="],
@@ -3215,6 +3213,10 @@
"@eslint-community/eslint-utils/eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="],
+ "@eslint/config-array/minimatch": ["minimatch@3.1.5", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w=="],
+
+ "@eslint/eslintrc/minimatch": ["minimatch@3.1.5", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w=="],
+
"@eslint/eslintrc/strip-json-comments": ["strip-json-comments@3.1.1", "", {}, "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig=="],
"@expo/cli/arg": ["arg@5.0.2", "", {}, "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg=="],
@@ -3235,8 +3237,6 @@
"@expo/fingerprint/glob": ["glob@13.0.6", "", { "dependencies": { "minimatch": "^10.2.2", "minipass": "^7.1.3", "path-scurry": "^2.0.2" } }, "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw=="],
- "@expo/fingerprint/minimatch": ["minimatch@10.2.4", "", { "dependencies": { "brace-expansion": "^5.0.2" } }, "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg=="],
-
"@expo/metro/metro-runtime": ["metro-runtime@0.83.3", "", { "dependencies": { "@babel/runtime": "^7.25.0", "flow-enums-runtime": "^0.0.6" } }, "sha512-JHCJb9ebr9rfJ+LcssFYA2x1qPYuSD/bbePupIGhpMrsla7RCwC/VL3yJ9cSU+nUhU4c9Ixxy8tBta+JbDeZWw=="],
"@expo/metro/metro-source-map": ["metro-source-map@0.83.3", "", { "dependencies": { "@babel/traverse": "^7.25.3", "@babel/traverse--for-generate-function-map": "npm:@babel/traverse@^7.25.3", "@babel/types": "^7.25.2", "flow-enums-runtime": "^0.0.6", "invariant": "^2.2.4", "metro-symbolicate": "0.83.3", "nullthrows": "^1.1.1", "ob1": "0.83.3", "source-map": "^0.5.6", "vlq": "^1.0.0" } }, "sha512-xkC3qwUBh2psVZgVavo8+r2C9Igkk3DibiOXSAht1aYRRcztEZNFtAMtfSB7sdO2iFMx2Mlyu++cBxz/fhdzQg=="],
@@ -3301,15 +3301,21 @@
"@react-native/codegen/hermes-parser": ["hermes-parser@0.32.0", "", { "dependencies": { "hermes-estree": "0.32.0" } }, "sha512-g4nBOWFpuiTqjR3LZdRxKUkij9iyveWeuks7INEsMX741f3r9xxrOe8TeQfUxtda0eXmiIFiMQzoeSQEno33Hw=="],
+ "@react-native/community-cli-plugin/metro": ["metro@0.83.5", "", { "dependencies": { "@babel/code-frame": "^7.29.0", "@babel/core": "^7.25.2", "@babel/generator": "^7.29.1", "@babel/parser": "^7.29.0", "@babel/template": "^7.28.6", "@babel/traverse": "^7.29.0", "@babel/types": "^7.29.0", "accepts": "^2.0.0", "chalk": "^4.0.0", "ci-info": "^2.0.0", "connect": "^3.6.5", "debug": "^4.4.0", "error-stack-parser": "^2.0.6", "flow-enums-runtime": "^0.0.6", "graceful-fs": "^4.2.4", "hermes-parser": "0.33.3", "image-size": "^1.0.2", "invariant": "^2.2.4", "jest-worker": "^29.7.0", "jsc-safe-url": "^0.2.2", "lodash.throttle": "^4.1.1", "metro-babel-transformer": "0.83.5", "metro-cache": "0.83.5", "metro-cache-key": "0.83.5", "metro-config": "0.83.5", "metro-core": "0.83.5", "metro-file-map": "0.83.5", "metro-resolver": "0.83.5", "metro-runtime": "0.83.5", "metro-source-map": "0.83.5", "metro-symbolicate": "0.83.5", "metro-transform-plugins": "0.83.5", "metro-transform-worker": "0.83.5", "mime-types": "^3.0.1", "nullthrows": "^1.1.1", "serialize-error": "^2.1.0", "source-map": "^0.5.6", "throat": "^5.0.0", "ws": "^7.5.10", "yargs": "^17.6.2" }, "bin": { "metro": "src/cli.js" } }, "sha512-BgsXevY1MBac/3ZYv/RfNFf/4iuW9X7f4H8ZNkiH+r667HD9sVujxcmu4jvEzGCAm4/WyKdZCuyhAcyhTHOucQ=="],
+
+ "@react-native/community-cli-plugin/metro-config": ["metro-config@0.83.5", "", { "dependencies": { "connect": "^3.6.5", "flow-enums-runtime": "^0.0.6", "jest-validate": "^29.7.0", "metro": "0.83.5", "metro-cache": "0.83.5", "metro-core": "0.83.5", "metro-runtime": "0.83.5", "yaml": "^2.6.1" } }, "sha512-JQ/PAASXH7yczgV6OCUSRhZYME+NU8NYjI2RcaG5ga4QfQ3T/XdiLzpSb3awWZYlDCcQb36l4Vl7i0Zw7/Tf9w=="],
+
+ "@react-native/community-cli-plugin/metro-core": ["metro-core@0.83.5", "", { "dependencies": { "flow-enums-runtime": "^0.0.6", "lodash.throttle": "^4.1.1", "metro-resolver": "0.83.5" } }, "sha512-YcVcLCrf0ed4mdLa82Qob0VxYqfhmlRxUS8+TO4gosZo/gLwSvtdeOjc/Vt0pe/lvMNrBap9LlmvZM8FIsMgJQ=="],
+
"@react-native/dev-middleware/open": ["open@7.4.2", "", { "dependencies": { "is-docker": "^2.0.0", "is-wsl": "^2.1.1" } }, "sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q=="],
"@react-native/dev-middleware/ws": ["ws@7.5.10", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": "^5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ=="],
- "@react-navigation/bottom-tabs/@react-navigation/elements": ["@react-navigation/elements@2.9.12", "", { "dependencies": { "color": "^4.2.3", "use-latest-callback": "^0.2.4", "use-sync-external-store": "^1.5.0" }, "peerDependencies": { "@react-native-masked-view/masked-view": ">= 0.2.0", "@react-navigation/native": "^7.2.0", "react": ">= 18.2.0", "react-native": "*", "react-native-safe-area-context": ">= 4.0.0" }, "optionalPeers": ["@react-native-masked-view/masked-view"] }, "sha512-LSaQUj5SV9OXVRcxT8mqETDoM7BOKCveCvuLjdAr9NZnPDM5HW8uDnvW/sCa8oEFy+22+ojoXtHFKsfnesgBbw=="],
+ "@react-navigation/bottom-tabs/@react-navigation/elements": ["@react-navigation/elements@2.9.13", "", { "dependencies": { "color": "^4.2.3", "use-latest-callback": "^0.2.4", "use-sync-external-store": "^1.5.0" }, "peerDependencies": { "@react-native-masked-view/masked-view": ">= 0.2.0", "@react-navigation/native": "^7.2.1", "react": ">= 18.2.0", "react-native": "*", "react-native-safe-area-context": ">= 4.0.0" }, "optionalPeers": ["@react-native-masked-view/masked-view"] }, "sha512-ZD8fPwhujgs3SwgaPRse+shLCFkeLhlfk9BHtQ604Qa7/p0/sRQV9HkTfREP8gtbt6nwk6WE+0vWfX2iqxOCKA=="],
"@react-navigation/core/react-is": ["react-is@19.2.4", "", {}, "sha512-W+EWGn2v0ApPKgKKCy/7s7WHXkboGcsrXE+2joLyVxkbyVQfO3MUEaUQDHoSmb8TFFrSKYa9mw64WZHNHSDzYA=="],
- "@react-navigation/native-stack/@react-navigation/elements": ["@react-navigation/elements@2.9.12", "", { "dependencies": { "color": "^4.2.3", "use-latest-callback": "^0.2.4", "use-sync-external-store": "^1.5.0" }, "peerDependencies": { "@react-native-masked-view/masked-view": ">= 0.2.0", "@react-navigation/native": "^7.2.0", "react": ">= 18.2.0", "react-native": "*", "react-native-safe-area-context": ">= 4.0.0" }, "optionalPeers": ["@react-native-masked-view/masked-view"] }, "sha512-LSaQUj5SV9OXVRcxT8mqETDoM7BOKCveCvuLjdAr9NZnPDM5HW8uDnvW/sCa8oEFy+22+ojoXtHFKsfnesgBbw=="],
+ "@react-navigation/native-stack/@react-navigation/elements": ["@react-navigation/elements@2.9.13", "", { "dependencies": { "color": "^4.2.3", "use-latest-callback": "^0.2.4", "use-sync-external-store": "^1.5.0" }, "peerDependencies": { "@react-native-masked-view/masked-view": ">= 0.2.0", "@react-navigation/native": "^7.2.1", "react": ">= 18.2.0", "react-native": "*", "react-native-safe-area-context": ">= 4.0.0" }, "optionalPeers": ["@react-native-masked-view/masked-view"] }, "sha512-ZD8fPwhujgs3SwgaPRse+shLCFkeLhlfk9BHtQ604Qa7/p0/sRQV9HkTfREP8gtbt6nwk6WE+0vWfX2iqxOCKA=="],
"@tailwindcss/node/lightningcss": ["lightningcss@1.31.1", "", { "dependencies": { "detect-libc": "^2.0.3" }, "optionalDependencies": { "lightningcss-android-arm64": "1.31.1", "lightningcss-darwin-arm64": "1.31.1", "lightningcss-darwin-x64": "1.31.1", "lightningcss-freebsd-x64": "1.31.1", "lightningcss-linux-arm-gnueabihf": "1.31.1", "lightningcss-linux-arm64-gnu": "1.31.1", "lightningcss-linux-arm64-musl": "1.31.1", "lightningcss-linux-x64-gnu": "1.31.1", "lightningcss-linux-x64-musl": "1.31.1", "lightningcss-win32-arm64-msvc": "1.31.1", "lightningcss-win32-x64-msvc": "1.31.1" } }, "sha512-l51N2r93WmGUye3WuFoN5k10zyvrVs0qfKBhyC5ogUQ6Ew6JUSswh78mbSO+IU3nTWsyOArqPCcShdQSadghBQ=="],
@@ -3341,10 +3347,6 @@
"@tanstack/zod-adapter/zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="],
- "@ts-morph/common/minimatch": ["minimatch@10.2.4", "", { "dependencies": { "brace-expansion": "^5.0.2" } }, "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg=="],
-
- "@typescript-eslint/typescript-estree/minimatch": ["minimatch@10.2.4", "", { "dependencies": { "brace-expansion": "^5.0.2" } }, "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg=="],
-
"@typescript-eslint/visitor-keys/eslint-visitor-keys": ["eslint-visitor-keys@5.0.1", "", {}, "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA=="],
"ajv-formats/ajv": ["ajv@8.18.0", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A=="],
@@ -3357,10 +3359,6 @@
"babel-plugin-syntax-hermes-parser/hermes-parser": ["hermes-parser@0.32.0", "", { "dependencies": { "hermes-estree": "0.32.0" } }, "sha512-g4nBOWFpuiTqjR3LZdRxKUkij9iyveWeuks7INEsMX741f3r9xxrOe8TeQfUxtda0eXmiIFiMQzoeSQEno33Hw=="],
- "better-auth/drizzle-kit": ["drizzle-kit@1.0.0-beta.18-7eb39f0", "", { "dependencies": { "@drizzle-team/brocli": "^0.11.0", "@js-temporal/polyfill": "^0.5.1", "esbuild": "^0.25.10", "get-tsconfig": "^4.13.6", "jiti": "^2.6.1" }, "bin": { "drizzle-kit": "bin.cjs" } }, "sha512-F3ozFBjlUr/VWHR0NIz06jM9cKmfFafoXnCDhKvwjhH91hRK/l6sxIjiDOwMxjGuoJH+/p9W8DE6juoGvn7kXA=="],
-
- "better-auth/drizzle-orm": ["drizzle-orm@1.0.0-beta.18-7eb39f0", "", { "peerDependencies": { "@aws-sdk/client-rds-data": ">=3", "@cloudflare/workers-types": ">=4", "@effect/sql": "^0.48.5", "@effect/sql-pg": "^0.49.7", "@electric-sql/pglite": ">=0.2.0", "@libsql/client": ">=0.10.0", "@libsql/client-wasm": ">=0.10.0", "@neondatabase/serverless": ">=0.10.0", "@op-engineering/op-sqlite": ">=2", "@opentelemetry/api": "^1.4.1", "@planetscale/database": ">=1.13", "@sinclair/typebox": ">=0.34.8", "@sqlitecloud/drivers": ">=1.0.653", "@tidbcloud/serverless": "*", "@tursodatabase/database": ">=0.2.1", "@tursodatabase/database-common": ">=0.2.1", "@tursodatabase/database-wasm": ">=0.2.1", "@types/better-sqlite3": "*", "@types/mssql": "^9.1.4", "@types/pg": "*", "@types/sql.js": "*", "@upstash/redis": ">=1.34.7", "@vercel/postgres": ">=0.8.0", "@xata.io/client": "*", "arktype": ">=2.0.0", "better-sqlite3": ">=9.3.0", "bun-types": "*", "expo-sqlite": ">=14.0.0", "gel": ">=2", "mssql": "^11.0.1", "mysql2": ">=2", "pg": ">=8", "postgres": ">=3", "sql.js": ">=1", "sqlite3": ">=5", "typebox": ">=1.0.0", "valibot": ">=1.0.0-beta.7", "zod": "^3.25.0 || ^4.0.0" }, "optionalPeers": ["@aws-sdk/client-rds-data", "@cloudflare/workers-types", "@effect/sql", "@effect/sql-pg", "@electric-sql/pglite", "@libsql/client", "@libsql/client-wasm", "@neondatabase/serverless", "@op-engineering/op-sqlite", "@opentelemetry/api", "@planetscale/database", "@sinclair/typebox", "@sqlitecloud/drivers", "@tidbcloud/serverless", "@tursodatabase/database", "@tursodatabase/database-common", "@tursodatabase/database-wasm", "@types/better-sqlite3", "@types/pg", "@types/sql.js", "@upstash/redis", "@vercel/postgres", "@xata.io/client", "arktype", "better-sqlite3", "bun-types", "expo-sqlite", "gel", "mysql2", "pg", "postgres", "sql.js", "sqlite3", "typebox", "valibot", "zod"] }, "sha512-piQ+esfm3krAiwXw3d8U559VdBjzA178jqazAkb0tSo7lJPZUL4SbB5uqg77ZUuCvY37hJuQ/o/gQoMX4m0E6Q=="],
-
"better-opn/open": ["open@8.4.2", "", { "dependencies": { "define-lazy-prop": "^2.0.0", "is-docker": "^2.1.1", "is-wsl": "^2.2.0" } }, "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ=="],
"body-parser/iconv-lite": ["iconv-lite@0.7.2", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw=="],
@@ -3371,12 +3369,12 @@
"chokidar/fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="],
- "chokidar/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="],
-
"chrome-launcher/is-wsl": ["is-wsl@2.2.0", "", { "dependencies": { "is-docker": "^2.0.0" } }, "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww=="],
"chromium-edge-launcher/is-wsl": ["is-wsl@2.2.0", "", { "dependencies": { "is-docker": "^2.0.0" } }, "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww=="],
+ "compressible/mime-db": ["mime-db@1.54.0", "", {}, "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ=="],
+
"compression/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="],
"compression/negotiator": ["negotiator@0.6.4", "", {}, "sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w=="],
@@ -3389,6 +3387,10 @@
"eciesjs/@noble/hashes": ["@noble/hashes@1.8.0", "", {}, "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A=="],
+ "eslint/glob-parent": ["glob-parent@6.0.2", "", { "dependencies": { "is-glob": "^4.0.3" } }, "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A=="],
+
+ "eslint/minimatch": ["minimatch@3.1.5", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w=="],
+
"expo-modules-autolinking/commander": ["commander@7.2.0", "", {}, "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw=="],
"expo-router/semver": ["semver@7.6.3", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A=="],
@@ -3409,8 +3411,6 @@
"express/serve-static": ["serve-static@2.2.1", "", { "dependencies": { "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "parseurl": "^1.3.3", "send": "^1.2.0" } }, "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw=="],
- "fast-glob/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="],
-
"figures/is-unicode-supported": ["is-unicode-supported@2.1.0", "", {}, "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ=="],
"finalhandler/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="],
@@ -3421,8 +3421,6 @@
"finalhandler/statuses": ["statuses@1.5.0", "", {}, "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA=="],
- "glob/minimatch": ["minimatch@10.2.4", "", { "dependencies": { "brace-expansion": "^5.0.2" } }, "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg=="],
-
"hoist-non-react-statics/react-is": ["react-is@16.13.1", "", {}, "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="],
"hosted-git-info/lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="],
@@ -3519,9 +3517,9 @@
"rimraf/glob": ["glob@7.2.3", "", { "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } }, "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q=="],
- "rolldown/@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-rc.11", "", {}, "sha512-xQO9vbwBecJRv9EUcQ/y0dzSTJgA7Q6UVN7xp6B81+tBGSLVAK03yJ9NkJaUA7JFD91kbjxRSC/mDnmvXzbHoQ=="],
+ "rolldown/@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-rc.12", "", {}, "sha512-HHMwmarRKvoFsJorqYlFeFRzXZqCt2ETQlEDOb9aqssrnVBB1/+xgTGtuTrIk5vzLNX1MjMtTf7W9z3tsSbrxw=="],
- "router/path-to-regexp": ["path-to-regexp@8.3.0", "", {}, "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA=="],
+ "router/path-to-regexp": ["path-to-regexp@8.4.0", "", {}, "sha512-PuseHIvAnz3bjrM2rGJtSgo1zjgxapTLZ7x2pjhzWwlp4SJQgK3f3iZIQwkpEnBaKz6seKBADpM4B4ySkuYypg=="],
"send/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="],
@@ -3549,6 +3547,8 @@
"test-exclude/glob": ["glob@7.2.3", "", { "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } }, "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q=="],
+ "test-exclude/minimatch": ["minimatch@3.1.5", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w=="],
+
"tsx/esbuild": ["esbuild@0.27.4", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.27.4", "@esbuild/android-arm": "0.27.4", "@esbuild/android-arm64": "0.27.4", "@esbuild/android-x64": "0.27.4", "@esbuild/darwin-arm64": "0.27.4", "@esbuild/darwin-x64": "0.27.4", "@esbuild/freebsd-arm64": "0.27.4", "@esbuild/freebsd-x64": "0.27.4", "@esbuild/linux-arm": "0.27.4", "@esbuild/linux-arm64": "0.27.4", "@esbuild/linux-ia32": "0.27.4", "@esbuild/linux-loong64": "0.27.4", "@esbuild/linux-mips64el": "0.27.4", "@esbuild/linux-ppc64": "0.27.4", "@esbuild/linux-riscv64": "0.27.4", "@esbuild/linux-s390x": "0.27.4", "@esbuild/linux-x64": "0.27.4", "@esbuild/netbsd-arm64": "0.27.4", "@esbuild/netbsd-x64": "0.27.4", "@esbuild/openbsd-arm64": "0.27.4", "@esbuild/openbsd-x64": "0.27.4", "@esbuild/openharmony-arm64": "0.27.4", "@esbuild/sunos-x64": "0.27.4", "@esbuild/win32-arm64": "0.27.4", "@esbuild/win32-ia32": "0.27.4", "@esbuild/win32-x64": "0.27.4" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-Rq4vbHnYkK5fws5NF7MYTU68FPRE1ajX7heQ/8QXXWqNgqqJ/GkmmyxIzUnf2Sr/bakf8l54716CcMGHYhMrrQ=="],
"tsx/fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="],
@@ -3581,7 +3581,9 @@
"@dotenvx/dotenvx/which/isexe": ["isexe@3.1.5", "", {}, "sha512-6B3tLtFqtQS4ekarvLVMZ+X+VlvQekbe4taUkf/rhVO3d/h0M2rfARm/pXLcPEsjjMsFgrFgSrhQIxcSVrBz8w=="],
- "@expo/cli/glob/minimatch": ["minimatch@10.2.4", "", { "dependencies": { "brace-expansion": "^5.0.2" } }, "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg=="],
+ "@eslint/config-array/minimatch/brace-expansion": ["brace-expansion@1.1.12", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="],
+
+ "@eslint/eslintrc/minimatch/brace-expansion": ["brace-expansion@1.1.12", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="],
"@expo/cli/ora/chalk": ["chalk@2.4.2", "", { "dependencies": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", "supports-color": "^5.3.0" } }, "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ=="],
@@ -3591,14 +3593,6 @@
"@expo/cli/ora/strip-ansi": ["strip-ansi@5.2.0", "", { "dependencies": { "ansi-regex": "^4.1.0" } }, "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA=="],
- "@expo/config-plugins/glob/minimatch": ["minimatch@10.2.4", "", { "dependencies": { "brace-expansion": "^5.0.2" } }, "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg=="],
-
- "@expo/config/glob/minimatch": ["minimatch@10.2.4", "", { "dependencies": { "brace-expansion": "^5.0.2" } }, "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg=="],
-
- "@expo/fingerprint/minimatch/brace-expansion": ["brace-expansion@5.0.4", "", { "dependencies": { "balanced-match": "^4.0.2" } }, "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg=="],
-
- "@expo/metro-config/glob/minimatch": ["minimatch@10.2.4", "", { "dependencies": { "brace-expansion": "^5.0.2" } }, "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg=="],
-
"@expo/metro-config/hermes-parser/hermes-estree": ["hermes-estree@0.32.1", "", {}, "sha512-ne5hkuDxheNBAikDjqvCZCwihnz0vVu9YsBzAEO1puiyFR4F1+PAz/SiPHSsNTuOveCYGRMX8Xbx4LOubeC0Qg=="],
"@expo/metro-config/lightningcss/lightningcss-darwin-arm64": ["lightningcss-darwin-arm64@1.32.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ=="],
@@ -3659,8 +3653,42 @@
"@radix-ui/react-tabs/@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="],
+ "@react-native/codegen/glob/minimatch": ["minimatch@3.1.5", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w=="],
+
"@react-native/codegen/hermes-parser/hermes-estree": ["hermes-estree@0.32.0", "", {}, "sha512-KWn3BqnlDOl97Xe1Yviur6NbgIZ+IP+UVSpshlZWkq+EtoHg6/cwiDj/osP9PCEgFE15KBm1O55JRwbMEm5ejQ=="],
+ "@react-native/community-cli-plugin/metro/accepts": ["accepts@2.0.0", "", { "dependencies": { "mime-types": "^3.0.0", "negotiator": "^1.0.0" } }, "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng=="],
+
+ "@react-native/community-cli-plugin/metro/ci-info": ["ci-info@2.0.0", "", {}, "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ=="],
+
+ "@react-native/community-cli-plugin/metro/hermes-parser": ["hermes-parser@0.33.3", "", { "dependencies": { "hermes-estree": "0.33.3" } }, "sha512-Yg3HgaG4CqgyowtYjX/FsnPAuZdHOqSMtnbpylbptsQ9nwwSKsy6uRWcGO5RK0EqiX12q8HvDWKgeAVajRO5DA=="],
+
+ "@react-native/community-cli-plugin/metro/metro-babel-transformer": ["metro-babel-transformer@0.83.5", "", { "dependencies": { "@babel/core": "^7.25.2", "flow-enums-runtime": "^0.0.6", "hermes-parser": "0.33.3", "nullthrows": "^1.1.1" } }, "sha512-d9FfmgUEVejTiSb7bkQeLRGl6aeno2UpuPm3bo3rCYwxewj03ymvOn8s8vnS4fBqAPQ+cE9iQM40wh7nGXR+eA=="],
+
+ "@react-native/community-cli-plugin/metro/metro-cache": ["metro-cache@0.83.5", "", { "dependencies": { "exponential-backoff": "^3.1.1", "flow-enums-runtime": "^0.0.6", "https-proxy-agent": "^7.0.5", "metro-core": "0.83.5" } }, "sha512-oH+s4U+IfZyg8J42bne2Skc90rcuESIYf86dYittcdWQtPfcaFXWpByPyTuWk3rR1Zz3Eh5HOrcVImfEhhJLng=="],
+
+ "@react-native/community-cli-plugin/metro/metro-cache-key": ["metro-cache-key@0.83.5", "", { "dependencies": { "flow-enums-runtime": "^0.0.6" } }, "sha512-Ycl8PBajB7bhbAI7Rt0xEyiF8oJ0RWX8EKkolV1KfCUlC++V/GStMSGpPLwnnBZXZWkCC5edBPzv1Hz1Yi0Euw=="],
+
+ "@react-native/community-cli-plugin/metro/metro-file-map": ["metro-file-map@0.83.5", "", { "dependencies": { "debug": "^4.4.0", "fb-watchman": "^2.0.0", "flow-enums-runtime": "^0.0.6", "graceful-fs": "^4.2.4", "invariant": "^2.2.4", "jest-worker": "^29.7.0", "micromatch": "^4.0.4", "nullthrows": "^1.1.1", "walker": "^1.0.7" } }, "sha512-ZEt8s3a1cnYbn40nyCD+CsZdYSlwtFh2kFym4lo+uvfM+UMMH+r/BsrC6rbNClSrt+B7rU9T+Te/sh/NL8ZZKQ=="],
+
+ "@react-native/community-cli-plugin/metro/metro-resolver": ["metro-resolver@0.83.5", "", { "dependencies": { "flow-enums-runtime": "^0.0.6" } }, "sha512-7p3GtzVUpbAweJeCcUJihJeOQl1bDuimO5ueo1K0BUpUtR41q5EilbQ3klt16UTPPMpA+tISWBtsrqU556mY1A=="],
+
+ "@react-native/community-cli-plugin/metro/metro-symbolicate": ["metro-symbolicate@0.83.5", "", { "dependencies": { "flow-enums-runtime": "^0.0.6", "invariant": "^2.2.4", "metro-source-map": "0.83.5", "nullthrows": "^1.1.1", "source-map": "^0.5.6", "vlq": "^1.0.0" }, "bin": { "metro-symbolicate": "src/index.js" } }, "sha512-EMIkrjNRz/hF+p0RDdxoE60+dkaTLPN3vaaGkFmX5lvFdO6HPfHA/Ywznzkev+za0VhPQ5KSdz49/MALBRteHA=="],
+
+ "@react-native/community-cli-plugin/metro/metro-transform-plugins": ["metro-transform-plugins@0.83.5", "", { "dependencies": { "@babel/core": "^7.25.2", "@babel/generator": "^7.29.1", "@babel/template": "^7.28.6", "@babel/traverse": "^7.29.0", "flow-enums-runtime": "^0.0.6", "nullthrows": "^1.1.1" } }, "sha512-KxYKzZL+lt3Os5H2nx7YkbkWVduLZL5kPrE/Yq+Prm/DE1VLhpfnO6HtPs8vimYFKOa58ncl60GpoX0h7Wm0Vw=="],
+
+ "@react-native/community-cli-plugin/metro/metro-transform-worker": ["metro-transform-worker@0.83.5", "", { "dependencies": { "@babel/core": "^7.25.2", "@babel/generator": "^7.29.1", "@babel/parser": "^7.29.0", "@babel/types": "^7.29.0", "flow-enums-runtime": "^0.0.6", "metro": "0.83.5", "metro-babel-transformer": "0.83.5", "metro-cache": "0.83.5", "metro-cache-key": "0.83.5", "metro-minify-terser": "0.83.5", "metro-source-map": "0.83.5", "metro-transform-plugins": "0.83.5", "nullthrows": "^1.1.1" } }, "sha512-8N4pjkNXc6ytlP9oAM6MwqkvUepNSW39LKYl9NjUMpRDazBQ7oBpQDc8Sz4aI8jnH6AGhF7s1m/ayxkN1t04yA=="],
+
+ "@react-native/community-cli-plugin/metro/mime-types": ["mime-types@3.0.2", "", { "dependencies": { "mime-db": "^1.54.0" } }, "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A=="],
+
+ "@react-native/community-cli-plugin/metro/source-map": ["source-map@0.5.7", "", {}, "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ=="],
+
+ "@react-native/community-cli-plugin/metro/ws": ["ws@7.5.10", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": "^5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ=="],
+
+ "@react-native/community-cli-plugin/metro-config/metro-cache": ["metro-cache@0.83.5", "", { "dependencies": { "exponential-backoff": "^3.1.1", "flow-enums-runtime": "^0.0.6", "https-proxy-agent": "^7.0.5", "metro-core": "0.83.5" } }, "sha512-oH+s4U+IfZyg8J42bne2Skc90rcuESIYf86dYittcdWQtPfcaFXWpByPyTuWk3rR1Zz3Eh5HOrcVImfEhhJLng=="],
+
+ "@react-native/community-cli-plugin/metro-core/metro-resolver": ["metro-resolver@0.83.5", "", { "dependencies": { "flow-enums-runtime": "^0.0.6" } }, "sha512-7p3GtzVUpbAweJeCcUJihJeOQl1bDuimO5ueo1K0BUpUtR41q5EilbQ3klt16UTPPMpA+tISWBtsrqU556mY1A=="],
+
"@react-native/dev-middleware/open/is-docker": ["is-docker@2.2.1", "", { "bin": { "is-docker": "cli.js" } }, "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ=="],
"@react-native/dev-middleware/open/is-wsl": ["is-wsl@2.2.0", "", { "dependencies": { "is-docker": "^2.0.0" } }, "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww=="],
@@ -3715,14 +3743,8 @@
"@tanstack/router-plugin/chokidar/fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="],
- "@tanstack/router-plugin/chokidar/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="],
-
"@tanstack/router-plugin/chokidar/readdirp": ["readdirp@3.6.0", "", { "dependencies": { "picomatch": "^2.2.1" } }, "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA=="],
- "@ts-morph/common/minimatch/brace-expansion": ["brace-expansion@5.0.4", "", { "dependencies": { "balanced-match": "^4.0.2" } }, "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg=="],
-
- "@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@5.0.4", "", { "dependencies": { "balanced-match": "^4.0.2" } }, "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg=="],
-
"ajv-formats/ajv/json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="],
"babel-plugin-syntax-hermes-parser/hermes-parser/hermes-estree": ["hermes-estree@0.32.0", "", {}, "sha512-KWn3BqnlDOl97Xe1Yviur6NbgIZ+IP+UVSpshlZWkq+EtoHg6/cwiDj/osP9PCEgFE15KBm1O55JRwbMEm5ejQ=="],
@@ -3741,7 +3763,7 @@
"connect/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="],
- "expo-updates/glob/minimatch": ["minimatch@10.2.4", "", { "dependencies": { "brace-expansion": "^5.0.2" } }, "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg=="],
+ "eslint/minimatch/brace-expansion": ["brace-expansion@1.1.12", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="],
"express/accepts/negotiator": ["negotiator@1.0.0", "", {}, "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg=="],
@@ -3749,8 +3771,6 @@
"finalhandler/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="],
- "glob/minimatch/brace-expansion": ["brace-expansion@5.0.4", "", { "dependencies": { "balanced-match": "^4.0.2" } }, "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg=="],
-
"lighthouse-logger/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="],
"metro-babel-transformer/hermes-parser/hermes-estree": ["hermes-estree@0.32.0", "", {}, "sha512-KWn3BqnlDOl97Xe1Yviur6NbgIZ+IP+UVSpshlZWkq+EtoHg6/cwiDj/osP9PCEgFE15KBm1O55JRwbMEm5ejQ=="],
@@ -3767,8 +3787,12 @@
"node-vibrant/@types/node/undici-types": ["undici-types@5.26.5", "", {}, "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="],
+ "react-native/glob/minimatch": ["minimatch@3.1.5", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w=="],
+
"readable-web-to-node-stream/readable-stream/buffer": ["buffer@6.0.3", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.2.1" } }, "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA=="],
+ "rimraf/glob/minimatch": ["minimatch@3.1.5", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w=="],
+
"send/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="],
"shadcn/ora/chalk": ["chalk@5.6.2", "", {}, "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA=="],
@@ -3789,6 +3813,8 @@
"tedious/bl/readable-stream": ["readable-stream@4.7.0", "", { "dependencies": { "abort-controller": "^3.0.0", "buffer": "^6.0.3", "events": "^3.3.0", "process": "^0.11.10", "string_decoder": "^1.3.0" } }, "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg=="],
+ "test-exclude/minimatch/brace-expansion": ["brace-expansion@1.1.12", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="],
+
"tsx/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.27.4", "", { "os": "aix", "cpu": "ppc64" }, "sha512-cQPwL2mp2nSmHHJlCyoXgHGhbEPMrEEU5xhkcy3Hs/O7nGZqEpZ2sUtLaL9MORLtDfRvVl2/3PAuEkYZH0Ty8Q=="],
"tsx/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.27.4", "", { "os": "android", "cpu": "arm" }, "sha512-X9bUgvxiC8CHAGKYufLIHGXPJWnr0OCdR0anD2e21vdvgCI8lIfqFbnoeOz7lBjdrAGUhqLZLcQo6MLhTO2DKQ=="],
@@ -3863,7 +3889,9 @@
"vite/lightningcss/lightningcss-win32-x64-msvc": ["lightningcss-win32-x64-msvc@1.32.0", "", { "os": "win32", "cpu": "x64" }, "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q=="],
- "@expo/cli/glob/minimatch/brace-expansion": ["brace-expansion@5.0.4", "", { "dependencies": { "balanced-match": "^4.0.2" } }, "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg=="],
+ "@eslint/config-array/minimatch/brace-expansion/balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="],
+
+ "@eslint/eslintrc/minimatch/brace-expansion/balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="],
"@expo/cli/ora/chalk/ansi-styles": ["ansi-styles@3.2.1", "", { "dependencies": { "color-convert": "^1.9.0" } }, "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA=="],
@@ -3875,14 +3903,6 @@
"@expo/cli/ora/strip-ansi/ansi-regex": ["ansi-regex@4.1.1", "", {}, "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g=="],
- "@expo/config-plugins/glob/minimatch/brace-expansion": ["brace-expansion@5.0.4", "", { "dependencies": { "balanced-match": "^4.0.2" } }, "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg=="],
-
- "@expo/config/glob/minimatch/brace-expansion": ["brace-expansion@5.0.4", "", { "dependencies": { "balanced-match": "^4.0.2" } }, "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg=="],
-
- "@expo/fingerprint/minimatch/brace-expansion/balanced-match": ["balanced-match@4.0.4", "", {}, "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA=="],
-
- "@expo/metro-config/glob/minimatch/brace-expansion": ["brace-expansion@5.0.4", "", { "dependencies": { "balanced-match": "^4.0.2" } }, "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg=="],
-
"@expo/package-manager/ora/chalk/ansi-styles": ["ansi-styles@3.2.1", "", { "dependencies": { "color-convert": "^1.9.0" } }, "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA=="],
"@expo/package-manager/ora/chalk/escape-string-regexp": ["escape-string-regexp@1.0.5", "", {}, "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg=="],
@@ -3897,6 +3917,16 @@
"@istanbuljs/load-nyc-config/js-yaml/argparse/sprintf-js": ["sprintf-js@1.0.3", "", {}, "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g=="],
+ "@react-native/codegen/glob/minimatch/brace-expansion": ["brace-expansion@1.1.12", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="],
+
+ "@react-native/community-cli-plugin/metro/accepts/negotiator": ["negotiator@1.0.0", "", {}, "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg=="],
+
+ "@react-native/community-cli-plugin/metro/hermes-parser/hermes-estree": ["hermes-estree@0.33.3", "", {}, "sha512-6kzYZHCk8Fy1Uc+t3HGYyJn3OL4aeqKLTyina4UFtWl8I0kSL7OmKThaiX+Uh2f8nGw3mo4Ifxg0M5Zk3/Oeqg=="],
+
+ "@react-native/community-cli-plugin/metro/metro-transform-worker/metro-minify-terser": ["metro-minify-terser@0.83.5", "", { "dependencies": { "flow-enums-runtime": "^0.0.6", "terser": "^5.15.0" } }, "sha512-Toe4Md1wS1PBqbvB0cFxBzKEVyyuYTUb0sgifAZh/mSvLH84qA1NAWik9sISWatzvfWf3rOGoUoO5E3f193a3Q=="],
+
+ "@react-native/community-cli-plugin/metro/mime-types/mime-db": ["mime-db@1.54.0", "", {}, "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ=="],
+
"@tailwindcss/vite/@tailwindcss/node/lightningcss/lightningcss-darwin-arm64": ["lightningcss-darwin-arm64@1.32.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ=="],
"@tailwindcss/vite/@tailwindcss/node/lightningcss/lightningcss-darwin-x64": ["lightningcss-darwin-x64@1.32.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w=="],
@@ -3931,13 +3961,11 @@
"@tanstack/router-plugin/chokidar/readdirp/picomatch": ["picomatch@2.3.2", "", {}, "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA=="],
- "@ts-morph/common/minimatch/brace-expansion/balanced-match": ["balanced-match@4.0.4", "", {}, "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA=="],
+ "eslint/minimatch/brace-expansion/balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="],
- "@typescript-eslint/typescript-estree/minimatch/brace-expansion/balanced-match": ["balanced-match@4.0.4", "", {}, "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA=="],
+ "react-native/glob/minimatch/brace-expansion": ["brace-expansion@1.1.12", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="],
- "expo-updates/glob/minimatch/brace-expansion": ["brace-expansion@5.0.4", "", { "dependencies": { "balanced-match": "^4.0.2" } }, "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg=="],
-
- "glob/minimatch/brace-expansion/balanced-match": ["balanced-match@4.0.4", "", {}, "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA=="],
+ "rimraf/glob/minimatch/brace-expansion": ["brace-expansion@1.1.12", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="],
"shadcn/ora/cli-cursor/restore-cursor": ["restore-cursor@5.1.0", "", { "dependencies": { "onetime": "^7.0.0", "signal-exit": "^4.1.0" } }, "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA=="],
@@ -3947,7 +3975,7 @@
"shadcn/ora/strip-ansi/ansi-regex": ["ansi-regex@6.2.2", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="],
- "@expo/cli/glob/minimatch/brace-expansion/balanced-match": ["balanced-match@4.0.4", "", {}, "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA=="],
+ "test-exclude/minimatch/brace-expansion/balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="],
"@expo/cli/ora/chalk/ansi-styles/color-convert": ["color-convert@1.9.3", "", { "dependencies": { "color-name": "1.1.3" } }, "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg=="],
@@ -3957,12 +3985,6 @@
"@expo/cli/ora/cli-cursor/restore-cursor/signal-exit": ["signal-exit@3.0.7", "", {}, "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="],
- "@expo/config-plugins/glob/minimatch/brace-expansion/balanced-match": ["balanced-match@4.0.4", "", {}, "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA=="],
-
- "@expo/config/glob/minimatch/brace-expansion/balanced-match": ["balanced-match@4.0.4", "", {}, "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA=="],
-
- "@expo/metro-config/glob/minimatch/brace-expansion/balanced-match": ["balanced-match@4.0.4", "", {}, "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA=="],
-
"@expo/package-manager/ora/chalk/ansi-styles/color-convert": ["color-convert@1.9.3", "", { "dependencies": { "color-name": "1.1.3" } }, "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg=="],
"@expo/package-manager/ora/chalk/supports-color/has-flag": ["has-flag@3.0.0", "", {}, "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw=="],
@@ -3973,7 +3995,11 @@
"@istanbuljs/load-nyc-config/find-up/locate-path/p-locate/p-limit": ["p-limit@2.3.0", "", { "dependencies": { "p-try": "^2.0.0" } }, "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w=="],
- "expo-updates/glob/minimatch/brace-expansion/balanced-match": ["balanced-match@4.0.4", "", {}, "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA=="],
+ "@react-native/codegen/glob/minimatch/brace-expansion/balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="],
+
+ "react-native/glob/minimatch/brace-expansion/balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="],
+
+ "rimraf/glob/minimatch/brace-expansion/balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="],
"shadcn/ora/cli-cursor/restore-cursor/onetime": ["onetime@7.0.0", "", { "dependencies": { "mimic-function": "^5.0.0" } }, "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ=="],
diff --git a/docs/bun.lock b/docs/bun.lock
index 42dcc45..30af1da 100644
--- a/docs/bun.lock
+++ b/docs/bun.lock
@@ -7,10 +7,10 @@
"dependencies": {
"@takumi-rs/image-response": "^0.73.1",
"@vercel/analytics": "^2.0.1",
- "fumadocs-core": "^16.7.5",
+ "fumadocs-core": "^16.7.6",
"fumadocs-mdx": "^14.2.11",
- "fumadocs-openapi": "^10.4.1",
- "fumadocs-ui": "^16.7.5",
+ "fumadocs-openapi": "^10.5.0",
+ "fumadocs-ui": "^16.7.6",
"lucide-react": "^0.577.0",
"next": "16.2.1",
"react": "19.2.4",
@@ -568,13 +568,13 @@
"framer-motion": ["framer-motion@12.38.0", "", { "dependencies": { "motion-dom": "^12.38.0", "motion-utils": "^12.36.0", "tslib": "^2.4.0" }, "peerDependencies": { "@emotion/is-prop-valid": "*", "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" }, "optionalPeers": ["@emotion/is-prop-valid", "react", "react-dom"] }, "sha512-rFYkY/pigbcswl1XQSb7q424kSTQ8q6eAC+YUsSKooHQYuLdzdHjrt6uxUC+PRAO++q5IS7+TamgIw1AphxR+g=="],
- "fumadocs-core": ["fumadocs-core@16.7.5", "", { "dependencies": { "@formatjs/intl-localematcher": "^0.8.2", "@orama/orama": "^3.1.18", "@shikijs/rehype": "^4.0.2", "@shikijs/transformers": "^4.0.2", "estree-util-value-to-estree": "^3.5.0", "github-slugger": "^2.0.0", "hast-util-to-estree": "^3.1.3", "hast-util-to-jsx-runtime": "^2.3.6", "image-size": "^2.0.2", "mdast-util-mdx": "^3.0.0", "mdast-util-to-markdown": "^2.1.2", "negotiator": "^1.0.0", "npm-to-yarn": "^3.0.1", "path-to-regexp": "^8.3.0", "remark": "^15.0.1", "remark-gfm": "^4.0.1", "remark-rehype": "^11.1.2", "scroll-into-view-if-needed": "^3.1.0", "shiki": "^4.0.2", "tinyglobby": "^0.2.15", "unified": "^11.0.5", "unist-util-visit": "^5.1.0", "vfile": "^6.0.3" }, "peerDependencies": { "@mdx-js/mdx": "*", "@mixedbread/sdk": "^0.46.0", "@orama/core": "1.x.x", "@oramacloud/client": "2.x.x", "@tanstack/react-router": "1.x.x", "@types/estree-jsx": "*", "@types/hast": "*", "@types/mdast": "*", "@types/react": "*", "algoliasearch": "5.x.x", "flexsearch": "*", "lucide-react": "*", "next": "16.x.x", "react": "^19.2.0", "react-dom": "^19.2.0", "react-router": "7.x.x", "waku": "^0.26.0 || ^0.27.0 || ^1.0.0", "zod": "4.x.x" }, "optionalPeers": ["@mdx-js/mdx", "@mixedbread/sdk", "@orama/core", "@oramacloud/client", "@tanstack/react-router", "@types/estree-jsx", "@types/hast", "@types/mdast", "@types/react", "algoliasearch", "flexsearch", "lucide-react", "next", "react", "react-dom", "react-router", "waku", "zod"] }, "sha512-qOAKQyNUCZQ0wRun2Jnw7f6VLEgCiGFomU+kQXUZI36cX9bJPiymXboKaSjCxT1PuIusjzUyxYTGapVZxzd9TA=="],
+ "fumadocs-core": ["fumadocs-core@16.7.6", "", { "dependencies": { "@formatjs/intl-localematcher": "^0.8.2", "@orama/orama": "^3.1.18", "@shikijs/rehype": "^4.0.2", "@shikijs/transformers": "^4.0.2", "estree-util-value-to-estree": "^3.5.0", "github-slugger": "^2.0.0", "hast-util-to-estree": "^3.1.3", "hast-util-to-jsx-runtime": "^2.3.6", "image-size": "^2.0.2", "mdast-util-mdx": "^3.0.0", "mdast-util-to-markdown": "^2.1.2", "negotiator": "^1.0.0", "npm-to-yarn": "^3.0.1", "path-to-regexp": "^8.3.0", "remark": "^15.0.1", "remark-gfm": "^4.0.1", "remark-rehype": "^11.1.2", "scroll-into-view-if-needed": "^3.1.0", "shiki": "^4.0.2", "tinyglobby": "^0.2.15", "unified": "^11.0.5", "unist-util-visit": "^5.1.0", "vfile": "^6.0.3" }, "peerDependencies": { "@mdx-js/mdx": "*", "@mixedbread/sdk": "^0.46.0", "@orama/core": "1.x.x", "@oramacloud/client": "2.x.x", "@tanstack/react-router": "1.x.x", "@types/estree-jsx": "*", "@types/hast": "*", "@types/mdast": "*", "@types/react": "*", "algoliasearch": "5.x.x", "flexsearch": "*", "lucide-react": "*", "next": "16.x.x", "react": "^19.2.0", "react-dom": "^19.2.0", "react-router": "7.x.x", "waku": "^0.26.0 || ^0.27.0 || ^1.0.0", "zod": "4.x.x" }, "optionalPeers": ["@mdx-js/mdx", "@mixedbread/sdk", "@orama/core", "@oramacloud/client", "@tanstack/react-router", "@types/estree-jsx", "@types/hast", "@types/mdast", "@types/react", "algoliasearch", "flexsearch", "lucide-react", "next", "react", "react-dom", "react-router", "waku", "zod"] }, "sha512-d4HtGupFpcSWQqLbWh184yoEg6D70pH68NP77Ct4mI0N61t/Uy63wYj9sbS1h/m6jlijUIXC6rz8D5JApOB9Wg=="],
"fumadocs-mdx": ["fumadocs-mdx@14.2.11", "", { "dependencies": { "@mdx-js/mdx": "^3.1.1", "@standard-schema/spec": "^1.1.0", "chokidar": "^5.0.0", "esbuild": "^0.27.3", "estree-util-value-to-estree": "^3.5.0", "js-yaml": "^4.1.1", "mdast-util-mdx": "^3.0.0", "mdast-util-to-markdown": "^2.1.2", "picocolors": "^1.1.1", "picomatch": "^4.0.3", "tinyexec": "^1.0.4", "tinyglobby": "^0.2.15", "unified": "^11.0.5", "unist-util-remove-position": "^5.0.0", "unist-util-visit": "^5.1.0", "vfile": "^6.0.3", "zod": "^4.3.6" }, "peerDependencies": { "@fumadocs/mdx-remote": "^1.4.0", "@types/mdast": "*", "@types/mdx": "*", "@types/react": "*", "fumadocs-core": "^15.0.0 || ^16.0.0", "mdast-util-directive": "*", "next": "^15.3.0 || ^16.0.0", "react": "*", "vite": "6.x.x || 7.x.x || 8.x.x" }, "optionalPeers": ["@fumadocs/mdx-remote", "@types/mdast", "@types/mdx", "@types/react", "mdast-util-directive", "next", "react", "vite"], "bin": { "fumadocs-mdx": "dist/bin.js" } }, "sha512-j0gHKs45c62ARteE8/yBM2Nu2I8AE2Cs37ktPEdc/8EX7TL66XP74un5OpHp6itLyWTu8Jur0imOiiIDq8+rDg=="],
- "fumadocs-openapi": ["fumadocs-openapi@10.4.1", "", { "dependencies": { "@fastify/deepmerge": "^3.2.1", "@fumari/json-schema-ts": "^0.0.2", "@fumari/stf": "1.0.3", "@radix-ui/react-accordion": "^1.2.12", "@radix-ui/react-dialog": "^1.1.15", "@radix-ui/react-select": "^2.2.6", "@radix-ui/react-slot": "^1.2.4", "@scalar/json-magic": "^0.12.4", "@scalar/openapi-upgrader": "^0.2.1", "ajv": "^8.18.0", "class-variance-authority": "^0.7.1", "dereference-json-schema": "^0.2.2", "github-slugger": "^2.0.0", "hast-util-to-jsx-runtime": "^2.3.6", "js-yaml": "^4.1.1", "lucide-react": "^0.577.0", "next-themes": "^0.4.6", "openapi-sampler": "^1.7.2", "react-hook-form": "^7.71.2", "remark": "^15.0.1", "remark-rehype": "^11.1.2", "tailwind-merge": "^3.5.0", "xml-js": "^1.6.11" }, "peerDependencies": { "@scalar/api-client-react": "*", "@types/react": "*", "fumadocs-core": "^16.7.0", "fumadocs-ui": "^16.7.0", "json-schema-typed": "*", "react": "^19.2.0", "react-dom": "^19.2.0", "shiki": "*" }, "optionalPeers": ["@scalar/api-client-react", "@types/react", "json-schema-typed", "shiki"] }, "sha512-iMEDyANcikDePUFlBE5sffjTbrUI5LELuIla+6kTZJYEuoQMLUK+plOnXq1iacPBMeCS7cSFadvE/Vp/h7oA+g=="],
+ "fumadocs-openapi": ["fumadocs-openapi@10.5.0", "", { "dependencies": { "@fastify/deepmerge": "^3.2.1", "@fumari/json-schema-ts": "^0.0.2", "@fumari/stf": "1.0.3", "@radix-ui/react-accordion": "^1.2.12", "@radix-ui/react-dialog": "^1.1.15", "@radix-ui/react-select": "^2.2.6", "@radix-ui/react-slot": "^1.2.4", "@scalar/json-magic": "^0.12.4", "@scalar/openapi-upgrader": "^0.2.2", "ajv": "^8.18.0", "class-variance-authority": "^0.7.1", "dereference-json-schema": "^0.2.2", "github-slugger": "^2.0.0", "hast-util-to-jsx-runtime": "^2.3.6", "js-yaml": "^4.1.1", "lucide-react": "^1.6.0", "next-themes": "^0.4.6", "openapi-sampler": "^1.7.2", "react-hook-form": "^7.72.0", "remark": "^15.0.1", "remark-rehype": "^11.1.2", "tailwind-merge": "^3.5.0", "xml-js": "^1.6.11" }, "peerDependencies": { "@scalar/api-client-react": "*", "@types/react": "*", "fumadocs-core": "^16.7.0", "fumadocs-ui": "^16.7.0", "json-schema-typed": "*", "react": "^19.2.0", "react-dom": "^19.2.0", "shiki": "*" }, "optionalPeers": ["@scalar/api-client-react", "@types/react", "json-schema-typed", "shiki"] }, "sha512-EG3/U/NI/ha0xhIUiUIKiRSmLS4AOjn+4xkZXOoiLB5rlV+HyHsq9qcN+wKgbK9u4qoNuAcMzTEYUXScoSUbXA=="],
- "fumadocs-ui": ["fumadocs-ui@16.7.5", "", { "dependencies": { "@fumadocs/tailwind": "0.0.3", "@radix-ui/react-accordion": "^1.2.12", "@radix-ui/react-collapsible": "^1.1.12", "@radix-ui/react-dialog": "^1.1.15", "@radix-ui/react-direction": "^1.1.1", "@radix-ui/react-navigation-menu": "^1.2.14", "@radix-ui/react-popover": "^1.1.15", "@radix-ui/react-presence": "^1.1.5", "@radix-ui/react-scroll-area": "^1.2.10", "@radix-ui/react-slot": "^1.2.4", "@radix-ui/react-tabs": "^1.1.13", "class-variance-authority": "^0.7.1", "lucide-react": "^0.577.0", "motion": "^12.38.0", "next-themes": "^0.4.6", "react-medium-image-zoom": "^5.4.1", "react-remove-scroll": "^2.7.2", "rehype-raw": "^7.0.0", "scroll-into-view-if-needed": "^3.1.0", "tailwind-merge": "^3.5.0", "unist-util-visit": "^5.1.0" }, "peerDependencies": { "@takumi-rs/image-response": "*", "@types/mdx": "*", "@types/react": "*", "fumadocs-core": "16.7.5", "next": "16.x.x", "react": "^19.2.0", "react-dom": "^19.2.0", "shiki": "*" }, "optionalPeers": ["@takumi-rs/image-response", "@types/mdx", "@types/react", "next", "shiki"] }, "sha512-i9zvslCFc25hxYsAmkBCJUgAwvWbKwMn4Xd5L7LcnQwW60r8VPihkfeWB/DDtHv4wJTGOPl+neb1AgNkm1Cujw=="],
+ "fumadocs-ui": ["fumadocs-ui@16.7.6", "", { "dependencies": { "@fumadocs/tailwind": "0.0.3", "@radix-ui/react-accordion": "^1.2.12", "@radix-ui/react-collapsible": "^1.1.12", "@radix-ui/react-dialog": "^1.1.15", "@radix-ui/react-direction": "^1.1.1", "@radix-ui/react-navigation-menu": "^1.2.14", "@radix-ui/react-popover": "^1.1.15", "@radix-ui/react-presence": "^1.1.5", "@radix-ui/react-scroll-area": "^1.2.10", "@radix-ui/react-slot": "^1.2.4", "@radix-ui/react-tabs": "^1.1.13", "class-variance-authority": "^0.7.1", "lucide-react": "^1.6.0", "motion": "^12.38.0", "next-themes": "^0.4.6", "react-medium-image-zoom": "^5.4.1", "react-remove-scroll": "^2.7.2", "rehype-raw": "^7.0.0", "scroll-into-view-if-needed": "^3.1.0", "tailwind-merge": "^3.5.0", "unist-util-visit": "^5.1.0" }, "peerDependencies": { "@takumi-rs/image-response": "*", "@types/mdx": "*", "@types/react": "*", "fumadocs-core": "16.7.6", "next": "16.x.x", "react": "^19.2.0", "react-dom": "^19.2.0", "shiki": "*" }, "optionalPeers": ["@takumi-rs/image-response", "@types/mdx", "@types/react", "next", "shiki"] }, "sha512-wjZnm8SiX2lj5zWOlOHnzSZ0YBFwNqYGBX1u5F3mZtdIkmkDVs+3+JngCkRHNZzYJVBulXjp8t5wzBz0yDJa8w=="],
"get-nonce": ["get-nonce@1.0.1", "", {}, "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q=="],
@@ -968,6 +968,10 @@
"@tailwindcss/oxide-wasm32-wasi/tslib": ["tslib@2.8.1", "", { "bundled": true }, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
+ "fumadocs-openapi/lucide-react": ["lucide-react@1.7.0", "", { "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-yI7BeItCLZJTXikmK4KNUGCKoGzSvbKlfCvw44bU4fXAL6v3gYS4uHD1jzsLkfwODYwI6Drw5Tu9Z5ulDe0TSg=="],
+
+ "fumadocs-ui/lucide-react": ["lucide-react@1.7.0", "", { "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-yI7BeItCLZJTXikmK4KNUGCKoGzSvbKlfCvw44bU4fXAL6v3gYS4uHD1jzsLkfwODYwI6Drw5Tu9Z5ulDe0TSg=="],
+
"next/postcss": ["postcss@8.4.31", "", { "dependencies": { "nanoid": "^3.3.6", "picocolors": "^1.0.0", "source-map-js": "^1.0.2" } }, "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ=="],
"parse-entities/@types/unist": ["@types/unist@2.0.11", "", {}, "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA=="],
diff --git a/docs/content/docs/api/integrations/integrations.create.mdx b/docs/content/docs/api/account/account.integrations.create.mdx
similarity index 93%
rename from docs/content/docs/api/integrations/integrations.create.mdx
rename to docs/content/docs/api/account/account.integrations.create.mdx
index 4700a57..29aa675 100644
--- a/docs/content/docs/api/integrations/integrations.create.mdx
+++ b/docs/content/docs/api/account/account.integrations.create.mdx
@@ -16,4 +16,4 @@ _openapi:
Create a new media server integration or update an existing one. Generates a unique webhook token for the provider.
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/docs/content/docs/api/integrations/integrations.delete.mdx b/docs/content/docs/api/account/account.integrations.delete.mdx
similarity index 89%
rename from docs/content/docs/api/integrations/integrations.delete.mdx
rename to docs/content/docs/api/account/account.integrations.delete.mdx
index b5e948d..d560771 100644
--- a/docs/content/docs/api/integrations/integrations.delete.mdx
+++ b/docs/content/docs/api/account/account.integrations.delete.mdx
@@ -14,4 +14,4 @@ _openapi:
Remove a media server integration and all its event history.
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/docs/content/docs/api/integrations/integrations.list.mdx b/docs/content/docs/api/account/account.integrations.list.mdx
similarity index 93%
rename from docs/content/docs/api/integrations/integrations.list.mdx
rename to docs/content/docs/api/account/account.integrations.list.mdx
index e403f39..def6aac 100644
--- a/docs/content/docs/api/integrations/integrations.list.mdx
+++ b/docs/content/docs/api/account/account.integrations.list.mdx
@@ -16,4 +16,4 @@ _openapi:
Fetch all configured media server integrations for the current user, including recent webhook/sync events for each.
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/docs/content/docs/api/integrations/integrations.regenerateToken.mdx b/docs/content/docs/api/account/account.integrations.regenerateToken.mdx
similarity index 88%
rename from docs/content/docs/api/integrations/integrations.regenerateToken.mdx
rename to docs/content/docs/api/account/account.integrations.regenerateToken.mdx
index 8bb9005..57421de 100644
--- a/docs/content/docs/api/integrations/integrations.regenerateToken.mdx
+++ b/docs/content/docs/api/account/account.integrations.regenerateToken.mdx
@@ -16,4 +16,4 @@ _openapi:
Generate a new webhook token for an integration. The old token is immediately invalidated.
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/docs/content/docs/api/admin/admin.registration.mdx b/docs/content/docs/api/admin/admin.registration.mdx
deleted file mode 100644
index ec4636d..0000000
--- a/docs/content/docs/api/admin/admin.registration.mdx
+++ /dev/null
@@ -1,17 +0,0 @@
----
-title: Get registration status
-full: true
-_openapi:
- method: GET
- toc: []
- structuredData:
- headings: []
- contents:
- - content: Check whether new user registration is currently open or closed.
----
-
-{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */}
-
-Check whether new user registration is currently open or closed.
-
-
\ No newline at end of file
diff --git a/docs/content/docs/api/admin/admin.telemetry.mdx b/docs/content/docs/api/admin/admin.settings.get.mdx
similarity index 57%
rename from docs/content/docs/api/admin/admin.telemetry.mdx
rename to docs/content/docs/api/admin/admin.settings.get.mdx
index a1ff6d5..ec7ba9d 100644
--- a/docs/content/docs/api/admin/admin.telemetry.mdx
+++ b/docs/content/docs/api/admin/admin.settings.get.mdx
@@ -1,5 +1,5 @@
---
-title: Get telemetry status
+title: Get admin settings
full: true
_openapi:
method: GET
@@ -8,12 +8,12 @@ _openapi:
headings: []
contents:
- content: >-
- Fetch whether anonymous telemetry is enabled and when the last report
- was sent.
+ Fetch all admin-configurable settings: registration, update checks,
+ and telemetry.
---
{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */}
-Fetch whether anonymous telemetry is enabled and when the last report was sent.
+Fetch all admin-configurable settings: registration, update checks, and telemetry.
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/docs/content/docs/api/admin/admin.settings.update.mdx b/docs/content/docs/api/admin/admin.settings.update.mdx
new file mode 100644
index 0000000..3aa7882
--- /dev/null
+++ b/docs/content/docs/api/admin/admin.settings.update.mdx
@@ -0,0 +1,19 @@
+---
+title: Update admin settings
+full: true
+_openapi:
+ method: PATCH
+ toc: []
+ structuredData:
+ headings: []
+ contents:
+ - content: >-
+ Partially update admin settings. Only provided sections are changed;
+ omitted sections keep their current values.
+---
+
+{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */}
+
+Partially update admin settings. Only provided sections are changed; omitted sections keep their current values.
+
+
\ No newline at end of file
diff --git a/docs/content/docs/api/admin/admin.toggleRegistration.mdx b/docs/content/docs/api/admin/admin.toggleRegistration.mdx
deleted file mode 100644
index 993c7b5..0000000
--- a/docs/content/docs/api/admin/admin.toggleRegistration.mdx
+++ /dev/null
@@ -1,17 +0,0 @@
----
-title: Toggle registration
-full: true
-_openapi:
- method: PUT
- toc: []
- structuredData:
- headings: []
- contents:
- - content: Open or close new user registration.
----
-
-{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */}
-
-Open or close new user registration.
-
-
\ No newline at end of file
diff --git a/docs/content/docs/api/admin/admin.toggleTelemetry.mdx b/docs/content/docs/api/admin/admin.toggleTelemetry.mdx
deleted file mode 100644
index 851ddf9..0000000
--- a/docs/content/docs/api/admin/admin.toggleTelemetry.mdx
+++ /dev/null
@@ -1,17 +0,0 @@
----
-title: Toggle telemetry
-full: true
-_openapi:
- method: PUT
- toc: []
- structuredData:
- headings: []
- contents:
- - content: Enable or disable anonymous telemetry reporting.
----
-
-{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */}
-
-Enable or disable anonymous telemetry reporting.
-
-
\ No newline at end of file
diff --git a/docs/content/docs/api/admin/admin.toggleUpdateCheck.mdx b/docs/content/docs/api/admin/admin.toggleUpdateCheck.mdx
deleted file mode 100644
index 0fc2f34..0000000
--- a/docs/content/docs/api/admin/admin.toggleUpdateCheck.mdx
+++ /dev/null
@@ -1,17 +0,0 @@
----
-title: Toggle update checks
-full: true
-_openapi:
- method: PUT
- toc: []
- structuredData:
- headings: []
- contents:
- - content: Enable or disable automatic update checks against the public API.
----
-
-{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */}
-
-Enable or disable automatic update checks against the public API.
-
-
\ No newline at end of file
diff --git a/docs/content/docs/api/dashboard/dashboard.stats.mdx b/docs/content/docs/api/dashboard/dashboard.stats.mdx
deleted file mode 100644
index bef3e88..0000000
--- a/docs/content/docs/api/dashboard/dashboard.stats.mdx
+++ /dev/null
@@ -1,19 +0,0 @@
----
-title: Get dashboard statistics
-full: true
-_openapi:
- method: GET
- toc: []
- structuredData:
- headings: []
- contents:
- - content: >-
- Fetch summary counts for the current user: movies watched this month,
- episodes this week, library size, and completed titles.
----
-
-{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */}
-
-Fetch summary counts for the current user: movies watched this month, episodes this week, library size, and completed titles.
-
-
\ No newline at end of file
diff --git a/docs/content/docs/api/discover/discover.mdx b/docs/content/docs/api/discover/discover.browse.mdx
similarity index 88%
rename from docs/content/docs/api/discover/discover.mdx
rename to docs/content/docs/api/discover/discover.browse.mdx
index e16ec06..41d084e 100644
--- a/docs/content/docs/api/discover/discover.mdx
+++ b/docs/content/docs/api/discover/discover.browse.mdx
@@ -1,5 +1,5 @@
---
-title: Discover titles by genre
+title: Browse titles with filters
full: true
_openapi:
method: GET
@@ -16,4 +16,4 @@ _openapi:
Browse movies or TV shows filtered by genre, sorted by popularity. Returns user statuses and episode progress.
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/docs/content/docs/api/explore/explore.genres.mdx b/docs/content/docs/api/discover/discover.genres.mdx
similarity index 93%
rename from docs/content/docs/api/explore/explore.genres.mdx
rename to docs/content/docs/api/discover/discover.genres.mdx
index aa55119..45e2989 100644
--- a/docs/content/docs/api/explore/explore.genres.mdx
+++ b/docs/content/docs/api/discover/discover.genres.mdx
@@ -16,4 +16,4 @@ _openapi:
Fetch the list of TMDB genres for movies or TV shows. Used to populate genre filters for discovery.
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/docs/content/docs/api/admin/admin.updateCheck.mdx b/docs/content/docs/api/discover/discover.platforms.mdx
similarity index 50%
rename from docs/content/docs/api/admin/admin.updateCheck.mdx
rename to docs/content/docs/api/discover/discover.platforms.mdx
index c950738..94b9e80 100644
--- a/docs/content/docs/api/admin/admin.updateCheck.mdx
+++ b/docs/content/docs/api/discover/discover.platforms.mdx
@@ -1,5 +1,5 @@
---
-title: Get update check status
+title: List available streaming platforms
full: true
_openapi:
method: GET
@@ -8,12 +8,12 @@ _openapi:
headings: []
contents:
- content: >-
- Fetch whether automatic update checks are enabled, and the latest
- cached check result if available.
+ Fetch all available streaming platforms, ordered by popularity.
+ Includes subscription status for filtering.
---
{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */}
-Fetch whether automatic update checks are enabled, and the latest cached check result if available.
+Fetch all available streaming platforms, ordered by popularity. Includes subscription status for filtering.
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/docs/content/docs/api/explore/explore.popular.mdx b/docs/content/docs/api/discover/discover.popular.mdx
similarity index 93%
rename from docs/content/docs/api/explore/explore.popular.mdx
rename to docs/content/docs/api/discover/discover.popular.mdx
index 77167a4..129acc4 100644
--- a/docs/content/docs/api/explore/explore.popular.mdx
+++ b/docs/content/docs/api/discover/discover.popular.mdx
@@ -16,4 +16,4 @@ _openapi:
Fetch currently popular movies or TV shows from TMDB with the user's tracking statuses.
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/docs/content/docs/api/dashboard/dashboard.recommendations.mdx b/docs/content/docs/api/discover/discover.recommendations.mdx
similarity index 91%
rename from docs/content/docs/api/dashboard/dashboard.recommendations.mdx
rename to docs/content/docs/api/discover/discover.recommendations.mdx
index e2b3113..192c843 100644
--- a/docs/content/docs/api/dashboard/dashboard.recommendations.mdx
+++ b/docs/content/docs/api/discover/discover.recommendations.mdx
@@ -16,4 +16,4 @@ _openapi:
Fetch personalized title recommendations based on the user's library and watch history.
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/docs/content/docs/api/search/search.mdx b/docs/content/docs/api/discover/discover.search.mdx
similarity index 93%
rename from docs/content/docs/api/search/search.mdx
rename to docs/content/docs/api/discover/discover.search.mdx
index 4771af5..5215b35 100644
--- a/docs/content/docs/api/search/search.mdx
+++ b/docs/content/docs/api/discover/discover.search.mdx
@@ -16,4 +16,4 @@ _openapi:
Full-text search across movies, TV shows, and people via TMDB. Optionally filter by media type.
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/docs/content/docs/api/explore/explore.trending.mdx b/docs/content/docs/api/discover/discover.trending.mdx
similarity index 93%
rename from docs/content/docs/api/explore/explore.trending.mdx
rename to docs/content/docs/api/discover/discover.trending.mdx
index 6c8efee..4168d5d 100644
--- a/docs/content/docs/api/explore/explore.trending.mdx
+++ b/docs/content/docs/api/discover/discover.trending.mdx
@@ -16,4 +16,4 @@ _openapi:
Fetch today's trending movies and/or TV shows from TMDB, including a featured hero title and the user's statuses.
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/docs/content/docs/api/episodes/episodes.batchWatch.mdx b/docs/content/docs/api/episodes/episodes.batchWatch.mdx
deleted file mode 100644
index ab47be3..0000000
--- a/docs/content/docs/api/episodes/episodes.batchWatch.mdx
+++ /dev/null
@@ -1,19 +0,0 @@
----
-title: Batch mark episodes watched
-full: true
-_openapi:
- method: POST
- toc: []
- structuredData:
- headings: []
- contents:
- - content: >-
- Record watch events for multiple episodes at once. Useful for marking
- a range of episodes as watched.
----
-
-{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */}
-
-Record watch events for multiple episodes at once. Useful for marking a range of episodes as watched.
-
-
\ No newline at end of file
diff --git a/docs/content/docs/api/episodes/episodes.unwatch.mdx b/docs/content/docs/api/episodes/episodes.unwatch.mdx
deleted file mode 100644
index a7664c6..0000000
--- a/docs/content/docs/api/episodes/episodes.unwatch.mdx
+++ /dev/null
@@ -1,17 +0,0 @@
----
-title: Mark episode unwatched
-full: true
-_openapi:
- method: POST
- toc: []
- structuredData:
- headings: []
- contents:
- - content: Remove the watch record for a single TV episode.
----
-
-{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */}
-
-Remove the watch record for a single TV episode.
-
-
\ No newline at end of file
diff --git a/docs/content/docs/api/episodes/episodes.watch.mdx b/docs/content/docs/api/episodes/episodes.watch.mdx
deleted file mode 100644
index d3d39a1..0000000
--- a/docs/content/docs/api/episodes/episodes.watch.mdx
+++ /dev/null
@@ -1,17 +0,0 @@
----
-title: Mark episode watched
-full: true
-_openapi:
- method: POST
- toc: []
- structuredData:
- headings: []
- contents:
- - content: Record a watch event for a single TV episode.
----
-
-{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */}
-
-Record a watch event for a single TV episode.
-
-
\ No newline at end of file
diff --git a/docs/content/docs/api/explore/explore.watchProviders.mdx b/docs/content/docs/api/explore/explore.watchProviders.mdx
deleted file mode 100644
index 24f8b2d..0000000
--- a/docs/content/docs/api/explore/explore.watchProviders.mdx
+++ /dev/null
@@ -1,19 +0,0 @@
----
-title: List available watch providers
-full: true
-_openapi:
- method: GET
- toc: []
- structuredData:
- headings: []
- contents:
- - content: >-
- Fetch the list of streaming providers available in the configured
- region. Used to populate provider filter dropdowns.
----
-
-{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */}
-
-Fetch the list of streaming providers available in the configured region. Used to populate provider filter dropdowns.
-
-
\ No newline at end of file
diff --git a/docs/content/docs/api/dashboard/dashboard.continueWatching.mdx b/docs/content/docs/api/library/library.continueWatching.mdx
similarity index 91%
rename from docs/content/docs/api/dashboard/dashboard.continueWatching.mdx
rename to docs/content/docs/api/library/library.continueWatching.mdx
index 44b1ed6..2972a22 100644
--- a/docs/content/docs/api/dashboard/dashboard.continueWatching.mdx
+++ b/docs/content/docs/api/library/library.continueWatching.mdx
@@ -16,4 +16,4 @@ _openapi:
Fetch TV shows the user is currently watching, with the next unwatched episode and progress for each.
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/docs/content/docs/api/platforms/platforms.list.mdx b/docs/content/docs/api/library/library.stats.mdx
similarity index 58%
rename from docs/content/docs/api/platforms/platforms.list.mdx
rename to docs/content/docs/api/library/library.stats.mdx
index 901de36..b6bea8f 100644
--- a/docs/content/docs/api/platforms/platforms.list.mdx
+++ b/docs/content/docs/api/library/library.stats.mdx
@@ -1,5 +1,5 @@
---
-title: List all platforms
+title: Get library statistics
full: true
_openapi:
method: GET
@@ -7,11 +7,11 @@ _openapi:
structuredData:
headings: []
contents:
- - content: Fetch all available streaming platforms, ordered by popularity.
+ - content: 'Fetch aggregate library counts: total titles and completed titles.'
---
{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */}
-Fetch all available streaming platforms, ordered by popularity.
+Fetch aggregate library counts: total titles and completed titles.
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/docs/content/docs/api/dashboard/dashboard.upcoming.mdx b/docs/content/docs/api/library/library.upcoming.mdx
similarity index 93%
rename from docs/content/docs/api/dashboard/dashboard.upcoming.mdx
rename to docs/content/docs/api/library/library.upcoming.mdx
index ea24f44..b3b7a5a 100644
--- a/docs/content/docs/api/dashboard/dashboard.upcoming.mdx
+++ b/docs/content/docs/api/library/library.upcoming.mdx
@@ -16,4 +16,4 @@ _openapi:
Fetch upcoming episodes and movie releases for titles in the user's library, sorted by date. Supports cursor-based pagination.
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/docs/content/docs/api/people/people.detail.mdx b/docs/content/docs/api/people/people.get.mdx
similarity index 100%
rename from docs/content/docs/api/people/people.detail.mdx
rename to docs/content/docs/api/people/people.get.mdx
diff --git a/docs/content/docs/api/seasons/seasons.unwatch.mdx b/docs/content/docs/api/seasons/seasons.unwatch.mdx
deleted file mode 100644
index f950df1..0000000
--- a/docs/content/docs/api/seasons/seasons.unwatch.mdx
+++ /dev/null
@@ -1,19 +0,0 @@
----
-title: Mark season unwatched
-full: true
-_openapi:
- method: POST
- toc: []
- structuredData:
- headings: []
- contents:
- - content: >-
- Remove all watch records for episodes in a season in a single
- operation.
----
-
-{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */}
-
-Remove all watch records for episodes in a season in a single operation.
-
-
\ No newline at end of file
diff --git a/docs/content/docs/api/seasons/seasons.watch.mdx b/docs/content/docs/api/seasons/seasons.watch.mdx
deleted file mode 100644
index 9485eed..0000000
--- a/docs/content/docs/api/seasons/seasons.watch.mdx
+++ /dev/null
@@ -1,17 +0,0 @@
----
-title: Mark season watched
-full: true
-_openapi:
- method: POST
- toc: []
- structuredData:
- headings: []
- contents:
- - content: Mark all episodes in a season as watched in a single operation.
----
-
-{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */}
-
-Mark all episodes in a season as watched in a single operation.
-
-
\ No newline at end of file
diff --git a/docs/content/docs/api/system/system.authConfig.mdx b/docs/content/docs/api/system/system.authConfig.mdx
deleted file mode 100644
index b04cfdf..0000000
--- a/docs/content/docs/api/system/system.authConfig.mdx
+++ /dev/null
@@ -1,20 +0,0 @@
----
-title: Get authentication config
-full: true
-_openapi:
- method: GET
- toc: []
- structuredData:
- headings: []
- contents:
- - content: >-
- Fetch the authentication configuration including OIDC availability,
- password login status, and registration state. Does not require
- authentication.
----
-
-{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */}
-
-Fetch the authentication configuration including OIDC availability, password login status, and registration state. Does not require authentication.
-
-
\ No newline at end of file
diff --git a/docs/content/docs/api/system/system.publicInfo.mdx b/docs/content/docs/api/system/system.publicInfo.mdx
index 2dab0e8..26d4824 100644
--- a/docs/content/docs/api/system/system.publicInfo.mdx
+++ b/docs/content/docs/api/system/system.publicInfo.mdx
@@ -8,12 +8,12 @@ _openapi:
headings: []
contents:
- content: >-
- Fetch public information about this Sofa instance. Does not require
- authentication. Used by the login screen to display instance details.
+ Fetch public information about this Sofa instance including
+ authentication configuration. Does not require authentication.
---
{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */}
-Fetch public information about this Sofa instance. Does not require authentication. Used by the login screen to display instance details.
+Fetch public information about this Sofa instance including authentication configuration. Does not require authentication.
\ No newline at end of file
diff --git a/docs/content/docs/api/titles/titles.detail.mdx b/docs/content/docs/api/titles/titles.get.mdx
similarity index 100%
rename from docs/content/docs/api/titles/titles.detail.mdx
rename to docs/content/docs/api/titles/titles.get.mdx
diff --git a/docs/content/docs/api/titles/titles.quickAdd.mdx b/docs/content/docs/api/titles/titles.quickAdd.mdx
deleted file mode 100644
index 55050c7..0000000
--- a/docs/content/docs/api/titles/titles.quickAdd.mdx
+++ /dev/null
@@ -1,20 +0,0 @@
----
-title: Quick add title to library
-full: true
-_openapi:
- method: POST
- toc: []
- structuredData:
- headings: []
- contents:
- - content: >-
- Add a title to the user's watchlist and trigger a full TMDB import if
- needed. If the title already exists in the user's library, returns
- alreadyAdded: true.
----
-
-{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */}
-
-Add a title to the user's watchlist and trigger a full TMDB import if needed. If the title already exists in the user's library, returns alreadyAdded: true.
-
-
\ No newline at end of file
diff --git a/docs/content/docs/api/titles/titles.recommendations.mdx b/docs/content/docs/api/titles/titles.similar.mdx
similarity index 86%
rename from docs/content/docs/api/titles/titles.recommendations.mdx
rename to docs/content/docs/api/titles/titles.similar.mdx
index 96375cc..7eab4fb 100644
--- a/docs/content/docs/api/titles/titles.recommendations.mdx
+++ b/docs/content/docs/api/titles/titles.similar.mdx
@@ -1,5 +1,5 @@
---
-title: Get title recommendations
+title: Get similar titles
full: true
_openapi:
method: GET
@@ -16,4 +16,4 @@ _openapi:
Fetch similar titles based on locally cached recommendation data, along with the user's statuses for each.
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/docs/content/docs/api/titles/titles.watchAll.mdx b/docs/content/docs/api/titles/titles.watchAll.mdx
deleted file mode 100644
index f7a72d5..0000000
--- a/docs/content/docs/api/titles/titles.watchAll.mdx
+++ /dev/null
@@ -1,19 +0,0 @@
----
-title: Mark all episodes watched
-full: true
-_openapi:
- method: POST
- toc: []
- structuredData:
- headings: []
- contents:
- - content: >-
- Mark every episode of a TV show as watched. Requires seasons to be
- hydrated first.
----
-
-{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */}
-
-Mark every episode of a TV show as watched. Requires seasons to be hydrated first.
-
-
\ No newline at end of file
diff --git a/docs/content/docs/api/titles/titles.watchMovie.mdx b/docs/content/docs/api/titles/titles.watchMovie.mdx
deleted file mode 100644
index 9fb5f21..0000000
--- a/docs/content/docs/api/titles/titles.watchMovie.mdx
+++ /dev/null
@@ -1,17 +0,0 @@
----
-title: Mark movie as watched
-full: true
-_openapi:
- method: POST
- toc: []
- structuredData:
- headings: []
- contents:
- - content: Log a watch event for a movie. Automatically sets status to completed.
----
-
-{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */}
-
-Log a watch event for a movie. Automatically sets status to completed.
-
-
\ No newline at end of file
diff --git a/docs/content/docs/api/titles/titles.updateRating.mdx b/docs/content/docs/api/tracking/tracking.rate.mdx
similarity index 90%
rename from docs/content/docs/api/titles/titles.updateRating.mdx
rename to docs/content/docs/api/tracking/tracking.rate.mdx
index 4ff8a0b..2779959 100644
--- a/docs/content/docs/api/titles/titles.updateRating.mdx
+++ b/docs/content/docs/api/tracking/tracking.rate.mdx
@@ -14,4 +14,4 @@ _openapi:
Set a 0-5 star rating for a title. Use 0 to clear the rating.
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/docs/content/docs/api/dashboard/dashboard.watchHistory.mdx b/docs/content/docs/api/tracking/tracking.stats.mdx
similarity index 72%
rename from docs/content/docs/api/dashboard/dashboard.watchHistory.mdx
rename to docs/content/docs/api/tracking/tracking.stats.mdx
index 26ee3ea..52a6283 100644
--- a/docs/content/docs/api/dashboard/dashboard.watchHistory.mdx
+++ b/docs/content/docs/api/tracking/tracking.stats.mdx
@@ -1,5 +1,5 @@
---
-title: Get watch history
+title: Get watch statistics
full: true
_openapi:
method: GET
@@ -9,11 +9,11 @@ _openapi:
contents:
- content: >-
Fetch the user's watch counts grouped by time period. Useful for
- rendering activity charts.
+ rendering activity charts and dashboard counters.
---
{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */}
-Fetch the user's watch counts grouped by time period. Useful for rendering activity charts.
+Fetch the user's watch counts grouped by time period. Useful for rendering activity charts and dashboard counters.
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/docs/content/docs/api/tracking/tracking.unwatch.mdx b/docs/content/docs/api/tracking/tracking.unwatch.mdx
new file mode 100644
index 0000000..dad3d89
--- /dev/null
+++ b/docs/content/docs/api/tracking/tracking.unwatch.mdx
@@ -0,0 +1,19 @@
+---
+title: Remove watch records
+full: true
+_openapi:
+ method: POST
+ toc: []
+ structuredData:
+ headings: []
+ contents:
+ - content: >-
+ Remove watch records for one or more items. Use scope to indicate what
+ the IDs refer to: movie, episode, season, or series.
+---
+
+{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */}
+
+Remove watch records for one or more items. Use scope to indicate what the IDs refer to: movie, episode, season, or series.
+
+
\ No newline at end of file
diff --git a/docs/content/docs/api/titles/titles.updateStatus.mdx b/docs/content/docs/api/tracking/tracking.updateStatus.mdx
similarity index 91%
rename from docs/content/docs/api/titles/titles.updateStatus.mdx
rename to docs/content/docs/api/tracking/tracking.updateStatus.mdx
index f98b784..e4f29e6 100644
--- a/docs/content/docs/api/titles/titles.updateStatus.mdx
+++ b/docs/content/docs/api/tracking/tracking.updateStatus.mdx
@@ -16,4 +16,4 @@ _openapi:
Set the user's tracking status for a title. Use null to remove the title from the library entirely.
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/docs/content/docs/api/titles/titles.userInfo.mdx b/docs/content/docs/api/tracking/tracking.userInfo.mdx
similarity index 85%
rename from docs/content/docs/api/titles/titles.userInfo.mdx
rename to docs/content/docs/api/tracking/tracking.userInfo.mdx
index 1d48e1b..61c4bc5 100644
--- a/docs/content/docs/api/titles/titles.userInfo.mdx
+++ b/docs/content/docs/api/tracking/tracking.userInfo.mdx
@@ -1,5 +1,5 @@
---
-title: Get user's title info
+title: Get user's tracking info for a title
full: true
_openapi:
method: GET
@@ -16,4 +16,4 @@ _openapi:
Fetch the current user's tracking status, rating, and watched episode IDs for a title.
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/docs/content/docs/api/tracking/tracking.watch.mdx b/docs/content/docs/api/tracking/tracking.watch.mdx
new file mode 100644
index 0000000..88deef7
--- /dev/null
+++ b/docs/content/docs/api/tracking/tracking.watch.mdx
@@ -0,0 +1,20 @@
+---
+title: Mark items as watched
+full: true
+_openapi:
+ method: POST
+ toc: []
+ structuredData:
+ headings: []
+ contents:
+ - content: >-
+ Mark one or more items as watched. Use scope to indicate what the IDs
+ refer to: movie (title IDs), episode (episode IDs), season (season
+ IDs), or series (title IDs to mark all episodes).
+---
+
+{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */}
+
+Mark one or more items as watched. Use scope to indicate what the IDs refer to: movie (title IDs), episode (episode IDs), season (season IDs), or series (title IDs to mark all episodes).
+
+
\ No newline at end of file
diff --git a/docs/content/docs/mobile-app.mdx b/docs/content/docs/mobile-app.mdx
index 6113b8c..7e9ce2c 100644
--- a/docs/content/docs/mobile-app.mdx
+++ b/docs/content/docs/mobile-app.mdx
@@ -4,10 +4,118 @@ description: Set up the iOS and Android app.
icon: Smartphone
---
+export const AppStoreBadge = (props) => (
+
+);
+
+export const GooglePlayBadge = (props) => (
+
+);
+
Sofa has a native mobile app for iOS and Android built with [Expo](https://expo.dev/) and React Native. It connects to your self-hosted Sofa server and provides the same core features as the web app.
+
+
- The mobile app is not currently available on the App Store or Google Play. You'll need to build it
+ The mobile app is not currently available on Google Play. You'll need to build it
from source.
diff --git a/docs/next.config.mjs b/docs/next.config.mjs
index 26d76b4..ab7da44 100644
--- a/docs/next.config.mjs
+++ b/docs/next.config.mjs
@@ -14,7 +14,7 @@ const config = {
return [
{
source: "/docs/api",
- destination: "/docs/api/account/account.removeAvatar",
+ destination: "/docs/api/account/account.integrations.create",
permanent: false,
},
{
@@ -32,6 +32,11 @@ const config = {
destination: "https://testflight.apple.com/join/tjSddcaZ",
permanent: false,
},
+ {
+ source: "/android",
+ destination: "https://play.google.com/store/apps/details?id=com.jakejarvis.sofa",
+ permanent: false,
+ }
];
},
async rewrites() {
diff --git a/docs/package.json b/docs/package.json
index dc86ade..9551917 100644
--- a/docs/package.json
+++ b/docs/package.json
@@ -16,10 +16,10 @@
"dependencies": {
"@takumi-rs/image-response": "^0.73.1",
"@vercel/analytics": "^2.0.1",
- "fumadocs-core": "^16.7.5",
+ "fumadocs-core": "^16.7.6",
"fumadocs-mdx": "^14.2.11",
- "fumadocs-openapi": "^10.4.1",
- "fumadocs-ui": "^16.7.5",
+ "fumadocs-openapi": "^10.5.0",
+ "fumadocs-ui": "^16.7.6",
"lucide-react": "^0.577.0",
"next": "16.2.1",
"react": "19.2.4",
diff --git a/docs/public/openapi.json b/docs/public/openapi.json
index 2e6a964..4aa9322 100644
--- a/docs/public/openapi.json
+++ b/docs/public/openapi.json
@@ -26,51 +26,39 @@
"tags": [
{
"name": "Titles",
- "description": "Movie and TV show management"
+ "description": "Movie and TV show metadata"
},
{
- "name": "Episodes",
- "description": "Episode watch tracking"
+ "name": "Tracking",
+ "description": "Watch tracking, ratings, and status management"
},
{
- "name": "Seasons",
- "description": "Season watch tracking"
+ "name": "Library",
+ "description": "User library browsing and feeds"
+ },
+ {
+ "name": "Discover",
+ "description": "Search, trending, and content discovery"
},
{
"name": "People",
"description": "Cast and crew information"
},
{
- "name": "Dashboard",
- "description": "User dashboard data"
- },
- {
- "name": "Explore",
- "description": "Discover trending and popular content"
- },
- {
- "name": "Search",
- "description": "Search for movies and TV shows"
- },
- {
- "name": "Discover",
- "description": "Advanced content discovery with filters"
+ "name": "Account",
+ "description": "User account and integrations"
},
{
"name": "System",
"description": "Server status and configuration"
},
- {
- "name": "Integrations",
- "description": "Media server integrations"
- },
{
"name": "Admin",
"description": "Server administration"
},
{
- "name": "Account",
- "description": "User account management"
+ "name": "Imports",
+ "description": "Data import from external services"
}
],
"components": {
@@ -1569,6 +1557,670 @@
},
"openapi": "3.1.1",
"paths": {
+ "/titles/{id}": {
+ "get": {
+ "operationId": "titles.get",
+ "summary": "Get title details",
+ "description": "Fetch full title metadata including seasons, cast, and streaming availability. Imports from TMDB on first access if not yet cached locally.",
+ "tags": [
+ "Titles"
+ ],
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string",
+ "minLength": 1,
+ "description": "Internal UUIDv7 identifier"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Full title with seasons, cast, and availability offers",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "properties": {
+ "title": {
+ "$ref": "#/components/schemas/Title"
+ },
+ "seasons": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/Season"
+ },
+ "description": "TV seasons (empty for movies)"
+ },
+ "availability": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "platformId": {
+ "type": "string",
+ "description": "Platform ID"
+ },
+ "providerName": {
+ "type": "string",
+ "description": "Display name (e.g. Netflix, Hulu)"
+ },
+ "logoPath": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "description": "Provider logo image path"
+ },
+ "offerType": {
+ "type": "string",
+ "description": "Offer category: stream or purchase"
+ },
+ "watchUrl": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "description": "Direct link to watch on this provider"
+ },
+ "isUserSubscribed": {
+ "type": "boolean",
+ "description": "Whether the user subscribes to this platform"
+ }
+ },
+ "required": [
+ "platformId",
+ "providerName",
+ "logoPath",
+ "offerType",
+ "watchUrl",
+ "isUserSubscribed"
+ ],
+ "description": "A streaming availability offer from a provider"
+ },
+ "description": "Streaming availability offers"
+ },
+ "cast": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/CastMember"
+ },
+ "description": "Cast and crew credits"
+ }
+ },
+ "required": [
+ "title",
+ "seasons",
+ "availability",
+ "cast"
+ ],
+ "description": "Full title details with seasons, cast, and streaming availability"
+ }
+ }
+ }
+ },
+ "404": {
+ "description": "404",
+ "content": {
+ "application/json": {
+ "schema": {
+ "oneOf": [
+ {
+ "type": "object",
+ "properties": {
+ "defined": {
+ "const": true
+ },
+ "code": {
+ "const": "NOT_FOUND"
+ },
+ "status": {
+ "const": 404
+ },
+ "message": {
+ "type": "string",
+ "default": "Title not found"
+ },
+ "data": {
+ "type": "object",
+ "properties": {
+ "code": {
+ "const": "TITLE_NOT_FOUND"
+ }
+ },
+ "required": [
+ "code"
+ ]
+ }
+ },
+ "required": [
+ "defined",
+ "code",
+ "status",
+ "message",
+ "data"
+ ]
+ },
+ {
+ "type": "object",
+ "properties": {
+ "defined": {
+ "const": false
+ },
+ "code": {
+ "type": "string"
+ },
+ "status": {
+ "type": "number"
+ },
+ "message": {
+ "type": "string"
+ },
+ "data": {}
+ },
+ "required": [
+ "defined",
+ "code",
+ "status",
+ "message"
+ ]
+ }
+ ]
+ }
+ }
+ }
+ }
+ },
+ "security": [
+ {
+ "session": []
+ }
+ ]
+ }
+ },
+ "/titles/{id}/similar": {
+ "get": {
+ "operationId": "titles.similar",
+ "summary": "Get similar titles",
+ "description": "Fetch similar titles based on locally cached recommendation data, along with the user's statuses for each.",
+ "tags": [
+ "Titles"
+ ],
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string",
+ "minLength": 1,
+ "description": "Internal UUIDv7 identifier"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Similar titles with user statuses",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "properties": {
+ "recommendations": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/Recommendation"
+ }
+ },
+ "userStatuses": {
+ "type": "object",
+ "propertyNames": {
+ "type": "string"
+ },
+ "additionalProperties": {
+ "enum": [
+ "in_watchlist",
+ "watching",
+ "caught_up",
+ "completed"
+ ],
+ "type": "string"
+ },
+ "description": "Map of title ID to the user's display status"
+ }
+ },
+ "required": [
+ "recommendations",
+ "userStatuses"
+ ],
+ "description": "Recommended titles with the user's statuses"
+ }
+ }
+ }
+ }
+ },
+ "security": [
+ {
+ "session": []
+ }
+ ]
+ }
+ },
+ "/tracking/watch": {
+ "post": {
+ "operationId": "tracking.watch",
+ "summary": "Mark items as watched",
+ "description": "Mark one or more items as watched. Use scope to indicate what the IDs refer to: movie (title IDs), episode (episode IDs), season (season IDs), or series (title IDs to mark all episodes).",
+ "tags": [
+ "Tracking"
+ ],
+ "requestBody": {
+ "required": true,
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "properties": {
+ "scope": {
+ "enum": [
+ "movie",
+ "episode",
+ "season",
+ "series"
+ ],
+ "type": "string",
+ "description": "What the IDs refer to: movie title, episode, season, or entire TV series"
+ },
+ "ids": {
+ "type": "array",
+ "minItems": 1,
+ "items": {
+ "type": "string",
+ "minLength": 1
+ },
+ "description": "IDs to mark as watched"
+ }
+ },
+ "required": [
+ "scope",
+ "ids"
+ ],
+ "description": "Mark one or more items as watched"
+ }
+ }
+ }
+ },
+ "responses": {
+ "200": {
+ "description": "OK"
+ }
+ },
+ "security": [
+ {
+ "session": []
+ }
+ ]
+ }
+ },
+ "/tracking/unwatch": {
+ "post": {
+ "operationId": "tracking.unwatch",
+ "summary": "Remove watch records",
+ "description": "Remove watch records for one or more items. Use scope to indicate what the IDs refer to: movie, episode, season, or series.",
+ "tags": [
+ "Tracking"
+ ],
+ "requestBody": {
+ "required": true,
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "properties": {
+ "scope": {
+ "enum": [
+ "movie",
+ "episode",
+ "season",
+ "series"
+ ],
+ "type": "string",
+ "description": "What the IDs refer to: movie title, episode, season, or entire TV series"
+ },
+ "ids": {
+ "type": "array",
+ "minItems": 1,
+ "items": {
+ "type": "string",
+ "minLength": 1
+ },
+ "description": "IDs to unwatch"
+ }
+ },
+ "required": [
+ "scope",
+ "ids"
+ ],
+ "description": "Remove watch records for one or more items"
+ }
+ }
+ }
+ },
+ "responses": {
+ "200": {
+ "description": "OK"
+ }
+ },
+ "security": [
+ {
+ "session": []
+ }
+ ]
+ }
+ },
+ "/tracking/titles/{id}/status": {
+ "put": {
+ "operationId": "tracking.updateStatus",
+ "summary": "Update tracking status",
+ "description": "Set the user's tracking status for a title. Use null to remove the title from the library entirely.",
+ "tags": [
+ "Tracking"
+ ],
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string",
+ "minLength": 1,
+ "description": "Title ID"
+ }
+ }
+ ],
+ "requestBody": {
+ "required": true,
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "properties": {
+ "status": {
+ "anyOf": [
+ {
+ "enum": [
+ "watchlist"
+ ],
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "description": "Set to watchlist to add to library, or null to remove. Other transitions happen via watch endpoints."
+ }
+ },
+ "required": [
+ "status"
+ ],
+ "description": "Add a title to the user's watchlist or remove from library"
+ }
+ }
+ }
+ },
+ "responses": {
+ "200": {
+ "description": "OK"
+ }
+ },
+ "security": [
+ {
+ "session": []
+ }
+ ]
+ }
+ },
+ "/tracking/titles/{id}/rating": {
+ "put": {
+ "operationId": "tracking.rate",
+ "summary": "Rate a title",
+ "description": "Set a 0-5 star rating for a title. Use 0 to clear the rating.",
+ "tags": [
+ "Tracking"
+ ],
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string",
+ "minLength": 1,
+ "description": "Title ID"
+ }
+ }
+ ],
+ "requestBody": {
+ "required": true,
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "properties": {
+ "stars": {
+ "type": "integer",
+ "minimum": 0,
+ "maximum": 5,
+ "description": "Star rating from 0 (clear) to 5"
+ }
+ },
+ "required": [
+ "stars"
+ ],
+ "description": "Set or clear a star rating for a title"
+ }
+ }
+ }
+ },
+ "responses": {
+ "200": {
+ "description": "OK"
+ }
+ },
+ "security": [
+ {
+ "session": []
+ }
+ ]
+ }
+ },
+ "/tracking/titles/{id}": {
+ "get": {
+ "operationId": "tracking.userInfo",
+ "summary": "Get user's tracking info for a title",
+ "description": "Fetch the current user's tracking status, rating, and watched episode IDs for a title.",
+ "tags": [
+ "Tracking"
+ ],
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string",
+ "minLength": 1,
+ "description": "Internal UUIDv7 identifier"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "User's status, rating, and list of watched episode IDs",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "properties": {
+ "status": {
+ "anyOf": [
+ {
+ "enum": [
+ "in_watchlist",
+ "watching",
+ "caught_up",
+ "completed"
+ ],
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "description": "User's display status, or null if not in library"
+ },
+ "rating": {
+ "anyOf": [
+ {
+ "type": "number"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "description": "User's star rating (0-5), or null if unrated"
+ },
+ "episodeWatches": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ },
+ "description": "IDs of episodes the user has watched"
+ }
+ },
+ "required": [
+ "status",
+ "rating",
+ "episodeWatches"
+ ],
+ "description": "The current user's tracking info for a title"
+ }
+ }
+ }
+ }
+ },
+ "security": [
+ {
+ "session": []
+ }
+ ]
+ }
+ },
+ "/tracking/stats": {
+ "get": {
+ "operationId": "tracking.stats",
+ "summary": "Get watch statistics",
+ "description": "Fetch the user's watch counts grouped by time period. Useful for rendering activity charts and dashboard counters.",
+ "tags": [
+ "Tracking"
+ ],
+ "parameters": [
+ {
+ "name": "type",
+ "in": "query",
+ "required": true,
+ "schema": {
+ "enum": [
+ "movie",
+ "episode"
+ ],
+ "type": "string",
+ "description": "What to count: movie watches or episode watches"
+ },
+ "allowEmptyValue": true,
+ "allowReserved": true
+ },
+ {
+ "name": "period",
+ "in": "query",
+ "required": true,
+ "schema": {
+ "enum": [
+ "today",
+ "this_week",
+ "this_month",
+ "this_year"
+ ],
+ "type": "string",
+ "description": "Time range for the histogram"
+ },
+ "allowEmptyValue": true,
+ "allowReserved": true
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Watch counts bucketed by time period",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "properties": {
+ "count": {
+ "type": "number",
+ "description": "Total watches in the selected period"
+ },
+ "history": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "bucket": {
+ "type": "string",
+ "description": "Time period label (e.g. date or week)"
+ },
+ "count": {
+ "type": "number",
+ "description": "Number of watches in this bucket"
+ }
+ },
+ "required": [
+ "bucket",
+ "count"
+ ],
+ "description": "A single time-bucketed watch count"
+ },
+ "description": "Watch counts bucketed by time period"
+ }
+ },
+ "required": [
+ "count",
+ "history"
+ ],
+ "description": "Watch history with time-bucketed counts"
+ }
+ }
+ }
+ }
+ },
+ "security": [
+ {
+ "session": []
+ }
+ ]
+ }
+ },
"/library": {
"get": {
"operationId": "library.list",
@@ -1982,1107 +2634,23 @@
]
}
},
- "/titles/{id}": {
+ "/library/stats": {
"get": {
- "operationId": "titles.detail",
- "summary": "Get title details",
- "description": "Fetch full title metadata including seasons, cast, and streaming availability. Imports from TMDB on first access if not yet cached locally.",
+ "operationId": "library.stats",
+ "summary": "Get library statistics",
+ "description": "Fetch aggregate library counts: total titles and completed titles.",
"tags": [
- "Titles"
- ],
- "parameters": [
- {
- "name": "id",
- "in": "path",
- "required": true,
- "schema": {
- "type": "string",
- "minLength": 1,
- "description": "Internal UUIDv7 identifier"
- }
- }
+ "Library"
],
"responses": {
"200": {
- "description": "Full title with seasons, cast, and availability offers",
+ "description": "Library size and completed count",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
- "title": {
- "$ref": "#/components/schemas/Title"
- },
- "seasons": {
- "type": "array",
- "items": {
- "$ref": "#/components/schemas/Season"
- },
- "description": "TV seasons (empty for movies)"
- },
- "availability": {
- "type": "array",
- "items": {
- "type": "object",
- "properties": {
- "platformId": {
- "type": "string",
- "description": "Platform ID"
- },
- "providerName": {
- "type": "string",
- "description": "Display name (e.g. Netflix, Hulu)"
- },
- "logoPath": {
- "anyOf": [
- {
- "type": "string"
- },
- {
- "type": "null"
- }
- ],
- "description": "Provider logo image path"
- },
- "offerType": {
- "type": "string",
- "description": "Offer category: stream or purchase"
- },
- "watchUrl": {
- "anyOf": [
- {
- "type": "string"
- },
- {
- "type": "null"
- }
- ],
- "description": "Direct link to watch on this provider"
- },
- "isUserSubscribed": {
- "type": "boolean",
- "description": "Whether the user subscribes to this platform"
- }
- },
- "required": [
- "platformId",
- "providerName",
- "logoPath",
- "offerType",
- "watchUrl",
- "isUserSubscribed"
- ],
- "description": "A streaming availability offer from a provider"
- },
- "description": "Streaming availability offers"
- },
- "cast": {
- "type": "array",
- "items": {
- "$ref": "#/components/schemas/CastMember"
- },
- "description": "Cast and crew credits"
- }
- },
- "required": [
- "title",
- "seasons",
- "availability",
- "cast"
- ],
- "description": "Full title details with seasons, cast, and streaming availability"
- }
- }
- }
- },
- "404": {
- "description": "404",
- "content": {
- "application/json": {
- "schema": {
- "oneOf": [
- {
- "type": "object",
- "properties": {
- "defined": {
- "const": true
- },
- "code": {
- "const": "NOT_FOUND"
- },
- "status": {
- "const": 404
- },
- "message": {
- "type": "string",
- "default": "Title not found"
- },
- "data": {
- "type": "object",
- "properties": {
- "code": {
- "const": "TITLE_NOT_FOUND"
- }
- },
- "required": [
- "code"
- ]
- }
- },
- "required": [
- "defined",
- "code",
- "status",
- "message",
- "data"
- ]
- },
- {
- "type": "object",
- "properties": {
- "defined": {
- "const": false
- },
- "code": {
- "type": "string"
- },
- "status": {
- "type": "number"
- },
- "message": {
- "type": "string"
- },
- "data": {}
- },
- "required": [
- "defined",
- "code",
- "status",
- "message"
- ]
- }
- ]
- }
- }
- }
- }
- },
- "security": [
- {
- "session": []
- }
- ]
- }
- },
- "/titles/{id}/status": {
- "put": {
- "operationId": "titles.updateStatus",
- "summary": "Update tracking status",
- "description": "Set the user's tracking status for a title. Use null to remove the title from the library entirely.",
- "tags": [
- "Titles"
- ],
- "parameters": [
- {
- "name": "id",
- "in": "path",
- "required": true,
- "schema": {
- "type": "string",
- "minLength": 1,
- "description": "Title ID"
- }
- }
- ],
- "requestBody": {
- "required": true,
- "content": {
- "application/json": {
- "schema": {
- "type": "object",
- "properties": {
- "status": {
- "anyOf": [
- {
- "enum": [
- "watchlist"
- ],
- "type": "string"
- },
- {
- "type": "null"
- }
- ],
- "description": "Set to watchlist to add to library, or null to remove. Other transitions happen via watch endpoints."
- }
- },
- "required": [
- "status"
- ],
- "description": "Add a title to the user's watchlist or remove from library"
- }
- }
- }
- },
- "responses": {
- "200": {
- "description": "OK"
- }
- },
- "security": [
- {
- "session": []
- }
- ]
- }
- },
- "/titles/{id}/rating": {
- "put": {
- "operationId": "titles.updateRating",
- "summary": "Rate a title",
- "description": "Set a 0-5 star rating for a title. Use 0 to clear the rating.",
- "tags": [
- "Titles"
- ],
- "parameters": [
- {
- "name": "id",
- "in": "path",
- "required": true,
- "schema": {
- "type": "string",
- "minLength": 1,
- "description": "Title ID"
- }
- }
- ],
- "requestBody": {
- "required": true,
- "content": {
- "application/json": {
- "schema": {
- "type": "object",
- "properties": {
- "stars": {
- "type": "integer",
- "minimum": 0,
- "maximum": 5,
- "description": "Star rating from 0 (clear) to 5"
- }
- },
- "required": [
- "stars"
- ],
- "description": "Set or clear a star rating for a title"
- }
- }
- }
- },
- "responses": {
- "200": {
- "description": "OK"
- }
- },
- "security": [
- {
- "session": []
- }
- ]
- }
- },
- "/titles/{id}/watch": {
- "post": {
- "operationId": "titles.watchMovie",
- "summary": "Mark movie as watched",
- "description": "Log a watch event for a movie. Automatically sets status to completed.",
- "tags": [
- "Titles"
- ],
- "parameters": [
- {
- "name": "id",
- "in": "path",
- "required": true,
- "schema": {
- "type": "string",
- "minLength": 1,
- "description": "Internal UUIDv7 identifier"
- }
- }
- ],
- "requestBody": {
- "required": false,
- "content": {
- "application/json": {
- "schema": {
- "type": "object"
- }
- }
- }
- },
- "responses": {
- "200": {
- "description": "OK"
- }
- },
- "security": [
- {
- "session": []
- }
- ]
- }
- },
- "/titles/{id}/watch-all": {
- "post": {
- "operationId": "titles.watchAll",
- "summary": "Mark all episodes watched",
- "description": "Mark every episode of a TV show as watched. Requires seasons to be hydrated first.",
- "tags": [
- "Titles"
- ],
- "parameters": [
- {
- "name": "id",
- "in": "path",
- "required": true,
- "schema": {
- "type": "string",
- "minLength": 1,
- "description": "Internal UUIDv7 identifier"
- }
- }
- ],
- "requestBody": {
- "required": false,
- "content": {
- "application/json": {
- "schema": {
- "type": "object"
- }
- }
- }
- },
- "responses": {
- "200": {
- "description": "OK"
- }
- },
- "security": [
- {
- "session": []
- }
- ]
- }
- },
- "/titles/{id}/user-info": {
- "get": {
- "operationId": "titles.userInfo",
- "summary": "Get user's title info",
- "description": "Fetch the current user's tracking status, rating, and watched episode IDs for a title.",
- "tags": [
- "Titles"
- ],
- "parameters": [
- {
- "name": "id",
- "in": "path",
- "required": true,
- "schema": {
- "type": "string",
- "minLength": 1,
- "description": "Internal UUIDv7 identifier"
- }
- }
- ],
- "responses": {
- "200": {
- "description": "User's status, rating, and list of watched episode IDs",
- "content": {
- "application/json": {
- "schema": {
- "type": "object",
- "properties": {
- "status": {
- "anyOf": [
- {
- "enum": [
- "in_watchlist",
- "watching",
- "caught_up",
- "completed"
- ],
- "type": "string"
- },
- {
- "type": "null"
- }
- ],
- "description": "User's display status, or null if not in library"
- },
- "rating": {
- "anyOf": [
- {
- "type": "number"
- },
- {
- "type": "null"
- }
- ],
- "description": "User's star rating (0-5), or null if unrated"
- },
- "episodeWatches": {
- "type": "array",
- "items": {
- "type": "string"
- },
- "description": "IDs of episodes the user has watched"
- }
- },
- "required": [
- "status",
- "rating",
- "episodeWatches"
- ],
- "description": "The current user's tracking info for a title"
- }
- }
- }
- }
- },
- "security": [
- {
- "session": []
- }
- ]
- }
- },
- "/titles/{id}/recommendations": {
- "get": {
- "operationId": "titles.recommendations",
- "summary": "Get title recommendations",
- "description": "Fetch similar titles based on locally cached recommendation data, along with the user's statuses for each.",
- "tags": [
- "Titles"
- ],
- "parameters": [
- {
- "name": "id",
- "in": "path",
- "required": true,
- "schema": {
- "type": "string",
- "minLength": 1,
- "description": "Internal UUIDv7 identifier"
- }
- }
- ],
- "responses": {
- "200": {
- "description": "Recommended titles with user statuses",
- "content": {
- "application/json": {
- "schema": {
- "type": "object",
- "properties": {
- "recommendations": {
- "type": "array",
- "items": {
- "$ref": "#/components/schemas/Recommendation"
- }
- },
- "userStatuses": {
- "type": "object",
- "propertyNames": {
- "type": "string"
- },
- "additionalProperties": {
- "enum": [
- "in_watchlist",
- "watching",
- "caught_up",
- "completed"
- ],
- "type": "string"
- },
- "description": "Map of title ID to the user's display status"
- }
- },
- "required": [
- "recommendations",
- "userStatuses"
- ],
- "description": "Recommended titles with the user's statuses"
- }
- }
- }
- }
- },
- "security": [
- {
- "session": []
- }
- ]
- }
- },
- "/titles/{id}/quick-add": {
- "post": {
- "operationId": "titles.quickAdd",
- "summary": "Quick add title to library",
- "description": "Add a title to the user's watchlist and trigger a full TMDB import if needed. If the title already exists in the user's library, returns alreadyAdded: true.",
- "tags": [
- "Titles"
- ],
- "parameters": [
- {
- "name": "id",
- "in": "path",
- "required": true,
- "schema": {
- "type": "string",
- "minLength": 1,
- "description": "Internal UUIDv7 identifier"
- }
- }
- ],
- "requestBody": {
- "required": false,
- "content": {
- "application/json": {
- "schema": {
- "type": "object"
- }
- }
- }
- },
- "responses": {
- "200": {
- "description": "Title ID and whether it was already in the library",
- "content": {
- "application/json": {
- "schema": {
- "type": "object",
- "properties": {
- "id": {
- "type": "string",
- "description": "Internal title ID"
- },
- "alreadyAdded": {
- "type": "boolean",
- "description": "True if the title was already in the user's library"
- }
- },
- "required": [
- "id",
- "alreadyAdded"
- ],
- "description": "Result of a quick-add operation"
- }
- }
- }
- },
- "404": {
- "description": "404",
- "content": {
- "application/json": {
- "schema": {
- "oneOf": [
- {
- "type": "object",
- "properties": {
- "defined": {
- "const": true
- },
- "code": {
- "const": "NOT_FOUND"
- },
- "status": {
- "const": 404
- },
- "message": {
- "type": "string",
- "default": "Title not found"
- },
- "data": {
- "type": "object",
- "properties": {
- "code": {
- "const": "TITLE_NOT_FOUND"
- }
- },
- "required": [
- "code"
- ]
- }
- },
- "required": [
- "defined",
- "code",
- "status",
- "message",
- "data"
- ]
- },
- {
- "type": "object",
- "properties": {
- "defined": {
- "const": false
- },
- "code": {
- "type": "string"
- },
- "status": {
- "type": "number"
- },
- "message": {
- "type": "string"
- },
- "data": {}
- },
- "required": [
- "defined",
- "code",
- "status",
- "message"
- ]
- }
- ]
- }
- }
- }
- }
- },
- "security": [
- {
- "session": []
- }
- ]
- }
- },
- "/episodes/{id}/watch": {
- "post": {
- "operationId": "episodes.watch",
- "summary": "Mark episode watched",
- "description": "Record a watch event for a single TV episode.",
- "tags": [
- "Episodes"
- ],
- "parameters": [
- {
- "name": "id",
- "in": "path",
- "required": true,
- "schema": {
- "type": "string",
- "minLength": 1,
- "description": "Internal UUIDv7 identifier"
- }
- }
- ],
- "requestBody": {
- "required": false,
- "content": {
- "application/json": {
- "schema": {
- "type": "object"
- }
- }
- }
- },
- "responses": {
- "200": {
- "description": "OK"
- }
- },
- "security": [
- {
- "session": []
- }
- ]
- }
- },
- "/episodes/{id}/unwatch": {
- "post": {
- "operationId": "episodes.unwatch",
- "summary": "Mark episode unwatched",
- "description": "Remove the watch record for a single TV episode.",
- "tags": [
- "Episodes"
- ],
- "parameters": [
- {
- "name": "id",
- "in": "path",
- "required": true,
- "schema": {
- "type": "string",
- "minLength": 1,
- "description": "Internal UUIDv7 identifier"
- }
- }
- ],
- "requestBody": {
- "required": false,
- "content": {
- "application/json": {
- "schema": {
- "type": "object"
- }
- }
- }
- },
- "responses": {
- "200": {
- "description": "OK"
- }
- },
- "security": [
- {
- "session": []
- }
- ]
- }
- },
- "/episodes/batch-watch": {
- "post": {
- "operationId": "episodes.batchWatch",
- "summary": "Batch mark episodes watched",
- "description": "Record watch events for multiple episodes at once. Useful for marking a range of episodes as watched.",
- "tags": [
- "Episodes"
- ],
- "requestBody": {
- "required": true,
- "content": {
- "application/json": {
- "schema": {
- "type": "object",
- "properties": {
- "episodeIds": {
- "type": "array",
- "minItems": 1,
- "items": {
- "type": "string"
- },
- "description": "List of episode IDs to mark as watched"
- }
- },
- "required": [
- "episodeIds"
- ],
- "description": "Batch of episode IDs to mark as watched"
- }
- }
- }
- },
- "responses": {
- "200": {
- "description": "OK"
- }
- },
- "security": [
- {
- "session": []
- }
- ]
- }
- },
- "/seasons/{id}/watch": {
- "post": {
- "operationId": "seasons.watch",
- "summary": "Mark season watched",
- "description": "Mark all episodes in a season as watched in a single operation.",
- "tags": [
- "Seasons"
- ],
- "parameters": [
- {
- "name": "id",
- "in": "path",
- "required": true,
- "schema": {
- "type": "string",
- "minLength": 1,
- "description": "Internal UUIDv7 identifier"
- }
- }
- ],
- "requestBody": {
- "required": false,
- "content": {
- "application/json": {
- "schema": {
- "type": "object"
- }
- }
- }
- },
- "responses": {
- "200": {
- "description": "OK"
- }
- },
- "security": [
- {
- "session": []
- }
- ]
- }
- },
- "/seasons/{id}/unwatch": {
- "post": {
- "operationId": "seasons.unwatch",
- "summary": "Mark season unwatched",
- "description": "Remove all watch records for episodes in a season in a single operation.",
- "tags": [
- "Seasons"
- ],
- "parameters": [
- {
- "name": "id",
- "in": "path",
- "required": true,
- "schema": {
- "type": "string",
- "minLength": 1,
- "description": "Internal UUIDv7 identifier"
- }
- }
- ],
- "requestBody": {
- "required": false,
- "content": {
- "application/json": {
- "schema": {
- "type": "object"
- }
- }
- }
- },
- "responses": {
- "200": {
- "description": "OK"
- }
- },
- "security": [
- {
- "session": []
- }
- ]
- }
- },
- "/people/{id}": {
- "get": {
- "operationId": "people.detail",
- "summary": "Get person details",
- "description": "Fetch a person's profile and paginated filmography. Imports from TMDB on first access if not yet cached locally.",
- "tags": [
- "People"
- ],
- "parameters": [
- {
- "name": "id",
- "in": "path",
- "required": true,
- "schema": {
- "type": "string",
- "minLength": 1,
- "description": "Internal UUIDv7 identifier"
- }
- },
- {
- "name": "page",
- "in": "query",
- "required": false,
- "schema": {
- "type": "integer",
- "minimum": 1,
- "maximum": 500,
- "default": 1,
- "description": "Page number (1-indexed)"
- },
- "allowEmptyValue": true,
- "allowReserved": true
- },
- {
- "name": "limit",
- "in": "query",
- "required": false,
- "schema": {
- "type": "integer",
- "minimum": 1,
- "maximum": 100,
- "default": 20,
- "description": "Results per page (1-100, default 20)"
- },
- "allowEmptyValue": true,
- "allowReserved": true
- }
- ],
- "responses": {
- "200": {
- "description": "Person profile, paginated filmography credits, and user statuses for their titles",
- "content": {
- "application/json": {
- "schema": {
- "type": "object",
- "properties": {
- "person": {
- "$ref": "#/components/schemas/Person"
- },
- "filmography": {
- "type": "array",
- "items": {
- "$ref": "#/components/schemas/PersonCredit"
- },
- "description": "Credits for this person (paginated)"
- },
- "userStatuses": {
- "type": "object",
- "propertyNames": {
- "type": "string"
- },
- "additionalProperties": {
- "enum": [
- "in_watchlist",
- "watching",
- "caught_up",
- "completed"
- ],
- "type": "string"
- },
- "description": "Map of title ID to the user's display status"
- },
- "page": {
- "type": "number",
- "description": "Current page number"
- },
- "totalPages": {
- "type": "number",
- "description": "Total number of pages available"
- },
- "totalResults": {
- "type": "number",
- "description": "Total number of results across all pages"
- }
- },
- "required": [
- "person",
- "filmography",
- "userStatuses",
- "page",
- "totalPages",
- "totalResults"
- ],
- "description": "Person profile with paginated filmography and user's statuses for their titles"
- }
- }
- }
- },
- "404": {
- "description": "404",
- "content": {
- "application/json": {
- "schema": {
- "oneOf": [
- {
- "type": "object",
- "properties": {
- "defined": {
- "const": true
- },
- "code": {
- "const": "NOT_FOUND"
- },
- "status": {
- "const": 404
- },
- "message": {
- "type": "string",
- "default": "Person not found"
- },
- "data": {
- "type": "object",
- "properties": {
- "code": {
- "const": "PERSON_NOT_FOUND"
- }
- },
- "required": [
- "code"
- ]
- }
- },
- "required": [
- "defined",
- "code",
- "status",
- "message",
- "data"
- ]
- },
- {
- "type": "object",
- "properties": {
- "defined": {
- "const": false
- },
- "code": {
- "type": "string"
- },
- "status": {
- "type": "number"
- },
- "message": {
- "type": "string"
- },
- "data": {}
- },
- "required": [
- "defined",
- "code",
- "status",
- "message"
- ]
- }
- ]
- }
- }
- }
- }
- },
- "security": [
- {
- "session": []
- }
- ]
- }
- },
- "/dashboard/stats": {
- "get": {
- "operationId": "dashboard.stats",
- "summary": "Get dashboard statistics",
- "description": "Fetch summary counts for the current user: movies watched this month, episodes this week, library size, and completed titles.",
- "tags": [
- "Dashboard"
- ],
- "responses": {
- "200": {
- "description": "Aggregate watch statistics",
- "content": {
- "application/json": {
- "schema": {
- "type": "object",
- "properties": {
- "moviesThisMonth": {
- "type": "number",
- "description": "Movies watched in the current calendar month"
- },
- "episodesThisWeek": {
- "type": "number",
- "description": "Episodes watched in the current calendar week"
- },
- "librarySize": {
+ "size": {
"type": "number",
"description": "Total titles in the user's library"
},
@@ -3092,12 +2660,10 @@
}
},
"required": [
- "moviesThisMonth",
- "episodesThisWeek",
- "librarySize",
+ "size",
"completed"
],
- "description": "Aggregate watch statistics for the dashboard"
+ "description": "Aggregate library statistics"
}
}
}
@@ -3110,13 +2676,13 @@
]
}
},
- "/dashboard/continue-watching": {
+ "/library/continue-watching": {
"get": {
- "operationId": "dashboard.continueWatching",
+ "operationId": "library.continueWatching",
"summary": "Get continue watching list",
"description": "Fetch TV shows the user is currently watching, with the next unwatched episode and progress for each.",
"tags": [
- "Dashboard"
+ "Library"
],
"responses": {
"200": {
@@ -3268,13 +2834,13 @@
]
}
},
- "/dashboard/upcoming": {
+ "/library/upcoming": {
"get": {
- "operationId": "dashboard.upcoming",
+ "operationId": "library.upcoming",
"summary": "Get upcoming episodes and movies",
"description": "Fetch upcoming episodes and movie releases for titles in the user's library, sorted by date. Supports cursor-based pagination.",
"tags": [
- "Dashboard"
+ "Library"
],
"parameters": [
{
@@ -3571,146 +3137,13 @@
]
}
},
- "/dashboard/recommendations": {
+ "/discover/trending": {
"get": {
- "operationId": "dashboard.recommendations",
- "summary": "Get personalized recommendations",
- "description": "Fetch personalized title recommendations based on the user's library and watch history.",
- "tags": [
- "Dashboard"
- ],
- "responses": {
- "200": {
- "description": "Recommended titles",
- "content": {
- "application/json": {
- "schema": {
- "type": "object",
- "properties": {
- "items": {
- "type": "array",
- "items": {
- "$ref": "#/components/schemas/Recommendation"
- }
- }
- },
- "required": [
- "items"
- ],
- "description": "Personalized title recommendations for the dashboard"
- }
- }
- }
- }
- },
- "security": [
- {
- "session": []
- }
- ]
- }
- },
- "/dashboard/watch-history": {
- "get": {
- "operationId": "dashboard.watchHistory",
- "summary": "Get watch history",
- "description": "Fetch the user's watch counts grouped by time period. Useful for rendering activity charts.",
- "tags": [
- "Dashboard"
- ],
- "parameters": [
- {
- "name": "type",
- "in": "query",
- "required": true,
- "schema": {
- "enum": [
- "movie",
- "episode"
- ],
- "type": "string",
- "description": "What to count: movie watches or episode watches"
- },
- "allowEmptyValue": true,
- "allowReserved": true
- },
- {
- "name": "period",
- "in": "query",
- "required": true,
- "schema": {
- "enum": [
- "today",
- "this_week",
- "this_month",
- "this_year"
- ],
- "type": "string",
- "description": "Time range for the histogram"
- },
- "allowEmptyValue": true,
- "allowReserved": true
- }
- ],
- "responses": {
- "200": {
- "description": "Watch counts bucketed by time period",
- "content": {
- "application/json": {
- "schema": {
- "type": "object",
- "properties": {
- "count": {
- "type": "number",
- "description": "Total watches in the selected period"
- },
- "history": {
- "type": "array",
- "items": {
- "type": "object",
- "properties": {
- "bucket": {
- "type": "string",
- "description": "Time period label (e.g. date or week)"
- },
- "count": {
- "type": "number",
- "description": "Number of watches in this bucket"
- }
- },
- "required": [
- "bucket",
- "count"
- ],
- "description": "A single time-bucketed watch count"
- },
- "description": "Watch counts bucketed by time period"
- }
- },
- "required": [
- "count",
- "history"
- ],
- "description": "Watch history with time-bucketed counts"
- }
- }
- }
- }
- },
- "security": [
- {
- "session": []
- }
- ]
- }
- },
- "/explore/trending": {
- "get": {
- "operationId": "explore.trending",
+ "operationId": "discover.trending",
"summary": "Get trending titles",
"description": "Fetch today's trending movies and/or TV shows from TMDB, including a featured hero title and the user's statuses.",
"tags": [
- "Explore"
+ "Discover"
],
"parameters": [
{
@@ -3964,13 +3397,13 @@
]
}
},
- "/explore/popular": {
+ "/discover/popular": {
"get": {
- "operationId": "explore.popular",
+ "operationId": "discover.popular",
"summary": "Get popular titles",
"description": "Fetch currently popular movies or TV shows from TMDB with the user's tracking statuses.",
"tags": [
- "Explore"
+ "Discover"
],
"parameters": [
{
@@ -4160,315 +3593,13 @@
]
}
},
- "/explore/genres": {
+ "/discover/search": {
"get": {
- "operationId": "explore.genres",
- "summary": "List genres",
- "description": "Fetch the list of TMDB genres for movies or TV shows. Used to populate genre filters for discovery.",
- "tags": [
- "Explore"
- ],
- "parameters": [
- {
- "name": "type",
- "in": "query",
- "required": true,
- "schema": {
- "enum": [
- "movie",
- "tv"
- ],
- "type": "string",
- "description": "Media type filter"
- },
- "allowEmptyValue": true,
- "allowReserved": true
- }
- ],
- "responses": {
- "200": {
- "description": "Genre ID/name pairs",
- "content": {
- "application/json": {
- "schema": {
- "type": "object",
- "properties": {
- "genres": {
- "type": "array",
- "items": {
- "type": "object",
- "properties": {
- "id": {
- "type": "number",
- "description": "TMDB genre ID"
- },
- "name": {
- "type": "string",
- "description": "Genre display name"
- }
- },
- "required": [
- "id",
- "name"
- ]
- }
- }
- },
- "required": [
- "genres"
- ],
- "description": "Available genres for filtering"
- }
- }
- }
- },
- "412": {
- "description": "412",
- "content": {
- "application/json": {
- "schema": {
- "oneOf": [
- {
- "type": "object",
- "properties": {
- "defined": {
- "const": true
- },
- "code": {
- "const": "PRECONDITION_FAILED"
- },
- "status": {
- "const": 412
- },
- "message": {
- "type": "string",
- "default": "TMDB API key is not configured"
- },
- "data": {
- "type": "object",
- "properties": {
- "code": {
- "const": "TMDB_NOT_CONFIGURED"
- }
- },
- "required": [
- "code"
- ]
- }
- },
- "required": [
- "defined",
- "code",
- "status",
- "message",
- "data"
- ]
- },
- {
- "type": "object",
- "properties": {
- "defined": {
- "const": false
- },
- "code": {
- "type": "string"
- },
- "status": {
- "type": "number"
- },
- "message": {
- "type": "string"
- },
- "data": {}
- },
- "required": [
- "defined",
- "code",
- "status",
- "message"
- ]
- }
- ]
- }
- }
- }
- }
- },
- "security": [
- {
- "session": []
- }
- ]
- }
- },
- "/explore/watch-providers": {
- "get": {
- "operationId": "explore.watchProviders",
- "summary": "List available watch providers",
- "description": "Fetch the list of streaming providers available in the configured region. Used to populate provider filter dropdowns.",
- "tags": [
- "Explore"
- ],
- "parameters": [
- {
- "name": "type",
- "in": "query",
- "required": true,
- "schema": {
- "enum": [
- "movie",
- "tv"
- ],
- "type": "string",
- "description": "Media type filter"
- },
- "allowEmptyValue": true,
- "allowReserved": true
- }
- ],
- "responses": {
- "200": {
- "description": "Provider list with logos",
- "content": {
- "application/json": {
- "schema": {
- "type": "object",
- "properties": {
- "providers": {
- "type": "array",
- "items": {
- "type": "object",
- "properties": {
- "id": {
- "type": "string",
- "description": "Platform ID"
- },
- "tmdbProviderIds": {
- "type": "array",
- "items": {
- "type": "number"
- },
- "description": "TMDB provider IDs"
- },
- "name": {
- "type": "string",
- "description": "Provider display name"
- },
- "logoPath": {
- "anyOf": [
- {
- "type": "string"
- },
- {
- "type": "null"
- }
- ],
- "description": "Provider logo image path"
- }
- },
- "required": [
- "id",
- "tmdbProviderIds",
- "name",
- "logoPath"
- ]
- }
- }
- },
- "required": [
- "providers"
- ],
- "description": "Available streaming providers for the user's region"
- }
- }
- }
- },
- "412": {
- "description": "412",
- "content": {
- "application/json": {
- "schema": {
- "oneOf": [
- {
- "type": "object",
- "properties": {
- "defined": {
- "const": true
- },
- "code": {
- "const": "PRECONDITION_FAILED"
- },
- "status": {
- "const": 412
- },
- "message": {
- "type": "string",
- "default": "TMDB API key is not configured"
- },
- "data": {
- "type": "object",
- "properties": {
- "code": {
- "const": "TMDB_NOT_CONFIGURED"
- }
- },
- "required": [
- "code"
- ]
- }
- },
- "required": [
- "defined",
- "code",
- "status",
- "message",
- "data"
- ]
- },
- {
- "type": "object",
- "properties": {
- "defined": {
- "const": false
- },
- "code": {
- "type": "string"
- },
- "status": {
- "type": "number"
- },
- "message": {
- "type": "string"
- },
- "data": {}
- },
- "required": [
- "defined",
- "code",
- "status",
- "message"
- ]
- }
- ]
- }
- }
- }
- }
- },
- "security": [
- {
- "session": []
- }
- ]
- }
- },
- "/search": {
- "get": {
- "operationId": "search",
+ "operationId": "discover.search",
"summary": "Search movies, TV shows, and people",
"description": "Full-text search across movies, TV shows, and people via TMDB. Optionally filter by media type.",
"tags": [
- "Search"
+ "Discover"
],
"parameters": [
{
@@ -4703,13 +3834,24 @@
"type": "string",
"default": "TMDB API key is not configured"
},
- "data": {}
+ "data": {
+ "type": "object",
+ "properties": {
+ "code": {
+ "const": "TMDB_NOT_CONFIGURED"
+ }
+ },
+ "required": [
+ "code"
+ ]
+ }
},
"required": [
"defined",
"code",
"status",
- "message"
+ "message",
+ "data"
]
},
{
@@ -4749,10 +3891,10 @@
]
}
},
- "/discover": {
+ "/discover/browse": {
"get": {
- "operationId": "discover",
- "summary": "Discover titles by genre",
+ "operationId": "discover.browse",
+ "summary": "Browse titles with filters",
"description": "Browse movies or TV shows filtered by genre, sorted by popularity. Returns user statuses and episode progress.",
"tags": [
"Discover"
@@ -5039,145 +4181,135 @@
]
}
},
- "/system/public-info": {
+ "/discover/genres": {
"get": {
- "operationId": "system.publicInfo",
- "summary": "Get public instance info",
- "description": "Fetch public information about this Sofa instance. Does not require authentication. Used by the login screen to display instance details.",
+ "operationId": "discover.genres",
+ "summary": "List genres",
+ "description": "Fetch the list of TMDB genres for movies or TV shows. Used to populate genre filters for discovery.",
"tags": [
- "System"
+ "Discover"
+ ],
+ "parameters": [
+ {
+ "name": "type",
+ "in": "query",
+ "required": true,
+ "schema": {
+ "enum": [
+ "movie",
+ "tv"
+ ],
+ "type": "string",
+ "description": "Media type filter"
+ },
+ "allowEmptyValue": true,
+ "allowReserved": true
+ }
],
"responses": {
"200": {
- "description": "Instance configuration and status",
+ "description": "Genre ID/name pairs",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
- "instanceId": {
- "type": "string",
- "description": "Unique instance identifier"
- },
- "tmdbConfigured": {
- "type": "boolean",
- "description": "Whether TMDB API is configured"
- },
- "userCount": {
- "type": "number",
- "description": "Number of registered users"
- },
- "registrationOpen": {
- "type": "boolean",
- "description": "Whether new user registration is open"
- },
- "posterUrls": {
+ "genres": {
"type": "array",
"items": {
- "type": "string"
- },
- "description": "Poster image URLs for the login screen collage"
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "number",
+ "description": "TMDB genre ID"
+ },
+ "name": {
+ "type": "string",
+ "description": "Genre display name"
+ }
+ },
+ "required": [
+ "id",
+ "name"
+ ]
+ }
}
},
"required": [
- "instanceId",
- "tmdbConfigured",
- "userCount",
- "registrationOpen",
- "posterUrls"
+ "genres"
],
- "description": "Public instance information shown on the login/setup screen"
+ "description": "Available genres for filtering"
}
}
}
- }
- }
- }
- },
- "/system/auth-config": {
- "get": {
- "operationId": "system.authConfig",
- "summary": "Get authentication config",
- "description": "Fetch the authentication configuration including OIDC availability, password login status, and registration state. Does not require authentication.",
- "tags": [
- "System"
- ],
- "responses": {
- "200": {
- "description": "Authentication provider configuration",
+ },
+ "412": {
+ "description": "412",
"content": {
"application/json": {
"schema": {
- "type": "object",
- "properties": {
- "oidcEnabled": {
- "type": "boolean",
- "description": "Whether OIDC/SSO login is available"
+ "oneOf": [
+ {
+ "type": "object",
+ "properties": {
+ "defined": {
+ "const": true
+ },
+ "code": {
+ "const": "PRECONDITION_FAILED"
+ },
+ "status": {
+ "const": 412
+ },
+ "message": {
+ "type": "string",
+ "default": "TMDB API key is not configured"
+ },
+ "data": {
+ "type": "object",
+ "properties": {
+ "code": {
+ "const": "TMDB_NOT_CONFIGURED"
+ }
+ },
+ "required": [
+ "code"
+ ]
+ }
+ },
+ "required": [
+ "defined",
+ "code",
+ "status",
+ "message",
+ "data"
+ ]
},
- "oidcProviderName": {
- "anyOf": [
- {
+ {
+ "type": "object",
+ "properties": {
+ "defined": {
+ "const": false
+ },
+ "code": {
"type": "string"
},
- {
- "type": "null"
- }
- ],
- "description": "Display name of the OIDC provider (e.g. Authelia, Keycloak)"
- },
- "passwordLoginDisabled": {
- "type": "boolean",
- "description": "Whether password-based login is disabled"
- },
- "registrationOpen": {
- "type": "boolean",
- "description": "Whether new user registration is open"
- },
- "userCount": {
- "type": "number",
- "description": "Number of registered users"
+ "status": {
+ "type": "number"
+ },
+ "message": {
+ "type": "string"
+ },
+ "data": {}
+ },
+ "required": [
+ "defined",
+ "code",
+ "status",
+ "message"
+ ]
}
- },
- "required": [
- "oidcEnabled",
- "oidcProviderName",
- "passwordLoginDisabled",
- "registrationOpen",
- "userCount"
- ],
- "description": "Authentication provider configuration"
- }
- }
- }
- }
- }
- }
- },
- "/system/status": {
- "get": {
- "operationId": "system.status",
- "summary": "Get internal system config",
- "description": "Returns internal configuration such as the public API URL. Requires authentication.",
- "tags": [
- "System"
- ],
- "responses": {
- "200": {
- "description": "Internal system configuration",
- "content": {
- "application/json": {
- "schema": {
- "type": "object",
- "properties": {
- "publicApiUrl": {
- "type": "string",
- "description": "Base URL of the centralized public API"
- }
- },
- "required": [
- "publicApiUrl"
- ],
- "description": "Internal system configuration for authenticated clients"
+ ]
}
}
}
@@ -5190,13 +4322,509 @@
]
}
},
- "/integrations": {
+ "/discover/platforms": {
"get": {
- "operationId": "integrations.list",
+ "operationId": "discover.platforms",
+ "summary": "List available streaming platforms",
+ "description": "Fetch all available streaming platforms, ordered by popularity. Includes subscription status for filtering.",
+ "tags": [
+ "Discover"
+ ],
+ "responses": {
+ "200": {
+ "description": "Platform list with logos and metadata",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "properties": {
+ "platforms": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "string",
+ "description": "Platform ID"
+ },
+ "name": {
+ "type": "string",
+ "description": "Display name"
+ },
+ "tmdbProviderIds": {
+ "type": "array",
+ "items": {
+ "type": "number"
+ },
+ "description": "TMDB provider IDs mapped to this platform"
+ },
+ "logoPath": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "description": "Logo image path"
+ },
+ "isSubscription": {
+ "type": "boolean",
+ "description": "True for subscription services, false for purchase/rental stores"
+ }
+ },
+ "required": [
+ "id",
+ "name",
+ "tmdbProviderIds",
+ "logoPath",
+ "isSubscription"
+ ],
+ "description": "A streaming platform"
+ }
+ }
+ },
+ "required": [
+ "platforms"
+ ]
+ }
+ }
+ }
+ }
+ },
+ "security": [
+ {
+ "session": []
+ }
+ ]
+ }
+ },
+ "/discover/recommendations": {
+ "get": {
+ "operationId": "discover.recommendations",
+ "summary": "Get personalized recommendations",
+ "description": "Fetch personalized title recommendations based on the user's library and watch history.",
+ "tags": [
+ "Discover"
+ ],
+ "responses": {
+ "200": {
+ "description": "Recommended titles",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "properties": {
+ "items": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/Recommendation"
+ }
+ }
+ },
+ "required": [
+ "items"
+ ],
+ "description": "Personalized title recommendations based on the user's library"
+ }
+ }
+ }
+ }
+ },
+ "security": [
+ {
+ "session": []
+ }
+ ]
+ }
+ },
+ "/people/{id}": {
+ "get": {
+ "operationId": "people.get",
+ "summary": "Get person details",
+ "description": "Fetch a person's profile and paginated filmography. Imports from TMDB on first access if not yet cached locally.",
+ "tags": [
+ "People"
+ ],
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string",
+ "minLength": 1,
+ "description": "Internal UUIDv7 identifier"
+ }
+ },
+ {
+ "name": "page",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "type": "integer",
+ "minimum": 1,
+ "maximum": 500,
+ "default": 1,
+ "description": "Page number (1-indexed)"
+ },
+ "allowEmptyValue": true,
+ "allowReserved": true
+ },
+ {
+ "name": "limit",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "type": "integer",
+ "minimum": 1,
+ "maximum": 100,
+ "default": 20,
+ "description": "Results per page (1-100, default 20)"
+ },
+ "allowEmptyValue": true,
+ "allowReserved": true
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Person profile, paginated filmography credits, and user statuses for their titles",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "properties": {
+ "person": {
+ "$ref": "#/components/schemas/Person"
+ },
+ "filmography": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/PersonCredit"
+ },
+ "description": "Credits for this person (paginated)"
+ },
+ "userStatuses": {
+ "type": "object",
+ "propertyNames": {
+ "type": "string"
+ },
+ "additionalProperties": {
+ "enum": [
+ "in_watchlist",
+ "watching",
+ "caught_up",
+ "completed"
+ ],
+ "type": "string"
+ },
+ "description": "Map of title ID to the user's display status"
+ },
+ "page": {
+ "type": "number",
+ "description": "Current page number"
+ },
+ "totalPages": {
+ "type": "number",
+ "description": "Total number of pages available"
+ },
+ "totalResults": {
+ "type": "number",
+ "description": "Total number of results across all pages"
+ }
+ },
+ "required": [
+ "person",
+ "filmography",
+ "userStatuses",
+ "page",
+ "totalPages",
+ "totalResults"
+ ],
+ "description": "Person profile with paginated filmography and user's statuses for their titles"
+ }
+ }
+ }
+ },
+ "404": {
+ "description": "404",
+ "content": {
+ "application/json": {
+ "schema": {
+ "oneOf": [
+ {
+ "type": "object",
+ "properties": {
+ "defined": {
+ "const": true
+ },
+ "code": {
+ "const": "NOT_FOUND"
+ },
+ "status": {
+ "const": 404
+ },
+ "message": {
+ "type": "string",
+ "default": "Person not found"
+ },
+ "data": {
+ "type": "object",
+ "properties": {
+ "code": {
+ "const": "PERSON_NOT_FOUND"
+ }
+ },
+ "required": [
+ "code"
+ ]
+ }
+ },
+ "required": [
+ "defined",
+ "code",
+ "status",
+ "message",
+ "data"
+ ]
+ },
+ {
+ "type": "object",
+ "properties": {
+ "defined": {
+ "const": false
+ },
+ "code": {
+ "type": "string"
+ },
+ "status": {
+ "type": "number"
+ },
+ "message": {
+ "type": "string"
+ },
+ "data": {}
+ },
+ "required": [
+ "defined",
+ "code",
+ "status",
+ "message"
+ ]
+ }
+ ]
+ }
+ }
+ }
+ }
+ },
+ "security": [
+ {
+ "session": []
+ }
+ ]
+ }
+ },
+ "/account/name": {
+ "put": {
+ "operationId": "account.updateName",
+ "summary": "Update display name",
+ "description": "Change the current user's display name.",
+ "tags": [
+ "Account"
+ ],
+ "requestBody": {
+ "required": true,
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 100,
+ "description": "New display name"
+ }
+ },
+ "required": [
+ "name"
+ ]
+ }
+ }
+ }
+ },
+ "responses": {
+ "200": {
+ "description": "OK"
+ }
+ },
+ "security": [
+ {
+ "session": []
+ }
+ ]
+ }
+ },
+ "/account/avatar": {
+ "post": {
+ "operationId": "account.uploadAvatar",
+ "summary": "Upload avatar",
+ "description": "Upload a new profile avatar image. Accepts JPEG, PNG, WebP, or GIF up to 2 MB.",
+ "tags": [
+ "Account"
+ ],
+ "requestBody": {
+ "required": true,
+ "content": {
+ "multipart/form-data": {
+ "schema": {
+ "type": "object",
+ "properties": {
+ "file": {
+ "type": "string",
+ "format": "binary"
+ }
+ },
+ "required": [
+ "file"
+ ]
+ }
+ }
+ }
+ },
+ "responses": {
+ "200": {
+ "description": "URL of the uploaded avatar image",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "properties": {
+ "imageUrl": {
+ "type": "string",
+ "description": "URL of the uploaded avatar image"
+ }
+ },
+ "required": [
+ "imageUrl"
+ ]
+ }
+ }
+ }
+ }
+ },
+ "security": [
+ {
+ "session": []
+ }
+ ]
+ },
+ "delete": {
+ "operationId": "account.removeAvatar",
+ "summary": "Remove avatar",
+ "description": "Delete the current user's profile avatar, reverting to the default.",
+ "tags": [
+ "Account"
+ ],
+ "responses": {
+ "200": {
+ "description": "OK"
+ }
+ },
+ "security": [
+ {
+ "session": []
+ }
+ ]
+ }
+ },
+ "/account/platforms": {
+ "get": {
+ "operationId": "account.platforms",
+ "summary": "Get user's streaming platforms",
+ "description": "Fetch the current user's subscribed streaming platform IDs.",
+ "tags": [
+ "Account"
+ ],
+ "responses": {
+ "200": {
+ "description": "List of platform IDs",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "properties": {
+ "platformIds": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ }
+ },
+ "required": [
+ "platformIds"
+ ]
+ }
+ }
+ }
+ }
+ },
+ "security": [
+ {
+ "session": []
+ }
+ ]
+ },
+ "put": {
+ "operationId": "account.updatePlatforms",
+ "summary": "Update streaming platforms",
+ "description": "Set the current user's subscribed streaming platforms.",
+ "tags": [
+ "Account"
+ ],
+ "requestBody": {
+ "required": true,
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "properties": {
+ "platformIds": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ },
+ "description": "List of platform IDs the user subscribes to"
+ }
+ },
+ "required": [
+ "platformIds"
+ ]
+ }
+ }
+ }
+ },
+ "responses": {
+ "200": {
+ "description": "OK"
+ }
+ },
+ "security": [
+ {
+ "session": []
+ }
+ ]
+ }
+ },
+ "/account/integrations": {
+ "get": {
+ "operationId": "account.integrations.list",
"summary": "List integrations",
"description": "Fetch all configured media server integrations for the current user, including recent webhook/sync events for each.",
"tags": [
- "Integrations"
+ "Account"
],
"responses": {
"200": {
@@ -5287,11 +4915,11 @@
]
},
"post": {
- "operationId": "integrations.create",
+ "operationId": "account.integrations.create",
"summary": "Create or update integration",
"description": "Create a new media server integration or update an existing one. Generates a unique webhook token for the provider.",
"tags": [
- "Integrations"
+ "Account"
],
"requestBody": {
"required": true,
@@ -5343,13 +4971,13 @@
]
}
},
- "/integrations/{provider}": {
+ "/account/integrations/{provider}": {
"delete": {
- "operationId": "integrations.delete",
+ "operationId": "account.integrations.delete",
"summary": "Delete integration",
"description": "Remove a media server integration and all its event history.",
"tags": [
- "Integrations"
+ "Account"
],
"parameters": [
{
@@ -5391,13 +5019,13 @@
]
}
},
- "/integrations/{provider}/regenerate-token": {
+ "/account/integrations/{provider}/regenerate-token": {
"post": {
- "operationId": "integrations.regenerateToken",
+ "operationId": "account.integrations.regenerateToken",
"summary": "Regenerate webhook token",
"description": "Generate a new webhook token for an integration. The old token is immediately invalidated.",
"tags": [
- "Integrations"
+ "Account"
],
"parameters": [
{
@@ -5517,6 +5145,330 @@
]
}
},
+ "/system/public-info": {
+ "get": {
+ "operationId": "system.publicInfo",
+ "summary": "Get public instance info",
+ "description": "Fetch public information about this Sofa instance including authentication configuration. Does not require authentication.",
+ "tags": [
+ "System"
+ ],
+ "responses": {
+ "200": {
+ "description": "Instance configuration, auth settings, and status",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "properties": {
+ "instanceId": {
+ "type": "string",
+ "description": "Unique instance identifier"
+ },
+ "tmdbConfigured": {
+ "type": "boolean",
+ "description": "Whether TMDB API is configured"
+ },
+ "userCount": {
+ "type": "number",
+ "description": "Number of registered users"
+ },
+ "registrationOpen": {
+ "type": "boolean",
+ "description": "Whether new user registration is open"
+ },
+ "posterUrls": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ },
+ "description": "Poster image URLs for the login screen collage"
+ },
+ "oidcEnabled": {
+ "type": "boolean",
+ "description": "Whether OIDC/SSO login is available"
+ },
+ "oidcProviderName": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "description": "Display name of the OIDC provider (e.g. Authelia, Keycloak)"
+ },
+ "passwordLoginDisabled": {
+ "type": "boolean",
+ "description": "Whether password-based login is disabled"
+ }
+ },
+ "required": [
+ "instanceId",
+ "tmdbConfigured",
+ "userCount",
+ "registrationOpen",
+ "posterUrls",
+ "oidcEnabled",
+ "oidcProviderName",
+ "passwordLoginDisabled"
+ ],
+ "description": "Public instance information and authentication configuration"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/system/status": {
+ "get": {
+ "operationId": "system.status",
+ "summary": "Get internal system config",
+ "description": "Returns internal configuration such as the public API URL. Requires authentication.",
+ "tags": [
+ "System"
+ ],
+ "responses": {
+ "200": {
+ "description": "Internal system configuration",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "properties": {
+ "publicApiUrl": {
+ "type": "string",
+ "description": "Base URL of the centralized public API"
+ }
+ },
+ "required": [
+ "publicApiUrl"
+ ],
+ "description": "Internal system configuration for authenticated clients"
+ }
+ }
+ }
+ }
+ },
+ "security": [
+ {
+ "session": []
+ }
+ ]
+ }
+ },
+ "/admin/settings": {
+ "get": {
+ "operationId": "admin.settings.get",
+ "summary": "Get admin settings",
+ "description": "Fetch all admin-configurable settings: registration, update checks, and telemetry.",
+ "tags": [
+ "Admin"
+ ],
+ "responses": {
+ "200": {
+ "description": "Current admin settings",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "properties": {
+ "registration": {
+ "type": "object",
+ "properties": {
+ "open": {
+ "type": "boolean",
+ "description": "Whether new user registration is open"
+ }
+ },
+ "required": [
+ "open"
+ ]
+ },
+ "updateCheck": {
+ "type": "object",
+ "properties": {
+ "enabled": {
+ "type": "boolean",
+ "description": "Whether automatic update checks are enabled"
+ },
+ "updateAvailable": {
+ "anyOf": [
+ {
+ "type": "boolean"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "description": "Whether a newer version is available"
+ },
+ "currentVersion": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "description": "Currently running version"
+ },
+ "latestVersion": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "description": "Latest available version"
+ },
+ "releaseUrl": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "description": "URL to the latest release page"
+ },
+ "lastCheckedAt": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "description": "When the last check was performed (ISO 8601)"
+ }
+ },
+ "required": [
+ "enabled",
+ "updateAvailable",
+ "currentVersion",
+ "latestVersion",
+ "releaseUrl",
+ "lastCheckedAt"
+ ]
+ },
+ "telemetry": {
+ "type": "object",
+ "properties": {
+ "enabled": {
+ "type": "boolean",
+ "description": "Whether anonymous telemetry is enabled"
+ },
+ "lastReportedAt": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "description": "ISO 8601 timestamp of the last telemetry report"
+ }
+ },
+ "required": [
+ "enabled",
+ "lastReportedAt"
+ ]
+ }
+ },
+ "required": [
+ "registration",
+ "updateCheck",
+ "telemetry"
+ ],
+ "description": "Combined admin settings for registration, update checks, and telemetry"
+ }
+ }
+ }
+ }
+ },
+ "security": [
+ {
+ "session": []
+ }
+ ]
+ },
+ "patch": {
+ "operationId": "admin.settings.update",
+ "summary": "Update admin settings",
+ "description": "Partially update admin settings. Only provided sections are changed; omitted sections keep their current values.",
+ "tags": [
+ "Admin"
+ ],
+ "requestBody": {
+ "required": true,
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "properties": {
+ "registration": {
+ "type": "object",
+ "properties": {
+ "open": {
+ "type": "boolean",
+ "description": "Whether new user registration is allowed"
+ }
+ },
+ "required": [
+ "open"
+ ]
+ },
+ "updateCheck": {
+ "type": "object",
+ "properties": {
+ "enabled": {
+ "type": "boolean",
+ "description": "Whether automatic update checks are enabled"
+ }
+ },
+ "required": [
+ "enabled"
+ ]
+ },
+ "telemetry": {
+ "type": "object",
+ "properties": {
+ "enabled": {
+ "type": "boolean",
+ "description": "Whether anonymous telemetry is enabled"
+ }
+ },
+ "required": [
+ "enabled"
+ ]
+ }
+ },
+ "description": "Partial update to admin settings"
+ }
+ }
+ }
+ },
+ "responses": {
+ "200": {
+ "description": "OK"
+ }
+ },
+ "security": [
+ {
+ "session": []
+ }
+ ]
+ }
+ },
"/admin/backups": {
"get": {
"operationId": "admin.backups.list",
@@ -6000,303 +5952,6 @@
]
}
},
- "/admin/registration": {
- "get": {
- "operationId": "admin.registration",
- "summary": "Get registration status",
- "description": "Check whether new user registration is currently open or closed.",
- "tags": [
- "Admin"
- ],
- "responses": {
- "200": {
- "description": "Registration open/closed state",
- "content": {
- "application/json": {
- "schema": {
- "type": "object",
- "properties": {
- "open": {
- "type": "boolean",
- "description": "Whether new user registration is open"
- }
- },
- "required": [
- "open"
- ],
- "description": "User registration status"
- }
- }
- }
- }
- },
- "security": [
- {
- "session": []
- }
- ]
- },
- "put": {
- "operationId": "admin.toggleRegistration",
- "summary": "Toggle registration",
- "description": "Open or close new user registration.",
- "tags": [
- "Admin"
- ],
- "requestBody": {
- "required": true,
- "content": {
- "application/json": {
- "schema": {
- "type": "object",
- "properties": {
- "open": {
- "type": "boolean",
- "description": "Whether new user registration is allowed"
- }
- },
- "required": [
- "open"
- ]
- }
- }
- }
- },
- "responses": {
- "200": {
- "description": "OK"
- }
- },
- "security": [
- {
- "session": []
- }
- ]
- }
- },
- "/admin/update-check": {
- "get": {
- "operationId": "admin.updateCheck",
- "summary": "Get update check status",
- "description": "Fetch whether automatic update checks are enabled, and the latest cached check result if available.",
- "tags": [
- "Admin"
- ],
- "responses": {
- "200": {
- "description": "Update check configuration and latest result",
- "content": {
- "application/json": {
- "schema": {
- "type": "object",
- "properties": {
- "enabled": {
- "type": "boolean",
- "description": "Whether automatic update checks are enabled"
- },
- "updateCheck": {
- "anyOf": [
- {
- "type": "object",
- "properties": {
- "updateAvailable": {
- "type": "boolean",
- "description": "Whether a newer version is available"
- },
- "currentVersion": {
- "type": "string",
- "description": "Currently running version"
- },
- "latestVersion": {
- "anyOf": [
- {
- "type": "string"
- },
- {
- "type": "null"
- }
- ],
- "description": "Latest available version, or null if check failed"
- },
- "releaseUrl": {
- "anyOf": [
- {
- "type": "string"
- },
- {
- "type": "null"
- }
- ],
- "description": "URL to the latest release page"
- },
- "lastCheckedAt": {
- "anyOf": [
- {
- "type": "string"
- },
- {
- "type": "null"
- }
- ],
- "description": "When the last check was performed (ISO 8601)"
- }
- },
- "required": [
- "updateAvailable",
- "currentVersion",
- "latestVersion",
- "releaseUrl",
- "lastCheckedAt"
- ],
- "description": "Result of an update availability check"
- },
- {
- "type": "null"
- }
- ],
- "description": "Latest check result, or null if checks are disabled or never ran"
- }
- },
- "required": [
- "enabled",
- "updateCheck"
- ],
- "description": "Update check configuration and latest result"
- }
- }
- }
- }
- },
- "security": [
- {
- "session": []
- }
- ]
- },
- "put": {
- "operationId": "admin.toggleUpdateCheck",
- "summary": "Toggle update checks",
- "description": "Enable or disable automatic update checks against the public API.",
- "tags": [
- "Admin"
- ],
- "requestBody": {
- "required": true,
- "content": {
- "application/json": {
- "schema": {
- "type": "object",
- "properties": {
- "enabled": {
- "type": "boolean",
- "description": "Whether automatic update checks are enabled"
- }
- },
- "required": [
- "enabled"
- ]
- }
- }
- }
- },
- "responses": {
- "200": {
- "description": "OK"
- }
- },
- "security": [
- {
- "session": []
- }
- ]
- }
- },
- "/admin/telemetry": {
- "get": {
- "operationId": "admin.telemetry",
- "summary": "Get telemetry status",
- "description": "Fetch whether anonymous telemetry is enabled and when the last report was sent.",
- "tags": [
- "Admin"
- ],
- "responses": {
- "200": {
- "description": "Telemetry configuration and last report time",
- "content": {
- "application/json": {
- "schema": {
- "type": "object",
- "properties": {
- "enabled": {
- "type": "boolean",
- "description": "Whether telemetry is currently enabled"
- },
- "lastReportedAt": {
- "anyOf": [
- {
- "type": "string"
- },
- {
- "type": "null"
- }
- ],
- "description": "ISO 8601 timestamp of the last telemetry report, or null if never sent"
- }
- },
- "required": [
- "enabled",
- "lastReportedAt"
- ],
- "description": "Telemetry configuration and last report time"
- }
- }
- }
- }
- },
- "security": [
- {
- "session": []
- }
- ]
- },
- "put": {
- "operationId": "admin.toggleTelemetry",
- "summary": "Toggle telemetry",
- "description": "Enable or disable anonymous telemetry reporting.",
- "tags": [
- "Admin"
- ],
- "requestBody": {
- "required": true,
- "content": {
- "application/json": {
- "schema": {
- "type": "object",
- "properties": {
- "enabled": {
- "type": "boolean",
- "description": "Whether anonymous telemetry reporting is enabled"
- }
- },
- "required": [
- "enabled"
- ]
- }
- }
- }
- },
- "responses": {
- "200": {
- "description": "OK"
- }
- },
- "security": [
- {
- "session": []
- }
- ]
- }
- },
"/admin/jobs/trigger": {
"post": {
"operationId": "admin.triggerJob",
@@ -6546,276 +6201,6 @@
]
}
},
- "/account/name": {
- "put": {
- "operationId": "account.updateName",
- "summary": "Update display name",
- "description": "Change the current user's display name.",
- "tags": [
- "Account"
- ],
- "requestBody": {
- "required": true,
- "content": {
- "application/json": {
- "schema": {
- "type": "object",
- "properties": {
- "name": {
- "type": "string",
- "minLength": 1,
- "maxLength": 100,
- "description": "New display name"
- }
- },
- "required": [
- "name"
- ]
- }
- }
- }
- },
- "responses": {
- "200": {
- "description": "OK"
- }
- },
- "security": [
- {
- "session": []
- }
- ]
- }
- },
- "/account/avatar": {
- "post": {
- "operationId": "account.uploadAvatar",
- "summary": "Upload avatar",
- "description": "Upload a new profile avatar image. Accepts JPEG, PNG, WebP, or GIF up to 2 MB.",
- "tags": [
- "Account"
- ],
- "requestBody": {
- "required": true,
- "content": {
- "multipart/form-data": {
- "schema": {
- "type": "object",
- "properties": {
- "file": {
- "type": "string",
- "format": "binary"
- }
- },
- "required": [
- "file"
- ]
- }
- }
- }
- },
- "responses": {
- "200": {
- "description": "URL of the uploaded avatar image",
- "content": {
- "application/json": {
- "schema": {
- "type": "object",
- "properties": {
- "imageUrl": {
- "type": "string",
- "description": "URL of the uploaded avatar image"
- }
- },
- "required": [
- "imageUrl"
- ]
- }
- }
- }
- }
- },
- "security": [
- {
- "session": []
- }
- ]
- },
- "delete": {
- "operationId": "account.removeAvatar",
- "summary": "Remove avatar",
- "description": "Delete the current user's profile avatar, reverting to the default.",
- "tags": [
- "Account"
- ],
- "responses": {
- "200": {
- "description": "OK"
- }
- },
- "security": [
- {
- "session": []
- }
- ]
- }
- },
- "/account/platforms": {
- "get": {
- "operationId": "account.platforms",
- "summary": "Get user's streaming platforms",
- "description": "Fetch the current user's subscribed streaming platform IDs.",
- "tags": [
- "Account"
- ],
- "responses": {
- "200": {
- "description": "List of platform IDs",
- "content": {
- "application/json": {
- "schema": {
- "type": "object",
- "properties": {
- "platformIds": {
- "type": "array",
- "items": {
- "type": "string"
- }
- }
- },
- "required": [
- "platformIds"
- ]
- }
- }
- }
- }
- },
- "security": [
- {
- "session": []
- }
- ]
- },
- "put": {
- "operationId": "account.updatePlatforms",
- "summary": "Update streaming platforms",
- "description": "Set the current user's subscribed streaming platforms.",
- "tags": [
- "Account"
- ],
- "requestBody": {
- "required": true,
- "content": {
- "application/json": {
- "schema": {
- "type": "object",
- "properties": {
- "platformIds": {
- "type": "array",
- "items": {
- "type": "string"
- },
- "description": "List of platform IDs the user subscribes to"
- }
- },
- "required": [
- "platformIds"
- ]
- }
- }
- }
- },
- "responses": {
- "200": {
- "description": "OK"
- }
- },
- "security": [
- {
- "session": []
- }
- ]
- }
- },
- "/platforms": {
- "get": {
- "operationId": "platforms.list",
- "summary": "List all platforms",
- "description": "Fetch all available streaming platforms, ordered by popularity.",
- "tags": [
- "Platforms"
- ],
- "responses": {
- "200": {
- "description": "All platforms with metadata",
- "content": {
- "application/json": {
- "schema": {
- "type": "object",
- "properties": {
- "platforms": {
- "type": "array",
- "items": {
- "type": "object",
- "properties": {
- "id": {
- "type": "string",
- "description": "Platform ID"
- },
- "name": {
- "type": "string",
- "description": "Display name"
- },
- "tmdbProviderIds": {
- "type": "array",
- "items": {
- "type": "number"
- },
- "description": "TMDB provider IDs mapped to this platform"
- },
- "logoPath": {
- "anyOf": [
- {
- "type": "string"
- },
- {
- "type": "null"
- }
- ],
- "description": "Logo image path"
- },
- "isSubscription": {
- "type": "boolean",
- "description": "True for subscription services, false for purchase/rental stores"
- }
- },
- "required": [
- "id",
- "name",
- "tmdbProviderIds",
- "logoPath",
- "isSubscription"
- ],
- "description": "A streaming platform"
- }
- }
- },
- "required": [
- "platforms"
- ]
- }
- }
- }
- }
- },
- "security": [
- {
- "session": []
- }
- ]
- }
- },
"/imports/parse-file": {
"post": {
"operationId": "imports.parseFile",
diff --git a/package.json b/package.json
index ff367ee..4db337c 100644
--- a/package.json
+++ b/package.json
@@ -16,21 +16,21 @@
"@lingui/metro-transformer": "5.9.3",
"@lingui/react": "5.9.3",
"@lingui/vite-plugin": "5.9.3",
- "@orpc/client": "1.13.10",
- "@orpc/contract": "1.13.10",
- "@orpc/json-schema": "1.13.10",
- "@orpc/openapi": "1.13.10",
- "@orpc/server": "1.13.10",
- "@orpc/tanstack-query": "1.13.10",
- "@orpc/zod": "1.13.10",
+ "@orpc/client": "1.13.13",
+ "@orpc/contract": "1.13.13",
+ "@orpc/json-schema": "1.13.13",
+ "@orpc/openapi": "1.13.13",
+ "@orpc/server": "1.13.13",
+ "@orpc/tanstack-query": "1.13.13",
+ "@orpc/zod": "1.13.13",
"@tanstack/react-form": "1.28.5",
"@tanstack/react-query": "5.95.2",
"@types/bun": "1.3.11",
"@types/node": "25.5.0",
"@types/react": "19.2.14",
- "@vitest/browser": "4.1.1",
- "@vitest/browser-playwright": "4.1.1",
- "@vitest/coverage-v8": "4.1.1",
+ "@vitest/browser": "4.1.2",
+ "@vitest/browser-playwright": "4.1.2",
+ "@vitest/coverage-v8": "4.1.2",
"better-auth": "1.5.6",
"drizzle-kit": "1.0.0-beta.19",
"drizzle-orm": "1.0.0-beta.19",
@@ -40,7 +40,7 @@
"tailwindcss": "4.2.2",
"thumbhash": "0.1.1",
"typescript": "5.9.3",
- "vitest": "4.1.1",
+ "vitest": "4.1.2",
"zod": "4.3.6"
}
},
@@ -72,7 +72,7 @@
"@vitest/browser-playwright": "catalog:",
"@vitest/coverage-v8": "catalog:",
"eslint-plugin-drizzle": "0.2.3",
- "eslint-plugin-lingui": "0.11.0",
+ "eslint-plugin-lingui": "0.12.0",
"eslint-plugin-react-hooks": "7.0.1",
"oxfmt": "0.42.0",
"oxlint": "1.57.0",
diff --git a/packages/api/src/contract.ts b/packages/api/src/contract.ts
index b6e7161..d6f1ccc 100644
--- a/packages/api/src/contract.ts
+++ b/packages/api/src/contract.ts
@@ -3,18 +3,17 @@ import { z } from "zod";
import { AppErrorCode, appErrorData } from "./errors";
import {
- AuthConfigOutput,
+ AdminSettingsOutput,
+ AdminSettingsUpdateInput,
BackupCreateOutput,
BackupScheduleOutput,
BackupsListOutput,
- BatchWatchInput,
ContinueWatchingOutput,
CreateImportJobInput,
CreateIntegrationInput,
- DashboardRecommendationsOutput,
- DashboardStatsOutput,
DiscoverInput,
DiscoverOutput,
+ DiscoverRecommendationsOutput,
FilenameParam,
GenresOutput,
IdParam,
@@ -26,41 +25,36 @@ import {
LibraryGenresOutput,
LibraryListInput,
LibraryListOutput,
+ LibraryStatsOutput,
MediaTypeParam,
PageParam,
PaginatedInput,
ParseFileInput,
- PlatformsListOutput,
ParsePayloadInput,
PersonDetailOutput,
+ PlatformsListOutput,
PopularOutput,
ProviderParam,
PublicInfoOutput,
PurgeImageCacheOutput,
PurgeMetadataCacheOutput,
- QuickAddOutput,
- RegistrationOutput,
RestoreBackupInput,
SearchInput,
SearchOutput,
SystemHealthOutput,
SystemStatusOutput,
- TelemetryOutput,
TitleDetailOutput,
TitleRecommendationsOutput,
- ToggleRegistrationInput,
- ToggleTelemetryInput,
- ToggleUpdateCheckInput,
TrendingOutput,
TrendingTypeParam,
TriggerJobInput,
TriggerJobOutput,
- UpdateCheckOutput,
- UpdateUserPlatformsInput,
+ UnwatchInput,
UpdateNameInput,
UpdateRatingInput,
UpdateScheduleInput,
UpdateStatusInput,
+ UpdateUserPlatformsInput,
UpcomingInput,
UpcomingOutput,
UploadAvatarInput,
@@ -69,12 +63,20 @@ import {
UserPlatformsOutput,
WatchHistoryInput,
WatchHistoryOutput,
- WatchProvidersOutput,
+ WatchInput,
} from "./schemas";
+const tmdbNotConfiguredError = {
+ PRECONDITION_FAILED: {
+ message: "TMDB API key is not configured",
+ data: appErrorData(AppErrorCode.TMDB_NOT_CONFIGURED),
+ },
+} as const;
+
export const contract = {
+ // βββ Titles βββββββββββββββββββββββββββββββββββββββββββββββββ
titles: {
- detail: oc
+ get: oc
.route({
method: "GET",
path: "/titles/{id}",
@@ -92,167 +94,98 @@ export const contract = {
data: appErrorData(AppErrorCode.TITLE_NOT_FOUND),
},
}),
+ similar: oc
+ .route({
+ method: "GET",
+ path: "/titles/{id}/similar",
+ tags: ["Titles"],
+ summary: "Get similar titles",
+ description:
+ "Fetch similar titles based on locally cached recommendation data, along with the user's statuses for each.",
+ successDescription: "Similar titles with user statuses",
+ })
+ .input(IdParam)
+ .output(TitleRecommendationsOutput),
+ },
+
+ // βββ Tracking βββββββββββββββββββββββββββββββββββββββββββββββ
+ tracking: {
+ watch: oc
+ .route({
+ method: "POST",
+ path: "/tracking/watch",
+ tags: ["Tracking"],
+ summary: "Mark items as watched",
+ description:
+ "Mark one or more items as watched. Use scope to indicate what the IDs refer to: movie (title IDs), episode (episode IDs), season (season IDs), or series (title IDs to mark all episodes).",
+ })
+ .input(WatchInput)
+ .output(z.void()),
+ unwatch: oc
+ .route({
+ method: "POST",
+ path: "/tracking/unwatch",
+ tags: ["Tracking"],
+ summary: "Remove watch records",
+ description:
+ "Remove watch records for one or more items. Use scope to indicate what the IDs refer to: movie, episode, season, or series.",
+ })
+ .input(UnwatchInput)
+ .output(z.void()),
updateStatus: oc
.route({
method: "PUT",
- path: "/titles/{id}/status",
- tags: ["Titles"],
+ path: "/tracking/titles/{id}/status",
+ tags: ["Tracking"],
summary: "Update tracking status",
description:
"Set the user's tracking status for a title. Use null to remove the title from the library entirely.",
})
.input(UpdateStatusInput)
- .output(z.void()),
- updateRating: oc
- .route({
- method: "PUT",
- path: "/titles/{id}/rating",
- tags: ["Titles"],
- summary: "Rate a title",
- description: "Set a 0-5 star rating for a title. Use 0 to clear the rating.",
- })
- .input(UpdateRatingInput)
- .output(z.void()),
- watchMovie: oc
- .route({
- method: "POST",
- path: "/titles/{id}/watch",
- tags: ["Titles"],
- summary: "Mark movie as watched",
- description: "Log a watch event for a movie. Automatically sets status to completed.",
- })
- .input(IdParam)
- .output(z.void()),
- watchAll: oc
- .route({
- method: "POST",
- path: "/titles/{id}/watch-all",
- tags: ["Titles"],
- summary: "Mark all episodes watched",
- description:
- "Mark every episode of a TV show as watched. Requires seasons to be hydrated first.",
- })
- .input(IdParam)
- .output(z.void()),
- userInfo: oc
- .route({
- method: "GET",
- path: "/titles/{id}/user-info",
- tags: ["Titles"],
- summary: "Get user's title info",
- description:
- "Fetch the current user's tracking status, rating, and watched episode IDs for a title.",
- successDescription: "User's status, rating, and list of watched episode IDs",
- })
- .input(IdParam)
- .output(UserInfoOutput),
- recommendations: oc
- .route({
- method: "GET",
- path: "/titles/{id}/recommendations",
- tags: ["Titles"],
- summary: "Get title recommendations",
- description:
- "Fetch similar titles based on locally cached recommendation data, along with the user's statuses for each.",
- successDescription: "Recommended titles with user statuses",
- })
- .input(IdParam)
- .output(TitleRecommendationsOutput),
- quickAdd: oc
- .route({
- method: "POST",
- path: "/titles/{id}/quick-add",
- tags: ["Titles"],
- summary: "Quick add title to library",
- description:
- "Add a title to the user's watchlist and trigger a full TMDB import if needed. If the title already exists in the user's library, returns alreadyAdded: true.",
- successDescription: "Title ID and whether it was already in the library",
- })
- .input(IdParam)
- .output(QuickAddOutput)
+ .output(z.void())
.errors({
NOT_FOUND: {
message: "Title not found",
data: appErrorData(AppErrorCode.TITLE_NOT_FOUND),
},
}),
- },
- episodes: {
- watch: oc
+ rate: oc
.route({
- method: "POST",
- path: "/episodes/{id}/watch",
- tags: ["Episodes"],
- summary: "Mark episode watched",
- description: "Record a watch event for a single TV episode.",
+ method: "PUT",
+ path: "/tracking/titles/{id}/rating",
+ tags: ["Tracking"],
+ summary: "Rate a title",
+ description: "Set a 0-5 star rating for a title. Use 0 to clear the rating.",
})
- .input(IdParam)
+ .input(UpdateRatingInput)
.output(z.void()),
- unwatch: oc
- .route({
- method: "POST",
- path: "/episodes/{id}/unwatch",
- tags: ["Episodes"],
- summary: "Mark episode unwatched",
- description: "Remove the watch record for a single TV episode.",
- })
- .input(IdParam)
- .output(z.void()),
- batchWatch: oc
- .route({
- method: "POST",
- path: "/episodes/batch-watch",
- tags: ["Episodes"],
- summary: "Batch mark episodes watched",
- description:
- "Record watch events for multiple episodes at once. Useful for marking a range of episodes as watched.",
- })
- .input(BatchWatchInput)
- .output(z.void()),
- },
- seasons: {
- watch: oc
- .route({
- method: "POST",
- path: "/seasons/{id}/watch",
- tags: ["Seasons"],
- summary: "Mark season watched",
- description: "Mark all episodes in a season as watched in a single operation.",
- })
- .input(IdParam)
- .output(z.void()),
- unwatch: oc
- .route({
- method: "POST",
- path: "/seasons/{id}/unwatch",
- tags: ["Seasons"],
- summary: "Mark season unwatched",
- description: "Remove all watch records for episodes in a season in a single operation.",
- })
- .input(IdParam)
- .output(z.void()),
- },
- people: {
- detail: oc
+ userInfo: oc
.route({
method: "GET",
- path: "/people/{id}",
- tags: ["People"],
- summary: "Get person details",
+ path: "/tracking/titles/{id}",
+ tags: ["Tracking"],
+ summary: "Get user's tracking info for a title",
description:
- "Fetch a person's profile and paginated filmography. Imports from TMDB on first access if not yet cached locally.",
- successDescription:
- "Person profile, paginated filmography credits, and user statuses for their titles",
+ "Fetch the current user's tracking status, rating, and watched episode IDs for a title.",
+ successDescription: "User's status, rating, and list of watched episode IDs",
})
- .input(IdParam.merge(PaginatedInput))
- .output(PersonDetailOutput)
- .errors({
- NOT_FOUND: {
- message: "Person not found",
- data: appErrorData(AppErrorCode.PERSON_NOT_FOUND),
- },
- }),
+ .input(IdParam)
+ .output(UserInfoOutput),
+ stats: oc
+ .route({
+ method: "GET",
+ path: "/tracking/stats",
+ tags: ["Tracking"],
+ summary: "Get watch statistics",
+ description:
+ "Fetch the user's watch counts grouped by time period. Useful for rendering activity charts and dashboard counters.",
+ successDescription: "Watch counts bucketed by time period",
+ })
+ .input(WatchHistoryInput)
+ .output(WatchHistoryOutput),
},
+
+ // βββ Library ββββββββββββββββββββββββββββββββββββββββββββββββ
library: {
list: oc
.route({
@@ -277,46 +210,32 @@ export const contract = {
successDescription: "Genres present in the library",
})
.output(LibraryGenresOutput),
- },
- dashboard: {
stats: oc
.route({
method: "GET",
- path: "/dashboard/stats",
- tags: ["Dashboard"],
- summary: "Get dashboard statistics",
- description:
- "Fetch summary counts for the current user: movies watched this month, episodes this week, library size, and completed titles.",
- successDescription: "Aggregate watch statistics",
+ path: "/library/stats",
+ tags: ["Library"],
+ summary: "Get library statistics",
+ description: "Fetch aggregate library counts: total titles and completed titles.",
+ successDescription: "Library size and completed count",
})
- .output(DashboardStatsOutput),
+ .output(LibraryStatsOutput),
continueWatching: oc
.route({
method: "GET",
- path: "/dashboard/continue-watching",
- tags: ["Dashboard"],
+ path: "/library/continue-watching",
+ tags: ["Library"],
summary: "Get continue watching list",
description:
"Fetch TV shows the user is currently watching, with the next unwatched episode and progress for each.",
successDescription: "In-progress shows with next episode and watch progress",
})
.output(ContinueWatchingOutput),
- recommendations: oc
- .route({
- method: "GET",
- path: "/dashboard/recommendations",
- tags: ["Dashboard"],
- summary: "Get personalized recommendations",
- description:
- "Fetch personalized title recommendations based on the user's library and watch history.",
- successDescription: "Recommended titles",
- })
- .output(DashboardRecommendationsOutput),
upcoming: oc
.route({
method: "GET",
- path: "/dashboard/upcoming",
- tags: ["Dashboard"],
+ path: "/library/upcoming",
+ tags: ["Library"],
summary: "Get upcoming episodes and movies",
description:
"Fetch upcoming episodes and movie releases for titles in the user's library, sorted by date. Supports cursor-based pagination.",
@@ -324,25 +243,15 @@ export const contract = {
})
.input(UpcomingInput)
.output(UpcomingOutput),
- watchHistory: oc
- .route({
- method: "GET",
- path: "/dashboard/watch-history",
- tags: ["Dashboard"],
- summary: "Get watch history",
- description:
- "Fetch the user's watch counts grouped by time period. Useful for rendering activity charts.",
- successDescription: "Watch counts bucketed by time period",
- })
- .input(WatchHistoryInput)
- .output(WatchHistoryOutput),
},
- explore: {
+
+ // βββ Discover βββββββββββββββββββββββββββββββββββββββββββββββ
+ discover: {
trending: oc
.route({
method: "GET",
- path: "/explore/trending",
- tags: ["Explore"],
+ path: "/discover/trending",
+ tags: ["Discover"],
summary: "Get trending titles",
description:
"Fetch today's trending movies and/or TV shows from TMDB, including a featured hero title and the user's statuses.",
@@ -350,17 +259,12 @@ export const contract = {
})
.input(TrendingTypeParam.merge(PageParam))
.output(TrendingOutput)
- .errors({
- PRECONDITION_FAILED: {
- message: "TMDB API key is not configured",
- data: appErrorData(AppErrorCode.TMDB_NOT_CONFIGURED),
- },
- }),
+ .errors(tmdbNotConfiguredError),
popular: oc
.route({
method: "GET",
- path: "/explore/popular",
- tags: ["Explore"],
+ path: "/discover/popular",
+ tags: ["Discover"],
summary: "Get popular titles",
description:
"Fetch currently popular movies or TV shows from TMDB with the user's tracking statuses.",
@@ -368,17 +272,38 @@ export const contract = {
})
.input(MediaTypeParam.merge(PageParam))
.output(PopularOutput)
- .errors({
- PRECONDITION_FAILED: {
- message: "TMDB API key is not configured",
- data: appErrorData(AppErrorCode.TMDB_NOT_CONFIGURED),
- },
- }),
+ .errors(tmdbNotConfiguredError),
+ search: oc
+ .route({
+ method: "GET",
+ path: "/discover/search",
+ tags: ["Discover"],
+ summary: "Search movies, TV shows, and people",
+ description:
+ "Full-text search across movies, TV shows, and people via TMDB. Optionally filter by media type.",
+ successDescription: "Search results with metadata",
+ })
+ .input(SearchInput)
+ .output(SearchOutput)
+ .errors(tmdbNotConfiguredError),
+ browse: oc
+ .route({
+ method: "GET",
+ path: "/discover/browse",
+ tags: ["Discover"],
+ summary: "Browse titles with filters",
+ description:
+ "Browse movies or TV shows filtered by genre, sorted by popularity. Returns user statuses and episode progress.",
+ successDescription: "Filtered titles with user statuses",
+ })
+ .input(DiscoverInput)
+ .output(DiscoverOutput)
+ .errors(tmdbNotConfiguredError),
genres: oc
.route({
method: "GET",
- path: "/explore/genres",
- tags: ["Explore"],
+ path: "/discover/genres",
+ tags: ["Discover"],
summary: "List genres",
description:
"Fetch the list of TMDB genres for movies or TV shows. Used to populate genre filters for discovery.",
@@ -386,64 +311,164 @@ export const contract = {
})
.input(MediaTypeParam)
.output(GenresOutput)
- .errors({
- PRECONDITION_FAILED: {
- message: "TMDB API key is not configured",
- data: appErrorData(AppErrorCode.TMDB_NOT_CONFIGURED),
- },
- }),
- watchProviders: oc
+ .errors(tmdbNotConfiguredError),
+ platforms: oc
.route({
method: "GET",
- path: "/explore/watch-providers",
- tags: ["Explore"],
- summary: "List available watch providers",
+ path: "/discover/platforms",
+ tags: ["Discover"],
+ summary: "List available streaming platforms",
description:
- "Fetch the list of streaming providers available in the configured region. Used to populate provider filter dropdowns.",
- successDescription: "Provider list with logos",
+ "Fetch all available streaming platforms, ordered by popularity. Includes subscription status for filtering.",
+ successDescription: "Platform list with logos and metadata",
})
- .input(MediaTypeParam)
- .output(WatchProvidersOutput)
+ .output(PlatformsListOutput),
+ recommendations: oc
+ .route({
+ method: "GET",
+ path: "/discover/recommendations",
+ tags: ["Discover"],
+ summary: "Get personalized recommendations",
+ description:
+ "Fetch personalized title recommendations based on the user's library and watch history.",
+ successDescription: "Recommended titles",
+ })
+ .output(DiscoverRecommendationsOutput),
+ },
+
+ // βββ People βββββββββββββββββββββββββββββββββββββββββββββββββ
+ people: {
+ get: oc
+ .route({
+ method: "GET",
+ path: "/people/{id}",
+ tags: ["People"],
+ summary: "Get person details",
+ description:
+ "Fetch a person's profile and paginated filmography. Imports from TMDB on first access if not yet cached locally.",
+ successDescription:
+ "Person profile, paginated filmography credits, and user statuses for their titles",
+ })
+ .input(IdParam.merge(PaginatedInput))
+ .output(PersonDetailOutput)
.errors({
- PRECONDITION_FAILED: {
- message: "TMDB API key is not configured",
- data: appErrorData(AppErrorCode.TMDB_NOT_CONFIGURED),
+ NOT_FOUND: {
+ message: "Person not found",
+ data: appErrorData(AppErrorCode.PERSON_NOT_FOUND),
},
}),
},
- search: oc
- .route({
- method: "GET",
- path: "/search",
- tags: ["Search"],
- summary: "Search movies, TV shows, and people",
- description:
- "Full-text search across movies, TV shows, and people via TMDB. Optionally filter by media type.",
- successDescription: "Search results with metadata",
- })
- .input(SearchInput)
- .output(SearchOutput)
- .errors({
- PRECONDITION_FAILED: { message: "TMDB API key is not configured" },
- }),
- discover: oc
- .route({
- method: "GET",
- path: "/discover",
- tags: ["Discover"],
- summary: "Discover titles by genre",
- description:
- "Browse movies or TV shows filtered by genre, sorted by popularity. Returns user statuses and episode progress.",
- successDescription: "Filtered titles with user statuses",
- })
- .input(DiscoverInput)
- .output(DiscoverOutput)
- .errors({
- PRECONDITION_FAILED: {
- message: "TMDB API key is not configured",
- data: appErrorData(AppErrorCode.TMDB_NOT_CONFIGURED),
- },
- }),
+
+ // βββ Account ββββββββββββββββββββββββββββββββββββββββββββββββ
+ account: {
+ updateName: oc
+ .route({
+ method: "PUT",
+ path: "/account/name",
+ tags: ["Account"],
+ summary: "Update display name",
+ description: "Change the current user's display name.",
+ })
+ .input(UpdateNameInput)
+ .output(z.void()),
+ uploadAvatar: oc
+ .route({
+ method: "POST",
+ path: "/account/avatar",
+ tags: ["Account"],
+ summary: "Upload avatar",
+ description:
+ "Upload a new profile avatar image. Accepts JPEG, PNG, WebP, or GIF up to 2 MB.",
+ successDescription: "URL of the uploaded avatar image",
+ })
+ .input(UploadAvatarInput)
+ .output(UploadAvatarOutput),
+ removeAvatar: oc
+ .route({
+ method: "DELETE",
+ path: "/account/avatar",
+ tags: ["Account"],
+ summary: "Remove avatar",
+ description: "Delete the current user's profile avatar, reverting to the default.",
+ })
+ .input(z.void())
+ .output(z.void()),
+ platforms: oc
+ .route({
+ method: "GET",
+ path: "/account/platforms",
+ tags: ["Account"],
+ summary: "Get user's streaming platforms",
+ description: "Fetch the current user's subscribed streaming platform IDs.",
+ successDescription: "List of platform IDs",
+ })
+ .output(UserPlatformsOutput),
+ updatePlatforms: oc
+ .route({
+ method: "PUT",
+ path: "/account/platforms",
+ tags: ["Account"],
+ summary: "Update streaming platforms",
+ description: "Set the current user's subscribed streaming platforms.",
+ })
+ .input(UpdateUserPlatformsInput)
+ .output(z.void()),
+ integrations: {
+ list: oc
+ .route({
+ method: "GET",
+ path: "/account/integrations",
+ tags: ["Account"],
+ summary: "List integrations",
+ description:
+ "Fetch all configured media server integrations for the current user, including recent webhook/sync events for each.",
+ successDescription: "Integrations with their recent events",
+ })
+ .output(IntegrationsListOutput),
+ create: oc
+ .route({
+ method: "POST",
+ path: "/account/integrations",
+ tags: ["Account"],
+ summary: "Create or update integration",
+ description:
+ "Create a new media server integration or update an existing one. Generates a unique webhook token for the provider.",
+ successDescription: "Created or updated integration with token",
+ })
+ .input(CreateIntegrationInput)
+ .output(IntegrationOutput),
+ delete: oc
+ .route({
+ method: "DELETE",
+ path: "/account/integrations/{provider}",
+ tags: ["Account"],
+ summary: "Delete integration",
+ description: "Remove a media server integration and all its event history.",
+ })
+ .input(ProviderParam)
+ .output(z.void()),
+ regenerateToken: oc
+ .route({
+ method: "POST",
+ path: "/account/integrations/{provider}/regenerate-token",
+ tags: ["Account"],
+ summary: "Regenerate webhook token",
+ description:
+ "Generate a new webhook token for an integration. The old token is immediately invalidated.",
+ successDescription: "Integration with new token",
+ })
+ .input(ProviderParam)
+ .output(IntegrationOutput)
+ .errors({
+ NOT_FOUND: {
+ message: "Integration not found",
+ data: appErrorData(AppErrorCode.INTEGRATION_NOT_FOUND),
+ },
+ }),
+ },
+ },
+
+ // βββ System βββββββββββββββββββββββββββββββββββββββββββββββββ
system: {
publicInfo: oc
.route({
@@ -452,21 +477,10 @@ export const contract = {
tags: ["System"],
summary: "Get public instance info",
description:
- "Fetch public information about this Sofa instance. Does not require authentication. Used by the login screen to display instance details.",
- successDescription: "Instance configuration and status",
+ "Fetch public information about this Sofa instance including authentication configuration. Does not require authentication.",
+ successDescription: "Instance configuration, auth settings, and status",
})
.output(PublicInfoOutput),
- authConfig: oc
- .route({
- method: "GET",
- path: "/system/auth-config",
- tags: ["System"],
- summary: "Get authentication config",
- description:
- "Fetch the authentication configuration including OIDC availability, password login status, and registration state. Does not require authentication.",
- successDescription: "Authentication provider configuration",
- })
- .output(AuthConfigOutput),
status: oc
.route({
method: "GET",
@@ -479,60 +493,33 @@ export const contract = {
})
.output(SystemStatusOutput),
},
- integrations: {
- list: oc
- .route({
- method: "GET",
- path: "/integrations",
- tags: ["Integrations"],
- summary: "List integrations",
- description:
- "Fetch all configured media server integrations for the current user, including recent webhook/sync events for each.",
- successDescription: "Integrations with their recent events",
- })
- .output(IntegrationsListOutput),
- create: oc
- .route({
- method: "POST",
- path: "/integrations",
- tags: ["Integrations"],
- summary: "Create or update integration",
- description:
- "Create a new media server integration or update an existing one. Generates a unique webhook token for the provider.",
- successDescription: "Created or updated integration with token",
- })
- .input(CreateIntegrationInput)
- .output(IntegrationOutput),
- delete: oc
- .route({
- method: "DELETE",
- path: "/integrations/{provider}",
- tags: ["Integrations"],
- summary: "Delete integration",
- description: "Remove a media server integration and all its event history.",
- })
- .input(ProviderParam)
- .output(z.void()),
- regenerateToken: oc
- .route({
- method: "POST",
- path: "/integrations/{provider}/regenerate-token",
- tags: ["Integrations"],
- summary: "Regenerate webhook token",
- description:
- "Generate a new webhook token for an integration. The old token is immediately invalidated.",
- successDescription: "Integration with new token",
- })
- .input(ProviderParam)
- .output(IntegrationOutput)
- .errors({
- NOT_FOUND: {
- message: "Integration not found",
- data: appErrorData(AppErrorCode.INTEGRATION_NOT_FOUND),
- },
- }),
- },
+
+ // βββ Admin ββββββββββββββββββββββββββββββββββββββββββββββββββ
admin: {
+ settings: {
+ get: oc
+ .route({
+ method: "GET",
+ path: "/admin/settings",
+ tags: ["Admin"],
+ summary: "Get admin settings",
+ description:
+ "Fetch all admin-configurable settings: registration, update checks, and telemetry.",
+ successDescription: "Current admin settings",
+ })
+ .output(AdminSettingsOutput),
+ update: oc
+ .route({
+ method: "PATCH",
+ path: "/admin/settings",
+ tags: ["Admin"],
+ summary: "Update admin settings",
+ description:
+ "Partially update admin settings. Only provided sections are changed; omitted sections keep their current values.",
+ })
+ .input(AdminSettingsUpdateInput)
+ .output(z.void()),
+ },
backups: {
list: oc
.route({
@@ -615,68 +602,6 @@ export const contract = {
.input(UpdateScheduleInput)
.output(z.void()),
},
- registration: oc
- .route({
- method: "GET",
- path: "/admin/registration",
- tags: ["Admin"],
- summary: "Get registration status",
- description: "Check whether new user registration is currently open or closed.",
- successDescription: "Registration open/closed state",
- })
- .output(RegistrationOutput),
- toggleRegistration: oc
- .route({
- method: "PUT",
- path: "/admin/registration",
- tags: ["Admin"],
- summary: "Toggle registration",
- description: "Open or close new user registration.",
- })
- .input(ToggleRegistrationInput)
- .output(z.void()),
- updateCheck: oc
- .route({
- method: "GET",
- path: "/admin/update-check",
- tags: ["Admin"],
- summary: "Get update check status",
- description:
- "Fetch whether automatic update checks are enabled, and the latest cached check result if available.",
- successDescription: "Update check configuration and latest result",
- })
- .output(UpdateCheckOutput),
- toggleUpdateCheck: oc
- .route({
- method: "PUT",
- path: "/admin/update-check",
- tags: ["Admin"],
- summary: "Toggle update checks",
- description: "Enable or disable automatic update checks against the public API.",
- })
- .input(ToggleUpdateCheckInput)
- .output(z.void()),
- telemetry: oc
- .route({
- method: "GET",
- path: "/admin/telemetry",
- tags: ["Admin"],
- summary: "Get telemetry status",
- description:
- "Fetch whether anonymous telemetry is enabled and when the last report was sent.",
- successDescription: "Telemetry configuration and last report time",
- })
- .output(TelemetryOutput),
- toggleTelemetry: oc
- .route({
- method: "PUT",
- path: "/admin/telemetry",
- tags: ["Admin"],
- summary: "Toggle telemetry",
- description: "Enable or disable anonymous telemetry reporting.",
- })
- .input(ToggleTelemetryInput)
- .output(z.void()),
triggerJob: oc
.route({
method: "POST",
@@ -729,72 +654,8 @@ export const contract = {
})
.output(SystemHealthOutput),
},
- account: {
- updateName: oc
- .route({
- method: "PUT",
- path: "/account/name",
- tags: ["Account"],
- summary: "Update display name",
- description: "Change the current user's display name.",
- })
- .input(UpdateNameInput)
- .output(z.void()),
- uploadAvatar: oc
- .route({
- method: "POST",
- path: "/account/avatar",
- tags: ["Account"],
- summary: "Upload avatar",
- description:
- "Upload a new profile avatar image. Accepts JPEG, PNG, WebP, or GIF up to 2 MB.",
- successDescription: "URL of the uploaded avatar image",
- })
- .input(UploadAvatarInput)
- .output(UploadAvatarOutput),
- removeAvatar: oc
- .route({
- method: "DELETE",
- path: "/account/avatar",
- tags: ["Account"],
- summary: "Remove avatar",
- description: "Delete the current user's profile avatar, reverting to the default.",
- })
- .input(z.void())
- .output(z.void()),
- platforms: oc
- .route({
- method: "GET",
- path: "/account/platforms",
- tags: ["Account"],
- summary: "Get user's streaming platforms",
- description: "Fetch the current user's subscribed streaming platform IDs.",
- successDescription: "List of platform IDs",
- })
- .output(UserPlatformsOutput),
- updatePlatforms: oc
- .route({
- method: "PUT",
- path: "/account/platforms",
- tags: ["Account"],
- summary: "Update streaming platforms",
- description: "Set the current user's subscribed streaming platforms.",
- })
- .input(UpdateUserPlatformsInput)
- .output(z.void()),
- },
- platforms: {
- list: oc
- .route({
- method: "GET",
- path: "/platforms",
- tags: ["Platforms"],
- summary: "List all platforms",
- description: "Fetch all available streaming platforms, ordered by popularity.",
- successDescription: "All platforms with metadata",
- })
- .output(PlatformsListOutput),
- },
+
+ // βββ Imports ββββββββββββββββββββββββββββββββββββββββββββββββ
imports: {
parseFile: oc
.route({
diff --git a/packages/api/src/schemas.ts b/packages/api/src/schemas.ts
index 76f8e33..76556a3 100644
--- a/packages/api/src/schemas.ts
+++ b/packages/api/src/schemas.ts
@@ -65,11 +65,23 @@ export const UpdateRatingInput = z
})
.meta({ description: "Set or clear a star rating for a title" });
-export const BatchWatchInput = z
+export const WatchScope = z
+ .enum(["movie", "episode", "season", "series"])
+ .describe("What the IDs refer to: movie title, episode, season, or entire TV series");
+
+export const WatchInput = z
.object({
- episodeIds: z.array(z.string()).min(1).describe("List of episode IDs to mark as watched"),
+ scope: WatchScope,
+ ids: z.array(z.string().min(1)).min(1).describe("IDs to mark as watched"),
})
- .meta({ description: "Batch of episode IDs to mark as watched" });
+ .meta({ description: "Mark one or more items as watched" });
+
+export const UnwatchInput = z
+ .object({
+ scope: WatchScope,
+ ids: z.array(z.string().min(1)).min(1).describe("IDs to unwatch"),
+ })
+ .meta({ description: "Remove watch records for one or more items" });
// βββ Search / Discover inputs ββββββββββββββββββββββββββββββββββ
@@ -138,24 +150,42 @@ export const CreateIntegrationInput = z
// βββ Admin inputs ββββββββββββββββββββββββββββββββββββββββββββββ
-export const ToggleRegistrationInput = z.object({
- open: z.boolean().describe("Whether new user registration is allowed"),
-});
-export const ToggleUpdateCheckInput = z.object({
- enabled: z.boolean().describe("Whether automatic update checks are enabled"),
-});
-export const ToggleTelemetryInput = z.object({
- enabled: z.boolean().describe("Whether anonymous telemetry reporting is enabled"),
-});
-export const TelemetryOutput = z
+export const AdminSettingsOutput = z
.object({
- enabled: z.boolean().describe("Whether telemetry is currently enabled"),
- lastReportedAt: z
- .string()
- .nullable()
- .describe("ISO 8601 timestamp of the last telemetry report, or null if never sent"),
+ registration: z.object({
+ open: z.boolean().describe("Whether new user registration is open"),
+ }),
+ updateCheck: z.object({
+ enabled: z.boolean().describe("Whether automatic update checks are enabled"),
+ updateAvailable: z.boolean().nullable().describe("Whether a newer version is available"),
+ currentVersion: z.string().nullable().describe("Currently running version"),
+ latestVersion: z.string().nullable().describe("Latest available version"),
+ releaseUrl: z.string().nullable().describe("URL to the latest release page"),
+ lastCheckedAt: z.string().nullable().describe("When the last check was performed (ISO 8601)"),
+ }),
+ telemetry: z.object({
+ enabled: z.boolean().describe("Whether anonymous telemetry is enabled"),
+ lastReportedAt: z
+ .string()
+ .nullable()
+ .describe("ISO 8601 timestamp of the last telemetry report"),
+ }),
})
- .meta({ description: "Telemetry configuration and last report time" });
+ .meta({ description: "Combined admin settings for registration, update checks, and telemetry" });
+
+export const AdminSettingsUpdateInput = z
+ .object({
+ registration: z
+ .object({ open: z.boolean().describe("Whether new user registration is allowed") })
+ .optional(),
+ updateCheck: z
+ .object({ enabled: z.boolean().describe("Whether automatic update checks are enabled") })
+ .optional(),
+ telemetry: z
+ .object({ enabled: z.boolean().describe("Whether anonymous telemetry is enabled") })
+ .optional(),
+ })
+ .meta({ description: "Partial update to admin settings" });
const cronJobName = z.enum([
"scheduledBackup",
@@ -507,14 +537,12 @@ export const PersonDetailOutput = z
// βββ Dashboard outputs βββββββββββββββββββββββββββββββββββββββββ
-export const DashboardStatsOutput = z
+export const LibraryStatsOutput = z
.object({
- moviesThisMonth: z.number().describe("Movies watched in the current calendar month"),
- episodesThisWeek: z.number().describe("Episodes watched in the current calendar week"),
- librarySize: z.number().describe("Total titles in the user's library"),
+ size: z.number().describe("Total titles in the user's library"),
completed: z.number().describe("Total titles with completed status"),
})
- .meta({ description: "Aggregate watch statistics for the dashboard" });
+ .meta({ description: "Aggregate library statistics" });
export const ContinueWatchingOutput = z
.object({
@@ -616,27 +644,12 @@ export const LibraryGenresOutput = z
})
.meta({ description: "Genres that exist in the user's library" });
-// βββ Watch providers ββββββββββββββββββββββββββββββββββββββββββ
-
-export const WatchProvidersOutput = z
- .object({
- providers: z.array(
- z.object({
- id: z.string().describe("Platform ID"),
- tmdbProviderIds: z.array(z.number()).describe("TMDB provider IDs"),
- name: z.string().describe("Provider display name"),
- logoPath: z.string().nullable().describe("Provider logo image path"),
- }),
- ),
- })
- .meta({ description: "Available streaming providers for the user's region" });
-
-export const DashboardRecommendationsOutput = z
+export const DiscoverRecommendationsOutput = z
.object({
items: z.array(RecommendationItemSchema),
})
.meta({
- description: "Personalized title recommendations for the dashboard",
+ description: "Personalized title recommendations based on the user's library",
});
// βββ Upcoming outputs βββββββββββββββββββββββββββββββββββββββββ
@@ -965,34 +978,6 @@ export const BackupScheduleOutput = z
})
.meta({ description: "Automated backup schedule configuration" });
-export const RegistrationOutput = z
- .object({
- open: z.boolean().describe("Whether new user registration is open"),
- })
- .meta({ description: "User registration status" });
-
-const UpdateCheckResultSchema = z
- .object({
- updateAvailable: z.boolean().describe("Whether a newer version is available"),
- currentVersion: z.string().describe("Currently running version"),
- latestVersion: z
- .string()
- .nullable()
- .describe("Latest available version, or null if check failed"),
- releaseUrl: z.string().nullable().describe("URL to the latest release page"),
- lastCheckedAt: z.string().nullable().describe("When the last check was performed (ISO 8601)"),
- })
- .meta({ description: "Result of an update availability check" });
-
-export const UpdateCheckOutput = z
- .object({
- enabled: z.boolean().describe("Whether automatic update checks are enabled"),
- updateCheck: UpdateCheckResultSchema.nullable().describe(
- "Latest check result, or null if checks are disabled or never ran",
- ),
- })
- .meta({ description: "Update check configuration and latest result" });
-
export const TriggerJobOutput = z.object({
ok: z.literal(true).describe("Always true on success"),
});
@@ -1013,17 +998,6 @@ export const PurgeImageCacheOutput = z
})
.meta({ description: "Result of purging the image cache from disk" });
-// βββ Quick add output ββββββββββββββββββββββββββββββββββββββββββ
-
-export const QuickAddOutput = z
- .object({
- id: z.string().describe("Internal title ID"),
- alreadyAdded: z.boolean().describe("True if the title was already in the user's library"),
- })
- .meta({
- description: "Result of a quick-add operation",
- });
-
// βββ System outputs βββββββββββββββββββββββββββββββββββββββββββ
export const PublicInfoOutput = z
@@ -1033,24 +1007,15 @@ export const PublicInfoOutput = z
userCount: z.number().describe("Number of registered users"),
registrationOpen: z.boolean().describe("Whether new user registration is open"),
posterUrls: z.array(z.string()).describe("Poster image URLs for the login screen collage"),
- })
- .meta({
- description: "Public instance information shown on the login/setup screen",
- });
-
-export const AuthConfigOutput = z
- .object({
oidcEnabled: z.boolean().describe("Whether OIDC/SSO login is available"),
oidcProviderName: z
.string()
.nullable()
.describe("Display name of the OIDC provider (e.g. Authelia, Keycloak)"),
passwordLoginDisabled: z.boolean().describe("Whether password-based login is disabled"),
- registrationOpen: z.boolean().describe("Whether new user registration is open"),
- userCount: z.number().describe("Number of registered users"),
})
.meta({
- description: "Authentication provider configuration",
+ description: "Public instance information and authentication configuration",
});
// βββ Imports ββββββββββββββββββββββββββββββββββββββββββββββββββ
@@ -1247,7 +1212,7 @@ export type BackupInfo = z.infer
;
export type CastMember = z.infer;
export type ColorPalette = z.infer;
export type CronJobName = z.infer;
-export type DashboardStats = z.infer;
+export type LibraryStats = z.infer;
export type Episode = z.infer;
export type HistoryBucket = z.infer;
export type PersonCredit = z.infer;
@@ -1261,4 +1226,5 @@ export type TimePeriod = z.infer["period"];
export type ImportJob = z.infer;
export type NormalizedImport = z.infer;
export type UpcomingItem = z.infer;
-export type UpdateCheckResult = z.infer;
+export type AdminSettings = z.infer;
+export type WatchScopeType = z.infer;
diff --git a/packages/core/src/tracking.ts b/packages/core/src/tracking.ts
index 60aa448..3300c79 100644
--- a/packages/core/src/tracking.ts
+++ b/packages/core/src/tracking.ts
@@ -4,8 +4,10 @@ import {
batchInsertEpisodeWatchesTransaction,
batchInsertMissingEpisodeWatches,
countDistinctEpisodeWatches,
+ deleteAllEpisodeWatchesForTitle,
deleteEpisodeWatch,
deleteEpisodeWatches,
+ deleteMovieWatches,
deleteRating,
deleteTitleStatus,
getAllEpisodeIdsForTitle,
@@ -157,6 +159,24 @@ export function unwatchSeason(userId: string, seasonId: string) {
}
}
+export function unwatchMovie(userId: string, titleId: string) {
+ deleteMovieWatches(userId, titleId);
+
+ const existing = getTitleStatus(userId, titleId);
+ if (existing && existing.status !== "watchlist") {
+ setTitleStatus(userId, titleId, "watchlist");
+ }
+}
+
+export function unwatchSeries(userId: string, titleId: string) {
+ deleteAllEpisodeWatchesForTitle(userId, titleId);
+
+ const existing = getTitleStatus(userId, titleId);
+ if (existing && existing.status !== "watchlist") {
+ setTitleStatus(userId, titleId, "watchlist");
+ }
+}
+
export function rateTitleStars(
userId: string,
titleId: string,
diff --git a/packages/core/test/tracking.test.ts b/packages/core/test/tracking.test.ts
index 42d8506..5142d62 100644
--- a/packages/core/test/tracking.test.ts
+++ b/packages/core/test/tracking.test.ts
@@ -26,7 +26,9 @@ import {
removeTitleStatus,
setTitleStatus,
unwatchEpisode,
+ unwatchMovie,
unwatchSeason,
+ unwatchSeries,
} from "../src/tracking";
beforeEach(() => {
@@ -495,6 +497,93 @@ describe("unwatchSeason", () => {
// ββ rateTitleStars ββββββββββββββββββββββββββββββββββββββββββββββββββ
+// ββ unwatchMovie βββββββββββββββββββββββββββββββββββββββββββββββββββββ
+
+describe("unwatchMovie", () => {
+ test("removes movie watch records and reverts completed to watchlist", () => {
+ insertUser();
+ insertTitle();
+ logMovieWatch("user-1", "title-1");
+
+ // Verify completed status
+ const before = testDb
+ .select()
+ .from(userTitleStatus)
+ .where(and(eq(userTitleStatus.userId, "user-1"), eq(userTitleStatus.titleId, "title-1")))
+ .get();
+ expect(before?.status).toBe("completed");
+
+ unwatchMovie("user-1", "title-1");
+
+ // Watch records removed
+ const watches = testDb
+ .select()
+ .from(userMovieWatches)
+ .where(and(eq(userMovieWatches.userId, "user-1"), eq(userMovieWatches.titleId, "title-1")))
+ .all();
+ expect(watches).toHaveLength(0);
+
+ // Status reverted to watchlist
+ const after = testDb
+ .select()
+ .from(userTitleStatus)
+ .where(and(eq(userTitleStatus.userId, "user-1"), eq(userTitleStatus.titleId, "title-1")))
+ .get();
+ expect(after?.status).toBe("watchlist");
+ });
+
+ test("no-op when movie was not watched", () => {
+ insertUser();
+ insertTitle();
+ setTitleStatus("user-1", "title-1", "watchlist");
+
+ unwatchMovie("user-1", "title-1");
+
+ const row = testDb
+ .select()
+ .from(userTitleStatus)
+ .where(and(eq(userTitleStatus.userId, "user-1"), eq(userTitleStatus.titleId, "title-1")))
+ .get();
+ expect(row?.status).toBe("watchlist");
+ });
+});
+
+// ββ unwatchSeries ββββββββββββββββββββββββββββββββββββββββββββββββββββ
+
+describe("unwatchSeries", () => {
+ test("removes all episode watches and reverts status to watchlist", () => {
+ const { episodeIds } = insertTvShow("tv-1");
+ insertUser();
+ logEpisodeWatch("user-1", episodeIds[0]);
+ logEpisodeWatch("user-1", episodeIds[1]);
+
+ const before = testDb
+ .select()
+ .from(userEpisodeWatches)
+ .where(eq(userEpisodeWatches.userId, "user-1"))
+ .all();
+ expect(before.length).toBeGreaterThan(0);
+
+ unwatchSeries("user-1", "tv-1");
+
+ const after = testDb
+ .select()
+ .from(userEpisodeWatches)
+ .where(eq(userEpisodeWatches.userId, "user-1"))
+ .all();
+ expect(after).toHaveLength(0);
+
+ const statusRow = testDb
+ .select()
+ .from(userTitleStatus)
+ .where(and(eq(userTitleStatus.userId, "user-1"), eq(userTitleStatus.titleId, "tv-1")))
+ .get();
+ expect(statusRow?.status).toBe("watchlist");
+ });
+});
+
+// ββ rateTitleStars βββββββββββββββββββββββββββββββββββββββββββββββββββ
+
describe("rateTitleStars", () => {
test("sets rating", () => {
insertUser();
diff --git a/packages/db/src/queries/tracking.ts b/packages/db/src/queries/tracking.ts
index bf92102..a341590 100644
--- a/packages/db/src/queries/tracking.ts
+++ b/packages/db/src/queries/tracking.ts
@@ -315,3 +315,22 @@ export function deleteEpisodeWatch(userId: string, episodeId: string): void {
.where(and(eq(userEpisodeWatches.userId, userId), eq(userEpisodeWatches.episodeId, episodeId)))
.run();
}
+
+export function deleteMovieWatches(userId: string, titleId: string): void {
+ db.delete(userMovieWatches)
+ .where(and(eq(userMovieWatches.userId, userId), eq(userMovieWatches.titleId, titleId)))
+ .run();
+}
+
+export function deleteAllEpisodeWatchesForTitle(userId: string, titleId: string): void {
+ const titleEpisodeIds = db
+ .select({ id: episodes.id })
+ .from(episodes)
+ .innerJoin(seasons, eq(episodes.seasonId, seasons.id))
+ .where(eq(seasons.titleId, titleId))
+ .all()
+ .map((r) => r.id);
+
+ if (titleEpisodeIds.length === 0) return;
+ deleteEpisodeWatches(userId, titleEpisodeIds);
+}