diff --git a/apps/nextjs/src/components/layout/header/user.tsx b/apps/nextjs/src/components/layout/header/user.tsx index 6ec355630..9d6bd5445 100644 --- a/apps/nextjs/src/components/layout/header/user.tsx +++ b/apps/nextjs/src/components/layout/header/user.tsx @@ -1,13 +1,21 @@ -import { UnstyledButton } from "@mantine/core"; +import { Indicator, UnstyledButton } from "@mantine/core"; + +import { api } from "@homarr/api/server"; +import { auth } from "@homarr/auth/next"; import { CurrentUserAvatar } from "~/components/user-avatar"; import { UserAvatarMenu } from "~/components/user-avatar-menu"; -export const UserButton = () => { +export const UserButton = async () => { + const data = await api.updateChecker.getAvailableUpdates(); + const session = await auth(); + const isAdmin = session?.user.permissions.includes("admin"); return ( - + - + + + ); diff --git a/apps/nextjs/src/components/user-avatar-menu.tsx b/apps/nextjs/src/components/user-avatar-menu.tsx index 503642482..8e8e2dc14 100644 --- a/apps/nextjs/src/components/user-avatar-menu.tsx +++ b/apps/nextjs/src/components/user-avatar-menu.tsx @@ -7,6 +7,7 @@ import { useRouter } from "next/navigation"; import { Center, Menu, Stack, Text, useMantineColorScheme } from "@mantine/core"; import { useHotkeys, useTimeout } from "@mantine/hooks"; import { + IconBellRinging, IconCheck, IconHome, IconLogin, @@ -23,14 +24,17 @@ import { useScopedI18n } from "@homarr/translation/client"; import "flag-icons/css/flag-icons.min.css"; +import type { RouterOutputs } from "@homarr/api"; + import { useAuthContext } from "~/app/[locale]/_client-providers/session"; import { CurrentLanguageCombobox } from "./language/current-language-combobox"; interface UserAvatarMenuProps { children: ReactNode; + availableUpdates: RouterOutputs["updateChecker"]["getAvailableUpdates"]; } -export const UserAvatarMenu = ({ children }: UserAvatarMenuProps) => { +export const UserAvatarMenu = ({ children, availableUpdates }: UserAvatarMenuProps) => { const t = useScopedI18n("common.userAvatar.menu"); const { colorScheme, toggleColorScheme } = useMantineColorScheme(); useHotkeys([["mod+J", toggleColorScheme]]); @@ -64,6 +68,21 @@ export const UserAvatarMenu = ({ children }: UserAvatarMenuProps) => { // We use keepMounted so we can add event listeners to prevent navigating away without saving the board + {availableUpdates && availableUpdates.length > 0 && availableUpdates[0] && ( + <> + } + > + + {t("updateAvailable", { countUpdates: availableUpdates.length, tag: availableUpdates[0].tag_name })} + + + + + )} }> {colorSchemeText} diff --git a/package.json b/package.json index 172c86c67..987c74d7a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,7 @@ { "name": "homarr", "private": true, + "version": "1.0.0", "scripts": { "build": "cross-env CI=true turbo build", "clean": "git clean -xdf node_modules", diff --git a/packages/api/src/root.ts b/packages/api/src/root.ts index 1f414de47..107adf6d2 100644 --- a/packages/api/src/root.ts +++ b/packages/api/src/root.ts @@ -13,6 +13,7 @@ import { logRouter } from "./router/log"; import { mediaRouter } from "./router/medias/media-router"; import { searchEngineRouter } from "./router/search-engine/search-engine-router"; import { serverSettingsRouter } from "./router/serverSettings"; +import { updateCheckerRouter } from "./router/update-checker"; import { userRouter } from "./router/user"; import { widgetRouter } from "./router/widgets"; import { createTRPCRouter } from "./trpc"; @@ -35,6 +36,7 @@ export const appRouter = createTRPCRouter({ cronJobs: cronJobsRouter, apiKeys: apiKeysRouter, media: mediaRouter, + updateChecker: updateCheckerRouter, }); // export type definition of API diff --git a/packages/api/src/router/update-checker.ts b/packages/api/src/router/update-checker.ts new file mode 100644 index 000000000..66f1a796f --- /dev/null +++ b/packages/api/src/router/update-checker.ts @@ -0,0 +1,17 @@ +import { createSubPubChannel } from "@homarr/redis"; + +import { createTRPCRouter, protectedProcedure } from "../trpc"; + +export const updateCheckerRouter = createTRPCRouter({ + getAvailableUpdates: protectedProcedure.query(async () => { + const channel = createSubPubChannel<{ + availableUpdates: { name: string | null; contentHtml?: string; url: string; tag_name: string }[]; + }>("homarr:update", { + persist: true, + }); + + const data = await channel.getLastDataAsync(); + + return data?.availableUpdates; + }), +}); diff --git a/packages/cron-jobs/package.json b/packages/cron-jobs/package.json index 1abaf0c1b..d7fe53504 100644 --- a/packages/cron-jobs/package.json +++ b/packages/cron-jobs/package.json @@ -38,7 +38,9 @@ "@homarr/request-handler": "workspace:^0.1.0", "@homarr/server-settings": "workspace:^0.1.0", "@homarr/translation": "workspace:^0.1.0", - "@homarr/validation": "workspace:^0.1.0" + "@homarr/validation": "workspace:^0.1.0", + "octokit": "^4.0.2", + "semver-parser": "^4.1.7" }, "devDependencies": { "@homarr/eslint-config": "workspace:^0.2.0", diff --git a/packages/cron-jobs/src/index.ts b/packages/cron-jobs/src/index.ts index cf4132911..45ddd4505 100644 --- a/packages/cron-jobs/src/index.ts +++ b/packages/cron-jobs/src/index.ts @@ -12,6 +12,7 @@ import { pingJob } from "./jobs/ping"; import type { RssFeed } from "./jobs/rss-feeds"; import { rssFeedsJob } from "./jobs/rss-feeds"; import { sessionCleanupJob } from "./jobs/session-cleanup"; +import { updateCheckerJob } from "./jobs/update-checker"; import { createCronJobGroup } from "./lib"; export const jobGroup = createCronJobGroup({ @@ -29,6 +30,7 @@ export const jobGroup = createCronJobGroup({ indexerManager: indexerManagerJob, healthMonitoring: healthMonitoringJob, sessionCleanup: sessionCleanupJob, + updateChecker: updateCheckerJob, }); export type JobGroupKeys = ReturnType<(typeof jobGroup)["getKeys"]>[number]; diff --git a/packages/cron-jobs/src/jobs/update-checker.ts b/packages/cron-jobs/src/jobs/update-checker.ts new file mode 100644 index 000000000..1e32e6efd --- /dev/null +++ b/packages/cron-jobs/src/jobs/update-checker.ts @@ -0,0 +1,58 @@ +import { Octokit } from "octokit"; +import { compareSemVer, isValidSemVer } from "semver-parser"; + +import { EVERY_HOUR } from "@homarr/cron-jobs-core/expressions"; +import { logger } from "@homarr/log"; +import { createSubPubChannel } from "@homarr/redis"; + +import packageJson from "../../../../package.json"; +import { createCronJob } from "../lib"; + +export const updateCheckerJob = createCronJob("updateChecker", EVERY_HOUR, { + runOnStart: true, +}).withCallback(async () => { + const octokit = new Octokit(); + const releases = await octokit.rest.repos.listReleases({ + owner: "homarr-labs", + repo: "homarr", + }); + + const currentVersion = (packageJson as { version: string }).version; + const availableReleases = []; + + for (const release of releases.data) { + if (!isValidSemVer(release.tag_name)) { + logger.warn(`Unable to parse semantic tag '${release.tag_name}'. Update check might not work.`); + continue; + } + + availableReleases.push(release); + } + + const availableNewerReleases = availableReleases + .filter((release) => compareSemVer(release.tag_name, currentVersion) > 0) + .sort((releaseA, releaseB) => compareSemVer(releaseB.tag_name, releaseA.tag_name)); + if (availableReleases.length > 0) { + logger.info( + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + `Update checker found a new available version: ${availableReleases[0]!.tag_name}. Current version is ${currentVersion}`, + ); + } else { + logger.debug(`Update checker did not find any available updates. Current version is ${currentVersion}`); + } + + const channel = createSubPubChannel<{ + availableUpdates: { name: string | null; contentHtml?: string; url: string; tag_name: string }[]; + }>("homarr:update", { + persist: true, + }); + + await channel.publishAsync({ + availableUpdates: availableNewerReleases.map((release) => ({ + name: release.name, + contentHtml: release.body_html, + url: release.html_url, + tag_name: release.tag_name, + })), + }); +}); diff --git a/packages/redis/src/index.ts b/packages/redis/src/index.ts index d30adfccd..0f4a99f38 100644 --- a/packages/redis/src/index.ts +++ b/packages/redis/src/index.ts @@ -7,6 +7,7 @@ export { createIntegrationOptionsChannel, createChannelWithLatestAndEvents, handshakeAsync, + createSubPubChannel, } from "./lib/channel"; export const exampleChannel = createSubPubChannel<{ message: string }>("example"); diff --git a/packages/translation/src/lang/en.json b/packages/translation/src/lang/en.json index 3ad0c7e85..95c6d6c21 100644 --- a/packages/translation/src/lang/en.json +++ b/packages/translation/src/lang/en.json @@ -733,7 +733,8 @@ "logout": "Logout", "login": "Login", "homeBoard": "Your home board", - "loggedOut": "Logged out" + "loggedOut": "Logged out", + "updateAvailable": "{countUpdates} updates available: {tag}" } }, "dangerZone": "Danger zone", @@ -2153,6 +2154,9 @@ }, "sessionCleanup": { "label": "Session Cleanup" + }, + "updateChecker": { + "label": "Update checker" } } }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 43163e35e..3aeac4814 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -797,6 +797,12 @@ importers: '@homarr/validation': specifier: workspace:^0.1.0 version: link:../validation + octokit: + specifier: ^4.0.2 + version: 4.0.2 + semver-parser: + specifier: ^4.1.7 + version: 4.1.7 devDependencies: '@homarr/eslint-config': specifier: workspace:^0.2.0 @@ -3047,6 +3053,113 @@ packages: resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} engines: {node: '>= 8'} + '@octokit/app@15.1.1': + resolution: {integrity: sha512-fk8xrCSPTJGpyBdBNI+DcZ224dm0aApv4vi6X7/zTmANXlegKV2Td+dJ+fd7APPaPN7R+xttUsj2Fm+AFDSfMQ==} + engines: {node: '>= 18'} + + '@octokit/auth-app@7.1.3': + resolution: {integrity: sha512-GZdkOp2kZTIy5dG9oXqvzUAZiPvDx4C/lMlN6yQjtG9d/+hYa7W8WXTJoOrXE8UdfL9A/sZMl206dmtkl9lwVQ==} + engines: {node: '>= 18'} + + '@octokit/auth-oauth-app@8.1.1': + resolution: {integrity: sha512-5UtmxXAvU2wfcHIPPDWzVSAWXVJzG3NWsxb7zCFplCWEmMCArSZV0UQu5jw5goLQXbFyOr5onzEH37UJB3zQQg==} + engines: {node: '>= 18'} + + '@octokit/auth-oauth-device@7.1.1': + resolution: {integrity: sha512-HWl8lYueHonuyjrKKIup/1tiy0xcmQCdq5ikvMO1YwkNNkxb6DXfrPjrMYItNLyCP/o2H87WuijuE+SlBTT8eg==} + engines: {node: '>= 18'} + + '@octokit/auth-oauth-user@5.1.1': + resolution: {integrity: sha512-rRkMz0ErOppdvEfnemHJXgZ9vTPhBuC6yASeFaB7I2yLMd7QpjfrL1mnvRPlyKo+M6eeLxrKanXJ9Qte29SRsw==} + engines: {node: '>= 18'} + + '@octokit/auth-token@5.1.1': + resolution: {integrity: sha512-rh3G3wDO8J9wSjfI436JUKzHIxq8NaiL0tVeB2aXmG6p/9859aUOAjA9pmSPNGGZxfwmaJ9ozOJImuNVJdpvbA==} + engines: {node: '>= 18'} + + '@octokit/auth-unauthenticated@6.1.0': + resolution: {integrity: sha512-zPSmfrUAcspZH/lOFQnVnvjQZsIvmfApQH6GzJrkIunDooU1Su2qt2FfMTSVPRp7WLTQyC20Kd55lF+mIYaohQ==} + engines: {node: '>= 18'} + + '@octokit/core@6.1.2': + resolution: {integrity: sha512-hEb7Ma4cGJGEUNOAVmyfdB/3WirWMg5hDuNFVejGEDFqupeOysLc2sG6HJxY2etBp5YQu5Wtxwi020jS9xlUwg==} + engines: {node: '>= 18'} + + '@octokit/endpoint@10.1.1': + resolution: {integrity: sha512-JYjh5rMOwXMJyUpj028cu0Gbp7qe/ihxfJMLc8VZBMMqSwLgOxDI1911gV4Enl1QSavAQNJcwmwBF9M0VvLh6Q==} + engines: {node: '>= 18'} + + '@octokit/graphql@8.1.1': + resolution: {integrity: sha512-ukiRmuHTi6ebQx/HFRCXKbDlOh/7xEV6QUXaE7MJEKGNAncGI/STSbOkl12qVXZrfZdpXctx5O9X1AIaebiDBg==} + engines: {node: '>= 18'} + + '@octokit/oauth-app@7.1.3': + resolution: {integrity: sha512-EHXbOpBkSGVVGF1W+NLMmsnSsJRkcrnVmDKt0TQYRBb6xWfWzoi9sBD4DIqZ8jGhOWO/V8t4fqFyJ4vDQDn9bg==} + engines: {node: '>= 18'} + + '@octokit/oauth-authorization-url@7.1.1': + resolution: {integrity: sha512-ooXV8GBSabSWyhLUowlMIVd9l1s2nsOGQdlP2SQ4LnkEsGXzeCvbSbCPdZThXhEFzleGPwbapT0Sb+YhXRyjCA==} + engines: {node: '>= 18'} + + '@octokit/oauth-methods@5.1.2': + resolution: {integrity: sha512-C5lglRD+sBlbrhCUTxgJAFjWgJlmTx5bQ7Ch0+2uqRjYv7Cfb5xpX4WuSC9UgQna3sqRGBL9EImX9PvTpMaQ7g==} + engines: {node: '>= 18'} + + '@octokit/openapi-types@22.2.0': + resolution: {integrity: sha512-QBhVjcUa9W7Wwhm6DBFu6ZZ+1/t/oYxqc2tp81Pi41YNuJinbFRx8B133qVOrAaBbF7D/m0Et6f9/pZt9Rc+tg==} + + '@octokit/openapi-webhooks-types@8.5.1': + resolution: {integrity: sha512-i3h1b5zpGSB39ffBbYdSGuAd0NhBAwPyA3QV3LYi/lx4lsbZiu7u2UHgXVUR6EpvOI8REOuVh1DZTRfHoJDvuQ==} + + '@octokit/plugin-paginate-graphql@5.2.4': + resolution: {integrity: sha512-pLZES1jWaOynXKHOqdnwZ5ULeVR6tVVCMm+AUbp0htdcyXDU95WbkYdU4R2ej1wKj5Tu94Mee2Ne0PjPO9cCyA==} + engines: {node: '>= 18'} + peerDependencies: + '@octokit/core': '>=6' + + '@octokit/plugin-paginate-rest@11.3.6': + resolution: {integrity: sha512-zcvqqf/+TicbTCa/Z+3w4eBJcAxCFymtc0UAIsR3dEVoNilWld4oXdscQ3laXamTszUZdusw97K8+DrbFiOwjw==} + engines: {node: '>= 18'} + peerDependencies: + '@octokit/core': '>=6' + + '@octokit/plugin-rest-endpoint-methods@13.2.6': + resolution: {integrity: sha512-wMsdyHMjSfKjGINkdGKki06VEkgdEldIGstIEyGX0wbYHGByOwN/KiM+hAAlUwAtPkP3gvXtVQA9L3ITdV2tVw==} + engines: {node: '>= 18'} + peerDependencies: + '@octokit/core': '>=6' + + '@octokit/plugin-retry@7.1.2': + resolution: {integrity: sha512-XOWnPpH2kJ5VTwozsxGurw+svB2e61aWlmk5EVIYZPwFK5F9h4cyPyj9CIKRyMXMHSwpIsI3mPOdpMmrRhe7UQ==} + engines: {node: '>= 18'} + peerDependencies: + '@octokit/core': '>=6' + + '@octokit/plugin-throttling@9.3.2': + resolution: {integrity: sha512-FqpvcTpIWFpMMwIeSoypoJXysSAQ3R+ALJhXXSG1HTP3YZOIeLmcNcimKaXxTcws+Sh6yoRl13SJ5r8sXc1Fhw==} + engines: {node: '>= 18'} + peerDependencies: + '@octokit/core': ^6.0.0 + + '@octokit/request-error@6.1.5': + resolution: {integrity: sha512-IlBTfGX8Yn/oFPMwSfvugfncK2EwRLjzbrpifNaMY8o/HTEAFqCA1FZxjD9cWvSKBHgrIhc4CSBIzMxiLsbzFQ==} + engines: {node: '>= 18'} + + '@octokit/request@9.1.3': + resolution: {integrity: sha512-V+TFhu5fdF3K58rs1pGUJIDH5RZLbZm5BI+MNF+6o/ssFNT4vWlCh/tVpF3NxGtP15HUxTTMUbsG5llAuU2CZA==} + engines: {node: '>= 18'} + + '@octokit/types@13.6.2': + resolution: {integrity: sha512-WpbZfZUcZU77DrSW4wbsSgTPfKcp286q3ItaIgvSbBpZJlu6mnYXAkjZz6LVZPXkEvLIM8McanyZejKTYUHipA==} + + '@octokit/webhooks-methods@5.1.0': + resolution: {integrity: sha512-yFZa3UH11VIxYnnoOYCVoJ3q4ChuSOk2IVBBQ0O3xtKX4x9bmKb/1t+Mxixv2iUhzMdOl1qeWJqEhouXXzB3rQ==} + engines: {node: '>= 18'} + + '@octokit/webhooks@13.4.1': + resolution: {integrity: sha512-I5YPUtfWidh+OzyrlDahJsUpkpGK0kCTmDRbuqGmlCUzOtxdEkX3R4d6Cd08ijQYwkVXQJanPdbKuZBeV2NMaA==} + engines: {node: '>= 18'} + '@panva/hkdf@1.2.1': resolution: {integrity: sha512-6oclG6Y3PiDFcoyk8srjLfVKyMfVCKJ27JwNPViuXziFpmdz+MZnZN/aKY0JGXgYuO/VghU0jcOAZgWXZ1Dmrw==} @@ -3693,6 +3806,9 @@ packages: '@types/asn1@0.2.4': resolution: {integrity: sha512-V91DSJ2l0h0gRhVP4oBfBzRBN9lAbPUkGDMCnwedqPKX2d84aAMc9CulOvxdw1f7DfEYx99afab+Rsm3e52jhA==} + '@types/aws-lambda@8.10.146': + resolution: {integrity: sha512-3BaDXYTh0e6UCJYL/jwV/3+GRslSc08toAiZSmleYtkAUyV5rtvdPYxrG/88uqvTuT6sb27WE9OS90ZNTIuQ0g==} + '@types/babel__core@7.20.5': resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} @@ -4306,6 +4422,9 @@ packages: resolution: {integrity: sha512-AGBHOG5hPYZ5Xl9KXzU5iKq9516yEmvCKDg3ecP5kX2aB6UqTeXZxk2ELnDgDm6BQSMlLt9rDB4LoSMx0rYwww==} engines: {node: '>= 10.0.0'} + before-after-hook@3.0.2: + resolution: {integrity: sha512-Nik3Sc0ncrMK4UUdXQmAnRtzmNQTAAXmXIopizwZ1W1t8QmfJj+zL4OA2I7XPTPW5z5TDqv4hRo/JzouDJnX3A==} + bellajs@11.2.0: resolution: {integrity: sha512-Wjss+Bc674ZABPr+SCKWTqA4V1pyYFhzDTjNBJy4jdmgOv0oGIGXeKBRJyINwP5tIy+iIZD9SfgZpztduzQ5QA==} engines: {node: '>= 18.4'} @@ -4322,6 +4441,9 @@ packages: bl@4.1.0: resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} + bottleneck@2.19.5: + resolution: {integrity: sha512-VHiNCbI1lKdl44tGrhNfU3lup0Tj/ZBMJB5/2ZbNXRCPuRCO7ed2mgcK4r17y+KB2EfuYuRaVlwNbAeaWGSpbw==} + boxen@5.1.2: resolution: {integrity: sha512-9gYgQKXx+1nP8mP7CzFyaUARhg7D3n1dF/FnErWmu9l6JvGpNUN278h0aSb+QjoiKSWG+iZ3uHrcqk0qrY9RQQ==} engines: {node: '>=10'} @@ -6511,6 +6633,10 @@ packages: resolution: {integrity: sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ==} engines: {node: '>= 0.4'} + octokit@4.0.2: + resolution: {integrity: sha512-wbqF4uc1YbcldtiBFfkSnquHtECEIpYD78YUXI6ri1Im5OO2NLo6ZVpRdbJpdnpZ05zMrVPssNiEo6JQtea+Qg==} + engines: {node: '>= 18'} + ofetch@1.4.1: resolution: {integrity: sha512-QZj2DfGplQAr2oj9KzceK9Hwz6Whxazmn85yYeVuS3u9XTMOGMRx0kO95MQ+vLsj/S/NwBDMMLU5hpxvI6Tklw==} @@ -7250,6 +7376,9 @@ packages: resolution: {integrity: sha512-GX0Ix/CJcHyB8c4ykpHGIAvLyOwOobtM/8d+TQkAd81/bEjgPHrfba41Vpesr7jX/t8Uh+R3EX9eAS5be+jQYg==} engines: {node: '>=8'} + semver-parser@4.1.7: + resolution: {integrity: sha512-RNNvmr1JKZ4Eia7Cyfxtr6aR7eiQs1bPGBv4EC3OJ5pTKEoHjLsT+dY1SSny2tq/uqx9bzNR4lL8tzaB70TN9A==} + semver@6.3.1: resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} hasBin: true @@ -7685,6 +7814,10 @@ packages: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} + toad-cache@3.7.0: + resolution: {integrity: sha512-/m8M+2BJUpoJdgAHoG+baCwBT+tf2VraSfkBgl0Y00qIWt41DJ8R5B8nsEw0I58YwF5IZH6z24/2TobDKnqSWw==} + engines: {node: '>=12'} + toggle-selection@1.0.6: resolution: {integrity: sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ==} @@ -7921,6 +8054,12 @@ packages: resolution: {integrity: sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==} engines: {node: '>=8'} + universal-github-app-jwt@2.2.0: + resolution: {integrity: sha512-G5o6f95b5BggDGuUfKDApKaCgNYy2x7OdHY0zSMF081O0EJobw+1130VONhrA7ezGSV2FNOGyM+KQpQZAr9bIQ==} + + universal-user-agent@7.0.2: + resolution: {integrity: sha512-0JCqzSKnStlRRQfCdowvqy3cy0Dvtlb8xecj/H8JFZuCze4rwjPZQOgvFvn0Ws/usCHQFGpyr+pB9adaGwXn4Q==} + universalify@2.0.1: resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} engines: {node: '>= 10.0.0'} @@ -9391,6 +9530,152 @@ snapshots: '@nodelib/fs.scandir': 2.1.5 fastq: 1.17.1 + '@octokit/app@15.1.1': + dependencies: + '@octokit/auth-app': 7.1.3 + '@octokit/auth-unauthenticated': 6.1.0 + '@octokit/core': 6.1.2 + '@octokit/oauth-app': 7.1.3 + '@octokit/plugin-paginate-rest': 11.3.6(@octokit/core@6.1.2) + '@octokit/types': 13.6.2 + '@octokit/webhooks': 13.4.1 + + '@octokit/auth-app@7.1.3': + dependencies: + '@octokit/auth-oauth-app': 8.1.1 + '@octokit/auth-oauth-user': 5.1.1 + '@octokit/request': 9.1.3 + '@octokit/request-error': 6.1.5 + '@octokit/types': 13.6.2 + toad-cache: 3.7.0 + universal-github-app-jwt: 2.2.0 + universal-user-agent: 7.0.2 + + '@octokit/auth-oauth-app@8.1.1': + dependencies: + '@octokit/auth-oauth-device': 7.1.1 + '@octokit/auth-oauth-user': 5.1.1 + '@octokit/request': 9.1.3 + '@octokit/types': 13.6.2 + universal-user-agent: 7.0.2 + + '@octokit/auth-oauth-device@7.1.1': + dependencies: + '@octokit/oauth-methods': 5.1.2 + '@octokit/request': 9.1.3 + '@octokit/types': 13.6.2 + universal-user-agent: 7.0.2 + + '@octokit/auth-oauth-user@5.1.1': + dependencies: + '@octokit/auth-oauth-device': 7.1.1 + '@octokit/oauth-methods': 5.1.2 + '@octokit/request': 9.1.3 + '@octokit/types': 13.6.2 + universal-user-agent: 7.0.2 + + '@octokit/auth-token@5.1.1': {} + + '@octokit/auth-unauthenticated@6.1.0': + dependencies: + '@octokit/request-error': 6.1.5 + '@octokit/types': 13.6.2 + + '@octokit/core@6.1.2': + dependencies: + '@octokit/auth-token': 5.1.1 + '@octokit/graphql': 8.1.1 + '@octokit/request': 9.1.3 + '@octokit/request-error': 6.1.5 + '@octokit/types': 13.6.2 + before-after-hook: 3.0.2 + universal-user-agent: 7.0.2 + + '@octokit/endpoint@10.1.1': + dependencies: + '@octokit/types': 13.6.2 + universal-user-agent: 7.0.2 + + '@octokit/graphql@8.1.1': + dependencies: + '@octokit/request': 9.1.3 + '@octokit/types': 13.6.2 + universal-user-agent: 7.0.2 + + '@octokit/oauth-app@7.1.3': + dependencies: + '@octokit/auth-oauth-app': 8.1.1 + '@octokit/auth-oauth-user': 5.1.1 + '@octokit/auth-unauthenticated': 6.1.0 + '@octokit/core': 6.1.2 + '@octokit/oauth-authorization-url': 7.1.1 + '@octokit/oauth-methods': 5.1.2 + '@types/aws-lambda': 8.10.146 + universal-user-agent: 7.0.2 + + '@octokit/oauth-authorization-url@7.1.1': {} + + '@octokit/oauth-methods@5.1.2': + dependencies: + '@octokit/oauth-authorization-url': 7.1.1 + '@octokit/request': 9.1.3 + '@octokit/request-error': 6.1.5 + '@octokit/types': 13.6.2 + + '@octokit/openapi-types@22.2.0': {} + + '@octokit/openapi-webhooks-types@8.5.1': {} + + '@octokit/plugin-paginate-graphql@5.2.4(@octokit/core@6.1.2)': + dependencies: + '@octokit/core': 6.1.2 + + '@octokit/plugin-paginate-rest@11.3.6(@octokit/core@6.1.2)': + dependencies: + '@octokit/core': 6.1.2 + '@octokit/types': 13.6.2 + + '@octokit/plugin-rest-endpoint-methods@13.2.6(@octokit/core@6.1.2)': + dependencies: + '@octokit/core': 6.1.2 + '@octokit/types': 13.6.2 + + '@octokit/plugin-retry@7.1.2(@octokit/core@6.1.2)': + dependencies: + '@octokit/core': 6.1.2 + '@octokit/request-error': 6.1.5 + '@octokit/types': 13.6.2 + bottleneck: 2.19.5 + + '@octokit/plugin-throttling@9.3.2(@octokit/core@6.1.2)': + dependencies: + '@octokit/core': 6.1.2 + '@octokit/types': 13.6.2 + bottleneck: 2.19.5 + + '@octokit/request-error@6.1.5': + dependencies: + '@octokit/types': 13.6.2 + + '@octokit/request@9.1.3': + dependencies: + '@octokit/endpoint': 10.1.1 + '@octokit/request-error': 6.1.5 + '@octokit/types': 13.6.2 + universal-user-agent: 7.0.2 + + '@octokit/types@13.6.2': + dependencies: + '@octokit/openapi-types': 22.2.0 + + '@octokit/webhooks-methods@5.1.0': {} + + '@octokit/webhooks@13.4.1': + dependencies: + '@octokit/openapi-webhooks-types': 8.5.1 + '@octokit/request-error': 6.1.5 + '@octokit/webhooks-methods': 5.1.0 + '@panva/hkdf@1.2.1': {} '@paralleldrive/cuid2@2.2.2': @@ -10239,6 +10524,8 @@ snapshots: dependencies: '@types/node': 22.10.2 + '@types/aws-lambda@8.10.146': {} + '@types/babel__core@7.20.5': dependencies: '@babel/parser': 7.26.2 @@ -11020,6 +11307,8 @@ snapshots: - encoding - supports-color + before-after-hook@3.0.2: {} + bellajs@11.2.0: {} better-sqlite3@11.7.0: @@ -11039,6 +11328,8 @@ snapshots: inherits: 2.0.4 readable-stream: 3.6.2 + bottleneck@2.19.5: {} + boxen@5.1.2: dependencies: ansi-align: 3.0.1 @@ -13424,6 +13715,19 @@ snapshots: define-properties: 1.2.1 es-object-atoms: 1.0.0 + octokit@4.0.2: + dependencies: + '@octokit/app': 15.1.1 + '@octokit/core': 6.1.2 + '@octokit/oauth-app': 7.1.3 + '@octokit/plugin-paginate-graphql': 5.2.4(@octokit/core@6.1.2) + '@octokit/plugin-paginate-rest': 11.3.6(@octokit/core@6.1.2) + '@octokit/plugin-rest-endpoint-methods': 13.2.6(@octokit/core@6.1.2) + '@octokit/plugin-retry': 7.1.2(@octokit/core@6.1.2) + '@octokit/plugin-throttling': 9.3.2(@octokit/core@6.1.2) + '@octokit/request-error': 6.1.5 + '@octokit/types': 13.6.2 + ofetch@1.4.1: dependencies: destr: 2.0.3 @@ -14319,6 +14623,8 @@ snapshots: dependencies: semver: 6.3.1 + semver-parser@4.1.7: {} + semver@6.3.1: {} semver@7.6.2: {} @@ -14876,6 +15182,8 @@ snapshots: dependencies: is-number: 7.0.0 + toad-cache@3.7.0: {} + toggle-selection@1.0.6: {} toidentifier@1.0.1: {} @@ -15114,6 +15422,10 @@ snapshots: dependencies: crypto-random-string: 2.0.0 + universal-github-app-jwt@2.2.0: {} + + universal-user-agent@7.0.2: {} + universalify@2.0.1: {} unpipe@1.0.0: {}