mirror of
https://github.com/Drop-OSS/drop.git
synced 2025-11-09 20:12:10 +10:00
Various fixes (#186)
* fix: #181 * fix: use taskHandler as source of truth for imports * fix: task formatting * fix: zip downloads * feat: re-enable import version button on delete + lint
This commit is contained in:
@ -22,21 +22,17 @@
|
||||
<!-- import games button -->
|
||||
|
||||
<NuxtLink
|
||||
:href="
|
||||
unimportedVersions.length > 0
|
||||
? `/admin/library/${game.id}/import`
|
||||
: ''
|
||||
"
|
||||
:href="canImport ? `/admin/library/${game.id}/import` : ''"
|
||||
type="button"
|
||||
:class="[
|
||||
unimportedVersions.length > 0
|
||||
canImport
|
||||
? 'bg-blue-600 hover:bg-blue-700'
|
||||
: 'bg-blue-800/50',
|
||||
'inline-flex w-fit items-center gap-x-2 rounded-md px-3 py-1 text-sm font-semibold font-display text-white shadow-sm focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blue-600',
|
||||
]"
|
||||
>
|
||||
{{
|
||||
unimportedVersions.length > 0
|
||||
canImport
|
||||
? $t("library.admin.import.version.import")
|
||||
: $t("library.admin.import.version.noVersions")
|
||||
}}
|
||||
@ -124,10 +120,16 @@ import { ExclamationCircleIcon } from "@heroicons/vue/24/outline";
|
||||
|
||||
// TODO implement UI for this page
|
||||
|
||||
defineProps<{ unimportedVersions: string[] }>();
|
||||
const props = defineProps<{ unimportedVersions: string[] }>();
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const hasDeleted = ref(false);
|
||||
|
||||
const canImport = computed(
|
||||
() => hasDeleted.value || props.unimportedVersions.length > 0,
|
||||
);
|
||||
|
||||
type GameAndVersions = GameModel & { versions: GameVersionModel[] };
|
||||
const game = defineModel<SerializeObject<GameAndVersions>>() as Ref<
|
||||
SerializeObject<GameAndVersions>
|
||||
@ -176,6 +178,7 @@ async function deleteVersion(versionName: string) {
|
||||
game.value.versions.findIndex((e) => e.versionName === versionName),
|
||||
1,
|
||||
);
|
||||
hasDeleted.value = true;
|
||||
} catch (e) {
|
||||
createModal(
|
||||
ModalType.Notification,
|
||||
|
||||
@ -18,8 +18,12 @@
|
||||
</i18n-t>
|
||||
</NuxtLink>
|
||||
|
||||
<DevOnly
|
||||
><h1 class="mt-3 text-sm text-gray-500">{{ $t("welcome") }}</h1>
|
||||
<DevOnly>
|
||||
<h1 class="mt-3 text-sm text-gray-500">{{ $t("welcome") }}</h1>
|
||||
</DevOnly>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ArrowTopRightOnSquareIcon } from "@heroicons/vue/24/outline";
|
||||
</script>
|
||||
|
||||
@ -21,7 +21,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@discordapp/twemoji": "^16.0.1",
|
||||
"@drop-oss/droplet": "1.6.0",
|
||||
"@drop-oss/droplet": "2.3.0",
|
||||
"@headlessui/vue": "^1.7.23",
|
||||
"@heroicons/vue": "^2.1.5",
|
||||
"@lobomfz/prismark": "0.0.3",
|
||||
|
||||
@ -47,7 +47,7 @@
|
||||
/>
|
||||
</div>
|
||||
<p class="mt-1 truncate text-sm text-zinc-400">
|
||||
{{ parseTaskLog(task.value.log.at(-1) ?? "").message }}
|
||||
{{ parseTaskLog(task.value.log.at(-1)).message }}
|
||||
</p>
|
||||
<NuxtLink
|
||||
type="button"
|
||||
@ -115,7 +115,7 @@
|
||||
{{ task.id }}
|
||||
</p>
|
||||
<p class="mt-1 truncate text-sm text-zinc-400">
|
||||
{{ parseTaskLog(task.log.at(-1) ?? "").message }}
|
||||
{{ parseTaskLog(task.log.at(-1)).message }}
|
||||
</p>
|
||||
<NuxtLink
|
||||
type="button"
|
||||
|
||||
@ -0,0 +1,15 @@
|
||||
/*
|
||||
Warnings:
|
||||
|
||||
- The primary key for the `Task` table will be changed. If it partially fails, the table could be left without primary key constraint.
|
||||
|
||||
*/
|
||||
-- DropIndex
|
||||
DROP INDEX "GameTag_name_idx";
|
||||
|
||||
-- AlterTable
|
||||
ALTER TABLE "Task" DROP CONSTRAINT "Task_pkey",
|
||||
ADD CONSTRAINT "Task_pkey" PRIMARY KEY ("id", "started");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "GameTag_name_idx" ON "GameTag" USING GIST ("name" gist_trgm_ops(siglen=32));
|
||||
@ -1,5 +1,5 @@
|
||||
model Task {
|
||||
id String @id
|
||||
id String
|
||||
taskGroup String
|
||||
name String
|
||||
|
||||
@ -12,4 +12,6 @@ model Task {
|
||||
log String[]
|
||||
|
||||
acls String[]
|
||||
|
||||
@@id([id, started])
|
||||
}
|
||||
|
||||
@ -2,6 +2,7 @@ import { type } from "arktype";
|
||||
import { readDropValidatedBody, throwingArktype } from "~/server/arktype";
|
||||
import contextManager from "~/server/internal/downloads/coordinator";
|
||||
import libraryManager from "~/server/internal/library";
|
||||
import { logger } from "~/server/internal/logging";
|
||||
|
||||
const GetChunk = type({
|
||||
context: "string",
|
||||
@ -58,13 +59,25 @@ export default defineEventHandler(async (h3) => {
|
||||
statusCode: 500,
|
||||
statusMessage: "Failed to create read stream",
|
||||
});
|
||||
let length = 0;
|
||||
await gameReadStream.pipeTo(
|
||||
new WritableStream({
|
||||
write(chunk) {
|
||||
h3.node.res.write(chunk);
|
||||
length += chunk.length;
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
if (length != file.end - file.start) {
|
||||
logger.warn(
|
||||
`failed to read enough from ${file.filename}. read ${length}, required: ${file.end - file.start}`,
|
||||
);
|
||||
throw createError({
|
||||
statusCode: 500,
|
||||
statusMessage: "Failed to read enough from stream.",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
await h3.node.res.end();
|
||||
|
||||
@ -15,12 +15,17 @@ import { GameNotFoundError, type LibraryProvider } from "./provider";
|
||||
import { logger } from "../logging";
|
||||
import type { GameModel } from "~/prisma/client/models";
|
||||
|
||||
export function createGameImportTaskId(libraryId: string, libraryPath: string) {
|
||||
return btoa(`import:${libraryId}:${libraryPath}`);
|
||||
}
|
||||
|
||||
export function createVersionImportTaskId(gameId: string, versionName: string) {
|
||||
return btoa(`import:${gameId}:${versionName}`);
|
||||
}
|
||||
|
||||
class LibraryManager {
|
||||
private libraries: Map<string, LibraryProvider<unknown>> = new Map();
|
||||
|
||||
private gameImportLocks: Map<string, Array<string>> = new Map(); // Library ID to Library Path
|
||||
private versionImportLocks: Map<string, Array<string>> = new Map(); // Game ID to Version Name
|
||||
|
||||
addLibrary(library: LibraryProvider<unknown>) {
|
||||
this.libraries.set(library.id(), library);
|
||||
}
|
||||
@ -58,12 +63,10 @@ class LibraryManager {
|
||||
|
||||
for (const [id, library] of this.libraries.entries()) {
|
||||
const providerGames = await library.listGames();
|
||||
const locks = this.gameImportLocks.get(id) ?? [];
|
||||
const providerUnimportedGames = providerGames.filter(
|
||||
(libraryPath) =>
|
||||
instanceGames[id] &&
|
||||
!instanceGames[id][libraryPath] &&
|
||||
!locks.includes(libraryPath),
|
||||
!instanceGames[id]?.[libraryPath] &&
|
||||
!taskHandler.hasTask(createGameImportTaskId(id, libraryPath)),
|
||||
);
|
||||
unimportedGames[id] = providerUnimportedGames;
|
||||
}
|
||||
@ -93,7 +96,7 @@ class LibraryManager {
|
||||
const unimportedVersions = versions.filter(
|
||||
(e) =>
|
||||
game.versions.findIndex((v) => v.versionName == e) == -1 &&
|
||||
!(this.versionImportLocks.get(game.id) ?? []).includes(e),
|
||||
!taskHandler.hasTask(createVersionImportTaskId(game.id, e)),
|
||||
);
|
||||
return unimportedVersions;
|
||||
} catch (e) {
|
||||
@ -177,7 +180,8 @@ class LibraryManager {
|
||||
for (const filename of files) {
|
||||
const basename = path.basename(filename);
|
||||
const dotLocation = filename.lastIndexOf(".");
|
||||
const ext = dotLocation == -1 ? "" : filename.slice(dotLocation);
|
||||
const ext =
|
||||
dotLocation == -1 ? "" : filename.slice(dotLocation).toLowerCase();
|
||||
for (const [platform, checkExts] of Object.entries(fileExts)) {
|
||||
for (const checkExt of checkExts) {
|
||||
if (checkExt != ext) continue;
|
||||
@ -215,70 +219,6 @@ class LibraryManager {
|
||||
}
|
||||
*/
|
||||
|
||||
/**
|
||||
* Locks the game so you can't be imported
|
||||
* @param libraryId
|
||||
* @param libraryPath
|
||||
*/
|
||||
async lockGame(libraryId: string, libraryPath: string) {
|
||||
let games = this.gameImportLocks.get(libraryId);
|
||||
if (!games) this.gameImportLocks.set(libraryId, (games = []));
|
||||
|
||||
if (!games.includes(libraryPath)) games.push(libraryPath);
|
||||
|
||||
this.gameImportLocks.set(libraryId, games);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unlocks the game, call once imported
|
||||
* @param libraryId
|
||||
* @param libraryPath
|
||||
*/
|
||||
async unlockGame(libraryId: string, libraryPath: string) {
|
||||
let games = this.gameImportLocks.get(libraryId);
|
||||
if (!games) this.gameImportLocks.set(libraryId, (games = []));
|
||||
|
||||
if (games.includes(libraryPath))
|
||||
games.splice(
|
||||
games.findIndex((e) => e === libraryPath),
|
||||
1,
|
||||
);
|
||||
|
||||
this.gameImportLocks.set(libraryId, games);
|
||||
}
|
||||
|
||||
/**
|
||||
* Locks a version so it can't be imported
|
||||
* @param gameId
|
||||
* @param versionName
|
||||
*/
|
||||
async lockVersion(gameId: string, versionName: string) {
|
||||
let versions = this.versionImportLocks.get(gameId);
|
||||
if (!versions) this.versionImportLocks.set(gameId, (versions = []));
|
||||
|
||||
if (!versions.includes(versionName)) versions.push(versionName);
|
||||
|
||||
this.versionImportLocks.set(gameId, versions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unlocks the version, call once imported
|
||||
* @param libraryId
|
||||
* @param libraryPath
|
||||
*/
|
||||
async unlockVersion(gameId: string, versionName: string) {
|
||||
let versions = this.versionImportLocks.get(gameId);
|
||||
if (!versions) this.versionImportLocks.set(gameId, (versions = []));
|
||||
|
||||
if (versions.includes(gameId))
|
||||
versions.splice(
|
||||
versions.findIndex((e) => e === versionName),
|
||||
1,
|
||||
);
|
||||
|
||||
this.versionImportLocks.set(gameId, versions);
|
||||
}
|
||||
|
||||
async importVersion(
|
||||
gameId: string,
|
||||
versionName: string,
|
||||
@ -295,7 +235,7 @@ class LibraryManager {
|
||||
umuId: string;
|
||||
},
|
||||
) {
|
||||
const taskId = `import:${gameId}:${versionName}`;
|
||||
const taskId = createVersionImportTaskId(gameId, versionName);
|
||||
|
||||
const platform = parsePlatform(metadata.platform);
|
||||
if (!platform) return undefined;
|
||||
@ -309,8 +249,6 @@ class LibraryManager {
|
||||
const library = this.libraries.get(game.libraryId);
|
||||
if (!library) return undefined;
|
||||
|
||||
await this.lockVersion(gameId, versionName);
|
||||
|
||||
taskHandler.create({
|
||||
id: taskId,
|
||||
taskGroup: "import:game",
|
||||
@ -387,9 +325,6 @@ class LibraryManager {
|
||||
|
||||
progress(100);
|
||||
},
|
||||
async finally() {
|
||||
await libraryManager.unlockVersion(gameId, versionName);
|
||||
},
|
||||
});
|
||||
|
||||
return taskId;
|
||||
@ -403,7 +338,7 @@ class LibraryManager {
|
||||
) {
|
||||
const library = this.libraries.get(libraryId);
|
||||
if (!library) return undefined;
|
||||
return library.peekFile(game, version, filename);
|
||||
return await library.peekFile(game, version, filename);
|
||||
}
|
||||
|
||||
async readFile(
|
||||
@ -415,7 +350,7 @@ class LibraryManager {
|
||||
) {
|
||||
const library = this.libraries.get(libraryId);
|
||||
if (!library) return undefined;
|
||||
return library.readFile(game, version, filename, options);
|
||||
return await library.readFile(game, version, filename, options);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -7,12 +7,14 @@ import {
|
||||
import { LibraryBackend } from "~/prisma/client/enums";
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
import droplet from "@drop-oss/droplet";
|
||||
import droplet, { DropletHandler } from "@drop-oss/droplet";
|
||||
|
||||
export const FilesystemProviderConfig = type({
|
||||
baseDir: "string",
|
||||
});
|
||||
|
||||
export const DROPLET_HANDLER = new DropletHandler();
|
||||
|
||||
export class FilesystemProvider
|
||||
implements LibraryProvider<typeof FilesystemProviderConfig.infer>
|
||||
{
|
||||
@ -57,7 +59,7 @@ export class FilesystemProvider
|
||||
const versionDirs = fs.readdirSync(gameDir);
|
||||
const validVersionDirs = versionDirs.filter((e) => {
|
||||
const fullDir = path.join(this.config.baseDir, game, e);
|
||||
return droplet.hasBackendForPath(fullDir);
|
||||
return DROPLET_HANDLER.hasBackendForPath(fullDir);
|
||||
});
|
||||
return validVersionDirs;
|
||||
}
|
||||
@ -65,7 +67,7 @@ export class FilesystemProvider
|
||||
async versionReaddir(game: string, version: string): Promise<string[]> {
|
||||
const versionDir = path.join(this.config.baseDir, game, version);
|
||||
if (!fs.existsSync(versionDir)) throw new VersionNotFoundError();
|
||||
return droplet.listFiles(versionDir);
|
||||
return DROPLET_HANDLER.listFiles(versionDir);
|
||||
}
|
||||
|
||||
async generateDropletManifest(
|
||||
@ -77,10 +79,16 @@ export class FilesystemProvider
|
||||
const versionDir = path.join(this.config.baseDir, game, version);
|
||||
if (!fs.existsSync(versionDir)) throw new VersionNotFoundError();
|
||||
const manifest = await new Promise<string>((r, j) =>
|
||||
droplet.generateManifest(versionDir, progress, log, (err, result) => {
|
||||
droplet.generateManifest(
|
||||
DROPLET_HANDLER,
|
||||
versionDir,
|
||||
progress,
|
||||
log,
|
||||
(err, result) => {
|
||||
if (err) return j(err);
|
||||
r(result);
|
||||
}),
|
||||
},
|
||||
),
|
||||
);
|
||||
return manifest;
|
||||
}
|
||||
@ -88,7 +96,7 @@ export class FilesystemProvider
|
||||
async peekFile(game: string, version: string, filename: string) {
|
||||
const filepath = path.join(this.config.baseDir, game, version);
|
||||
if (!fs.existsSync(filepath)) return undefined;
|
||||
const stat = droplet.peekFile(filepath, filename);
|
||||
const stat = DROPLET_HANDLER.peekFile(filepath, filename);
|
||||
return { size: Number(stat) };
|
||||
}
|
||||
|
||||
@ -100,13 +108,17 @@ export class FilesystemProvider
|
||||
) {
|
||||
const filepath = path.join(this.config.baseDir, game, version);
|
||||
if (!fs.existsSync(filepath)) return undefined;
|
||||
const stream = droplet.readFile(
|
||||
let stream;
|
||||
while (!(stream instanceof ReadableStream)) {
|
||||
const v = DROPLET_HANDLER.readFile(
|
||||
filepath,
|
||||
filename,
|
||||
options?.start ? BigInt(options.start) : undefined,
|
||||
options?.end ? BigInt(options.end) : undefined,
|
||||
);
|
||||
if (!stream) return undefined;
|
||||
if (!v) return undefined;
|
||||
stream = v.getStream() as ReadableStream<unknown>;
|
||||
}
|
||||
|
||||
return stream;
|
||||
}
|
||||
|
||||
@ -5,6 +5,7 @@ import { LibraryBackend } from "~/prisma/client/enums";
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
import droplet from "@drop-oss/droplet";
|
||||
import { DROPLET_HANDLER } from "./filesystem";
|
||||
|
||||
export const FlatFilesystemProviderConfig = type({
|
||||
baseDir: "string",
|
||||
@ -46,7 +47,7 @@ export class FlatFilesystemProvider
|
||||
const versionDirs = fs.readdirSync(this.config.baseDir);
|
||||
const validVersionDirs = versionDirs.filter((e) => {
|
||||
const fullDir = path.join(this.config.baseDir, e);
|
||||
return droplet.hasBackendForPath(fullDir);
|
||||
return DROPLET_HANDLER.hasBackendForPath(fullDir);
|
||||
});
|
||||
return validVersionDirs;
|
||||
}
|
||||
@ -63,7 +64,7 @@ export class FlatFilesystemProvider
|
||||
async versionReaddir(game: string, _version: string) {
|
||||
const versionDir = path.join(this.config.baseDir, game);
|
||||
if (!fs.existsSync(versionDir)) throw new VersionNotFoundError();
|
||||
return droplet.listFiles(versionDir);
|
||||
return DROPLET_HANDLER.listFiles(versionDir);
|
||||
}
|
||||
|
||||
async generateDropletManifest(
|
||||
@ -75,17 +76,23 @@ export class FlatFilesystemProvider
|
||||
const versionDir = path.join(this.config.baseDir, game);
|
||||
if (!fs.existsSync(versionDir)) throw new VersionNotFoundError();
|
||||
const manifest = await new Promise<string>((r, j) =>
|
||||
droplet.generateManifest(versionDir, progress, log, (err, result) => {
|
||||
droplet.generateManifest(
|
||||
DROPLET_HANDLER,
|
||||
versionDir,
|
||||
progress,
|
||||
log,
|
||||
(err, result) => {
|
||||
if (err) return j(err);
|
||||
r(result);
|
||||
}),
|
||||
},
|
||||
),
|
||||
);
|
||||
return manifest;
|
||||
}
|
||||
async peekFile(game: string, _version: string, filename: string) {
|
||||
const filepath = path.join(this.config.baseDir, game);
|
||||
if (!fs.existsSync(filepath)) return undefined;
|
||||
const stat = droplet.peekFile(filepath, filename);
|
||||
const stat = DROPLET_HANDLER.peekFile(filepath, filename);
|
||||
return { size: Number(stat) };
|
||||
}
|
||||
async readFile(
|
||||
@ -96,7 +103,7 @@ export class FlatFilesystemProvider
|
||||
) {
|
||||
const filepath = path.join(this.config.baseDir, game);
|
||||
if (!fs.existsSync(filepath)) return undefined;
|
||||
const stream = droplet.readFile(
|
||||
const stream = DROPLET_HANDLER.readFile(
|
||||
filepath,
|
||||
filename,
|
||||
options?.start ? BigInt(options.start) : undefined,
|
||||
@ -104,6 +111,6 @@ export class FlatFilesystemProvider
|
||||
);
|
||||
if (!stream) return undefined;
|
||||
|
||||
return stream;
|
||||
return stream.getStream();
|
||||
}
|
||||
}
|
||||
|
||||
@ -18,7 +18,7 @@ import taskHandler, { wrapTaskContext } from "../tasks";
|
||||
import { randomUUID } from "crypto";
|
||||
import { fuzzy } from "fast-fuzzy";
|
||||
import { logger } from "~/server/internal/logging";
|
||||
import libraryManager from "../library";
|
||||
import { createGameImportTaskId } from "../library";
|
||||
import type { GameTagModel } from "~/prisma/client/models";
|
||||
|
||||
export class MissingMetadataProviderConfig extends Error {
|
||||
@ -185,11 +185,9 @@ export class MetadataHandler {
|
||||
});
|
||||
if (existing) return undefined;
|
||||
|
||||
await libraryManager.lockGame(libraryId, libraryPath);
|
||||
|
||||
const gameId = randomUUID();
|
||||
|
||||
const taskId = `import:${gameId}`;
|
||||
const taskId = createGameImportTaskId(libraryId, libraryPath);
|
||||
await taskHandler.create({
|
||||
name: `Import game "${result.name}" (${libraryPath})`,
|
||||
id: taskId,
|
||||
@ -280,9 +278,6 @@ export class MetadataHandler {
|
||||
logger.info(`Finished game import.`);
|
||||
progress(100);
|
||||
},
|
||||
async finally() {
|
||||
await libraryManager.unlockGame(libraryId, libraryPath);
|
||||
},
|
||||
});
|
||||
|
||||
return taskId;
|
||||
|
||||
@ -73,6 +73,8 @@ class TaskHandler {
|
||||
}
|
||||
|
||||
async create(task: Task) {
|
||||
if (this.hasTask(task.id)) throw new Error("Task with ID already exists.");
|
||||
|
||||
let updateCollectTimeout: NodeJS.Timeout | undefined;
|
||||
let updateCollectResolves: Array<(value: unknown) => void> = [];
|
||||
let logOffset: number = 0;
|
||||
@ -206,8 +208,6 @@ class TaskHandler {
|
||||
};
|
||||
}
|
||||
|
||||
if (task.finally) await task.finally();
|
||||
|
||||
taskEntry.endTime = new Date().toISOString();
|
||||
await updateAllClients();
|
||||
|
||||
@ -247,7 +247,10 @@ class TaskHandler {
|
||||
) {
|
||||
const task =
|
||||
this.taskPool.get(taskId) ??
|
||||
(await prisma.task.findUnique({ where: { id: taskId } }));
|
||||
(await prisma.task.findFirst({
|
||||
where: { id: taskId },
|
||||
orderBy: { started: "desc" },
|
||||
}));
|
||||
if (!task) {
|
||||
peer.send(
|
||||
`error/${taskId}/Unknown task/Drop couldn't find the task you're looking for.`,
|
||||
@ -324,6 +327,10 @@ class TaskHandler {
|
||||
.toArray();
|
||||
}
|
||||
|
||||
hasTask(id: string) {
|
||||
return this.taskPool.has(id);
|
||||
}
|
||||
|
||||
dailyTasks() {
|
||||
return this.dailyScheduledTasks;
|
||||
}
|
||||
@ -429,7 +436,6 @@ export interface Task {
|
||||
taskGroup: TaskGroup;
|
||||
name: string;
|
||||
run: (context: TaskRunContext) => Promise<void>;
|
||||
finally?: () => Promise<void> | void;
|
||||
acls: GlobalACL[];
|
||||
}
|
||||
|
||||
|
||||
@ -1,6 +1,9 @@
|
||||
import type { TaskLog } from "~/server/internal/tasks";
|
||||
|
||||
export function parseTaskLog(logStr: string): typeof TaskLog.infer {
|
||||
export function parseTaskLog(
|
||||
logStr?: string | undefined,
|
||||
): typeof TaskLog.infer {
|
||||
if (!logStr) return { message: "", timestamp: "" };
|
||||
const log = JSON.parse(logStr);
|
||||
|
||||
return {
|
||||
|
||||
108
yarn.lock
108
yarn.lock
@ -342,71 +342,71 @@
|
||||
jsonfile "^5.0.0"
|
||||
universalify "^0.1.2"
|
||||
|
||||
"@drop-oss/droplet-darwin-arm64@1.6.0":
|
||||
version "1.6.0"
|
||||
resolved "https://registry.yarnpkg.com/@drop-oss/droplet-darwin-arm64/-/droplet-darwin-arm64-1.6.0.tgz#9697e38c46b02192e8e180b7deaaa20a389a9b0d"
|
||||
integrity sha512-EqTx+Mk5SHP17n19r5coacUDd7lklT4opJ2keNQyGsQjrcf+9FeCX1O5Y+PGIjpQK6UkAVdnBqM+jR7NeFmkAQ==
|
||||
"@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-universal@1.6.0":
|
||||
version "1.6.0"
|
||||
resolved "https://registry.yarnpkg.com/@drop-oss/droplet-darwin-universal/-/droplet-darwin-universal-1.6.0.tgz#2f780416052ac7d1752b0a7828dc3ef9d1789c92"
|
||||
integrity sha512-TxVpoVDI9aGuBCHA8HktbrIkS/C1gu5laM5+ZbIZkXnIUpTicJIbHRyneXJ4MLnW703gUbW8LTISgm7xKwZJsg==
|
||||
"@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-x64@1.6.0":
|
||||
version "1.6.0"
|
||||
resolved "https://registry.yarnpkg.com/@drop-oss/droplet-darwin-x64/-/droplet-darwin-x64-1.6.0.tgz#5d6a3c596eca706e40b35cdf49ada65e59c51b8d"
|
||||
integrity sha512-V/1xh4s16AmesDOEHiQ4vj9XQq6AWmXRY5RQf4RKBQqkxsHzmQoa37CTLK25Wf9OUoiJFGpnjViqKOFG4y5Q+g==
|
||||
"@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-linux-arm64-gnu@1.6.0":
|
||||
version "1.6.0"
|
||||
resolved "https://registry.yarnpkg.com/@drop-oss/droplet-linux-arm64-gnu/-/droplet-linux-arm64-gnu-1.6.0.tgz#265d5e7854c4c61081b8fd74b3e8305ea2c7b5ac"
|
||||
integrity sha512-WjaRl9VW0qE+YkOCaYuNIXzyBbps2lopbpeXELZ9/f/1jBfzfmIe4m6C2hMy4NWUcWnrBbiVTEjnq2cHj/TaBA==
|
||||
"@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-musl@1.6.0":
|
||||
version "1.6.0"
|
||||
resolved "https://registry.yarnpkg.com/@drop-oss/droplet-linux-arm64-musl/-/droplet-linux-arm64-musl-1.6.0.tgz#7126e194e5ef9018d61ef7dd0cc3af80734e00e2"
|
||||
integrity sha512-B8KoBYk0YVUZIL+etCcOc99NuoBcTm6KDOIQkN9SHWC4YLRu8um3w8DHzv4VV3arUnEGjyDHuraaOSONfP6NqA==
|
||||
"@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-riscv64-gnu@1.6.0":
|
||||
version "1.6.0"
|
||||
resolved "https://registry.yarnpkg.com/@drop-oss/droplet-linux-riscv64-gnu/-/droplet-linux-riscv64-gnu-1.6.0.tgz#40d060eafaca08b47a468950d7dc5ec4f1fb2a5a"
|
||||
integrity sha512-nbNr/38EX8Mjj20+paohlOD35apmaNKZan4OO97KOwvq5oZ/pXbkjOGC0zkpsizyxbwKx7Jl4Se7teRVPWWVWw==
|
||||
"@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-x64-gnu@1.6.0":
|
||||
version "1.6.0"
|
||||
resolved "https://registry.yarnpkg.com/@drop-oss/droplet-linux-x64-gnu/-/droplet-linux-x64-gnu-1.6.0.tgz#c3a8408644194e59ac2110229e9a99885b3bc533"
|
||||
integrity sha512-n/zA1ftqGey5yQK/1HiCok3MaLA4stVTzQEuRUzyq8BQ1BC6TmKCgdFnI4Q3tuGm3/Mz2CCbfbHY4bYwND9qOQ==
|
||||
"@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-musl@1.6.0":
|
||||
version "1.6.0"
|
||||
resolved "https://registry.yarnpkg.com/@drop-oss/droplet-linux-x64-musl/-/droplet-linux-x64-musl-1.6.0.tgz#206b5c85b02b7fdf53bc5f0cdf68a9d9a7d501cd"
|
||||
integrity sha512-egZWqKK1+vHoVKNuMle2Kn8WbbJ7Y9WJScUNXjF8hdUDNo9eHwJT/DfnA+BhvFQuJXkU58vwv6MqZ5VLdOsGiA==
|
||||
"@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-win32-arm64-msvc@1.6.0":
|
||||
version "1.6.0"
|
||||
resolved "https://registry.yarnpkg.com/@drop-oss/droplet-win32-arm64-msvc/-/droplet-win32-arm64-msvc-1.6.0.tgz#fbb0387536f5b2a88f03877d730f7f863646ce08"
|
||||
integrity sha512-AwGYHae8ZmQV2QGp+3B0DhsBdYynrZ4AS1xNc+U1tXt5CiMp9wLLM/4a+WySYHX7XrEo8pKmRRa0I8QdAdxk5A==
|
||||
"@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-x64-msvc@1.6.0":
|
||||
version "1.6.0"
|
||||
resolved "https://registry.yarnpkg.com/@drop-oss/droplet-win32-x64-msvc/-/droplet-win32-x64-msvc-1.6.0.tgz#600058775641b4c5c051291e5a13135aa1ae28bb"
|
||||
integrity sha512-Viz+J87rF7I++nLpPBvdhsjUQAHivA6wSHrBXa+4MwIymUvlQXcvNReFqzObRH4eiuiY4e3s3t9X7+paqd847Q==
|
||||
"@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@1.6.0":
|
||||
version "1.6.0"
|
||||
resolved "https://registry.yarnpkg.com/@drop-oss/droplet/-/droplet-1.6.0.tgz#b6aa382dc5df494c4233a2bd8f19721878edad71"
|
||||
integrity sha512-nTZvLo+GFLlpxgFlObP4zitVctz02bRD3ZSVDiMv7jXxYK0V/GktITJFcKK0J87ZRxneoFHYbLs1lH3MFYoSIw==
|
||||
"@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==
|
||||
optionalDependencies:
|
||||
"@drop-oss/droplet-darwin-arm64" "1.6.0"
|
||||
"@drop-oss/droplet-darwin-universal" "1.6.0"
|
||||
"@drop-oss/droplet-darwin-x64" "1.6.0"
|
||||
"@drop-oss/droplet-linux-arm64-gnu" "1.6.0"
|
||||
"@drop-oss/droplet-linux-arm64-musl" "1.6.0"
|
||||
"@drop-oss/droplet-linux-riscv64-gnu" "1.6.0"
|
||||
"@drop-oss/droplet-linux-x64-gnu" "1.6.0"
|
||||
"@drop-oss/droplet-linux-x64-musl" "1.6.0"
|
||||
"@drop-oss/droplet-win32-arm64-msvc" "1.6.0"
|
||||
"@drop-oss/droplet-win32-x64-msvc" "1.6.0"
|
||||
"@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"
|
||||
|
||||
"@emnapi/core@^1.4.3":
|
||||
version "1.4.5"
|
||||
|
||||
Reference in New Issue
Block a user