diff --git a/.oxfmtrc.json b/.oxfmtrc.json new file mode 100644 index 0000000..46c6c09 --- /dev/null +++ b/.oxfmtrc.json @@ -0,0 +1,10 @@ +{ + "$schema": "./node_modules/oxfmt/configuration_schema.json", + "printWidth": 100, + "tabWidth": 2, + "useTabs": false, + "semi": true, + "singleQuote": false, + "trailingComma": "all", + "ignorePatterns": ["dist/**", "node_modules/**", "convex/_generated/**"] +} diff --git a/.oxlintrc.json b/.oxlintrc.json new file mode 100644 index 0000000..11ddd35 --- /dev/null +++ b/.oxlintrc.json @@ -0,0 +1,15 @@ +{ + "$schema": "./node_modules/oxlint/configuration_schema.json", + "plugins": [ + "unicorn", + "typescript", + "oxc", + "react", + "jsx-a11y", + "react-perf", + "promise", + "node", + "vitest" + ], + "ignorePatterns": ["dist/**", "node_modules/**", "convex/_generated/**"] +} diff --git a/bun.lock b/bun.lock index fd8af6d..c6d44c2 100644 --- a/bun.lock +++ b/bun.lock @@ -16,25 +16,31 @@ "@tanstack/react-router": "^1.170.16", "ai": "7.0.0-beta.184", "convex": "^1.41.0", - "react": "^19.2.7", - "react-dom": "^19.2.7", + "react": "19.2.8", + "react-dom": "19.2.8", "shiki": "^4.2.0", "zod": "^4.4.3", }, "devDependencies": { + "@babel/core": "^8.0.1", "@edge-runtime/vm": "^5.0.0", + "@rolldown/plugin-babel": "^0.2.3", "@tailwindcss/vite": "^4.3.1", + "@types/babel__core": "^7.20.5", "@types/bun": "^1.3.14", "@types/node": "^26.0.0", "@types/react": "^19.2.17", "@types/react-dom": "^19.2.3", - "@vitejs/plugin-react": "^6.0.3", + "@vitejs/plugin-react": "6.0.5", + "babel-plugin-react-compiler": "1.0.0", "convex-test": "^0.0.54", "npm-run-all": "^4.1.5", + "oxfmt": "0.62.0", + "oxlint": "1.77.0", "react-scan": "^0.5.7", "tailwindcss": "^4.3.1", "typescript": "^6.0.3", - "vite": "^8.1.0", + "vite": "8.2.0", "vitest": "^4.1.10", }, }, @@ -58,37 +64,37 @@ "@apm-js-collab/tracing-hooks": ["@apm-js-collab/tracing-hooks@0.13.0", "", { "dependencies": { "@apm-js-collab/code-transformer": "^0.18.0", "debug": "^4.4.1", "module-details-from-path": "^1.0.4" } }, "sha512-mTvWz9rnQwx1U3h0XPTHaX7bgfkpipLLTQyjlC2cdhQpQEuoLT0AGzoydeoq2NxfEVv6fWOOETcSbb2nptleyw=="], - "@babel/code-frame": ["@babel/code-frame@7.29.7", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.29.7", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw=="], + "@babel/code-frame": ["@babel/code-frame@8.0.0", "", { "dependencies": { "@babel/helper-validator-identifier": "^8.0.0", "js-tokens": "^10.0.0" } }, "sha512-dYYg153EyN2Ekbqw2zAsbd6/JR+9N2SEoC7YV2GyyqMM7x9bLDTjBD6XBhSMLH0wtIVyJj03jWNriQhaN+eoCw=="], - "@babel/compat-data": ["@babel/compat-data@7.29.7", "", {}, "sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg=="], + "@babel/compat-data": ["@babel/compat-data@8.0.0", "", {}, "sha512-DOjnob/cXOUgDOozCDeq/aK2p5y8dUIVdf6tNhEV1HQRd6I8aQ4f4fbtHRVEvb6lP3BGomrKHiS8ICAASSVQSw=="], - "@babel/core": ["@babel/core@7.29.7", "", { "dependencies": { "@babel/code-frame": "^7.29.7", "@babel/generator": "^7.29.7", "@babel/helper-compilation-targets": "^7.29.7", "@babel/helper-module-transforms": "^7.29.7", "@babel/helpers": "^7.29.7", "@babel/parser": "^7.29.7", "@babel/template": "^7.29.7", "@babel/traverse": "^7.29.7", "@babel/types": "^7.29.7", "@jridgewell/remapping": "^2.3.5", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", "json5": "^2.2.3", "semver": "^6.3.1" } }, "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA=="], + "@babel/core": ["@babel/core@8.0.1", "", { "dependencies": { "@babel/code-frame": "^8.0.0", "@babel/generator": "^8.0.0", "@babel/helper-compilation-targets": "^8.0.0", "@babel/helpers": "^8.0.0", "@babel/parser": "^8.0.0", "@babel/template": "^8.0.0", "@babel/traverse": "^8.0.0", "@babel/types": "^8.0.0", "@types/gensync": "^1.0.5", "convert-source-map": "^2.0.0", "empathic": "^2.0.1", "gensync": "^1.0.0-beta.2", "import-meta-resolve": "^4.2.0", "json5": "^2.2.3", "obug": "^2.1.1", "semver": "^7.7.3" } }, "sha512-5FgxM4dLQpMJHSiVATk8foW263dVHQHBVpXYiimNECVWG01f4nFyEbQixeT6Mwvg7TayREJ2gpKl3o2RoMdnqw=="], - "@babel/generator": ["@babel/generator@7.29.7", "", { "dependencies": { "@babel/parser": "^7.29.7", "@babel/types": "^7.29.7", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ=="], + "@babel/generator": ["@babel/generator@8.0.0", "", { "dependencies": { "@babel/parser": "^8.0.0", "@babel/types": "^8.0.0", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "@types/jsesc": "^2.5.0", "jsesc": "^3.0.2" } }, "sha512-NT9NrVwJsbSV6Y2FSstWa71EETOnzrjkL5/wX3D2mYHtKM+qvqB1DvR4D0Setb/gDBsHzRICifwEWMO8CnTF6g=="], - "@babel/helper-compilation-targets": ["@babel/helper-compilation-targets@7.29.7", "", { "dependencies": { "@babel/compat-data": "^7.29.7", "@babel/helper-validator-option": "^7.29.7", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" } }, "sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g=="], + "@babel/helper-compilation-targets": ["@babel/helper-compilation-targets@8.0.0", "", { "dependencies": { "@babel/compat-data": "^8.0.0", "@babel/helper-validator-option": "^8.0.0", "browserslist": "^4.24.0", "lru-cache": "^11.0.0", "semver": "^7.7.3" } }, "sha512-JwculLABZvyPvyLBpwU/E/IbH2uM3mnxNtIJpxnIfb24y1PrdVxK5Dqjle4DpgqpGRnwgC7G8IkzPdSXZrO1Ew=="], - "@babel/helper-globals": ["@babel/helper-globals@7.29.7", "", {}, "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA=="], + "@babel/helper-globals": ["@babel/helper-globals@8.0.0", "", {}, "sha512-lLozHOM6sWWlxNo8CYqHy4MBZeTvHXNgVPBfPOGsjPKUzHC2Az9QwB6gxdQmpwHl6GlQtbGgS+lj5887guDiLw=="], "@babel/helper-module-imports": ["@babel/helper-module-imports@7.29.7", "", { "dependencies": { "@babel/traverse": "^7.29.7", "@babel/types": "^7.29.7" } }, "sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g=="], "@babel/helper-module-transforms": ["@babel/helper-module-transforms@7.29.7", "", { "dependencies": { "@babel/helper-module-imports": "^7.29.7", "@babel/helper-validator-identifier": "^7.29.7", "@babel/traverse": "^7.29.7" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg=="], - "@babel/helper-string-parser": ["@babel/helper-string-parser@7.29.7", "", {}, "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw=="], + "@babel/helper-string-parser": ["@babel/helper-string-parser@8.0.0", "", {}, "sha512-6mJgmFFFIIO82vvoLt9XtRC7/TkzXfts1t/SpRX4IHSzMgqoPYCWesVu1udUPUWioAE/2fcG6WuI8zrkE1gwrg=="], - "@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.29.7", "", {}, "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg=="], + "@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@8.0.4", "", {}, "sha512-4wFaiLd0bVo4cIoTXI3zKI038NIWE/cr3jvBjejOVYVxV/m8Ltav1USiGzG1fmS5J2RhgEOgXNNK46cRPnRsrg=="], - "@babel/helper-validator-option": ["@babel/helper-validator-option@7.29.7", "", {}, "sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw=="], + "@babel/helper-validator-option": ["@babel/helper-validator-option@8.0.0", "", {}, "sha512-U4Dybxh4WESWHt5XhBeExi4DrY0/DNK1aHpQbsrQXCUbFHuMweT0TpLEWKvaraV2Y6fS+ZXunsZ8zIuZIgvF2Q=="], - "@babel/helpers": ["@babel/helpers@7.29.7", "", { "dependencies": { "@babel/template": "^7.29.7", "@babel/types": "^7.29.7" } }, "sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg=="], + "@babel/helpers": ["@babel/helpers@8.0.0", "", { "dependencies": { "@babel/template": "^8.0.0", "@babel/types": "^8.0.0" } }, "sha512-wfbi91pM3py96oIiJEz7qIpyXDytgr9zQC1HEWwlGNVRAEmItuU/0a41ZUKu1sJGyhhOIpc4t5vk4PYzt8wpsg=="], - "@babel/parser": ["@babel/parser@7.29.7", "", { "dependencies": { "@babel/types": "^7.29.7" }, "bin": "./bin/babel-parser.js" }, "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg=="], + "@babel/parser": ["@babel/parser@8.0.4", "", { "dependencies": { "@babel/types": "^8.0.4" }, "bin": "./bin/babel-parser.js" }, "sha512-srpptsAkEbbNIC/q8nT7o+m6CQe8CJUTV/t7MYc9NnWlgYVtHOb7JH6SorxMhN0kuRJjVqXbKClG6xSbPtzz+g=="], - "@babel/template": ["@babel/template@7.29.7", "", { "dependencies": { "@babel/code-frame": "^7.29.7", "@babel/parser": "^7.29.7", "@babel/types": "^7.29.7" } }, "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg=="], + "@babel/template": ["@babel/template@8.0.0", "", { "dependencies": { "@babel/code-frame": "^8.0.0", "@babel/parser": "^8.0.0", "@babel/types": "^8.0.0" } }, "sha512-eAD0QW/AlbamBbw0FeGiwasbCVPq5ncW0HNVyLP3B9czqLyh4gvw+5JTSNt6le9+ziAU7mqDZsKTHf3jTb4chQ=="], - "@babel/traverse": ["@babel/traverse@7.29.7", "", { "dependencies": { "@babel/code-frame": "^7.29.7", "@babel/generator": "^7.29.7", "@babel/helper-globals": "^7.29.7", "@babel/parser": "^7.29.7", "@babel/template": "^7.29.7", "@babel/types": "^7.29.7", "debug": "^4.3.1" } }, "sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw=="], + "@babel/traverse": ["@babel/traverse@8.0.4", "", { "dependencies": { "@babel/code-frame": "^8.0.0", "@babel/generator": "^8.0.0", "@babel/helper-globals": "^8.0.0", "@babel/parser": "^8.0.4", "@babel/template": "^8.0.0", "@babel/types": "^8.0.4", "obug": "^2.1.1" } }, "sha512-bZnmqzGG8UZneG1lLxBoWIH0G6Gr1D846Yu4/3XnY6FhCndMR49u26nTY08u/dAxWmLWF9vGQOuC+84FfIUoeg=="], - "@babel/types": ["@babel/types@7.29.7", "", { "dependencies": { "@babel/helper-string-parser": "^7.29.7", "@babel/helper-validator-identifier": "^7.29.7" } }, "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA=="], + "@babel/types": ["@babel/types@8.0.4", "", { "dependencies": { "@babel/helper-string-parser": "^8.0.0", "@babel/helper-validator-identifier": "^8.0.4" } }, "sha512-eY+Yn3dCqTGmyiq2QRU66lA5FL8lqqqvecHt0fF3uHONIa7ToYsaCiWV8lOKqAs0Rb2SjixiKFROngnulPtt2g=="], "@clerk/clerk-react": ["@clerk/clerk-react@5.61.3", "", { "dependencies": { "@clerk/shared": "^3.47.2", "tslib": "2.8.1" }, "peerDependencies": { "react": "^18.0.0 || ~19.0.3 || ~19.1.4 || ~19.2.3 || ~19.3.0-0", "react-dom": "^18.0.0 || ~19.0.3 || ~19.1.4 || ~19.2.3 || ~19.3.0-0" } }, "sha512-W21aNEeHtqh3xJLuW5g2ydben/1D5pSnxsl/kCnv0IY1zma7lO+aIJ7Br2bR4FKKkiu695mPnjtY+fvkQmCXBg=="], @@ -100,9 +106,9 @@ "@edge-runtime/vm": ["@edge-runtime/vm@5.0.0", "", { "dependencies": { "@edge-runtime/primitives": "6.0.0" } }, "sha512-NKBGBSIKUG584qrS1tyxVpX/AKJKQw5HgjYEnPLC0QsTw79JrGn+qUr8CXFb955Iy7GUdiiUv1rJ6JBGvaKb6w=="], - "@emnapi/core": ["@emnapi/core@1.11.1", "", { "dependencies": { "@emnapi/wasi-threads": "1.2.2", "tslib": "^2.4.0" } }, "sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ=="], + "@emnapi/core": ["@emnapi/core@1.11.2", "", { "dependencies": { "@emnapi/wasi-threads": "1.2.2", "tslib": "^2.4.0" } }, "sha512-TC8MkTuZUtcTSiFeuC0ksCh9QIJ5+F21MvZ4Wn4ORfYaFJ/0dsiudv5tVkejgwZlwQ39jL9WWDe2lz8x0WglOA=="], - "@emnapi/runtime": ["@emnapi/runtime@1.11.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw=="], + "@emnapi/runtime": ["@emnapi/runtime@1.11.2", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-kyOl3X0DuTiT1h2ft8r2fYO8JYtU9a9Xis/zBSiGArNaagCOWx90N1k2wxp18czFDH+OgcWGb5ZP/XMt3dcyPA=="], "@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.2.2", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA=="], @@ -196,7 +202,7 @@ "@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="], - "@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.1.5", "", { "dependencies": { "@tybys/wasm-util": "^0.10.2" }, "peerDependencies": { "@emnapi/core": "^1.7.1", "@emnapi/runtime": "^1.7.1" } }, "sha512-AWPoBRJ9tsnVhor4sjO7rkni+7p+2IAEFj6cx06UgP10jkQHqay/36uRV/bFkgrh18D9vb4cr8Q0Pthskgzy+Q=="], + "@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.2.0", "", { "dependencies": { "@tybys/wasm-util": "^0.10.3" }, "peerDependencies": { "@emnapi/core": "^2.0.0-alpha.3", "@emnapi/runtime": "^2.0.0-alpha.3" } }, "sha512-kDoONqMa+VnZ4vvvu/ZUurpJ4gkZU57e7g69qpNgWhYcZFPUHZM2CEMKm+cG6ufDVALbjMvfmMjFVqaK7uEMnA=="], "@nodelib/fs.scandir": ["@nodelib/fs.scandir@2.1.5", "", { "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g=="], @@ -260,7 +266,7 @@ "@oxc-parser/binding-win32-x64-msvc": ["@oxc-parser/binding-win32-x64-msvc@0.141.0", "", { "os": "win32", "cpu": "x64" }, "sha512-HI/wsvbWT5RHHw5c37D0fEgeTd8/1Q4OJs5jUmEBc17VZFG6SsCIe4barq7NsAPPks/JW+3ayi3Rp+PQI5h4Kg=="], - "@oxc-project/types": ["@oxc-project/types@0.137.0", "", {}, "sha512-WT+Gb24i8hmvo85AIv2oEYouEXkRlKAlT9WaCa3TfLgNCN+GhrJOGZuIlMouAh38Qe4QOx26eUOVsq70qXrywA=="], + "@oxc-project/types": ["@oxc-project/types@0.142.0", "", {}, "sha512-7W+2q5AKQVU36fkaryontrHn3YDt1RyUYXatw9i5H8ocYe2sPKSFB6eS8WNPeRKiN1qAWWZUPm7gwFzJGrccqQ=="], "@oxc-resolver/binding-android-arm-eabi": ["@oxc-resolver/binding-android-arm-eabi@11.24.2", "", { "os": "android", "cpu": "arm" }, "sha512-y09e0L0SRI2OA2tUIrjBgoV3eH5hvUKXNkJqXmNo5V2WxIjyC7I7aJfRLMEVpA8yi95f90gFDvO0VMgrDw+vwA=="], @@ -300,43 +306,81 @@ "@oxc-resolver/binding-win32-x64-msvc": ["@oxc-resolver/binding-win32-x64-msvc@11.24.2", "", { "os": "win32", "cpu": "x64" }, "sha512-UqGPmo56KDfLlfXFAFIrNflHT8tFxWGEivWg3Zeyp4Uy2NlKN1FGPr6/BxcLGG3+kZ6Wp14g5Uj+n71boqZfiw=="], - "@oxlint/binding-android-arm-eabi": ["@oxlint/binding-android-arm-eabi@1.74.0", "", { "os": "android", "cpu": "arm" }, "sha512-+gHd12muVI9ZLBaWLPkHt3Fj7jihFjgQ1MGtBaRL8vWrWrI0P7dLUty/cHrHS0oqPYIRgQUJsPu2CExQuMcwNw=="], + "@oxfmt/binding-android-arm-eabi": ["@oxfmt/binding-android-arm-eabi@0.62.0", "", { "os": "android", "cpu": "arm" }, "sha512-pdsv0C4gPjJ8H1+sd8u0BDx+yLACTL+rgeMIOL1ln4ihSnhw8CWXtYWgvcSkyTfgGBIzFKab+d8rx9Xl4en/Kw=="], - "@oxlint/binding-android-arm64": ["@oxlint/binding-android-arm64@1.74.0", "", { "os": "android", "cpu": "arm64" }, "sha512-xjKdoMB+H+RCOByv/7l7nfIGW9mlOisqYdcyC75UqYuQecLpReAeEYUf2CNeDEI3KtmUgxpRw/+c63y4AeF/Bw=="], + "@oxfmt/binding-android-arm64": ["@oxfmt/binding-android-arm64@0.62.0", "", { "os": "android", "cpu": "arm64" }, "sha512-WC3YQ7uS/KtDrjmqwBviwFKe9qeoi+eXx8aX1z/ffG23Md75myjrJaQqTuJvdOLPoa4EYTjDWH0dHXfwulCVog=="], - "@oxlint/binding-darwin-arm64": ["@oxlint/binding-darwin-arm64@1.74.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-iUK7wvc6sejMKsC+Pt67mntoF5weFcyEunhZfLJceU6gL419mexz5wBkSx/EnkFBExMLNtOi9fnDSc5xfK0IzQ=="], + "@oxfmt/binding-darwin-arm64": ["@oxfmt/binding-darwin-arm64@0.62.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-GM8Yf3LjjaR1I8PD0SfeoIlwhsh9GvSF+cQ8sf624Yxnjsyumn95aFzYfKJVefblfDIiOAnZ7QVm2sa21Er/0Q=="], - "@oxlint/binding-darwin-x64": ["@oxlint/binding-darwin-x64@1.74.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-ggKc/tn5SJ1u2yG2izC6VKODfYKV8MQ2AicJlNzOjuyrC29udvOef6/JzK2r32xqCnBDLFouR1VCkjzEI0/N9Q=="], + "@oxfmt/binding-darwin-x64": ["@oxfmt/binding-darwin-x64@0.62.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-d5THp7F8bCxLqNogEXDORRsQD6dosf3EyFtnXfBer6v+8tGdcWIjoDX9WaXrrF/26zOmL8qHpPTKCEvpBDmZkQ=="], - "@oxlint/binding-freebsd-x64": ["@oxlint/binding-freebsd-x64@1.74.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-u++dH/43jy9hTLbneaWlS0gla/Bp1JdwJ2zgevCl8nDFUh6qRCGMxcL0f0lb7By3A9p/LfFr+7cG4HU1hG856g=="], + "@oxfmt/binding-freebsd-x64": ["@oxfmt/binding-freebsd-x64@0.62.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-1DnrtXGZooOZ0fHgAXZUaDQzBVh1CM2MNW4oBXyQ2aWKvCHjyljvT9fgBkOM0fEOb96X5eqtcfJ0YUVt9jj66g=="], - "@oxlint/binding-linux-arm-gnueabihf": ["@oxlint/binding-linux-arm-gnueabihf@1.74.0", "", { "os": "linux", "cpu": "arm" }, "sha512-Sj1zmtFDVTPeIbIz4ZfcXAbFHqCmKCXdCUlAJzvTF7I20NTH1RDpoF2PhkqNODutJzVhJYmm3oz0GwgY+tvE2g=="], + "@oxfmt/binding-linux-arm-gnueabihf": ["@oxfmt/binding-linux-arm-gnueabihf@0.62.0", "", { "os": "linux", "cpu": "arm" }, "sha512-4pQDHOYRH+Huqe0StIaWyvk2CVl/aTaqSrbZpA3/pLS2xH24ME7lBgYprhQF2fRkHBzhGGGKliwxFsDdHwx59g=="], - "@oxlint/binding-linux-arm-musleabihf": ["@oxlint/binding-linux-arm-musleabihf@1.74.0", "", { "os": "linux", "cpu": "arm" }, "sha512-//PKyQb/tQXcHArx2f7z+oVI/eMS2Jpv+edNuAtOrgIhWdGcpHxogveAxzmF2rpH1AIHp4Hq04RF/rgJdiICnQ=="], + "@oxfmt/binding-linux-arm-musleabihf": ["@oxfmt/binding-linux-arm-musleabihf@0.62.0", "", { "os": "linux", "cpu": "arm" }, "sha512-X0jAaZJFMCVKhB6YyWVTQ/wN2DLsBcZKSMqTS76bF6riT+XZdtg2FPEdjDvdVbunO9cG+tWiVaEs4Zs38lxYog=="], - "@oxlint/binding-linux-arm64-gnu": ["@oxlint/binding-linux-arm64-gnu@1.74.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-/k1Me+aX2tjuH10K62mLS0y8cLkJBHX6Ce0xPK+eWeel4bSdEGZ8dv4+hYMzg0GrSmjwy4yAYsDPeEeKBft/2w=="], + "@oxfmt/binding-linux-arm64-gnu": ["@oxfmt/binding-linux-arm64-gnu@0.62.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-682Z8T5s8T5ATArYtsejKvbIfd8LEAXyyDkKkoZVq8HND7Vx8TYLlrDjDSeYfodMeVwHOgkj13lJYR8cj6vUSg=="], - "@oxlint/binding-linux-arm64-musl": ["@oxlint/binding-linux-arm64-musl@1.74.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-3tFSjBxc5D8/zvjEuLvOqcA8ZXKD0+6NuaVO/edeamNc49MoAsbfaC9s1UiwODwgF6slGaF8yJA2TPkukd77tg=="], + "@oxfmt/binding-linux-arm64-musl": ["@oxfmt/binding-linux-arm64-musl@0.62.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-lk25fAl7KWaLWVJcW0CHEXB7QlQZtx5eDkjpaGMK0hzXTjUe0Wmlu8IKuFHoviSOcEJedRTs4VE/506VqGxGew=="], - "@oxlint/binding-linux-ppc64-gnu": ["@oxlint/binding-linux-ppc64-gnu@1.74.0", "", { "os": "linux", "cpu": "ppc64" }, "sha512-9QggtPkSPXOCTu8Szis7auOK/sC7KdQaN+/TujP7YVVhzCAOhgdRfgv8uEz0r2tk5xdgus5rLYUrCDoZNtiRUw=="], + "@oxfmt/binding-linux-ppc64-gnu": ["@oxfmt/binding-linux-ppc64-gnu@0.62.0", "", { "os": "linux", "cpu": "ppc64" }, "sha512-SFyNqHQLwySceWNLhiSldx7wPXRAzP0L0WcW9GegP3uWrpZGJiZlQO85NbHAFPEfxR9PhZ9qSnZryEh7+v+4Gw=="], - "@oxlint/binding-linux-riscv64-gnu": ["@oxlint/binding-linux-riscv64-gnu@1.74.0", "", { "os": "linux", "cpu": "none" }, "sha512-VM5VPUJ4DJIWiK+AZn8FScUqMr6OFrCAYybMYjEEi7W13ParI64MByiXTkKMqZpBmvQ9zxl9Ebq2VUOiZRJYUg=="], + "@oxfmt/binding-linux-riscv64-gnu": ["@oxfmt/binding-linux-riscv64-gnu@0.62.0", "", { "os": "linux", "cpu": "none" }, "sha512-KYj55C1ywJfHo6+aKDuEmUtVEdJALsC5GwayDGsI6FGz2GxFqNr/mA8nxVsNbJzm7sE5MRqTQ9ziImSzhYXysA=="], - "@oxlint/binding-linux-riscv64-musl": ["@oxlint/binding-linux-riscv64-musl@1.74.0", "", { "os": "linux", "cpu": "none" }, "sha512-SaDY1gh9rOA592J54g+gu5hkOFFQBZsMmIYHs+NRHG+Uq0OxtuuCXMWQ3vu1830Eugv5uMXyjG+bv2Z9y4IXjw=="], + "@oxfmt/binding-linux-riscv64-musl": ["@oxfmt/binding-linux-riscv64-musl@0.62.0", "", { "os": "linux", "cpu": "none" }, "sha512-BhZDNo5GOU5nC378RhD0/XpvaEBHsH3HLgJp8YZX3A0InC7oivzA63HsRmiXFLtLSHAstEVrDf6fbC7Rs8Jh/A=="], - "@oxlint/binding-linux-s390x-gnu": ["@oxlint/binding-linux-s390x-gnu@1.74.0", "", { "os": "linux", "cpu": "s390x" }, "sha512-ZATQeHZCyr6MbDveg0obD5sxLHFOghtOdC5jwVwYlvFWqtFOxctgFEG6Ef/64hYvZrWyhyCckB10AelqLopeDA=="], + "@oxfmt/binding-linux-s390x-gnu": ["@oxfmt/binding-linux-s390x-gnu@0.62.0", "", { "os": "linux", "cpu": "s390x" }, "sha512-UyAFmyHkgSgUJ/wOM4p3U8AC2yAFvRH5PNBs7TnK0fObTT/XSWcdr/lAzPSWaekHaZFaMeFZyk9n93Joq3J93A=="], - "@oxlint/binding-linux-x64-gnu": ["@oxlint/binding-linux-x64-gnu@1.74.0", "", { "os": "linux", "cpu": "x64" }, "sha512-+aIvJyrdeD7LwCQ2WYLMUWNmnbeDRSPb40aBYtPjD9+PTqUwgJnk+HK5yLfSMeqXrMrDhE9uTmtt2y50tvjhHw=="], + "@oxfmt/binding-linux-x64-gnu": ["@oxfmt/binding-linux-x64-gnu@0.62.0", "", { "os": "linux", "cpu": "x64" }, "sha512-1iYMP0leytWazFubD/WnINJuIrzRPuoL1aWEJdlGezEzDbTxcd29R4r8IUzP2oWeKst5V02uMJgR2NILlPlG6w=="], - "@oxlint/binding-linux-x64-musl": ["@oxlint/binding-linux-x64-musl@1.74.0", "", { "os": "linux", "cpu": "x64" }, "sha512-XyktaR8lhK2qWiCK0Tk8oYD+/cgn+oHA6ddRnxSSXUKkkojkV78CmShZUxQF+yrBFs0SuW+JBOPG6hecyc/iZg=="], + "@oxfmt/binding-linux-x64-musl": ["@oxfmt/binding-linux-x64-musl@0.62.0", "", { "os": "linux", "cpu": "x64" }, "sha512-4rA/URtJSTVNVAQz6Q8wf7SaRvOXVy+TizriT9hs/Y1XhLR/R+92uWKRQG8yFWRAIEBbFHJ6WevQcl/G9SXEfw=="], - "@oxlint/binding-openharmony-arm64": ["@oxlint/binding-openharmony-arm64@1.74.0", "", { "os": "none", "cpu": "arm64" }, "sha512-mzbjrPl4neaVUiJ1fUiEUxTGaSZBoiKtaoB6jmIpz9S+VOA2vDYmJpihQ82w6178V5jxziclTg8Cgj5yF6tTDg=="], + "@oxfmt/binding-openharmony-arm64": ["@oxfmt/binding-openharmony-arm64@0.62.0", "", { "os": "none", "cpu": "arm64" }, "sha512-mSZuFHU2ar1KLUjXpI2QBQcJ1VsOB3mOCgQXuXCpKs19dgh4u+OaovNfrWDfiJb+ihJ2+f7YFcaO9bS2dlTCXA=="], - "@oxlint/binding-win32-arm64-msvc": ["@oxlint/binding-win32-arm64-msvc@1.74.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-vUAe9okpS2Oa5+lX67lqHMuNUvfkleRKwrUDJ/WJBsgmddvZ1mrsh2HVmuFDRzqFELhaJhFaCNOuR6a7L3rtIA=="], + "@oxfmt/binding-win32-arm64-msvc": ["@oxfmt/binding-win32-arm64-msvc@0.62.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-OfwuhkcjDlqC4EgDojtiV9mzpLqeB9KqTOWPOjLEYBVdDCVSxqW3qzp/xcIxsbtI0UgGCnKvAqYKyY25kf5JZw=="], - "@oxlint/binding-win32-ia32-msvc": ["@oxlint/binding-win32-ia32-msvc@1.74.0", "", { "os": "win32", "cpu": "ia32" }, "sha512-yyXXJyYYSXL4I8K8jAWjJs+J3fa9gH2JmEbo4f5adm+1tNC9itseicBNuwK7BDHvqQ5J534s+yDULu89vYL2ZQ=="], + "@oxfmt/binding-win32-ia32-msvc": ["@oxfmt/binding-win32-ia32-msvc@0.62.0", "", { "os": "win32", "cpu": "ia32" }, "sha512-P9uDDNFRzghO3X8QAzhkjKhK7JvtABsVn8UYtFX7uor12IAnwNt8nNIctvfWj1JkQU/kE+fmLRPiw7XlrIHsZw=="], - "@oxlint/binding-win32-x64-msvc": ["@oxlint/binding-win32-x64-msvc@1.74.0", "", { "os": "win32", "cpu": "x64" }, "sha512-VTC9IYTIMrVUk/i6Ms1ohzzDKZFkWn0KU2OBbPBzgmVZ2V30165T/zK4LztTr0Xgp9fZ1qQZ1rsZAu/rEmySlA=="], + "@oxfmt/binding-win32-x64-msvc": ["@oxfmt/binding-win32-x64-msvc@0.62.0", "", { "os": "win32", "cpu": "x64" }, "sha512-dlI5SY7XYQCiCBafntWagCR6HcAJB/NpsLtdlPx8x08+Osz8Ok1HHz1GZuusegCe/VoJ6pAnF5a4pd5OZAq7qQ=="], + + "@oxlint/binding-android-arm-eabi": ["@oxlint/binding-android-arm-eabi@1.77.0", "", { "os": "android", "cpu": "arm" }, "sha512-E06sKWS6PiI6HRxS1wyQg22HvApt01hI7fV+T3wUk3OSbaaP4a3hYGY/MIQDmASqCiRjBdpRQYkgMkqH82cWmQ=="], + + "@oxlint/binding-android-arm64": ["@oxlint/binding-android-arm64@1.77.0", "", { "os": "android", "cpu": "arm64" }, "sha512-NvsKz0KZxTp9cYWPLf+FXaSZwB3oO3peAjtukpOMBgse2vhQSoIIVqeO1yR0lEo/UcdZIDL18uq+kL0LzQ0ytA=="], + + "@oxlint/binding-darwin-arm64": ["@oxlint/binding-darwin-arm64@1.77.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-bgjTn6nW4bQCFBvSvuHCpDD+sONvmpo4lGI4PxzMt1quBA+xYxhczk6RiCn3GZ9gY8uhaBbwhj9MdKGfu6T9DA=="], + + "@oxlint/binding-darwin-x64": ["@oxlint/binding-darwin-x64@1.77.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-aotaIttH1R6j1Rwhx0M0htgeZyGtVQqYNTVEYMN/UcgHPquGA6kmk9OyuDc3a2GKUQBC+3C3GVQCcrRPMYqAFA=="], + + "@oxlint/binding-freebsd-x64": ["@oxlint/binding-freebsd-x64@1.77.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-nNx/wta7ksRAdYvq+l4AWjXkLxEXHALhENxjj2cYbQAIR4ybaA5L+hCbE63HOmft5czQ6ks+hb8vmEAnn7YGPg=="], + + "@oxlint/binding-linux-arm-gnueabihf": ["@oxlint/binding-linux-arm-gnueabihf@1.77.0", "", { "os": "linux", "cpu": "arm" }, "sha512-tMLLjM7xXtzXisVCzkOTXNCy9bZVId2wteNwjohlFDR/jY6WagpEDA1c1wu4xRc20Hojaxj+V6DSR7gbKxijWA=="], + + "@oxlint/binding-linux-arm-musleabihf": ["@oxlint/binding-linux-arm-musleabihf@1.77.0", "", { "os": "linux", "cpu": "arm" }, "sha512-MiAFDFaqR0tmHTAyo0YDcZ5hyLREdYw/RQhc2R3cbT+8O3tB+zqPM2th9TTQ+Uo3jn/embS+DO+HyX9ztCPkOQ=="], + + "@oxlint/binding-linux-arm64-gnu": ["@oxlint/binding-linux-arm64-gnu@1.77.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-/xqQ3B16i1T4cyt/9Mn+4CpzhUXoBXp7kVpIwzOXNFLj5JmK1bIjsbSnX296Gg8A/o7oDtKWikFgBx0SLwztkw=="], + + "@oxlint/binding-linux-arm64-musl": ["@oxlint/binding-linux-arm64-musl@1.77.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-LSbwuRKiNCenPDcbARqAZ5RfBy7gmj7vOvfJRLeCDU3gFtSxWbhv/+VTlaUqzUhNj1gFLHB8h7ALnxa/Az6z6g=="], + + "@oxlint/binding-linux-ppc64-gnu": ["@oxlint/binding-linux-ppc64-gnu@1.77.0", "", { "os": "linux", "cpu": "ppc64" }, "sha512-QWdcH31mXEUe5Nq1s0CfCpceaKjIo9uZtwDjAuL681g1axf+5x8xrg/eXWaw//4NCxYZ4V4e5Hu5tvdR+pTBlg=="], + + "@oxlint/binding-linux-riscv64-gnu": ["@oxlint/binding-linux-riscv64-gnu@1.77.0", "", { "os": "linux", "cpu": "none" }, "sha512-GnOfYgJxbcElOiPZaDFDl406ONddwvOWk2jvAAAEjwAl4GofNoHF+/HHUIBYa6bFCArlcGPi0XjC4cU1pkgF/Q=="], + + "@oxlint/binding-linux-riscv64-musl": ["@oxlint/binding-linux-riscv64-musl@1.77.0", "", { "os": "linux", "cpu": "none" }, "sha512-AyEMTUCf0xY+hHF+IxqXFQIX0yQOIR8ykpY0lJNOw9xYqOzUX8dyZfRvlG0RfXwuQn2eonf/8NrMmDSZJjdqsA=="], + + "@oxlint/binding-linux-s390x-gnu": ["@oxlint/binding-linux-s390x-gnu@1.77.0", "", { "os": "linux", "cpu": "s390x" }, "sha512-sPLzEcNvxd/oyVQ5oZo92CiHkFkpBeRop13E/P3TPY+hZfXHKCOWKI70TE2RYwMKFJDc20EMjH16L7NZICtKTw=="], + + "@oxlint/binding-linux-x64-gnu": ["@oxlint/binding-linux-x64-gnu@1.77.0", "", { "os": "linux", "cpu": "x64" }, "sha512-1Oh2ssH2L7lwyvkdSqaMUfsGfwU2Wfvew+obBUYjRVqhpBcUpwnsPSEr1IzVi9XqkuY10geiLsNKecqaZC34Dw=="], + + "@oxlint/binding-linux-x64-musl": ["@oxlint/binding-linux-x64-musl@1.77.0", "", { "os": "linux", "cpu": "x64" }, "sha512-0j/2wRgNGO+Qj/M1uu/p57h/hFTTWWcfie0ufkbabeus2s5+/QqkCflnMOwLLN5m2GsNeWp4xdl4cPa4n7QCOQ=="], + + "@oxlint/binding-openharmony-arm64": ["@oxlint/binding-openharmony-arm64@1.77.0", "", { "os": "none", "cpu": "arm64" }, "sha512-BJ/j54qS0usEnyDkLYURMj2iiD9h5Cyy+ppzeMSXBGRXaGRNWnj1Mw14NqWMR5E/PzdgB30OOCCzLzbRoduafw=="], + + "@oxlint/binding-win32-arm64-msvc": ["@oxlint/binding-win32-arm64-msvc@1.77.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-Yh8w+g2Lpx7StrvtYkoz9JJvXjB9wxgFChFNb85nrXm/wj/XTwGWS1hve9+900HL7llrntYB3YP+y32E3tRqzA=="], + + "@oxlint/binding-win32-ia32-msvc": ["@oxlint/binding-win32-ia32-msvc@1.77.0", "", { "os": "win32", "cpu": "ia32" }, "sha512-zja5b7+6a7UsRFgAQSrnax5vrzliEyNPLCjfXONu/vTWswaIVZGFajJZptaeRvPE4LghtFdAzVFlexTm7MVTGA=="], + + "@oxlint/binding-win32-x64-msvc": ["@oxlint/binding-win32-x64-msvc@1.77.0", "", { "os": "win32", "cpu": "x64" }, "sha512-+teyvPDZ2RjUvo+SuCqS/UhaJl1QtdW5fWT5NJTV61V5MIuIS90Db9LixmtEGvXixyttiK62P96MSu3UlpviBw=="], "@plausible-analytics/tracker": ["@plausible-analytics/tracker@0.4.5", "", {}, "sha512-6BfAGejXY+YA3Cw6LYT2Zpn4hTxDtPQAawFsYUsQCOg78wIS5C4deAGXTfJffa5VleMWITv5lpJ/EYuQBl1tPA=="], @@ -346,35 +390,37 @@ "@react-grab/cli": ["@react-grab/cli@0.1.50", "", { "dependencies": { "agent-install": "^0.0.6", "commander": "^14.0.3", "ignore": "^7.0.5", "ora": "^9.4.0", "package-manager-detector": "^1.6.0", "picocolors": "^1.1.1", "prompts": "^2.4.2", "tinyexec": "^1.1.2" }, "bin": { "react-grab": "bin/cli.js" } }, "sha512-Px/Hwhhyk2PubCA4ZaRFsfvwxhbxXsetJyvqC6aFFi8WhJhA+oVC33aTzuAeWmM3fhb4/8ce8YsHXI1d6ChcKg=="], - "@rolldown/binding-android-arm64": ["@rolldown/binding-android-arm64@1.1.2", "", { "os": "android", "cpu": "arm64" }, "sha512-2cZ+7xRS+DBcuJBJKnfzsbleumJhBqSlJVpuzHC0nTqfd3QQ7Vx2/x5YR/D7cBamKSeWplwo82Fn9lqYUDEMfA=="], + "@rolldown/binding-android-arm64": ["@rolldown/binding-android-arm64@1.2.2", "", { "os": "android", "cpu": "arm64" }, "sha512-l7x215OGvo1s52JWmR8U/DAVzEDWBCIbTm28aeJV/WDTSHgcKXaZTuBT0hJMs5NggilfJTW3clZVvd24yfKJxA=="], - "@rolldown/binding-darwin-arm64": ["@rolldown/binding-darwin-arm64@1.1.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-RkPMJnygxsgOYdkfqgpwY0/Fzm8d0VQe6HGU2/B00Xa9eqdLbrII+DOKAodbJAn3ZL1AJxGHkZRPYazgGY6Ljw=="], + "@rolldown/binding-darwin-arm64": ["@rolldown/binding-darwin-arm64@1.2.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-9u9Xv6c1AJZT0FfwH5vrMG5Jjcwhc1MlyrPu0XfTqkzsmqfks2M6W/o5XwAJgVVN/jHpqqngC1WevHKKTIUtIA=="], - "@rolldown/binding-darwin-x64": ["@rolldown/binding-darwin-x64@1.1.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-Uiczh6vFhwyfd7WNe7Q7mCA4KxAiLdz7jPE/WGizfRpIieoyFuNVMmM8HqZ9HwudTkY6/AeMQwlNJ9NJijguWw=="], + "@rolldown/binding-darwin-x64": ["@rolldown/binding-darwin-x64@1.2.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-9W1mbGZAfW3oqd85bhBkmpyHCCzL1TeG/zFFP3vg7b0rlly8cxOcre5nXwz+LHazCwac2MNWgPdPCHndABjpWQ=="], - "@rolldown/binding-freebsd-x64": ["@rolldown/binding-freebsd-x64@1.1.2", "", { "os": "freebsd", "cpu": "x64" }, "sha512-+TpdtTRgHiJFjCVFbw311SuLk3KfytPOQQn+VlAEv+gBxYPtL7E6JS9e/tk+8CwxhIZvemJKo4rTKgfWNsKkkA=="], + "@rolldown/binding-freebsd-x64": ["@rolldown/binding-freebsd-x64@1.2.2", "", { "os": "freebsd", "cpu": "x64" }, "sha512-0p1lhiCSCyaerFwtrdZQUx7NqGk6LQnaRKWX7tFQqwQgvX0rjM15cIkm3pax1UpEakK14C4mOxx/jSqCBdBRqQ=="], - "@rolldown/binding-linux-arm-gnueabihf": ["@rolldown/binding-linux-arm-gnueabihf@1.1.2", "", { "os": "linux", "cpu": "arm" }, "sha512-4lv1/tkmi7ueIVHnyreaOeUpiZP26BH9rRy6hoYfR9310A2B9nUEVRDvBx69vx64Nr3eTPPRkyciqJJs+j9Jmw=="], + "@rolldown/binding-linux-arm-gnueabihf": ["@rolldown/binding-linux-arm-gnueabihf@1.2.2", "", { "os": "linux", "cpu": "arm" }, "sha512-e+cOJXrJ2L3zx6YzqPg+f6Wbk3V1cKB8bOhbaYdVYN3DdquzNdRAmrbETz1qnt5yp/c7JNlNjmITiA2cVneQ7w=="], - "@rolldown/binding-linux-arm64-gnu": ["@rolldown/binding-linux-arm64-gnu@1.1.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-gBSUVO0eaWgw1JMjK3gB8BMlX2Mk148s2lTiVT3e9vjVxbl7UDfMWWY8CfIaaqiXuM9fVTMxIpUz6CAo/B6Vlw=="], + "@rolldown/binding-linux-arm64-gnu": ["@rolldown/binding-linux-arm64-gnu@1.2.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-JsSMsj6sNat/MuhG5fnBD7QgbtpHKVe30x5/bAVirDHdhoQRXJkF6xc0Jqk8O4fiCUQAzMOoH9wZi3m60c8wtg=="], - "@rolldown/binding-linux-arm64-musl": ["@rolldown/binding-linux-arm64-musl@1.1.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-LjQP/iZLBu8o8PjIfk4x3At0/mT6h282pvz8Z5LAyhGbu/kDezyO7ea62rF5uoqmgnIYqbN/MqJ3Si3Aymi7xQ=="], + "@rolldown/binding-linux-arm64-musl": ["@rolldown/binding-linux-arm64-musl@1.2.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-B5G/zJdHaoJn9vD50eGHWkiWfmq8Uhi3IiLPJTzmZTrAalk1bztUikSXo0qga18ibE0IXboyeMUnhPjhAJ45wQ=="], - "@rolldown/binding-linux-ppc64-gnu": ["@rolldown/binding-linux-ppc64-gnu@1.1.2", "", { "os": "linux", "cpu": "ppc64" }, "sha512-X/7bVLWelEsbyWDUSXt7zVsTniLLPIY2n1rH58qr78l9i7MNbbxBWD8gI2vRfBWf4NUXJCUuQnfZDsp32LqsfQ=="], + "@rolldown/binding-linux-ppc64-gnu": ["@rolldown/binding-linux-ppc64-gnu@1.2.2", "", { "os": "linux", "cpu": "ppc64" }, "sha512-6mC/awzKka8W6EoekjegpfGkjz8jXWDX63pqu/HYVpyKtZfu65Jsh4QAH3Kej3CAv/c1oGX7psTmFEbr0mDxLA=="], - "@rolldown/binding-linux-s390x-gnu": ["@rolldown/binding-linux-s390x-gnu@1.1.2", "", { "os": "linux", "cpu": "s390x" }, "sha512-gb6dYKW/1KDorGXyy48glEBJs/sxVSC5pcVrox/pFGV4mvwSFeg2sK5L2tRkVsVlh7kueqOgg4GEcuipJcGuKg=="], + "@rolldown/binding-linux-s390x-gnu": ["@rolldown/binding-linux-s390x-gnu@1.2.2", "", { "os": "linux", "cpu": "s390x" }, "sha512-412MX9fJLdA1IK28EZnc8jYv2HRTleOZgfLQumJ5zy7OeJLZlg/CETwFaXjNmGVxG51cFHpKLqb5LKvBC+HsHA=="], - "@rolldown/binding-linux-x64-gnu": ["@rolldown/binding-linux-x64-gnu@1.1.2", "", { "os": "linux", "cpu": "x64" }, "sha512-JY4w85pU3iAiJVMh5nuk4/Mh9GjMsupe8MrIN53rwxAZW64GKrWeJBuN6SxQg9QTU5uB1cxyhDzW8jqRn1EABw=="], + "@rolldown/binding-linux-x64-gnu": ["@rolldown/binding-linux-x64-gnu@1.2.2", "", { "os": "linux", "cpu": "x64" }, "sha512-Q/+HI/ToJafZ1iCqGgVQXUEkIjufHCTF0gBQ2a5o3cg7GJ2h0qyq3nvvSmU+bGda2/7ygXpTY4TM6gO9OhQ0ZA=="], - "@rolldown/binding-linux-x64-musl": ["@rolldown/binding-linux-x64-musl@1.1.2", "", { "os": "linux", "cpu": "x64" }, "sha512-xvpA7o5KCYLB0Rwscmuylb1/zHHSUx4g4xilm4prC5jP76pEUlzBmMbgpbh7bVDbId4NcfT96gN5i6mE6UDaiw=="], + "@rolldown/binding-linux-x64-musl": ["@rolldown/binding-linux-x64-musl@1.2.2", "", { "os": "linux", "cpu": "x64" }, "sha512-ZKp/w41n6wCvxzxQHtQSbuphfX3Y4cCvbjkKHusrLx4lh+JWLTU7StSltO/DKARISzbj368d+qUaCjI8K2wzXw=="], - "@rolldown/binding-openharmony-arm64": ["@rolldown/binding-openharmony-arm64@1.1.2", "", { "os": "none", "cpu": "arm64" }, "sha512-p/ts6KBLjuk49Bp21XH77poQGt02iNz7ChgHep7tudPOaLinR/De/RHdxF8w8Yj4r/bF/bqXwH6PZrB2sA+Nvw=="], + "@rolldown/binding-openharmony-arm64": ["@rolldown/binding-openharmony-arm64@1.2.2", "", { "os": "none", "cpu": "arm64" }, "sha512-pxE6xD4KS3eAROkKK5yrhB9/3+vhlhVGMvlQLbdpzrBGDbKrnzx3RLwPaHvasLo6jgaiBLn7e04Df9C4tYhjmA=="], "@rolldown/binding-wasm32-wasi": ["@rolldown/binding-wasm32-wasi@1.1.2", "", { "dependencies": { "@emnapi/core": "1.11.1", "@emnapi/runtime": "1.11.1", "@napi-rs/wasm-runtime": "^1.1.5" }, "cpu": "none" }, "sha512-VMu/wmrZ9hJzYlRhbw7jK5PODlugyKZ5mOdX78+lS8OvuFkWNQdz1pFLrI2p3P0pjXOmUZ7B48o5VnMH9QOGtg=="], - "@rolldown/binding-win32-arm64-msvc": ["@rolldown/binding-win32-arm64-msvc@1.1.2", "", { "os": "win32", "cpu": "arm64" }, "sha512-xtUJqs8qEkuSviS0n1tsohaPuz3a1SPhZywOji4Oo+sgrJs8daEDMZ0QtqL0OS7dx8PoVpg2J/ZZycPY5I2+Zg=="], + "@rolldown/binding-win32-arm64-msvc": ["@rolldown/binding-win32-arm64-msvc@1.2.2", "", { "os": "win32", "cpu": "arm64" }, "sha512-4MqEue5re+xIZzAWsB8sj0P1kqZySWqIuN4t6QaIO/YA6SFwySOLruvWQFzfmqk8LBK2P30KCSJwf6mJCZZ5/A=="], + + "@rolldown/binding-win32-x64-msvc": ["@rolldown/binding-win32-x64-msvc@1.2.2", "", { "os": "win32", "cpu": "x64" }, "sha512-NweNxxD0Nf9t8v7kodun45Ijp3EIwYY+uydPP6qBEYvfBqhIjN6dZMzlQja3tqX/aLs3F3Uz+AxDpKgRhpOZQg=="], - "@rolldown/binding-win32-x64-msvc": ["@rolldown/binding-win32-x64-msvc@1.1.2", "", { "os": "win32", "cpu": "x64" }, "sha512-85YiLQqjUKgSO/Zjnf9e0XIn5Ymrh1fLDWBeAkZqpuBR/3R8TpfoHXuyblqyQrftSSgWO9qpcHN8mkyKsLraoA=="], + "@rolldown/plugin-babel": ["@rolldown/plugin-babel@0.2.3", "", { "dependencies": { "picomatch": "^4.0.4" }, "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-+zEk16yGlz1F9STiRr6uG9hmIXb6nprjLczV/htGptYuLoCuxb+itZ03RKCEeOhBpDDd1NU7qF6x1VLMUp62bw=="], "@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.1", "", {}, "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw=="], @@ -466,6 +512,14 @@ "@tybys/wasm-util": ["@tybys/wasm-util@0.10.3", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg=="], + "@types/babel__core": ["@types/babel__core@7.20.5", "", { "dependencies": { "@babel/parser": "^7.20.7", "@babel/types": "^7.20.7", "@types/babel__generator": "*", "@types/babel__template": "*", "@types/babel__traverse": "*" } }, "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA=="], + + "@types/babel__generator": ["@types/babel__generator@7.27.0", "", { "dependencies": { "@babel/types": "^7.0.0" } }, "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg=="], + + "@types/babel__template": ["@types/babel__template@7.4.4", "", { "dependencies": { "@babel/parser": "^7.1.0", "@babel/types": "^7.0.0" } }, "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A=="], + + "@types/babel__traverse": ["@types/babel__traverse@7.28.0", "", { "dependencies": { "@babel/types": "^7.28.2" } }, "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q=="], + "@types/bun": ["@types/bun@1.3.14", "", { "dependencies": { "bun-types": "1.3.14" } }, "sha512-h1hFqFVcvAvD9j9K7ZW7vd82aSA+rTdznZa+5bwvCwqSB1jmmfLcbIWhOLx1/+boy/xmjgCs/OMUL8hRJSmnPw=="], "@types/chai": ["@types/chai@5.2.3", "", { "dependencies": { "@types/deep-eql": "*", "assertion-error": "^2.0.1" } }, "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA=="], @@ -476,8 +530,12 @@ "@types/estree": ["@types/estree@1.0.9", "", {}, "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg=="], + "@types/gensync": ["@types/gensync@1.0.5", "", {}, "sha512-MbsRCT7mTikHwKZ0X+LVUTLRrZZRLipTuXEO9qOYO+zmjMVk81axyClMROf6uoPD9MRVu46bx8zoR0Ad9q3NAg=="], + "@types/hast": ["@types/hast@3.0.4", "", { "dependencies": { "@types/unist": "*" } }, "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ=="], + "@types/jsesc": ["@types/jsesc@2.5.1", "", {}, "sha512-9VN+6yxLOPLOav+7PwjZbxiID2bVaeq0ED4qSQmdQTdjnXJSaCVKTR58t15oqH1H5t8Ng2ZX1SabJVoN9Q34bw=="], + "@types/json-schema": ["@types/json-schema@7.0.15", "", {}, "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="], "@types/mdast": ["@types/mdast@4.0.4", "", { "dependencies": { "@types/unist": "*" } }, "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA=="], @@ -496,7 +554,7 @@ "@vercel/oidc": ["@vercel/oidc@3.2.0", "", {}, "sha512-UycprH3T6n3jH0k44NHMa7pnFHGu/N05MjojYr+Mc6I7obkoLIJujSWwin1pCvdy/eOxrI/l3uDLQsmcrOb4ug=="], - "@vitejs/plugin-react": ["@vitejs/plugin-react@6.0.3", "", { "dependencies": { "@rolldown/pluginutils": "^1.0.1" }, "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-vmFvco5/QuC2f9Oj+wTk0+9XeDFkHxSamwZKYc7MxYwKICfvUvlMhqKI0VuICPltGqh1neqBKDvO4kes1ya8vg=="], + "@vitejs/plugin-react": ["@vitejs/plugin-react@6.0.5", "", { "dependencies": { "@rolldown/pluginutils": "^1.0.1" }, "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-BOVzne/NL162sMdResB25mUv+vWMF5NoAjNf09TeGlE7ZpszZWSD3winycicLJw72yeVsoCn/2kOhEuCvEShMA=="], "@vitest/expect": ["@vitest/expect@4.1.10", "", { "dependencies": { "@standard-schema/spec": "^1.1.0", "@types/chai": "^5.2.2", "@vitest/spy": "4.1.10", "@vitest/utils": "4.1.10", "chai": "^6.2.2", "tinyrainbow": "^3.1.0" } }, "sha512-YsCn+qAk1GWjQOWFEsEcL2gNQ0zmVmQu3T03qP6UyjhtmdtwtbuI+DASn/7iQB3HGTXkdBwGddzxPlmiql5vlA=="], @@ -548,6 +606,8 @@ "available-typed-arrays": ["available-typed-arrays@1.0.7", "", { "dependencies": { "possible-typed-array-names": "^1.0.0" } }, "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ=="], + "babel-plugin-react-compiler": ["babel-plugin-react-compiler@1.0.0", "", { "dependencies": { "@babel/types": "^7.26.0" } }, "sha512-Ixm8tFfoKKIPYdCCKYTsqv+Fd4IJ0DQqMyEimo+pxUOMUR9cVPlwTrFt9Avu+3cb6Zp3mAzl+t1MrG2fxxKsxw=="], + "balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], "baseline-browser-mapping": ["baseline-browser-mapping@2.11.7", "", { "bin": { "baseline-browser-mapping": "dist/cli.cjs" } }, "sha512-APw5YuIQAg6L9w4sHDI6j26DGFJI6RpYOhnkMPdC9lWbkKvsyPHzDsve1yd73lk21yz7Y09Kci8B2Pp9FonzWA=="], @@ -650,6 +710,8 @@ "electron-to-chromium": ["electron-to-chromium@1.5.398", "", {}, "sha512-AsvhAxopJGh6museTDMIjn6JpDYOfgu4RLlygomt87MUwBUqTfd/1EiPtx10/LZE8xpTvkP2E9Gafq7lkLtodQ=="], + "empathic": ["empathic@2.0.1", "", {}, "sha512-YGRs8knHhKHVShLkFET/rWAU8kmHbOV5LwN938RHI0pljAJ1Gf6SzXsSmRaEzcXTtOOmVqJ5+WtQPL5uigY50Q=="], + "enhanced-resolve": ["enhanced-resolve@5.21.6", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.3.3" } }, "sha512-aNnGCvbJ/RIyWo1IuhNdVjnNF+EjH9wpzpNHt+ci/m9He9LJvUN8wrCcXjp9cWsGNAuvSpVFTx/vraAFQ8qGjQ=="], "env-paths": ["env-paths@3.0.0", "", {}, "sha512-dtJUTepzMW3Lm/NPxRf3wP4642UWhjL2sQxc+ym2YMj1m/H2zDNQOlezafzkHwn6sMstjHTwG6iQQsctDW/b1A=="], @@ -794,6 +856,8 @@ "import-in-the-middle": ["import-in-the-middle@3.3.2", "", { "dependencies": { "cjs-module-lexer": "^2.2.0", "es-module-lexer": "^2.2.0", "module-details-from-path": "^1.0.4" } }, "sha512-jTd2FfOgOWOdgjkHuk/1Ms8VKFXkPs15ymYBETw1sAOrO/dY3XeGVRWir9qBbw7pXr0T2eTFwfCZ+N02HmiNGA=="], + "import-meta-resolve": ["import-meta-resolve@4.2.0", "", {}, "sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg=="], + "imurmurhash": ["imurmurhash@0.1.4", "", {}, "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA=="], "indent-string": ["indent-string@5.0.0", "", {}, "sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg=="], @@ -876,7 +940,7 @@ "js-cookie": ["js-cookie@3.0.7", "", {}, "sha512-z/wZZgDrkNV1eA0ULjM/F9/50Ya8fbzgKneSpoPsXSGd0KnpdtHfOZWK+GcwLk+EZbS4F9RBhU+K2RgzuDaItw=="], - "js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="], + "js-tokens": ["js-tokens@10.0.0", "", {}, "sha512-lM/UBzQmfJRo9ABXbPWemivdCW8V2G8FHaHdypQaIy523snUjog0W71ayWXTjiR+ixeMyVHN2XcpnTd/liPg/Q=="], "jsesc": ["jsesc@3.1.0", "", { "bin": { "jsesc": "bin/jsesc" } }, "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA=="], @@ -902,29 +966,29 @@ "levn": ["levn@0.4.1", "", { "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" } }, "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ=="], - "lightningcss": ["lightningcss@1.32.0", "", { "dependencies": { "detect-libc": "^2.0.3" }, "optionalDependencies": { "lightningcss-android-arm64": "1.32.0", "lightningcss-darwin-arm64": "1.32.0", "lightningcss-darwin-x64": "1.32.0", "lightningcss-freebsd-x64": "1.32.0", "lightningcss-linux-arm-gnueabihf": "1.32.0", "lightningcss-linux-arm64-gnu": "1.32.0", "lightningcss-linux-arm64-musl": "1.32.0", "lightningcss-linux-x64-gnu": "1.32.0", "lightningcss-linux-x64-musl": "1.32.0", "lightningcss-win32-arm64-msvc": "1.32.0", "lightningcss-win32-x64-msvc": "1.32.0" } }, "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ=="], + "lightningcss": ["lightningcss@1.33.0", "", { "dependencies": { "detect-libc": "^2.0.3" }, "optionalDependencies": { "lightningcss-android-arm64": "1.33.0", "lightningcss-darwin-arm64": "1.33.0", "lightningcss-darwin-x64": "1.33.0", "lightningcss-freebsd-x64": "1.33.0", "lightningcss-linux-arm-gnueabihf": "1.33.0", "lightningcss-linux-arm64-gnu": "1.33.0", "lightningcss-linux-arm64-musl": "1.33.0", "lightningcss-linux-x64-gnu": "1.33.0", "lightningcss-linux-x64-musl": "1.33.0", "lightningcss-win32-arm64-msvc": "1.33.0", "lightningcss-win32-x64-msvc": "1.33.0" } }, "sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA=="], - "lightningcss-android-arm64": ["lightningcss-android-arm64@1.32.0", "", { "os": "android", "cpu": "arm64" }, "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg=="], + "lightningcss-android-arm64": ["lightningcss-android-arm64@1.33.0", "", { "os": "android", "cpu": "arm64" }, "sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg=="], - "lightningcss-darwin-arm64": ["lightningcss-darwin-arm64@1.32.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ=="], + "lightningcss-darwin-arm64": ["lightningcss-darwin-arm64@1.33.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg=="], - "lightningcss-darwin-x64": ["lightningcss-darwin-x64@1.32.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w=="], + "lightningcss-darwin-x64": ["lightningcss-darwin-x64@1.33.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ=="], - "lightningcss-freebsd-x64": ["lightningcss-freebsd-x64@1.32.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig=="], + "lightningcss-freebsd-x64": ["lightningcss-freebsd-x64@1.33.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg=="], - "lightningcss-linux-arm-gnueabihf": ["lightningcss-linux-arm-gnueabihf@1.32.0", "", { "os": "linux", "cpu": "arm" }, "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw=="], + "lightningcss-linux-arm-gnueabihf": ["lightningcss-linux-arm-gnueabihf@1.33.0", "", { "os": "linux", "cpu": "arm" }, "sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ=="], - "lightningcss-linux-arm64-gnu": ["lightningcss-linux-arm64-gnu@1.32.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ=="], + "lightningcss-linux-arm64-gnu": ["lightningcss-linux-arm64-gnu@1.33.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg=="], - "lightningcss-linux-arm64-musl": ["lightningcss-linux-arm64-musl@1.32.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg=="], + "lightningcss-linux-arm64-musl": ["lightningcss-linux-arm64-musl@1.33.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ=="], - "lightningcss-linux-x64-gnu": ["lightningcss-linux-x64-gnu@1.32.0", "", { "os": "linux", "cpu": "x64" }, "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA=="], + "lightningcss-linux-x64-gnu": ["lightningcss-linux-x64-gnu@1.33.0", "", { "os": "linux", "cpu": "x64" }, "sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg=="], - "lightningcss-linux-x64-musl": ["lightningcss-linux-x64-musl@1.32.0", "", { "os": "linux", "cpu": "x64" }, "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg=="], + "lightningcss-linux-x64-musl": ["lightningcss-linux-x64-musl@1.33.0", "", { "os": "linux", "cpu": "x64" }, "sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw=="], - "lightningcss-win32-arm64-msvc": ["lightningcss-win32-arm64-msvc@1.32.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw=="], + "lightningcss-win32-arm64-msvc": ["lightningcss-win32-arm64-msvc@1.33.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA=="], - "lightningcss-win32-x64-msvc": ["lightningcss-win32-x64-msvc@1.32.0", "", { "os": "win32", "cpu": "x64" }, "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q=="], + "lightningcss-win32-x64-msvc": ["lightningcss-win32-x64-msvc@1.33.0", "", { "os": "win32", "cpu": "x64" }, "sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA=="], "load-json-file": ["load-json-file@4.0.0", "", { "dependencies": { "graceful-fs": "^4.1.2", "parse-json": "^4.0.0", "pify": "^3.0.0", "strip-bom": "^3.0.0" } }, "sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw=="], @@ -932,7 +996,7 @@ "log-symbols": ["log-symbols@7.0.1", "", { "dependencies": { "is-unicode-supported": "^2.0.0", "yoctocolors": "^2.1.1" } }, "sha512-ja1E3yCr9i/0hmBVaM0bfwDjnGy8I/s6PP4DFp+yP+a+mrHO4Rm7DtmnqROTUkHIkqffC84YY7AeqX6oFk0WFg=="], - "lru-cache": ["lru-cache@5.1.1", "", { "dependencies": { "yallist": "^3.0.2" } }, "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w=="], + "lru-cache": ["lru-cache@11.5.2", "", {}, "sha512-4pfM1Ff0x50o0tQwb5ucw/RzNyD0/YJME6IVcStalZuMWxdt3sR3huStTtxz4PUmvZfRguvDejasvQ2kifR11g=="], "magic-string": ["magic-string@0.30.21", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ=="], @@ -970,7 +1034,7 @@ "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], - "nanoid": ["nanoid@3.3.15", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-y7Wygv/7mEOvxTuEQDB8StXdMRBWf1kR/tlhAzBRUFkB2jfcLOAxO/SHmOO2zgz1pVgK29/kyupn059/bCHdjA=="], + "nanoid": ["nanoid@3.3.17", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-xQLf0A3HOMlgHq0n247/LRuAOYmB7dXJ/DvAxGvsSBij45XtBSmQycu+F8ODbHwns/XyFZagyL1+J0Offw1E0g=="], "natural-compare": ["natural-compare@1.4.0", "", {}, "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw=="], @@ -1006,7 +1070,9 @@ "oxc-resolver": ["oxc-resolver@11.24.2", "", { "optionalDependencies": { "@oxc-resolver/binding-android-arm-eabi": "11.24.2", "@oxc-resolver/binding-android-arm64": "11.24.2", "@oxc-resolver/binding-darwin-arm64": "11.24.2", "@oxc-resolver/binding-darwin-x64": "11.24.2", "@oxc-resolver/binding-freebsd-x64": "11.24.2", "@oxc-resolver/binding-linux-arm-gnueabihf": "11.24.2", "@oxc-resolver/binding-linux-arm-musleabihf": "11.24.2", "@oxc-resolver/binding-linux-arm64-gnu": "11.24.2", "@oxc-resolver/binding-linux-arm64-musl": "11.24.2", "@oxc-resolver/binding-linux-ppc64-gnu": "11.24.2", "@oxc-resolver/binding-linux-riscv64-gnu": "11.24.2", "@oxc-resolver/binding-linux-riscv64-musl": "11.24.2", "@oxc-resolver/binding-linux-s390x-gnu": "11.24.2", "@oxc-resolver/binding-linux-x64-gnu": "11.24.2", "@oxc-resolver/binding-linux-x64-musl": "11.24.2", "@oxc-resolver/binding-openharmony-arm64": "11.24.2", "@oxc-resolver/binding-wasm32-wasi": "11.24.2", "@oxc-resolver/binding-win32-arm64-msvc": "11.24.2", "@oxc-resolver/binding-win32-x64-msvc": "11.24.2" } }, "sha512-FY91FiDBj7ls5MsFS9jN3tjz2o0/zsdSsymlakySaBwVJZorHhkWyICLZMKxlu1R9vYo+sd3z1jwb4J8x7bNDw=="], - "oxlint": ["oxlint@1.74.0", "", { "optionalDependencies": { "@oxlint/binding-android-arm-eabi": "1.74.0", "@oxlint/binding-android-arm64": "1.74.0", "@oxlint/binding-darwin-arm64": "1.74.0", "@oxlint/binding-darwin-x64": "1.74.0", "@oxlint/binding-freebsd-x64": "1.74.0", "@oxlint/binding-linux-arm-gnueabihf": "1.74.0", "@oxlint/binding-linux-arm-musleabihf": "1.74.0", "@oxlint/binding-linux-arm64-gnu": "1.74.0", "@oxlint/binding-linux-arm64-musl": "1.74.0", "@oxlint/binding-linux-ppc64-gnu": "1.74.0", "@oxlint/binding-linux-riscv64-gnu": "1.74.0", "@oxlint/binding-linux-riscv64-musl": "1.74.0", "@oxlint/binding-linux-s390x-gnu": "1.74.0", "@oxlint/binding-linux-x64-gnu": "1.74.0", "@oxlint/binding-linux-x64-musl": "1.74.0", "@oxlint/binding-openharmony-arm64": "1.74.0", "@oxlint/binding-win32-arm64-msvc": "1.74.0", "@oxlint/binding-win32-ia32-msvc": "1.74.0", "@oxlint/binding-win32-x64-msvc": "1.74.0" }, "peerDependencies": { "oxlint-tsgolint": ">=0.24.0", "vite-plus": "*" }, "optionalPeers": ["oxlint-tsgolint", "vite-plus"], "bin": { "oxlint": "bin/oxlint" } }, "sha512-odGl2s2x5IOJoj3A0v1k0PGBXVFBZeZ2+AK/+K2MJur7Ghi3bkyX5NuLUWHKqa4js1wjep3hJeuTQJOlr+4+dA=="], + "oxfmt": ["oxfmt@0.62.0", "", { "dependencies": { "tinypool": "2.1.0" }, "optionalDependencies": { "@oxfmt/binding-android-arm-eabi": "0.62.0", "@oxfmt/binding-android-arm64": "0.62.0", "@oxfmt/binding-darwin-arm64": "0.62.0", "@oxfmt/binding-darwin-x64": "0.62.0", "@oxfmt/binding-freebsd-x64": "0.62.0", "@oxfmt/binding-linux-arm-gnueabihf": "0.62.0", "@oxfmt/binding-linux-arm-musleabihf": "0.62.0", "@oxfmt/binding-linux-arm64-gnu": "0.62.0", "@oxfmt/binding-linux-arm64-musl": "0.62.0", "@oxfmt/binding-linux-ppc64-gnu": "0.62.0", "@oxfmt/binding-linux-riscv64-gnu": "0.62.0", "@oxfmt/binding-linux-riscv64-musl": "0.62.0", "@oxfmt/binding-linux-s390x-gnu": "0.62.0", "@oxfmt/binding-linux-x64-gnu": "0.62.0", "@oxfmt/binding-linux-x64-musl": "0.62.0", "@oxfmt/binding-openharmony-arm64": "0.62.0", "@oxfmt/binding-win32-arm64-msvc": "0.62.0", "@oxfmt/binding-win32-ia32-msvc": "0.62.0", "@oxfmt/binding-win32-x64-msvc": "0.62.0" }, "peerDependencies": { "svelte": "^5.0.0", "vite-plus": "*" }, "optionalPeers": ["svelte", "vite-plus"], "bin": { "oxfmt": "bin/oxfmt" } }, "sha512-vxgGHTmnDU9j4CX7dDBLzxgmHxfda/yPcgJkGCMUSCwRmz+euo/V08xXLNgXTeqAB9Fhf3Pe2nO1RNKLCVgphQ=="], + + "oxlint": ["oxlint@1.77.0", "", { "optionalDependencies": { "@oxlint/binding-android-arm-eabi": "1.77.0", "@oxlint/binding-android-arm64": "1.77.0", "@oxlint/binding-darwin-arm64": "1.77.0", "@oxlint/binding-darwin-x64": "1.77.0", "@oxlint/binding-freebsd-x64": "1.77.0", "@oxlint/binding-linux-arm-gnueabihf": "1.77.0", "@oxlint/binding-linux-arm-musleabihf": "1.77.0", "@oxlint/binding-linux-arm64-gnu": "1.77.0", "@oxlint/binding-linux-arm64-musl": "1.77.0", "@oxlint/binding-linux-ppc64-gnu": "1.77.0", "@oxlint/binding-linux-riscv64-gnu": "1.77.0", "@oxlint/binding-linux-riscv64-musl": "1.77.0", "@oxlint/binding-linux-s390x-gnu": "1.77.0", "@oxlint/binding-linux-x64-gnu": "1.77.0", "@oxlint/binding-linux-x64-musl": "1.77.0", "@oxlint/binding-openharmony-arm64": "1.77.0", "@oxlint/binding-win32-arm64-msvc": "1.77.0", "@oxlint/binding-win32-ia32-msvc": "1.77.0", "@oxlint/binding-win32-x64-msvc": "1.77.0" }, "peerDependencies": { "oxlint-tsgolint": ">=7.0.2001", "vite-plus": "*" }, "optionalPeers": ["oxlint-tsgolint", "vite-plus"], "bin": { "oxlint": "bin/oxlint" } }, "sha512-qnGh8XJHaQ0dprrDXNQZgS0FgjI6v+V3+X8DwmaV++5Aamy6jGKfDdQ1TUvhUxtmKFAbEf4/WeO5QZX+5WSngg=="], "oxlint-plugin-react-doctor": ["oxlint-plugin-react-doctor@0.9.2", "", { "dependencies": { "@shaderfrog/glsl-parser": "^7.0.1", "@typescript-eslint/types": "^8.59.3", "eslint-scope": "^9.1.2", "eslint-visitor-keys": "^5.0.1", "oxc-parser": "^0.141.0" } }, "sha512-fTciSOgAGe/KAgvFDKLz9crjxHyAuu0BvT5sXfSdo2B5QmY5Y/j3nliqX6FaGr7Wud1SYvkAky+/m+58b6K6fQ=="], @@ -1032,7 +1098,7 @@ "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="], - "picomatch": ["picomatch@4.0.4", "", {}, "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A=="], + "picomatch": ["picomatch@4.0.5", "", {}, "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A=="], "pidtree": ["pidtree@0.3.1", "", { "bin": { "pidtree": "bin/pidtree.js" } }, "sha512-qQbW94hLHEqCg7nhby4yRC7G2+jYHY4Rguc2bjw7Uug4GIJuu1tvf2uHaZv5Q8zdt+WKJ6qK1FOI6amaWUo5FA=="], @@ -1040,7 +1106,7 @@ "possible-typed-array-names": ["possible-typed-array-names@1.1.0", "", {}, "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg=="], - "postcss": ["postcss@8.5.15", "", { "dependencies": { "nanoid": "^3.3.12", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A=="], + "postcss": ["postcss@8.5.25", "", { "dependencies": { "nanoid": "^3.3.16", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-DTPx3RWSSnWyzLxQnlH0rJP+EW5ekl16ZU4/psbIhA0e53kJfdgaN5vKM+xP7yJtXVu+nfdVFmlgFDEKAe4Pyw=="], "preact": ["preact@10.29.7", "", { "peerDependencies": { "preact-render-to-string": ">=5" }, "optionalPeers": ["preact-render-to-string"] }, "sha512-DCHYrK/B10yUD3ZjLfhZ3WIE/9Vf9VFUODcRE2dRomTYDpJk6z6L9wecSfhfE6M9ZTHUdyQkoC46arIDhEV84Q=="], @@ -1056,11 +1122,11 @@ "queue-microtask": ["queue-microtask@1.2.3", "", {}, "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="], - "react": ["react@19.2.7", "", {}, "sha512-HNe9WslTbXmFK8o8cmwgAeJFSBvt1bPdHCVKtaaV+WlAN36mpT4hcRpwbf3fY56ar2oIXzsBpOAiIRHAdY0OlQ=="], + "react": ["react@19.2.8", "", {}, "sha512-PWaYA1L/q9u2u7xYQi+Y3L3Yfnie7XyLeaJICV1MGD6LprsBxcAqGjYyr0eY3p+QdsA+x/Irkt4Qif8D63+Sbw=="], "react-doctor": ["react-doctor@0.9.2", "", { "dependencies": { "@babel/code-frame": "^7.29.0", "@sentry/node": "^10.54.0", "agent-install": "0.0.5", "conf": "^15.1.0", "confbox": "^0.2.4", "deslop-js": "0.9.2", "eslint-plugin-react-hooks": "^7.1.1", "figures": "^6.1.0", "ink": "^7.1.0", "ink-spinner": "^5.0.0", "jiti": "^2.7.0", "magicast": "^0.5.3", "oxc-resolver": "^11.24.2", "oxlint": ">=1.74.0 <1.75.0", "oxlint-plugin-react-doctor": "0.9.2", "prompts": "^2.4.2", "react": "19.2.5", "typescript": ">=5.0.4 <6", "vscode-languageserver": "^9.0.1", "vscode-languageserver-textdocument": "^1.0.12", "vscode-uri": "^3.1.0", "yaml": "^2.9.0" }, "bin": { "react-doctor": "bin/react-doctor.js" } }, "sha512-A/e21t0y3j7zUTS8lJyNI2pKMfYLDH+zZwqAC7+MQW1vCD3xqWc2x8XCCWKnrQQwtFd6dwSZw2017x1iSLnGTA=="], - "react-dom": ["react-dom@19.2.7", "", { "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { "react": "^19.2.7" } }, "sha512-t0BRVXvbiE/o20Hfw669rLbMCDWtYZLvmJigy2f0MxsXF+71pxhR3xOkspmsO8h3ZlNzyibAmtCa3l4lYKk6gQ=="], + "react-dom": ["react-dom@19.2.8", "", { "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { "react": "^19.2.8" } }, "sha512-rVprimfGBG3DR+Tq0IQG2DT5PxKth1WIGDmj5yPmlzr4YBe7uyE+Du4oVqTDXZSHGGGXRtTJEGSSePyQCMBglQ=="], "react-grab": ["react-grab@0.1.50", "", { "dependencies": { "@react-grab/cli": "0.1.50", "bippy": "^0.6.1" }, "peerDependencies": { "react": ">=17.0.0" }, "optionalPeers": ["react"], "bin": { "react-grab": "bin/cli.js" } }, "sha512-zRkHKq/8a1msCpEOp8BDROeQZT50m0OH2XPrP6jk5op+JAHrlsm3pj7eAQMOsct87EZDeGNnu4r+sGsJJzyw1Q=="], @@ -1090,7 +1156,7 @@ "reusify": ["reusify@1.1.0", "", {}, "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw=="], - "rolldown": ["rolldown@1.1.2", "", { "dependencies": { "@oxc-project/types": "=0.137.0", "@rolldown/pluginutils": "^1.0.0" }, "optionalDependencies": { "@rolldown/binding-android-arm64": "1.1.2", "@rolldown/binding-darwin-arm64": "1.1.2", "@rolldown/binding-darwin-x64": "1.1.2", "@rolldown/binding-freebsd-x64": "1.1.2", "@rolldown/binding-linux-arm-gnueabihf": "1.1.2", "@rolldown/binding-linux-arm64-gnu": "1.1.2", "@rolldown/binding-linux-arm64-musl": "1.1.2", "@rolldown/binding-linux-ppc64-gnu": "1.1.2", "@rolldown/binding-linux-s390x-gnu": "1.1.2", "@rolldown/binding-linux-x64-gnu": "1.1.2", "@rolldown/binding-linux-x64-musl": "1.1.2", "@rolldown/binding-openharmony-arm64": "1.1.2", "@rolldown/binding-wasm32-wasi": "1.1.2", "@rolldown/binding-win32-arm64-msvc": "1.1.2", "@rolldown/binding-win32-x64-msvc": "1.1.2" }, "bin": { "rolldown": "./bin/cli.mjs" } }, "sha512-x0CrQQqCXWGeI8dTvFfN/Dnv3yMKT9hv5jFjlOreKAx9wqLq9wz7VvLLHyaAXC90/CpggTu9SisSbsJJTPSjNQ=="], + "rolldown": ["rolldown@1.2.2", "", { "dependencies": { "@oxc-project/types": "=0.142.0", "@rolldown/pluginutils": "^1.0.0" }, "optionalDependencies": { "@rolldown/binding-android-arm64": "1.2.2", "@rolldown/binding-darwin-arm64": "1.2.2", "@rolldown/binding-darwin-x64": "1.2.2", "@rolldown/binding-freebsd-x64": "1.2.2", "@rolldown/binding-linux-arm-gnueabihf": "1.2.2", "@rolldown/binding-linux-arm64-gnu": "1.2.2", "@rolldown/binding-linux-arm64-musl": "1.2.2", "@rolldown/binding-linux-ppc64-gnu": "1.2.2", "@rolldown/binding-linux-s390x-gnu": "1.2.2", "@rolldown/binding-linux-x64-gnu": "1.2.2", "@rolldown/binding-linux-x64-musl": "1.2.2", "@rolldown/binding-openharmony-arm64": "1.2.2", "@rolldown/binding-win32-arm64-msvc": "1.2.2", "@rolldown/binding-win32-x64-msvc": "1.2.2" }, "bin": { "rolldown": "./bin/cli.mjs" } }, "sha512-opwpo1tQBAcpSUJDt94B7hhLNGOKjCdE//XXjeLrnx9b83bjnw45tXdg1b09yEw/VLFBJGZpwRULMmOZo7ol+A=="], "run-parallel": ["run-parallel@1.2.0", "", { "dependencies": { "queue-microtask": "^1.2.2" } }, "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA=="], @@ -1104,7 +1170,7 @@ "semifies": ["semifies@1.0.0", "", {}, "sha512-xXR3KGeoxTNWPD4aBvL5NUpMTT7WMANr3EWnaS190QVkY52lqqcVRD7Q05UVbBhiWDGWMlJEUam9m7uFFGVScw=="], - "semver": ["semver@5.7.2", "", { "bin": { "semver": "bin/semver" } }, "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g=="], + "semver": ["semver@7.8.5", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA=="], "seroval": ["seroval@1.5.4", "", {}, "sha512-46uFvgrXTVxZcUorgSSRZ4y+ieqLLQRMlG4bnCZKW3qI6BZm7Rg4ntMW4p1mILEEBZWrFlcpp0AyIIlM6jD9iw=="], @@ -1204,6 +1270,8 @@ "tinyglobby": ["tinyglobby@0.2.17", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.4" } }, "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g=="], + "tinypool": ["tinypool@2.1.0", "", {}, "sha512-Pugqs6M0m7Lv1I7FtxN4aoyToKg1C4tu+/381vH35y8oENM/Ai7f7C4StcoK4/+BSw9ebcS8jRiVrORFKCALLw=="], + "tinyrainbow": ["tinyrainbow@3.1.0", "", {}, "sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw=="], "to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="], @@ -1256,7 +1324,7 @@ "vfile-message": ["vfile-message@4.0.3", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-stringify-position": "^4.0.0" } }, "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw=="], - "vite": ["vite@8.1.0", "", { "dependencies": { "lightningcss": "^1.32.0", "picomatch": "^4.0.4", "postcss": "^8.5.15", "rolldown": "~1.1.2", "tinyglobby": "^0.2.17" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "@vitejs/devtools": "^0.3.0", "esbuild": "^0.27.0 || ^0.28.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-BuJcQK/56NQTWDGn4ABea3q4SSBdNPWwNZKTkkUpcMPnLoquSYH8llRtSUIgoL1KSCpHt5eghLShn50mH36y7Q=="], + "vite": ["vite@8.2.0", "", { "dependencies": { "lightningcss": "^1.33.0", "picomatch": "^4.0.5", "postcss": "^8.5.23", "rolldown": "~1.2.0", "tinyglobby": "^0.2.17" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "@vitejs/devtools": "^0.4.0", "esbuild": "^0.27.0 || ^0.28.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-pn+CFpM0lwDeKwmOq1ZaBK/9sjorZcgqxki6MbY/jPEVd9vichIlmlD4HmQ5wdP5EgqQCFRaACBxMC7uEGc6lQ=="], "vitest": ["vitest@4.1.10", "", { "dependencies": { "@vitest/expect": "4.1.10", "@vitest/mocker": "4.1.10", "@vitest/pretty-format": "4.1.10", "@vitest/runner": "4.1.10", "@vitest/snapshot": "4.1.10", "@vitest/spy": "4.1.10", "@vitest/utils": "4.1.10", "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.10", "@vitest/browser-preview": "4.1.10", "@vitest/browser-webdriverio": "4.1.10", "@vitest/coverage-istanbul": "4.1.10", "@vitest/coverage-v8": "4.1.10", "@vitest/ui": "4.1.10", "happy-dom": "*", "jsdom": "*" }, "optionalPeers": ["@edge-runtime/vm", "@opentelemetry/api", "@types/node", "@vitest/browser-playwright", "@vitest/browser-preview", "@vitest/browser-webdriverio", "@vitest/coverage-istanbul", "@vitest/coverage-v8", "@vitest/ui", "happy-dom", "jsdom"], "bin": { "vitest": "./vitest.mjs" } }, "sha512-R9jUTe5S4Qb0HCd4TNqpC7oGcrMssMRGXLW80ubjWsW9VH5GF8y1Y0SFLY9AbqSk6nt0PnOx4H4WNJYZ13GUPw=="], @@ -1314,9 +1382,13 @@ "@alcalzone/ansi-tokenize/ansi-styles": ["ansi-styles@6.2.3", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="], - "@babel/core/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], + "@babel/helper-module-imports/@babel/traverse": ["@babel/traverse@7.29.7", "", { "dependencies": { "@babel/code-frame": "^7.29.7", "@babel/generator": "^7.29.7", "@babel/helper-globals": "^7.29.7", "@babel/parser": "^7.29.7", "@babel/template": "^7.29.7", "@babel/types": "^7.29.7", "debug": "^4.3.1" } }, "sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw=="], + + "@babel/helper-module-imports/@babel/types": ["@babel/types@7.29.7", "", { "dependencies": { "@babel/helper-string-parser": "^7.29.7", "@babel/helper-validator-identifier": "^7.29.7" } }, "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA=="], - "@babel/helper-compilation-targets/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], + "@babel/helper-module-transforms/@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.29.7", "", {}, "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg=="], + + "@babel/helper-module-transforms/@babel/traverse": ["@babel/traverse@7.29.7", "", { "dependencies": { "@babel/code-frame": "^7.29.7", "@babel/generator": "^7.29.7", "@babel/helper-globals": "^7.29.7", "@babel/parser": "^7.29.7", "@babel/template": "^7.29.7", "@babel/types": "^7.29.7", "debug": "^4.3.1" } }, "sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw=="], "@clerk/shared/csstype": ["csstype@3.1.3", "", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="], @@ -1326,19 +1398,15 @@ "@eslint/config-array/minimatch": ["minimatch@10.2.6", "", { "dependencies": { "brace-expansion": "^5.0.8" } }, "sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A=="], - "@oxc-parser/binding-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.11.2", "", { "dependencies": { "@emnapi/wasi-threads": "1.2.2", "tslib": "^2.4.0" } }, "sha512-TC8MkTuZUtcTSiFeuC0ksCh9QIJ5+F21MvZ4Wn4ORfYaFJ/0dsiudv5tVkejgwZlwQ39jL9WWDe2lz8x0WglOA=="], - - "@oxc-parser/binding-wasm32-wasi/@emnapi/runtime": ["@emnapi/runtime@1.11.2", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-kyOl3X0DuTiT1h2ft8r2fYO8JYtU9a9Xis/zBSiGArNaagCOWx90N1k2wxp18czFDH+OgcWGb5ZP/XMt3dcyPA=="], - - "@oxc-parser/binding-wasm32-wasi/@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.2.0", "", { "dependencies": { "@tybys/wasm-util": "^0.10.3" }, "peerDependencies": { "@emnapi/core": "^2.0.0-alpha.3", "@emnapi/runtime": "^2.0.0-alpha.3" } }, "sha512-kDoONqMa+VnZ4vvvu/ZUurpJ4gkZU57e7g69qpNgWhYcZFPUHZM2CEMKm+cG6ufDVALbjMvfmMjFVqaK7uEMnA=="], + "@react-grab/cli/agent-install": ["agent-install@0.0.6", "", { "dependencies": { "@iarna/toml": "^2.2.5", "commander": "^14.0.0", "jsonc-parser": "^3.3.1", "picocolors": "^1.1.1", "prompts": "^2.4.2", "yaml": "^2.8.3" }, "bin": { "agent-install": "bin/agent-install.mjs" } }, "sha512-7NRMZ/ZDz2vHevQTgJsocBFpakB1/Wx5ip19YSJuj4VOXpraWztTerViNtdSyARKZT9e2yVwUUB5JXXCE7mNrA=="], - "@oxc-resolver/binding-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.11.2", "", { "dependencies": { "@emnapi/wasi-threads": "1.2.2", "tslib": "^2.4.0" } }, "sha512-TC8MkTuZUtcTSiFeuC0ksCh9QIJ5+F21MvZ4Wn4ORfYaFJ/0dsiudv5tVkejgwZlwQ39jL9WWDe2lz8x0WglOA=="], + "@rolldown/binding-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.11.1", "", { "dependencies": { "@emnapi/wasi-threads": "1.2.2", "tslib": "^2.4.0" } }, "sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ=="], - "@oxc-resolver/binding-wasm32-wasi/@emnapi/runtime": ["@emnapi/runtime@1.11.2", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-kyOl3X0DuTiT1h2ft8r2fYO8JYtU9a9Xis/zBSiGArNaagCOWx90N1k2wxp18czFDH+OgcWGb5ZP/XMt3dcyPA=="], + "@rolldown/binding-wasm32-wasi/@emnapi/runtime": ["@emnapi/runtime@1.11.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw=="], - "@oxc-resolver/binding-wasm32-wasi/@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.2.0", "", { "dependencies": { "@tybys/wasm-util": "^0.10.3" }, "peerDependencies": { "@emnapi/core": "^2.0.0-alpha.3", "@emnapi/runtime": "^2.0.0-alpha.3" } }, "sha512-kDoONqMa+VnZ4vvvu/ZUurpJ4gkZU57e7g69qpNgWhYcZFPUHZM2CEMKm+cG6ufDVALbjMvfmMjFVqaK7uEMnA=="], + "@rolldown/binding-wasm32-wasi/@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.1.5", "", { "dependencies": { "@tybys/wasm-util": "^0.10.2" }, "peerDependencies": { "@emnapi/core": "^1.7.1", "@emnapi/runtime": "^1.7.1" } }, "sha512-AWPoBRJ9tsnVhor4sjO7rkni+7p+2IAEFj6cx06UgP10jkQHqay/36uRV/bFkgrh18D9vb4cr8Q0Pthskgzy+Q=="], - "@react-grab/cli/agent-install": ["agent-install@0.0.6", "", { "dependencies": { "@iarna/toml": "^2.2.5", "commander": "^14.0.0", "jsonc-parser": "^3.3.1", "picocolors": "^1.1.1", "prompts": "^2.4.2", "yaml": "^2.8.3" }, "bin": { "agent-install": "bin/agent-install.mjs" } }, "sha512-7NRMZ/ZDz2vHevQTgJsocBFpakB1/Wx5ip19YSJuj4VOXpraWztTerViNtdSyARKZT9e2yVwUUB5JXXCE7mNrA=="], + "@rollup/pluginutils/picomatch": ["picomatch@4.0.4", "", {}, "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A=="], "@sentry/node/@sentry/conventions": ["@sentry/conventions@0.16.0", "", {}, "sha512-fO9PLmHdVURcSPUpWCItWAtgKiMwGdJHbovoSEyLplX5sxs2ugvI4CBPTrkkgqhObnZOD0CnWBKDzSVQYBKEyQ=="], @@ -1356,6 +1424,8 @@ "@sentry/server-utils/@sentry/core": ["@sentry/core@10.69.0", "", { "dependencies": { "@sentry/conventions": "^0.16.0" } }, "sha512-+uuqVEeiDzYuAKjZLqsROKXvRTbl/QeH0gfGRtpYib1cud4rAFWRIkFmcR7Jb7JGFYwmReyQotiTj/hcDszTZg=="], + "@tailwindcss/node/lightningcss": ["lightningcss@1.32.0", "", { "dependencies": { "detect-libc": "^2.0.3" }, "optionalDependencies": { "lightningcss-android-arm64": "1.32.0", "lightningcss-darwin-arm64": "1.32.0", "lightningcss-darwin-x64": "1.32.0", "lightningcss-freebsd-x64": "1.32.0", "lightningcss-linux-arm-gnueabihf": "1.32.0", "lightningcss-linux-arm64-gnu": "1.32.0", "lightningcss-linux-arm64-musl": "1.32.0", "lightningcss-linux-x64-gnu": "1.32.0", "lightningcss-linux-x64-musl": "1.32.0", "lightningcss-win32-arm64-msvc": "1.32.0", "lightningcss-win32-x64-msvc": "1.32.0" } }, "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ=="], + "@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.11.1", "", { "dependencies": { "@emnapi/wasi-threads": "1.2.2", "tslib": "^2.4.0" }, "bundled": true }, "sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ=="], "@tailwindcss/oxide-wasm32-wasi/@emnapi/runtime": ["@emnapi/runtime@1.11.1", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw=="], @@ -1368,9 +1438,23 @@ "@tailwindcss/oxide-wasm32-wasi/tslib": ["tslib@2.8.1", "", { "bundled": true }, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], + "@types/babel__core/@babel/parser": ["@babel/parser@7.29.7", "", { "dependencies": { "@babel/types": "^7.29.7" }, "bin": "./bin/babel-parser.js" }, "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg=="], + + "@types/babel__core/@babel/types": ["@babel/types@7.29.7", "", { "dependencies": { "@babel/helper-string-parser": "^7.29.7", "@babel/helper-validator-identifier": "^7.29.7" } }, "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA=="], + + "@types/babel__generator/@babel/types": ["@babel/types@7.29.7", "", { "dependencies": { "@babel/helper-string-parser": "^7.29.7", "@babel/helper-validator-identifier": "^7.29.7" } }, "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA=="], + + "@types/babel__template/@babel/parser": ["@babel/parser@7.29.7", "", { "dependencies": { "@babel/types": "^7.29.7" }, "bin": "./bin/babel-parser.js" }, "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg=="], + + "@types/babel__template/@babel/types": ["@babel/types@7.29.7", "", { "dependencies": { "@babel/helper-string-parser": "^7.29.7", "@babel/helper-validator-identifier": "^7.29.7" } }, "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA=="], + + "@types/babel__traverse/@babel/types": ["@babel/types@7.29.7", "", { "dependencies": { "@babel/helper-string-parser": "^7.29.7", "@babel/helper-validator-identifier": "^7.29.7" } }, "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA=="], + "@vitest/mocker/estree-walker": ["estree-walker@3.0.3", "", { "dependencies": { "@types/estree": "^1.0.0" } }, "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g=="], - "conf/semver": ["semver@7.8.5", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA=="], + "babel-plugin-react-compiler/@babel/types": ["@babel/types@7.29.7", "", { "dependencies": { "@babel/helper-string-parser": "^7.29.7", "@babel/helper-validator-identifier": "^7.29.7" } }, "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA=="], + + "cross-spawn/semver": ["semver@5.7.2", "", { "bin": { "semver": "bin/semver" } }, "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g=="], "deslop-js/@oxc-project/types": ["@oxc-project/types@0.141.0", "", {}, "sha512-S4as7z0j0xQkXcJlyY5ehntwK8/wRkQb9Cyqw+J/N2rkWGQGK0SxD6X6DhQTc7qsxVTBxXbxZtBJh3mr3PtIzQ=="], @@ -1390,12 +1474,22 @@ "eslint/minimatch": ["minimatch@10.2.6", "", { "dependencies": { "brace-expansion": "^5.0.8" } }, "sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A=="], + "eslint-plugin-react-hooks/@babel/core": ["@babel/core@7.29.7", "", { "dependencies": { "@babel/code-frame": "^7.29.7", "@babel/generator": "^7.29.7", "@babel/helper-compilation-targets": "^7.29.7", "@babel/helper-module-transforms": "^7.29.7", "@babel/helpers": "^7.29.7", "@babel/parser": "^7.29.7", "@babel/template": "^7.29.7", "@babel/traverse": "^7.29.7", "@babel/types": "^7.29.7", "@jridgewell/remapping": "^2.3.5", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", "json5": "^2.2.3", "semver": "^6.3.1" } }, "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA=="], + + "eslint-plugin-react-hooks/@babel/parser": ["@babel/parser@7.29.7", "", { "dependencies": { "@babel/types": "^7.29.7" }, "bin": "./bin/babel-parser.js" }, "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg=="], + "ink/ansi-styles": ["ansi-styles@6.2.3", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="], "ink/chalk": ["chalk@5.6.2", "", {}, "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA=="], + "magicast/@babel/parser": ["@babel/parser@7.29.7", "", { "dependencies": { "@babel/types": "^7.29.7" }, "bin": "./bin/babel-parser.js" }, "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg=="], + + "magicast/@babel/types": ["@babel/types@7.29.7", "", { "dependencies": { "@babel/helper-string-parser": "^7.29.7", "@babel/helper-validator-identifier": "^7.29.7" } }, "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA=="], + "micromatch/picomatch": ["picomatch@2.3.2", "", {}, "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA=="], + "normalize-package-data/semver": ["semver@5.7.2", "", { "bin": { "semver": "bin/semver" } }, "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g=="], + "ora/chalk": ["chalk@5.6.2", "", {}, "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA=="], "ora/cli-cursor": ["cli-cursor@5.0.0", "", { "dependencies": { "restore-cursor": "^5.0.0" } }, "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw=="], @@ -1404,22 +1498,124 @@ "oxc-parser/@oxc-project/types": ["@oxc-project/types@0.141.0", "", {}, "sha512-S4as7z0j0xQkXcJlyY5ehntwK8/wRkQb9Cyqw+J/N2rkWGQGK0SxD6X6DhQTc7qsxVTBxXbxZtBJh3mr3PtIzQ=="], + "react-doctor/@babel/code-frame": ["@babel/code-frame@7.29.7", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.29.7", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw=="], + + "react-doctor/oxlint": ["oxlint@1.74.0", "", { "optionalDependencies": { "@oxlint/binding-android-arm-eabi": "1.74.0", "@oxlint/binding-android-arm64": "1.74.0", "@oxlint/binding-darwin-arm64": "1.74.0", "@oxlint/binding-darwin-x64": "1.74.0", "@oxlint/binding-freebsd-x64": "1.74.0", "@oxlint/binding-linux-arm-gnueabihf": "1.74.0", "@oxlint/binding-linux-arm-musleabihf": "1.74.0", "@oxlint/binding-linux-arm64-gnu": "1.74.0", "@oxlint/binding-linux-arm64-musl": "1.74.0", "@oxlint/binding-linux-ppc64-gnu": "1.74.0", "@oxlint/binding-linux-riscv64-gnu": "1.74.0", "@oxlint/binding-linux-riscv64-musl": "1.74.0", "@oxlint/binding-linux-s390x-gnu": "1.74.0", "@oxlint/binding-linux-x64-gnu": "1.74.0", "@oxlint/binding-linux-x64-musl": "1.74.0", "@oxlint/binding-openharmony-arm64": "1.74.0", "@oxlint/binding-win32-arm64-msvc": "1.74.0", "@oxlint/binding-win32-ia32-msvc": "1.74.0", "@oxlint/binding-win32-x64-msvc": "1.74.0" }, "peerDependencies": { "oxlint-tsgolint": ">=0.24.0", "vite-plus": "*" }, "optionalPeers": ["oxlint-tsgolint", "vite-plus"], "bin": { "oxlint": "bin/oxlint" } }, "sha512-odGl2s2x5IOJoj3A0v1k0PGBXVFBZeZ2+AK/+K2MJur7Ghi3bkyX5NuLUWHKqa4js1wjep3hJeuTQJOlr+4+dA=="], + "react-doctor/react": ["react@19.2.5", "", {}, "sha512-llUJLzz1zTUBrskt2pwZgLq59AemifIftw4aB7JxOqf1HY2FDaGDxgwpAPVzHU1kdWabH7FauP4i1oEeer2WCA=="], "react-doctor/typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="], "react-grab/bippy": ["bippy@0.6.1", "", { "peerDependencies": { "react": ">=17.0.1" } }, "sha512-ky4m94Y/KfsddjGkKTsV4uFjZqkJjpOjQ2t5gKPdX6XH1MNxMNX5FrVefsxV4lpjemEmEdwe0e0YbzAMNs3oUQ=="], + "react-scan/@babel/core": ["@babel/core@7.29.7", "", { "dependencies": { "@babel/code-frame": "^7.29.7", "@babel/generator": "^7.29.7", "@babel/helper-compilation-targets": "^7.29.7", "@babel/helper-module-transforms": "^7.29.7", "@babel/helpers": "^7.29.7", "@babel/parser": "^7.29.7", "@babel/template": "^7.29.7", "@babel/traverse": "^7.29.7", "@babel/types": "^7.29.7", "@jridgewell/remapping": "^2.3.5", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", "json5": "^2.2.3", "semver": "^6.3.1" } }, "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA=="], + + "react-scan/@babel/types": ["@babel/types@7.29.7", "", { "dependencies": { "@babel/helper-string-parser": "^7.29.7", "@babel/helper-validator-identifier": "^7.29.7" } }, "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA=="], + "slice-ansi/ansi-styles": ["ansi-styles@6.2.3", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="], "stack-utils/escape-string-regexp": ["escape-string-regexp@2.0.0", "", {}, "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w=="], + "tinyglobby/picomatch": ["picomatch@4.0.4", "", {}, "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A=="], + + "unplugin/picomatch": ["picomatch@4.0.4", "", {}, "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A=="], + + "vitest/picomatch": ["picomatch@4.0.4", "", {}, "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A=="], + + "vitest/vite": ["vite@8.1.0", "", { "dependencies": { "lightningcss": "^1.32.0", "picomatch": "^4.0.4", "postcss": "^8.5.15", "rolldown": "~1.1.2", "tinyglobby": "^0.2.17" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "@vitejs/devtools": "^0.3.0", "esbuild": "^0.27.0 || ^0.28.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-BuJcQK/56NQTWDGn4ABea3q4SSBdNPWwNZKTkkUpcMPnLoquSYH8llRtSUIgoL1KSCpHt5eghLShn50mH36y7Q=="], + "wrap-ansi/ansi-styles": ["ansi-styles@6.2.3", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="], + "@babel/helper-module-imports/@babel/traverse/@babel/code-frame": ["@babel/code-frame@7.29.7", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.29.7", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw=="], + + "@babel/helper-module-imports/@babel/traverse/@babel/generator": ["@babel/generator@7.29.7", "", { "dependencies": { "@babel/parser": "^7.29.7", "@babel/types": "^7.29.7", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ=="], + + "@babel/helper-module-imports/@babel/traverse/@babel/helper-globals": ["@babel/helper-globals@7.29.7", "", {}, "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA=="], + + "@babel/helper-module-imports/@babel/traverse/@babel/parser": ["@babel/parser@7.29.7", "", { "dependencies": { "@babel/types": "^7.29.7" }, "bin": "./bin/babel-parser.js" }, "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg=="], + + "@babel/helper-module-imports/@babel/traverse/@babel/template": ["@babel/template@7.29.7", "", { "dependencies": { "@babel/code-frame": "^7.29.7", "@babel/parser": "^7.29.7", "@babel/types": "^7.29.7" } }, "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg=="], + + "@babel/helper-module-imports/@babel/types/@babel/helper-string-parser": ["@babel/helper-string-parser@7.29.7", "", {}, "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw=="], + + "@babel/helper-module-imports/@babel/types/@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.29.7", "", {}, "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg=="], + + "@babel/helper-module-transforms/@babel/traverse/@babel/code-frame": ["@babel/code-frame@7.29.7", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.29.7", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw=="], + + "@babel/helper-module-transforms/@babel/traverse/@babel/generator": ["@babel/generator@7.29.7", "", { "dependencies": { "@babel/parser": "^7.29.7", "@babel/types": "^7.29.7", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ=="], + + "@babel/helper-module-transforms/@babel/traverse/@babel/helper-globals": ["@babel/helper-globals@7.29.7", "", {}, "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA=="], + + "@babel/helper-module-transforms/@babel/traverse/@babel/parser": ["@babel/parser@7.29.7", "", { "dependencies": { "@babel/types": "^7.29.7" }, "bin": "./bin/babel-parser.js" }, "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg=="], + + "@babel/helper-module-transforms/@babel/traverse/@babel/template": ["@babel/template@7.29.7", "", { "dependencies": { "@babel/code-frame": "^7.29.7", "@babel/parser": "^7.29.7", "@babel/types": "^7.29.7" } }, "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg=="], + + "@babel/helper-module-transforms/@babel/traverse/@babel/types": ["@babel/types@7.29.7", "", { "dependencies": { "@babel/helper-string-parser": "^7.29.7", "@babel/helper-validator-identifier": "^7.29.7" } }, "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA=="], + "@eslint/config-array/minimatch/brace-expansion": ["brace-expansion@5.0.8", "", { "dependencies": { "balanced-match": "^4.0.2" } }, "sha512-JZyDyq3D4AUifKTPOB7DELf6XsB3WdPuNxCtob1vFXPsSXhdAiHBWJ/tJ8HAc9aH84BK+5JFZLNkJKx3G9kzQg=="], + "@tailwindcss/node/lightningcss/lightningcss-android-arm64": ["lightningcss-android-arm64@1.32.0", "", { "os": "android", "cpu": "arm64" }, "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg=="], + + "@tailwindcss/node/lightningcss/lightningcss-darwin-arm64": ["lightningcss-darwin-arm64@1.32.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ=="], + + "@tailwindcss/node/lightningcss/lightningcss-darwin-x64": ["lightningcss-darwin-x64@1.32.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w=="], + + "@tailwindcss/node/lightningcss/lightningcss-freebsd-x64": ["lightningcss-freebsd-x64@1.32.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig=="], + + "@tailwindcss/node/lightningcss/lightningcss-linux-arm-gnueabihf": ["lightningcss-linux-arm-gnueabihf@1.32.0", "", { "os": "linux", "cpu": "arm" }, "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw=="], + + "@tailwindcss/node/lightningcss/lightningcss-linux-arm64-gnu": ["lightningcss-linux-arm64-gnu@1.32.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ=="], + + "@tailwindcss/node/lightningcss/lightningcss-linux-arm64-musl": ["lightningcss-linux-arm64-musl@1.32.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg=="], + + "@tailwindcss/node/lightningcss/lightningcss-linux-x64-gnu": ["lightningcss-linux-x64-gnu@1.32.0", "", { "os": "linux", "cpu": "x64" }, "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA=="], + + "@tailwindcss/node/lightningcss/lightningcss-linux-x64-musl": ["lightningcss-linux-x64-musl@1.32.0", "", { "os": "linux", "cpu": "x64" }, "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg=="], + + "@tailwindcss/node/lightningcss/lightningcss-win32-arm64-msvc": ["lightningcss-win32-arm64-msvc@1.32.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw=="], + + "@tailwindcss/node/lightningcss/lightningcss-win32-x64-msvc": ["lightningcss-win32-x64-msvc@1.32.0", "", { "os": "win32", "cpu": "x64" }, "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q=="], + + "@types/babel__core/@babel/types/@babel/helper-string-parser": ["@babel/helper-string-parser@7.29.7", "", {}, "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw=="], + + "@types/babel__core/@babel/types/@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.29.7", "", {}, "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg=="], + + "@types/babel__generator/@babel/types/@babel/helper-string-parser": ["@babel/helper-string-parser@7.29.7", "", {}, "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw=="], + + "@types/babel__generator/@babel/types/@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.29.7", "", {}, "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg=="], + + "@types/babel__template/@babel/types/@babel/helper-string-parser": ["@babel/helper-string-parser@7.29.7", "", {}, "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw=="], + + "@types/babel__template/@babel/types/@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.29.7", "", {}, "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg=="], + + "@types/babel__traverse/@babel/types/@babel/helper-string-parser": ["@babel/helper-string-parser@7.29.7", "", {}, "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw=="], + + "@types/babel__traverse/@babel/types/@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.29.7", "", {}, "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg=="], + + "babel-plugin-react-compiler/@babel/types/@babel/helper-string-parser": ["@babel/helper-string-parser@7.29.7", "", {}, "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw=="], + + "babel-plugin-react-compiler/@babel/types/@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.29.7", "", {}, "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg=="], + "deslop-js/minimatch/brace-expansion": ["brace-expansion@5.0.8", "", { "dependencies": { "balanced-match": "^4.0.2" } }, "sha512-JZyDyq3D4AUifKTPOB7DELf6XsB3WdPuNxCtob1vFXPsSXhdAiHBWJ/tJ8HAc9aH84BK+5JFZLNkJKx3G9kzQg=="], + "eslint-plugin-react-hooks/@babel/core/@babel/code-frame": ["@babel/code-frame@7.29.7", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.29.7", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw=="], + + "eslint-plugin-react-hooks/@babel/core/@babel/generator": ["@babel/generator@7.29.7", "", { "dependencies": { "@babel/parser": "^7.29.7", "@babel/types": "^7.29.7", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ=="], + + "eslint-plugin-react-hooks/@babel/core/@babel/helper-compilation-targets": ["@babel/helper-compilation-targets@7.29.7", "", { "dependencies": { "@babel/compat-data": "^7.29.7", "@babel/helper-validator-option": "^7.29.7", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" } }, "sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g=="], + + "eslint-plugin-react-hooks/@babel/core/@babel/helpers": ["@babel/helpers@7.29.7", "", { "dependencies": { "@babel/template": "^7.29.7", "@babel/types": "^7.29.7" } }, "sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg=="], + + "eslint-plugin-react-hooks/@babel/core/@babel/template": ["@babel/template@7.29.7", "", { "dependencies": { "@babel/code-frame": "^7.29.7", "@babel/parser": "^7.29.7", "@babel/types": "^7.29.7" } }, "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg=="], + + "eslint-plugin-react-hooks/@babel/core/@babel/traverse": ["@babel/traverse@7.29.7", "", { "dependencies": { "@babel/code-frame": "^7.29.7", "@babel/generator": "^7.29.7", "@babel/helper-globals": "^7.29.7", "@babel/parser": "^7.29.7", "@babel/template": "^7.29.7", "@babel/types": "^7.29.7", "debug": "^4.3.1" } }, "sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw=="], + + "eslint-plugin-react-hooks/@babel/core/@babel/types": ["@babel/types@7.29.7", "", { "dependencies": { "@babel/helper-string-parser": "^7.29.7", "@babel/helper-validator-identifier": "^7.29.7" } }, "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA=="], + + "eslint-plugin-react-hooks/@babel/core/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], + + "eslint-plugin-react-hooks/@babel/parser/@babel/types": ["@babel/types@7.29.7", "", { "dependencies": { "@babel/helper-string-parser": "^7.29.7", "@babel/helper-validator-identifier": "^7.29.7" } }, "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA=="], + "eslint/ajv/json-schema-traverse": ["json-schema-traverse@0.4.1", "", {}, "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="], "eslint/cross-spawn/path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="], @@ -1430,12 +1626,112 @@ "eslint/minimatch/brace-expansion": ["brace-expansion@5.0.8", "", { "dependencies": { "balanced-match": "^4.0.2" } }, "sha512-JZyDyq3D4AUifKTPOB7DELf6XsB3WdPuNxCtob1vFXPsSXhdAiHBWJ/tJ8HAc9aH84BK+5JFZLNkJKx3G9kzQg=="], + "magicast/@babel/types/@babel/helper-string-parser": ["@babel/helper-string-parser@7.29.7", "", {}, "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw=="], + + "magicast/@babel/types/@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.29.7", "", {}, "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg=="], + "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=="], + "react-doctor/@babel/code-frame/@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.29.7", "", {}, "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg=="], + + "react-doctor/@babel/code-frame/js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="], + + "react-doctor/oxlint/@oxlint/binding-android-arm-eabi": ["@oxlint/binding-android-arm-eabi@1.74.0", "", { "os": "android", "cpu": "arm" }, "sha512-+gHd12muVI9ZLBaWLPkHt3Fj7jihFjgQ1MGtBaRL8vWrWrI0P7dLUty/cHrHS0oqPYIRgQUJsPu2CExQuMcwNw=="], + + "react-doctor/oxlint/@oxlint/binding-android-arm64": ["@oxlint/binding-android-arm64@1.74.0", "", { "os": "android", "cpu": "arm64" }, "sha512-xjKdoMB+H+RCOByv/7l7nfIGW9mlOisqYdcyC75UqYuQecLpReAeEYUf2CNeDEI3KtmUgxpRw/+c63y4AeF/Bw=="], + + "react-doctor/oxlint/@oxlint/binding-darwin-arm64": ["@oxlint/binding-darwin-arm64@1.74.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-iUK7wvc6sejMKsC+Pt67mntoF5weFcyEunhZfLJceU6gL419mexz5wBkSx/EnkFBExMLNtOi9fnDSc5xfK0IzQ=="], + + "react-doctor/oxlint/@oxlint/binding-darwin-x64": ["@oxlint/binding-darwin-x64@1.74.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-ggKc/tn5SJ1u2yG2izC6VKODfYKV8MQ2AicJlNzOjuyrC29udvOef6/JzK2r32xqCnBDLFouR1VCkjzEI0/N9Q=="], + + "react-doctor/oxlint/@oxlint/binding-freebsd-x64": ["@oxlint/binding-freebsd-x64@1.74.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-u++dH/43jy9hTLbneaWlS0gla/Bp1JdwJ2zgevCl8nDFUh6qRCGMxcL0f0lb7By3A9p/LfFr+7cG4HU1hG856g=="], + + "react-doctor/oxlint/@oxlint/binding-linux-arm-gnueabihf": ["@oxlint/binding-linux-arm-gnueabihf@1.74.0", "", { "os": "linux", "cpu": "arm" }, "sha512-Sj1zmtFDVTPeIbIz4ZfcXAbFHqCmKCXdCUlAJzvTF7I20NTH1RDpoF2PhkqNODutJzVhJYmm3oz0GwgY+tvE2g=="], + + "react-doctor/oxlint/@oxlint/binding-linux-arm-musleabihf": ["@oxlint/binding-linux-arm-musleabihf@1.74.0", "", { "os": "linux", "cpu": "arm" }, "sha512-//PKyQb/tQXcHArx2f7z+oVI/eMS2Jpv+edNuAtOrgIhWdGcpHxogveAxzmF2rpH1AIHp4Hq04RF/rgJdiICnQ=="], + + "react-doctor/oxlint/@oxlint/binding-linux-arm64-gnu": ["@oxlint/binding-linux-arm64-gnu@1.74.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-/k1Me+aX2tjuH10K62mLS0y8cLkJBHX6Ce0xPK+eWeel4bSdEGZ8dv4+hYMzg0GrSmjwy4yAYsDPeEeKBft/2w=="], + + "react-doctor/oxlint/@oxlint/binding-linux-arm64-musl": ["@oxlint/binding-linux-arm64-musl@1.74.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-3tFSjBxc5D8/zvjEuLvOqcA8ZXKD0+6NuaVO/edeamNc49MoAsbfaC9s1UiwODwgF6slGaF8yJA2TPkukd77tg=="], + + "react-doctor/oxlint/@oxlint/binding-linux-ppc64-gnu": ["@oxlint/binding-linux-ppc64-gnu@1.74.0", "", { "os": "linux", "cpu": "ppc64" }, "sha512-9QggtPkSPXOCTu8Szis7auOK/sC7KdQaN+/TujP7YVVhzCAOhgdRfgv8uEz0r2tk5xdgus5rLYUrCDoZNtiRUw=="], + + "react-doctor/oxlint/@oxlint/binding-linux-riscv64-gnu": ["@oxlint/binding-linux-riscv64-gnu@1.74.0", "", { "os": "linux", "cpu": "none" }, "sha512-VM5VPUJ4DJIWiK+AZn8FScUqMr6OFrCAYybMYjEEi7W13ParI64MByiXTkKMqZpBmvQ9zxl9Ebq2VUOiZRJYUg=="], + + "react-doctor/oxlint/@oxlint/binding-linux-riscv64-musl": ["@oxlint/binding-linux-riscv64-musl@1.74.0", "", { "os": "linux", "cpu": "none" }, "sha512-SaDY1gh9rOA592J54g+gu5hkOFFQBZsMmIYHs+NRHG+Uq0OxtuuCXMWQ3vu1830Eugv5uMXyjG+bv2Z9y4IXjw=="], + + "react-doctor/oxlint/@oxlint/binding-linux-s390x-gnu": ["@oxlint/binding-linux-s390x-gnu@1.74.0", "", { "os": "linux", "cpu": "s390x" }, "sha512-ZATQeHZCyr6MbDveg0obD5sxLHFOghtOdC5jwVwYlvFWqtFOxctgFEG6Ef/64hYvZrWyhyCckB10AelqLopeDA=="], + + "react-doctor/oxlint/@oxlint/binding-linux-x64-gnu": ["@oxlint/binding-linux-x64-gnu@1.74.0", "", { "os": "linux", "cpu": "x64" }, "sha512-+aIvJyrdeD7LwCQ2WYLMUWNmnbeDRSPb40aBYtPjD9+PTqUwgJnk+HK5yLfSMeqXrMrDhE9uTmtt2y50tvjhHw=="], + + "react-doctor/oxlint/@oxlint/binding-linux-x64-musl": ["@oxlint/binding-linux-x64-musl@1.74.0", "", { "os": "linux", "cpu": "x64" }, "sha512-XyktaR8lhK2qWiCK0Tk8oYD+/cgn+oHA6ddRnxSSXUKkkojkV78CmShZUxQF+yrBFs0SuW+JBOPG6hecyc/iZg=="], + + "react-doctor/oxlint/@oxlint/binding-openharmony-arm64": ["@oxlint/binding-openharmony-arm64@1.74.0", "", { "os": "none", "cpu": "arm64" }, "sha512-mzbjrPl4neaVUiJ1fUiEUxTGaSZBoiKtaoB6jmIpz9S+VOA2vDYmJpihQ82w6178V5jxziclTg8Cgj5yF6tTDg=="], + + "react-doctor/oxlint/@oxlint/binding-win32-arm64-msvc": ["@oxlint/binding-win32-arm64-msvc@1.74.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-vUAe9okpS2Oa5+lX67lqHMuNUvfkleRKwrUDJ/WJBsgmddvZ1mrsh2HVmuFDRzqFELhaJhFaCNOuR6a7L3rtIA=="], + + "react-doctor/oxlint/@oxlint/binding-win32-ia32-msvc": ["@oxlint/binding-win32-ia32-msvc@1.74.0", "", { "os": "win32", "cpu": "ia32" }, "sha512-yyXXJyYYSXL4I8K8jAWjJs+J3fa9gH2JmEbo4f5adm+1tNC9itseicBNuwK7BDHvqQ5J534s+yDULu89vYL2ZQ=="], + + "react-doctor/oxlint/@oxlint/binding-win32-x64-msvc": ["@oxlint/binding-win32-x64-msvc@1.74.0", "", { "os": "win32", "cpu": "x64" }, "sha512-VTC9IYTIMrVUk/i6Ms1ohzzDKZFkWn0KU2OBbPBzgmVZ2V30165T/zK4LztTr0Xgp9fZ1qQZ1rsZAu/rEmySlA=="], + + "react-scan/@babel/core/@babel/code-frame": ["@babel/code-frame@7.29.7", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.29.7", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw=="], + + "react-scan/@babel/core/@babel/generator": ["@babel/generator@7.29.7", "", { "dependencies": { "@babel/parser": "^7.29.7", "@babel/types": "^7.29.7", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ=="], + + "react-scan/@babel/core/@babel/helper-compilation-targets": ["@babel/helper-compilation-targets@7.29.7", "", { "dependencies": { "@babel/compat-data": "^7.29.7", "@babel/helper-validator-option": "^7.29.7", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" } }, "sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g=="], + + "react-scan/@babel/core/@babel/helpers": ["@babel/helpers@7.29.7", "", { "dependencies": { "@babel/template": "^7.29.7", "@babel/types": "^7.29.7" } }, "sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg=="], + + "react-scan/@babel/core/@babel/parser": ["@babel/parser@7.29.7", "", { "dependencies": { "@babel/types": "^7.29.7" }, "bin": "./bin/babel-parser.js" }, "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg=="], + + "react-scan/@babel/core/@babel/template": ["@babel/template@7.29.7", "", { "dependencies": { "@babel/code-frame": "^7.29.7", "@babel/parser": "^7.29.7", "@babel/types": "^7.29.7" } }, "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg=="], + + "react-scan/@babel/core/@babel/traverse": ["@babel/traverse@7.29.7", "", { "dependencies": { "@babel/code-frame": "^7.29.7", "@babel/generator": "^7.29.7", "@babel/helper-globals": "^7.29.7", "@babel/parser": "^7.29.7", "@babel/template": "^7.29.7", "@babel/types": "^7.29.7", "debug": "^4.3.1" } }, "sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw=="], + + "react-scan/@babel/core/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], + + "react-scan/@babel/types/@babel/helper-string-parser": ["@babel/helper-string-parser@7.29.7", "", {}, "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw=="], + + "react-scan/@babel/types/@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.29.7", "", {}, "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg=="], + + "vitest/vite/lightningcss": ["lightningcss@1.32.0", "", { "dependencies": { "detect-libc": "^2.0.3" }, "optionalDependencies": { "lightningcss-android-arm64": "1.32.0", "lightningcss-darwin-arm64": "1.32.0", "lightningcss-darwin-x64": "1.32.0", "lightningcss-freebsd-x64": "1.32.0", "lightningcss-linux-arm-gnueabihf": "1.32.0", "lightningcss-linux-arm64-gnu": "1.32.0", "lightningcss-linux-arm64-musl": "1.32.0", "lightningcss-linux-x64-gnu": "1.32.0", "lightningcss-linux-x64-musl": "1.32.0", "lightningcss-win32-arm64-msvc": "1.32.0", "lightningcss-win32-x64-msvc": "1.32.0" } }, "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ=="], + + "vitest/vite/postcss": ["postcss@8.5.15", "", { "dependencies": { "nanoid": "^3.3.12", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A=="], + + "vitest/vite/rolldown": ["rolldown@1.1.2", "", { "dependencies": { "@oxc-project/types": "=0.137.0", "@rolldown/pluginutils": "^1.0.0" }, "optionalDependencies": { "@rolldown/binding-android-arm64": "1.1.2", "@rolldown/binding-darwin-arm64": "1.1.2", "@rolldown/binding-darwin-x64": "1.1.2", "@rolldown/binding-freebsd-x64": "1.1.2", "@rolldown/binding-linux-arm-gnueabihf": "1.1.2", "@rolldown/binding-linux-arm64-gnu": "1.1.2", "@rolldown/binding-linux-arm64-musl": "1.1.2", "@rolldown/binding-linux-ppc64-gnu": "1.1.2", "@rolldown/binding-linux-s390x-gnu": "1.1.2", "@rolldown/binding-linux-x64-gnu": "1.1.2", "@rolldown/binding-linux-x64-musl": "1.1.2", "@rolldown/binding-openharmony-arm64": "1.1.2", "@rolldown/binding-wasm32-wasi": "1.1.2", "@rolldown/binding-win32-arm64-msvc": "1.1.2", "@rolldown/binding-win32-x64-msvc": "1.1.2" }, "bin": { "rolldown": "./bin/cli.mjs" } }, "sha512-x0CrQQqCXWGeI8dTvFfN/Dnv3yMKT9hv5jFjlOreKAx9wqLq9wz7VvLLHyaAXC90/CpggTu9SisSbsJJTPSjNQ=="], + + "@babel/helper-module-imports/@babel/traverse/@babel/code-frame/@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.29.7", "", {}, "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg=="], + + "@babel/helper-module-imports/@babel/traverse/@babel/code-frame/js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="], + + "@babel/helper-module-transforms/@babel/traverse/@babel/code-frame/js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="], + + "@babel/helper-module-transforms/@babel/traverse/@babel/types/@babel/helper-string-parser": ["@babel/helper-string-parser@7.29.7", "", {}, "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw=="], + "@eslint/config-array/minimatch/brace-expansion/balanced-match": ["balanced-match@4.0.4", "", {}, "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA=="], "deslop-js/minimatch/brace-expansion/balanced-match": ["balanced-match@4.0.4", "", {}, "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA=="], + "eslint-plugin-react-hooks/@babel/core/@babel/code-frame/@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.29.7", "", {}, "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg=="], + + "eslint-plugin-react-hooks/@babel/core/@babel/code-frame/js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="], + + "eslint-plugin-react-hooks/@babel/core/@babel/helper-compilation-targets/@babel/compat-data": ["@babel/compat-data@7.29.7", "", {}, "sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg=="], + + "eslint-plugin-react-hooks/@babel/core/@babel/helper-compilation-targets/@babel/helper-validator-option": ["@babel/helper-validator-option@7.29.7", "", {}, "sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw=="], + + "eslint-plugin-react-hooks/@babel/core/@babel/helper-compilation-targets/lru-cache": ["lru-cache@5.1.1", "", { "dependencies": { "yallist": "^3.0.2" } }, "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w=="], + + "eslint-plugin-react-hooks/@babel/core/@babel/traverse/@babel/helper-globals": ["@babel/helper-globals@7.29.7", "", {}, "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA=="], + + "eslint-plugin-react-hooks/@babel/core/@babel/types/@babel/helper-string-parser": ["@babel/helper-string-parser@7.29.7", "", {}, "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw=="], + + "eslint-plugin-react-hooks/@babel/core/@babel/types/@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.29.7", "", {}, "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg=="], + + "eslint-plugin-react-hooks/@babel/parser/@babel/types/@babel/helper-string-parser": ["@babel/helper-string-parser@7.29.7", "", {}, "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw=="], + + "eslint-plugin-react-hooks/@babel/parser/@babel/types/@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.29.7", "", {}, "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg=="], + "eslint/cross-spawn/shebang-command/shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="], "eslint/minimatch/brace-expansion/balanced-match": ["balanced-match@4.0.4", "", {}, "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA=="], @@ -1443,5 +1739,71 @@ "ora/cli-cursor/restore-cursor/onetime": ["onetime@7.0.0", "", { "dependencies": { "mimic-function": "^5.0.0" } }, "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ=="], "ora/cli-cursor/restore-cursor/signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="], + + "react-scan/@babel/core/@babel/code-frame/@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.29.7", "", {}, "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg=="], + + "react-scan/@babel/core/@babel/code-frame/js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="], + + "react-scan/@babel/core/@babel/helper-compilation-targets/@babel/compat-data": ["@babel/compat-data@7.29.7", "", {}, "sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg=="], + + "react-scan/@babel/core/@babel/helper-compilation-targets/@babel/helper-validator-option": ["@babel/helper-validator-option@7.29.7", "", {}, "sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw=="], + + "react-scan/@babel/core/@babel/helper-compilation-targets/lru-cache": ["lru-cache@5.1.1", "", { "dependencies": { "yallist": "^3.0.2" } }, "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w=="], + + "react-scan/@babel/core/@babel/traverse/@babel/helper-globals": ["@babel/helper-globals@7.29.7", "", {}, "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA=="], + + "vitest/vite/lightningcss/lightningcss-android-arm64": ["lightningcss-android-arm64@1.32.0", "", { "os": "android", "cpu": "arm64" }, "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg=="], + + "vitest/vite/lightningcss/lightningcss-darwin-arm64": ["lightningcss-darwin-arm64@1.32.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ=="], + + "vitest/vite/lightningcss/lightningcss-darwin-x64": ["lightningcss-darwin-x64@1.32.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w=="], + + "vitest/vite/lightningcss/lightningcss-freebsd-x64": ["lightningcss-freebsd-x64@1.32.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig=="], + + "vitest/vite/lightningcss/lightningcss-linux-arm-gnueabihf": ["lightningcss-linux-arm-gnueabihf@1.32.0", "", { "os": "linux", "cpu": "arm" }, "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw=="], + + "vitest/vite/lightningcss/lightningcss-linux-arm64-gnu": ["lightningcss-linux-arm64-gnu@1.32.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ=="], + + "vitest/vite/lightningcss/lightningcss-linux-arm64-musl": ["lightningcss-linux-arm64-musl@1.32.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg=="], + + "vitest/vite/lightningcss/lightningcss-linux-x64-gnu": ["lightningcss-linux-x64-gnu@1.32.0", "", { "os": "linux", "cpu": "x64" }, "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA=="], + + "vitest/vite/lightningcss/lightningcss-linux-x64-musl": ["lightningcss-linux-x64-musl@1.32.0", "", { "os": "linux", "cpu": "x64" }, "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg=="], + + "vitest/vite/lightningcss/lightningcss-win32-arm64-msvc": ["lightningcss-win32-arm64-msvc@1.32.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw=="], + + "vitest/vite/lightningcss/lightningcss-win32-x64-msvc": ["lightningcss-win32-x64-msvc@1.32.0", "", { "os": "win32", "cpu": "x64" }, "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q=="], + + "vitest/vite/postcss/nanoid": ["nanoid@3.3.15", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-y7Wygv/7mEOvxTuEQDB8StXdMRBWf1kR/tlhAzBRUFkB2jfcLOAxO/SHmOO2zgz1pVgK29/kyupn059/bCHdjA=="], + + "vitest/vite/rolldown/@oxc-project/types": ["@oxc-project/types@0.137.0", "", {}, "sha512-WT+Gb24i8hmvo85AIv2oEYouEXkRlKAlT9WaCa3TfLgNCN+GhrJOGZuIlMouAh38Qe4QOx26eUOVsq70qXrywA=="], + + "vitest/vite/rolldown/@rolldown/binding-android-arm64": ["@rolldown/binding-android-arm64@1.1.2", "", { "os": "android", "cpu": "arm64" }, "sha512-2cZ+7xRS+DBcuJBJKnfzsbleumJhBqSlJVpuzHC0nTqfd3QQ7Vx2/x5YR/D7cBamKSeWplwo82Fn9lqYUDEMfA=="], + + "vitest/vite/rolldown/@rolldown/binding-darwin-arm64": ["@rolldown/binding-darwin-arm64@1.1.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-RkPMJnygxsgOYdkfqgpwY0/Fzm8d0VQe6HGU2/B00Xa9eqdLbrII+DOKAodbJAn3ZL1AJxGHkZRPYazgGY6Ljw=="], + + "vitest/vite/rolldown/@rolldown/binding-darwin-x64": ["@rolldown/binding-darwin-x64@1.1.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-Uiczh6vFhwyfd7WNe7Q7mCA4KxAiLdz7jPE/WGizfRpIieoyFuNVMmM8HqZ9HwudTkY6/AeMQwlNJ9NJijguWw=="], + + "vitest/vite/rolldown/@rolldown/binding-freebsd-x64": ["@rolldown/binding-freebsd-x64@1.1.2", "", { "os": "freebsd", "cpu": "x64" }, "sha512-+TpdtTRgHiJFjCVFbw311SuLk3KfytPOQQn+VlAEv+gBxYPtL7E6JS9e/tk+8CwxhIZvemJKo4rTKgfWNsKkkA=="], + + "vitest/vite/rolldown/@rolldown/binding-linux-arm-gnueabihf": ["@rolldown/binding-linux-arm-gnueabihf@1.1.2", "", { "os": "linux", "cpu": "arm" }, "sha512-4lv1/tkmi7ueIVHnyreaOeUpiZP26BH9rRy6hoYfR9310A2B9nUEVRDvBx69vx64Nr3eTPPRkyciqJJs+j9Jmw=="], + + "vitest/vite/rolldown/@rolldown/binding-linux-arm64-gnu": ["@rolldown/binding-linux-arm64-gnu@1.1.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-gBSUVO0eaWgw1JMjK3gB8BMlX2Mk148s2lTiVT3e9vjVxbl7UDfMWWY8CfIaaqiXuM9fVTMxIpUz6CAo/B6Vlw=="], + + "vitest/vite/rolldown/@rolldown/binding-linux-arm64-musl": ["@rolldown/binding-linux-arm64-musl@1.1.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-LjQP/iZLBu8o8PjIfk4x3At0/mT6h282pvz8Z5LAyhGbu/kDezyO7ea62rF5uoqmgnIYqbN/MqJ3Si3Aymi7xQ=="], + + "vitest/vite/rolldown/@rolldown/binding-linux-ppc64-gnu": ["@rolldown/binding-linux-ppc64-gnu@1.1.2", "", { "os": "linux", "cpu": "ppc64" }, "sha512-X/7bVLWelEsbyWDUSXt7zVsTniLLPIY2n1rH58qr78l9i7MNbbxBWD8gI2vRfBWf4NUXJCUuQnfZDsp32LqsfQ=="], + + "vitest/vite/rolldown/@rolldown/binding-linux-s390x-gnu": ["@rolldown/binding-linux-s390x-gnu@1.1.2", "", { "os": "linux", "cpu": "s390x" }, "sha512-gb6dYKW/1KDorGXyy48glEBJs/sxVSC5pcVrox/pFGV4mvwSFeg2sK5L2tRkVsVlh7kueqOgg4GEcuipJcGuKg=="], + + "vitest/vite/rolldown/@rolldown/binding-linux-x64-gnu": ["@rolldown/binding-linux-x64-gnu@1.1.2", "", { "os": "linux", "cpu": "x64" }, "sha512-JY4w85pU3iAiJVMh5nuk4/Mh9GjMsupe8MrIN53rwxAZW64GKrWeJBuN6SxQg9QTU5uB1cxyhDzW8jqRn1EABw=="], + + "vitest/vite/rolldown/@rolldown/binding-linux-x64-musl": ["@rolldown/binding-linux-x64-musl@1.1.2", "", { "os": "linux", "cpu": "x64" }, "sha512-xvpA7o5KCYLB0Rwscmuylb1/zHHSUx4g4xilm4prC5jP76pEUlzBmMbgpbh7bVDbId4NcfT96gN5i6mE6UDaiw=="], + + "vitest/vite/rolldown/@rolldown/binding-openharmony-arm64": ["@rolldown/binding-openharmony-arm64@1.1.2", "", { "os": "none", "cpu": "arm64" }, "sha512-p/ts6KBLjuk49Bp21XH77poQGt02iNz7ChgHep7tudPOaLinR/De/RHdxF8w8Yj4r/bF/bqXwH6PZrB2sA+Nvw=="], + + "vitest/vite/rolldown/@rolldown/binding-win32-arm64-msvc": ["@rolldown/binding-win32-arm64-msvc@1.1.2", "", { "os": "win32", "cpu": "arm64" }, "sha512-xtUJqs8qEkuSviS0n1tsohaPuz3a1SPhZywOji4Oo+sgrJs8daEDMZ0QtqL0OS7dx8PoVpg2J/ZZycPY5I2+Zg=="], + + "vitest/vite/rolldown/@rolldown/binding-win32-x64-msvc": ["@rolldown/binding-win32-x64-msvc@1.1.2", "", { "os": "win32", "cpu": "x64" }, "sha512-85YiLQqjUKgSO/Zjnf9e0XIn5Ymrh1fLDWBeAkZqpuBR/3R8TpfoHXuyblqyQrftSSgWO9qpcHN8mkyKsLraoA=="], } } diff --git a/convex/access.ts b/convex/access.ts index 659d826..cbeb77c 100644 --- a/convex/access.ts +++ b/convex/access.ts @@ -34,7 +34,7 @@ export const checkInvite = query({ .withIndex("by_code", (q) => q.eq("code", code)) .unique(); if (!invite || !inviteIsUsable(invite)) return { valid: false as const }; - const orgId = invite.orgId ?? await getProductOrgId(ctx); + const orgId = invite.orgId ?? (await getProductOrgId(ctx)); const org = await ctx.db.get(orgId); if (!org) return { valid: false as const }; return { valid: true as const, note: invite.note, orgName: org.name }; @@ -61,7 +61,7 @@ export const redeemInvite = mutation({ message: "That invite code is not valid anymore.", }); } - const targetOrgId = invite.orgId ?? await getProductOrgId(ctx); + const targetOrgId = invite.orgId ?? (await getProductOrgId(ctx)); if (viewer.orgId && viewer.orgId !== targetOrgId) { throw new ConvexError({ code: "FORBIDDEN", @@ -117,15 +117,13 @@ export const claimTargetedInvite = mutation({ : await ctx.db .query("invites") .withIndex("by_target", (q) => - q - .eq("targetKind", candidate.kind) - .eq("targetValue", candidate.value), + q.eq("targetKind", candidate.kind).eq("targetValue", candidate.value), ) .collect(); const invite = invites.find(inviteIsUsable); if (!invite) continue; - const orgId = invite.orgId ?? await getProductOrgId(ctx); + const orgId = invite.orgId ?? (await getProductOrgId(ctx)); await ctx.db.patch(invite._id, { usedCount: invite.usedCount + 1 }); await ctx.db.patch(viewer._id, { orgId, status: "active" }); await logAudit(ctx, { diff --git a/convex/admin.ts b/convex/admin.ts index 5b48a89..8c4d4c9 100644 --- a/convex/admin.ts +++ b/convex/admin.ts @@ -2,20 +2,13 @@ import { ConvexError, v } from "convex/values"; import { mutation, query } from "./_generated/server"; import type { Doc, Id } from "./_generated/dataModel"; import type { MutationCtx, QueryCtx } from "./_generated/server"; -import { - ensureActiveViewerUser, - getViewerFromAuth, -} from "./authUsers"; +import { ensureActiveViewerUser, getViewerFromAuth } from "./authUsers"; import { publicUser } from "./users"; import { parseInviteTarget, type InviteTarget } from "./lib/inviteTargets"; import { logInfo } from "./lib/observability"; import { parse, profileTitleSchema } from "./lib/validation"; import { aiGenerationKind } from "./schema"; -import { - DEFAULT_OPENROUTER_MODEL, - normalizeModelId, - type AiGenerationKind, -} from "./lib/aiModels"; +import { DEFAULT_OPENROUTER_MODEL, normalizeModelId, type AiGenerationKind } from "./lib/aiModels"; /** * Admin control plane — users, invites, access requests, audit history. @@ -137,10 +130,7 @@ export const listUsers = query({ }, }); -const AI_GENERATION_KINDS: readonly AiGenerationKind[] = [ - "postSummary", - "agentTask", -]; +const AI_GENERATION_KINDS: readonly AiGenerationKind[] = ["postSummary", "agentTask"]; export const aiModelSettings = query({ args: {}, @@ -169,9 +159,7 @@ export const aiModelSettings = query({ effectiveModelId: setting?.modelId ?? process.env.OPENROUTER_MODEL ?? DEFAULT_OPENROUTER_MODEL, updatedAt: setting?.updatedAt ?? null, - updatedByName: setting - ? (updaterNames.get(setting.updatedById) ?? "unknown") - : null, + updatedByName: setting ? (updaterNames.get(setting.updatedById) ?? "unknown") : null, }; }), }; @@ -185,9 +173,7 @@ export const setAiModelSetting = mutation({ const modelId = normalizeModelId(args.modelId); const existing = await ctx.db .query("aiGenerationSettings") - .withIndex("by_org_id_and_kind", (q) => - q.eq("orgId", admin.orgId).eq("kind", args.kind), - ) + .withIndex("by_org_id_and_kind", (q) => q.eq("orgId", admin.orgId).eq("kind", args.kind)) .first(); const now = Date.now(); if (existing) { @@ -224,9 +210,7 @@ export const resetAiModelSetting = mutation({ const admin = await requireAdminForWrite(ctx); const existing = await ctx.db .query("aiGenerationSettings") - .withIndex("by_org_id_and_kind", (q) => - q.eq("orgId", admin.orgId).eq("kind", args.kind), - ) + .withIndex("by_org_id_and_kind", (q) => q.eq("orgId", admin.orgId).eq("kind", args.kind)) .collect(); await Promise.all(existing.map((setting) => ctx.db.delete(setting._id))); await logAudit(ctx, { @@ -290,9 +274,7 @@ export const listAccessRequests = query({ const admin = await requireAdminForRead(ctx); const requests = await ctx.db .query("accessRequests") - .withIndex("by_org_id_and_status_and_created_at", (q) => - q.eq("orgId", admin.orgId), - ) + .withIndex("by_org_id_and_status_and_created_at", (q) => q.eq("orgId", admin.orgId)) .collect(); return requests.sort((a, b) => b.createdAt - a.createdAt); }, @@ -372,9 +354,7 @@ export const createInvite = mutation({ }); } // Targeted invites admit exactly the one person. - const maxUses = target - ? 1 - : Math.min(Math.max(Math.floor(args.maxUses ?? 1), 0), 1000); + const maxUses = target ? 1 : Math.min(Math.max(Math.floor(args.maxUses ?? 1), 0), 1000); const note = args.note?.trim().slice(0, 200) || undefined; const expiresAt = args.expiresInDays && args.expiresInDays > 0 diff --git a/convex/agentTasks.ts b/convex/agentTasks.ts index bbde5db..b294dec 100644 --- a/convex/agentTasks.ts +++ b/convex/agentTasks.ts @@ -1,6 +1,12 @@ import { generateText } from "ai"; import { ConvexError, v } from "convex/values"; -import { internalAction, internalMutation, internalQuery, mutation, query } from "./_generated/server"; +import { + internalAction, + internalMutation, + internalQuery, + mutation, + query, +} from "./_generated/server"; import { internal } from "./_generated/api"; import type { Doc, Id } from "./_generated/dataModel"; import type { ActionCtx } from "./_generated/server"; @@ -29,13 +35,10 @@ async function generateAgentResult(args: { prompt: string; contextText: string; }): Promise { - const openRouterModelId = await args.ctx.runQuery( - internal.ai.getGenerationModelSetting, - { - orgId: args.orgId, - kind: "agentTask", - }, - ); + const openRouterModelId = await args.ctx.runQuery(internal.ai.getGenerationModelSetting, { + orgId: args.orgId, + kind: "agentTask", + }); // Demo fallback: when no AI provider is configured on the deployment, the // action returns a disabled signal instead of throwing — so the client shows @@ -51,8 +54,7 @@ async function generateAgentResult(args: { // agentName is interpolated into the system prompt — collapse whitespace and // cap length so it can't carry injected multi-line instructions. - const agentName = - args.agentName.replace(/\s+/g, " ").trim().slice(0, 60) || "an agent"; + const agentName = args.agentName.replace(/\s+/g, " ").trim().slice(0, 60) || "an agent"; const { model, modelId } = resolveModel({ openRouterModelId }); const { text } = await generateText({ model, @@ -109,11 +111,8 @@ export const forPost = query({ if (scope.authenticated && !scope.viewer) return []; const viewer = scope.viewer; const post = await ctx.db.get(args.postId); - if ( - !post || - post.orgId !== scope.orgId || - !(await canAccessPost(ctx, post, viewer?._id)) - ) return []; + if (!post || post.orgId !== scope.orgId || !(await canAccessPost(ctx, post, viewer?._id))) + return []; const tasks = await ctx.db .query("agentTasks") .withIndex("by_org_id_and_post_id", (q) => @@ -149,11 +148,7 @@ export const create = mutation({ if (args.sourceReplyId) { const sourceReply = await ctx.db.get(args.sourceReplyId); - if ( - !sourceReply || - sourceReply.orgId !== orgId || - sourceReply.postId !== args.postId - ) { + if (!sourceReply || sourceReply.orgId !== orgId || sourceReply.postId !== args.postId) { notFound("Source reply not found."); } } @@ -211,10 +206,7 @@ export const getRunnableTask = internalQuery({ handler: async (ctx, args) => { const task = await ctx.db.get(args.taskId); if (!task) return null; - const [post, agent] = await Promise.all([ - ctx.db.get(task.postId), - ctx.db.get(task.agentId), - ]); + const [post, agent] = await Promise.all([ctx.db.get(task.postId), ctx.db.get(task.agentId)]); if (!post || !agent || post.orgId !== task.orgId || agent.orgId !== task.orgId) { return null; } @@ -247,9 +239,7 @@ export const getRunnableTask = internalQuery({ "", "REPLIES:", ...(replies.length - ? replies.map( - (reply) => `- ${authorNames.get(reply.authorId) ?? "Unknown"}: ${reply.body}`, - ) + ? replies.map((reply) => `- ${authorNames.get(reply.authorId) ?? "Unknown"}: ${reply.body}`) : ["(no replies yet)"]), ].join("\n"); @@ -310,7 +300,11 @@ export const runSimulated = internalAction({ try { const res = await generateAgentResult({ ctx, - orgId: runnable.task.orgId ?? (() => { throw new Error("Agent task missing orgId"); })(), + orgId: + runnable.task.orgId ?? + (() => { + throw new Error("Agent task missing orgId"); + })(), agentName: runnable.agentName, prompt: runnable.task.prompt, contextText: runnable.contextText, @@ -326,15 +320,12 @@ export const runSimulated = internalAction({ return; } - const replyId: Id<"replies"> = await ctx.runMutation( - internal.replies.createAsAgent, - { - postId: runnable.task.postId, - parentId: runnable.task.sourceReplyId, - authorId: runnable.task.agentId, - body: res.result, - }, - ); + const replyId: Id<"replies"> = await ctx.runMutation(internal.replies.createAsAgent, { + postId: runnable.task.postId, + parentId: runnable.task.sourceReplyId, + authorId: runnable.task.agentId, + body: res.result, + }); await ctx.runMutation(internal.agentTasks.setStatus, { taskId: args.taskId, @@ -349,9 +340,7 @@ export const runSimulated = internalAction({ await ctx.runMutation(internal.agentTasks.setStatus, { taskId: args.taskId, status: "failed", - error: /API_KEY|not set/i.test(msg) - ? "AI is disabled for the time of the demo." - : msg, + error: /API_KEY|not set/i.test(msg) ? "AI is disabled for the time of the demo." : msg, completedAt: Date.now(), }); } diff --git a/convex/ai.ts b/convex/ai.ts index 8d9d755..a2e9c33 100644 --- a/convex/ai.ts +++ b/convex/ai.ts @@ -48,9 +48,10 @@ type OpenRouterModel = { * Pioneer (OpenAI-compatible, https://docs.pioneer.ai): * PIONEER_API_KEY, PIONEER_MODEL, PIONEER_BASE_URL? */ -export function resolveModel( - options: ResolveModelOptions = {}, -): { model: LanguageModel; modelId: string } { +export function resolveModel(options: ResolveModelOptions = {}): { + model: LanguageModel; + modelId: string; +} { if (options.openRouterModelId) { const modelId = normalizeModelId(options.openRouterModelId); return resolveOpenRouterModel(modelId); @@ -140,8 +141,7 @@ export function aiConfigured(options: ResolveModelOptions = {}): boolean { const provider = (process.env.AI_PROVIDER ?? "openrouter").toLowerCase(); if (provider === "gateway") return !!process.env.AI_GATEWAY_API_KEY; if (provider === "openrouter") return !!process.env.OPENROUTER_API_KEY; - if (provider === "pioneer") - return !!process.env.PIONEER_API_KEY && !!process.env.PIONEER_MODEL; + if (provider === "pioneer") return !!process.env.PIONEER_API_KEY && !!process.env.PIONEER_MODEL; if (provider === "openai") return !!process.env.OPENAI_API_KEY; return false; } @@ -155,9 +155,7 @@ export const getGenerationModelSetting = internalQuery({ const orgId = args.orgId; const setting = await ctx.db .query("aiGenerationSettings") - .withIndex("by_org_id_and_kind", (q) => - q.eq("orgId", orgId).eq("kind", args.kind), - ) + .withIndex("by_org_id_and_kind", (q) => q.eq("orgId", orgId).eq("kind", args.kind)) .first(); return setting?.modelId ?? null; }, @@ -192,18 +190,10 @@ function isAllowedOpenRouterModel(model: OpenRouterModel): boolean { const id = model.id.toLowerCase(); const name = model.name?.toLowerCase() ?? ""; - if ( - id.startsWith("liquid/") || - id.startsWith("meta-llama/") || - id.includes("nemotron") - ) { + if (id.startsWith("liquid/") || id.startsWith("meta-llama/") || id.includes("nemotron")) { return false; } - if ( - /channel[-_ ]?rating|content[-_ ]?safety|moderation|guardrail/.test( - `${id} ${name}`, - ) - ) { + if (/channel[-_ ]?rating|content[-_ ]?safety|moderation|guardrail/.test(`${id} ${name}`)) { return false; } @@ -219,9 +209,7 @@ function isAllowedOpenRouterModel(model: OpenRouterModel): boolean { export const listOpenRouterFreeModels = action({ args: {}, - handler: async (): Promise< - { id: string; name: string; contextLength?: number }[] - > => { + handler: async (): Promise<{ id: string; name: string; contextLength?: number }[]> => { const response = await fetch( `${DEFAULT_OPENROUTER_BASE_URL}/models?output_modalities=text&sort=pricing-low-to-high`, ); @@ -234,10 +222,7 @@ export const listOpenRouterFreeModels = action({ const payload = (await response.json()) as { data?: OpenRouterModel[] }; const models = payload.data ?? []; const free = models - .filter( - (model) => - isFreeOpenRouterModel(model) && isAllowedOpenRouterModel(model), - ) + .filter((model) => isFreeOpenRouterModel(model) && isAllowedOpenRouterModel(model)) .map((model) => ({ id: model.id, name: model.name ?? model.id, @@ -322,7 +307,11 @@ export const summarizePost = action({ ].join("\n"); const openRouterModelId = await ctx.runQuery(internal.ai.getGenerationModelSetting, { - orgId: accessiblePost.orgId ?? (() => { throw new Error("Post missing orgId"); })(), + orgId: + accessiblePost.orgId ?? + (() => { + throw new Error("Post missing orgId"); + })(), kind: "postSummary", }); const { model, modelId } = resolveModel({ openRouterModelId }); diff --git a/convex/attachments.test.ts b/convex/attachments.test.ts index 0b2af14..d3f685e 100644 --- a/convex/attachments.test.ts +++ b/convex/attachments.test.ts @@ -30,20 +30,23 @@ function attachmentContext( let ticketExists = true; const ctx = { db: { - get: async () => (ticketExists - ? { - _id: uploadToken, - userId: ownerId, - orgId: undefined, - storageId: ticketStorageId, - expiresAt: Date.now() + 60_000, - } - : null), + get: async () => + ticketExists + ? { + _id: uploadToken, + userId: ownerId, + orgId: undefined, + storageId: ticketStorageId, + expiresAt: Date.now() + 60_000, + } + : null, system: { get: async () => ({ contentType: "image/png", size: 12 }), }, query: () => ({ - withIndex: () => ({ unique: async () => (existingAttachment ? { _id: "attachment-1" } : null) }), + withIndex: () => ({ + unique: async () => (existingAttachment ? { _id: "attachment-1" } : null), + }), }), delete: async () => { ticketExists = false; @@ -80,9 +83,9 @@ describe("attachment upload tickets", () => { return { ticketId }; }); - await expect( - t.mutation(internal.attachments.cleanupExpiredUploadTickets, {}), - ).resolves.toEqual({ deleted: 1 }); + await expect(t.mutation(internal.attachments.cleanupExpiredUploadTickets, {})).resolves.toEqual( + { deleted: 1 }, + ); await expect(t.run(async (ctx) => await ctx.db.get(ticketId))).resolves.toBeNull(); }); @@ -91,7 +94,10 @@ describe("attachment upload tickets", () => { const ctx = attachmentContext(storageId); await expect( - validateStoredAttachment(ctx, ownerId, undefined, { ...attachment, storageId: mismatchedStorageId }), + validateStoredAttachment(ctx, ownerId, undefined, { + ...attachment, + storageId: mismatchedStorageId, + }), ).rejects.toBeInstanceOf(ConvexError); }); @@ -104,7 +110,11 @@ describe("attachment upload tickets", () => { test("consumes a claimed ticket exactly once", async () => { const ctx = attachmentContext(storageId); - await expect(validateStoredAttachment(ctx, ownerId, undefined, attachment)).resolves.toMatchObject({ storageId }); - await expect(validateStoredAttachment(ctx, ownerId, undefined, attachment)).rejects.toBeInstanceOf(ConvexError); + await expect( + validateStoredAttachment(ctx, ownerId, undefined, attachment), + ).resolves.toMatchObject({ storageId }); + await expect( + validateStoredAttachment(ctx, ownerId, undefined, attachment), + ).rejects.toBeInstanceOf(ConvexError); }); }); diff --git a/convex/attachments.ts b/convex/attachments.ts index ca49d7a..d1c3e31 100644 --- a/convex/attachments.ts +++ b/convex/attachments.ts @@ -3,11 +3,7 @@ import { internalMutation, mutation, query } from "./_generated/server"; import { ConvexError, v } from "convex/values"; import type { Id } from "./_generated/dataModel"; import type { MutationCtx } from "./_generated/server"; -import { - ensureActiveViewerUser, - canAccessPost, - resolveReadScope, -} from "./authUsers"; +import { ensureActiveViewerUser, canAccessPost, resolveReadScope } from "./authUsers"; import { rateLimiter } from "./lib/rateLimit"; import { logInfo } from "./lib/observability"; import { attachmentMediaKind, type AttachmentMediaKind } from "./lib/validation"; @@ -167,20 +163,14 @@ export const listForPost = query({ if (scope.authenticated && !scope.viewer) return []; const viewer = scope.viewer; const post = await ctx.db.get(args.postId); - if ( - !post || - post.orgId !== scope.orgId || - !(await canAccessPost(ctx, post, viewer?._id)) - ) { + if (!post || post.orgId !== scope.orgId || !(await canAccessPost(ctx, post, viewer?._id))) { return []; } const orgId = post.orgId; const attachments = await ctx.db .query("postAttachments") - .withIndex("by_org_id_and_post_id", (q) => - q.eq("orgId", orgId).eq("postId", args.postId), - ) + .withIndex("by_org_id_and_post_id", (q) => q.eq("orgId", orgId).eq("postId", args.postId)) .order("asc") .collect(); diff --git a/convex/auth.config.ts b/convex/auth.config.ts index bab8e57..496471d 100644 --- a/convex/auth.config.ts +++ b/convex/auth.config.ts @@ -3,9 +3,7 @@ import type { AuthConfig } from "convex/server"; const clerkIssuerDomain = process.env.CLERK_JWT_ISSUER_DOMAIN; if (!clerkIssuerDomain) { - throw new Error( - "CLERK_JWT_ISSUER_DOMAIN must be set on the Convex deployment.", - ); + throw new Error("CLERK_JWT_ISSUER_DOMAIN must be set on the Convex deployment."); } export default { diff --git a/convex/authUsers.ts b/convex/authUsers.ts index ac16881..a30cf8d 100644 --- a/convex/authUsers.ts +++ b/convex/authUsers.ts @@ -14,17 +14,24 @@ export const DEFAULT_ORG_SLUG = DEMO_ORG_SLUG; export const DEFAULT_ORG_NAME = DEMO_ORG_NAME; async function getOrgIdBySlug(ctx: AuthCtx, slug: string, message: string) { - const org = await ctx.db.query("orgs").withIndex("by_slug", (q) => q.eq("slug", slug)).unique(); + const org = await ctx.db + .query("orgs") + .withIndex("by_slug", (q) => q.eq("slug", slug)) + .unique(); if (!org) notFound(message); return org._id; } -export const getDemoOrgId = (ctx: AuthCtx) => getOrgIdBySlug(ctx, DEMO_ORG_SLUG, "Demo organization not found. Run the demo seed first."); -export const getProductOrgId = (ctx: AuthCtx) => getOrgIdBySlug(ctx, PRODUCT_ORG_SLUG, "Product organization not found. Run migrations:ensureProductOrg first."); +export const getDemoOrgId = (ctx: AuthCtx) => + getOrgIdBySlug(ctx, DEMO_ORG_SLUG, "Demo organization not found. Run the demo seed first."); +export const getProductOrgId = (ctx: AuthCtx) => + getOrgIdBySlug( + ctx, + PRODUCT_ORG_SLUG, + "Product organization not found. Run migrations:ensureProductOrg first.", + ); -export type AuthIdentity = NonNullable< - Awaited> ->; +export type AuthIdentity = NonNullable>>; export function initialsFrom(name: string): string { const parts = name.trim().split(/\s+/).filter(Boolean); @@ -85,12 +92,7 @@ export async function applyAvatarAction( message: "That upload could not be found.", }); } - const ALLOWED_AVATAR_TYPES = [ - "image/png", - "image/jpeg", - "image/gif", - "image/webp", - ]; + const ALLOWED_AVATAR_TYPES = ["image/png", "image/jpeg", "image/gif", "image/webp"]; if (!meta.contentType || !ALLOWED_AVATAR_TYPES.includes(meta.contentType)) { throw new ConvexError({ code: "INVALID_INPUT", @@ -144,9 +146,7 @@ export async function findUserForIdentity( // authenticated member can be found in whichever org owns their user row. const byToken = await ctx.db .query("users") - .withIndex("by_token_identifier", (q) => - q.eq("tokenIdentifier", identity.tokenIdentifier), - ) + .withIndex("by_token_identifier", (q) => q.eq("tokenIdentifier", identity.tokenIdentifier)) .unique(); if (byToken) return byToken; @@ -173,7 +173,10 @@ export async function findUserForIdentity( } export async function countAdmins(ctx: AuthCtx, orgId: Id<"orgs">): Promise { - const admins = await ctx.db.query("users").withIndex("by_org_id_and_role", (q) => q.eq("orgId", orgId).eq("role", "admin")).take(2); + const admins = await ctx.db + .query("users") + .withIndex("by_org_id_and_role", (q) => q.eq("orgId", orgId).eq("role", "admin")) + .take(2); return admins.length; } @@ -203,9 +206,7 @@ export function requireOrgId(user: Pick, "orgId">): Id<"orgs"> { return user.orgId; } -export async function getViewerFromAuth( - ctx: AuthCtx, -): Promise | null> { +export async function getViewerFromAuth(ctx: AuthCtx): Promise | null> { const identity = await ctx.auth.getUserIdentity(); if (!identity) return null; const legacyOrgId = await getProductOrgId(ctx); @@ -214,13 +215,20 @@ export async function getViewerFromAuth( export type ReadScope = { orgId: Id<"orgs">; viewer: Doc<"users"> | null; authenticated: boolean }; -export async function resolveReadScope(ctx: QueryCtx, requestedViewerId?: Id<"users">): Promise { +export async function resolveReadScope( + ctx: QueryCtx, + requestedViewerId?: Id<"users">, +): Promise { const identity = await ctx.auth.getUserIdentity(); if (identity) { const productOrgId = await getProductOrgId(ctx); const viewer = await findUserForIdentity(ctx, identity, productOrgId); const orgId = viewer?.orgId ?? productOrgId; - return { orgId, viewer: viewer?.status === "active" && !viewer.deactivatedAt ? viewer : null, authenticated: true }; + return { + orgId, + viewer: viewer?.status === "active" && !viewer.deactivatedAt ? viewer : null, + authenticated: true, + }; } const orgId = await getDemoOrgId(ctx); const requested = requestedViewerId ? await ctx.db.get(requestedViewerId) : null; @@ -273,9 +281,7 @@ export async function ensureViewerUser( if (!existing.avatarColor) patch.avatarColor = colorFor(identity.tokenIdentifier); if (!existing.role) { patch.role = - existing.orgId && (await countAdmins(ctx, existing.orgId)) === 0 - ? "admin" - : "member"; + existing.orgId && (await countAdmins(ctx, existing.orgId)) === 0 ? "admin" : "member"; } if (Object.keys(patch).length > 0) { @@ -362,7 +368,7 @@ export async function canAccessSpace( ): Promise { const space = await ctx.db.get(spaceId); const viewer = viewerId ? await ctx.db.get(viewerId) : null; - const orgId = viewer?.orgId ?? await getDemoOrgId(ctx); + const orgId = viewer?.orgId ?? (await getDemoOrgId(ctx)); if (!space || space.orgId !== orgId) { return false; } @@ -371,7 +377,7 @@ export async function canAccessSpace( return await isSpaceMember(ctx, spaceId, viewerId); } - return orgId === await getDemoOrgId(ctx); + return orgId === (await getDemoOrgId(ctx)); } export async function canAccessPost( diff --git a/convex/catchUpComposer.test.ts b/convex/catchUpComposer.test.ts index 3c2281e..3e088cb 100644 --- a/convex/catchUpComposer.test.ts +++ b/convex/catchUpComposer.test.ts @@ -62,10 +62,7 @@ describe("composeCatchUpDigest", () => { 2, ); - expect(digest.items.map((item) => item.summary.status)).toEqual([ - "missing", - "stale", - ]); + expect(digest.items.map((item) => item.summary.status)).toEqual(["missing", "stale"]); expect(digest).toMatchObject({ eligibleInWindow: 3, omittedEligibleInWindow: 1, diff --git a/convex/catchUpComposer.ts b/convex/catchUpComposer.ts index 509be28..ff0b636 100644 --- a/convex/catchUpComposer.ts +++ b/convex/catchUpComposer.ts @@ -49,9 +49,7 @@ export function composeCatchUpDigest( candidates: readonly CatchUpCandidate[], limit = DEFAULT_CATCH_UP_LIMIT, ): CatchUpDigest { - const eligible = candidates - .filter((candidate) => candidate.unread) - .sort(compareCandidates); + const eligible = candidates.filter((candidate) => candidate.unread).sort(compareCandidates); const boundedLimit = Math.max(1, Math.floor(limit)); return { @@ -64,10 +62,7 @@ export function composeCatchUpDigest( }; } -function compareCandidates( - a: CatchUpCandidate, - b: CatchUpCandidate, -): number { +function compareCandidates(a: CatchUpCandidate, b: CatchUpCandidate): number { return ( PRIORITY_ORDER[a.priority] - PRIORITY_ORDER[b.priority] || b.lastActivityAt - a.lastActivityAt || @@ -76,9 +71,7 @@ function compareCandidates( ); } -function projectSummary( - candidate: CatchUpCandidate, -): CatchUpSummary { +function projectSummary(candidate: CatchUpCandidate): CatchUpSummary { const text = candidate.summary?.trim(); if (!text) { return { status: "missing", text: null, model: null, updatedAt: null }; diff --git a/convex/catchUpDigest.test.ts b/convex/catchUpDigest.test.ts index c038d9f..4598f14 100644 --- a/convex/catchUpDigest.test.ts +++ b/convex/catchUpDigest.test.ts @@ -217,16 +217,16 @@ describe("posts.catchUpDigest", () => { test("requires authentication and rejects pending viewers", async () => { const state = await setup(); - await expect( - state.t.query(api.posts.catchUpDigest, {}), - ).rejects.toThrow("Sign in to view your catch-up digest."); + await expect(state.t.query(api.posts.catchUpDigest, {})).rejects.toThrow( + "Sign in to view your catch-up digest.", + ); await state.t.run(async (ctx) => { await ctx.db.patch(state.viewerId, { status: "pending" }); }); - await expect( - state.authed.query(api.posts.catchUpDigest, {}), - ).rejects.toThrow("Your account cannot access the catch-up digest."); + await expect(state.authed.query(api.posts.catchUpDigest, {})).rejects.toThrow( + "Your account cannot access the catch-up digest.", + ); await state.t.run(async (ctx) => { await ctx.db.patch(state.viewerId, { @@ -234,8 +234,8 @@ describe("posts.catchUpDigest", () => { deactivatedAt: 10, }); }); - await expect( - state.authed.query(api.posts.catchUpDigest, {}), - ).rejects.toThrow("Your account cannot access the catch-up digest."); + await expect(state.authed.query(api.posts.catchUpDigest, {})).rejects.toThrow( + "Your account cannot access the catch-up digest.", + ); }); }); diff --git a/convex/connectors.test.ts b/convex/connectors.test.ts index 7876db1..8fd22dc 100644 --- a/convex/connectors.test.ts +++ b/convex/connectors.test.ts @@ -99,9 +99,7 @@ describe("connector secret key rotation", () => { CONNECTOR_SECRET_ENCRYPTION_ACTIVE_KEY: ACTIVE_ENCRYPTION_KEY, }); - await expect(decryptConnectorSecret(oldCiphertext, activeOnly)).rejects.toThrow( - "unavailable", - ); + await expect(decryptConnectorSecret(oldCiphertext, activeOnly)).rejects.toThrow("unavailable"); }); }); @@ -118,7 +116,7 @@ describe("connector agent task boundary", () => { role: "member", status: "active", isAgent: true, - }) + }), ); const connected = await state.t.mutation(internal.connectors.provisionRecord, { adminTokenIdentifier: ADMIN_TOKEN, @@ -141,7 +139,7 @@ describe("connector agent task boundary", () => { prompt: "Wait for the connector.", }); const scheduled = await state.t.run(async (ctx) => - ctx.db.system.query("_scheduled_functions").take(10) + ctx.db.system.query("_scheduled_functions").take(10), ); const tasks = await state.t.run(async (ctx) => ({ demo: await ctx.db.get(demoTaskId), @@ -170,13 +168,9 @@ describe("connector agent task boundary", () => { expect(provisioned.token).toMatch(/^pwc\.[a-f0-9]{16}\.[a-f0-9]{64}$/); const credential = parseConnectorToken(`Bearer ${provisioned.token}`); expect(credential).not.toBeNull(); - const stored = await state.t.run(async (ctx) => - ctx.db.get(provisioned.connectorId), - ); + const stored = await state.t.run(async (ctx) => ctx.db.get(provisioned.connectorId)); expect(stored?.credentialId).toBe(credential?.credentialId); - expect(stored?.secretHash).toBe( - await hashConnectorSecret(credential?.secret ?? ""), - ); + expect(stored?.secretHash).toBe(await hashConnectorSecret(credential?.secret ?? "")); expect(JSON.stringify(stored)).not.toContain(credential?.secret); }); @@ -576,9 +570,7 @@ describe("x cross-posting", () => { const stored = await state.t.run(async (ctx) => { const connector = await ctx.db .query("connectors") - .withIndex("by_org_id_and_slug", (q) => - q.eq("orgId", state.orgId).eq("slug", "x"), - ) + .withIndex("by_org_id_and_slug", (q) => q.eq("orgId", state.orgId).eq("slug", "x")) .unique(); return { connector, @@ -620,9 +612,7 @@ describe("x cross-posting", () => { const connector = await state.t.run(async (ctx) => ctx.db .query("connectors") - .withIndex("by_org_id_and_slug", (q) => - q.eq("orgId", state.orgId).eq("slug", "x"), - ) + .withIndex("by_org_id_and_slug", (q) => q.eq("orgId", state.orgId).eq("slug", "x")) .unique(), ); expect(connector?.xSyncHandle).toBeUndefined(); @@ -675,7 +665,9 @@ describe("x cross-posting", () => { space: "Growth", title: "@pronsh on x: wrec 3.0 is live. threads, agents, the lot.", }); - expect(post?.body).toContain("Cross-posted from https://x.com/pronsh/status/2080000000000000001"); + expect(post?.body).toContain( + "Cross-posted from https://x.com/pronsh/status/2080000000000000001", + ); const retry = await state.t.mutation(internal.connectors.recordXCrossPostFromSync, { connectorId: connector.connectorId, diff --git a/convex/connectors.ts b/convex/connectors.ts index 3f9dfa2..d610d39 100644 --- a/convex/connectors.ts +++ b/convex/connectors.ts @@ -1,12 +1,5 @@ import { ConvexError, v } from "convex/values"; -import { - action, - internalMutation, - internalQuery, - mutation, - query, - env, -} from "./_generated/server"; +import { action, internalMutation, internalQuery, mutation, query, env } from "./_generated/server"; import { internal } from "./_generated/api"; import type { Doc, Id } from "./_generated/dataModel"; import type { MutationCtx, QueryCtx } from "./_generated/server"; @@ -122,9 +115,7 @@ export const xSyncStatus = query({ const admin = await requireAdminForRead(ctx); const connector = await ctx.db .query("connectors") - .withIndex("by_org_id_and_slug", (q) => - q.eq("orgId", admin.orgId).eq("slug", "x"), - ) + .withIndex("by_org_id_and_slug", (q) => q.eq("orgId", admin.orgId).eq("slug", "x")) .unique(); if (!connector || connector.revokedAt) { return { configured: false, handle: null, agentName: null }; @@ -142,18 +133,15 @@ export const setXSyncHandle = mutation({ args: { handle: v.union(v.string(), v.null()) }, handler: async (ctx, args) => { const admin = await requireAdminForWrite(ctx); - const normalized = args.handle === null - ? null - : args.handle.trim().replace(/^@/, "").toLowerCase(); + const normalized = + args.handle === null ? null : args.handle.trim().replace(/^@/, "").toLowerCase(); if (normalized !== null && !/^[a-z0-9_]{1,15}$/.test(normalized)) { invalid("X handle must be 1–15 letters, numbers, or underscores."); } let connector = await ctx.db .query("connectors") - .withIndex("by_org_id_and_slug", (q) => - q.eq("orgId", admin.orgId).eq("slug", "x"), - ) + .withIndex("by_org_id_and_slug", (q) => q.eq("orgId", admin.orgId).eq("slug", "x")) .unique(); if (connector?.revokedAt) invalid("The X connector was revoked."); if (!connector && normalized !== null) { @@ -217,7 +205,10 @@ export const provision = action({ capability: connectorCapability, authStrategy: connectorAuthStrategy, }, - handler: async (ctx, args): Promise<{ + handler: async ( + ctx, + args, + ): Promise<{ connectorId: Id<"connectors">; agentId: Id<"users">; token: string | null; @@ -229,7 +220,8 @@ export const provision = action({ // agentTasks connectors poll with a bearer token. inboundEvents accepts // either a provider signature (github) or a bearer token (the opt-in // browser-extension flow, e.g. the x digest). - args.capability === "agentTasks" && args.authStrategy !== "bearer" + args.capability === "agentTasks" && + args.authStrategy !== "bearer" ) { invalid("That authentication strategy does not match the connector capability."); } @@ -237,14 +229,13 @@ export const provision = action({ const credentialId = args.authStrategy === "bearer" ? randomHex(8) : undefined; const secret = randomHex(32); const secretHash = secret ? await hashConnectorSecret(secret) : undefined; - const encryptedSecret = args.authStrategy === "providerSignature" - ? await encryptConnectorSecret( - secret, - connectorSecretKeyring(env), - ) - : undefined; - const created: { connectorId: Id<"connectors">; agentId: Id<"users"> } = - await ctx.runMutation(internal.connectors.provisionRecord, { + const encryptedSecret = + args.authStrategy === "providerSignature" + ? await encryptConnectorSecret(secret, connectorSecretKeyring(env)) + : undefined; + const created: { connectorId: Id<"connectors">; agentId: Id<"users"> } = await ctx.runMutation( + internal.connectors.provisionRecord, + { adminTokenIdentifier: identity.tokenIdentifier, name: args.name, slug: args.slug, @@ -253,7 +244,8 @@ export const provision = action({ credentialId, secretHash, encryptedSecret, - }); + }, + ); return { ...created, token: credentialId ? `${TOKEN_PREFIX}.${credentialId}.${secret}` : null, @@ -264,7 +256,10 @@ export const provision = action({ export const rewrapGithubSecret = action({ args: { connectorId: v.id("connectors") }, - handler: async (ctx, args): Promise<{ + handler: async ( + ctx, + args, + ): Promise<{ connectorId: Id<"connectors">; keyId: string; rewrapped: boolean; @@ -318,9 +313,7 @@ export const provisionRecord = internalMutation({ handler: async (ctx, args) => { const admin = await ctx.db .query("users") - .withIndex("by_token_identifier", (q) => - q.eq("tokenIdentifier", args.adminTokenIdentifier), - ) + .withIndex("by_token_identifier", (q) => q.eq("tokenIdentifier", args.adminTokenIdentifier)) .unique(); if ( !admin?.orgId || @@ -439,9 +432,7 @@ export const getGithubSecretForRewrap = internalQuery({ handler: async (ctx, args) => { const admin = await ctx.db .query("users") - .withIndex("by_token_identifier", (q) => - q.eq("tokenIdentifier", args.adminTokenIdentifier), - ) + .withIndex("by_token_identifier", (q) => q.eq("tokenIdentifier", args.adminTokenIdentifier)) .unique(); if ( !admin?.orgId || @@ -479,9 +470,7 @@ export const commitGithubSecretRewrap = internalMutation({ handler: async (ctx, args) => { const admin = await ctx.db .query("users") - .withIndex("by_token_identifier", (q) => - q.eq("tokenIdentifier", args.adminTokenIdentifier), - ) + .withIndex("by_token_identifier", (q) => q.eq("tokenIdentifier", args.adminTokenIdentifier)) .unique(); if ( !admin?.orgId || @@ -576,10 +565,7 @@ async function authenticatedBearerConnector( } async function taskContext(ctx: MutationCtx, task: Doc<"agentTasks">) { - const [post, agent] = await Promise.all([ - ctx.db.get(task.postId), - ctx.db.get(task.agentId), - ]); + const [post, agent] = await Promise.all([ctx.db.get(task.postId), ctx.db.get(task.agentId)]); if (!post || !agent || post.orgId !== task.orgId || agent.orgId !== task.orgId) { invalid("Task context could not be loaded."); } @@ -615,11 +601,7 @@ export const claimAgentTask = internalMutation({ externalRunId: v.string(), }, handler: async (ctx, args) => { - const connector = await authenticatedBearerConnector( - ctx, - args.credentialId, - args.secretHash, - ); + const connector = await authenticatedBearerConnector(ctx, args.credentialId, args.secretHash); if (connector.capability !== "agentTasks") forbidden("Connector cannot run tasks."); const task = await ctx.db.get(args.taskId); if ( @@ -672,11 +654,7 @@ export const finishAgentTask = internalMutation({ ), }, handler: async (ctx, args) => { - const connector = await authenticatedBearerConnector( - ctx, - args.credentialId, - args.secretHash, - ); + const connector = await authenticatedBearerConnector(ctx, args.credentialId, args.secretHash); const externalRunId = normalizeExternalRunId(args.externalRunId); const task = await ctx.db.get(args.taskId); if ( @@ -874,9 +852,7 @@ export const recordInboundEvent = internalMutation({ await ctx.db.insert("auditLog", { orgId: connector.orgId, actorId: connector.agentId, - action: agentTaskId - ? "connector.github.agent_task.queued" - : "connector.github.post.created", + action: agentTaskId ? "connector.github.agent_task.queued" : "connector.github.post.created", targetType: agentTaskId ? "agentTask" : "post", targetId: agentTaskId ?? postId, metadata: JSON.stringify({ connectorId: connector._id, eventId, eventType }), @@ -967,11 +943,7 @@ export const recordXCrossPost = internalMutation({ }), }, handler: async (ctx, args) => { - const connector = await authenticatedBearerConnector( - ctx, - args.credentialId, - args.secretHash, - ); + const connector = await authenticatedBearerConnector(ctx, args.credentialId, args.secretHash); if (connector.capability !== "inboundEvents") { forbidden("Connector cannot record inbound events."); } @@ -988,9 +960,7 @@ export const findXSyncConnector = internalQuery({ const connectors = await ctx.db.query("connectors").take(500); const match = connectors.find( (connector) => - connector.slug === "x" && - connector.capability === "inboundEvents" && - !connector.revokedAt, + connector.slug === "x" && connector.capability === "inboundEvents" && !connector.revokedAt, ); return match ? { connectorId: match._id } : null; }, @@ -1029,11 +999,7 @@ export const recordXCrossPostFromSync = internalMutation({ }, handler: async (ctx, args) => { const connector = await ctx.db.get(args.connectorId); - if ( - !connector || - connector.capability !== "inboundEvents" || - connector.revokedAt - ) { + if (!connector || connector.capability !== "inboundEvents" || connector.revokedAt) { forbidden("Inbound connector is unavailable."); } const agent = await ctx.db.get(connector.agentId); diff --git a/convex/crons.ts b/convex/crons.ts index 4701d0b..15f826a 100644 --- a/convex/crons.ts +++ b/convex/crons.ts @@ -14,11 +14,6 @@ crons.interval( // X → Postwork cross-posting. A no-op until X_SYNC_HANDLE and // X_SYNC_CONNECTOR_ID are set on the deployment. -crons.interval( - "mirror new x posts into postwork", - { minutes: 30 }, - internal.xSync.run, - {}, -); +crons.interval("mirror new x posts into postwork", { minutes: 30 }, internal.xSync.run, {}); export default crons; diff --git a/convex/discussions.ts b/convex/discussions.ts index 48e7204..b3c0767 100644 --- a/convex/discussions.ts +++ b/convex/discussions.ts @@ -26,7 +26,8 @@ async function ensureThreadDoc( viewerId: Id<"users">, ): Promise> { const viewer = await ctx.db.get(viewerId); - if (!viewer?.orgId) throw new ConvexError({ code: "FORBIDDEN", message: "Product identity required." }); + if (!viewer?.orgId) + throw new ConvexError({ code: "FORBIDDEN", message: "Product identity required." }); const orgId = viewer.orgId; const existing = await ctx.db .query("posts") @@ -102,7 +103,8 @@ export const listCounts = query({ args: { slugs: v.array(v.string()) }, handler: async (ctx, { slugs }) => { const { orgId, viewer, authenticated } = await resolveReadScope(ctx); - if (authenticated && !viewer) return slugs.map((slug) => ({ slug, replyCount: 0, exists: false })); + if (authenticated && !viewer) + return slugs.map((slug) => ({ slug, replyCount: 0, exists: false })); return await Promise.all( slugs.map(async (slug) => { const post = await ctx.db diff --git a/convex/flashExperiments.ts b/convex/flashExperiments.ts index ef73f2e..d6690ce 100644 --- a/convex/flashExperiments.ts +++ b/convex/flashExperiments.ts @@ -22,8 +22,7 @@ export const listVotes = query({ viewerVote: identity === null ? null - : (votes.find((vote) => vote.voterSubject === identity.subject) - ?.vote ?? null), + : (votes.find((vote) => vote.voterSubject === identity.subject)?.vote ?? null), }; }), ); @@ -43,9 +42,7 @@ export const setVote = mutation({ const existing = await ctx.db .query("flashExperimentVotes") - .withIndex("by_slug_voter", (q) => - q.eq("slug", slug).eq("voterSubject", identity.subject), - ) + .withIndex("by_slug_voter", (q) => q.eq("slug", slug).eq("voterSubject", identity.subject)) .unique(); if (vote === null) { diff --git a/convex/githubWebhooks.test.ts b/convex/githubWebhooks.test.ts index d5a1d18..72ca32b 100644 --- a/convex/githubWebhooks.test.ts +++ b/convex/githubWebhooks.test.ts @@ -6,10 +6,7 @@ import { beforeEach, describe, expect, test } from "vitest"; import { api } from "./_generated/api"; import type { Id } from "./_generated/dataModel"; import schema from "./schema"; -import { - GITHUB_WEBHOOK_MAX_BYTES, - signGitHubPayload, -} from "./lib/githubWebhooks"; +import { GITHUB_WEBHOOK_MAX_BYTES, signGitHubPayload } from "./lib/githubWebhooks"; const modules = import.meta.glob("./**/*.ts"); const ENCRYPTION_KEY = "11".repeat(32); @@ -121,7 +118,7 @@ describe("GitHub webhook ingestion", () => { const response = await deliver(state); expect(response.status).toBe(202); - const result = await response.json() as { + const result = (await response.json()) as { duplicate: boolean; postId: string; agentTaskId?: string; @@ -203,9 +200,7 @@ describe("GitHub webhook ingestion", () => { expect(stored.events).toEqual([]); expect(stored.posts).toEqual([]); expect(stored.tasks).toEqual([]); - expect(stored.audit.map((entry) => entry.action)).toEqual([ - "connector.provisioned", - ]); + expect(stored.audit.map((entry) => entry.action)).toEqual(["connector.provisioned"]); }); test("rejects an invalidly signed ping before any mutation", async () => { @@ -338,7 +333,7 @@ describe("GitHub webhook ingestion", () => { state.authed.action(api.connectors.rewrapGithubSecret, { connectorId: other.connectorId, }), - ).rejects.toThrow(); + ).rejects.toThrow("GitHub connector not found."); const after = await state.t.run(async (ctx) => ctx.db.get(other.connectorId)); expect(after?.encryptedSecret).toBe(before?.encryptedSecret); }); @@ -350,7 +345,7 @@ describe("GitHub webhook ingestion", () => { expect(first.status).toBe(202); expect(duplicate.status).toBe(200); - const firstBody = await first.json() as { eventId: string; postId: string }; + const firstBody = (await first.json()) as { eventId: string; postId: string }; expect(await duplicate.json()).toMatchObject({ eventId: firstBody.eventId, postId: firstBody.postId, @@ -371,9 +366,7 @@ describe("GitHub webhook ingestion", () => { }); expect(response.status).toBe(422); - const events = await state.t.run(async (ctx) => - ctx.db.query("connectorEvents").collect(), - ); + const events = await state.t.run(async (ctx) => ctx.db.query("connectorEvents").collect()); expect(events).toEqual([]); }); @@ -424,10 +417,8 @@ describe("GitHub webhook ingestion", () => { payload: issuePayload("reopened"), }); expect(accepted.status).toBe(202); - const result = await accepted.json() as { postId: string }; - const post = await state.t.run(async (ctx) => - ctx.db.get(result.postId as Id<"posts">), - ); + const result = (await accepted.json()) as { postId: string }; + const post = await state.t.run(async (ctx) => ctx.db.get(result.postId as Id<"posts">)); expect(post).toMatchObject({ orgId: other.orgId, authorId: otherConnector.agentId, @@ -453,7 +444,7 @@ describe("GitHub webhook ingestion", () => { }); expect(response.status).toBe(202); - const result = await response.json() as { postId: string; agentTaskId: string }; + const result = (await response.json()) as { postId: string; agentTaskId: string }; const stored = await state.t.run(async (ctx) => ({ post: await ctx.db.get(result.postId as never), task: await ctx.db.get(result.agentTaskId as never), diff --git a/convex/http.ts b/convex/http.ts index 6b6658b..74a909f 100644 --- a/convex/http.ts +++ b/convex/http.ts @@ -3,10 +3,7 @@ import { env, httpAction } from "./_generated/server"; import { internal } from "./_generated/api"; import type { Id } from "./_generated/dataModel"; import { hashConnectorSecret, parseConnectorToken } from "./connectors"; -import { - connectorSecretKeyring, - decryptConnectorSecret, -} from "./lib/connectorSecrets"; +import { connectorSecretKeyring, decryptConnectorSecret } from "./lib/connectorSecrets"; import { GITHUB_WEBHOOK_MAX_BYTES, githubExternalEventId, @@ -92,18 +89,12 @@ http.route({ let secret: string; try { - secret = (await decryptConnectorSecret( - material.encryptedSecret, - connectorSecretKeyring(env), - )).secret; + secret = (await decryptConnectorSecret(material.encryptedSecret, connectorSecretKeyring(env))) + .secret; } catch { return json({ error: "connector_unavailable" }, 503); } - if (!(await verifyGitHubSignature( - bytes, - request.headers.get("x-hub-signature-256"), - secret, - ))) { + if (!(await verifyGitHubSignature(bytes, request.headers.get("x-hub-signature-256"), secret))) { return json({ error: "invalid_signature" }, 401); } @@ -138,11 +129,7 @@ http.route({ const credential = parseConnectorToken(request.headers.get("authorization")); if (!credential) return json({ error: "unauthorized" }, 401); const body = objectBody(await request.json().catch(() => null)); - if ( - !body || - typeof body.taskId !== "string" || - typeof body.externalRunId !== "string" - ) { + if (!body || typeof body.taskId !== "string" || typeof body.externalRunId !== "string") { return json({ error: "invalid_request" }, 400); } try { @@ -210,17 +197,18 @@ http.route({ ) { return json({ error: "invalid_request" }, 400); } - const outcome = body.status === "done" - ? typeof body.body === "string" - ? { - status: "done" as const, - body: body.body, - model: typeof body.model === "string" ? body.model : undefined, - } - : null - : typeof body.error === "string" - ? { status: "failed" as const, error: body.error } - : null; + const outcome = + body.status === "done" + ? typeof body.body === "string" + ? { + status: "done" as const, + body: body.body, + model: typeof body.model === "string" ? body.model : undefined, + } + : null + : typeof body.error === "string" + ? { status: "failed" as const, error: body.error } + : null; if (!outcome) return json({ error: "invalid_request" }, 400); try { const result = await ctx.runMutation(internal.connectors.finishAgentTask, { diff --git a/convex/lib/aiModels.ts b/convex/lib/aiModels.ts index 0e10a12..1728c74 100644 --- a/convex/lib/aiModels.ts +++ b/convex/lib/aiModels.ts @@ -30,7 +30,8 @@ export function normalizeModelId(value: string): string { throw new ConvexError({ code: "INVALID_INPUT", field: "modelId", - message: "Model ID can only contain letters, numbers, dots, dashes, underscores, tildes, slashes, colons, and plus signs.", + message: + "Model ID can only contain letters, numbers, dots, dashes, underscores, tildes, slashes, colons, and plus signs.", }); } return modelId; diff --git a/convex/lib/attachmentStorage.ts b/convex/lib/attachmentStorage.ts index ca9782f..06e84d5 100644 --- a/convex/lib/attachmentStorage.ts +++ b/convex/lib/attachmentStorage.ts @@ -1,19 +1,13 @@ import { ConvexError } from "convex/values"; import type { Id } from "../_generated/dataModel"; import type { MutationCtx } from "../_generated/server"; -import { - attachmentMaxBytes, - type attachmentInputSchema, -} from "./validation"; +import { attachmentMaxBytes, type attachmentInputSchema } from "./validation"; import type { z } from "zod"; type AttachmentInput = z.infer; /** Reject storage already owned by a durable post or reply attachment. */ -export async function assertStorageUnattached( - ctx: MutationCtx, - storageId: Id<"_storage">, -) { +export async function assertStorageUnattached(ctx: MutationCtx, storageId: Id<"_storage">) { const existingAttachment = await ctx.db .query("postAttachments") .withIndex("by_storage_id", (q) => q.eq("storageId", storageId)) diff --git a/convex/lib/connectorSecrets.ts b/convex/lib/connectorSecrets.ts index 2e4b34d..cab9826 100644 --- a/convex/lib/connectorSecrets.ts +++ b/convex/lib/connectorSecrets.ts @@ -70,16 +70,10 @@ function previousKeys(value: string | undefined): Array<[string, string]> { export function connectorSecretKeyring( environment: ConnectorSecretEnvironment, ): ConnectorSecretKeyring { - const activeKeyId = normalizedKeyId( - environment.CONNECTOR_SECRET_ENCRYPTION_ACTIVE_KEY_ID, - ); - const activeKey = normalizedKey( - environment.CONNECTOR_SECRET_ENCRYPTION_ACTIVE_KEY, - ); + const activeKeyId = normalizedKeyId(environment.CONNECTOR_SECRET_ENCRYPTION_ACTIVE_KEY_ID); + const activeKey = normalizedKey(environment.CONNECTOR_SECRET_ENCRYPTION_ACTIVE_KEY); const keys = new Map([[activeKeyId, activeKey]]); - for (const [keyId, key] of previousKeys( - environment.CONNECTOR_SECRET_ENCRYPTION_PREVIOUS_KEYS, - )) { + for (const [keyId, key] of previousKeys(environment.CONNECTOR_SECRET_ENCRYPTION_PREVIOUS_KEYS)) { if (keys.has(keyId)) { throw new Error("Connector encryption key IDs must be unique."); } diff --git a/convex/lib/githubWebhooks.ts b/convex/lib/githubWebhooks.ts index 9d5355f..bca1a64 100644 --- a/convex/lib/githubWebhooks.ts +++ b/convex/lib/githubWebhooks.ts @@ -33,9 +33,7 @@ function text(value: unknown, max: number): string | null { } function integer(value: unknown): number | null { - return typeof value === "number" && Number.isSafeInteger(value) && value > 0 - ? value - : null; + return typeof value === "number" && Number.isSafeInteger(value) && value > 0 ? value : null; } function safeUrl(value: unknown): string | null { @@ -71,10 +69,7 @@ function postBody(fields: { ].join("\n"); } -function routeIssue( - action: string, - payload: Record, -): GitHubEventRoute | null { +function routeIssue(action: string, payload: Record): GitHubEventRoute | null { if (!new Set(["opened", "reopened", "closed"]).has(action)) return null; const issue = record(payload.issue); const repository = repositoryName(payload); @@ -135,16 +130,18 @@ function routeWorkflowRun( if (!workflow || !repository || !actor || !name || !conclusion || !url || !runNumber) { return null; } - if (!new Set([ - "action_required", - "cancelled", - "failure", - "neutral", - "skipped", - "stale", - "startup_failure", - "timed_out", - ]).has(conclusion)) { + if ( + !new Set([ + "action_required", + "cancelled", + "failure", + "neutral", + "skipped", + "stale", + "startup_failure", + "timed_out", + ]).has(conclusion) + ) { return null; } const title = `GitHub workflow ${conclusion}: ${name} #${runNumber}`.slice(0, 160); @@ -173,9 +170,7 @@ export function routeGitHubEvent(event: string, value: unknown): GitHubEventRout export function githubExternalEventId(deliveryId: string): string | null { const normalized = deliveryId.trim(); - return /^[a-zA-Z0-9][a-zA-Z0-9._:-]{0,199}$/.test(normalized) - ? `github:${normalized}` - : null; + return /^[a-zA-Z0-9][a-zA-Z0-9._:-]{0,199}$/.test(normalized) ? `github:${normalized}` : null; } export async function signGitHubPayload( diff --git a/convex/lib/inviteTargets.ts b/convex/lib/inviteTargets.ts index fdcd3fd..e0f6548 100644 --- a/convex/lib/inviteTargets.ts +++ b/convex/lib/inviteTargets.ts @@ -68,7 +68,5 @@ export function formatInviteTarget( invite: Pick, "targetKind" | "targetValue">, ): string | undefined { if (!invite.targetKind || !invite.targetValue) return undefined; - return invite.targetKind === "github" - ? `@${invite.targetValue}` - : invite.targetValue; + return invite.targetKind === "github" ? `@${invite.targetValue}` : invite.targetValue; } diff --git a/convex/lib/observability.ts b/convex/lib/observability.ts index da9874a..7036a93 100644 --- a/convex/lib/observability.ts +++ b/convex/lib/observability.ts @@ -42,10 +42,7 @@ export function logError(event: string, data?: Record) { * ConvexError(s) (auth, validation, rate-limit, not-found) are re-thrown * as-is — they carry client-facing codes and should not be swallowed. */ -export function withLogging unknown>( - event: string, - fn: T, -): T { +export function withLogging unknown>(event: string, fn: T): T { return ((...args: never[]) => { try { const result = fn(...args); diff --git a/convex/lib/validation.test.ts b/convex/lib/validation.test.ts index 5e1f3e2..25931d8 100644 --- a/convex/lib/validation.test.ts +++ b/convex/lib/validation.test.ts @@ -16,9 +16,7 @@ describe("attachment metadata validation", () => { }); test("rejects a mismatched media kind", () => { - expect( - attachmentInputSchema.safeParse({ ...base, mediaKind: "image" }).success, - ).toBe(false); + expect(attachmentInputSchema.safeParse({ ...base, mediaKind: "image" }).success).toBe(false); }); test("applies image and video size limits independently", () => { diff --git a/convex/lib/validation.ts b/convex/lib/validation.ts index 1e1720e..114e5c4 100644 --- a/convex/lib/validation.ts +++ b/convex/lib/validation.ts @@ -93,47 +93,53 @@ export const searchTermSchema = z .max(LIMITS.SEARCH_TERM_MAX, "Search term is too long."); /** Attachment metadata validated before storing a post attachment record. */ -export const attachmentInputSchema = z.object({ - storageId: z.string().min(1), - uploadToken: z.string().min(1), - filename: z.string().min(1).max(200), - contentType: z.string().trim().min(1), - mediaKind: z.enum(["image", "video", "file"]), - size: z.number().positive(), - width: z.number().positive().optional(), - height: z.number().positive().optional(), - durationMs: z.number().positive().optional(), -}).superRefine((attachment, ctx) => { - const expectedKind = attachmentMediaKind(attachment.contentType); - if (attachment.mediaKind !== expectedKind) { - ctx.addIssue({ code: "custom", message: "Media kind does not match its content type." }); - } - const maxBytes = attachmentMaxBytes(attachment.contentType); - if (maxBytes !== null && attachment.size > maxBytes) { - ctx.addIssue({ - code: "too_big", - maximum: maxBytes, - origin: "number", - inclusive: true, - message: attachment.mediaKind === "video" - ? `Videos must be ${formatMediaSize(LIMITS.ATTACHMENT_MAX_VIDEO_BYTES)} or smaller.` - : attachment.mediaKind === "image" - ? `Images must be ${formatMediaSize(LIMITS.ATTACHMENT_MAX_IMAGE_BYTES)} or smaller.` - : `Files must be ${formatMediaSize(LIMITS.ATTACHMENT_MAX_FILE_BYTES)} or smaller.`, - }); - } - if (attachment.mediaKind === "image" && attachment.durationMs !== undefined) { - ctx.addIssue({ code: "custom", message: "Images cannot include a duration." }); - } - if ( - attachment.mediaKind === "file" && - (attachment.width !== undefined || - attachment.height !== undefined || - attachment.durationMs !== undefined) - ) { - ctx.addIssue({ code: "custom", message: "Files cannot include media dimensions or duration." }); - } -}); +export const attachmentInputSchema = z + .object({ + storageId: z.string().min(1), + uploadToken: z.string().min(1), + filename: z.string().min(1).max(200), + contentType: z.string().trim().min(1), + mediaKind: z.enum(["image", "video", "file"]), + size: z.number().positive(), + width: z.number().positive().optional(), + height: z.number().positive().optional(), + durationMs: z.number().positive().optional(), + }) + .superRefine((attachment, ctx) => { + const expectedKind = attachmentMediaKind(attachment.contentType); + if (attachment.mediaKind !== expectedKind) { + ctx.addIssue({ code: "custom", message: "Media kind does not match its content type." }); + } + const maxBytes = attachmentMaxBytes(attachment.contentType); + if (maxBytes !== null && attachment.size > maxBytes) { + ctx.addIssue({ + code: "too_big", + maximum: maxBytes, + origin: "number", + inclusive: true, + message: + attachment.mediaKind === "video" + ? `Videos must be ${formatMediaSize(LIMITS.ATTACHMENT_MAX_VIDEO_BYTES)} or smaller.` + : attachment.mediaKind === "image" + ? `Images must be ${formatMediaSize(LIMITS.ATTACHMENT_MAX_IMAGE_BYTES)} or smaller.` + : `Files must be ${formatMediaSize(LIMITS.ATTACHMENT_MAX_FILE_BYTES)} or smaller.`, + }); + } + if (attachment.mediaKind === "image" && attachment.durationMs !== undefined) { + ctx.addIssue({ code: "custom", message: "Images cannot include a duration." }); + } + if ( + attachment.mediaKind === "file" && + (attachment.width !== undefined || + attachment.height !== undefined || + attachment.durationMs !== undefined) + ) { + ctx.addIssue({ + code: "custom", + message: "Files cannot include media dimensions or duration.", + }); + } + }); /** * Parse a value through a zod schema, throwing a typed ConvexError on failure. diff --git a/convex/linkPreviews.test.ts b/convex/linkPreviews.test.ts index 6d82c8d..009abff 100644 --- a/convex/linkPreviews.test.ts +++ b/convex/linkPreviews.test.ts @@ -17,7 +17,9 @@ describe("link previews", () => { await t.mutation(api.linkPreviews.request, { urls: [url] }); const pending = await t.query(api.linkPreviews.get, { urls: [url] }); - const scheduled = await t.run(async (ctx) => ctx.db.system.query("_scheduled_functions").take(10)); + const scheduled = await t.run(async (ctx) => + ctx.db.system.query("_scheduled_functions").take(10), + ); expect(pending).toHaveLength(1); expect(pending[0]).toMatchObject({ url, status: "pending" }); expect(scheduled).toHaveLength(1); @@ -31,7 +33,11 @@ describe("link previews", () => { siteName: "Example", }); const stored = await t.query(api.linkPreviews.get, { urls: [url] }); - expect(stored[0]).toMatchObject({ status: "ok", title: "A useful article", siteName: "Example" }); + expect(stored[0]).toMatchObject({ + status: "ok", + title: "A useful article", + siteName: "Example", + }); }); test("rejects private and credential-bearing URLs", async () => { @@ -45,12 +51,16 @@ describe("link previews", () => { "https://user:password@example.com/", ]) { expect(normalizePreviewUrl(url)).toBeNull(); - await expect(t.mutation(api.linkPreviews.request, { urls: [url] })).rejects.toThrow("Invalid or unsafe"); + await expect(t.mutation(api.linkPreviews.request, { urls: [url] })).rejects.toThrow( + "Invalid or unsafe", + ); } }); test("parses OpenGraph fields, title fallback, and safe relative images", () => { - expect(parseOpenGraph(` + expect( + parseOpenGraph( + ` Fallback title @@ -58,13 +68,21 @@ describe("link previews", () => { - `, "https://example.com/posts/one")).toEqual({ + `, + "https://example.com/posts/one", + ), + ).toEqual({ title: "Postwork & friends", description: "A durable team record.", imageUrl: "https://example.com/preview.jpg", siteName: "Example Site", }); - expect(parseOpenGraph("Only a title", "https://example.com")).toEqual({ + expect( + parseOpenGraph( + "Only a title", + "https://example.com", + ), + ).toEqual({ title: "Only a title", description: undefined, imageUrl: undefined, diff --git a/convex/linkPreviews.ts b/convex/linkPreviews.ts index 912eed1..3049fe8 100644 --- a/convex/linkPreviews.ts +++ b/convex/linkPreviews.ts @@ -10,7 +10,13 @@ const FETCH_TIMEOUT_MS = 8_000; function isPrivateHostname(hostname: string): boolean { const host = hostname.toLowerCase().replace(/^\[|\]$/g, ""); - if (host === "localhost" || host.endsWith(".local") || host === "::1" || host === "0:0:0:0:0:0:0:1") return true; + if ( + host === "localhost" || + host.endsWith(".local") || + host === "::1" || + host === "0:0:0:0:0:0:0:1" + ) + return true; if (/^(?:0|10|127)(?:\.|$)/.test(host) || /^192\.168(?:\.|$)/.test(host)) return true; const match172 = host.match(/^172\.(\d+)(?:\.|$)/); if (match172 && Number(match172[1]) >= 16 && Number(match172[1]) <= 31) return true; @@ -22,7 +28,13 @@ export function normalizePreviewUrl(value: string): string | null { if (value.length === 0 || value.length > MAX_URL_LENGTH) return null; try { const url = new URL(value); - if ((url.protocol !== "http:" && url.protocol !== "https:") || url.username || url.password || isPrivateHostname(url.hostname)) return null; + if ( + (url.protocol !== "http:" && url.protocol !== "https:") || + url.username || + url.password || + isPrivateHostname(url.hostname) + ) + return null; url.hash = ""; return url.toString(); } catch { @@ -79,8 +91,17 @@ export const get = query({ args: { urls: v.array(v.string()) }, handler: async (ctx, { urls }) => { if (urls.length > MAX_URLS) throw new ConvexError("At most 5 link previews can be requested"); - const normalized = [...new Set(urls.map(normalizePreviewUrl).filter((url): url is string => url !== null))]; - return Promise.all(normalized.map((url) => ctx.db.query("linkPreviews").withIndex("by_url", (q) => q.eq("url", url)).unique())); + const normalized = [ + ...new Set(urls.map(normalizePreviewUrl).filter((url): url is string => url !== null)), + ]; + return Promise.all( + normalized.map((url) => + ctx.db + .query("linkPreviews") + .withIndex("by_url", (q) => q.eq("url", url)) + .unique(), + ), + ); }, }); @@ -89,14 +110,22 @@ export const request = mutation({ handler: async (ctx, { urls }) => { if (urls.length > MAX_URLS) throw new ConvexError("At most 5 link previews can be requested"); const now = Date.now(); - const normalized = [...new Set(urls.map((value) => { - const url = normalizePreviewUrl(value); - if (!url) throw new ConvexError("Invalid or unsafe preview URL"); - return url; - }))]; + const normalized = [ + ...new Set( + urls.map((value) => { + const url = normalizePreviewUrl(value); + if (!url) throw new ConvexError("Invalid or unsafe preview URL"); + return url; + }), + ), + ]; for (const url of normalized) { - const existing = await ctx.db.query("linkPreviews").withIndex("by_url", (q) => q.eq("url", url)).unique(); - if (existing && (existing.status !== "failed" || now - existing.fetchedAt < RETRY_AFTER_MS)) continue; + const existing = await ctx.db + .query("linkPreviews") + .withIndex("by_url", (q) => q.eq("url", url)) + .unique(); + if (existing && (existing.status !== "failed" || now - existing.fetchedAt < RETRY_AFTER_MS)) + continue; if (existing) await ctx.db.patch(existing._id, { status: "pending", fetchedAt: now }); else await ctx.db.insert("linkPreviews", { url, status: "pending", fetchedAt: now }); await ctx.scheduler.runAfter(0, internal.linkPreviews.fetchPreview, { url }); @@ -115,7 +144,10 @@ export const storePreview = internalMutation({ siteName: v.optional(v.string()), }, handler: async (ctx, args) => { - const row = await ctx.db.query("linkPreviews").withIndex("by_url", (q) => q.eq("url", args.url)).unique(); + const row = await ctx.db + .query("linkPreviews") + .withIndex("by_url", (q) => q.eq("url", args.url)) + .unique(); const value = { ...args, fetchedAt: Date.now() }; if (row) await ctx.db.patch(row._id, value); else await ctx.db.insert("linkPreviews", value); @@ -140,7 +172,10 @@ async function readLimited(response: Response): Promise { } const bytes = new Uint8Array(size); let offset = 0; - for (const chunk of chunks) { bytes.set(chunk, offset); offset += chunk.byteLength; } + for (const chunk of chunks) { + bytes.set(chunk, offset); + offset += chunk.byteLength; + } return new TextDecoder().decode(bytes); } diff --git a/convex/migrations.ts b/convex/migrations.ts index 3396aab..442df72 100644 --- a/convex/migrations.ts +++ b/convex/migrations.ts @@ -5,19 +5,33 @@ import { PRODUCT_ORG_NAME, PRODUCT_ORG_SLUG, notFound } from "./authUsers"; export const ensureProductOrg = internalMutation({ args: {}, handler: async (ctx) => { - const existing = await ctx.db.query("orgs").withIndex("by_slug", (q) => q.eq("slug", PRODUCT_ORG_SLUG)).unique(); + const existing = await ctx.db + .query("orgs") + .withIndex("by_slug", (q) => q.eq("slug", PRODUCT_ORG_SLUG)) + .unique(); if (existing) return existing._id; - return await ctx.db.insert("orgs", { name: PRODUCT_ORG_NAME, slug: PRODUCT_ORG_SLUG, createdAt: Date.now() }); + return await ctx.db.insert("orgs", { + name: PRODUCT_ORG_NAME, + slug: PRODUCT_ORG_SLUG, + createdAt: Date.now(), + }); }, }); export const activateFirstProductAdmin = internalMutation({ args: { userId: v.id("users") }, handler: async (ctx, args) => { - const org = await ctx.db.query("orgs").withIndex("by_slug", (q) => q.eq("slug", PRODUCT_ORG_SLUG)).unique(); + const org = await ctx.db + .query("orgs") + .withIndex("by_slug", (q) => q.eq("slug", PRODUCT_ORG_SLUG)) + .unique(); if (!org) notFound("Product organization not found. Run migrations:ensureProductOrg first."); - const admins = await ctx.db.query("users").withIndex("by_org_id_and_role", (q) => q.eq("orgId", org._id).eq("role", "admin")).collect(); - if (admins.some((user) => user.status === "active" && !user.deactivatedAt)) return { activated: false as const }; + const admins = await ctx.db + .query("users") + .withIndex("by_org_id_and_role", (q) => q.eq("orgId", org._id).eq("role", "admin")) + .collect(); + if (admins.some((user) => user.status === "active" && !user.deactivatedAt)) + return { activated: false as const }; const user = await ctx.db.get(args.userId); if (!user || user.orgId !== org._id || !user.tokenIdentifier || user.isAgent) { notFound("Specified Clerk product user not found."); @@ -40,17 +54,38 @@ export const auditTenantOwnership = internalQuery({ handler: async (ctx) => { const missingOrg: string[] = []; const crossOrg: string[] = []; - for (const table of ["users", "spaces", "spaceMemberships", "posts", "replies", "postReads", "notificationPreferences", "notificationDeliveries", "agentTasks", "connectors", "connectorEvents", "aiGenerationSettings", "postAttachments", "attachmentUploadTickets", "invites", "accessRequests", "auditLog"] as const) { - for (const row of await ctx.db.query(table).collect()) if (!row.orgId) missingOrg.push(`${table}:${row._id}`); + for (const table of [ + "users", + "spaces", + "spaceMemberships", + "posts", + "replies", + "postReads", + "notificationPreferences", + "notificationDeliveries", + "agentTasks", + "connectors", + "connectorEvents", + "aiGenerationSettings", + "postAttachments", + "attachmentUploadTickets", + "invites", + "accessRequests", + "auditLog", + ] as const) { + for (const row of await ctx.db.query(table).collect()) + if (!row.orgId) missingOrg.push(`${table}:${row._id}`); } for (const row of await ctx.db.query("posts").collect()) { const author = await ctx.db.get(row.authorId); const space = row.spaceId ? await ctx.db.get(row.spaceId) : null; if (!author || author.orgId !== row.orgId) crossOrg.push(`posts:${row._id}:authorId`); - if (row.spaceId && (!space || space.orgId !== row.orgId)) crossOrg.push(`posts:${row._id}:spaceId`); + if (row.spaceId && (!space || space.orgId !== row.orgId)) + crossOrg.push(`posts:${row._id}:spaceId`); for (const participantId of row.participantIds) { const participant = await ctx.db.get(participantId); - if (!participant || participant.orgId !== row.orgId) crossOrg.push(`posts:${row._id}:participantIds`); + if (!participant || participant.orgId !== row.orgId) + crossOrg.push(`posts:${row._id}:participantIds`); } } for (const row of await ctx.db.query("replies").collect()) { @@ -59,7 +94,8 @@ export const auditTenantOwnership = internalQuery({ const parent = row.parentId ? await ctx.db.get(row.parentId) : null; if (!post || post.orgId !== row.orgId) crossOrg.push(`replies:${row._id}:postId`); if (!author || author.orgId !== row.orgId) crossOrg.push(`replies:${row._id}:authorId`); - if (row.parentId && (!parent || parent.orgId !== row.orgId || parent.postId !== row.postId)) crossOrg.push(`replies:${row._id}:parentId`); + if (row.parentId && (!parent || parent.orgId !== row.orgId || parent.postId !== row.postId)) + crossOrg.push(`replies:${row._id}:parentId`); } for (const row of await ctx.db.query("spaceMemberships").collect()) { const space = await ctx.db.get(row.spaceId); @@ -78,8 +114,10 @@ export const auditTenantOwnership = internalQuery({ const uploader = await ctx.db.get(row.uploadedBy); const reply = row.replyId ? await ctx.db.get(row.replyId) : null; if (!post || post.orgId !== row.orgId) crossOrg.push(`postAttachments:${row._id}:postId`); - if (!uploader || uploader.orgId !== row.orgId) crossOrg.push(`postAttachments:${row._id}:uploadedBy`); - if (row.replyId && (!reply || reply.orgId !== row.orgId || reply.postId !== row.postId)) crossOrg.push(`postAttachments:${row._id}:replyId`); + if (!uploader || uploader.orgId !== row.orgId) + crossOrg.push(`postAttachments:${row._id}:uploadedBy`); + if (row.replyId && (!reply || reply.orgId !== row.orgId || reply.postId !== row.postId)) + crossOrg.push(`postAttachments:${row._id}:replyId`); } for (const row of await ctx.db.query("agentTasks").collect()) { const [post, agent, requester] = await Promise.all([ @@ -89,10 +127,12 @@ export const auditTenantOwnership = internalQuery({ ]); if (!post || post.orgId !== row.orgId) crossOrg.push(`agentTasks:${row._id}:postId`); if (!agent || agent.orgId !== row.orgId) crossOrg.push(`agentTasks:${row._id}:agentId`); - if (!requester || requester.orgId !== row.orgId) crossOrg.push(`agentTasks:${row._id}:requestedById`); + if (!requester || requester.orgId !== row.orgId) + crossOrg.push(`agentTasks:${row._id}:requestedById`); if (row.connectorId) { const connector = await ctx.db.get(row.connectorId); - if (!connector || connector.orgId !== row.orgId || connector.agentId !== row.agentId) crossOrg.push(`agentTasks:${row._id}:connectorId`); + if (!connector || connector.orgId !== row.orgId || connector.agentId !== row.agentId) + crossOrg.push(`agentTasks:${row._id}:connectorId`); } } for (const row of await ctx.db.query("connectors").collect()) { @@ -100,12 +140,15 @@ export const auditTenantOwnership = internalQuery({ ctx.db.get(row.agentId), ctx.db.get(row.createdById), ]); - if (!agent?.isAgent || agent.orgId !== row.orgId) crossOrg.push(`connectors:${row._id}:agentId`); - if (!creator || creator.orgId !== row.orgId) crossOrg.push(`connectors:${row._id}:createdById`); + if (!agent?.isAgent || agent.orgId !== row.orgId) + crossOrg.push(`connectors:${row._id}:agentId`); + if (!creator || creator.orgId !== row.orgId) + crossOrg.push(`connectors:${row._id}:createdById`); } for (const row of await ctx.db.query("connectorEvents").collect()) { const connector = await ctx.db.get(row.connectorId); - if (!connector || connector.orgId !== row.orgId) crossOrg.push(`connectorEvents:${row._id}:connectorId`); + if (!connector || connector.orgId !== row.orgId) + crossOrg.push(`connectorEvents:${row._id}:connectorId`); } return { ok: missingOrg.length === 0 && crossOrg.length === 0, diff --git a/convex/notificationComposer.test.ts b/convex/notificationComposer.test.ts index 1f9cf00..71d0757 100644 --- a/convex/notificationComposer.test.ts +++ b/convex/notificationComposer.test.ts @@ -87,18 +87,10 @@ describe("composeOutboundCandidates", () => { ], }); expect(isWithinQuietHours(duringQuietHours, enabledPreferences)).toBe(true); - expect( - isWithinQuietHours( - new Date("2026-07-10T22:00:00.000Z"), - enabledPreferences, - ), - ).toBe(true); - expect( - isWithinQuietHours( - new Date("2026-07-11T08:00:00.000Z"), - enabledPreferences, - ), - ).toBe(false); + expect(isWithinQuietHours(new Date("2026-07-10T22:00:00.000Z"), enabledPreferences)).toBe(true); + expect(isWithinQuietHours(new Date("2026-07-11T08:00:00.000Z"), enabledPreferences)).toBe( + false, + ); }); test("digest composition includes urgent when immediate is off and remains bounded", () => { @@ -106,11 +98,7 @@ describe("composeOutboundCandidates", () => { item(`normal-${index}`, "normal", index), ); const candidates = composeOutboundCandidates({ - items: [ - item("urgent", "urgent", 100), - item("high", "high", 90), - ...extraNormalItems, - ], + items: [item("urgent", "urgent", 100), item("high", "high", 90), ...extraNormalItems], preferences: { ...enabledPreferences, immediateUrgentEnabled: false, @@ -121,9 +109,10 @@ describe("composeOutboundCandidates", () => { expect(candidates).toHaveLength(1); expect(candidates[0]?.kind).toBe("digest"); expect(candidates[0]?.items).toHaveLength(25); - expect( - candidates[0]?.items.slice(0, 2).map(({ postId }) => postId), - ).toEqual(["urgent", "high"]); + expect(candidates[0]?.items.slice(0, 2).map(({ postId }) => postId)).toEqual([ + "urgent", + "high", + ]); expect(candidates[0]?.omittedCount).toBe(2); }); }); diff --git a/convex/notificationComposer.ts b/convex/notificationComposer.ts index 0a05819..5a8d17b 100644 --- a/convex/notificationComposer.ts +++ b/convex/notificationComposer.ts @@ -96,10 +96,7 @@ export function isWithinQuietHours( now: Date, preferences: Pick< NotificationPreferences, - | "quietHoursEnabled" - | "quietHoursStart" - | "quietHoursEnd" - | "quietHoursTimeZone" + "quietHoursEnabled" | "quietHoursStart" | "quietHoursEnd" | "quietHoursTimeZone" >, ): boolean { if (!preferences.quietHoursEnabled) return false; @@ -113,9 +110,7 @@ export function isWithinQuietHours( return currentMinutes >= start || currentMinutes < end; } -function deduplicateUnreadItems( - items: readonly NotificationItem[], -): NotificationItem[] { +function deduplicateUnreadItems(items: readonly NotificationItem[]): NotificationItem[] { const byPost = new Map(); for (const item of items) { if (!item.unread) continue; diff --git a/convex/notificationDelivery.test.ts b/convex/notificationDelivery.test.ts index 6bce309..adfb9c7 100644 --- a/convex/notificationDelivery.test.ts +++ b/convex/notificationDelivery.test.ts @@ -78,9 +78,7 @@ describe("notification provider plan", () => { "https://postwork.example/app", "https://user:secret@postwork.example", ])("rejects a non-origin POSTWORK_APP_URL: %s", (appUrl) => { - expect( - planProviderDispatch(candidates, false, { ...config, appUrl }), - ).toEqual({ + expect(planProviderDispatch(candidates, false, { ...config, appUrl })).toEqual({ status: "provider_configuration_invalid", candidateCount: 1, variable: "POSTWORK_APP_URL", @@ -209,10 +207,10 @@ describe("Resend adapter", () => { test("classifies provider throttling as observable and retryable", async () => { const fetcher = vi.fn().mockResolvedValue( - new Response( - JSON.stringify({ name: "rate_limit_exceeded", message: "Try later." }), - { status: 429, headers: { "Content-Type": "application/json" } }, - ), + new Response(JSON.stringify({ name: "rate_limit_exceeded", message: "Try later." }), { + status: 429, + headers: { "Content-Type": "application/json" }, + }), ); await expect( @@ -235,10 +233,7 @@ describe("Resend adapter", () => { test.each([ ["concurrent_idempotent_requests", true], ["invalid_idempotent_request", false], - ] as const)("classifies Resend conflict %s with retryable=%s", async ( - code, - retryable, - ) => { + ] as const)("classifies Resend conflict %s with retryable=%s", async (code, retryable) => { const fetcher = vi.fn().mockResolvedValue( new Response(JSON.stringify({ name: code, message: "Conflict." }), { status: 409, @@ -307,8 +302,6 @@ describe("Resend adapter", () => { expect(content.html).toContain("Urgent <release>"); expect(content.html).not.toContain("attacker.example"); expect(content.html).not.toContain("javascript:"); - expect(content.html).toContain( - 'href="https://postwork.example/posts/post-1"', - ); + expect(content.html).toContain('href="https://postwork.example/posts/post-1"'); }); }); diff --git a/convex/notificationDelivery.ts b/convex/notificationDelivery.ts index cf59721..230d7e9 100644 --- a/convex/notificationDelivery.ts +++ b/convex/notificationDelivery.ts @@ -1,29 +1,19 @@ import { v } from "convex/values"; import { internal } from "./_generated/api"; import type { Id } from "./_generated/dataModel"; -import { - env, - internalAction, - internalMutation, - type ActionCtx, -} from "./_generated/server"; +import { env, internalAction, internalMutation, type ActionCtx } from "./_generated/server"; import { logError, logInfo, logWarn } from "./lib/observability"; import type { OutboundDeliveryCandidate } from "./notificationComposer"; const RESEND_IDEMPOTENCY_RETENTION_MS = 24 * 60 * 60 * 1_000; -export const DELIVERY_RETRY_WINDOW_MS = - RESEND_IDEMPOTENCY_RETENTION_MS - 60 * 60 * 1_000; +export const DELIVERY_RETRY_WINDOW_MS = RESEND_IDEMPOTENCY_RETENTION_MS - 60 * 60 * 1_000; const DELIVERY_ATTEMPT_LEASE_MS = 60 * 1_000; export const RESEND_REQUEST_TIMEOUT_MS = 10 * 1_000; const notificationItem = v.object({ postId: v.string(), title: v.string(), - priority: v.union( - v.literal("urgent"), - v.literal("high"), - v.literal("normal"), - ), + priority: v.union(v.literal("urgent"), v.literal("high"), v.literal("normal")), lastActivityAt: v.number(), unread: v.boolean(), space: v.optional(v.string()), @@ -118,7 +108,7 @@ export const claimDelivery = internalMutation({ const existing = await ctx.db .query("notificationDeliveries") .withIndex("by_org_id_and_idempotency_key", (q) => - q.eq("orgId", args.orgId).eq("idempotencyKey", args.idempotencyKey) + q.eq("orgId", args.orgId).eq("idempotencyKey", args.idempotencyKey), ) .unique(); @@ -186,7 +176,7 @@ export const recordDeliveryResult = internalMutation({ const existing = await ctx.db .query("notificationDeliveries") .withIndex("by_org_id_and_idempotency_key", (q) => - q.eq("orgId", args.orgId).eq("idempotencyKey", args.idempotencyKey) + q.eq("orgId", args.orgId).eq("idempotencyKey", args.idempotencyKey), ) .unique(); if (!existing || existing.status === "sent") return null; @@ -270,9 +260,7 @@ export const dispatch = internalAction({ if (claim.status !== "ready") { const retryable = claim.status === "in_progress"; const code = retryable ? "delivery_in_progress" : claim.code; - const error = retryable - ? "Another delivery attempt is still in progress." - : claim.message; + const error = retryable ? "Another delivery attempt is still in progress." : claim.message; logWarn("notification.deliveryDeferred", { kind: candidate.kind, code, @@ -293,13 +281,7 @@ export const dispatch = internalAction({ candidate, idempotencyKey: providerIdempotencyKey, }); - await recordResult( - ctx, - args.orgId, - providerIdempotencyKey, - attemptId, - result, - ); + await recordResult(ctx, args.orgId, providerIdempotencyKey, attemptId, result); if (!result.ok) { const log = result.retryable ? logWarn : logError; log("notification.providerFailed", { @@ -453,8 +435,7 @@ export async function sendResendEmail({ ok: false, statusCode: response.status, code, - message: - getStringField(body, "message") ?? `Resend returned HTTP ${response.status}.`, + message: getStringField(body, "message") ?? `Resend returned HTTP ${response.status}.`, retryable: isRetryableResendFailure(response.status, code), }; } catch (error) { @@ -511,14 +492,14 @@ export function renderNotificationEmail( appUrl: string, ): { subject: string; html: string; text: string } { const count = candidate.items.length + candidate.omittedCount; - const subject = candidate.kind === "immediate" - ? count === 1 - ? `urgent: ${candidate.items[0]?.title ?? "post needs attention"}` - : `${count} urgent posts need attention` - : `your Postwork digest: ${count} unread ${count === 1 ? "post" : "posts"}`; - const heading = candidate.kind === "immediate" - ? "urgent posts need attention" - : "your unread Postwork digest"; + const subject = + candidate.kind === "immediate" + ? count === 1 + ? `urgent: ${candidate.items[0]?.title ?? "post needs attention"}` + : `${count} urgent posts need attention` + : `your Postwork digest: ${count} unread ${count === 1 ? "post" : "posts"}`; + const heading = + candidate.kind === "immediate" ? "urgent posts need attention" : "your unread Postwork digest"; const rows = candidate.items.map((item) => { const url = safeItemUrl(item.url, item.postId, appUrl); const label = `${item.priority}: ${item.title}`; @@ -527,9 +508,10 @@ export function renderNotificationEmail( text: `- ${label}${item.space ? ` in ${item.space}` : ""}\n ${url}`, }; }); - const omitted = candidate.omittedCount > 0 - ? `${candidate.omittedCount} more unread ${candidate.omittedCount === 1 ? "post is" : "posts are"} waiting in Postwork.` - : ""; + const omitted = + candidate.omittedCount > 0 + ? `${candidate.omittedCount} more unread ${candidate.omittedCount === 1 ? "post is" : "posts are"} waiting in Postwork.` + : ""; const settings = `${appUrl.replace(/\/$/, "")}/`; return { @@ -549,7 +531,9 @@ export function renderNotificationEmail( "", `Open Postwork: ${settings}`, "Change outbound notifications in your Postwork profile.", - ].filter(Boolean).join("\n"), + ] + .filter(Boolean) + .join("\n"), }; } @@ -606,8 +590,11 @@ function parseHttpOrigin(value: string): string | null { const parsed = new URL(value); if (parsed.protocol !== "http:" && parsed.protocol !== "https:") return null; if ( - parsed.username || parsed.password || parsed.pathname !== "/" || - parsed.search || parsed.hash + parsed.username || + parsed.password || + parsed.pathname !== "/" || + parsed.search || + parsed.hash ) { return null; } @@ -618,13 +605,17 @@ function parseHttpOrigin(value: string): string | null { } function escapeHtml(value: string): string { - return value.replace(/[&<>"']/g, (character) => ({ - "&": "&", - "<": "<", - ">": ">", - '"': """, - "'": "'", - })[character] ?? character); + return value.replace( + /[&<>"']/g, + (character) => + ({ + "&": "&", + "<": "<", + ">": ">", + '"': """, + "'": "'", + })[character] ?? character, + ); } function getStringField(value: unknown, field: string): string | undefined { diff --git a/convex/notificationPreferences.test.ts b/convex/notificationPreferences.test.ts index a7664c0..9fd193e 100644 --- a/convex/notificationPreferences.test.ts +++ b/convex/notificationPreferences.test.ts @@ -11,9 +11,9 @@ describe("notification preferences API", () => { test("requires authentication", async () => { const t = convexTest(schema, modules); - await expect( - t.query(api.notificationPreferences.current, {}), - ).rejects.toThrow("Sign in to view notification preferences."); + await expect(t.query(api.notificationPreferences.current, {})).rejects.toThrow( + "Sign in to view notification preferences.", + ); }); test("returns safe defaults and upserts the current user's org-scoped row", async () => { @@ -44,9 +44,7 @@ describe("notification preferences API", () => { issuer: "https://issuer.example", }); - await expect( - authed.query(api.notificationPreferences.current, {}), - ).resolves.toMatchObject({ + await expect(authed.query(api.notificationPreferences.current, {})).resolves.toMatchObject({ browserEnabled: false, outboundEnabled: false, immediateUrgentEnabled: true, @@ -80,9 +78,7 @@ describe("notification preferences API", () => { quietHoursTimeZone: "Europe/Warsaw", }); - await expect( - authed.query(api.notificationPreferences.current, {}), - ).resolves.toMatchObject({ + await expect(authed.query(api.notificationPreferences.current, {})).resolves.toMatchObject({ browserEnabled: true, outboundEnabled: true, immediateUrgentEnabled: false, @@ -92,9 +88,7 @@ describe("notification preferences API", () => { const stored = await t.run(async (ctx) => ctx.db .query("notificationPreferences") - .withIndex("by_org_id_and_user_id", (q) => - q.eq("orgId", orgId).eq("userId", userId), - ) + .withIndex("by_org_id_and_user_id", (q) => q.eq("orgId", orgId).eq("userId", userId)) .unique(), ); expect(stored).toMatchObject({ @@ -145,8 +139,9 @@ describe("notification preferences API", () => { subject: "legacy-member", issuer: "https://issuer.example", }); - await expect( - authed.query(api.notificationPreferences.current, {}), - ).resolves.toMatchObject({ browserEnabled: false, isDefault: false }); + await expect(authed.query(api.notificationPreferences.current, {})).resolves.toMatchObject({ + browserEnabled: false, + isDefault: false, + }); }); }); diff --git a/convex/notificationPreferences.ts b/convex/notificationPreferences.ts index db445eb..8a2a909 100644 --- a/convex/notificationPreferences.ts +++ b/convex/notificationPreferences.ts @@ -31,20 +31,15 @@ export const current = query({ if (!identity) unauthenticated("Sign in to view notification preferences."); const viewer = await getViewerFromAuth(ctx); - if (!viewer) - unauthenticated("Finish account setup to view notification preferences."); + if (!viewer) unauthenticated("Finish account setup to view notification preferences."); if (viewer.status === "pending" || viewer.deactivatedAt) { - forbidden( - "An active account is required to view notification preferences.", - ); + forbidden("An active account is required to view notification preferences."); } const orgId = requireOrgId(viewer); const stored = await ctx.db .query("notificationPreferences") - .withIndex("by_org_id_and_user_id", (q) => - q.eq("orgId", orgId).eq("userId", viewer._id), - ) + .withIndex("by_org_id_and_user_id", (q) => q.eq("orgId", orgId).eq("userId", viewer._id)) .unique(); if (!stored) { @@ -69,34 +64,30 @@ export async function savePreferences( viewer: Awaited>, args: NotificationPreferences, ) { - const orgId = requireOrgId(viewer); - const preferences = validatePreferences(args); - const existing = await ctx.db - .query("notificationPreferences") - .withIndex("by_org_id_and_user_id", (q) => - q.eq("orgId", orgId).eq("userId", viewer._id), - ) - .unique(); - const now = Date.now(); + const orgId = requireOrgId(viewer); + const preferences = validatePreferences(args); + const existing = await ctx.db + .query("notificationPreferences") + .withIndex("by_org_id_and_user_id", (q) => q.eq("orgId", orgId).eq("userId", viewer._id)) + .unique(); + const now = Date.now(); - if (existing) { - await ctx.db.patch(existing._id, { ...preferences, updatedAt: now }); - } else { - await ctx.db.insert("notificationPreferences", { - orgId, - userId: viewer._id, - ...preferences, - createdAt: now, - updatedAt: now, - }); - } + if (existing) { + await ctx.db.patch(existing._id, { ...preferences, updatedAt: now }); + } else { + await ctx.db.insert("notificationPreferences", { + orgId, + userId: viewer._id, + ...preferences, + createdAt: now, + updatedAt: now, + }); + } - return { ...preferences, isDefault: false as const }; + return { ...preferences, isDefault: false as const }; } -function toPublicPreferences( - stored: Doc<"notificationPreferences">, -): NotificationPreferences { +function toPublicPreferences(stored: Doc<"notificationPreferences">): NotificationPreferences { return { browserEnabled: stored.browserEnabled ?? false, outboundEnabled: stored.outboundEnabled, @@ -109,19 +100,11 @@ function toPublicPreferences( }; } -function validatePreferences( - args: NotificationPreferences, -): NotificationPreferences { +function validatePreferences(args: NotificationPreferences): NotificationPreferences { validateTime(args.quietHoursStart, "quietHoursStart"); validateTime(args.quietHoursEnd, "quietHoursEnd"); - if ( - args.quietHoursEnabled && - args.quietHoursStart === args.quietHoursEnd - ) { - invalidPreference( - "quietHoursEnd", - "Quiet hours must have different start and end times.", - ); + if (args.quietHoursEnabled && args.quietHoursStart === args.quietHoursEnd) { + invalidPreference("quietHoursEnd", "Quiet hours must have different start and end times."); } validateTimeZone(args.quietHoursTimeZone); return args; diff --git a/convex/orgs.test.ts b/convex/orgs.test.ts index a106a0a..7bf18c9 100644 --- a/convex/orgs.test.ts +++ b/convex/orgs.test.ts @@ -27,7 +27,7 @@ describe("organization slugs", () => { expect(defaultOrgSlug(" Acme & Sons ")).toBe("acme-sons"); expect(orgSlugError("acme-2")).toBeNull(); for (const slug of ["ab", "-acme", "acme-", "Acme", "acme_team", "postwork", "staging"]) { - expect(orgSlugError(slug), slug).not.toBeNull(); + expect(orgSlugError(slug)).not.toBeNull(); } }); @@ -52,15 +52,17 @@ describe("organization slugs", () => { name: "Second Owner", }); await second.mutation(api.users.ensureViewer, {}); - await expect(second.mutation(api.orgs.create, { name: "Another Acme", slug: "acme" })) - .rejects.toThrow("already in use"); + await expect( + second.mutation(api.orgs.create, { name: "Another Acme", slug: "acme" }), + ).rejects.toThrow("already in use"); }); test("only an admin can backfill or change its workspace slug", async () => { const { owner } = await setup(); await owner.mutation(api.users.ensureViewer, {}); await owner.mutation(api.orgs.create, { name: "Acme", slug: "acme" }); - await expect(owner.mutation(api.orgs.setSlug, { slug: "acme-team" })) - .resolves.toEqual({ slug: "acme-team" }); + await expect(owner.mutation(api.orgs.setSlug, { slug: "acme-team" })).resolves.toEqual({ + slug: "acme-team", + }); }); }); diff --git a/convex/orgs.ts b/convex/orgs.ts index f1960a9..730bad1 100644 --- a/convex/orgs.ts +++ b/convex/orgs.ts @@ -1,14 +1,18 @@ import { ConvexError, v } from "convex/values"; import { mutation } from "./_generated/server"; -import { - ensureViewerUser, - forbidden, -} from "./authUsers"; +import { ensureViewerUser, forbidden } from "./authUsers"; import { logAudit } from "./admin"; import { logInfo } from "./lib/observability"; export const RESERVED_ORG_SLUGS = new Set([ - "www", "app", "api", "demo", "admin", "postwork", "beta", "staging", + "www", + "app", + "api", + "demo", + "admin", + "postwork", + "beta", + "staging", ]); export function defaultOrgSlug(name: string) { @@ -23,8 +27,10 @@ export function defaultOrgSlug(name: string) { } export function orgSlugError(value: string): string | null { - if (value.length < 3 || value.length > 32) return "Workspace slug must be between 3 and 32 characters."; - if (!/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(value)) return "Workspace slug may only contain lowercase letters, numbers, and internal hyphens."; + if (value.length < 3 || value.length > 32) + return "Workspace slug must be between 3 and 32 characters."; + if (!/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(value)) + return "Workspace slug may only contain lowercase letters, numbers, and internal hyphens."; if (RESERVED_ORG_SLUGS.has(value)) return "That workspace slug is reserved."; return null; } @@ -61,7 +67,12 @@ export const create = mutation({ const slug = args.slug === undefined ? defaultOrgSlug(name) : args.slug.trim(); const validationError = orgSlugError(slug); if (validationError) invalidInput(validationError); - if (await ctx.db.query("orgs").withIndex("by_slug", (q) => q.eq("slug", slug)).unique()) { + if ( + await ctx.db + .query("orgs") + .withIndex("by_slug", (q) => q.eq("slug", slug)) + .unique() + ) { invalidInput("That workspace slug is already in use."); } @@ -92,14 +103,26 @@ export const setSlug = mutation({ args: { slug: v.string() }, handler: async (ctx, args) => { const viewer = await ensureViewerUser(ctx); - if (!viewer.orgId || viewer.role !== "admin") forbidden("Only organization admins can change the workspace slug."); + if (!viewer.orgId || viewer.role !== "admin") + forbidden("Only organization admins can change the workspace slug."); const slug = args.slug.trim(); const validationError = orgSlugError(slug); if (validationError) invalidInput(validationError); - const existing = await ctx.db.query("orgs").withIndex("by_slug", (q) => q.eq("slug", slug)).unique(); - if (existing && existing._id !== viewer.orgId) invalidInput("That workspace slug is already in use."); + const existing = await ctx.db + .query("orgs") + .withIndex("by_slug", (q) => q.eq("slug", slug)) + .unique(); + if (existing && existing._id !== viewer.orgId) + invalidInput("That workspace slug is already in use."); await ctx.db.patch(viewer.orgId, { slug }); - await logAudit(ctx, { orgId: viewer.orgId, actorId: viewer._id, action: "org.slug_updated", targetType: "org", targetId: viewer.orgId, metadata: { slug } }); + await logAudit(ctx, { + orgId: viewer.orgId, + actorId: viewer._id, + action: "org.slug_updated", + targetType: "org", + targetId: viewer.orgId, + metadata: { slug }, + }); return { slug }; }, }); diff --git a/convex/posts.ts b/convex/posts.ts index bae4ab0..3425afc 100644 --- a/convex/posts.ts +++ b/convex/posts.ts @@ -60,9 +60,7 @@ async function enrich( ): Promise { const orgId = post.orgId; const author = publicUser(await ctx.db.get(post.authorId)); - const participants = ( - await Promise.all(post.participantIds.map((id) => ctx.db.get(id))) - ) + const participants = (await Promise.all(post.participantIds.map((id) => ctx.db.get(id)))) .filter((u): u is Doc<"users"> => u !== null) .map((u) => publicUser(u)); @@ -104,9 +102,7 @@ export const catchUpDigest = query({ // composer reports any eligible items omitted from its smaller UI payload. const posts = await ctx.db .query("posts") - .withIndex("by_org_id_and_last_activity_at", (q) => - q.eq("orgId", viewer.orgId), - ) + .withIndex("by_org_id_and_last_activity_at", (q) => q.eq("orgId", viewer.orgId)) .order("desc") .take(CATCH_UP_SCAN_LIMIT); @@ -115,9 +111,7 @@ export const catchUpDigest = query({ if (await canAccessPost(ctx, post, viewer._id)) allowed.push(post); } - const enriched = await Promise.all( - allowed.map((post) => enrich(ctx, post, viewer._id)), - ); + const enriched = await Promise.all(allowed.map((post) => enrich(ctx, post, viewer._id))); const digest = composeCatchUpDigest( enriched.map((post) => ({ @@ -220,9 +214,7 @@ export const feed = query({ return b.lastActivityAt - a.lastActivityAt; }); - let enriched = await Promise.all( - allowed.map((p) => enrich(ctx, p, viewer?._id)), - ); + let enriched = await Promise.all(allowed.map((p) => enrich(ctx, p, viewer?._id))); if (args.onlyUnread) enriched = enriched.filter((p) => p.unread); return enriched; }, @@ -246,17 +238,16 @@ export const feedPaginated = query({ const viewer = scope.viewer; const orgId = scope.orgId; - const result = await (args.space - ? ctx.db - .query("posts") - .withIndex("by_org_id_and_space_and_last_activity_at", (q) => - q.eq("orgId", orgId).eq("space", args.space!), - ) - : ctx.db - .query("posts") - .withIndex("by_org_id_and_last_activity_at", (q) => - q.eq("orgId", orgId), - ) + const result = await ( + args.space + ? ctx.db + .query("posts") + .withIndex("by_org_id_and_space_and_last_activity_at", (q) => + q.eq("orgId", orgId).eq("space", args.space!), + ) + : ctx.db + .query("posts") + .withIndex("by_org_id_and_last_activity_at", (q) => q.eq("orgId", orgId)) ) .order("desc") .paginate(args.paginationOpts); @@ -270,9 +261,7 @@ export const feedPaginated = query({ } } - const enriched = await Promise.all( - allowed.map((p) => enrich(ctx, p, viewer?._id)), - ); + const enriched = await Promise.all(allowed.map((p) => enrich(ctx, p, viewer?._id))); return { ...result, page: enriched }; }, }); @@ -302,9 +291,7 @@ export const counts = query({ .take(200) : await ctx.db .query("posts") - .withIndex("by_org_id_and_last_activity_at", (q) => - q.eq("orgId", orgId), - ) + .withIndex("by_org_id_and_last_activity_at", (q) => q.eq("orgId", orgId)) .order("desc") .take(200); @@ -337,15 +324,11 @@ export const search = query({ const [byBody, byTitle] = await Promise.all([ ctx.db .query("posts") - .withSearchIndex("search_body", (q) => - q.search("body", term).eq("orgId", orgId), - ) + .withSearchIndex("search_body", (q) => q.search("body", term).eq("orgId", orgId)) .take(40), ctx.db .query("posts") - .withSearchIndex("search_title", (q) => - q.search("title", term).eq("orgId", orgId), - ) + .withSearchIndex("search_title", (q) => q.search("title", term).eq("orgId", orgId)) .take(40), ]); @@ -625,9 +608,7 @@ export const remove = mutation({ // so we scan by orgId and filter by postId in-memory. const allReads = await ctx.db .query("postReads") - .withIndex("by_org_id_and_user_id_and_post_id", (q) => - q.eq("orgId", orgId), - ) + .withIndex("by_org_id_and_user_id_and_post_id", (q) => q.eq("orgId", orgId)) .take(1000); for (const read of allReads) { if (read.postId === args.postId) await ctx.db.delete(read._id); @@ -636,9 +617,7 @@ export const remove = mutation({ // Delete all attachments for this post. const attachments = await ctx.db .query("postAttachments") - .withIndex("by_org_id_and_post_id", (q) => - q.eq("orgId", orgId).eq("postId", args.postId), - ) + .withIndex("by_org_id_and_post_id", (q) => q.eq("orgId", orgId).eq("postId", args.postId)) .take(100); for (const att of attachments) { await ctx.storage.delete(att.storageId); diff --git a/convex/replies.ts b/convex/replies.ts index 1cd664f..2327e3c 100644 --- a/convex/replies.ts +++ b/convex/replies.ts @@ -13,12 +13,7 @@ import { } from "./authUsers"; import { publicUser, type PublicUser } from "./users"; import { rateLimiter } from "./lib/rateLimit"; -import { - parse, - replyBodySchema, - attachmentInputSchema, - LIMITS, -} from "./lib/validation"; +import { parse, replyBodySchema, attachmentInputSchema, LIMITS } from "./lib/validation"; import { logInfo } from "./lib/observability"; import { validateStoredAttachment } from "./lib/attachmentStorage"; @@ -167,11 +162,7 @@ export const listForPostPaginated = query({ } const viewer = scope.viewer; const post = await ctx.db.get(args.postId); - if ( - !post || - post.orgId !== scope.orgId || - !(await canAccessPost(ctx, post, viewer?._id)) - ) { + if (!post || post.orgId !== scope.orgId || !(await canAccessPost(ctx, post, viewer?._id))) { return { page: [], isDone: true, continueCursor: "" }; } @@ -374,9 +365,7 @@ export const remove = mutation({ for (const id of toDelete) { const atts = await ctx.db .query("postAttachments") - .withIndex("by_org_id_and_reply_id", (q) => - q.eq("orgId", orgId).eq("replyId", id), - ) + .withIndex("by_org_id_and_reply_id", (q) => q.eq("orgId", orgId).eq("replyId", id)) .take(20); for (const att of atts) { await ctx.storage.delete(att.storageId); diff --git a/convex/schema.ts b/convex/schema.ts index 62821e1..7da8a51 100644 --- a/convex/schema.ts +++ b/convex/schema.ts @@ -9,11 +9,7 @@ import { v } from "convex/values"; * replies → nested replies (self-referential via `parentId`) * postReads → per-user read state, drives unread badges */ -export const priority = v.union( - v.literal("urgent"), - v.literal("high"), - v.literal("normal"), -); +export const priority = v.union(v.literal("urgent"), v.literal("high"), v.literal("normal")); export const agentTaskStatus = v.union( v.literal("queued"), @@ -23,20 +19,11 @@ export const agentTaskStatus = v.union( v.literal("cancelled"), ); -export const connectorCapability = v.union( - v.literal("agentTasks"), - v.literal("inboundEvents"), -); +export const connectorCapability = v.union(v.literal("agentTasks"), v.literal("inboundEvents")); -export const connectorAuthStrategy = v.union( - v.literal("bearer"), - v.literal("providerSignature"), -); +export const connectorAuthStrategy = v.union(v.literal("bearer"), v.literal("providerSignature")); -export const aiGenerationKind = v.union( - v.literal("postSummary"), - v.literal("agentTask"), -); +export const aiGenerationKind = v.union(v.literal("postSummary"), v.literal("agentTask")); export default defineSchema({ orgs: defineTable({ @@ -52,9 +39,7 @@ export default defineSchema({ title: v.string(), avatarColor: v.string(), initials: v.string(), - role: v.optional( - v.union(v.literal("admin"), v.literal("tester"), v.literal("member")), - ), + role: v.optional(v.union(v.literal("admin"), v.literal("tester"), v.literal("member"))), status: v.optional(v.union(v.literal("pending"), v.literal("active"))), profileCompletedAt: v.optional(v.number()), avatarStorageId: v.optional(v.id("_storage")), @@ -132,7 +117,11 @@ export default defineSchema({ .index("by_org_id_and_last_activity_at", ["orgId", "lastActivityAt"]) .index("by_org_id_and_space_and_last_activity_at", ["orgId", "space", "lastActivityAt"]) .index("by_org_id_and_space_id_and_last_activity_at", ["orgId", "spaceId", "lastActivityAt"]) - .index("by_org_id_and_wall_owner_id_and_last_activity_at", ["orgId", "wallOwnerId", "lastActivityAt"]) + .index("by_org_id_and_wall_owner_id_and_last_activity_at", [ + "orgId", + "wallOwnerId", + "lastActivityAt", + ]) .index("by_org_id_and_experiment_slug", ["orgId", "experimentSlug"]) .searchIndex("search_body", { searchField: "body", @@ -360,11 +349,7 @@ export default defineSchema({ email: v.string(), name: v.optional(v.string()), message: v.optional(v.string()), - status: v.union( - v.literal("pending"), - v.literal("approved"), - v.literal("denied"), - ), + status: v.union(v.literal("pending"), v.literal("approved"), v.literal("denied")), createdAt: v.number(), resolvedBy: v.optional(v.id("users")), resolvedAt: v.optional(v.number()), diff --git a/convex/seed.ts b/convex/seed.ts index 2c344a1..1c73324 100644 --- a/convex/seed.ts +++ b/convex/seed.ts @@ -23,13 +23,18 @@ export const run = internalMutation({ throw new Error("Demo seed requires DEMO=true on the Convex deployment."); } - const existingDemoOrg = await ctx.db.query("orgs").withIndex("by_slug", (q) => q.eq("slug", DEMO_ORG_SLUG)).unique(); + const existingDemoOrg = await ctx.db + .query("orgs") + .withIndex("by_slug", (q) => q.eq("slug", DEMO_ORG_SLUG)) + .unique(); const now = Date.now(); - const orgId = existingDemoOrg?._id ?? await ctx.db.insert("orgs", { - name: DEMO_ORG_NAME, - slug: DEMO_ORG_SLUG, - createdAt: now, - }); + const orgId = + existingDemoOrg?._id ?? + (await ctx.db.insert("orgs", { + name: DEMO_ORG_NAME, + slug: DEMO_ORG_SLUG, + createdAt: now, + })); // Reset only demo-owned rows. The order removes dependants before parents, // and deliberately preserves the demo org document and every product row. @@ -123,8 +128,7 @@ export const run = internalMutation({ key: "nw_globex_support", name: "northwind × globex — vendor support", slug: "northwind-globex-vendor-support", - description: - "Operational support lane for incidents, renewals, and account coordination.", + description: "Operational support lane for incidents, renewals, and account coordination.", createdAgo: 24 * 8 * HOUR, members: ["MC", "AK", "LW", "Cu"], }, @@ -139,8 +143,10 @@ export const run = internalMutation({ }, ] as const; - const spaceIds: Record<(typeof spaceDefs)[number]["key"], Id<"spaces">> = - {} as Record<(typeof spaceDefs)[number]["key"], Id<"spaces">>; + const spaceIds: Record<(typeof spaceDefs)[number]["key"], Id<"spaces">> = {} as Record< + (typeof spaceDefs)[number]["key"], + Id<"spaces"> + >; for (const space of spaceDefs) { const spaceId = await ctx.db.insert("spaces", { diff --git a/convex/spaces.test.ts b/convex/spaces.test.ts index 8fd760a..4ab39f8 100644 --- a/convex/spaces.test.ts +++ b/convex/spaces.test.ts @@ -80,9 +80,7 @@ describe("space creation", () => { const state = await t.run(async (ctx) => ({ users: await ctx.db .query("users") - .withIndex("by_token_identifier", (q) => - q.eq("tokenIdentifier", tokenIdentifier), - ) + .withIndex("by_token_identifier", (q) => q.eq("tokenIdentifier", tokenIdentifier)) .collect(), space: await ctx.db.get(created.spaceId), membership: await ctx.db @@ -108,12 +106,14 @@ describe("space creation", () => { description: "Coordinate the launch.", }); - await expect( - authed.mutation(api.spaces.create, { name: "Another space" }), - ).rejects.toThrow("Your role can create up to 1 space."); - await expect( - authed.query(api.spaces.creationStatus, {}), - ).resolves.toEqual({ limit: 1, createdCount: 1, canCreate: false }); + await expect(authed.mutation(api.spaces.create, { name: "Another space" })).rejects.toThrow( + "Your role can create up to 1 space.", + ); + await expect(authed.query(api.spaces.creationStatus, {})).resolves.toEqual({ + limit: 1, + createdCount: 1, + canCreate: false, + }); const membership = await t.run(async (ctx) => ctx.db @@ -133,12 +133,14 @@ describe("space creation", () => { await authed.mutation(api.spaces.create, { name: `Tester space ${index}` }); } - await expect( - authed.mutation(api.spaces.create, { name: "Tester space 4" }), - ).rejects.toThrow("Your role can create up to 3 spaces."); - await expect( - authed.query(api.spaces.creationStatus, {}), - ).resolves.toEqual({ limit: 3, createdCount: 3, canCreate: false }); + await expect(authed.mutation(api.spaces.create, { name: "Tester space 4" })).rejects.toThrow( + "Your role can create up to 3 spaces.", + ); + await expect(authed.query(api.spaces.creationStatus, {})).resolves.toEqual({ + limit: 3, + createdCount: 3, + canCreate: false, + }); }); test("lets admins create unlimited spaces and generates unique slugs", async () => { @@ -152,8 +154,10 @@ describe("space creation", () => { expect(first.slug).toBe("roadmap"); expect(second.slug).toBe("roadmap-2"); - await expect( - authed.query(api.spaces.creationStatus, {}), - ).resolves.toEqual({ limit: null, createdCount: 0, canCreate: true }); + await expect(authed.query(api.spaces.creationStatus, {})).resolves.toEqual({ + limit: null, + createdCount: 0, + canCreate: true, + }); }); }); diff --git a/convex/spaces.ts b/convex/spaces.ts index e41bbbc..8a0d2a5 100644 --- a/convex/spaces.ts +++ b/convex/spaces.ts @@ -29,9 +29,7 @@ async function countCreatedSpaces( return ( await ctx.db .query("spaces") - .withIndex("by_org_id_and_created_by", (q) => - q.eq("orgId", orgId).eq("createdBy", userId), - ) + .withIndex("by_org_id_and_created_by", (q) => q.eq("orgId", orgId).eq("createdBy", userId)) .take(limit) ).length; } @@ -118,9 +116,7 @@ export const create = mutation({ while ( await ctx.db .query("spaces") - .withIndex("by_org_id_and_slug", (q) => - q.eq("orgId", orgId).eq("slug", slug), - ) + .withIndex("by_org_id_and_slug", (q) => q.eq("orgId", orgId).eq("slug", slug)) .unique() ) { slug = `${slugBase}-${suffix}`; @@ -169,9 +165,7 @@ export const list = query({ visible.map(async (space) => { const memberships = await ctx.db .query("spaceMemberships") - .withIndex("by_org_id_and_space_id", (q) => - q.eq("orgId", orgId).eq("spaceId", space._id), - ) + .withIndex("by_org_id_and_space_id", (q) => q.eq("orgId", orgId).eq("spaceId", space._id)) .collect(); const posts = await ctx.db .query("posts") @@ -201,18 +195,14 @@ export const getBySlug = query({ const orgId = scope.orgId; const space = await ctx.db .query("spaces") - .withIndex("by_org_id_and_slug", (q) => - q.eq("orgId", orgId).eq("slug", args.slug), - ) + .withIndex("by_org_id_and_slug", (q) => q.eq("orgId", orgId).eq("slug", args.slug)) .unique(); if (!space) return null; if (!(await canAccessSpace(ctx, space._id, viewer?._id))) return null; const memberships = await ctx.db .query("spaceMemberships") - .withIndex("by_org_id_and_space_id", (q) => - q.eq("orgId", orgId).eq("spaceId", space._id), - ) + .withIndex("by_org_id_and_space_id", (q) => q.eq("orgId", orgId).eq("spaceId", space._id)) .collect(); return { @@ -234,9 +224,7 @@ export const membershipsForSpace = query({ const memberships = await ctx.db .query("spaceMemberships") - .withIndex("by_org_id_and_space_id", (q) => - q.eq("orgId", orgId).eq("spaceId", args.spaceId), - ) + .withIndex("by_org_id_and_space_id", (q) => q.eq("orgId", orgId).eq("spaceId", args.spaceId)) .collect(); return await Promise.all( diff --git a/convex/tenantIsolation.test.ts b/convex/tenantIsolation.test.ts index ce601c7..d64e8f8 100644 --- a/convex/tenantIsolation.test.ts +++ b/convex/tenantIsolation.test.ts @@ -125,12 +125,8 @@ describe("shared deployment tenant isolation", () => { await ctx.db.patch(state.productUserId, { status: "pending" }); }); - await expect( - state.authed.query(api.users.list, {}), - ).resolves.toEqual([]); - await expect( - state.authed.query(api.posts.feed, {}), - ).resolves.toEqual([]); + await expect(state.authed.query(api.users.list, {})).resolves.toEqual([]); + await expect(state.authed.query(api.posts.feed, {})).resolves.toEqual([]); }); test("demo seed preserves product rows", async () => { diff --git a/convex/users.ts b/convex/users.ts index 54b2cc3..844d18d 100644 --- a/convex/users.ts +++ b/convex/users.ts @@ -22,11 +22,13 @@ import { preferenceArgs, savePreferences } from "./notificationPreferences"; export type PublicUser = Omit, "tokenIdentifier" | "subject">; -const avatarActionValidator = v.optional(v.union( - v.object({ type: v.literal("upload"), storageId: v.id("_storage") }), - v.object({ type: v.literal("remove") }), - v.object({ type: v.literal("useProvider") }), -)); +const avatarActionValidator = v.optional( + v.union( + v.object({ type: v.literal("upload"), storageId: v.id("_storage") }), + v.object({ type: v.literal("remove") }), + v.object({ type: v.literal("useProvider") }), + ), +); type ProfileUpdateArgs = { name: string; @@ -39,11 +41,7 @@ export function publicUser(user: Doc<"users">): PublicUser; export function publicUser(user: Doc<"users"> | null): PublicUser | null; export function publicUser(user: Doc<"users"> | null): PublicUser | null { if (!user) return null; - const { - tokenIdentifier: _tokenIdentifier, - subject: _subject, - ...rest - } = user; + const { tokenIdentifier: _tokenIdentifier, subject: _subject, ...rest } = user; return rest; } @@ -94,7 +92,7 @@ export const me = query({ return { user: publicUser(user), org: org ? { name: org.name, slug: org.slug } : null, - status: user.status ?? "active" as const, + status: user.status ?? ("active" as const), needsProfileSetup: user.profileCompletedAt === undefined && user.tokenIdentifier !== undefined, needsOrg: user.orgId === undefined, @@ -232,9 +230,8 @@ async function updateProfileFields( }); const name = parse(profileNameSchema, args.name, "name"); - const title = args.title === undefined - ? undefined - : parse(profileTitleSchema, args.title, "title"); + const title = + args.title === undefined ? undefined : parse(profileTitleSchema, args.title, "title"); const initials = parse(profileInitialsSchema, args.initials, "initials"); const avatarPatch = await applyAvatarAction(ctx, user, args.avatar); await ctx.db.patch(user._id, { @@ -249,11 +246,7 @@ async function updateProfileFields( export const setRole = mutation({ args: { userId: v.id("users"), - role: v.union( - v.literal("admin"), - v.literal("tester"), - v.literal("member"), - ), + role: v.union(v.literal("admin"), v.literal("tester"), v.literal("member")), }, handler: async (ctx, args) => { const viewer = await ensureActiveViewerUser(ctx); diff --git a/convex/xSync.ts b/convex/xSync.ts index d953e32..2420719 100644 --- a/convex/xSync.ts +++ b/convex/xSync.ts @@ -37,16 +37,11 @@ type RunResult = | { skipped: false; results: SyncResult[] } | { skipped: false; handle: string; created: number; duplicates: number }; -async function syncHandle( - ctx: ActionCtx, - handle: string, - connectorId: Id<"connectors">, -) { +async function syncHandle(ctx: ActionCtx, handle: string, connectorId: Id<"connectors">) { const base = process.env.X_SYNC_API_BASE?.trim() || DEFAULT_API_BASE; - const response = await fetch( - `${base}/${encodeURIComponent(handle)}?limit=${MAX_POSTS_PER_RUN}`, - { headers: { accept: "application/json" } }, - ); + const response = await fetch(`${base}/${encodeURIComponent(handle)}?limit=${MAX_POSTS_PER_RUN}`, { + headers: { accept: "application/json" }, + }); if (!response.ok) { throw new Error(`X proxy responded ${response.status} for @${handle}.`); } @@ -68,13 +63,10 @@ async function syncHandle( let created = 0; let duplicates = 0; for (const post of candidates) { - const receipt = await ctx.runMutation( - internal.connectors.recordXCrossPostFromSync, - { - connectorId, - tweet: { id: post.id, handle, text: post.text, url: post.url }, - }, - ); + const receipt = await ctx.runMutation(internal.connectors.recordXCrossPostFromSync, { + connectorId, + tweet: { id: post.id, handle, text: post.text, url: post.url }, + }); if (receipt.duplicate) duplicates++; else created++; } @@ -113,11 +105,10 @@ export const run = internalAction({ } } - const handle = (args.handle ?? process.env.X_SYNC_HANDLE) - ?.trim() - .replace(/^@/, ""); - let connectorId = (args.connectorId ?? - process.env.X_SYNC_CONNECTOR_ID) as Id<"connectors"> | undefined; + const handle = (args.handle ?? process.env.X_SYNC_HANDLE)?.trim().replace(/^@/, ""); + let connectorId = (args.connectorId ?? process.env.X_SYNC_CONNECTOR_ID) as + | Id<"connectors"> + | undefined; if (!connectorId) { const found = await ctx.runQuery(internal.connectors.findXSyncConnector, {}); connectorId = found?.connectorId; @@ -126,7 +117,7 @@ export const run = internalAction({ return { skipped: true as const, reason: - "Set X_SYNC_HANDLE and provision an inboundEvents connector with slug \"x\" (or set X_SYNC_CONNECTOR_ID).", + 'Set X_SYNC_HANDLE and provision an inboundEvents connector with slug "x" (or set X_SYNC_CONNECTOR_ID).', }; } diff --git a/package.json b/package.json index 999ed56..bfcb00d 100644 --- a/package.json +++ b/package.json @@ -8,6 +8,10 @@ "dev:web": "vite", "dev:convex": "convex dev", "build": "tsc -b && vite build", + "lint": "oxlint src convex scripts tests vite.config.ts vitest.config.ts", + "lint:fix": "oxlint --fix src convex scripts tests vite.config.ts vitest.config.ts", + "format": "oxfmt src convex scripts tests vite.config.ts vitest.config.ts", + "format:check": "oxfmt --check src convex scripts tests vite.config.ts vitest.config.ts", "test": "bun run test:unit && bun run test:convex", "test:unit": "bun test tests src convex/lib", "test:convex": "vitest run convex/*.test.ts", @@ -36,25 +40,31 @@ "@tanstack/react-router": "^1.170.16", "ai": "7.0.0-beta.184", "convex": "^1.41.0", - "react": "^19.2.7", - "react-dom": "^19.2.7", + "react": "19.2.8", + "react-dom": "19.2.8", "shiki": "^4.2.0", "zod": "^4.4.3" }, "devDependencies": { + "@babel/core": "^8.0.1", "@edge-runtime/vm": "^5.0.0", + "@rolldown/plugin-babel": "^0.2.3", "@tailwindcss/vite": "^4.3.1", + "@types/babel__core": "^7.20.5", "@types/bun": "^1.3.14", "@types/node": "^26.0.0", "@types/react": "^19.2.17", "@types/react-dom": "^19.2.3", - "@vitejs/plugin-react": "^6.0.3", + "@vitejs/plugin-react": "6.0.5", + "babel-plugin-react-compiler": "1.0.0", "convex-test": "^0.0.54", "npm-run-all": "^4.1.5", + "oxfmt": "0.62.0", + "oxlint": "1.77.0", "react-scan": "^0.5.7", "tailwindcss": "^4.3.1", "typescript": "^6.0.3", - "vite": "^8.1.0", + "vite": "8.2.0", "vitest": "^4.1.10" } } diff --git a/scripts/agent-task-runner.ts b/scripts/agent-task-runner.ts index 83db119..15c106f 100644 --- a/scripts/agent-task-runner.ts +++ b/scripts/agent-task-runner.ts @@ -69,9 +69,7 @@ function outputLimit(value: string | undefined): number { "POSTWORK_AGENT_MAX_OUTPUT_BYTES", ); if (parsed > DEFAULT_MAX_OUTPUT_BYTES) { - throw new Error( - `POSTWORK_AGENT_MAX_OUTPUT_BYTES must be at most ${DEFAULT_MAX_OUTPUT_BYTES}.`, - ); + throw new Error(`POSTWORK_AGENT_MAX_OUTPUT_BYTES must be at most ${DEFAULT_MAX_OUTPUT_BYTES}.`); } return parsed; } @@ -83,7 +81,8 @@ function configuredCommand(args: string[], value: string | undefined): string[] if (command.length === 0) throw new Error("A command must follow --."); return command; } - if (!value) throw new Error("Pass the coding-agent command after -- or set POSTWORK_AGENT_COMMAND_JSON."); + if (!value) + throw new Error("Pass the coding-agent command after -- or set POSTWORK_AGENT_COMMAND_JSON."); const parsed: unknown = JSON.parse(value); if ( !Array.isArray(parsed) || @@ -162,9 +161,12 @@ function isClaim(value: unknown): value is AgentTaskClaim { export function formatAgentInput(claim: AgentTaskClaim): string { const replies = claim.replies.length - ? claim.replies.map((reply) => - `- reply ${reply.id} by ${reply.authorId}${reply.parentId ? ` to ${reply.parentId}` : ""}: ${reply.body}` - ).join("\n") + ? claim.replies + .map( + (reply) => + `- reply ${reply.id} by ${reply.authorId}${reply.parentId ? ` to ${reply.parentId}` : ""}: ${reply.body}`, + ) + .join("\n") : "(no replies)"; const truncation = claim.repliesTruncated ? "\nThe reply window was truncated. Base the response on the available context." @@ -370,9 +372,10 @@ export async function runAgentTask( } : { status: "failed", - error: output.length === 0 && commandResult.exitCode === 0 && !commandResult.timedOut - ? "Coding-agent command returned no output." - : boundedFailure(commandResult, timeoutMs), + error: + output.length === 0 && commandResult.exitCode === 0 && !commandResult.timedOut + ? "Coding-agent command returned no output." + : boundedFailure(commandResult, timeoutMs), }; } catch (error) { outcome = { status: "failed", error: launchFailure(error) }; diff --git a/scripts/linear-fix-states.mjs b/scripts/linear-fix-states.mjs index 2e688ff..3cd03c4 100644 --- a/scripts/linear-fix-states.mjs +++ b/scripts/linear-fix-states.mjs @@ -76,7 +76,10 @@ function flattenTree(nodes, output = []) { } async function loadBuildTree() { - const setupSource = await readFile(new URL("./linear-setup-postwork.mjs", import.meta.url), "utf8"); + const setupSource = await readFile( + new URL("./linear-setup-postwork.mjs", import.meta.url), + "utf8", + ); const start = setupSource.indexOf("function buildTree(labels, milestones) {"); const end = setupSource.indexOf("\nconst STATE_MAP =", start); if (start < 0 || end < 0) throw new Error("Could not extract buildTree from setup script"); @@ -90,7 +93,9 @@ async function main() { const expectedByTitle = new Map(expected.map((item) => [item.title, item.status])); if (expectedByTitle.size !== expected.length) { - throw new Error(`Issue tree contains ${expected.length - expectedByTitle.size} duplicate title(s)`); + throw new Error( + `Issue tree contains ${expected.length - expectedByTitle.size} duplicate title(s)`, + ); } const data = await gql( @@ -146,7 +151,10 @@ async function main() { console.log(`Already correct: ${alreadyCorrect}`); console.log(`Unmatched: ${unmatched.length}`); console.log(`Errors: ${errors.length}`); - if (unmatched.length) console.log(`Unmatched issues: ${unmatched.map((issue) => `${issue.identifier} (${issue.title})`).join(", ")}`); + if (unmatched.length) + console.log( + `Unmatched issues: ${unmatched.map((issue) => `${issue.identifier} (${issue.title})`).join(", ")}`, + ); if (errors.length) console.error(`Errors:\n${errors.join("\n")}`); if (errors.length) process.exitCode = 1; } diff --git a/scripts/linear-pull.mjs b/scripts/linear-pull.mjs index 678e244..63b5c2a 100644 --- a/scripts/linear-pull.mjs +++ b/scripts/linear-pull.mjs @@ -21,9 +21,7 @@ if (!KEY) { console.error( "LINEAR_API_KEY is required. Get one at https://linear.app/settings/account/security", ); - console.error( - "Store it in .env as LINEAR_API_KEY=lin_api_... (the file is gitignored).", - ); + console.error("Store it in .env as LINEAR_API_KEY=lin_api_... (the file is gitignored)."); process.exit(1); } @@ -144,7 +142,6 @@ async function main() { const allLabels = [...labelMap.values()].sort((a, b) => a.name.localeCompare(b.name)); // Build issue lookup for dependency resolution - const issueMap = new Map(issues.map((i) => [i.id, i])); // Build children map from parent field const childrenMap = new Map(); // parentId -> [issue, ...] @@ -183,9 +180,7 @@ async function main() { priority: i.priorityLabel, assignee: i.assignee?.name, milestone: i.projectMilestone?.name, - parent: i.parent - ? `${i.parent.identifier} — ${i.parent.title}` - : null, + parent: i.parent ? `${i.parent.identifier} — ${i.parent.title}` : null, children: (childrenMap.get(i.id) || []).map((c) => ({ identifier: c.identifier, title: c.title, @@ -216,7 +211,9 @@ async function main() { lines.push(`> Pulled from [Linear](${project.url}) on ${new Date().toISOString().split("T")[0]}`); lines.push(`> Live source of truth: ${project.url}`); lines.push(""); - lines.push(`**State:** ${project.state} | **Target:** ${project.targetDate || "unset"} | **Issues:** ${issues.length}`); + lines.push( + `**State:** ${project.state} | **Target:** ${project.targetDate || "unset"} | **Issues:** ${issues.length}`, + ); lines.push(""); // Summary counts @@ -252,9 +249,7 @@ async function main() { // Issues grouped by milestone const filterMs = MILESTONE_FILTER - ? milestones.find((m) => - m.name.toLowerCase().includes(MILESTONE_FILTER.toLowerCase()), - ) + ? milestones.find((m) => m.name.toLowerCase().includes(MILESTONE_FILTER.toLowerCase())) : null; if (filterMs) { @@ -264,7 +259,7 @@ async function main() { .filter((i) => i.projectMilestone?.id === filterMs.id) .sort((a, b) => a.identifier.localeCompare(b.identifier)); for (const issue of msIssues.filter((i) => !i.parent)) { - lines.push(formatIssue(issue, issueMap, childrenMap, 0, issues)); + lines.push(formatIssue(issue, childrenMap, 0)); } } else { for (const m of milestones) { @@ -276,7 +271,7 @@ async function main() { lines.push(""); const roots = msIssues.filter((i) => !i.parent); for (const issue of roots) { - lines.push(formatIssue(issue, issueMap, childrenMap, 0, issues)); + lines.push(formatIssue(issue, childrenMap, 0)); } lines.push(""); } @@ -290,7 +285,7 @@ async function main() { lines.push(""); const roots = noMs.filter((i) => !i.parent); for (const issue of roots) { - lines.push(formatIssue(issue, issueMap, childrenMap, 0, issues)); + lines.push(formatIssue(issue, childrenMap, 0)); } lines.push(""); } @@ -307,7 +302,7 @@ async function main() { console.log(lines.join("\n")); } -function formatIssue(issue, issueMap, childrenMap, depth, allIssues) { +function formatIssue(issue, childrenMap, depth) { const indent = " ".repeat(depth); const stateBadge = issue.state?.name || "unknown"; const priority = @@ -315,9 +310,7 @@ function formatIssue(issue, issueMap, childrenMap, depth, allIssues) { ? ` **[${issue.priorityLabel}]**` : ""; const labels = - issue.labels.nodes.length > 0 - ? ` *${issue.labels.nodes.map((l) => l.name).join(", ")}*` - : ""; + issue.labels.nodes.length > 0 ? ` *${issue.labels.nodes.map((l) => l.name).join(", ")}*` : ""; const assignee = issue.assignee ? ` @${issue.assignee.name}` : ""; let line = `${indent}- [${issue.identifier}](${issue.url}) [${stateBadge}]${priority}${labels}${assignee} — ${issue.title}`; @@ -326,7 +319,7 @@ function formatIssue(issue, issueMap, childrenMap, depth, allIssues) { if (children?.length > 0) { const childLines = children .sort((a, b) => a.identifier.localeCompare(b.identifier)) - .map((child) => formatIssue(child, issueMap, childrenMap, depth + 1, allIssues)) + .map((child) => formatIssue(child, childrenMap, depth + 1)) .join("\n"); return line + "\n" + childLines; } diff --git a/scripts/linear-setup-postwork.mjs b/scripts/linear-setup-postwork.mjs index 3ee9855..882d2af 100644 --- a/scripts/linear-setup-postwork.mjs +++ b/scripts/linear-setup-postwork.mjs @@ -62,7 +62,7 @@ function sleep(ms) { async function ensureLabel(name, color, teamId = TEAM_ID) { // Check team labels - const data = await gql( + await gql( `query($teamId: String!) { team(id: $teamId) { labels(filter: { name: { eq: $name } }) { @@ -116,7 +116,7 @@ async function createProject() { }`); if (existing.projects.nodes.length) { const p = existing.projects.nodes[0]; - const count = await gql( + await gql( `{ issues(filter: { project: { id: { eq: "${p.id}" } } }, first: 1) { nodes { id } } }`, ).catch(() => null); console.error( @@ -268,9 +268,7 @@ async function createIssue({ { input }, ); const issue = data.issueCreate.issue; - process.stdout.write( - ` ${issue.identifier} [${issue.state.name}] ${issue.title}\n`, - ); + process.stdout.write(` ${issue.identifier} [${issue.state.name}] ${issue.title}\n`); await sleep(120); // gentle rate limit return issue; } catch (e) { @@ -319,7 +317,8 @@ Default public demo to demo=true so later work lands on the correct side of the children: [ { title: "Add src/lib/demoMode.ts helper", - description: "Single frontend module; never scatter raw `import.meta.env.VITE_DEMO` reads.", + description: + "Single frontend module; never scatter raw `import.meta.env.VITE_DEMO` reads.", status: "done", labels: [L.phase0, L.Frontend], }, @@ -482,7 +481,8 @@ store.tsx becomes thin interface with two implementations selected by demo mode. }, { title: "Demo path: keep session overlay as-is", - description: "Visitor writes vanish on refresh; backend stays read-only for visitors.", + description: + "Visitor writes vanish on refresh; backend stays read-only for visitors.", status: "done", labels: [L.phase1, L.Frontend], }, @@ -577,7 +577,8 @@ Cannot use product without redeeming invite. After activation, blocking profile }, { title: "Provider photo import + remove/useProvider actions", - description: "Avatar amendment 2026-07-08: default provider pfp; user can remove to initials.", + description: + "Avatar amendment 2026-07-08: default provider pfp; user can remove to initials.", status: "done", labels: [L.phase1, L.Frontend, L.Backend], }, @@ -628,7 +629,8 @@ Open: true multi-org product flow (see \`docs/organizations.md\`).`, children: [ { title: "orgs table + orgId columns + indexes", - description: "posts, spaces, users, postReads carry orgId; org-prefixed indexes including search filterFields.", + description: + "posts, spaces, users, postReads carry orgId; org-prefixed indexes including search filterFields.", status: "done", labels: [L.phase2, L.Backend], }, @@ -683,7 +685,8 @@ Non-goals for first milestone: billing, org branding, cross-org. First milestone }, { title: "Org switcher / org-scoped routing", - description: "Either /o/$orgSlug/app/... or session-selected org. Needed only when multi-membership exists.", + description: + "Either /o/$orgSlug/app/... or session-selected org. Needed only when multi-membership exists.", status: "backlog", labels: [L.phase2, L.Frontend], }, @@ -730,7 +733,8 @@ Open: external error reporting, priority-aware outbound notifications, deployed }, { title: "Image attachments via Convex storage (product mode)", - description: "Decision Q5=b: images only in v1. Paste/drop screenshots; size limits. Generic files deferred. Disabled in demo (overlay can't hold files).", + description: + "Decision Q5=b: images only in v1. Paste/drop screenshots; size limits. Generic files deferred. Disabled in demo (overlay can't hold files).", status: "done", labels: [L.phase3, L.Feature, L.Frontend, L.Backend], children: [ @@ -925,7 +929,8 @@ Areas: convex/ai.ts, posts.ts, replies.ts, AgentSummary.tsx. children: [ { title: "Compute isStale from lastActivityAt vs summaryUpdatedAt", - description: "Backend projection or client derive; include in post feed/detail payloads.", + description: + "Backend projection or client derive; include in post feed/detail payloads.", status: "todo", priority: 1, labels: [L.phase4, L.Backend], @@ -940,7 +945,8 @@ Areas: convex/ai.ts, posts.ts, replies.ts, AgentSummary.tsx. }, { title: "Manual regenerate path remains clear + rate-limited", - description: "Generate/Regenerate button; friendly configure-provider message without key.", + description: + "Generate/Regenerate button; friendly configure-provider message without key.", status: "todo", labels: [L.phase4, L.Frontend, L.Backend], }, @@ -1063,7 +1069,7 @@ Open: banner, reseed cadence, feature-flag/lab policy. { title: "Quiet public-demo banner", description: - "On-brand: \"public demo — data resets, pick a teammate\". Lowercase chrome, wine accent, no hype. App chrome only when isDemo.", + 'On-brand: "public demo — data resets, pick a teammate". Lowercase chrome, wine accent, no hype. App chrome only when isDemo.', status: "todo", priority: 2, labels: [L.phase5, L.Frontend, L.UX], @@ -1134,7 +1140,8 @@ Open: banner, reseed cadence, feature-flag/lab policy. }, { title: "README + live-doc synchronization", - description: "Split run-the-demo vs run-the-product docs; fix drifted schema claims. Done 2026-07-10 cleanup.", + description: + "Split run-the-demo vs run-the-product docs; fix drifted schema claims. Done 2026-07-10 cleanup.", status: "done", labels: [L.phase5, L.Improvement], }, @@ -1160,7 +1167,8 @@ Builds: \`bun run build\` and \`VITE_DEMO=false bun run build\` green as of 2026 children: [ { title: "Record bun run build green after each implementation change", - description: "Canonical check: tsc -b && vite build. Covers convex via generated api.d.ts.", + description: + "Canonical check: tsc -b && vite build. Covers convex via generated api.d.ts.", status: "todo", labels: [L.qa, L.DevOps], }, @@ -1285,8 +1293,7 @@ Branch policy: beta is active main; do not touch main until demo→product compl }, { title: "Protect against dual convex dev on anonymous deployment", - description: - "Known footgun documented in AGENTS.md. Ops note only unless tooling added.", + description: "Known footgun documented in AGENTS.md. Ops note only unless tooling added.", status: "done", labels: [L.DevOps], }, diff --git a/scripts/validate-deploy-env.mjs b/scripts/validate-deploy-env.mjs index e847799..baf0bcf 100644 --- a/scripts/validate-deploy-env.mjs +++ b/scripts/validate-deploy-env.mjs @@ -38,28 +38,15 @@ function requireConvexDeploymentUrl(name, value) { } if (demo !== "true" && demo !== "false") { - throw new Error( - "VITE_DEMO must be explicitly set to true or false for deployment builds.", - ); + throw new Error("VITE_DEMO must be explicitly set to true or false for deployment builds."); } -const demoDeployment = requireConvexDeploymentUrl( - "DEMO_CONVEX_URL", - demoConvexUrl, -); -const productDeployment = requireConvexDeploymentUrl( - "PRODUCT_CONVEX_URL", - productConvexUrl, -); -requireConvexDeploymentUrl( - "VITE_CONVEX_URL", - frontendConvexUrl, -); +const demoDeployment = requireConvexDeploymentUrl("DEMO_CONVEX_URL", demoConvexUrl); +const productDeployment = requireConvexDeploymentUrl("PRODUCT_CONVEX_URL", productConvexUrl); +requireConvexDeploymentUrl("VITE_CONVEX_URL", frontendConvexUrl); if (demoDeployment === productDeployment) { - throw new Error( - "DEMO_CONVEX_URL and PRODUCT_CONVEX_URL must be different deployments.", - ); + throw new Error("DEMO_CONVEX_URL and PRODUCT_CONVEX_URL must be different deployments."); } const expectedFrontendUrl = demo === "true" ? demoConvexUrl : productConvexUrl; diff --git a/src/components/AgentSummary.tsx b/src/components/AgentSummary.tsx index c71ebd0..bace663 100644 --- a/src/components/AgentSummary.tsx +++ b/src/components/AgentSummary.tsx @@ -9,7 +9,12 @@ function teaserFrom(summary?: string) { if (!summary) return "Generate a summary of key decisions and open questions."; const line = summary .split("\n") - .map((part) => part.replace(/^[#>\-*\s]+/, "").replace(/\*\*/g, "").trim()) + .map((part) => + part + .replace(/^[#>\-*\s]+/, "") + .replace(/\*\*/g, "") + .trim(), + ) .find((part) => part.length > 0 && !/^tl;?dr:?$/i.test(part)); return line ?? "Summary available."; } @@ -69,10 +74,7 @@ export function AgentSummary({ agent summary {!expanded && summary && isStale ? ( -