From 6d89b7e510d15b420bf1c91db33e9df89c2f427c Mon Sep 17 00:00:00 2001 From: DecDuck Date: Tue, 19 Aug 2025 15:03:20 +1000 Subject: [PATCH] Admin task UI update & QoL (#194) * feat: revise library source names & update droplet * feat: add internal name hint to library sources * feat: update library source table with new name + icons * fix: admin invitation localisation issue * feat: #164 * feat: overhaul task UIs, #163 * fix: remove debug task * fix: lint --- components/DropLogo.vue | 10 ++ components/LogLine.vue | 27 ++++ components/TaskWidget.vue | 55 ++++++++ i18n/locales/en_us.json | 46 ++++--- package.json | 4 +- pages/admin/library/index.vue | 37 +++++- pages/admin/library/sources/index.vue | 55 ++++++-- pages/admin/task/[id]/index.vue | 30 ++--- pages/admin/task/index.vue | 153 ++++++++--------------- server/api/v1/admin/library/index.get.ts | 3 +- server/api/v1/admin/task/index.get.ts | 5 +- server/api/v1/admin/task/index.post.ts | 31 +++++ server/internal/library/index.ts | 6 +- server/internal/tasks/group.ts | 3 + server/internal/tasks/index.ts | 24 ++-- server/internal/tasks/registry/debug.ts | 18 +++ utils/parseTaskLog.ts | 20 ++- yarn.lock | 108 ++++++++-------- 18 files changed, 420 insertions(+), 215 deletions(-) create mode 100644 components/LogLine.vue create mode 100644 components/TaskWidget.vue create mode 100644 server/api/v1/admin/task/index.post.ts create mode 100644 server/internal/tasks/registry/debug.ts diff --git a/components/DropLogo.vue b/components/DropLogo.vue index 7dde700..383a228 100644 --- a/components/DropLogo.vue +++ b/components/DropLogo.vue @@ -10,6 +10,16 @@ d="M4 13.5C4 11.0008 5.38798 8.76189 7.00766 7C8.43926 5.44272 10.0519 4.25811 11.0471 3.5959C11.6287 3.20893 12.3713 3.20893 12.9529 3.5959C13.9481 4.25811 15.5607 5.44272 16.9923 7C18.612 8.76189 20 11.0008 20 13.5C20 17.9183 16.4183 21.5 12 21.5C7.58172 21.5 4 17.9183 4 13.5Z" stroke="currentColor" stroke-width="2" + stroke-dasharray="100" + :stroke-dashoffset="dashArray" /> + + diff --git a/components/LogLine.vue b/components/LogLine.vue new file mode 100644 index 0000000..64fbf2f --- /dev/null +++ b/components/LogLine.vue @@ -0,0 +1,27 @@ + + + diff --git a/components/TaskWidget.vue b/components/TaskWidget.vue new file mode 100644 index 0000000..3738e10 --- /dev/null +++ b/components/TaskWidget.vue @@ -0,0 +1,55 @@ + + + diff --git a/i18n/locales/en_us.json b/i18n/locales/en_us.json index 7cb3bb2..2356acb 100644 --- a/i18n/locales/en_us.json +++ b/i18n/locales/en_us.json @@ -137,6 +137,10 @@ "usernameTaken": "Username already taken." }, "backHome": "{arrow} Back to home", + "externalUrl": { + "subtitle": "This message is only visible to admins.", + "title": "Accessing over different EXTERNAL_URL. Please check the docs." + }, "game": { "banner": { "description": "Drop failed to update the banner image: {0}", @@ -212,10 +216,6 @@ "desc": "Drop encountered an error while updating the version: {error}", "title": "There an error while updating the version order" } - }, - "externalUrl": { - "title": "Accessing over different EXTERNAL_URL. Please check the docs.", - "subtitle": "This message is only visible to admins." } }, "footer": { @@ -327,6 +327,7 @@ "description": "Companies organize games by who they were developed or published by.", "editor": { "action": "Add Game {plus}", + "descriptionPlaceholder": "{'<'}description{'>'}", "developed": "Developed", "libraryDescription": "Add, remove, or customise what this company has developed and/or published.", "libraryTitle": "Game Library", @@ -334,25 +335,23 @@ "published": "Published", "uploadBanner": "Upload banner", "uploadIcon": "Upload icon", - "descriptionPlaceholder": "{'<'}description{'>'}", "websitePlaceholder": "{'<'}website{'>'}" }, "modals": { + "createDescription": "Create a company to further organize your games.", + "createFieldDescription": "Company Description", + "createFieldDescriptionPlaceholder": "A small indie studio that...", + "createFieldName": "Company Name", + "createFieldNamePlaceholder": "My New Company...", + "createFieldWebsite": "Company Website", + "createFieldWebsitePlaceholder": "https://example.com/", + "createTitle": "Create a company", "nameDescription": "Edit the company's name. Used to match to new game imports.", "nameTitle": "Edit company name", "shortDeckDescription": "Edit the company's description. Doesn't affect long (markdown) description.", "shortDeckTitle": "Edit company description", "websiteDescription": "Edit the company's website. Note: this will be a link, and won't have redirect protection.", - "websiteTitle": "Edit company website", - - "createTitle": "Create a company", - "createDescription": "Create a company to further organize your games.", - "createFieldName": "Company Name", - "createFieldNamePlaceholder": "My New Company...", - "createFieldDescription": "Company Description", - "createFieldDescriptionPlaceholder": "A small indie studio that...", - "createFieldWebsite": "Company Website", - "createFieldWebsitePlaceholder": "https://example.com/" + "websiteTitle": "Edit company website" }, "noCompanies": "No companies", "noGames": "No games", @@ -373,6 +372,8 @@ }, "metadataProvider": "Metadata provider", "noGames": "No games imported", + "libraryHint": "No libraries configured.", + "libraryHintDocsLink": "What does this mean? {arrow}", "offline": "Drop couldn't access this game.", "offlineTitle": "Game offline", "openEditor": "Open in Editor {arrow}", @@ -382,12 +383,15 @@ "create": "Create source", "createDesc": "Drop will use this source to access your game library, and make them available.", "desc": "Configure your library sources, where Drop will look for new games and versions to import.", + "documentationLink": "Documentation {arrow}", "edit": "Edit source", "fsDesc": "Imports games from a path on disk. Requires version-based folder structure, and supports archived games.", "fsFlatDesc": "Imports games from a path on disk, but without a separate version subfolder. Useful when migrating an existing library to Drop.", + "fsFlatTitle": "Compatibility", "fsPath": "Path", "fsPathDesc": "An absolute path to your game library.", "fsPathPlaceholder": "/mnt/games", + "fsTitle": "Drop-style", "link": "Sources {arrow}", "nameDesc": "The name of your source, for reference.", "namePlaceholder": "My New Source", @@ -514,13 +518,13 @@ "images": "Game Images", "lookAt": "Check it out", "noDevelopers": "No developers", - "noGame": "NO GAME", "noFeatured": "NO FEATURED GAMES", - "openFeatured": "Star games in Admin Library {arrow}", + "noGame": "NO GAME", "noImages": "No images", "noPublishers": "No publishers.", "noTags": "No tags", "openAdminDashboard": "Open in Admin Dashboard", + "openFeatured": "Star games in Admin Library {arrow}", "platform": "Platform | Platform | Platforms", "publishers": "Publishers | Publisher | Publishers", "rating": "Rating", @@ -560,7 +564,9 @@ "cleanupSessionsName": "Clean up sessions." }, "viewTask": "View {arrow}", - "weeklyScheduledTitle": "Weekly scheduled tasks" + "weeklyScheduledTitle": "Weekly scheduled tasks", + "progress": "{0}%", + "execute": "{arrow} Execute" } }, "title": "Drop", @@ -585,7 +591,6 @@ "admin": { "adminHeader": "Admin?", "adminUserLabel": "Admin user", - "authLink": "Authentication {arrow}", "authentication": { "configure": "Configure", "description": "Drop supports a variety of \"authentication mechanisms\". As you enable or disable them, they are shown on the sign in screen for users to select from. Click the dot menu to configure the authentication mechanism.", @@ -597,6 +602,7 @@ "srOpenOptions": "Open options", "title": "Authentication" }, + "authLink": "Authentication {arrow}", "authoptionsHeader": "Auth Options", "delete": "Delete", "deleteUser": "Delete user {0}", @@ -609,7 +615,7 @@ "createInvitation": "Create invitation", "description": "Simple authentication uses a system of 'invitations' to create users. You can create an invitation, and optionally specify a username or email for the user, and then it will generate a magic URL that can be used to create an account.", "expires": "Expires: {expiry}", - "invitationTitle": "invitations", + "invitationTitle": "Invitations", "invite3Days": "3 days", "invite6Months": "6 months", "inviteAdminSwitchDescription": "Create this user as an administrator", diff --git a/package.json b/package.json index 3a68d92..9eb9722 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "drop", - "version": "0.3.1", + "version": "0.3.2", "private": true, "type": "module", "license": "AGPL-3.0-or-later", @@ -21,7 +21,7 @@ }, "dependencies": { "@discordapp/twemoji": "^16.0.1", - "@drop-oss/droplet": "2.3.0", + "@drop-oss/droplet": "2.3.1", "@headlessui/vue": "^1.7.23", "@heroicons/vue": "^2.1.5", "@lobomfz/prismark": "0.0.3", diff --git a/pages/admin/library/index.vue b/pages/admin/library/index.vue index f113509..cae7817 100644 --- a/pages/admin/library/index.vue +++ b/pages/admin/library/index.vue @@ -242,11 +242,40 @@ {{ $t("common.noResults") }}

{{ $t("library.admin.noGames") }}

+

+ {{ + $t("library.admin.libraryHint") + }} + + + + + + +

@@ -256,7 +285,11 @@ import { ExclamationTriangleIcon, ExclamationCircleIcon, } from "@heroicons/vue/16/solid"; -import { InformationCircleIcon, StarIcon } from "@heroicons/vue/20/solid"; +import { + ArrowTopRightOnSquareIcon, + InformationCircleIcon, + StarIcon, +} from "@heroicons/vue/20/solid"; import { MagnifyingGlassIcon } from "@heroicons/vue/24/outline"; const { t } = useI18n(); diff --git a/pages/admin/library/sources/index.vue b/pages/admin/library/sources/index.vue index d7b6c14..7787001 100644 --- a/pages/admin/library/sources/index.vue +++ b/pages/admin/library/sources/index.vue @@ -64,8 +64,14 @@ > {{ source.name }} - - {{ source.backend }} + + + {{ optionsMetadata[source.backend].title }} {{ source }}{{ metadata.title }} + {{ + source + }} + - {{ metadata.description }} + + + + + {{ task.name }} -
+
+ +
+
-
- -
-
{{
-          formatLine(line)
-        }}
+ {{ + $t("tasks.admin.progress", [Math.round(task.progress * 10) / 10]) + }}
@@ -90,11 +97,6 @@ const taskId = route.params.id.toString(); const task = useTask(taskId); -function formatLine(line: string): string { - const res = parseTaskLog(line); - return `[${res.timestamp}] ${res.message}`; -} - definePageMeta({ layout: "admin", }); diff --git a/pages/admin/task/index.vue b/pages/admin/task/index.vue index b9f3f6c..22068de 100644 --- a/pages/admin/task/index.vue +++ b/pages/admin/task/index.vue @@ -13,62 +13,7 @@ :key="task.value?.id" class="col-span-1 divide-y divide-gray-200 rounded-lg bg-zinc-800 border border-zinc-700 shadow-sm" > -
-
-
-
- - -
-
-

- {{ task.value.name }} -

-
-

- {{ task.value.id }} -

-
-
-
-

- {{ parseTaskLog(task.value.log.at(-1)).message }} -

- - - - - -
-
-
- -
+
-
-
-
-
- - -
-
-

- {{ task.name }} -

- -
-

- {{ task.id }} -

-

- {{ parseTaskLog(task.log.at(-1)).message }} -

- - - - - -
-
+
@@ -157,6 +58,21 @@

{{ scheduledTasks[task].description }}

+
@@ -180,6 +96,21 @@

{{ scheduledTasks[task].description }}

+
@@ -189,7 +120,7 @@
diff --git a/server/api/v1/admin/library/index.get.ts b/server/api/v1/admin/library/index.get.ts index d0a0047..5df358b 100644 --- a/server/api/v1/admin/library/index.get.ts +++ b/server/api/v1/admin/library/index.get.ts @@ -7,8 +7,9 @@ export default defineEventHandler(async (h3) => { const unimportedGames = await libraryManager.fetchUnimportedGames(); const games = await libraryManager.fetchGamesWithStatus(); + const libraries = await libraryManager.fetchLibraries(); // Fetch other library data here - return { unimportedGames, games }; + return { unimportedGames, games, hasLibraries: libraries.length > 0 }; }); diff --git a/server/api/v1/admin/task/index.get.ts b/server/api/v1/admin/task/index.get.ts index a81492c..690dce8 100644 --- a/server/api/v1/admin/task/index.get.ts +++ b/server/api/v1/admin/task/index.get.ts @@ -1,5 +1,6 @@ import aclManager from "~/server/internal/acls"; import prisma from "~/server/internal/db/database"; +import type { TaskMessage } from "~/server/internal/tasks"; import taskHandler from "~/server/internal/tasks"; export default defineEventHandler(async (h3) => { @@ -13,7 +14,7 @@ export default defineEventHandler(async (h3) => { }); const runningTasks = (await taskHandler.runningTasks()).map((e) => e.id); - const historicalTasks = await prisma.task.findMany({ + const historicalTasks = (await prisma.task.findMany({ where: { OR: [ { @@ -28,7 +29,7 @@ export default defineEventHandler(async (h3) => { ended: "desc", }, take: 10, - }); + })) as Array; const dailyTasks = await taskHandler.dailyTasks(); const weeklyTasks = await taskHandler.weeklyTasks(); diff --git a/server/api/v1/admin/task/index.post.ts b/server/api/v1/admin/task/index.post.ts new file mode 100644 index 0000000..dc28190 --- /dev/null +++ b/server/api/v1/admin/task/index.post.ts @@ -0,0 +1,31 @@ +import { type } from "arktype"; +import { readDropValidatedBody, throwingArktype } from "~/server/arktype"; +import aclManager from "~/server/internal/acls"; +import taskHandler from "~/server/internal/tasks"; +import type { TaskGroup } from "~/server/internal/tasks/group"; +import { taskGroups } from "~/server/internal/tasks/group"; + +const StartTask = type({ + taskGroup: type("string"), +}).configure(throwingArktype); + +export default defineEventHandler(async (h3) => { + const allowed = await aclManager.allowSystemACL(h3, ["task:start"]); + if (!allowed) throw createError({ statusCode: 403 }); + + const body = await readDropValidatedBody(h3, StartTask); + const taskGroup = body.taskGroup as TaskGroup; + if (!taskGroups[taskGroup]) + throw createError({ + statusCode: 400, + statusMessage: "Invalid task group.", + }); + + const task = await taskHandler.runTaskGroupByName(taskGroup); + if (!task) + throw createError({ + statusCode: 500, + statusMessage: "Could not start task.", + }); + return { id: task }; +}); diff --git a/server/internal/library/index.ts b/server/internal/library/index.ts index ce9f6ca..0a74dfe 100644 --- a/server/internal/library/index.ts +++ b/server/internal/library/index.ts @@ -116,7 +116,11 @@ class LibraryManager { async fetchGamesWithStatus() { const games = await prisma.game.findMany({ include: { - versions: true, + versions: { + select: { + versionName: true, + }, + }, library: true, }, orderBy: { diff --git a/server/internal/tasks/group.ts b/server/internal/tasks/group.ts index d95db5e..f914272 100644 --- a/server/internal/tasks/group.ts +++ b/server/internal/tasks/group.ts @@ -14,6 +14,9 @@ export const taskGroups = { "import:game": { concurrency: true, }, + debug: { + concurrency: true, + }, } as const; export type TaskGroup = keyof typeof taskGroups; diff --git a/server/internal/tasks/index.ts b/server/internal/tasks/index.ts index f6f93fe..54433a9 100644 --- a/server/internal/tasks/index.ts +++ b/server/internal/tasks/index.ts @@ -53,6 +53,7 @@ class TaskHandler { "cleanup:invitations", "cleanup:sessions", "check:update", + "debug", ]; private weeklyScheduledTasks: TaskGroup[] = ["cleanup:objects"]; @@ -62,6 +63,7 @@ class TaskHandler { this.saveScheduledTask(cleanupSessions); this.saveScheduledTask(checkUpdate); this.saveScheduledTask(cleanupObjects); + //this.saveScheduledTask(debug); } /** @@ -162,6 +164,13 @@ class TaskHandler { // You can configure timestamp, level, etc. here timestamp: pino.stdTimeFunctions.isoTime, base: null, // Remove pid/hostname if not needed + formatters: { + level(label) { + return { + level: label, + }; + }, + }, }, logStream, ); @@ -339,13 +348,15 @@ class TaskHandler { return this.weeklyScheduledTasks; } - runTaskGroupByName(name: TaskGroup) { - const task = this.taskCreators.get(name); - if (!task) { + async runTaskGroupByName(name: TaskGroup) { + const taskConstructor = this.taskCreators.get(name); + if (!taskConstructor) { logger.warn(`No task found for group ${name}`); return; } - this.create(task()); + const task = taskConstructor(); + await this.create(task); + return task.id; } /** @@ -444,7 +455,7 @@ export type TaskMessage = { name: string; success: boolean; progress: number; - error: undefined | { title: string; description: string }; + error: null | undefined | { title: string; description: string }; log: string[]; reset?: boolean; }; @@ -470,6 +481,7 @@ interface DropTask { export const TaskLog = type({ timestamp: "string", message: "string", + level: "string", }); // /** @@ -499,8 +511,6 @@ export const TaskLog = type({ // } export function defineDropTask(buildTask: BuildTask): DropTask { - // TODO: only let one task with the same taskGroup run at the same time if specified - return { taskGroup: buildTask.taskGroup, build: () => ({ diff --git a/server/internal/tasks/registry/debug.ts b/server/internal/tasks/registry/debug.ts new file mode 100644 index 0000000..9cd8a4a --- /dev/null +++ b/server/internal/tasks/registry/debug.ts @@ -0,0 +1,18 @@ +import { defineDropTask } from ".."; + +export default defineDropTask({ + buildId: () => `debug:${new Date().toISOString()}`, + name: "Debug Task", + acls: ["system:maintenance:read"], + taskGroup: "debug", + async run({ progress, logger }) { + const amount = 1000; + for (let i = 0; i < amount; i++) { + progress((i / amount) * 100); + logger.info(`dajksdkajd ${i}`); + logger.warn("warning"); + logger.error("error\nmultiline and stuff\nwoah more lines"); + await new Promise((r) => setTimeout(r, 1500)); + } + }, +}); diff --git a/utils/parseTaskLog.ts b/utils/parseTaskLog.ts index 4cc0b4f..93f1e21 100644 --- a/utils/parseTaskLog.ts +++ b/utils/parseTaskLog.ts @@ -1,13 +1,31 @@ import type { TaskLog } from "~/server/internal/tasks"; +const labelNumberMap = { + 100: "silent", + 60: "fatal", + 50: "error", + 40: "warn", + 30: "info", + 20: "debug", + 10: "trace", + 0: "off", +}; + export function parseTaskLog( logStr?: string | undefined, ): typeof TaskLog.infer { - if (!logStr) return { message: "", timestamp: "" }; + if (!logStr) return { message: "", timestamp: "", level: "" }; const log = JSON.parse(logStr); + if (typeof log.level === "number") { + log.level = labelNumberMap[ + log.level as keyof typeof labelNumberMap + ] as string; + } + return { message: log.msg, timestamp: log.time, + level: log.level, }; } diff --git a/yarn.lock b/yarn.lock index 7d3f9a9..9aad4fa 100644 --- a/yarn.lock +++ b/yarn.lock @@ -342,71 +342,71 @@ jsonfile "^5.0.0" universalify "^0.1.2" -"@drop-oss/droplet-darwin-arm64@2.3.0": - version "2.3.0" - resolved "https://registry.yarnpkg.com/@drop-oss/droplet-darwin-arm64/-/droplet-darwin-arm64-2.3.0.tgz#f4f0ded9c9f5b5cac25dd56f59817e1c13e865ab" - integrity sha512-5k1VwGZTFc61FvKyL4cvYxFYB7aCY5cWCo0Q7yTkkj+KR+ewH6ucylU8kDG7M+aBLvbC/zbntXUp4RtYZi4AZQ== +"@drop-oss/droplet-darwin-arm64@2.3.1": + version "2.3.1" + resolved "https://registry.yarnpkg.com/@drop-oss/droplet-darwin-arm64/-/droplet-darwin-arm64-2.3.1.tgz#ee1c32b4f180fd40bde57b4d428bdf51119cf6c8" + integrity sha512-s0qORIlGTyjnDL2W6Eljoz8yloBXVTpuyiEqnOCVTTtse3uLgqG+78wF2sVhiCKKCNuNwELv5icomJ2k4w2K5w== -"@drop-oss/droplet-darwin-universal@2.3.0": - version "2.3.0" - resolved "https://registry.yarnpkg.com/@drop-oss/droplet-darwin-universal/-/droplet-darwin-universal-2.3.0.tgz#1d8659bc2869e5d30308622bcc6cb230030d738e" - integrity sha512-4V/HMnNtmHgn156pTpa3mVTAwTmO9jqtZrDcVko7PdSotEbXiwBpTFzbgb4bPafbPmkSNoRh4G9d3BLQCh4mgw== +"@drop-oss/droplet-darwin-universal@2.3.1": + version "2.3.1" + resolved "https://registry.yarnpkg.com/@drop-oss/droplet-darwin-universal/-/droplet-darwin-universal-2.3.1.tgz#c6529184bd959d326c85ed97c7e8d2b07529abbb" + integrity sha512-L1fZjj2LQpLAesNtP3s0PzFPsJ77nVbyXbBeQGaoaq5kF0/npO4NYQ/7flkllGhIlJf/OQENWcARvfablM/G8g== -"@drop-oss/droplet-darwin-x64@2.3.0": - version "2.3.0" - resolved "https://registry.yarnpkg.com/@drop-oss/droplet-darwin-x64/-/droplet-darwin-x64-2.3.0.tgz#c7ff5dae8ba520866b7cd49714625ada8fa0a7c2" - integrity sha512-PUcNjE09N7qEFsbssKxL8rjmCt9AUYPz1yK34d8N2W9DboS1KI+PShWdd/NOk4GYzTJQuJhMp8wNcUrljfqXmQ== +"@drop-oss/droplet-darwin-x64@2.3.1": + version "2.3.1" + resolved "https://registry.yarnpkg.com/@drop-oss/droplet-darwin-x64/-/droplet-darwin-x64-2.3.1.tgz#711855d85821080ac11e80b81ec91df1eaa23405" + integrity sha512-4UvveQRCq10iEoVfArdCeBuQg51jbn5tkW68LXRoFWzicQz6MFhbowVmzz5RbjYcT5kYwI4c7jBZgeKBGOvc+g== -"@drop-oss/droplet-linux-arm64-gnu@2.3.0": - version "2.3.0" - resolved "https://registry.yarnpkg.com/@drop-oss/droplet-linux-arm64-gnu/-/droplet-linux-arm64-gnu-2.3.0.tgz#8819b34c5ff8bd8182c5cd0c3f1784dc2afd9507" - integrity sha512-6VyOwYu9sMrCL82UZOvvjU9G/4wHdA8P6q3+EDIVdABg5jVEYZsxkrT0Kp/5h9Xs0mPFNB/su8ZwB9FRQ63o1w== +"@drop-oss/droplet-linux-arm64-gnu@2.3.1": + version "2.3.1" + resolved "https://registry.yarnpkg.com/@drop-oss/droplet-linux-arm64-gnu/-/droplet-linux-arm64-gnu-2.3.1.tgz#f3a3a00239a9063bba1fbcb2209303b337cd862d" + integrity sha512-URlpIZZDG/u2pwxM+PaWK240+Phr4RETzwcUQyERAPv22vTjl4l/s4sYa4o4fHX/eosgNLncO+jYgO0f9FS3kg== -"@drop-oss/droplet-linux-arm64-musl@2.3.0": - version "2.3.0" - resolved "https://registry.yarnpkg.com/@drop-oss/droplet-linux-arm64-musl/-/droplet-linux-arm64-musl-2.3.0.tgz#06601aa8af4bffeb26956ff79ed494265e313342" - integrity sha512-2BZreAg1XOBxr+iY2hFcX4x6bFC7AKXkIHa9130rmStH/HxnGq6K5H49eJd6ezzNMH/lQ7Sm7uJP2+sH8mjeCw== +"@drop-oss/droplet-linux-arm64-musl@2.3.1": + version "2.3.1" + resolved "https://registry.yarnpkg.com/@drop-oss/droplet-linux-arm64-musl/-/droplet-linux-arm64-musl-2.3.1.tgz#8743132ad2fb050ef5f3cd5ddbe93b1b35ebda73" + integrity sha512-+m5IVmD4J979KggUSpyrE4PD4mjHAnvJ8EY2AOMSxmCu3JmONMIKHNmx1TA/k9PlesGKe6GjAs2WGlxwZRCZ2g== -"@drop-oss/droplet-linux-riscv64-gnu@2.3.0": - version "2.3.0" - resolved "https://registry.yarnpkg.com/@drop-oss/droplet-linux-riscv64-gnu/-/droplet-linux-riscv64-gnu-2.3.0.tgz#6d5629631aeeceadb292998e21b6e2b2cf839bdc" - integrity sha512-E7i86Q8IU7rh2FVtXa0NxoGRhB7AZU+AWPumTAuDQS3xPg3sj+c3q/A7YI1Ay4lnvzR/fevP2p/7iSJUEDcchQ== +"@drop-oss/droplet-linux-riscv64-gnu@2.3.1": + version "2.3.1" + resolved "https://registry.yarnpkg.com/@drop-oss/droplet-linux-riscv64-gnu/-/droplet-linux-riscv64-gnu-2.3.1.tgz#d7f03d78f107a04f952c8958a024c27df6ad9818" + integrity sha512-AkSbSjt8nvcuxJYBeEvGGh6HaQSPO+OeLYUjc4pBZDACUOULHLWnZ5LdD/GUN97JcZOfq+BYfP50mz/1BvWoSA== -"@drop-oss/droplet-linux-x64-gnu@2.3.0": - version "2.3.0" - resolved "https://registry.yarnpkg.com/@drop-oss/droplet-linux-x64-gnu/-/droplet-linux-x64-gnu-2.3.0.tgz#a924aada38dbc54f6967b17435c10bf3b6e6ffb0" - integrity sha512-eIHhhoSgpvMAc9tn/K0ldZRXvDe1Xyd9okSSqaclCEKjdVfWU8UMycUz1SzQH9YefiqEB4Qjd3y1iRgaEa8niA== +"@drop-oss/droplet-linux-x64-gnu@2.3.1": + version "2.3.1" + resolved "https://registry.yarnpkg.com/@drop-oss/droplet-linux-x64-gnu/-/droplet-linux-x64-gnu-2.3.1.tgz#7deff7a3de0697de13685c81ec4e9c8e6186ed0a" + integrity sha512-jaswkLtTJ4U5GhyBn+86r7cEf8dnHQmcklxomEA8P/DWTHkab1g23XlgaSGHX/FaiRpKAxXGNLTPyY8lR6wePQ== -"@drop-oss/droplet-linux-x64-musl@2.3.0": - version "2.3.0" - resolved "https://registry.yarnpkg.com/@drop-oss/droplet-linux-x64-musl/-/droplet-linux-x64-musl-2.3.0.tgz#4eb71112f7641e1fad3b53f5f8d1b98b9cb84bf0" - integrity sha512-0taR945NvK+xNBicSYriKDJgBxpcozzgcALDp/cX2UaYV9cb5PF/xw80DArCyUDvKOfRzeFALx4KRC2ghPr6tw== +"@drop-oss/droplet-linux-x64-musl@2.3.1": + version "2.3.1" + resolved "https://registry.yarnpkg.com/@drop-oss/droplet-linux-x64-musl/-/droplet-linux-x64-musl-2.3.1.tgz#31a2f5107817a59ff5a516cd1912261c85b2e0c7" + integrity sha512-xn+W31FOnPVvkoxks104QusnHS1/J0V4Lwzegu3hPjPpZnc0YqFEo74pcxb2VfltgjMTtBOmzyKaApt9Snhd8Q== -"@drop-oss/droplet-win32-arm64-msvc@2.3.0": - version "2.3.0" - resolved "https://registry.yarnpkg.com/@drop-oss/droplet-win32-arm64-msvc/-/droplet-win32-arm64-msvc-2.3.0.tgz#36568f87024eb48ce7e82d76ea83a2c6ec25a856" - integrity sha512-5HkO98h/PboM+/wPulKVGFTklijlqht8w13iW1ipUcRFsOHmS1o8nejjLL7KEr2X8G4JwYOqBeX8tY3OhaU9bw== +"@drop-oss/droplet-win32-arm64-msvc@2.3.1": + version "2.3.1" + resolved "https://registry.yarnpkg.com/@drop-oss/droplet-win32-arm64-msvc/-/droplet-win32-arm64-msvc-2.3.1.tgz#4cf431b654b33c58df4cef256e84881b0556a046" + integrity sha512-MwDz/4SfFSCEYzGCsptVD38amB18c2MSheJM+mV7Y8qjVDfjzAGrdX9923e8BYIm8eY8ypnOQBTy4/qae8+3uw== -"@drop-oss/droplet-win32-x64-msvc@2.3.0": - version "2.3.0" - resolved "https://registry.yarnpkg.com/@drop-oss/droplet-win32-x64-msvc/-/droplet-win32-x64-msvc-2.3.0.tgz#e794ea7cfdc0ea148707e4f3e60f2aa547328c03" - integrity sha512-6lNXOMyy9sPaO4wbklOIr2jbuvZHIVrd+dXu2UOI2YqFlHdxiDD1sZnqSZmlfCP58yeA+SpTfhxDHwUHJTFI/g== +"@drop-oss/droplet-win32-x64-msvc@2.3.1": + version "2.3.1" + resolved "https://registry.yarnpkg.com/@drop-oss/droplet-win32-x64-msvc/-/droplet-win32-x64-msvc-2.3.1.tgz#01b33c5241cca9aa026605cbb18027d82f57cc6f" + integrity sha512-5FRfblFZBI9jpLNS0z+QSqYzxpDg3YSTP/2LvEqI2Z7ZYC02j4SKnW/+C4zzblNuR3e/igfaP0I9yBv4uIb6ew== -"@drop-oss/droplet@2.3.0": - version "2.3.0" - resolved "https://registry.yarnpkg.com/@drop-oss/droplet/-/droplet-2.3.0.tgz#eb2891346cf7fadcc847d5dee37674fc1106d2fc" - integrity sha512-ffEoS3LYBfPm0++p7f7F/NkYH5PfauQzuj1gTz7qVWZOSP5VQWYhOc9BEg0fsCCzTB/mct0jwOsK92URmthpxA== +"@drop-oss/droplet@2.3.1": + version "2.3.1" + resolved "https://registry.yarnpkg.com/@drop-oss/droplet/-/droplet-2.3.1.tgz#664e42bf0b205ffa06f5d5f48f0080012145d8c2" + integrity sha512-oar7wvwMC1Evau4lXYcGerFcK/HqTi2Fn/4mcBldIF7B33NhsXhg4RncSNGS5J0WfwG4rkbI249nbs4aDeIrFg== optionalDependencies: - "@drop-oss/droplet-darwin-arm64" "2.3.0" - "@drop-oss/droplet-darwin-universal" "2.3.0" - "@drop-oss/droplet-darwin-x64" "2.3.0" - "@drop-oss/droplet-linux-arm64-gnu" "2.3.0" - "@drop-oss/droplet-linux-arm64-musl" "2.3.0" - "@drop-oss/droplet-linux-riscv64-gnu" "2.3.0" - "@drop-oss/droplet-linux-x64-gnu" "2.3.0" - "@drop-oss/droplet-linux-x64-musl" "2.3.0" - "@drop-oss/droplet-win32-arm64-msvc" "2.3.0" - "@drop-oss/droplet-win32-x64-msvc" "2.3.0" + "@drop-oss/droplet-darwin-arm64" "2.3.1" + "@drop-oss/droplet-darwin-universal" "2.3.1" + "@drop-oss/droplet-darwin-x64" "2.3.1" + "@drop-oss/droplet-linux-arm64-gnu" "2.3.1" + "@drop-oss/droplet-linux-arm64-musl" "2.3.1" + "@drop-oss/droplet-linux-riscv64-gnu" "2.3.1" + "@drop-oss/droplet-linux-x64-gnu" "2.3.1" + "@drop-oss/droplet-linux-x64-musl" "2.3.1" + "@drop-oss/droplet-win32-arm64-msvc" "2.3.1" + "@drop-oss/droplet-win32-x64-msvc" "2.3.1" "@emnapi/core@^1.4.3": version "1.4.5"