From a2195ca15b643dc30ebd46fa59aeca6373266c39 Mon Sep 17 00:00:00 2001 From: 186526 Date: Wed, 25 Jan 2023 21:55:57 +0800 Subject: [PATCH] init --- .gitignore | 24 +++ index.html | 97 +++++++++++ package.json | 18 ++ src/app.ts | 74 ++++++++ src/declaration.d.ts | 3 + src/lib/getPeers.ts | 34 ++++ src/schema/ixf.d.ts | 389 +++++++++++++++++++++++++++++++++++++++++++ src/style.css | 335 +++++++++++++++++++++++++++++++++++++ tsconfig.json | 19 +++ yarn.lock | 289 ++++++++++++++++++++++++++++++++ 10 files changed, 1282 insertions(+) create mode 100644 .gitignore create mode 100644 index.html create mode 100644 package.json create mode 100644 src/app.ts create mode 100644 src/declaration.d.ts create mode 100644 src/lib/getPeers.ts create mode 100644 src/schema/ixf.d.ts create mode 100644 src/style.css create mode 100644 tsconfig.json create mode 100644 yarn.lock diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a547bf3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/index.html b/index.html new file mode 100644 index 0000000..5b0c7d9 --- /dev/null +++ b/index.html @@ -0,0 +1,97 @@ + + + + + + + + + + + + + + + + + + + + + + + + MoeIX | Moe Internet Exchanges + + + +
+
+

MoeIX

+ Moe Internet Exchanges +
+ - 一个非商业性的互联网交换点
+ - 由 MoeQing Network 运营 +
+
+ +
+
+
+

介绍

+
+

+ MoeIX 是于 2020 年成立的非商业互联网交换点(IXP),主要为小型 ISP 、私人网络提供流量交换服务。 MoeIX + 仅用作于学习、实验,禁止用于商业服务,也没有任何 SLA 保障。 +

+
+
+
+

位置

+
    +
  • 中国 湖南 长沙 | Changsha, Hunan, China
  • +
  • 中国 河南 郑州 | Zhengzhou, Henan, China
  • +
  • 美国 华盛顿州 西雅图 | Seattle, Washington, USA +
  • +
  • 美国 得克萨斯州 达拉斯 | Dallas, Texas, USA
  • +
+
+
+

要求

+
    +
  • 具有有效的 Public ASN
  • +
  • 具有至少一段 /48 IPv6 地址
  • +
  • 具有最新的 PeeringDB 信息
  • +
  • 具有最新的 RIR Whois 信息
  • +
  • 仅向 IX LAN 宣告的目的主机发送流量
  • +
  • 禁止发送任何 multicast 数据包( ARP / IPv6 ND 除外 )
  • +
  • 禁止用于商业服务、禁止滥用
  • +
  • 至少配置过两个以上 公网 / DN42 节点,并提供跨节点的 ip transit; 或与之相同的经验。
  • +
+
+
+

特别感谢

+
    +
  • AS138517 KSKB for Technical support
  • +
+
+
+

Q&A

+

+ Q: 我可以通过什么方式接入 MoeIX?
+ A: 每个位置要求不同,详情请见 MoeIX文档。 +

+
+
+
+ +
+ + + + \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..55489ca --- /dev/null +++ b/package.json @@ -0,0 +1,18 @@ +{ + "name": "moeix-home", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "tsc && vite build", + "preview": "vite preview" + }, + "devDependencies": { + "typescript": "^4.9.3", + "vite": "^4.0.0" + }, + "dependencies": { + "axios": "^1.2.3" + } +} diff --git a/src/app.ts b/src/app.ts new file mode 100644 index 0000000..6990277 --- /dev/null +++ b/src/app.ts @@ -0,0 +1,74 @@ +import "./style.css"; +import { createPage, getMembers } from "./lib/getPeers"; +window.addEventListener("load", () => { + getMembers("https://ixpm-sea.moeqing.com/api/v4/member-export/ixf/0.6").then( + (list) => { + const seaMembers = createPage("app-sea-members", "MoeIX", "Seattle"); + seaMembers.style.display = "none"; + const groups = document.createElement("div"); + groups.classList.add("group"); + groups.classList.add("pops"); + groups.id = "pops"; + list.member_list.forEach((member) => { + const memberElement = document.createElement("div"); + memberElement.classList.add("pop"); + if ( + member.connection_list.find((k) => k.ixp_id == 1)?.state != "active" + ) + memberElement.setAttribute("status", "error"); + else memberElement.setAttribute("status", "working"); + + const connection = member.connection_list.find((k) => k.ixp_id == 1); + + const span = document.createElement("span"); + const name = document.createElement("name"); + const asn = document.createElement("asn"); + name.innerHTML = member.name ?? `AS${member.asnum}`; + asn.innerHTML = `AS${member.asnum} @ ${ + connection?.if_list?.find((k) => k.switch_id == 1)?.if_speed ?? 0 + }Mbps`; + span.appendChild(name); + span.appendChild(asn); + memberElement.appendChild(span); + + memberElement.appendChild(document.createElement("br")); + + const vSwitch = connection?.vlan_list?.find((k) => k.vlan_id == 1); + if (vSwitch?.ipv4) { + const ipv4 = document.createElement("ipv4"); + ipv4.innerHTML = `- ${vSwitch.ipv4.address}`; + memberElement.appendChild(ipv4); + } + if (vSwitch?.ipv6) { + const ipv6 = document.createElement("ipv6"); + ipv6.innerHTML = `- ${vSwitch.ipv6.address}`; + memberElement.appendChild(ipv6); + } + + memberElement.appendChild(document.createElement("br")); + + const asSets = vSwitch?.ipv4?.as_macro ?? vSwitch?.ipv6?.as_macro; + if (asSets) { + const ASSets = document.createElement("div"); + ASSets.innerHTML = `AS-SETS: ${asSets}`; + memberElement.appendChild(ASSets); + } + + const type = document.createElement("div"); + type.innerHTML = `Peering Policy: ${member.peering_policy ?? "unknown"}`; + memberElement.appendChild(type); + + groups.appendChild(memberElement); + }); + seaMembers.appendChild(groups); + document.querySelector("body")?.appendChild(seaMembers); + } + ); + + document.getElementById("sea-member")!.addEventListener("click", () => { + if (document.getElementById("app-sea-members")) { + document.getElementById("app-main")!.style.display = "none"; + document.getElementById("app-sea-members")!.style.display = "block"; + } + }); +}); diff --git a/src/declaration.d.ts b/src/declaration.d.ts new file mode 100644 index 0000000..620456e --- /dev/null +++ b/src/declaration.d.ts @@ -0,0 +1,3 @@ +/// +declare module "*.svg"; + diff --git a/src/lib/getPeers.ts b/src/lib/getPeers.ts new file mode 100644 index 0000000..f8d5404 --- /dev/null +++ b/src/lib/getPeers.ts @@ -0,0 +1,34 @@ +import { IXPMemberListSchema } from "../schema/ixf.d"; +import axios from "axios"; +export const getMembers = async (ixfEndpoint: string): Promise => { + const Response = await axios.get(ixfEndpoint); + if (Response.status != 200) { + throw new Error("IX-F Member Export Request Error"); + } + const data = Response.data; + if (!("version" in data)) { + throw new Error("Not IX-F Member Export file."); + } + if (parseFloat(data.version) != 0.6) { + throw new Error("IX-F Member Export file version is not supported."); + } + return data; +}; +export const createPage = (id: string, title: string, subTitle: string) => { + const App = document.createElement("div"); + App.id = id; + App.classList.add("app"); + App.appendChild( + (() => { + const Headers = document.createElement("header"); + const Title = document.createElement("h1"); + Title.innerHTML = title; + Headers.appendChild(Title); + const SubTitle = document.createElement("description"); + SubTitle.innerHTML = subTitle; + Headers.appendChild(SubTitle); + return Headers; + })() + ); + return App; +}; diff --git a/src/schema/ixf.d.ts b/src/schema/ixf.d.ts new file mode 100644 index 0000000..9500488 --- /dev/null +++ b/src/schema/ixf.d.ts @@ -0,0 +1,389 @@ +/* eslint-disable */ +/** + * This file was automatically generated by json-schema-to-typescript. + * DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file, + * and run json-schema-to-typescript to regenerate this file. + */ + +/** + * Version number of the schema; this is not the version of the file within an IXP; but the schema version + */ +export type IXPMemberListSchemaVersion = string; +/** + * Timestamp of when the data was exported + */ +export type IXPMemberListSchemaExportTimestamp = string; +/** + * A list of schemas representing IXP information + */ +export type IXPInfoList = { + /** + * IXP numeric identifier, persistent value free to set by the IXP + */ + ixp_id: number; + /** + * Short name of the IXP + */ + shortname: string; + /** + * Name of the IXP + */ + name?: string; + /** + * Website of the IXP + */ + url?: string; + /** + * URL to statistics API + */ + stats_api?: string; + /** + * ISO country code where the IXP is located + */ + country?: string; + /** + * IXP ID from the IX-F database + */ + ixf_id: number; + /** + * PeeringDB ID of the IX - the y in: https://www.peeringdb.com/ix/y + */ + peeringdb_id?: number; + /** + * Support email address + */ + support_email?: string; + /** + * Support phone number + */ + support_phone?: string; + /** + * Support contact hours, eg. 24/7, 8/5, etc + */ + support_contact_hours?: string; + /** + * Emergency support email address + */ + emergency_email?: string; + /** + * Emergency support phone number + */ + emergency_phone?: string; + /** + * Emergency support contact hours, eg. 24/7, 8/5, etc + */ + emergency_contact_hours?: string; + /** + * Billing email address + */ + billing_email?: string; + /** + * Billing phone number + */ + billing_phone?: string; + /** + * Billing contact hours, eg. 24/7, 8/5, etc + */ + billing_contact_hours?: string; + /** + * Peering policy choices available in the member list + */ + peering_policy_list?: string[]; + /** + * Switches available at the IXP + */ + switch?: { + /** + * Unique ID of the switch + */ + id?: number; + /** + * Switch name + */ + name?: string; + /** + * Colo the switch is located in + */ + colo?: string; + /** + * Peering DB ID of the data centre + */ + pdb_facility_id?: number; + /** + * City the switch is located in + */ + city?: string; + /** + * Country the switch is located in + */ + country?: string; + /** + * Free text field to indicate the manufacturer of the switch for informational purposes. + */ + manufacturer?: string; + /** + * Free text field to indicate the model of the switch for informational purposes. Should not be present without manufacturer. + */ + model?: string; + /** + * Free text field to indicate the software of the switch for informational purposes. + */ + software?: string; + [k: string]: unknown; + }[]; + /** + * VLANs available at the IXP + */ + vlan?: { + /** + * Unique ID of the VLAN + */ + id?: number; + /** + * VLAN name + */ + name?: string; + /** + * IPv4 details in this VLAN + */ + ipv4?: { + /** + * Prefix of the IPv4 address + */ + prefix?: string; + /** + * Mask length of the IPv4 address + */ + mask_length?: number; + /** + * URLs of looking glass(es) available for this VLAN and protocol. + */ + looking_glass_urls?: string[]; + [k: string]: unknown; + }; + /** + * IPv6 details in this VLAN + */ + ipv6?: { + /** + * Prefix of the IPv6 address + */ + prefix?: string; + /** + * Mask length of the IPv6 address + */ + mask_length?: number; + /** + * URLs of looking glass(es) available for this VLAN and protocol. + */ + looking_glass_urls?: string[]; + [k: string]: unknown; + }; + [k: string]: unknown; + }[]; + [k: string]: unknown; +}[]; +/** + * A list of schemas representing IXP members + */ +export type IXPMemberList = { + /** + * AS number of the network + */ + asnum: number; + /** + * Participant type: peering, ixp or other + */ + member_type?: "peering" | "ixp" | "other"; + /** + * Name of the network + */ + name?: string; + /** + * URL to website of the network + */ + url?: string; + /** + * Peering policy of the network + */ + peering_policy?: string; + /** + * URL to the network's peering policy + */ + peering_policy_url?: string; + /** + * Date when the network joined the IXP + */ + member_since?: string; + /** + * List of contact email addresses of the network + */ + contact_email?: string[]; + /** + * List of contact phone numbers of the network + */ + contact_phone?: string[]; + /** + * Network contact hours, eg. 24/7, 8/5, etc + */ + contact_hours?: string; + /** + * List of connections of the network at the IXP + */ + connection_list: { + /** + * IXP numeric identifier, persistent value free to set by the IXP, referencing the same ID as used in the ixp_list element + */ + ixp_id: number; + /** + * State of the connection + */ + state?: string; + /** + * Date when the connection was activated + */ + connected_since?: string; + /** + * List of interfaces making up the connection + */ + if_list?: { + /** + * Switch ID where the interface or LAG is connected, persistent value free to set by the IXP + */ + switch_id?: number; + /** + * Speed of the interface or LAG in Mb, i.e. 10G = 10000 + */ + if_speed?: number; + /** + * Type of the interface or LAG + */ + if_type?: string; + [k: string]: unknown; + }[]; + vlan_list?: { + /** + * The VLAN ID this connection is placed in, persistent value free to set by the IXP + */ + vlan_id?: number; + /** + * The network's IPv4 address details in this VLAN + */ + ipv4?: { + /** + * The IPv4 address + */ + address?: string; + /** + * The IPv4 AS-MACRO, if applicable + */ + as_macro?: string; + /** + * The max number of prefixes on this connection for IPv4 + */ + max_prefix?: number; + /** + * Does this IPv4 address connect to the IXP routeserver + */ + routeserver?: boolean; + /** + * MAC address of the member interface (format: lowercase string xx:xx:xx:xx:xx:xx) + */ + mac_addresses?: string[]; + /** + * Array of IX operated service(s) available from this connection. + */ + services?: { + /** + * The IX operated service available from this connection. + */ + type?: "ixroutecollector" | "ixrouteserver"; + /** + * Free text field to indicate the operating system of the route server for informational purposes. + */ + os?: string; + /** + * Free text field to indicate the operating system version of the route server for informational purposes. Should not be present without os. + */ + os_version?: string; + /** + * Free text field to indicate the BGP software daemon that provides the route server for informational purposes. + */ + daemon?: string; + /** + * Free text field to indicate the BGP software daemon version that provides the route server for informational purposes. Should not be present without daemon. + */ + daemon_version?: string; + [k: string]: unknown; + }[]; + [k: string]: unknown; + }; + /** + * The network's IPv6 address details in this VLAN + */ + ipv6?: { + /** + * The IPv6 address + */ + address?: string; + /** + * The IPv6 AS-MACRO, if applicable + */ + as_macro?: string; + /** + * The max number of prefixes on this connection for IPv6 + */ + max_prefix?: number; + /** + * Does this IPv6 address connect to the IXP routeserver + */ + routeserver?: boolean; + /** + * MAC address of the member interface (format: lowercase string xx:xx:xx:xx:xx:xx) + */ + mac_addresses?: string[]; + /** + * Array of IX operated service(s) available from this connection. + */ + services?: { + /** + * The IX operated service available from this connection. + */ + type?: "ixroutecollector" | "ixrouteserver"; + /** + * Free text field to indicate the operating system of the route server for informational purposes. + */ + os?: string; + /** + * Free text field to indicate the operating system version of the route server for informational purposes. Should not be present without os. + */ + os_version?: string; + /** + * Free text field to indicate the BGP software daemon that provides the route server for informational purposes. + */ + daemon?: string; + /** + * Free text field to indicate the BGP software daemon version that provides the route server for informational purposes. Should not be present without daemon. + */ + daemon_version?: string; + [k: string]: unknown; + }[]; + [k: string]: unknown; + }; + [k: string]: unknown; + }[]; + [k: string]: unknown; + }[]; + [k: string]: unknown; +}[]; + +/** + * A JSON schema representing an IXP Member List + */ +export interface IXPMemberListSchema { + version: IXPMemberListSchemaVersion; + timestamp: IXPMemberListSchemaExportTimestamp; + ixp_list: IXPInfoList; + member_list: IXPMemberList; + [k: string]: unknown; +} diff --git a/src/style.css b/src/style.css new file mode 100644 index 0000000..f5d18b8 --- /dev/null +++ b/src/style.css @@ -0,0 +1,335 @@ +:root { + --background-color: #fff; + --font-color: #000; + --font-color-lighter: rgb(87, 89, 88); + --font-size-main: 3.045rem; + --font-size-description: 1.245rem; + --lineheight-description: 1.845rem; + --box-color: #f2f2f2; + --working-color: #137333; + --working-color-background: #e6f4ea; + --error-color-background: #fce8e6; + --error-color: #c5221f; + --working-with-error-color: #b05a00; + --working-with-error-color-background: #fef7e0; + --linear-start-color: #f37335; + --linear-end-color: #ff4200fc; + --icon-size: 48px; +} + +header > h1, +h2 { + background: linear-gradient( + 90deg, + var(--linear-start-color), + var(--linear-end-color) + ); + -webkit-background-clip: text; + background-clip: text; + -webkit-text-fill-color: transparent; + font-family: "Red Hat Display", Inter, Noto Sans SC, sans-serif; +} + +p { + font-family: Inter, Noto Sans SC, sans-serif; + line-height: 140%; +} + +body { + margin: 2rem 2rem; + font-family: Inter, Noto Sans SC, sans-serif; + color: var(--font-color); + background-color: var(--background-color); +} + +a { + text-decoration: none; + color: #1967d2; +} + +header { + display: flex; + flex-direction: row; + flex-wrap: wrap; + align-items: flex-end; + justify-content: flex-start; + align-content: stretch; +} + +header description { + font-family: "Red Hat Display", Inter, Noto Sans SC, sans-serif; + font-size: var(--font-size-description); + line-height: var(--lineheight-description); + color: var(--font-color-lighter); + margin-left: 5px; +} + +header div { + line-height: var(--lineheight-description); + margin-top: 1rem; + flex-basis: 100%; +} + +header h1 { + font-size: var(--font-size-main); + line-height: var(--font-size-main); + margin: 0; +} + +code { + font-family: Fira Mono, monospace; +} + +none { + display: none; +} + +icon { + font-size: var(--icon-size) !important; +} + +footer { + color: var(--font-color-lighter); + font-size: calc(var(--font-size-description) - 0.2rem); +} + +footer > text { + margin-top: 1rem; + font-size: calc(var(--font-size-description) - 0.4rem); +} + +footer > * { + display: block; +} + +.contents { + display: flex; + flex-direction: column; + flex-wrap: wrap; + justify-content: space-between; + align-items: flex-start; +} + +.contents .group { + margin: 1rem 0; + display: flex; + flex-direction: column; + flex-wrap: wrap; + justify-content: space-between; + align-items: flex-start; + min-width: 0; +} + +.contents .content { + display: block; + min-width: 0; +} + +.contents .content h2 { + font-weight: 500; + margin-block-start: 0.5rem !important; + margin-block-end: 0.5rem !important; +} + +.contents .content h2.zh-cn { + font-weight: 900 !important; +} + +code { + padding: 0.15em 0.3em; + margin: 0; + font-size: 85%; + background-color: var(--box-color); + border-radius: 3px; + line-height: 200%; +} + +.group#pops { + display: flex; + flex-direction: row; + flex-wrap: wrap; + justify-content: space-between; + align-items: stretch; +} + +.group#pops .pop[status="working"] { + background-color: var(--working-color-background); +} + +.group.pops .pop[status="working-with-error"] { + background-color: var(--working-with-error-color-background); +} + +.group.pops .pop[status="error"] { + background-color: var(--error-color-background); +} + +.error-text { + color: var(--error-color); +} +.working-with-error-text { + color: var(--working-with-error-color); +} +.working-text { + color: var(--working-color); +} + +.group#pops .pop { + background-color: var(--box-color); + padding: 2rem; + min-height: 3rem; + min-width: 14rem; + border-radius: 9px; + flex-grow: 1; + margin-top: 1rem; + margin-right: 1rem; + flex-basis: 2rem; +} + +.pops name { + font-size: calc(var(--font-size-description) + 0.1rem); + vertical-align: middle; + font-family: "Red Hat Display", Inter, Noto Sans SC, sans-serif; +} + +.pops asn { + font-size: calc(var(--font-size-description) - 0.2rem); + color: var(--font-color-lighter); + vertical-align: middle; + display: inherit; + font-family: "Red Hat Display", Inter, Noto Sans SC, sans-serif; +} + +.pops > status > i { + vertical-align: middle; +} + +.pops ipv4, +.pops ipv6, +.pops div { + font-size: calc(var(--font-size-description) - 0.25rem); + font-family: Inter, Noto Sans SC, sans-serif; +} + +@media screen and (max-width: 480px) { + body { + margin: 6rem 2rem; + } + :root { + --font-size-main: 3rem; + --font-size-description: 1.045rem; + } + + footer { + font-size: calc(var(--font-size-description) - 0.2rem); + } + + footer > text { + font-size: calc(var(--font-size-description) - 0.4rem); + } + + header description { + width: 100%; + order: -1; + } +} + +@media screen and (max-width: 1126px) { + .contents .group { + justify-content: flex-start; + flex-direction: column; + } + .group#pops { + width: 100%; + } +} + +@media screen and (max-width: 768px) { + .contents .group { + justify-content: flex-start; + flex-direction: column; + } + .contents { + justify-content: flex-start; + flex-direction: column; + } + .group#pops { + width: 100%; + } + .pop > * { + overflow-x: auto; + overflow-y: hidden; + white-space: nowrap; + } +} + +@media screen and (min-width: 1126px) { + .group#picture { + width: 100%; + } + .contents .group#text { + flex-direction: column; + } + .contents { + justify-content: flex-start; + flex-direction: column; + } +} + +@media screen and (min-width: 768px) { + body { + margin: 8% 10%; + } + + header > * { + display: inline-block; + } +} + +@media (prefers-color-scheme: dark) { + :root { + --font-color: #e8eaed; + --font-color-lighter: #9aa0a6; + --background-color: #121212; + --box-color: rgb(40 40 40 / 73%); + --working-color-background: rgba(129, 201, 149, 0.24); + --error-color-background: rgba(242, 139, 130, 0.24); + --working-with-error-color-background: rgba(253, 214, 99, 0.24); + --working-color: #81c995; + --error-color: #f28b82; + --working-with-error-color: #fdd663; + --linear-start-color: #0c8cca; + --linear-end-color: #00bdff; + } + .contents .content#path img { + filter: invert(93%); + } +} + +.content#prefix ul { + padding: unset; +} + +.content#prefix ul > li { + list-style-type: ""; +} + +.pop > * { + display: block; +} + +::-webkit-scrollbar { + width: 0px; +} + +ul { + padding-inline: 20px; +} + +.content.loc > ul > li { + margin: 10px 0; +} + +.content.loc > ul > li > ul > li { + margin: 5px 0; +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..eac16d1 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,19 @@ +{ + "compilerOptions": { + "target": "ESNext", + "useDefineForClassFields": true, + "module": "ESNext", + "lib": ["ESNext", "DOM"], + "moduleResolution": "Node", + "strict": true, + "resolveJsonModule": true, + "isolatedModules": true, + "esModuleInterop": true, + "noEmit": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noImplicitReturns": true, + "skipLibCheck": true + }, + "include": ["src"] +} diff --git a/yarn.lock b/yarn.lock new file mode 100644 index 0000000..c1186f8 --- /dev/null +++ b/yarn.lock @@ -0,0 +1,289 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@esbuild/android-arm64@0.16.17": + version "0.16.17" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.16.17.tgz#cf91e86df127aa3d141744edafcba0abdc577d23" + integrity sha512-MIGl6p5sc3RDTLLkYL1MyL8BMRN4tLMRCn+yRJJmEDvYZ2M7tmAf80hx1kbNEUX2KJ50RRtxZ4JHLvCfuB6kBg== + +"@esbuild/android-arm@0.16.17": + version "0.16.17" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.16.17.tgz#025b6246d3f68b7bbaa97069144fb5fb70f2fff2" + integrity sha512-N9x1CMXVhtWEAMS7pNNONyA14f71VPQN9Cnavj1XQh6T7bskqiLLrSca4O0Vr8Wdcga943eThxnVp3JLnBMYtw== + +"@esbuild/android-x64@0.16.17": + version "0.16.17" + resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.16.17.tgz#c820e0fef982f99a85c4b8bfdd582835f04cd96e" + integrity sha512-a3kTv3m0Ghh4z1DaFEuEDfz3OLONKuFvI4Xqczqx4BqLyuFaFkuaG4j2MtA6fuWEFeC5x9IvqnX7drmRq/fyAQ== + +"@esbuild/darwin-arm64@0.16.17": + version "0.16.17" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.16.17.tgz#edef4487af6b21afabba7be5132c26d22379b220" + integrity sha512-/2agbUEfmxWHi9ARTX6OQ/KgXnOWfsNlTeLcoV7HSuSTv63E4DqtAc+2XqGw1KHxKMHGZgbVCZge7HXWX9Vn+w== + +"@esbuild/darwin-x64@0.16.17": + version "0.16.17" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.16.17.tgz#42829168730071c41ef0d028d8319eea0e2904b4" + integrity sha512-2By45OBHulkd9Svy5IOCZt376Aa2oOkiE9QWUK9fe6Tb+WDr8hXL3dpqi+DeLiMed8tVXspzsTAvd0jUl96wmg== + +"@esbuild/freebsd-arm64@0.16.17": + version "0.16.17" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.16.17.tgz#1f4af488bfc7e9ced04207034d398e793b570a27" + integrity sha512-mt+cxZe1tVx489VTb4mBAOo2aKSnJ33L9fr25JXpqQqzbUIw/yzIzi+NHwAXK2qYV1lEFp4OoVeThGjUbmWmdw== + +"@esbuild/freebsd-x64@0.16.17": + version "0.16.17" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.16.17.tgz#636306f19e9bc981e06aa1d777302dad8fddaf72" + integrity sha512-8ScTdNJl5idAKjH8zGAsN7RuWcyHG3BAvMNpKOBaqqR7EbUhhVHOqXRdL7oZvz8WNHL2pr5+eIT5c65kA6NHug== + +"@esbuild/linux-arm64@0.16.17": + version "0.16.17" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.16.17.tgz#a003f7ff237c501e095d4f3a09e58fc7b25a4aca" + integrity sha512-7S8gJnSlqKGVJunnMCrXHU9Q8Q/tQIxk/xL8BqAP64wchPCTzuM6W3Ra8cIa1HIflAvDnNOt2jaL17vaW+1V0g== + +"@esbuild/linux-arm@0.16.17": + version "0.16.17" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.16.17.tgz#b591e6a59d9c4fe0eeadd4874b157ab78cf5f196" + integrity sha512-iihzrWbD4gIT7j3caMzKb/RsFFHCwqqbrbH9SqUSRrdXkXaygSZCZg1FybsZz57Ju7N/SHEgPyaR0LZ8Zbe9gQ== + +"@esbuild/linux-ia32@0.16.17": + version "0.16.17" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.16.17.tgz#24333a11027ef46a18f57019450a5188918e2a54" + integrity sha512-kiX69+wcPAdgl3Lonh1VI7MBr16nktEvOfViszBSxygRQqSpzv7BffMKRPMFwzeJGPxcio0pdD3kYQGpqQ2SSg== + +"@esbuild/linux-loong64@0.16.17": + version "0.16.17" + resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.16.17.tgz#d5ad459d41ed42bbd4d005256b31882ec52227d8" + integrity sha512-dTzNnQwembNDhd654cA4QhbS9uDdXC3TKqMJjgOWsC0yNCbpzfWoXdZvp0mY7HU6nzk5E0zpRGGx3qoQg8T2DQ== + +"@esbuild/linux-mips64el@0.16.17": + version "0.16.17" + resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.16.17.tgz#4e5967a665c38360b0a8205594377d4dcf9c3726" + integrity sha512-ezbDkp2nDl0PfIUn0CsQ30kxfcLTlcx4Foz2kYv8qdC6ia2oX5Q3E/8m6lq84Dj/6b0FrkgD582fJMIfHhJfSw== + +"@esbuild/linux-ppc64@0.16.17": + version "0.16.17" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.16.17.tgz#206443a02eb568f9fdf0b438fbd47d26e735afc8" + integrity sha512-dzS678gYD1lJsW73zrFhDApLVdM3cUF2MvAa1D8K8KtcSKdLBPP4zZSLy6LFZ0jYqQdQ29bjAHJDgz0rVbLB3g== + +"@esbuild/linux-riscv64@0.16.17": + version "0.16.17" + resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.16.17.tgz#c351e433d009bf256e798ad048152c8d76da2fc9" + integrity sha512-ylNlVsxuFjZK8DQtNUwiMskh6nT0vI7kYl/4fZgV1llP5d6+HIeL/vmmm3jpuoo8+NuXjQVZxmKuhDApK0/cKw== + +"@esbuild/linux-s390x@0.16.17": + version "0.16.17" + resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.16.17.tgz#661f271e5d59615b84b6801d1c2123ad13d9bd87" + integrity sha512-gzy7nUTO4UA4oZ2wAMXPNBGTzZFP7mss3aKR2hH+/4UUkCOyqmjXiKpzGrY2TlEUhbbejzXVKKGazYcQTZWA/w== + +"@esbuild/linux-x64@0.16.17": + version "0.16.17" + resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.16.17.tgz#e4ba18e8b149a89c982351443a377c723762b85f" + integrity sha512-mdPjPxfnmoqhgpiEArqi4egmBAMYvaObgn4poorpUaqmvzzbvqbowRllQ+ZgzGVMGKaPkqUmPDOOFQRUFDmeUw== + +"@esbuild/netbsd-x64@0.16.17": + version "0.16.17" + resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.16.17.tgz#7d4f4041e30c5c07dd24ffa295c73f06038ec775" + integrity sha512-/PzmzD/zyAeTUsduZa32bn0ORug+Jd1EGGAUJvqfeixoEISYpGnAezN6lnJoskauoai0Jrs+XSyvDhppCPoKOA== + +"@esbuild/openbsd-x64@0.16.17": + version "0.16.17" + resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.16.17.tgz#970fa7f8470681f3e6b1db0cc421a4af8060ec35" + integrity sha512-2yaWJhvxGEz2RiftSk0UObqJa/b+rIAjnODJgv2GbGGpRwAfpgzyrg1WLK8rqA24mfZa9GvpjLcBBg8JHkoodg== + +"@esbuild/sunos-x64@0.16.17": + version "0.16.17" + resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.16.17.tgz#abc60e7c4abf8b89fb7a4fe69a1484132238022c" + integrity sha512-xtVUiev38tN0R3g8VhRfN7Zl42YCJvyBhRKw1RJjwE1d2emWTVToPLNEQj/5Qxc6lVFATDiy6LjVHYhIPrLxzw== + +"@esbuild/win32-arm64@0.16.17": + version "0.16.17" + resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.16.17.tgz#7b0ff9e8c3265537a7a7b1fd9a24e7bd39fcd87a" + integrity sha512-ga8+JqBDHY4b6fQAmOgtJJue36scANy4l/rL97W+0wYmijhxKetzZdKOJI7olaBaMhWt8Pac2McJdZLxXWUEQw== + +"@esbuild/win32-ia32@0.16.17": + version "0.16.17" + resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.16.17.tgz#e90fe5267d71a7b7567afdc403dfd198c292eb09" + integrity sha512-WnsKaf46uSSF/sZhwnqE4L/F89AYNMiD4YtEcYekBt9Q7nj0DiId2XH2Ng2PHM54qi5oPrQ8luuzGszqi/veig== + +"@esbuild/win32-x64@0.16.17": + version "0.16.17" + resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.16.17.tgz#c5a1a4bfe1b57f0c3e61b29883525c6da3e5c091" + integrity sha512-y+EHuSchhL7FjHgvQL/0fnnFmO4T1bhvWANX6gcnqTjtnKWbTvUMCpGnv2+t+31d7RzyEAYAd4u2fnIhHL6N/Q== + +asynckit@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== + +axios@^1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/axios/-/axios-1.2.3.tgz#31a3d824c0ebf754a004b585e5f04a5f87e6c4ff" + integrity sha512-pdDkMYJeuXLZ6Xj/Q5J3Phpe+jbGdsSzlQaFVkMQzRUL05+6+tetX8TV3p4HrU4kzuO9bt+io/yGQxuyxA/xcw== + dependencies: + follow-redirects "^1.15.0" + form-data "^4.0.0" + proxy-from-env "^1.1.0" + +combined-stream@^1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" + integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== + dependencies: + delayed-stream "~1.0.0" + +delayed-stream@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== + +esbuild@^0.16.3: + version "0.16.17" + resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.16.17.tgz#fc2c3914c57ee750635fee71b89f615f25065259" + integrity sha512-G8LEkV0XzDMNwXKgM0Jwu3nY3lSTwSGY6XbxM9cr9+s0T/qSV1q1JVPBGzm3dcjhCic9+emZDmMffkwgPeOeLg== + optionalDependencies: + "@esbuild/android-arm" "0.16.17" + "@esbuild/android-arm64" "0.16.17" + "@esbuild/android-x64" "0.16.17" + "@esbuild/darwin-arm64" "0.16.17" + "@esbuild/darwin-x64" "0.16.17" + "@esbuild/freebsd-arm64" "0.16.17" + "@esbuild/freebsd-x64" "0.16.17" + "@esbuild/linux-arm" "0.16.17" + "@esbuild/linux-arm64" "0.16.17" + "@esbuild/linux-ia32" "0.16.17" + "@esbuild/linux-loong64" "0.16.17" + "@esbuild/linux-mips64el" "0.16.17" + "@esbuild/linux-ppc64" "0.16.17" + "@esbuild/linux-riscv64" "0.16.17" + "@esbuild/linux-s390x" "0.16.17" + "@esbuild/linux-x64" "0.16.17" + "@esbuild/netbsd-x64" "0.16.17" + "@esbuild/openbsd-x64" "0.16.17" + "@esbuild/sunos-x64" "0.16.17" + "@esbuild/win32-arm64" "0.16.17" + "@esbuild/win32-ia32" "0.16.17" + "@esbuild/win32-x64" "0.16.17" + +follow-redirects@^1.15.0: + version "1.15.2" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.2.tgz#b460864144ba63f2681096f274c4e57026da2c13" + integrity sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA== + +form-data@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452" + integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.8" + mime-types "^2.1.12" + +fsevents@~2.3.2: + version "2.3.2" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" + integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== + +function-bind@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" + integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== + +has@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" + integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== + dependencies: + function-bind "^1.1.1" + +is-core-module@^2.9.0: + version "2.11.0" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.11.0.tgz#ad4cb3e3863e814523c96f3f58d26cc570ff0144" + integrity sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw== + dependencies: + has "^1.0.3" + +mime-db@1.52.0: + version "1.52.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" + integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== + +mime-types@^2.1.12: + version "2.1.35" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" + integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== + dependencies: + mime-db "1.52.0" + +nanoid@^3.3.4: + version "3.3.4" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.4.tgz#730b67e3cd09e2deacf03c027c81c9d9dbc5e8ab" + integrity sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw== + +path-parse@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" + integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== + +picocolors@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" + integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== + +postcss@^8.4.20: + version "8.4.21" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.21.tgz#c639b719a57efc3187b13a1d765675485f4134f4" + integrity sha512-tP7u/Sn/dVxK2NnruI4H9BG+x+Wxz6oeZ1cJ8P6G/PZY0IKk4k/63TDsQf2kQq3+qoJeLm2kIBUNlZe3zgb4Zg== + dependencies: + nanoid "^3.3.4" + picocolors "^1.0.0" + source-map-js "^1.0.2" + +proxy-from-env@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" + integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== + +resolve@^1.22.1: + version "1.22.1" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.1.tgz#27cb2ebb53f91abb49470a928bba7558066ac177" + integrity sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw== + dependencies: + is-core-module "^2.9.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + +rollup@^3.7.0: + version "3.10.1" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-3.10.1.tgz#56278901ed11fc2898421e8e3e2c8155bc7b40b4" + integrity sha512-3Er+yel3bZbZX1g2kjVM+FW+RUWDxbG87fcqFM5/9HbPCTpbVp6JOLn7jlxnNlbu7s/N/uDA4EV/91E2gWnxzw== + optionalDependencies: + fsevents "~2.3.2" + +source-map-js@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c" + integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw== + +supports-preserve-symlinks-flag@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" + integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== + +typescript@^4.9.3: + version "4.9.4" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.9.4.tgz#a2a3d2756c079abda241d75f149df9d561091e78" + integrity sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg== + +vite@^4.0.0: + version "4.0.4" + resolved "https://registry.yarnpkg.com/vite/-/vite-4.0.4.tgz#4612ce0b47bbb233a887a54a4ae0c6e240a0da31" + integrity sha512-xevPU7M8FU0i/80DMR+YhgrzR5KS2ORy1B4xcX/cXLsvnUWvfHuqMmVU6N0YiJ4JWGRJJsLCgjEzKjG9/GKoSw== + dependencies: + esbuild "^0.16.3" + postcss "^8.4.20" + resolve "^1.22.1" + rollup "^3.7.0" + optionalDependencies: + fsevents "~2.3.2"