Merge branch 'feature/store' of github.com:logspace-ai/langflow into feature/store

This commit is contained in:
cristhianzl 2023-10-30 15:04:18 -03:00
commit 5f2bfba9e3
7 changed files with 93 additions and 147 deletions

View file

@ -130,6 +130,7 @@ async def search_endpoint(
page: int = Query(1),
limit: int = Query(10),
status: Optional[str] = Query(None),
is_component: Optional[bool] = Query(None),
tags: Optional[List[str]] = Query(None),
date_from: Optional[datetime] = Query(None),
date_to: Optional[datetime] = Query(None),
@ -146,6 +147,7 @@ async def search_endpoint(
page=page,
limit=limit,
status=status,
is_component=is_component,
tags=tags,
date_from=date_from,
date_to=date_to,

View file

@ -80,6 +80,7 @@ class StoreService(Service):
page: int = 1,
limit: int = 10,
status: Optional[str] = None,
is_component: Optional[bool] = None,
tags: Optional[List[str]] = None,
date_from: Optional[datetime] = None,
date_to: Optional[datetime] = None,
@ -102,6 +103,8 @@ class StoreService(Service):
if status:
params["filter[status]"] = status
if is_component:
params["filter[is_component][_eq]"] = is_component
if tags:
params["filter[tags][_in]"] = ",".join(tags)

View file

@ -3223,20 +3223,20 @@
}
},
"node_modules/@tabler/icons": {
"version": "2.39.0",
"resolved": "https://registry.npmjs.org/@tabler/icons/-/icons-2.39.0.tgz",
"integrity": "sha512-iK3j2jIEGIUaJcbYYg5iwyG1Y/m4lzUxAUbxRpvgeXCWP29jvZaH5hajZmU3KaSealddHuJg7PSQislPHpCsoQ==",
"version": "2.40.0",
"resolved": "https://registry.npmjs.org/@tabler/icons/-/icons-2.40.0.tgz",
"integrity": "sha512-VqKsBSX159cLFTnCzkCmGhZtSPJHNN0lM2sC4xe0HPOfPUnjiex7rDHDdut4oe4iKRecDDpwXwM9BcU6xCPlCg==",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/codecalm"
}
},
"node_modules/@tabler/icons-react": {
"version": "2.39.0",
"resolved": "https://registry.npmjs.org/@tabler/icons-react/-/icons-react-2.39.0.tgz",
"integrity": "sha512-MyUK1jqtmHPZBnDXqIc1Y5OnfoqG+tGaSB1/gcl0mlY462fJ5f3QB0ZIZzAHMAGYb6K2iJSdFIFavhcgpDDZ7Q==",
"version": "2.40.0",
"resolved": "https://registry.npmjs.org/@tabler/icons-react/-/icons-react-2.40.0.tgz",
"integrity": "sha512-C+dDOZowFbwI3LGQP0fdua+hOPkGkW7XeMcRXTSdEKc5fD75W6zRO5nXnWivIMRKsi/Y26EDmnQo15N8JX378w==",
"dependencies": {
"@tabler/icons": "2.39.0",
"@tabler/icons": "2.40.0",
"prop-types": "^15.7.2"
},
"funding": {
@ -3857,12 +3857,12 @@
"dev": true
},
"node_modules/@vitejs/plugin-react-swc": {
"version": "3.4.0",
"resolved": "https://registry.npmjs.org/@vitejs/plugin-react-swc/-/plugin-react-swc-3.4.0.tgz",
"integrity": "sha512-m7UaA4Uvz82N/0EOVpZL4XsFIakRqrFKeSNxa1FBLSXGvWrWRBwmZb4qxk+ZIVAZcW3c3dn5YosomDgx62XWcQ==",
"version": "3.4.1",
"resolved": "https://registry.npmjs.org/@vitejs/plugin-react-swc/-/plugin-react-swc-3.4.1.tgz",
"integrity": "sha512-7YQOQcVV5x1luD8nkbCDdyYygFvn1hjqJk68UvNAzY2QG4o4N5EwAhLLFNOcd1HrdMwDl0VElP8VutoWf9IvJg==",
"dev": true,
"dependencies": {
"@swc/core": "^1.3.85"
"@swc/core": "^1.3.95"
},
"peerDependencies": {
"vite": "^4"
@ -3879,14 +3879,14 @@
"integrity": "sha512-jbQfFaw+57OBwPt7qSNHuW+RA8smmRwkWRS1Ozh6K/QxUspBgBV/LpdSzlY7vee8TomS6j3D33B9rIeH1qMwsA=="
},
"node_modules/ace-builds": {
"version": "1.31.0",
"resolved": "https://registry.npmjs.org/ace-builds/-/ace-builds-1.31.0.tgz",
"integrity": "sha512-nitIhcUYA6wyO3lo2WZBPX5fcjllW6XFt4EFyHwcN2Fp70/IZwz8tdw6a0+8udDEwDj/ebt3aWEClIyCs/6qYA=="
"version": "1.31.1",
"resolved": "https://registry.npmjs.org/ace-builds/-/ace-builds-1.31.1.tgz",
"integrity": "sha512-3DnE5bZF6Ji+l4F5acoLk+rV7mxrUt1C4r61Xy9owp5rVM4lj5NL8GJfoX6Jnnbhx6kKV7Vdpb+Tco+0ORTvhg=="
},
"node_modules/acorn": {
"version": "8.10.0",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz",
"integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==",
"version": "8.11.2",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.2.tgz",
"integrity": "sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w==",
"bin": {
"acorn": "bin/acorn"
},
@ -3904,9 +3904,9 @@
}
},
"node_modules/acorn-walk": {
"version": "8.2.0",
"resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz",
"integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==",
"version": "8.3.0",
"resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.0.tgz",
"integrity": "sha512-FS7hV565M5l1R08MXqo8odwMTB02C2UqzB17RVgu9EyuYFBqJZ3/ZY97sQD5FewVu1UyDFc1yztUDrAwT0EypA==",
"engines": {
"node": ">=0.4.0"
}
@ -4121,9 +4121,9 @@
}
},
"node_modules/axios": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.5.1.tgz",
"integrity": "sha512-Q28iYCWzNHjAm+yEAot5QaAMxhMghWLFVf7rRdwhUI+c2jix2DUXjAHXVi+s1ibs3mjPO/cCgbA++3BjD0vP/A==",
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.6.0.tgz",
"integrity": "sha512-EZ1DYihju9pwVB+jg67ogm+Tmqc6JmhamRN6I4Zt8DfZu5lbcQGw3ozH9lFejSJgs/ibaef3A9PMXPLeefFGJg==",
"dependencies": {
"follow-redirects": "^1.15.0",
"form-data": "^4.0.0",
@ -4517,9 +4517,9 @@
}
},
"node_modules/caniuse-lite": {
"version": "1.0.30001554",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001554.tgz",
"integrity": "sha512-A2E3U//MBwbJVzebddm1YfNp7Nud5Ip+IPn4BozBmn4KqVX7AvluoIDFWjsv5OkGnKUXQVmMSoMKLa3ScCblcQ==",
"version": "1.0.30001558",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001558.tgz",
"integrity": "sha512-/Et7DwLqpjS47JPEcz6VnxU9PwcIdVi0ciLXRWBQdj1XFye68pSQYpV0QtPTfUKWuOaEig+/Vez2l74eDc1tPQ==",
"funding": [
{
"type": "opencollective",
@ -4954,9 +4954,9 @@
}
},
"node_modules/daisyui": {
"version": "3.9.3",
"resolved": "https://registry.npmjs.org/daisyui/-/daisyui-3.9.3.tgz",
"integrity": "sha512-8li177QCu6dqlEOzE3h/dAV1y9Movbjx5bzJIO/hNqMNZtJkbHM0trjTzbDejV7N57eNGdjBvAGtxZYKzS4jow==",
"version": "3.9.4",
"resolved": "https://registry.npmjs.org/daisyui/-/daisyui-3.9.4.tgz",
"integrity": "sha512-fvi2RGH4YV617/6DntOVGcOugOPym9jTGWW2XySb5ZpvdWO4L7bEG77VHirrnbRUEWvIEVXkBpxUz2KFj0rVnA==",
"dev": true,
"dependencies": {
"colord": "^2.9",
@ -5237,9 +5237,9 @@
}
},
"node_modules/electron-to-chromium": {
"version": "1.4.567",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.567.tgz",
"integrity": "sha512-8KR114CAYQ4/r5EIEsOmOMqQ9j0MRbJZR3aXD/KFA8RuKzyoUB4XrUCg+l8RUGqTVQgKNIgTpjaG8YHRPAbX2w=="
"version": "1.4.569",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.569.tgz",
"integrity": "sha512-LsrJjZ0IbVy12ApW3gpYpcmHS3iRxH4bkKOW98y1/D+3cvDUWGcbzbsFinfUS8knpcZk/PG/2p/RnkMCYN7PVg=="
},
"node_modules/emoji-regex": {
"version": "8.0.0",
@ -13233,16 +13233,16 @@
}
},
"@tabler/icons": {
"version": "2.39.0",
"resolved": "https://registry.npmjs.org/@tabler/icons/-/icons-2.39.0.tgz",
"integrity": "sha512-iK3j2jIEGIUaJcbYYg5iwyG1Y/m4lzUxAUbxRpvgeXCWP29jvZaH5hajZmU3KaSealddHuJg7PSQislPHpCsoQ=="
"version": "2.40.0",
"resolved": "https://registry.npmjs.org/@tabler/icons/-/icons-2.40.0.tgz",
"integrity": "sha512-VqKsBSX159cLFTnCzkCmGhZtSPJHNN0lM2sC4xe0HPOfPUnjiex7rDHDdut4oe4iKRecDDpwXwM9BcU6xCPlCg=="
},
"@tabler/icons-react": {
"version": "2.39.0",
"resolved": "https://registry.npmjs.org/@tabler/icons-react/-/icons-react-2.39.0.tgz",
"integrity": "sha512-MyUK1jqtmHPZBnDXqIc1Y5OnfoqG+tGaSB1/gcl0mlY462fJ5f3QB0ZIZzAHMAGYb6K2iJSdFIFavhcgpDDZ7Q==",
"version": "2.40.0",
"resolved": "https://registry.npmjs.org/@tabler/icons-react/-/icons-react-2.40.0.tgz",
"integrity": "sha512-C+dDOZowFbwI3LGQP0fdua+hOPkGkW7XeMcRXTSdEKc5fD75W6zRO5nXnWivIMRKsi/Y26EDmnQo15N8JX378w==",
"requires": {
"@tabler/icons": "2.39.0",
"@tabler/icons": "2.40.0",
"prop-types": "^15.7.2"
}
},
@ -13816,12 +13816,12 @@
"dev": true
},
"@vitejs/plugin-react-swc": {
"version": "3.4.0",
"resolved": "https://registry.npmjs.org/@vitejs/plugin-react-swc/-/plugin-react-swc-3.4.0.tgz",
"integrity": "sha512-m7UaA4Uvz82N/0EOVpZL4XsFIakRqrFKeSNxa1FBLSXGvWrWRBwmZb4qxk+ZIVAZcW3c3dn5YosomDgx62XWcQ==",
"version": "3.4.1",
"resolved": "https://registry.npmjs.org/@vitejs/plugin-react-swc/-/plugin-react-swc-3.4.1.tgz",
"integrity": "sha512-7YQOQcVV5x1luD8nkbCDdyYygFvn1hjqJk68UvNAzY2QG4o4N5EwAhLLFNOcd1HrdMwDl0VElP8VutoWf9IvJg==",
"dev": true,
"requires": {
"@swc/core": "^1.3.85"
"@swc/core": "^1.3.95"
}
},
"abab": {
@ -13835,14 +13835,14 @@
"integrity": "sha512-jbQfFaw+57OBwPt7qSNHuW+RA8smmRwkWRS1Ozh6K/QxUspBgBV/LpdSzlY7vee8TomS6j3D33B9rIeH1qMwsA=="
},
"ace-builds": {
"version": "1.31.0",
"resolved": "https://registry.npmjs.org/ace-builds/-/ace-builds-1.31.0.tgz",
"integrity": "sha512-nitIhcUYA6wyO3lo2WZBPX5fcjllW6XFt4EFyHwcN2Fp70/IZwz8tdw6a0+8udDEwDj/ebt3aWEClIyCs/6qYA=="
"version": "1.31.1",
"resolved": "https://registry.npmjs.org/ace-builds/-/ace-builds-1.31.1.tgz",
"integrity": "sha512-3DnE5bZF6Ji+l4F5acoLk+rV7mxrUt1C4r61Xy9owp5rVM4lj5NL8GJfoX6Jnnbhx6kKV7Vdpb+Tco+0ORTvhg=="
},
"acorn": {
"version": "8.10.0",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz",
"integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw=="
"version": "8.11.2",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.2.tgz",
"integrity": "sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w=="
},
"acorn-globals": {
"version": "7.0.1",
@ -13854,9 +13854,9 @@
}
},
"acorn-walk": {
"version": "8.2.0",
"resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz",
"integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA=="
"version": "8.3.0",
"resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.0.tgz",
"integrity": "sha512-FS7hV565M5l1R08MXqo8odwMTB02C2UqzB17RVgu9EyuYFBqJZ3/ZY97sQD5FewVu1UyDFc1yztUDrAwT0EypA=="
},
"add": {
"version": "2.0.6",
@ -13991,9 +13991,9 @@
"dev": true
},
"axios": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.5.1.tgz",
"integrity": "sha512-Q28iYCWzNHjAm+yEAot5QaAMxhMghWLFVf7rRdwhUI+c2jix2DUXjAHXVi+s1ibs3mjPO/cCgbA++3BjD0vP/A==",
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.6.0.tgz",
"integrity": "sha512-EZ1DYihju9pwVB+jg67ogm+Tmqc6JmhamRN6I4Zt8DfZu5lbcQGw3ozH9lFejSJgs/ibaef3A9PMXPLeefFGJg==",
"requires": {
"follow-redirects": "^1.15.0",
"form-data": "^4.0.0",
@ -14248,9 +14248,9 @@
"integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA=="
},
"caniuse-lite": {
"version": "1.0.30001554",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001554.tgz",
"integrity": "sha512-A2E3U//MBwbJVzebddm1YfNp7Nud5Ip+IPn4BozBmn4KqVX7AvluoIDFWjsv5OkGnKUXQVmMSoMKLa3ScCblcQ=="
"version": "1.0.30001558",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001558.tgz",
"integrity": "sha512-/Et7DwLqpjS47JPEcz6VnxU9PwcIdVi0ciLXRWBQdj1XFye68pSQYpV0QtPTfUKWuOaEig+/Vez2l74eDc1tPQ=="
},
"ccount": {
"version": "2.0.1",
@ -14555,9 +14555,9 @@
}
},
"daisyui": {
"version": "3.9.3",
"resolved": "https://registry.npmjs.org/daisyui/-/daisyui-3.9.3.tgz",
"integrity": "sha512-8li177QCu6dqlEOzE3h/dAV1y9Movbjx5bzJIO/hNqMNZtJkbHM0trjTzbDejV7N57eNGdjBvAGtxZYKzS4jow==",
"version": "3.9.4",
"resolved": "https://registry.npmjs.org/daisyui/-/daisyui-3.9.4.tgz",
"integrity": "sha512-fvi2RGH4YV617/6DntOVGcOugOPym9jTGWW2XySb5ZpvdWO4L7bEG77VHirrnbRUEWvIEVXkBpxUz2KFj0rVnA==",
"dev": true,
"requires": {
"colord": "^2.9",
@ -14768,9 +14768,9 @@
}
},
"electron-to-chromium": {
"version": "1.4.567",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.567.tgz",
"integrity": "sha512-8KR114CAYQ4/r5EIEsOmOMqQ9j0MRbJZR3aXD/KFA8RuKzyoUB4XrUCg+l8RUGqTVQgKNIgTpjaG8YHRPAbX2w=="
"version": "1.4.569",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.569.tgz",
"integrity": "sha512-LsrJjZ0IbVy12ApW3gpYpcmHS3iRxH4bkKOW98y1/D+3cvDUWGcbzbsFinfUS8knpcZk/PG/2p/RnkMCYN7PVg=="
},
"emoji-regex": {
"version": "8.0.0",

View file

@ -16,7 +16,7 @@ const ElementStack: React.FC<ElementStackProps> = ({ children }) => {
style={{
gridColumn: 1,
gridRow: 1,
transform: `translateX(${index * 0.3}rem)`,
transform: `translateX(${index * 0.1}rem)`,
zIndex: children.length - index,
}}
>

View file

@ -1,4 +1,4 @@
import { ReactNode, useContext, useEffect, useRef, useState } from "react";
import { useContext, useEffect, useRef, useState } from "react";
import ShadTooltip from "../../../components/ShadTooltipComponent";
import IconComponent from "../../../components/genericIconComponent";
import ElementStack from "../../../components/stackedComponents";
@ -26,27 +26,6 @@ export const MarketCardComponent = ({ data }: { data: storeComponent }) => {
const { addFlow } = useContext(TabsContext);
const { setSuccessData, setErrorData } = useContext(alertContext);
const flowData = useRef<FlowType>();
const tagsPopUp = useRef<HTMLDivElement & ReactNode>(null);
const testTags = ["teste", "teste2", "teste3"];
useEffect(() => {
//@ts-ignore
tagsPopUp.current = (
<div className="flex flex-col flex-wrap gap-1">
{testTags.map((tag, index) => (
<div className="">
<Badge
key={index}
className="shadow-lg"
size="sm"
variant="outline"
>
{tag}
</Badge>
</div>
))}
</div>
);
}, []);
useEffect(() => {
setAdded(savedFlows.has(data.id) ? true : false);
@ -60,7 +39,10 @@ export const MarketCardComponent = ({ data }: { data: storeComponent }) => {
const newFLow = cloneFLowWithParent(res, res.id, data.is_component);
flowData.current = newFLow;
console.log(newFLow);
saveFlowStore(newFLow, data.tags)
saveFlowStore(
newFLow,
data.tags.map((tag) => tag.id)
)
.then(() => {
setAdded(true);
setLoading(false);
@ -87,7 +69,6 @@ export const MarketCardComponent = ({ data }: { data: storeComponent }) => {
});
} else {
getComponent(data.id).then((res) => {
console.log(res);
const newFLow = cloneFLowWithParent(res, res.id, data.is_component);
flowData.current = newFLow;
addFlow(true, newFLow);
@ -100,21 +81,6 @@ export const MarketCardComponent = ({ data }: { data: storeComponent }) => {
<Card className="group relative flex flex-col justify-between overflow-hidden transition-all hover:shadow-md">
<div>
<CardHeader>
{/*
<div className="mb-2 flex flex-wrap gap-1">
{data.tags.map((tag) => (
<Badge
size="sm"
className={
tagGradients[parseInt(tag, 35) % tagGradients.length] +
" " +
tagText[parseInt(tag, 35) % tagText.length]
}
>
{tag}
</Badge>
))}
</div> */}
<div>
<CardTitle className="flex w-full items-center justify-between gap-3 text-xl">
<span className="flex w-full items-center gap-2 word-break-break-word">
@ -124,17 +90,6 @@ export const MarketCardComponent = ({ data }: { data: storeComponent }) => {
Free
</Badge>
</CardTitle>
{/* <span className="inline-flex items-center text-sm">
<img
className="mr-2 h-4 w-4 rounded-full"
src={data.image}
/>
{data.creator.name}
</span>
<span className="flex text-xs items-center gap-2 text-ring">
<Download className="h-3 w-3" />
{nFormatter(data.downloads, 2)}
</span> */}
</div>
<CardDescription className="pb-2 pt-2">
<div className="truncate-doubleline">{data.description}</div>
@ -151,32 +106,34 @@ export const MarketCardComponent = ({ data }: { data: storeComponent }) => {
side="right"
content={
<div className="flex flex-wrap gap-1">
{testTags.map((tag, index) => (
<div className="">
<Badge
key={index}
className="bg-card shadow-md"
size="sm"
variant="outline"
>
{tag}
</Badge>
</div>
))}
{data.tags
.sort((a, b) => a.name.length - b.name.length)
.map((tag, index) => (
<div className="">
<Badge
key={index}
className="bg-card shadow-md"
size="sm"
variant="outline"
>
{tag.name}
</Badge>
</div>
))}
</div>
}
>
<div className="pr-2 hover:opacity-40">
{testTags.length > 0 ? (
{data.tags.length > 0 ? (
<ElementStack>
{testTags.map((tag, index) => (
{data.tags.map((tag, index) => (
<Badge
key={index}
size="md"
className="bg-card"
variant="outline"
>
{"tag"}
{tag.name}
</Badge>
))}
</ElementStack>
@ -206,17 +163,6 @@ export const MarketCardComponent = ({ data }: { data: storeComponent }) => {
</span>
</ShadTooltip>
</div>
{/* {data.isChat ? (
<Button size="sm" variant="outline">
<Plus className="h-4 mr-2" />
Add
</Button>
) : (
<Button size="sm" variant="success">
<Check className="h-4 mr-2" />
Added
</Button>
)} */}
<Button
variant="outline"
size="sm"

View file

@ -69,11 +69,6 @@ export default function StorePage(): JSX.Element {
setSavedFlows(savedIds);
}
useEffect(() => {
//TODO get tags from API
setTags(["tag1", "tag2", "tag3"]);
}, []);
useEffect(() => {
getNumberOfComponents().then((res) => {
setTotalRowsCount(Number(res["count"]));

View file

@ -1,7 +1,7 @@
export type storeComponent = {
id: string;
is_component: boolean;
tags: string[];
tags: { id: string; name: string }[];
metadata?: {};
downloads_count: number;
name: string;