Prettier reformat

This commit is contained in:
2023-05-20 21:29:27 -05:00
parent d9285647c2
commit 6e27447a99
18 changed files with 628 additions and 493 deletions

View File

@@ -2,4 +2,4 @@
A private RDAP query client built with React & Next.js.
[xevion-github]: https://github.com/Xevion
[xevion-github]: https://github.com/Xevion

View File

@@ -7,11 +7,11 @@
/** @type {import("next").NextConfig} */
const config = {
reactStrictMode: true,
swcMinify: true,
i18n: {
locales: ["en"],
defaultLocale: "en",
}
reactStrictMode: true,
swcMinify: true,
i18n: {
locales: ["en"],
defaultLocale: "en",
},
};
export default config;

View File

@@ -11,25 +11,25 @@
* @returns {number} The relative position of the value to the range as -1, 0 or 1.
*/
function compareASN(value: number, range: string): number {
const [start, end] = range.split('-', 2) as [string, string];
if (value < parseInt(start)) return -1;
if (value > parseInt(end)) return 1;
return 0;
const [start, end] = range.split("-", 2) as [string, string];
if (value < parseInt(start)) return -1;
if (value > parseInt(end)) return 1;
return 0;
}
/**
* Find the range in which a given ASN exists via binary search. If not found, -1 is used.
*/
export function findASN(asn: number, ranges: string[]) {
let start = 0;
let end = ranges.length - 1;
let start = 0;
let end = ranges.length - 1;
while (start <= end) {
const mid = Math.floor((start + end) / 2);
const comparison = compareASN(asn, ranges[mid] as string);
if (comparison == 0) return mid; // Success case
if (comparison == -1) end = mid - 1;
else start = mid + 1;
}
return -1; // Failure case
}
while (start <= end) {
const mid = Math.floor((start + end) / 2);
const comparison = compareASN(asn, ranges[mid] as string);
if (comparison == 0) return mid; // Success case
if (comparison == -1) end = mid - 1;
else start = mid + 1;
}
return -1; // Failure case
}

View File

@@ -1,30 +1,36 @@
import type {FunctionComponent} from "react";
import {useBoolean} from "usehooks-ts";
import {format, formatDistanceToNow} from "date-fns";
import type { FunctionComponent } from "react";
import { useBoolean } from "usehooks-ts";
import { format, formatDistanceToNow } from "date-fns";
type DynamicDateProps = {
value: Date | number;
absoluteFormat?: string;
}
value: Date | number;
absoluteFormat?: string;
};
/**
* A component for a toggleable switch between the absolute & human-relative date.
* @param value The date to be displayed, the Date value, or
* @param absoluteFormat Optional - the date-fns format string to use for the absolute date rendering.
*/
const DynamicDate: FunctionComponent<DynamicDateProps> = ({value, absoluteFormat}) => {
const {value: showAbsolute, toggle: toggleFormat} = useBoolean(true);
const DynamicDate: FunctionComponent<DynamicDateProps> = ({
value,
absoluteFormat,
}) => {
const { value: showAbsolute, toggle: toggleFormat } = useBoolean(true);
const date = new Date(value);
return (
<button onClick={toggleFormat}>
<span title={date.toISOString()}>
{showAbsolute
? format(date, absoluteFormat ?? "LLL do, y HH:mm:ss")
: formatDistanceToNow(date, {includeSeconds: true, addSuffix: true})}
</span>
</button>
)
}
const date = new Date(value);
return (
<button onClick={toggleFormat}>
<span title={date.toISOString()}>
{showAbsolute
? format(date, absoluteFormat ?? "LLL do, y HH:mm:ss")
: formatDistanceToNow(date, {
includeSeconds: true,
addSuffix: true,
})}
</span>
</button>
);
};
export default DynamicDate;
export default DynamicDate;

View File

@@ -1,19 +1,26 @@
import type {FunctionComponent, ReactFragment, ReactNode} from "react";
import type { FunctionComponent, ReactFragment, ReactNode } from "react";
import React from "react";
import {classNames} from "@/helpers";
import { classNames } from "@/helpers";
type PropertyProps = {
title: string | ReactNode | ReactFragment;
children: string | ReactNode | ReactFragment;
titleClass?: string;
valueClass?: string;
title: string | ReactNode | ReactFragment;
children: string | ReactNode | ReactFragment;
titleClass?: string;
valueClass?: string;
};
const Property: FunctionComponent<PropertyProps> = ({title, children, titleClass, valueClass}) => {
return <>
<dt className={titleClass}>{title}:</dt>
<dd className={classNames("mt-2 mb-2 ml-6", valueClass)}>{children}</dd>
const Property: FunctionComponent<PropertyProps> = ({
title,
children,
titleClass,
valueClass,
}) => {
return (
<>
<dt className={titleClass}>{title}:</dt>
<dd className={classNames("mt-2 mb-2 ml-6", valueClass)}>{children}</dd>
</>
}
);
};
export default Property;
export default Property;

View File

@@ -1,42 +1,45 @@
import type {FunctionComponent} from "react";
import type { FunctionComponent } from "react";
import React from "react";
import {rdapStatusInfo} from "@/constants";
import type {Domain} from "@/types";
import Events from "@/components/lookup/Events"
import { rdapStatusInfo } from "@/constants";
import type { Domain } from "@/types";
import Events from "@/components/lookup/Events";
import Property from "@/components/common/Property";
export type DomainProps = {
data: Domain;
data: Domain;
};
const DomainCard: FunctionComponent<DomainProps> = ({data}: DomainProps) => {
return <div className="card">
<div className="card-header">{data.ldhName ?? data.unicodeName} ({data.handle})</div>
<div className="card-body">
<dl>
{data.unicodeName != undefined ? <Property title="Unicode Name">
{data.unicodeName}
</Property> : null}
<Property title={data.unicodeName != undefined ? "LHD Name" : "Name"}>
{data.ldhName}
</Property>
<Property title="Handle">
{data.handle}
</Property>
<Property title="Events">
<Events key={0} data={data.events}/>
</Property>
<Property title="Status">
<ul key={2} className="list-disc">
{data.status.map((statusKey, index) =>
<li key={index}>
<span title={rdapStatusInfo[statusKey]}>{statusKey}</span>
</li>)}
</ul>
</Property>
</dl>
</div>
const DomainCard: FunctionComponent<DomainProps> = ({ data }: DomainProps) => {
return (
<div className="card">
<div className="card-header">
{data.ldhName ?? data.unicodeName} ({data.handle})
</div>
<div className="card-body">
<dl>
{data.unicodeName != undefined ? (
<Property title="Unicode Name">{data.unicodeName}</Property>
) : null}
<Property title={data.unicodeName != undefined ? "LHD Name" : "Name"}>
{data.ldhName}
</Property>
<Property title="Handle">{data.handle}</Property>
<Property title="Events">
<Events key={0} data={data.events} />
</Property>
<Property title="Status">
<ul key={2} className="list-disc">
{data.status.map((statusKey, index) => (
<li key={index}>
<span title={rdapStatusInfo[statusKey]}>{statusKey}</span>
</li>
))}
</ul>
</Property>
</dl>
</div>
</div>
}
);
};
export default DomainCard;
export default DomainCard;

View File

@@ -1,25 +1,27 @@
import type {FunctionComponent} from "react";
import type {Event} from "@/types";
import {Fragment} from "react";
import type { FunctionComponent } from "react";
import type { Event } from "@/types";
import { Fragment } from "react";
import DynamicDate from "@/components/common/DynamicDate";
export type EventsProps = {
data: Event[];
}
const Events: FunctionComponent<EventsProps> = ({data}) => {
return <dl>
{data.map(({eventAction, eventDate, eventActor}, index) => {
return <Fragment key={index}>
<dt className="font-weight-bolder">
{eventAction}:
</dt>
<dd>
<DynamicDate value={new Date(eventDate)}/>
{eventActor != null ? ` (by ${eventActor})` : null}
</dd>
</Fragment>
})}
data: Event[];
};
const Events: FunctionComponent<EventsProps> = ({ data }) => {
return (
<dl>
{data.map(({ eventAction, eventDate, eventActor }, index) => {
return (
<Fragment key={index}>
<dt className="font-weight-bolder">{eventAction}:</dt>
<dd>
<DynamicDate value={new Date(eventDate)} />
{eventActor != null ? ` (by ${eventActor})` : null}
</dd>
</Fragment>
);
})}
</dl>
}
);
};
export default Events;
export default Events;

View File

@@ -1,34 +1,46 @@
import type {FunctionComponent} from "react";
import type { FunctionComponent } from "react";
import DomainCard from "@/components/lookup/DomainCard";
import type {Domain, AutonomousNumber, Entity, Nameserver, IpNetwork} from "@/types";
import type {
Domain,
AutonomousNumber,
Entity,
Nameserver,
IpNetwork,
} from "@/types";
export type ParsedGeneric = Domain | Nameserver | Entity | AutonomousNumber | IpNetwork;
export type ParsedGeneric =
| Domain
| Nameserver
| Entity
| AutonomousNumber
| IpNetwork;
export type ObjectProps = {
data: ParsedGeneric;
data: ParsedGeneric;
};
const Generic: FunctionComponent<ObjectProps> = ({data}: ObjectProps) => {
switch (data.objectClassName) {
case "domain":
return <DomainCard data={data}/>
case "autnum":
case "entity":
case "ip network":
case "nameserver":
default:
return <div className="card my-2">
<div className="card-header">Not implemented. (
<pre>{data.objectClassName ?? "null"}</pre>
)
</div>
</div>
}
const Generic: FunctionComponent<ObjectProps> = ({ data }: ObjectProps) => {
switch (data.objectClassName) {
case "domain":
return <DomainCard data={data} />;
case "autnum":
case "entity":
case "ip network":
case "nameserver":
default:
return (
<div className="card my-2">
<div className="card-header">
Not implemented. (<pre>{data.objectClassName ?? "null"}</pre>)
</div>
</div>
);
}
// const title: string = (data.unicodeName ?? data.ldhName ?? data.handle)?.toUpperCase() ?? "Response";
// return <div className="card">
// <div className="card-header">{title}</div>
// {objectFragment}
// </div>
}
// const title: string = (data.unicodeName ?? data.ldhName ?? data.handle)?.toUpperCase() ?? "Response";
// return <div className="card">
// <div className="card-header">{title}</div>
// {objectFragment}
// </div>
};
export default Generic;
export default Generic;

View File

@@ -1,60 +1,92 @@
// see https://www.iana.org/assignments/rdap-json-values
import type {RdapStatusType, RootRegistryType, TargetType} from "@/types";
import type { RdapStatusType, RootRegistryType, TargetType } from "@/types";
export const rdapStatusInfo: Record<RdapStatusType, string> = {
"validated": "Signifies that the data of the object instance has been found to be accurate. This type of status is usually found on entity object instances to note the validity of identifying contact information.",
"renew prohibited": "Renewal or reregistration of the object instance is forbidden.",
"update prohibited": "Updates to the object instance are forbidden.",
"transfer prohibited": "Transfers of the registration from one registrar to another are forbidden. This type of status normally applies to DNR domain names.",
"delete prohibited": "Deletion of the registration of the object instance is forbidden. This type of status normally applies to DNR domain names.",
"proxy": "The registration of the object instance has been performed by a third party. This is most commonly applied to entities.",
"private": "The information of the object instance is not designated for public consumption. This is most commonly applied to entities.",
"removed": "Some of the information of the object instance has not been made available and has been removed. This is most commonly applied to entities.",
"obscured": "Some of the information of the object instance has been altered for the purposes of not readily revealing the actual information of the object instance. This is most commonly applied to entities.",
"associated": "The object instance is associated with other object instances in the registry. This is most commonly used to signify that a nameserver is associated with a domain or that an entity is associated with a network resource or domain.",
"active": "The object instance is in use. For domain names, it signifies that the domain name is published in DNS. For network and autnum registrations it signifies that they are allocated or assigned for use in operational networks. This maps to the Extensible Provisioning Protocol (EPP) [RFC5730] 'OK' status.",
"inactive": "The object instance is not in use. See 'active'.",
"locked": "Changes to the object instance cannot be made, including the association of other object instances.",
"pending create": "A request has been received for the creation of the object instance but this action is not yet complete.",
"pending renew": "A request has been received for the renewal of the object instance but this action is not yet complete.",
"pending transfer": "A request has been received for the transfer of the object instance but this action is not yet complete.",
"pending update": "A request has been received for the update or modification of the object instance but this action is not yet complete.",
"pending delete": "A request has been received for the deletion or removal of the object instance but this action is not yet complete. For domains, this might mean that the name is no longer published in DNS but has not yet been purged from the registry database.",
"add period": "This grace period is provided after the initial registration of the object. If the object is deleted by the client during this period, the server provides a credit to the client for the cost of the registration. This maps to the Domain Registry Grace Period Mapping for the Extensible Provisioning Protocol (EPP) [RFC3915] 'addPeriod' status.",
"auto renew period": "This grace period is provided after an object registration period expires and is extended (renewed) automatically by the server. If the object is deleted by the client during this period, the server provides a credit to the client for the cost of the auto renewal. This maps to the Domain Registry Grace Period Mapping for the Extensible Provisioning Protocol (EPP) [RFC3915] 'autoRenewPeriod' status.",
"client delete prohibited": "The client requested that requests to delete the object MUST be rejected. This maps to the Extensible Provisioning Protocol (EPP) Domain Name Mapping [RFC5731], Extensible Provisioning Protocol (EPP) Host Mapping [RFC5732], and Extensible Provisioning Protocol (EPP) Contact Mapping [RFC5733] 'clientDeleteProhibited' status.",
"client hold": "The client requested that the DNS delegation information MUST NOT be published for the object. This maps to the Extensible Provisioning Protocol (EPP) Domain Name Mapping [RFC5731] 'clientHold' status.",
"client renew prohibited": "The client requested that requests to renew the object MUST be rejected. This maps to the Extensible Provisioning Protocol (EPP) Domain Name Mapping [RFC5731] 'clientRenewProhibited' status.",
"client transfer prohibited": "The client requested that requests to transfer the object MUST be rejected. This maps to the Extensible Provisioning Protocol (EPP) Domain Name Mapping [RFC5731] and Extensible Provisioning Protocol (EPP) Contact Mapping [RFC5733] 'clientTransferProhibited' status.",
"client update prohibited": "The client requested that requests to update the object (other than to remove this status) MUST be rejected. This maps to the Extensible Provisioning Protocol (EPP) Domain Name Mapping [RFC5731], Extensible Provisioning Protocol (EPP) Host Mapping [RFC5732], and Extensible Provisioning Protocol (EPP) Contact Mapping [RFC5733] 'clientUpdateProhibited' status.",
"pending restore": "An object is in the process of being restored after being in the redemption period state. This maps to the Domain Registry Grace Period Mapping for the Extensible Provisioning Protocol (EPP) [RFC3915] 'pendingRestore' status.",
"redemption period": "A delete has been received, but the object has not yet been purged because an opportunity exists to restore the object and abort the deletion process. This maps to the Domain Registry Grace Period Mapping for the Extensible Provisioning Protocol (EPP) [RFC3915] 'redemptionPeriod' status.",
"renew period": "This grace period is provided after an object registration period is explicitly extended (renewed) by the client. If the object is deleted by the client during this period, the server provides a credit to the client for the cost of the renewal. This maps to the Domain Registry Grace Period Mapping for the Extensible Provisioning Protocol (EPP) [RFC3915] 'renewPeriod' status.",
"server delete prohibited": "The server set the status so that requests to delete the object MUST be rejected. This maps to the Extensible Provisioning Protocol (EPP) Domain Name Mapping [RFC5731], Extensible Provisioning Protocol (EPP) Host Mapping [RFC5732], and Extensible Provisioning Protocol (EPP) Contact Mapping [RFC5733] 'serverDeleteProhibited' status.",
"server renew prohibited": "The server set the status so that requests to renew the object MUST be rejected. This maps to the Extensible Provisioning Protocol (EPP) Domain Name Mapping [RFC5731] 'serverRenewProhibited' status.",
"server transfer prohibited": "The server set the status so that requests to transfer the object MUST be rejected. This maps to the Extensible Provisioning Protocol (EPP) Domain Name Mapping [RFC5731] and Extensible Provisioning Protocol (EPP) Contact Mapping [RFC5733] 'serverTransferProhibited' status.",
"server update prohibited": "The server set the status so that requests to update the object (other than to remove this status) MUST be rejected. This maps to the Extensible Provisioning Protocol (EPP) Domain Name Mapping [RFC5731], Extensible Provisioning Protocol (EPP) Host Mapping [RFC5732], and Extensible Provisioning Protocol (EPP) Contact Mapping [RFC5733] 'serverUpdateProhibited' status.",
"server hold": "The server set the status so that DNS delegation information MUST NOT be published for the object. This maps to the Extensible Provisioning Protocol (EPP) Domain Name Mapping [RFC5731] 'serverHold' status.",
"transfer period": "This grace period is provided after the successful transfer of object registration sponsorship from one client to another client. If the object is deleted by the client during this period, the server provides a credit to the client for the cost of the transfer. This maps to the Domain Registry Grace Period Mapping for the Extensible Provisioning Protocol (EPP) [RFC3915] 'transferPeriod' status."
validated:
"Signifies that the data of the object instance has been found to be accurate. This type of status is usually found on entity object instances to note the validity of identifying contact information.",
"renew prohibited":
"Renewal or reregistration of the object instance is forbidden.",
"update prohibited": "Updates to the object instance are forbidden.",
"transfer prohibited":
"Transfers of the registration from one registrar to another are forbidden. This type of status normally applies to DNR domain names.",
"delete prohibited":
"Deletion of the registration of the object instance is forbidden. This type of status normally applies to DNR domain names.",
proxy:
"The registration of the object instance has been performed by a third party. This is most commonly applied to entities.",
private:
"The information of the object instance is not designated for public consumption. This is most commonly applied to entities.",
removed:
"Some of the information of the object instance has not been made available and has been removed. This is most commonly applied to entities.",
obscured:
"Some of the information of the object instance has been altered for the purposes of not readily revealing the actual information of the object instance. This is most commonly applied to entities.",
associated:
"The object instance is associated with other object instances in the registry. This is most commonly used to signify that a nameserver is associated with a domain or that an entity is associated with a network resource or domain.",
active:
"The object instance is in use. For domain names, it signifies that the domain name is published in DNS. For network and autnum registrations it signifies that they are allocated or assigned for use in operational networks. This maps to the Extensible Provisioning Protocol (EPP) [RFC5730] 'OK' status.",
inactive: "The object instance is not in use. See 'active'.",
locked:
"Changes to the object instance cannot be made, including the association of other object instances.",
"pending create":
"A request has been received for the creation of the object instance but this action is not yet complete.",
"pending renew":
"A request has been received for the renewal of the object instance but this action is not yet complete.",
"pending transfer":
"A request has been received for the transfer of the object instance but this action is not yet complete.",
"pending update":
"A request has been received for the update or modification of the object instance but this action is not yet complete.",
"pending delete":
"A request has been received for the deletion or removal of the object instance but this action is not yet complete. For domains, this might mean that the name is no longer published in DNS but has not yet been purged from the registry database.",
"add period":
"This grace period is provided after the initial registration of the object. If the object is deleted by the client during this period, the server provides a credit to the client for the cost of the registration. This maps to the Domain Registry Grace Period Mapping for the Extensible Provisioning Protocol (EPP) [RFC3915] 'addPeriod' status.",
"auto renew period":
"This grace period is provided after an object registration period expires and is extended (renewed) automatically by the server. If the object is deleted by the client during this period, the server provides a credit to the client for the cost of the auto renewal. This maps to the Domain Registry Grace Period Mapping for the Extensible Provisioning Protocol (EPP) [RFC3915] 'autoRenewPeriod' status.",
"client delete prohibited":
"The client requested that requests to delete the object MUST be rejected. This maps to the Extensible Provisioning Protocol (EPP) Domain Name Mapping [RFC5731], Extensible Provisioning Protocol (EPP) Host Mapping [RFC5732], and Extensible Provisioning Protocol (EPP) Contact Mapping [RFC5733] 'clientDeleteProhibited' status.",
"client hold":
"The client requested that the DNS delegation information MUST NOT be published for the object. This maps to the Extensible Provisioning Protocol (EPP) Domain Name Mapping [RFC5731] 'clientHold' status.",
"client renew prohibited":
"The client requested that requests to renew the object MUST be rejected. This maps to the Extensible Provisioning Protocol (EPP) Domain Name Mapping [RFC5731] 'clientRenewProhibited' status.",
"client transfer prohibited":
"The client requested that requests to transfer the object MUST be rejected. This maps to the Extensible Provisioning Protocol (EPP) Domain Name Mapping [RFC5731] and Extensible Provisioning Protocol (EPP) Contact Mapping [RFC5733] 'clientTransferProhibited' status.",
"client update prohibited":
"The client requested that requests to update the object (other than to remove this status) MUST be rejected. This maps to the Extensible Provisioning Protocol (EPP) Domain Name Mapping [RFC5731], Extensible Provisioning Protocol (EPP) Host Mapping [RFC5732], and Extensible Provisioning Protocol (EPP) Contact Mapping [RFC5733] 'clientUpdateProhibited' status.",
"pending restore":
"An object is in the process of being restored after being in the redemption period state. This maps to the Domain Registry Grace Period Mapping for the Extensible Provisioning Protocol (EPP) [RFC3915] 'pendingRestore' status.",
"redemption period":
"A delete has been received, but the object has not yet been purged because an opportunity exists to restore the object and abort the deletion process. This maps to the Domain Registry Grace Period Mapping for the Extensible Provisioning Protocol (EPP) [RFC3915] 'redemptionPeriod' status.",
"renew period":
"This grace period is provided after an object registration period is explicitly extended (renewed) by the client. If the object is deleted by the client during this period, the server provides a credit to the client for the cost of the renewal. This maps to the Domain Registry Grace Period Mapping for the Extensible Provisioning Protocol (EPP) [RFC3915] 'renewPeriod' status.",
"server delete prohibited":
"The server set the status so that requests to delete the object MUST be rejected. This maps to the Extensible Provisioning Protocol (EPP) Domain Name Mapping [RFC5731], Extensible Provisioning Protocol (EPP) Host Mapping [RFC5732], and Extensible Provisioning Protocol (EPP) Contact Mapping [RFC5733] 'serverDeleteProhibited' status.",
"server renew prohibited":
"The server set the status so that requests to renew the object MUST be rejected. This maps to the Extensible Provisioning Protocol (EPP) Domain Name Mapping [RFC5731] 'serverRenewProhibited' status.",
"server transfer prohibited":
"The server set the status so that requests to transfer the object MUST be rejected. This maps to the Extensible Provisioning Protocol (EPP) Domain Name Mapping [RFC5731] and Extensible Provisioning Protocol (EPP) Contact Mapping [RFC5733] 'serverTransferProhibited' status.",
"server update prohibited":
"The server set the status so that requests to update the object (other than to remove this status) MUST be rejected. This maps to the Extensible Provisioning Protocol (EPP) Domain Name Mapping [RFC5731], Extensible Provisioning Protocol (EPP) Host Mapping [RFC5732], and Extensible Provisioning Protocol (EPP) Contact Mapping [RFC5733] 'serverUpdateProhibited' status.",
"server hold":
"The server set the status so that DNS delegation information MUST NOT be published for the object. This maps to the Extensible Provisioning Protocol (EPP) Domain Name Mapping [RFC5731] 'serverHold' status.",
"transfer period":
"This grace period is provided after the successful transfer of object registration sponsorship from one client to another client. If the object is deleted by the client during this period, the server provides a credit to the client for the cost of the transfer. This maps to the Domain Registry Grace Period Mapping for the Extensible Provisioning Protocol (EPP) [RFC3915] 'transferPeriod' status.",
};
// list of RDAP bootstrap registry URLs
export const registryURLs: Record<RootRegistryType, string> = {
"autnum": "https://data.iana.org/rdap/asn.json",
"domain": "https://data.iana.org/rdap/dns.json",
"ip4": "https://data.iana.org/rdap/ipv4.json",
"ip6": "https://data.iana.org/rdap/ipv6.json",
"entity": "https://data.iana.org/rdap/object-tags.json",
autnum: "https://data.iana.org/rdap/asn.json",
domain: "https://data.iana.org/rdap/dns.json",
ip4: "https://data.iana.org/rdap/ipv4.json",
ip6: "https://data.iana.org/rdap/ipv6.json",
entity: "https://data.iana.org/rdap/object-tags.json",
};
export const placeholders: Record<TargetType, string> = {
'ip4': '192.168.0.1/16',
'ip6': 'TODO: Complete this placeholder',
'autnum': '65535',
'entity': 'ABC123-EXAMPLE',
'url': 'https://rdap.org/domain/example.com',
'tld': 'example',
'registrar': '9999',
'json': '{ (paste JSON) }',
'domain': 'example.com'
}
ip4: "192.168.0.1/16",
ip6: "TODO: Complete this placeholder",
autnum: "65535",
entity: "ABC123-EXAMPLE",
url: "https://rdap.org/domain/example.com",
tld: "example",
registrar: "9999",
json: "{ (paste JSON) }",
domain: "example.com",
};

6
src/env/client.mjs vendored
View File

@@ -5,7 +5,7 @@ const _clientEnv = clientSchema.safeParse(clientEnv);
export const formatErrors = (
/** @type {import('zod').ZodFormattedError<Map<string,string>,string>} */
errors,
errors
) =>
Object.entries(errors)
.map(([name, value]) => {
@@ -17,7 +17,7 @@ export const formatErrors = (
if (!_clientEnv.success) {
console.error(
"❌ Invalid environment variables:\n",
...formatErrors(_clientEnv.error.format()),
...formatErrors(_clientEnv.error.format())
);
throw new Error("Invalid environment variables");
}
@@ -25,7 +25,7 @@ if (!_clientEnv.success) {
for (let key of Object.keys(_clientEnv.data)) {
if (!key.startsWith("NEXT_PUBLIC_")) {
console.warn(
`❌ Invalid public environment variable name: ${key}. It must begin with 'NEXT_PUBLIC_'`,
`❌ Invalid public environment variable name: ${key}. It must begin with 'NEXT_PUBLIC_'`
);
throw new Error("Invalid public environment variable name");

2
src/env/server.mjs vendored
View File

@@ -11,7 +11,7 @@ const _serverEnv = serverSchema.safeParse(serverEnv);
if (!_serverEnv.success) {
console.error(
"❌ Invalid environment variables:\n",
...formatErrors(_serverEnv.error.format()),
...formatErrors(_serverEnv.error.format())
);
throw new Error("Invalid environment variables");
}

View File

@@ -1,18 +1,18 @@
import type {SyntheticEvent} from "react";
import type { SyntheticEvent } from "react";
export function truthy(value: string | null | undefined) {
if (value == undefined) return false;
return value.toLowerCase() == 'true' || value == '1';
if (value == undefined) return false;
return value.toLowerCase() == "true" || value == "1";
}
export function onPromise<T>(promise: (event: SyntheticEvent) => Promise<T>) {
return (event: SyntheticEvent) => {
if (promise) {
promise(event).catch((error) => {
console.log("Unexpected error", error);
});
}
};
return (event: SyntheticEvent) => {
if (promise) {
promise(event).catch((error) => {
console.log("Unexpected error", error);
});
}
};
}
/**
@@ -22,10 +22,12 @@ export function onPromise<T>(promise: (event: SyntheticEvent) => Promise<T>) {
* @param maxLength A positive number representing the maximum length the input string should be.
* @param ellipsis A string representing what should be placed on the end when the max length is hit.
*/
export function truncated(input: string, maxLength: number, ellipsis = '...') {
if (maxLength <= 0) return '';
if (input.length <= maxLength) return input;
return input.substring(0, Math.max(0, maxLength - ellipsis.length)) + ellipsis;
export function truncated(input: string, maxLength: number, ellipsis = "...") {
if (maxLength <= 0) return "";
if (input.length <= maxLength) return input;
return (
input.substring(0, Math.max(0, maxLength - ellipsis.length)) + ellipsis
);
}
/**
@@ -33,5 +35,5 @@ export function truncated(input: string, maxLength: number, ellipsis = '...') {
* @param classes
*/
export function classNames(...classes: (string | null | undefined)[]) {
return classes.filter(Boolean).join(" ");
return classes.filter(Boolean).join(" ");
}

View File

@@ -1,163 +1,193 @@
import {useEffect, useMemo, useRef, useState} from "react";
import {domainMatchPredicate, getBestURL, getType} from "@/rdap";
import type {AutonomousNumber, Domain, IpNetwork, Register, RootRegistryType, TargetType} from "@/types";
import {registryURLs} from "@/constants";
import {AutonomousNumberSchema, DomainSchema, IpNetworkSchema, RegisterSchema, RootRegistryEnum} from "@/schema";
import {truncated} from "@/helpers";
import type {ZodSchema} from "zod";
import type {ParsedGeneric} from "@/components/lookup/Generic";
import { useEffect, useMemo, useRef, useState } from "react";
import { domainMatchPredicate, getBestURL, getType } from "@/rdap";
import type {
AutonomousNumber,
Domain,
IpNetwork,
Register,
RootRegistryType,
TargetType,
} from "@/types";
import { registryURLs } from "@/constants";
import {
AutonomousNumberSchema,
DomainSchema,
IpNetworkSchema,
RegisterSchema,
RootRegistryEnum,
} from "@/schema";
import { truncated } from "@/helpers";
import type { ZodSchema } from "zod";
import type { ParsedGeneric } from "@/components/lookup/Generic";
export type WarningHandler = (warning: { message: string }) => void;
const useLookup = (warningHandler?: WarningHandler) => {
const registryDataRef = useRef<Record<RootRegistryType, Register | null>>({} as Record<TargetType, Register>)
const [error, setError] = useState<string | null>(null);
const [target, setTarget] = useState<string>("");
const registryDataRef = useRef<Record<RootRegistryType, Register | null>>(
{} as Record<TargetType, Register>
);
const [error, setError] = useState<string | null>(null);
const [target, setTarget] = useState<string>("");
const uriType = useMemo<TargetType | 'unknown'>(function () {
return getType(target) ?? 'unknown';
}, [target]);
const uriType = useMemo<TargetType | "unknown">(
function () {
return getType(target) ?? "unknown";
},
[target]
);
// Fetch & load a specific registry's data into memory.
async function loadBootstrap(type: RootRegistryType, force = false) {
// Early preload exit condition
if (registryDataRef.current[type] != null && !force)
return;
// Fetch & load a specific registry's data into memory.
async function loadBootstrap(type: RootRegistryType, force = false) {
// Early preload exit condition
if (registryDataRef.current[type] != null && !force) return;
// Fetch the bootstrapping file from the registry
const response = await fetch(registryURLs[type]);
if (response.status != 200)
throw new Error(`Error: ${response.statusText}`)
// Fetch the bootstrapping file from the registry
const response = await fetch(registryURLs[type]);
if (response.status != 200)
throw new Error(`Error: ${response.statusText}`);
// Parse it, so we don't make any false assumptions during development & while maintaining the tool.
const parsedRegister = RegisterSchema.safeParse(await response.json());
if (!parsedRegister.success)
throw new Error(`Could not parse IANA bootstrap response (type: ${type}).`)
// Parse it, so we don't make any false assumptions during development & while maintaining the tool.
const parsedRegister = RegisterSchema.safeParse(await response.json());
if (!parsedRegister.success)
throw new Error(
`Could not parse IANA bootstrap response (type: ${type}).`
);
// Set it in state so we can use it.
registryDataRef.current = {
...registryDataRef.current,
[type]: parsedRegister.data
// Set it in state so we can use it.
registryDataRef.current = {
...registryDataRef.current,
[type]: parsedRegister.data,
};
}
function getRegistryURL(
type: RootRegistryType,
lookupTarget: string
): string {
const bootstrap = registryDataRef.current[type];
if (bootstrap == null)
throw new Error(
`Cannot acquire RDAP URL without bootstrap data for ${type} lookup.`
);
let url: string | null = null;
typeSwitch: switch (type) {
case "domain":
for (const bootstrapItem of bootstrap.services) {
if (bootstrapItem[0].some(domainMatchPredicate(lookupTarget))) {
url = getBestURL(bootstrapItem[1]);
break typeSwitch;
}
}
throw new Error(`No matching domain found.`);
case "ip4":
throw new Error(`No matching ip4 found.`);
case "ip6":
throw new Error(`No matching ip6 found.`);
case "entity":
throw new Error(`No matching entity found.`);
case "autnum":
throw new Error(`No matching autnum found.`);
default:
throw new Error("Invalid lookup target provided.");
}
function getRegistryURL(type: RootRegistryType, lookupTarget: string): string {
const bootstrap = registryDataRef.current[type];
if (bootstrap == null) throw new Error(`Cannot acquire RDAP URL without bootstrap data for ${type} lookup.`)
if (url == null) throw new Error("No lookup target was resolved.");
let url: string | null = null;
return `${url}${type}/${lookupTarget}`;
}
typeSwitch:
switch (type) {
case "domain":
for (const bootstrapItem of bootstrap.services) {
if (bootstrapItem[0].some(domainMatchPredicate(lookupTarget))) {
url = getBestURL(bootstrapItem[1]);
break typeSwitch;
}
}
throw new Error(`No matching domain found.`)
case "ip4":
throw new Error(`No matching ip4 found.`)
case "ip6":
throw new Error(`No matching ip6 found.`)
case "entity":
throw new Error(`No matching entity found.`)
case "autnum":
throw new Error(`No matching autnum found.`)
default:
throw new Error("Invalid lookup target provided.")
}
useEffect(() => {
const preload = async () => {
if (uriType === "unknown") return;
const registryUri = RootRegistryEnum.safeParse(uriType);
if (!registryUri.success) return;
console.log({
registryData: registryDataRef.current,
registryUri: registryUri.data,
});
if (registryDataRef.current[registryUri.data] != null) return;
if (url == null) throw new Error('No lookup target was resolved.')
return `${url}${type}/${lookupTarget}`;
}
useEffect(() => {
const preload = async () => {
if (uriType === 'unknown') return;
const registryUri = RootRegistryEnum.safeParse(uriType);
if (!registryUri.success) return;
console.log({registryData: registryDataRef.current, registryUri: registryUri.data});
if (registryDataRef.current[registryUri.data] != null) return;
try {
await loadBootstrap(registryUri.data);
} catch (e) {
if (warningHandler != undefined) {
const message = e instanceof Error ? `(${truncated(e.message, 15)})` : '.';
warningHandler({
message: `Failed to preload registry${message}`
})
}
}
try {
await loadBootstrap(registryUri.data);
} catch (e) {
if (warningHandler != undefined) {
const message =
e instanceof Error ? `(${truncated(e.message, 15)})` : ".";
warningHandler({
message: `Failed to preload registry${message}`,
});
}
}
};
preload().catch(console.error);
}, [target]);
preload().catch(console.error);
}, [target]);
async function getAndParse<T>(url: string, schema: ZodSchema): Promise<T | undefined> {
const response = await fetch(url);
if (response.status == 200)
return schema.parse(await response.json()) as T
async function getAndParse<T>(
url: string,
schema: ZodSchema
): Promise<T | undefined> {
const response = await fetch(url);
if (response.status == 200) return schema.parse(await response.json()) as T;
}
async function submitInternal(): Promise<ParsedGeneric | undefined> {
if (target == null)
throw new Error("A target must be given in order to execute a lookup.");
const targetType = getType(target);
switch (targetType) {
// Block scoped case to allow url const reuse
case "ip4": {
await loadBootstrap("ip4");
const url = getRegistryURL(targetType, target);
return await getAndParse<IpNetwork>(url, IpNetworkSchema);
}
case "ip6": {
await loadBootstrap("ip6");
const url = getRegistryURL(targetType, target);
return await getAndParse<IpNetwork>(url, IpNetworkSchema);
}
case "domain": {
await loadBootstrap("domain");
const url = getRegistryURL(targetType, target);
return await getAndParse<Domain>(url, DomainSchema);
}
case "autnum": {
await loadBootstrap("autnum");
const url = getRegistryURL(targetType, target);
return await getAndParse<AutonomousNumber>(url, AutonomousNumberSchema);
}
case null:
throw new Error("The type could not be detected given the target.");
case "url":
case "tld":
case "registrar":
case "json":
default:
throw new Error("The type detected has not been implemented.");
}
}
async function submitInternal(): Promise<ParsedGeneric | undefined> {
if (target == null)
throw new Error("A target must be given in order to execute a lookup.")
async function submit(): Promise<ParsedGeneric | undefined> {
try {
const response = await submitInternal();
if (response == undefined)
throw new Error("Internal submission failed to yield any data.");
const targetType = getType(target);
switch (targetType) {
// Block scoped case to allow url const reuse
case "ip4": {
await loadBootstrap("ip4");
const url = getRegistryURL(targetType, target);
return await getAndParse<IpNetwork>(url, IpNetworkSchema)
}
case "ip6": {
await loadBootstrap("ip6");
const url = getRegistryURL(targetType, target);
return await getAndParse<IpNetwork>(url, IpNetworkSchema);
}
case "domain": {
await loadBootstrap("domain");
const url = getRegistryURL(targetType, target);
return await getAndParse<Domain>(url, DomainSchema);
}
case "autnum": {
await loadBootstrap("autnum");
const url = getRegistryURL(targetType, target);
return await getAndParse<AutonomousNumber>(url, AutonomousNumberSchema);
}
case null:
throw new Error("The type could not be detected given the target.")
case "url":
case "tld":
case "registrar":
case "json":
default:
throw new Error("The type detected has not been implemented.")
}
setError(null);
return response;
} catch (e) {
if (!(e instanceof Error))
setError("An unknown, unprocessable error has occurred.");
setError((e as Error).message);
}
}
async function submit(): Promise<ParsedGeneric | undefined> {
try {
const response = await submitInternal();
if (response == undefined)
throw new Error("Internal submission failed to yield any data.")
return { error, setTarget, submit, currentType: uriType };
};
setError(null);
return response;
} catch (e) {
if (!(e instanceof Error))
setError("An unknown, unprocessable error has occurred.");
setError((e as Error).message);
}
}
return {error, setTarget, submit, currentType: uriType};
}
export default useLookup;
export default useLookup;

View File

@@ -1,22 +1,22 @@
import type {TargetType} from "@/types";
import type { TargetType } from "@/types";
// keeps track of the elements we've created so we can assign a unique ID
// let elementCounter = 123456;
const cardTitles = {
"domain": "Domain Name",
"ip network": "IP Network",
"nameserver": "Nameserver",
"entity": "Entity",
"autnum": "AS Number",
domain: "Domain Name",
"ip network": "IP Network",
nameserver: "Nameserver",
entity: "Entity",
autnum: "AS Number",
};
export function domainMatchPredicate(domain: string): (tld: string) => boolean {
return (tld) => domainMatch(tld, domain);
return (tld) => domainMatch(tld, domain);
}
export function domainMatch(tld: string, domain: string): boolean {
return domain.toUpperCase().endsWith(`.${tld.toUpperCase()}`);
return domain.toUpperCase().endsWith(`.${tld.toUpperCase()}`);
}
/*
@@ -45,11 +45,10 @@ export function ipMatch(prefix: string, ip: string) {
// return the first HTTPS url, or the first URL
export function getBestURL(urls: string[]): string {
urls.forEach((url) => {
if (url.startsWith('https://'))
return url;
})
return urls[0]!;
urls.forEach((url) => {
if (url.startsWith("https://")) return url;
});
return urls[0]!;
}
// given a URL, injects that URL into the query input,
@@ -736,7 +735,7 @@ export function processUnknown(object, dl, toplevel = false) {
}
*/
// given an object, return the "self" URL (if any)
// given an object, return the "self" URL (if any)
/*
export function getSelfLink(object) {
if (object.links) for (let i = 0; i < object.links.length; i++) if ('self' == object.links[i].rel) return object.links[i].href;
@@ -762,19 +761,21 @@ export function createRDAPLink(url, title) {
// TODO: Provide full domain, TLD, Ipv4 & Ipv6 validators
const URIPatterns: [RegExp, TargetType][] = [
[/^\d+$/, "autnum"],
[/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\/?\d*$/, "ip4"],
[/^[0-9a-f:]{2,}\/?\d*$/, "ip6"],
[/^https?:/, "url"],
[/^{/, "json"],
[/^\.\w+$/, "tld"],
[/[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?/, "domain"],
[/^\d+$/, "autnum"],
[/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\/?\d*$/, "ip4"],
[/^[0-9a-f:]{2,}\/?\d*$/, "ip6"],
[/^https?:/, "url"],
[/^{/, "json"],
[/^\.\w+$/, "tld"],
[
/[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?/,
"domain",
],
];
export function getType(value: string): TargetType | null {
for (const [pattern, type] of URIPatterns) {
if (pattern.test(value))
return type;
}
return null;
}
for (const [pattern, type] of URIPatterns) {
if (pattern.test(value)) return type;
}
return null;
}

View File

@@ -1,111 +1,167 @@
import {z} from "zod";
import { z } from "zod";
export const ObjectTypeEnum = z.enum(['ip', 'autnum', 'entity', 'url', 'tld', 'registrar', 'json', 'domain'])
export const RootRegistryEnum = z.enum(['autnum', 'domain', 'ip4', 'ip6', 'entity'])
export const StatusEnum = z.enum(["validated", "renew prohibited", "update prohibited", "transfer prohibited", "delete prohibited", "proxy", "private", "removed", "obscured", "associated", "active", "inactive", "locked", "pending create", "pending renew", "pending transfer", "pending update", "pending delete", "add period", "auto renew period", "client delete prohibited", "client hold", "client renew prohibited", "client transfer prohibited", "client update prohibited", "pending restore", "redemption period", "renew period", "server delete prohibited", "server renew prohibited", "server transfer prohibited", "server update prohibited", "server hold", "transfer period"])
export const ObjectTypeEnum = z.enum([
"ip",
"autnum",
"entity",
"url",
"tld",
"registrar",
"json",
"domain",
]);
export const RootRegistryEnum = z.enum([
"autnum",
"domain",
"ip4",
"ip6",
"entity",
]);
export const StatusEnum = z.enum([
"validated",
"renew prohibited",
"update prohibited",
"transfer prohibited",
"delete prohibited",
"proxy",
"private",
"removed",
"obscured",
"associated",
"active",
"inactive",
"locked",
"pending create",
"pending renew",
"pending transfer",
"pending update",
"pending delete",
"add period",
"auto renew period",
"client delete prohibited",
"client hold",
"client renew prohibited",
"client transfer prohibited",
"client update prohibited",
"pending restore",
"redemption period",
"renew period",
"server delete prohibited",
"server renew prohibited",
"server transfer prohibited",
"server update prohibited",
"server hold",
"transfer period",
]);
export const LinkSchema = z.object({
value: z.string().optional(),
rel: z.string(),
href: z.string(),
type: z.string()
})
value: z.string().optional(),
rel: z.string(),
href: z.string(),
type: z.string(),
});
export const EntitySchema = z.object({
objectClassName: z.literal('entity'),
handle: z.string(),
roles: z.array(z.string()),
publicIds: z.array(z.object({
objectClassName: z.literal("entity"),
handle: z.string(),
roles: z.array(z.string()),
publicIds: z
.array(
z.object({
type: z.string(),
identifier: z.string(),
})).optional()
})
})
)
.optional(),
});
export const NameserverSchema = z.object({
objectClassName: z.literal('nameserver'),
ldhName: z.string()
})
objectClassName: z.literal("nameserver"),
ldhName: z.string(),
});
export const EventSchema = z.object({
eventAction: z.string(),
eventActor: z.string().optional(),
eventDate: z.string()
})
eventAction: z.string(),
eventActor: z.string().optional(),
eventDate: z.string(),
});
export const NoticeSchema = z.object({
title: z.string().optional(),
description: z.string().array(),
links: z.array(z.object({
title: z.string().optional(),
description: z.string().array(),
links: z
.array(
z.object({
href: z.string(),
type: z.string()
})).optional()
})
type: z.string(),
})
)
.optional(),
});
export type Notice = z.infer<typeof NoticeSchema>;
export const IpNetworkSchema = z.object({
objectClassName: z.literal('ip network'),
handle: z.string(),
startAddress: z.string(),
endAddress: z.string(),
ipVersion: z.enum(['v4', 'v6']),
name: z.string(),
type: z.string(),
country: z.string(),
parentHandle: z.string(),
status: z.string().array(),
entities: z.array(EntitySchema),
remarks: z.any(),
links: z.any(),
port43: z.any().optional(),
events: z.array(EventSchema)
})
objectClassName: z.literal("ip network"),
handle: z.string(),
startAddress: z.string(),
endAddress: z.string(),
ipVersion: z.enum(["v4", "v6"]),
name: z.string(),
type: z.string(),
country: z.string(),
parentHandle: z.string(),
status: z.string().array(),
entities: z.array(EntitySchema),
remarks: z.any(),
links: z.any(),
port43: z.any().optional(),
events: z.array(EventSchema),
});
export const AutonomousNumberSchema = z.object({
objectClassName: z.literal('autnum'),
handle: z.string(),
startAutnum: z.number().positive(), // TODO: 32bit
endAutnum: z.number().positive(), // TODO: 32bit
name: z.string(),
type: z.string(),
status: z.array(z.string()),
country: z.string().length(2),
events: z.array(EventSchema),
entities: z.array(EntitySchema),
roles: z.array(z.string()),
links: z.array(LinkSchema)
})
objectClassName: z.literal("autnum"),
handle: z.string(),
startAutnum: z.number().positive(), // TODO: 32bit
endAutnum: z.number().positive(), // TODO: 32bit
name: z.string(),
type: z.string(),
status: z.array(z.string()),
country: z.string().length(2),
events: z.array(EventSchema),
entities: z.array(EntitySchema),
roles: z.array(z.string()),
links: z.array(LinkSchema),
});
export const DomainSchema = z.object({
objectClassName: z.literal('domain'),
handle: z.string(),
ldhName: z.string(),
unicodeName: z.string().optional(),
links: z.array(LinkSchema).optional(),
status: z.array(StatusEnum),
entities: z.array(EntitySchema),
events: z.array(EventSchema),
secureDNS: z.any(), // TODO: Complete schema
nameservers: z.array(NameserverSchema),
rdapConformance: z.string().array(), // TODO: Complete
notices: z.array(NoticeSchema),
network: IpNetworkSchema.optional(),
})
objectClassName: z.literal("domain"),
handle: z.string(),
ldhName: z.string(),
unicodeName: z.string().optional(),
links: z.array(LinkSchema).optional(),
status: z.array(StatusEnum),
entities: z.array(EntitySchema),
events: z.array(EventSchema),
secureDNS: z.any(), // TODO: Complete schema
nameservers: z.array(NameserverSchema),
rdapConformance: z.string().array(), // TODO: Complete
notices: z.array(NoticeSchema),
network: IpNetworkSchema.optional(),
});
const RegistrarSchema = z.tuple([
z.array(z.string()).min(1),
z.array(z.string()).min(1)
]).or(z.tuple([
z.array(z.string()).min(1),
z.array(z.string()).min(1),
z.array(z.string()).min(1)
]))
const RegistrarSchema = z
.tuple([z.array(z.string()).min(1), z.array(z.string()).min(1)])
.or(
z.tuple([
z.array(z.string()).min(1),
z.array(z.string()).min(1),
z.array(z.string()).min(1),
])
);
export const RegisterSchema = z.object({
description: z.string(),
publication: z.string(),
services: z.array(RegistrarSchema),
version: z.string()
description: z.string(),
publication: z.string(),
services: z.array(RegistrarSchema),
version: z.string(),
});

View File

@@ -1,21 +1,21 @@
import type {z} from "zod";
import type { z } from "zod";
import type {
AutonomousNumberSchema,
DomainSchema,
EntitySchema,
EventSchema,
IpNetworkSchema,
LinkSchema,
NameserverSchema,
ObjectTypeEnum,
RegisterSchema,
StatusEnum,
RootRegistryEnum
AutonomousNumberSchema,
DomainSchema,
EntitySchema,
EventSchema,
IpNetworkSchema,
LinkSchema,
NameserverSchema,
ObjectTypeEnum,
RegisterSchema,
StatusEnum,
RootRegistryEnum,
} from "@/schema";
export type ObjectType = z.infer<typeof ObjectTypeEnum>
export type ObjectType = z.infer<typeof ObjectTypeEnum>;
export type RootRegistryType = z.infer<typeof RootRegistryEnum>;
export type TargetType = Exclude<ObjectType, 'ip'> | 'ip4' | 'ip6';
export type TargetType = Exclude<ObjectType, "ip"> | "ip4" | "ip6";
export type RdapStatusType = z.infer<typeof StatusEnum>;
export type Link = z.infer<typeof LinkSchema>;

View File

@@ -5,8 +5,8 @@ module.exports = {
extend: {
colors: {
zinc: {
850: "#1D1D20"
}
850: "#1D1D20",
},
},
},
},

View File

@@ -1,11 +1,7 @@
{
"compilerOptions": {
"target": "es2017",
"lib": [
"dom",
"dom.iterable",
"esnext"
],
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
@@ -21,22 +17,10 @@
"noUncheckedIndexedAccess": true,
"baseUrl": "./src/",
"paths": {
"@/config/*": [
"../config/*"
],
"@/*": [
"./*"
]
"@/config/*": ["../config/*"],
"@/*": ["./*"]
}
},
"include": [
"next-env.d.ts",
"**/*.ts",
"**/*.tsx",
"**/*.cjs",
"**/*.mjs"
],
"exclude": [
"node_modules"
]
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", "**/*.cjs", "**/*.mjs"],
"exclude": ["node_modules"]
}