@eventcatalog/core 2.33.6 → 2.33.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -37,7 +37,7 @@ var import_axios = __toESM(require("axios"), 1);
37
37
  var import_os = __toESM(require("os"), 1);
38
38
 
39
39
  // package.json
40
- var version = "2.33.6";
40
+ var version = "2.33.8";
41
41
 
42
42
  // src/constants.ts
43
43
  var VERSION = version;
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  raiseEvent
3
- } from "../chunk-UPH7SKWZ.js";
4
- import "../chunk-Q22YAXYM.js";
3
+ } from "../chunk-HPV7UK2I.js";
4
+ import "../chunk-SSJWAEJA.js";
5
5
  export {
6
6
  raiseEvent
7
7
  };
@@ -106,7 +106,7 @@ var import_axios = __toESM(require("axios"), 1);
106
106
  var import_os = __toESM(require("os"), 1);
107
107
 
108
108
  // package.json
109
- var version = "2.33.6";
109
+ var version = "2.33.8";
110
110
 
111
111
  // src/constants.ts
112
112
  var VERSION = version;
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  log_build_default
3
- } from "../chunk-CHTRHQHZ.js";
4
- import "../chunk-UPH7SKWZ.js";
5
- import "../chunk-Q22YAXYM.js";
3
+ } from "../chunk-DCU5UE7Y.js";
4
+ import "../chunk-HPV7UK2I.js";
5
+ import "../chunk-SSJWAEJA.js";
6
6
  import "../chunk-E7TXTI7G.js";
7
7
  export {
8
8
  log_build_default as default
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  raiseEvent
3
- } from "./chunk-UPH7SKWZ.js";
3
+ } from "./chunk-HPV7UK2I.js";
4
4
  import {
5
5
  getEventCatalogConfigFile,
6
6
  verifyRequiredFieldsAreInCatalogConfigFile
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  VERSION
3
- } from "./chunk-Q22YAXYM.js";
3
+ } from "./chunk-SSJWAEJA.js";
4
4
 
5
5
  // src/analytics/analytics.js
6
6
  import axios from "axios";
@@ -1,5 +1,5 @@
1
1
  // package.json
2
- var version = "2.33.6";
2
+ var version = "2.33.8";
3
3
 
4
4
  // src/constants.ts
5
5
  var VERSION = version;
@@ -25,7 +25,7 @@ __export(constants_exports, {
25
25
  module.exports = __toCommonJS(constants_exports);
26
26
 
27
27
  // package.json
28
- var version = "2.33.6";
28
+ var version = "2.33.8";
29
29
 
30
30
  // src/constants.ts
31
31
  var VERSION = version;
package/dist/constants.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  VERSION
3
- } from "./chunk-Q22YAXYM.js";
3
+ } from "./chunk-SSJWAEJA.js";
4
4
  export {
5
5
  VERSION
6
6
  };
@@ -157,7 +157,7 @@ var import_axios = __toESM(require("axios"), 1);
157
157
  var import_os = __toESM(require("os"), 1);
158
158
 
159
159
  // package.json
160
- var version = "2.33.6";
160
+ var version = "2.33.8";
161
161
 
162
162
  // src/constants.ts
163
163
  var VERSION = version;
@@ -81,6 +81,9 @@ interface Config {
81
81
  customDocs?: {
82
82
  sidebar?: (ManualSideBarConfig | AutoGeneratedSideBarConfig)[];
83
83
  };
84
+ api?: {
85
+ fullCatalogAPIEnabled?: boolean;
86
+ };
84
87
  }
85
88
 
86
89
  export type { Config, SideBarConfig };
@@ -81,6 +81,9 @@ interface Config {
81
81
  customDocs?: {
82
82
  sidebar?: (ManualSideBarConfig | AutoGeneratedSideBarConfig)[];
83
83
  };
84
+ api?: {
85
+ fullCatalogAPIEnabled?: boolean;
86
+ };
84
87
  }
85
88
 
86
89
  export type { Config, SideBarConfig };
@@ -6,15 +6,15 @@ import {
6
6
  } from "./chunk-UKJ7F5WR.js";
7
7
  import {
8
8
  log_build_default
9
- } from "./chunk-CHTRHQHZ.js";
10
- import "./chunk-UPH7SKWZ.js";
9
+ } from "./chunk-DCU5UE7Y.js";
10
+ import "./chunk-HPV7UK2I.js";
11
11
  import {
12
12
  catalogToAstro,
13
13
  checkAndConvertMdToMdx
14
14
  } from "./chunk-7SI5EVOX.js";
15
15
  import {
16
16
  VERSION
17
- } from "./chunk-Q22YAXYM.js";
17
+ } from "./chunk-SSJWAEJA.js";
18
18
  import {
19
19
  isBackstagePluginEnabled,
20
20
  isEventCatalogScaleEnabled,
@@ -232,21 +232,24 @@ const NodeGraphBuilder = ({
232
232
  );
233
233
 
234
234
  const getNodesByCollectionWithColors = useCallback((nodes: Node[]) => {
235
- const colors = {
236
- events: 'orange',
237
- services: 'pink',
238
- commands: 'blue',
239
- queries: 'green',
240
- channels: 'gray',
235
+ const colorClasses = {
236
+ events: 'bg-orange-600',
237
+ services: 'bg-pink-600',
238
+ commands: 'bg-blue-600',
239
+ queries: 'bg-green-600',
240
+ channels: 'bg-gray-600',
241
+ externalSystem: 'bg-pink-600',
242
+ actor: 'bg-yellow-500',
243
+ step: 'bg-gray-700',
241
244
  };
242
245
 
243
- return nodes.reduce((acc: { [key: string]: { count: number; color: string } }, node) => {
246
+ return nodes.reduce((acc: { [key: string]: { count: number; colorClass: string } }, node) => {
244
247
  const collection = node.type;
245
248
  if (collection) {
246
249
  if (acc[collection]) {
247
250
  acc[collection].count += 1;
248
251
  } else {
249
- acc[collection] = { count: 1, color: colors[collection as keyof typeof colors] || 'black' };
252
+ acc[collection] = { count: 1, colorClass: colorClasses[collection as keyof typeof colorClasses] || 'bg-black' };
250
253
  }
251
254
  }
252
255
  return acc;
@@ -348,13 +351,13 @@ const NodeGraphBuilder = ({
348
351
  <Panel position="bottom-right">
349
352
  <div className=" bg-white font-light px-4 text-[12px] shadow-md py-1 rounded-md">
350
353
  <ul className="m-0 p-0 ">
351
- {Object.entries(legend).map(([key, { count, color }]) => (
354
+ {Object.entries(legend).map(([key, { count, colorClass }]) => (
352
355
  <li
353
356
  key={key}
354
357
  className="flex space-x-2 items-center text-[10px] cursor-pointer hover:text-purple-600 hover:underline"
355
358
  onClick={() => handleLegendClick(key)}
356
359
  >
357
- <span className={`w-2 h-2 block`} style={{ backgroundColor: color }} />
360
+ <span className={`w-2 h-2 block ${colorClass}`} />
358
361
  <span className="block capitalize">
359
362
  {key} ({count})
360
363
  </span>
@@ -1,11 +1,10 @@
1
- import { BoltIcon } from '@heroicons/react/16/solid';
2
1
  import { ArrowsRightLeftIcon } from '@heroicons/react/20/solid';
3
- import type { CollectionMessageTypes, CollectionTypes } from '@types';
2
+ import type { CollectionMessageTypes } from '@types';
4
3
  import type { CollectionEntry } from 'astro:content';
5
4
  import { Handle } from '@xyflow/react';
6
5
  import * as ContextMenu from '@radix-ui/react-context-menu';
7
6
  import { buildUrl } from '@utils/url-builder';
8
-
7
+ import { getIcon } from '@utils/badges';
9
8
  interface Data {
10
9
  title: string;
11
10
  label: string;
@@ -43,11 +42,16 @@ const getIconForProtocol = (icon: keyof typeof protocolIcons) => {
43
42
  export default function ChannelNode({ data, sourcePosition, targetPosition }: any) {
44
43
  const { mode, channel, source, target } = data as Data;
45
44
 
46
- const { id, name, version, summary, owners = [], address, protocols = [] } = channel.data;
45
+ const { id, name, version, summary, owners = [], address, protocols = [], styles } = channel.data;
47
46
  const protocol = protocols[0];
47
+ const { node: { color = 'gray', label } = {}, icon = 'ArrowsRightLeftIcon' } = styles || {};
48
48
 
49
49
  const Icon = getIconForProtocol(protocol);
50
50
 
51
+ const SideBarIcon = getIcon(icon);
52
+ const nodeLabel = label || channel?.data?.sidebar?.badge || 'Channel';
53
+ const fontSize = nodeLabel.length > 10 ? '7px' : '9px';
54
+
51
55
  const getAddress = () => {
52
56
  const sourceChannel = source.data.channels?.find((channel) => channel.id === id);
53
57
  const targetChannel = target.data.channels?.find((channel) => channel.id === id);
@@ -74,19 +78,21 @@ export default function ChannelNode({ data, sourcePosition, targetPosition }: an
74
78
  <div
75
79
  className={classNames(
76
80
  mode === 'simple' ? 'min-h-[3em]' : 'min-h-[6.5em]',
77
- 'w-full rounded-md border flex justify-start bg-white text-black border-gray-400 transform '
81
+ `w-full rounded-md border flex justify-start bg-white text-black border-${color}-400 transform `
78
82
  )}
79
83
  >
80
84
  <div
81
85
  className={classNames(
82
- 'bg-gradient-to-b from-gray-500 to-gray-700 relative flex items-center w-5 justify-center rounded-l-sm text-gray-100-500',
83
- `border-r-[1px] border-gray-500`
86
+ `bg-gradient-to-b from-${color}-500 to-${color}-700 relative flex items-center w-5 justify-center rounded-l-sm text-${color}-100`,
87
+ `border-r-[1px] border-${color}-500`
84
88
  )}
85
89
  >
86
- <ArrowsRightLeftIcon className="w-4 h-4 opacity-90 text-white absolute top-1 " />
90
+ {SideBarIcon && <SideBarIcon className="w-4 h-4 opacity-90 text-white absolute top-1 " />}
87
91
  {mode === 'full' && (
88
- <span className="rotate -rotate-90 w-1/2 text-center absolute bottom-1 text-[9px] text-white font-bold uppercase tracking-[3px] ">
89
- Channel
92
+ <span
93
+ className={`rotate -rotate-90 w-1/2 text-center absolute bottom-1 text-[${fontSize}] text-white font-bold uppercase tracking-[3px] `}
94
+ >
95
+ {nodeLabel}
90
96
  </span>
91
97
  )}
92
98
  </div>
@@ -2,6 +2,7 @@ import { ChatBubbleLeftIcon } from '@heroicons/react/16/solid';
2
2
  import type { CollectionEntry } from 'astro:content';
3
3
  import { Handle } from '@xyflow/react';
4
4
  import MessageContextMenu from './MessageContextMenu';
5
+ import { getIcon } from '@utils/badges';
5
6
 
6
7
  interface Data {
7
8
  title: string;
@@ -21,21 +22,28 @@ function classNames(...classes: any) {
21
22
  export default function CommandNode({ data, sourcePosition, targetPosition }: any) {
22
23
  const { mode, message } = data as Data;
23
24
 
24
- const { id, name, version, summary, owners = [], producers = [], consumers = [], schemaPath } = message.data;
25
+ const { id, name, version, summary, owners = [], producers = [], consumers = [], schemaPath, styles } = message.data;
26
+ const { node: { color = 'blue', label } = {}, icon = 'ChatBubbleLeftIcon' } = styles || {};
27
+
28
+ const Icon = getIcon(icon);
29
+ const nodeLabel = label || message?.data?.sidebar?.badge || 'Command';
30
+ const fontSize = nodeLabel.length > 10 ? '7px' : '9px';
25
31
 
26
32
  return (
27
33
  <MessageContextMenu message={message} messageType="commands">
28
- <div className={classNames('w-full rounded-md border flex justify-start bg-white text-black border-blue-400')}>
34
+ <div className={classNames(`w-full rounded-md border flex justify-start bg-white text-black border-${color}-400`)}>
29
35
  <div
30
36
  className={classNames(
31
- 'bg-gradient-to-b from-blue-500 to-blue-700 relative flex items-center w-5 justify-center rounded-l-sm text-blue-100-500',
32
- `border-r-[1px] border-blue-500`
37
+ `bg-gradient-to-b from-${color}-500 to-${color}-700 relative flex items-center w-5 justify-center rounded-l-sm text-${color}-100`,
38
+ `border-r-[1px] border-${color}-500`
33
39
  )}
34
40
  >
35
- <ChatBubbleLeftIcon className="w-4 h-4 opacity-90 text-white absolute top-1 " />
41
+ {Icon && <Icon className="w-4 h-4 opacity-90 text-white absolute top-1 " />}
36
42
  {mode === 'full' && (
37
- <span className="rotate -rotate-90 w-1/2 text-center absolute bottom-1 text-[9px] text-white font-bold uppercase tracking-[3px] ">
38
- Command
43
+ <span
44
+ className={`rotate -rotate-90 w-1/2 text-center absolute bottom-1 text-[${fontSize}] text-white font-bold uppercase tracking-[3px] `}
45
+ >
46
+ {nodeLabel}
39
47
  </span>
40
48
  )}
41
49
  </div>
@@ -2,7 +2,7 @@ import { BoltIcon } from '@heroicons/react/16/solid';
2
2
  import type { CollectionEntry } from 'astro:content';
3
3
  import { Handle } from '@xyflow/react';
4
4
  import MessageContextMenu from './MessageContextMenu';
5
-
5
+ import { getIcon } from '@utils/badges';
6
6
  interface Data {
7
7
  title: string;
8
8
  label: string;
@@ -20,21 +20,28 @@ function classNames(...classes: any) {
20
20
 
21
21
  export default function EventNode({ data, sourcePosition, targetPosition }: any) {
22
22
  const { mode, message } = data as Data;
23
- const { name, version, summary, owners = [], producers = [], consumers = [] } = message.data;
23
+ const { name, version, summary, owners = [], producers = [], consumers = [], styles } = message.data;
24
+ const { node: { color = 'orange', label } = {}, icon = 'BoltIcon' } = styles || {};
25
+
26
+ const Icon = getIcon(icon);
27
+ const nodeLabel = label || message?.data?.sidebar?.badge || 'Event';
28
+ const fontSize = nodeLabel.length > 10 ? '7px' : '9px';
24
29
 
25
30
  return (
26
31
  <MessageContextMenu message={message} messageType="events">
27
- <div className={classNames('w-full rounded-md border flex justify-start bg-white text-black border-orange-400')}>
32
+ <div className={classNames(`w-full rounded-md border flex justify-start bg-white text-black border-${color}-400`)}>
28
33
  <div
29
34
  className={classNames(
30
- 'bg-gradient-to-b from-orange-500 to-orange-700 relative flex items-center w-5 justify-center rounded-l-sm text-orange-100-500',
31
- `border-r-[1px] border-orange-500`
35
+ `bg-gradient-to-b from-${color}-500 to-${color}-700 relative flex items-center w-5 justify-center rounded-l-sm text-${color}-100`,
36
+ `border-r-[1px] border-${color}-500`
32
37
  )}
33
38
  >
34
- <BoltIcon className="w-4 h-4 opacity-90 text-white absolute top-1 " />
39
+ {Icon && <Icon className="w-4 h-4 opacity-90 text-white absolute top-1 " />}
35
40
  {mode === 'full' && (
36
- <span className="rotate -rotate-90 w-1/2 text-center absolute bottom-1 text-[9px] text-white font-bold uppercase tracking-[3px] ">
37
- Event
41
+ <span
42
+ className={`rotate -rotate-90 w-1/2 text-center absolute bottom-1 text-[${fontSize}] text-white font-bold uppercase tracking-[3px] `}
43
+ >
44
+ {nodeLabel}
38
45
  </span>
39
46
  )}
40
47
  </div>
@@ -2,6 +2,7 @@ import { MagnifyingGlassIcon } from '@heroicons/react/16/solid';
2
2
  import type { CollectionEntry } from 'astro:content';
3
3
  import { Handle } from '@xyflow/react';
4
4
  import MessageContextMenu from './MessageContextMenu';
5
+ import { getIcon } from '@utils/badges';
5
6
  interface Data {
6
7
  title: string;
7
8
  label: string;
@@ -9,8 +10,6 @@ interface Data {
9
10
  color: string;
10
11
  mode: 'simple' | 'full';
11
12
  message: CollectionEntry<'queries'>;
12
- showTarget?: boolean;
13
- showSource?: boolean;
14
13
  }
15
14
 
16
15
  function classNames(...classes: any) {
@@ -18,26 +17,32 @@ function classNames(...classes: any) {
18
17
  }
19
18
 
20
19
  export default function QueryNode({ data, sourcePosition, targetPosition }: any) {
21
- const { mode, message, showTarget = true, showSource = true } = data as Data;
20
+ const { mode, message } = data as Data;
22
21
 
23
- const { name, version, summary, owners = [], producers = [], consumers = [] } = message.data;
22
+ const { name, version, summary, owners = [], producers = [], consumers = [], styles } = message.data;
23
+ const { node: { color = 'green', label } = {}, icon = 'MagnifyingGlassIcon' } = styles || {};
24
24
 
25
+ const Icon = getIcon(icon);
26
+ const nodeLabel = label || message?.data?.sidebar?.badge || 'Query';
27
+ const fontSize = nodeLabel.length > 10 ? '7px' : '9px';
25
28
  const renderTarget = true;
26
29
  const renderSource = true;
27
30
 
28
31
  return (
29
32
  <MessageContextMenu message={message} messageType="queries">
30
- <div className={classNames('w-full rounded-md border flex justify-start bg-white text-black border-green-400')}>
33
+ <div className={classNames(`w-full rounded-md border flex justify-start bg-white text-black border-${color}-400`)}>
31
34
  <div
32
35
  className={classNames(
33
- 'bg-gradient-to-b from-green-500 to-green-700 relative flex items-center w-5 justify-center rounded-l-sm text-green-100-500',
34
- `border-r-[1px] border-green-500`
36
+ `bg-gradient-to-b from-${color}-500 to-${color}-700 relative flex items-center w-5 justify-center rounded-l-sm text-${color}-100`,
37
+ `border-r-[1px] border-${color}-500`
35
38
  )}
36
39
  >
37
- <MagnifyingGlassIcon className="w-4 h-4 opacity-90 text-white absolute top-1 " />
40
+ {Icon && <Icon className="w-4 h-4 opacity-90 text-white absolute top-1 " />}
38
41
  {mode === 'full' && (
39
- <span className="rotate -rotate-90 w-1/2 text-center absolute bottom-1 text-[9px] text-white font-bold uppercase tracking-[3px] ">
40
- Query
42
+ <span
43
+ className={`rotate -rotate-90 w-1/2 text-center absolute bottom-1 text-[${fontSize}] text-white font-bold uppercase tracking-[3px] `}
44
+ >
45
+ {nodeLabel}
41
46
  </span>
42
47
  )}
43
48
  </div>
@@ -3,6 +3,7 @@ import type { CollectionEntry } from 'astro:content';
3
3
  import { Handle } from '@xyflow/react';
4
4
  import * as ContextMenu from '@radix-ui/react-context-menu';
5
5
  import { buildUrl } from '@utils/url-builder';
6
+ import { getIcon } from '@utils/badges';
6
7
 
7
8
  interface Data {
8
9
  label: string;
@@ -19,9 +20,15 @@ function classNames(...classes: any) {
19
20
  }
20
21
 
21
22
  export default function ServiceNode({ data, sourcePosition, targetPosition }: any) {
22
- const { label, bgColor = 'bg-blue-500', mode, service } = data as Data;
23
+ const { mode, service } = data as Data;
24
+
25
+ const { id, version, owners = [], sends = [], receives = [], name, specifications, repository, styles } = service.data;
26
+ const { node: { color = 'pink', label } = {}, icon = 'ServerIcon' } = styles || {};
27
+
28
+ const Icon = getIcon(icon);
29
+ const nodeLabel = label || service?.data?.sidebar?.badge || 'Service';
30
+ const fontSize = nodeLabel.length > 10 ? '7px' : '9px';
23
31
 
24
- const { id, version, owners = [], sends = [], receives = [], name, specifications, repository } = service.data;
25
32
  const asyncApiPath = specifications?.asyncapiPath;
26
33
  const openApiPath = specifications?.openapiPath;
27
34
  const repositoryUrl = repository?.url;
@@ -29,17 +36,19 @@ export default function ServiceNode({ data, sourcePosition, targetPosition }: an
29
36
  return (
30
37
  <ContextMenu.Root>
31
38
  <ContextMenu.Trigger>
32
- <div className={classNames('w-full rounded-md border flex justify-start bg-white text-black border-pink-500')}>
39
+ <div className={classNames(`w-full rounded-md border flex justify-start bg-white text-black border-${color}-400`)}>
33
40
  <div
34
41
  className={classNames(
35
- 'bg-gradient-to-b from-pink-500 to-pink-700 relative flex items-center w-5 justify-center rounded-l-sm text-red-100-500',
36
- `border-r-[1px] border-pink-500`
42
+ `bg-gradient-to-b from-${color}-500 to-${color}-700 relative flex items-center w-5 justify-center rounded-l-sm text-${color}-100`,
43
+ `border-r-[1px] border-${color}-500`
37
44
  )}
38
45
  >
39
- <ServerIcon className="w-4 h-4 opacity-90 text-white absolute top-1 " />
46
+ {Icon && <Icon className="w-4 h-4 opacity-90 text-white absolute top-1 " />}
40
47
  {mode === 'full' && (
41
- <span className="rotate -rotate-90 w-1/2 text-center absolute bottom-1 text-[9px] text-white font-bold uppercase tracking-[3px] ">
42
- Service
48
+ <span
49
+ className={`rotate -rotate-90 w-1/2 text-center absolute bottom-1 text-[${fontSize}] text-white font-bold uppercase tracking-[3px] `}
50
+ >
51
+ {nodeLabel}
43
52
  </span>
44
53
  )}
45
54
  </div>
@@ -104,6 +104,17 @@ const baseSchema = z.object({
104
104
  })
105
105
  )
106
106
  .optional(),
107
+ styles: z
108
+ .object({
109
+ icon: z.string().optional(),
110
+ node: z
111
+ .object({
112
+ color: z.string().optional(),
113
+ label: z.string().optional(),
114
+ })
115
+ .optional(),
116
+ })
117
+ .optional(),
107
118
  // Used by eventcatalog
108
119
  versions: z.array(z.string()).optional(),
109
120
  latestVersion: z.string().optional(),
@@ -0,0 +1,26 @@
1
+ import type { APIRoute } from 'astro';
2
+ import utils from '@eventcatalog/sdk';
3
+ import config from '@config';
4
+
5
+ /**
6
+ * Route the will dump the whole catalog as JSON (without markdown)
7
+ * Experimental API
8
+ * @param param0
9
+ * @returns
10
+ */
11
+ export const GET: APIRoute = async ({ params, request }) => {
12
+ if (!config.api?.fullCatalogAPIEnabled) {
13
+ return new Response(JSON.stringify({ error: 'Full catalog API is not enabled' }), {
14
+ status: 404,
15
+ });
16
+ }
17
+
18
+ const { dumpCatalog } = utils(process.env.PROJECT_DIR || '');
19
+ const catalog = await dumpCatalog({ includeMarkdown: false });
20
+
21
+ return new Response(JSON.stringify(catalog), {
22
+ headers: {
23
+ 'Content-Type': 'application/json',
24
+ },
25
+ });
26
+ };
package/package.json CHANGED
@@ -6,7 +6,7 @@
6
6
  "url": "https://github.com/event-catalog/eventcatalog.git"
7
7
  },
8
8
  "type": "module",
9
- "version": "2.33.6",
9
+ "version": "2.33.8",
10
10
  "publishConfig": {
11
11
  "access": "public"
12
12
  },
@@ -30,6 +30,7 @@
30
30
  "@asyncapi/parser": "^3.4.0",
31
31
  "@asyncapi/react-component": "^2.4.3",
32
32
  "@eventcatalog/generator-ai": "^1.0.0",
33
+ "@eventcatalog/sdk": "^2.2.3",
33
34
  "@fontsource/inter": "^5.2.5",
34
35
  "@headlessui/react": "^2.0.3",
35
36
  "@heroicons/react": "^2.1.3",