refactor: tinyflow

pull/209/head
xingyu4j 2025-09-03 19:14:10 +08:00
parent e802a39aa3
commit 78d005f7e1
11 changed files with 220 additions and 18343 deletions

View File

@ -28,6 +28,7 @@
"dependencies": {
"@form-create/ant-design-vue": "catalog:",
"@form-create/antd-designer": "catalog:",
"@tinyflow-ai/vue": "1.1.1",
"@tinymce/tinymce-vue": "catalog:",
"@vben/access": "workspace:*",
"@vben/common-ui": "workspace:*",

View File

@ -1,73 +1,74 @@
<script setup lang="ts">
import type { Item } from './ui/typing';
<script lang="ts" setup>
import { ref } from 'vue';
import { onMounted, onUnmounted, ref } from 'vue';
import { Tinyflow } from '@tinyflow-ai/vue';
import { Tinyflow as TinyflowNative } from './ui/index';
import '@tinyflow-ai/vue/dist/index.css';
import './ui/index.css';
const props = defineProps<{
className?: string;
data?: Record<string, any>;
provider?: {
internal?: () => Item[] | Promise<Item[]>;
knowledge?: () => Item[] | Promise<Item[]>;
llm?: () => Item[] | Promise<Item[]>;
};
style?: Record<string, string>;
defineProps<{
data: any;
provider: any;
}>();
const divRef = ref<HTMLDivElement | null>(null);
let tinyflow: null | TinyflowNative = null;
// provider
const defaultProvider = {
llm: () => [] as Item[],
knowledge: () => [] as Item[],
internal: () => [] as Item[],
};
onMounted(() => {
if (divRef.value) {
// provider props.provider
const mergedProvider = {
...defaultProvider,
...props.provider,
};
tinyflow = new TinyflowNative({
element: divRef.value as Element,
data: props.data || {},
provider: mergedProvider,
});
}
});
onUnmounted(() => {
if (tinyflow) {
tinyflow.destroy();
tinyflow = null;
}
});
const getData = () => {
if (tinyflow) {
return tinyflow.getData();
}
console.warn('Tinyflow instance is not initialized');
return null;
};
const tinyflowRef = ref<InstanceType<typeof Tinyflow> | null>(null);
defineExpose({
getData,
getData: () => tinyflowRef.value?.getData(),
});
</script>
<template>
<div
ref="divRef"
class="tinyflow"
:class="[className]"
:style="style"
style="height: 100%"
></div>
<Tinyflow
ref="tinyflowRef"
class-name="custom-class"
:data="data"
:provider="provider"
/>
</template>
<style scoped>
:deep(.custom-tinyflow) {
select {
appearance: auto !important;
}
/* 如果使用checkbox需要添加 */
input[type='checkbox'] {
width: 16px;
height: 16px;
margin: 0 4px;
border: 1px solid #ccc;
}
}
input[type='checkbox'] {
position: relative;
width: 18px;
height: 18px;
margin: 0 8px 0 0;
cursor: pointer;
border: 2px solid #d9d9d9;
border-radius: 4px;
transition: all 0.3s;
&:checked {
background-color: #1890ff;
border-color: #1890ff;
&::after {
position: absolute;
top: 50%;
left: 50%;
width: 8px;
height: 12px;
content: '';
border: 2px solid #fff;
border-top: 0;
border-left: 0;
transform: translate(-50%, -60%) rotate(45deg);
}
}
&:hover {
border-color: #40a9ff;
}
}
</style>

View File

@ -1,3 +1 @@
export { default as Tinyflow } from './tinyflow.vue';
export * from './ui/typing';

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@ -1,68 +0,0 @@
export interface Item {
children?: Item[];
label: string;
value: number | string;
}
export interface Position {
x: number;
y: number;
}
export interface Viewport {
x: number;
y: number;
zoom: number;
}
export interface Node {
data?: Record<string, any>;
draggable?: boolean;
height?: number;
id: string;
position: Position;
selected?: boolean;
type?: string;
width?: number;
}
export interface Edge {
animated?: boolean;
id: string;
label?: string;
source: string;
target: string;
type?: string;
}
export type TinyflowData = Partial<{
edges: Edge[];
nodes: Node[];
viewport: Viewport;
}>;
export interface TinyflowOptions {
data?: TinyflowData;
element: Element | string;
provider?: {
internal?: () => Item[] | Promise<Item[]>;
knowledge?: () => Item[] | Promise<Item[]>;
llm?: () => Item[] | Promise<Item[]>;
};
}
export declare class Tinyflow {
private _init;
private _setOptions;
private options;
private rootEl;
private svelteFlowInstance;
constructor(options: TinyflowOptions);
destroy(): void;
getData(): {
edges: Edge[];
nodes: Node[];
viewport: Viewport;
};
getOptions(): TinyflowOptions;
setData(data: TinyflowData): void;
}

View File

@ -56,15 +56,12 @@ const formData: any = ref({
const llmProvider = ref<any>([]);
const workflowData = ref<any>({});
const workflowId = ref<string>('');
provide('workflowData', workflowData);
/** 初始化数据 */
const actionType = route.params.type as string;
async function initData() {
if (actionType === 'update') {
const workflowId = route.params.id as string;
formData.value = await getWorkflow(workflowId);
if (workflowId.value) {
formData.value = await getWorkflow(workflowId.value);
workflowData.value = JSON.parse(formData.value.graph);
}
const models = await getModelSimpleList(AiModelTypeEnum.CHAT);
@ -113,9 +110,7 @@ async function handleSave() {
...formData.value,
graph: JSON.stringify(workflowData.value),
};
await (actionType === 'update'
? updateWorkflow(data)
: createWorkflow(data));
await (workflowId.value ? updateWorkflow(data) : createWorkflow(data));
//
message.success('保存成功');
@ -191,6 +186,7 @@ function handleBack() {
/** 初始化 */
onMounted(async () => {
workflowId.value = route.query.id as string;
await initData();
});
@ -247,30 +243,26 @@ onBeforeUnmount(() => {
>
{{ index + 1 }}
</div>
<span class="whitespace-nowrap text-base font-bold">{{
step.title
}}</span>
<span class="whitespace-nowrap text-base font-bold">
{{ step.title }}
</span>
</div>
</div>
</div>
<!-- 右侧按钮 -->
<div class="flex w-48 items-center justify-end gap-2">
<Button
v-if="actionType === 'update'"
type="primary"
@click="handleDeploy"
>
<Button v-if="workflowId" type="primary" @click="handleDeploy">
</Button>
<Button type="primary" @click="handleSave">
<span v-if="actionType === 'definition'"> </span>
<span v-if="workflowId"> </span>
<span v-else> </span>
</Button>
</div>
</div>
<!-- 主体内容 -->
<Card :body-style="{ padding: '10px' }" class="mb-4">
<Card class="mb-4 p-4">
<div class="mt-12">
<!-- 第一步基本信息 -->
<div v-if="currentStep === 0" class="mx-auto w-4/6">

View File

@ -16,7 +16,7 @@ defineProps<{
provider: any;
}>();
const tinyflowRef = ref();
const tinyflowRef = ref<InstanceType<typeof Tinyflow> | null>(null);
const workflowData = inject('workflowData') as Ref;
const params4Test = ref<any[]>([]);
const paramsOfStartNode = ref<any>({});
@ -35,7 +35,7 @@ function testWorkflowModel() {
const startNode = getStartNode();
//
const parameters = startNode.data?.parameters || [];
const parameters: any[] = (startNode.data?.parameters as any[]) || [];
const paramDefinitions: Record<string, any> = {};
// 便
@ -70,7 +70,7 @@ function testWorkflowModel() {
/** 运行流程 */
async function goRun() {
try {
const val = tinyflowRef.value.getData();
const val = tinyflowRef.value?.getData();
loading.value = true;
error.value = null;
testResult.value = null;
@ -78,7 +78,7 @@ async function goRun() {
const startNode = getStartNode();
//
const parameters = startNode.data?.parameters || [];
const parameters: any[] = (startNode.data?.parameters as any[]) || [];
const paramDefinitions: Record<string, any> = {};
parameters.forEach((param: any) => {
paramDefinitions[param.name] = param.dataType;
@ -119,12 +119,15 @@ async function goRun() {
/** 获取开始节点 */
function getStartNode() {
const val = tinyflowRef.value.getData();
const startNode = val.nodes.find((node: any) => node.type === 'startNode');
if (!startNode) {
throw new Error('流程缺少开始节点');
if (tinyflowRef.value) {
const val = tinyflowRef.value.getData();
const startNode = val!.nodes.find((node: any) => node.type === 'startNode');
if (!startNode) {
throw new Error('流程缺少开始节点');
}
return startNode;
}
return startNode;
throw new Error('请设计流程');
}
/** 添加参数项 */
@ -171,7 +174,7 @@ function convertParamValue(value: string, dataType: string) {
/** 表单校验 */
async function validate() {
//
if (!workflowData.value) {
if (!workflowData.value || !tinyflowRef.value) {
throw new Error('请设计流程');
}
workflowData.value = tinyflowRef.value.getData();
@ -182,7 +185,7 @@ defineExpose({ validate });
</script>
<template>
<div class="relative h-[700px] w-full">
<div class="relative h-[800px] w-full">
<Tinyflow
v-if="workflowData"
ref="tinyflowRef"

View File

@ -30,7 +30,6 @@ function handleEdit(row: any) {
name: 'AiWorkflowCreate',
query: {
id: row.id,
type: 'update',
},
});
}

View File

@ -725,6 +725,9 @@ importers:
'@form-create/antd-designer':
specifier: 'catalog:'
version: 3.3.0(vue@3.5.18(typescript@5.9.2))
'@tinyflow-ai/vue':
specifier: 1.1.1
version: 1.1.1(svelte@5.38.6)(vue@3.5.18(typescript@5.9.2))
'@tinymce/tinymce-vue':
specifier: 'catalog:'
version: 6.3.0(tinymce@7.9.1)(vue@3.5.18(typescript@5.9.2))
@ -4557,6 +4560,16 @@ packages:
'@surma/rollup-plugin-off-main-thread@2.2.3':
resolution: {integrity: sha512-lR8q/9W7hZpMWweNiAKU7NQerBnzQQLvi8qnTDU/fxItPhtZVMbPV3lbCwjhIlNBe9Bbr5V+KHshvWmVSG9cxQ==}
'@svelte-put/shortcut@4.1.0':
resolution: {integrity: sha512-wImNEIkbxAIWFqlfuhcbC+jRPDeRa/uJGIXHMEVVD+jqL9xCwWNnkGQJ6Qb2XVszuRLHlb8SGZDL3Io/h3vs8w==}
peerDependencies:
svelte: ^5.1.0
'@sveltejs/acorn-typescript@1.0.5':
resolution: {integrity: sha512-IwQk4yfwLdibDlrXVE04jTZYlLnwsTT2PIOQQGNLWfjavGifnk1JD1LcZjZaBTRcxZu2FfPfNLOE04DSu9lqtQ==}
peerDependencies:
acorn: ^8.9.0
'@swc/helpers@0.5.17':
resolution: {integrity: sha512-5IKx/Y13RsYd+sauPb2x+U/xZikHjolzfuDgTAl/Tdf3Q8rslRvC19NKDLgAJQ6wsqADk10ntlv08nPFw/gO/A==}
@ -4609,6 +4622,14 @@ packages:
peerDependencies:
vue: ^3.5.17
'@tinyflow-ai/ui@1.1.1':
resolution: {integrity: sha512-OjL4bXcoLtDtUshP71gP53zy3bjgromVGbuhUakBSGdlvSLifpLi+B1ORI4KOQNsOTwb7dt1LtCGRHzUcET3xQ==}
'@tinyflow-ai/vue@1.1.1':
resolution: {integrity: sha512-MrUxpprssnFmXCf/fkvH8D6/NiPja3lAt9MIcEcoXTqAuxhv1G5XLM3e6/NepwG2jhLOhkeShU3QxVDNHDo73A==}
peerDependencies:
vue: ^3.5.17
'@tinymce/tinymce-vue@6.3.0':
resolution: {integrity: sha512-DSP8Jhd3XqCCliTnusfbmz3D8GqQ4iRzkc4aadYHDcJPVjkaqopJ61McOdH82CSy599vGLkPjGzqJYWJkRMiUA==}
peerDependencies:
@ -5369,6 +5390,14 @@ packages:
resolution: {integrity: sha512-ueFCcIPaMgtuYDS9u0qlUoEvj6GiSsKrwnOLPp9SshqjtcRaR1IEHRjoReq3sXNydsF5i0ZnmuYgXq9dV53t0g==}
engines: {node: '>=18.0.0'}
'@xyflow/svelte@1.2.4':
resolution: {integrity: sha512-CygKmc3t+KevPdx9VEWa6Q0O7DegJ6qzYrOH5dQo5zp9Inm2cYAZpkUuk64ry9Djw/gwy7EvrJTjyXetuvBGOg==}
peerDependencies:
svelte: ^5.25.0
'@xyflow/system@0.0.68':
resolution: {integrity: sha512-QDG2wxIG4qX+uF8yzm1ULVZrcXX3MxPBoxv7O52FWsX87qIImOqifUhfa/TwsvLdzn7ic2DDBH1uI8TKbdNTYA==}
JSONStream@1.3.5:
resolution: {integrity: sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==}
hasBin: true
@ -5519,6 +5548,10 @@ packages:
resolution: {integrity: sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==}
engines: {node: '>=10'}
aria-query@5.3.2:
resolution: {integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==}
engines: {node: '>= 0.4'}
array-buffer-byte-length@1.0.2:
resolution: {integrity: sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==}
engines: {node: '>= 0.4'}
@ -5605,6 +5638,10 @@ packages:
axios@1.11.0:
resolution: {integrity: sha512-1Lx3WLFQWm3ooKDYZD1eXmoGO9fxYQjrycfHFC8P0sCfQVXyROp0p9PFWBehewBOdCwHc+f/b8I0fMto5eSfwA==}
axobject-query@4.1.0:
resolution: {integrity: sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==}
engines: {node: '>= 0.4'}
b4a@1.6.7:
resolution: {integrity: sha512-OnAYlL5b7LEkALw87fUVafQw5rVR9RjwGd4KUwNQ6DrrNmaVaUCgLipfVlzrPQ4tWOR9P0IXGNOx50jYCCdSJg==}
@ -7077,6 +7114,9 @@ packages:
jiti:
optional: true
esm-env@1.2.2:
resolution: {integrity: sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA==}
espree@10.4.0:
resolution: {integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
@ -7094,6 +7134,9 @@ packages:
resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==}
engines: {node: '>=0.10'}
esrap@2.1.0:
resolution: {integrity: sha512-yzmPNpl7TBbMRC5Lj2JlJZNPml0tzqoqP5B1JXycNUwtqma9AKCO0M2wHrdgsHcy1WRW7S9rJknAMtByg3usgA==}
esrecurse@4.3.0:
resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==}
engines: {node: '>=4.0'}
@ -7941,6 +7984,9 @@ packages:
is-reference@1.2.1:
resolution: {integrity: sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==}
is-reference@3.0.3:
resolution: {integrity: sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw==}
is-regex@1.2.1:
resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==}
engines: {node: '>= 0.4'}
@ -8320,6 +8366,9 @@ packages:
resolution: {integrity: sha512-WunYko2W1NcdfAFpuLUoucsgULmgDBRkdxHxWQ7mK0cQqwPiy8E1enjuRBrhLtZkB5iScJ1XIPdhVEFK8aOLSg==}
engines: {node: '>=14'}
locate-character@3.0.0:
resolution: {integrity: sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA==}
locate-path@5.0.0:
resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==}
engines: {node: '>=8'}
@ -10666,6 +10715,10 @@ packages:
resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
engines: {node: '>= 0.4'}
svelte@5.38.6:
resolution: {integrity: sha512-ltBPlkvqk3bgCK7/N323atUpP3O3Y+DrGV4dcULrsSn4fZaaNnOmdplNznwfdWclAgvSr5rxjtzn/zJhRm6TKg==}
engines: {node: '>=18'}
sver@1.8.4:
resolution: {integrity: sha512-71o1zfzyawLfIWBOmw8brleKyvnbn73oVHNCsu51uPMz/HWiKkkXsI31JjHW5zqXEqnPYkIiHd8ZmL7FCimLEA==}
@ -11727,6 +11780,9 @@ packages:
resolution: {integrity: sha512-rY2A2lSF7zC+l7HH9Mq+83D1dLlsPnEvy8jTouzaptDZM6geqZ3aJe/b7ULCwRURPtWV3vbDjA2DDMdoBol0HQ==}
engines: {node: '>=18'}
zimmerframe@1.1.2:
resolution: {integrity: sha512-rAbqEGa8ovJy4pyBxZM70hg4pE6gDgaQ0Sl9M3enG3I0d6H4XSAM3GeNGLKnsBpuijUow064sf7ww1nutC5/3w==}
zip-stream@6.0.1:
resolution: {integrity: sha512-zK7YHHz4ZXpW89AHXUPbQVGKI7uvkd3hzusTdotCg1UxyaVtg0zFJSTfW/Dq5f7OBBVnq6cZIaC8Ti4hb6dtCA==}
engines: {node: '>= 14'}
@ -14653,6 +14709,14 @@ snapshots:
magic-string: 0.25.9
string.prototype.matchall: 4.0.12
'@svelte-put/shortcut@4.1.0(svelte@5.38.6)':
dependencies:
svelte: 5.38.6
'@sveltejs/acorn-typescript@1.0.5(acorn@8.15.0)':
dependencies:
acorn: 8.15.0
'@swc/helpers@0.5.17':
dependencies:
tslib: 2.8.1
@ -14701,6 +14765,20 @@ snapshots:
'@tanstack/virtual-core': 3.13.12
vue: 3.5.18(typescript@5.9.2)
'@tinyflow-ai/ui@1.1.1(svelte@5.38.6)':
dependencies:
'@floating-ui/dom': 1.7.3
'@xyflow/svelte': 1.2.4(svelte@5.38.6)
transitivePeerDependencies:
- svelte
'@tinyflow-ai/vue@1.1.1(svelte@5.38.6)(vue@3.5.18(typescript@5.9.2))':
dependencies:
'@tinyflow-ai/ui': 1.1.1(svelte@5.38.6)
vue: 3.5.18(typescript@5.9.2)
transitivePeerDependencies:
- svelte
'@tinymce/tinymce-vue@6.3.0(tinymce@7.9.1)(vue@3.5.18(typescript@5.9.2))':
dependencies:
vue: 3.5.18(typescript@5.9.2)
@ -15582,6 +15660,24 @@ snapshots:
'@whatwg-node/promise-helpers': 1.3.2
tslib: 2.8.1
'@xyflow/svelte@1.2.4(svelte@5.38.6)':
dependencies:
'@svelte-put/shortcut': 4.1.0(svelte@5.38.6)
'@xyflow/system': 0.0.68
svelte: 5.38.6
'@xyflow/system@0.0.68':
dependencies:
'@types/d3-drag': 3.0.7
'@types/d3-interpolate': 3.0.4
'@types/d3-selection': 3.0.11
'@types/d3-transition': 3.0.9
'@types/d3-zoom': 3.0.8
d3-drag: 3.0.0
d3-interpolate: 3.0.1
d3-selection: 3.0.0
d3-zoom: 3.0.0
JSONStream@1.3.5:
dependencies:
jsonparse: 1.3.1
@ -15766,6 +15862,8 @@ snapshots:
dependencies:
tslib: 2.8.1
aria-query@5.3.2: {}
array-buffer-byte-length@1.0.2:
dependencies:
call-bound: 1.0.4
@ -15848,6 +15946,8 @@ snapshots:
transitivePeerDependencies:
- debug
axobject-query@4.1.0: {}
b4a@1.6.7: {}
babel-plugin-polyfill-corejs2@0.4.14(@babel/core@7.28.3):
@ -17570,6 +17670,8 @@ snapshots:
transitivePeerDependencies:
- supports-color
esm-env@1.2.2: {}
espree@10.4.0:
dependencies:
acorn: 8.15.0
@ -17588,6 +17690,10 @@ snapshots:
dependencies:
estraverse: 5.3.0
esrap@2.1.0:
dependencies:
'@jridgewell/sourcemap-codec': 1.5.5
esrecurse@4.3.0:
dependencies:
estraverse: 5.3.0
@ -18457,6 +18563,10 @@ snapshots:
dependencies:
'@types/estree': 1.0.8
is-reference@3.0.3:
dependencies:
'@types/estree': 1.0.8
is-regex@1.2.1:
dependencies:
call-bound: 1.0.4
@ -18818,6 +18928,8 @@ snapshots:
pkg-types: 2.2.0
quansync: 0.2.11
locate-character@3.0.0: {}
locate-path@5.0.0:
dependencies:
p-locate: 4.1.0
@ -21313,6 +21425,23 @@ snapshots:
supports-preserve-symlinks-flag@1.0.0: {}
svelte@5.38.6:
dependencies:
'@jridgewell/remapping': 2.3.5
'@jridgewell/sourcemap-codec': 1.5.5
'@sveltejs/acorn-typescript': 1.0.5(acorn@8.15.0)
'@types/estree': 1.0.8
acorn: 8.15.0
aria-query: 5.3.2
axobject-query: 4.1.0
clsx: 2.1.1
esm-env: 1.2.2
esrap: 2.1.0
is-reference: 3.0.3
locate-character: 3.0.0
magic-string: 0.30.17
zimmerframe: 1.1.2
sver@1.8.4:
optionalDependencies:
semver: 6.3.1
@ -22718,6 +22847,8 @@ snapshots:
cookie: 1.0.2
youch-core: 0.3.3
zimmerframe@1.1.2: {}
zip-stream@6.0.1:
dependencies:
archiver-utils: 5.0.2