diff --git a/app/package.json b/app/package.json
index cfc4c5f..7a48be6 100644
--- a/app/package.json
+++ b/app/package.json
@@ -48,6 +48,7 @@
"@trpc/react-query": "^10.26.0",
"@trpc/server": "^10.26.0",
"@vercel/og": "^0.5.9",
+ "archiver": "^6.0.0",
"ast-types": "^0.14.2",
"chroma-js": "^2.4.2",
"concurrently": "^8.2.0",
@@ -99,6 +100,7 @@
"replicate": "^0.12.3",
"socket.io": "^4.7.1",
"socket.io-client": "^4.7.1",
+ "stream-buffers": "^3.0.2",
"superjson": "1.12.2",
"trpc-openapi": "^1.2.0",
"tsx": "^3.12.7",
@@ -111,6 +113,7 @@
},
"devDependencies": {
"@openapi-contrib/openapi-schema-to-json-schema": "^4.0.5",
+ "@types/archiver": "^5.3.2",
"@types/babel__core": "^7.20.1",
"@types/babel__standalone": "^7.1.4",
"@types/chroma-js": "^2.4.0",
@@ -127,6 +130,7 @@
"@types/react": "^18.2.6",
"@types/react-dom": "^18.2.4",
"@types/react-syntax-highlighter": "^15.5.7",
+ "@types/stream-buffers": "^3.0.4",
"@types/uuid": "^9.0.2",
"@typescript-eslint/eslint-plugin": "^5.59.6",
"@typescript-eslint/parser": "^5.59.6",
diff --git a/app/src/components/requestLogs/ExportButton.tsx b/app/src/components/requestLogs/ExportButton.tsx
new file mode 100644
index 0000000..512948b
--- /dev/null
+++ b/app/src/components/requestLogs/ExportButton.tsx
@@ -0,0 +1,197 @@
+import { useState, useEffect } from "react";
+import {
+ Modal,
+ ModalOverlay,
+ ModalContent,
+ ModalHeader,
+ ModalCloseButton,
+ ModalBody,
+ ModalFooter,
+ HStack,
+ VStack,
+ Icon,
+ Text,
+ Button,
+ Checkbox,
+ NumberInput,
+ NumberInputField,
+ NumberInputStepper,
+ NumberIncrementStepper,
+ NumberDecrementStepper,
+ Collapse,
+ useDisclosure,
+ type UseDisclosureReturn,
+} from "@chakra-ui/react";
+import { BiExport } from "react-icons/bi";
+
+import { useHandledAsyncCallback } from "~/utils/hooks";
+import { api } from "~/utils/api";
+import { useAppStore } from "~/state/store";
+import ActionButton from "./ActionButton";
+import InputDropdown from "../InputDropdown";
+import { FiChevronUp, FiChevronDown } from "react-icons/fi";
+
+const SUPPORTED_EXPORT_FORMATS = ["alpaca-finetune", "openai-fine-tune", "unformatted"];
+
+const ExportButton = () => {
+ const selectedLogIds = useAppStore((s) => s.selectedLogs.selectedLogIds);
+
+ const disclosure = useDisclosure();
+
+ return (
+ <>
+
+
+ >
+ );
+};
+
+export default ExportButton;
+
+const ExportLogsModal = ({ disclosure }: { disclosure: UseDisclosureReturn }) => {
+ const selectedProjectId = useAppStore((s) => s.selectedProjectId);
+ const selectedLogIds = useAppStore((s) => s.selectedLogs.selectedLogIds);
+ const clearSelectedLogIds = useAppStore((s) => s.selectedLogs.clearSelectedLogIds);
+
+ const [selectedExportFormat, setSelectedExportFormat] = useState(SUPPORTED_EXPORT_FORMATS[0]);
+ const [testingSplit, setTestingSplit] = useState(10);
+ const [removeDuplicates, setRemoveDuplicates] = useState(true);
+ const [showAdvancedOptions, setShowAdvancedOptions] = useState(false);
+
+ useEffect(() => {
+ if (disclosure.isOpen) {
+ setSelectedExportFormat(SUPPORTED_EXPORT_FORMATS[0]);
+ setTestingSplit(10);
+ setRemoveDuplicates(true);
+ }
+ }, [disclosure.isOpen]);
+
+ const exportLogsMutation = api.loggedCalls.export.useMutation();
+
+ const [exportLogs, exportInProgress] = useHandledAsyncCallback(async () => {
+ if (!selectedProjectId || !selectedLogIds.size || !testingSplit || !selectedExportFormat)
+ return;
+ const response = await exportLogsMutation.mutateAsync({
+ projectId: selectedProjectId,
+ selectedLogIds: Array.from(selectedLogIds),
+ testingSplit,
+ selectedExportFormat,
+ removeDuplicates,
+ });
+
+ const dataUrl = `data:application/pdf;base64,${response}`;
+ const blob = await fetch(dataUrl).then((res) => res.blob());
+ const url = URL.createObjectURL(blob);
+ const a = document.createElement("a");
+
+ a.href = url;
+ a.download = `data.zip`;
+ document.body.appendChild(a);
+ a.click();
+ document.body.removeChild(a);
+
+ disclosure.onClose();
+ clearSelectedLogIds();
+ }, [
+ exportLogsMutation,
+ selectedProjectId,
+ selectedLogIds,
+ testingSplit,
+ selectedExportFormat,
+ removeDuplicates,
+ ]);
+
+ return (
+
+
+
+
+
+
+ Export Logs
+
+
+
+
+
+
+ We'll export the {selectedLogIds.size} logs you have selected in the format of
+ your choice.
+
+
+
+
+ Format:
+
+ setSelectedExportFormat(option)}
+ inputGroupProps={{ w: 48 }}
+ />
+
+
+
+ Testing Split:
+
+
+ setTestingSplit(num)}
+ min={1}
+ max={100}
+ w={48}
+ >
+
+
+
+
+
+
+
+
+
+
+
+
+
+ setRemoveDuplicates(e.target.checked)}
+ >
+ Remove duplicates? (recommended)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+};
diff --git a/app/src/pages/request-logs/index.tsx b/app/src/pages/request-logs/index.tsx
index 1cd5a4b..4799361 100644
--- a/app/src/pages/request-logs/index.tsx
+++ b/app/src/pages/request-logs/index.tsx
@@ -11,6 +11,7 @@ import { FiFilter } from "react-icons/fi";
import LogFilters from "~/components/requestLogs/LogFilters/LogFilters";
import ColumnVisiblityDropdown from "~/components/requestLogs/ColumnVisiblityDropdown";
import FineTuneButton from "~/components/requestLogs/FineTuneButton";
+import ExportButton from "~/components/requestLogs/ExportButton";
export default function LoggedCalls() {
const selectedLogIds = useAppStore((s) => s.selectedLogs.selectedLogIds);
@@ -35,6 +36,7 @@ export default function LoggedCalls() {
icon={RiFlaskLine}
isDisabled={selectedLogIds.size === 0}
/>
+
{
diff --git a/app/src/server/api/routers/loggedCalls.router.ts b/app/src/server/api/routers/loggedCalls.router.ts
index 4798b1f..1c0281b 100644
--- a/app/src/server/api/routers/loggedCalls.router.ts
+++ b/app/src/server/api/routers/loggedCalls.router.ts
@@ -1,11 +1,16 @@
import { z } from "zod";
import { type Expression, type SqlBool, sql, type RawBuilder } from "kysely";
import { jsonArrayFrom } from "kysely/helpers/postgres";
+import archiver from "archiver";
+import { WritableStreamBuffer } from "stream-buffers";
+import { type JsonValue } from "type-fest";
+import { shuffle } from "lodash-es";
import { createTRPCRouter, protectedProcedure } from "~/server/api/trpc";
import { kysely, prisma } from "~/server/db";
import { comparators, defaultFilterableFields } from "~/state/logFiltersSlice";
import { requireCanViewProject } from "~/utils/accessControl";
+import hashObject from "~/server/utils/hashObject";
// create comparator type based off of comparators
const comparatorToSqlExpression = (comparator: (typeof comparators)[number], value: string) => {
@@ -180,4 +185,101 @@ export const loggedCallsRouter = createTRPCRouter({
return tags.map((tag) => tag.name);
}),
+ export: protectedProcedure
+ .input(
+ z.object({
+ projectId: z.string(),
+ selectedLogIds: z.string().array(),
+ testingSplit: z.number(),
+ selectedExportFormat: z.string(),
+ removeDuplicates: z.boolean(),
+ }),
+ )
+ .mutation(async ({ input, ctx }) => {
+ await requireCanViewProject(input.projectId, ctx);
+
+ // Fetch the real data using Prisma
+ const loggedCallsFromDb = await ctx.prisma.loggedCallModelResponse.findMany({
+ where: {
+ originalLoggedCall: {
+ projectId: input.projectId,
+ id: { in: input.selectedLogIds },
+ },
+ },
+ });
+
+ // Convert the database data into the desired format
+ let formattedLoggedCalls: { input: JsonValue[]; output: JsonValue }[] = loggedCallsFromDb.map(
+ (call) => ({
+ input: (call.reqPayload as unknown as Record).messages as JsonValue[],
+ output: (call.respPayload as unknown as { choices: { message: unknown }[] }).choices[0]
+ ?.message as JsonValue,
+ }),
+ );
+
+ if (input.removeDuplicates) {
+ const deduplicatedLoggedCalls = [];
+ const loggedCallHashSet = new Set();
+ for (const loggedCall of formattedLoggedCalls) {
+ const loggedCallHash = hashObject(loggedCall);
+ if (!loggedCallHashSet.has(loggedCallHash)) {
+ loggedCallHashSet.add(loggedCallHash);
+ deduplicatedLoggedCalls.push(loggedCall);
+ }
+ }
+ formattedLoggedCalls = deduplicatedLoggedCalls;
+ }
+
+ // Remove duplicate messages from input
+ const inputMessageHashMap = new Map();
+ for (const loggedCall of formattedLoggedCalls) {
+ for (const message of loggedCall.input) {
+ const hash = hashObject(message);
+ if (inputMessageHashMap.has(hash)) {
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+ inputMessageHashMap.set(hash, inputMessageHashMap.get(hash)! + 1);
+ } else {
+ inputMessageHashMap.set(hash, 0);
+ }
+ }
+ }
+ for (const loggedCall of formattedLoggedCalls) {
+ loggedCall.input = loggedCall.input.filter((message) => {
+ const hash = hashObject(message);
+ // If the same message appears in a single input multiple times, there is some danger of
+ // it being removed from all logged calls. This is enough of an edge case that we don't
+ // need to worry about it for now.
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+ return inputMessageHashMap.get(hash)! < formattedLoggedCalls.length;
+ });
+ }
+
+ // Stringify inputs and outputs
+ const stringifiedLoggedCalls = shuffle(formattedLoggedCalls).map((loggedCall) => ({
+ input: JSON.stringify(loggedCall.input),
+ output: JSON.stringify(loggedCall.output),
+ }));
+
+ const splitIndex = Math.floor((stringifiedLoggedCalls.length * input.testingSplit) / 100);
+
+ const testingData = stringifiedLoggedCalls.slice(0, splitIndex);
+ const trainingData = stringifiedLoggedCalls.slice(splitIndex);
+
+ // Convert arrays to JSONL format
+ const trainingDataJSONL = trainingData.map((item) => JSON.stringify(item)).join("\n");
+ const testingDataJSONL = testingData.map((item) => JSON.stringify(item)).join("\n");
+
+ const output = new WritableStreamBuffer();
+ const archive = archiver("zip");
+
+ archive.pipe(output);
+ archive.append(trainingDataJSONL, { name: "train.jsonl" });
+ archive.append(testingDataJSONL, { name: "test.jsonl" });
+ await archive.finalize();
+
+ // Convert buffer to base64
+ const base64 = output.getContents().toString("base64");
+
+ return base64;
+ }),
});
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 73723e0..4368004 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -80,6 +80,9 @@ importers:
'@vercel/og':
specifier: ^0.5.9
version: 0.5.9
+ archiver:
+ specifier: ^6.0.0
+ version: 6.0.0
ast-types:
specifier: ^0.14.2
version: 0.14.2
@@ -233,6 +236,9 @@ importers:
socket.io-client:
specifier: ^4.7.1
version: 4.7.1
+ stream-buffers:
+ specifier: ^3.0.2
+ version: 3.0.2
superjson:
specifier: 1.12.2
version: 1.12.2
@@ -264,6 +270,9 @@ importers:
'@openapi-contrib/openapi-schema-to-json-schema':
specifier: ^4.0.5
version: 4.0.5
+ '@types/archiver':
+ specifier: ^5.3.2
+ version: 5.3.2
'@types/babel__core':
specifier: ^7.20.1
version: 7.20.1
@@ -312,6 +321,9 @@ importers:
'@types/react-syntax-highlighter':
specifier: ^15.5.7
version: 15.5.7
+ '@types/stream-buffers':
+ specifier: ^3.0.4
+ version: 3.0.4
'@types/uuid':
specifier: ^9.0.2
version: 9.0.2
@@ -2950,6 +2962,12 @@ packages:
resolution: {integrity: sha512-+Wt0NFAeflVSNiUnHIDNN3C8jP7XIRmYrcgJ6IsAnm0lK4p/FkpCpeu1aig5qxrgZx30PHNDLZ/3FttVSEW2aQ==}
dev: false
+ /@types/archiver@5.3.2:
+ resolution: {integrity: sha512-IctHreBuWE5dvBDz/0WeKtyVKVRs4h75IblxOACL92wU66v+HGAfEYAOyXkOFphvRJMhuXdI9huDXpX0FC6lCw==}
+ dependencies:
+ '@types/readdir-glob': 1.1.1
+ dev: true
+
/@types/babel__core@7.20.1:
resolution: {integrity: sha512-aACu/U/omhdk15O4Nfb+fHgH/z3QsfQzpnvRZhYhThms83ZnAOZz7zZAWO7mn2yyNQaA4xTO8GLK3uqFU4bYYw==}
dependencies:
@@ -3265,6 +3283,12 @@ packages:
'@types/scheduler': 0.16.3
csstype: 3.1.2
+ /@types/readdir-glob@1.1.1:
+ resolution: {integrity: sha512-ImM6TmoF8bgOwvehGviEj3tRdRBbQujr1N+0ypaln/GWjaerOB26jb93vsRHmdMtvVQZQebOlqt2HROark87mQ==}
+ dependencies:
+ '@types/node': 20.4.10
+ dev: true
+
/@types/request@2.48.8:
resolution: {integrity: sha512-whjk1EDJPcAR2kYHRbFl/lKeeKYTi05A15K9bnLInCVroNDCtXce57xKdI0/rQaA3K+6q0eFyUBPmqfSndUZdQ==}
dependencies:
@@ -3296,6 +3320,12 @@ packages:
'@types/node': 20.4.10
dev: true
+ /@types/stream-buffers@3.0.4:
+ resolution: {integrity: sha512-qU/K1tb2yUdhXkLIATzsIPwbtX6BpZk0l3dPW6xqWyhfzzM1ECaQ/8faEnu3CNraLiQ9LHyQQPBGp7N9Fbs25w==}
+ dependencies:
+ '@types/node': 20.4.10
+ dev: true
+
/@types/tough-cookie@4.0.2:
resolution: {integrity: sha512-Q5vtl1W5ue16D+nIaW8JWebSSraJVlK+EthKn7e7UcD4KWsaSJ8BqGPXNaPghgtcn/fhvrN17Tv8ksUsQpiplw==}
dev: false
@@ -3699,6 +3729,51 @@ packages:
picomatch: 2.3.1
dev: false
+ /archiver-utils@2.1.0:
+ resolution: {integrity: sha512-bEL/yUb/fNNiNTuUz979Z0Yg5L+LzLxGJz8x79lYmR54fmTIb6ob/hNQgkQnIUDWIFjZVQwl9Xs356I6BAMHfw==}
+ engines: {node: '>= 6'}
+ dependencies:
+ glob: 7.2.3
+ graceful-fs: 4.2.11
+ lazystream: 1.0.1
+ lodash.defaults: 4.2.0
+ lodash.difference: 4.5.0
+ lodash.flatten: 4.4.0
+ lodash.isplainobject: 4.0.6
+ lodash.union: 4.6.0
+ normalize-path: 3.0.0
+ readable-stream: 2.3.8
+ dev: false
+
+ /archiver-utils@3.0.3:
+ resolution: {integrity: sha512-fXzpEZTKgBJMWy0eUT0/332CAQnJ27OJd7sGcvNZzxS2Yzg7iITivMhXOm+zUTO4vT8ZqlPCqiaLPmB8qWhWRA==}
+ engines: {node: '>= 10'}
+ dependencies:
+ glob: 7.2.3
+ graceful-fs: 4.2.11
+ lazystream: 1.0.1
+ lodash.defaults: 4.2.0
+ lodash.difference: 4.5.0
+ lodash.flatten: 4.4.0
+ lodash.isplainobject: 4.0.6
+ lodash.union: 4.6.0
+ normalize-path: 3.0.0
+ readable-stream: 3.6.2
+ dev: false
+
+ /archiver@6.0.0:
+ resolution: {integrity: sha512-EPGa+bYaxaMiCT8DCbEDqFz8IjeBSExrJzyUOJx2FBkFJ/OZzJuso3lMSk901M50gMqXxTQcumlGajOFlXhVhw==}
+ engines: {node: '>= 12.0.0'}
+ dependencies:
+ archiver-utils: 3.0.3
+ async: 3.2.4
+ buffer-crc32: 0.2.13
+ readable-stream: 3.6.2
+ readdir-glob: 1.1.3
+ tar-stream: 2.2.0
+ zip-stream: 4.1.0
+ dev: false
+
/argparse@2.0.1:
resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}
@@ -3837,6 +3912,10 @@ packages:
tslib: 2.6.1
dev: false
+ /async@3.2.4:
+ resolution: {integrity: sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==}
+ dev: false
+
/asynckit@0.4.0:
resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==}
@@ -3956,6 +4035,14 @@ packages:
engines: {node: '>=8'}
dev: false
+ /bl@4.1.0:
+ resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==}
+ dependencies:
+ buffer: 5.7.1
+ inherits: 2.0.4
+ readable-stream: 3.6.2
+ dev: false
+
/bluebird@3.7.2:
resolution: {integrity: sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==}
dev: false
@@ -4008,6 +4095,10 @@ packages:
node-releases: 2.0.13
update-browserslist-db: 1.0.11(browserslist@4.21.10)
+ /buffer-crc32@0.2.13:
+ resolution: {integrity: sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==}
+ dev: false
+
/buffer-from@0.1.2:
resolution: {integrity: sha512-RiWIenusJsmI2KcvqQABB83tLxCByE3upSP8QU3rJDMVFGPWLvPQJt/O1Su9moRWeH7d+Q2HYb68f6+v+tw2vg==}
dev: false
@@ -4020,6 +4111,13 @@ packages:
engines: {node: '>=4'}
dev: false
+ /buffer@5.7.1:
+ resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==}
+ dependencies:
+ base64-js: 1.5.1
+ ieee754: 1.2.1
+ dev: false
+
/busboy@1.6.0:
resolution: {integrity: sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==}
engines: {node: '>=10.16.0'}
@@ -4246,6 +4344,16 @@ packages:
resolution: {integrity: sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==}
dev: false
+ /compress-commons@4.1.1:
+ resolution: {integrity: sha512-QLdDLCKNV2dtoTorqgxngQCMA+gWXkM/Nwu7FpeBhk/RdkzimqC3jueb/FDmaZeXh+uby1jkBqE3xArsLBE5wQ==}
+ engines: {node: '>= 10'}
+ dependencies:
+ buffer-crc32: 0.2.13
+ crc32-stream: 4.0.2
+ normalize-path: 3.0.0
+ readable-stream: 3.6.2
+ dev: false
+
/compute-scroll-into-view@1.0.20:
resolution: {integrity: sha512-UCB0ioiyj8CRjtrvaceBLqqhZCVP+1B8+NWQhmdsm0VXOJtobBCf1dBQmebCCo34qZmUwZfIH2MZLqNHazrfjg==}
dev: false
@@ -4353,6 +4461,20 @@ packages:
yaml: 1.10.2
dev: false
+ /crc-32@1.2.2:
+ resolution: {integrity: sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==}
+ engines: {node: '>=0.8'}
+ hasBin: true
+ dev: false
+
+ /crc32-stream@4.0.2:
+ resolution: {integrity: sha512-DxFZ/Hk473b/muq1VJ///PMNLj0ZMnzye9thBpmjpJKCc5eMgB95aK8zCGrGfQ90cWo561Te6HK9D+j4KPdM6w==}
+ engines: {node: '>= 10'}
+ dependencies:
+ crc-32: 1.2.2
+ readable-stream: 3.6.2
+ dev: false
+
/create-emotion@10.0.27:
resolution: {integrity: sha512-fIK73w82HPPn/RsAij7+Zt8eCE8SptcJ3WoRMfxMtjteYxud8GDTKKld7MYwAX2TVhrw29uR1N/bVGxeStHILg==}
dependencies:
@@ -4735,6 +4857,12 @@ packages:
iconv-lite: 0.6.3
dev: false
+ /end-of-stream@1.4.4:
+ resolution: {integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==}
+ dependencies:
+ once: 1.4.0
+ dev: false
+
/engine.io-client@6.5.2:
resolution: {integrity: sha512-CQZqbrpEYnrpGqC07a9dJDz4gePZUgTPMU3NKJPSeQOyw27Tst4Pl3FemKoFGAlHzgZmKjoRmiJvbWfhCXUlIg==}
dependencies:
@@ -5577,6 +5705,10 @@ packages:
engines: {node: '>= 0.6'}
dev: false
+ /fs-constants@1.0.0:
+ resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==}
+ dev: false
+
/fs-extra@11.1.1:
resolution: {integrity: sha512-MGIE4HOvQCeUCzmlHs0vXpih4ysz4wg9qiSAu6cd42lVwPbTM1TjV7RusoyQqMmk/95gdQZX72u+YW+c3eEpFQ==}
engines: {node: '>=14.14'}
@@ -5969,6 +6101,10 @@ packages:
safer-buffer: 2.1.2
dev: false
+ /ieee754@1.2.1:
+ resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==}
+ dev: false
+
/ignore@5.2.4:
resolution: {integrity: sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==}
engines: {node: '>= 4'}
@@ -6434,6 +6570,13 @@ packages:
language-subtag-registry: 0.3.22
dev: true
+ /lazystream@1.0.1:
+ resolution: {integrity: sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==}
+ engines: {node: '>= 0.6.3'}
+ dependencies:
+ readable-stream: 2.3.8
+ dev: false
+
/levn@0.4.1:
resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==}
engines: {node: '>= 0.8.0'}
@@ -6502,6 +6645,22 @@ packages:
resolution: {integrity: sha512-/u14pXGviLaweY5JI0IUzgzF2J6Ne8INyzAZjImcryjgkZ+ebruBxy2/JaOOkTqScddcYtakjhSaeemV8lR0tA==}
dev: false
+ /lodash.defaults@4.2.0:
+ resolution: {integrity: sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==}
+ dev: false
+
+ /lodash.difference@4.5.0:
+ resolution: {integrity: sha512-dS2j+W26TQ7taQBGN8Lbbq04ssV3emRw4NY58WErlTO29pIqS0HmoT5aJ9+TUQ1N3G+JOZSji4eugsWwGp9yPA==}
+ dev: false
+
+ /lodash.flatten@4.4.0:
+ resolution: {integrity: sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g==}
+ dev: false
+
+ /lodash.isplainobject@4.0.6:
+ resolution: {integrity: sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==}
+ dev: false
+
/lodash.merge@4.6.2:
resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==}
dev: true
@@ -6510,6 +6669,10 @@ packages:
resolution: {integrity: sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==}
dev: false
+ /lodash.union@4.6.0:
+ resolution: {integrity: sha512-c4pB2CdGrGdjMKYLA+XiRDO7Y0PRQbm/Gzg8qMj+QH+pFVAoTp5sBpO0odL3FjoPCGjK96p6qsP+yQoiLoOBcw==}
+ dev: false
+
/lodash@4.17.21:
resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==}
dev: false
@@ -7855,6 +8018,21 @@ packages:
util-deprecate: 1.0.2
dev: false
+ /readable-stream@3.6.2:
+ resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==}
+ engines: {node: '>= 6'}
+ dependencies:
+ inherits: 2.0.4
+ string_decoder: 1.1.1
+ util-deprecate: 1.0.2
+ dev: false
+
+ /readdir-glob@1.1.3:
+ resolution: {integrity: sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==}
+ dependencies:
+ minimatch: 5.1.6
+ dev: false
+
/readdirp@3.6.0:
resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==}
engines: {node: '>=8.10.0'}
@@ -8310,6 +8488,11 @@ packages:
resolution: {integrity: sha512-Rz6yejtVyWnVjC1RFvNmYL10kgjC49EOghxWn0RFqlCHGFpQx+Xe7yW3I4ceK1SGrWIGMjD5Kbue8W/udkbMJg==}
dev: true
+ /stream-buffers@3.0.2:
+ resolution: {integrity: sha512-DQi1h8VEBA/lURbSwFtEHnSTb9s2/pwLEaFuNhXwy1Dx3Sa0lOuYT2yNUr4/j2fs8oCAMANtrZ5OrPZtyVs3MQ==}
+ engines: {node: '>= 0.10.0'}
+ dev: false
+
/streamsearch@1.1.0:
resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==}
engines: {node: '>=10.0.0'}
@@ -8457,6 +8640,17 @@ packages:
resolution: {integrity: sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==}
engines: {node: '>=6'}
+ /tar-stream@2.2.0:
+ resolution: {integrity: sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==}
+ engines: {node: '>=6'}
+ dependencies:
+ bl: 4.1.0
+ end-of-stream: 1.4.4
+ fs-constants: 1.0.0
+ inherits: 2.0.4
+ readable-stream: 3.6.2
+ dev: false
+
/terser-webpack-plugin@5.3.9(webpack@5.88.2):
resolution: {integrity: sha512-ZuXsqE07EcggTWQjXUj+Aot/OMcD0bMKGgF63f7UxYcu5/AJF53aIpK1YoP5xR9l6s/Hy2b+t1AM0bLNPRuhwA==}
engines: {node: '>= 10.13.0'}
@@ -9341,6 +9535,15 @@ packages:
resolution: {integrity: sha512-N+d4UJSJbt/R3wqY7Coqs5pcV0aUj2j9IaQ3rNj9bVCLld8tTGKRa2USARjnvZJWVx1NDmQev8EknoczaOQDOA==}
dev: false
+ /zip-stream@4.1.0:
+ resolution: {integrity: sha512-zshzwQW7gG7hjpBlgeQP9RuyPGNxvJdzR8SUM3QhxCnLjWN2E7j3dOvpeDcQoETfHx0urRS7EtmVToql7YpU4A==}
+ engines: {node: '>= 10'}
+ dependencies:
+ archiver-utils: 2.1.0
+ compress-commons: 4.1.1
+ readable-stream: 3.6.2
+ dev: false
+
/zod-to-json-schema@3.21.4(zod@3.21.4):
resolution: {integrity: sha512-fjUZh4nQ1s6HMccgIeE0VP4QG/YRGPmyjO9sAh890aQKPEk3nqbfUXhMFaC+Dr5KvYBm8BCyvfpZf2jY9aGSsw==}
peerDependencies: