mirror of
https://github.com/jakejarvis/domainstack.io.git
synced 2026-09-11 05:05:34 -04:00
chore: refine oxlint rules and fix resulting fallout
This commit is contained in:
@@ -37,10 +37,6 @@ jobs:
|
||||
continue-on-error: true
|
||||
run: pnpm lint
|
||||
|
||||
- name: Run typecheck
|
||||
continue-on-error: true
|
||||
run: pnpm check-types
|
||||
|
||||
- name: Run tests with coverage report
|
||||
continue-on-error: true
|
||||
run: pnpm test:coverage
|
||||
|
||||
+60
-2
@@ -1,11 +1,48 @@
|
||||
{
|
||||
"$schema": "./node_modules/oxlint/configuration_schema.json",
|
||||
"plugins": ["oxc", "eslint", "typescript", "react", "nextjs", "import", "unicorn", "vitest"],
|
||||
"plugins": [
|
||||
"oxc",
|
||||
"eslint",
|
||||
"typescript",
|
||||
"unicorn",
|
||||
"import",
|
||||
"node",
|
||||
"promise",
|
||||
"jsdoc",
|
||||
"vitest",
|
||||
"react",
|
||||
"jsx-a11y",
|
||||
"nextjs"
|
||||
],
|
||||
"categories": {
|
||||
"correctness": "error",
|
||||
"suspicious": "warn",
|
||||
"perf": "warn"
|
||||
},
|
||||
"options": {
|
||||
"typeAware": true,
|
||||
"typeCheck": true,
|
||||
"reportUnusedDisableDirectives": "error"
|
||||
},
|
||||
"env": {
|
||||
"builtin": true,
|
||||
"node": true
|
||||
},
|
||||
"settings": {
|
||||
"next": {
|
||||
"rootDir": "apps/web"
|
||||
},
|
||||
"react": {
|
||||
"version": "19.2.8",
|
||||
"linkComponents": [{ "name": "Link", "attribute": "href" }]
|
||||
},
|
||||
"jsx-a11y": {
|
||||
"components": {
|
||||
"Link": "a",
|
||||
"Button": "button"
|
||||
}
|
||||
}
|
||||
},
|
||||
"rules": {
|
||||
"import/no-named-as-default-member": "off",
|
||||
"import/no-unassigned-import": "off",
|
||||
@@ -21,7 +58,28 @@
|
||||
"unicorn/consistent-function-scoping": "off",
|
||||
"unicorn/filename-case": "off",
|
||||
"unicorn/no-array-sort": "off",
|
||||
"unicorn/no-null": "off"
|
||||
"unicorn/no-null": "off",
|
||||
"typescript/no-unsafe-type-assertion": "off",
|
||||
"typescript/consistent-return": "off",
|
||||
"typescript/no-unnecessary-type-conversion": "off",
|
||||
"typescript/no-unnecessary-type-parameters": "off",
|
||||
"jsdoc/check-tag-names": "off",
|
||||
"jsx-a11y/prefer-tag-over-role": "off",
|
||||
"jsx-a11y/control-has-associated-label": "off",
|
||||
"jsx-a11y/no-autofocus": "off",
|
||||
"react/jsx-no-target-blank": "off",
|
||||
"import/no-amd": "error",
|
||||
"import/no-cycle": "error",
|
||||
"import/no-duplicates": "error",
|
||||
"node/no-new-require": "error",
|
||||
"node/no-path-concat": "error",
|
||||
"react/button-has-type": "error",
|
||||
"react/rules-of-hooks": "error",
|
||||
"typescript/no-misused-promises": ["error", { "checksVoidReturn": { "attributes": false } }],
|
||||
"typescript/only-throw-error": "error",
|
||||
"typescript/switch-exhaustiveness-check": "error",
|
||||
"unicorn/no-abusive-eslint-disable": "error",
|
||||
"unicorn/prefer-node-protocol": "error"
|
||||
},
|
||||
"overrides": [
|
||||
{
|
||||
|
||||
@@ -2,14 +2,13 @@
|
||||
|
||||
## Pre-Commit Checklist
|
||||
|
||||
**CRITICAL:** Before declaring victory on any task and before committing to git, the following four commands must pass with NO WARNINGS:
|
||||
**CRITICAL:** Before declaring victory on any task and before committing to git, the following three commands must pass with NO WARNINGS:
|
||||
|
||||
1. `pnpm lint` — Must pass with zero warnings
|
||||
2. `pnpm fmt:check` — Must pass with zero warnings
|
||||
3. `pnpm check-types` — Must pass with zero warnings
|
||||
4. `pnpm test` — Must pass with zero warnings
|
||||
3. `pnpm test` — Must pass with zero warnings
|
||||
|
||||
Do not proceed with commits until all four checks are clean.
|
||||
Do not proceed with commits until all three checks are clean.
|
||||
|
||||
<!-- intent-skills:start -->
|
||||
## Skill Loading
|
||||
@@ -28,11 +27,10 @@ Before editing files for a substantial task:
|
||||
|
||||
- `pnpm dev` — Start Next.js dev server at http://localhost:3000
|
||||
- `pnpm build` — Compile production bundle
|
||||
- `pnpm check-types` — Run `tsc --noEmit` for type diagnostics
|
||||
|
||||
### Linting & Formatting
|
||||
|
||||
- `pnpm lint` — Run oxlint lint
|
||||
- `pnpm lint` — Run oxlint (includes type-aware linting and type checking)
|
||||
- `pnpm fmt` — Apply oxfmt formatting
|
||||
|
||||
### Testing
|
||||
|
||||
+20
-1
@@ -1,4 +1,23 @@
|
||||
{
|
||||
"$schema": "../../node_modules/oxlint/configuration_schema.json",
|
||||
"extends": ["../../.oxlintrc.json"]
|
||||
"extends": ["../../.oxlintrc.json"],
|
||||
"env": {
|
||||
"browser": true,
|
||||
"node": true
|
||||
},
|
||||
"settings": {
|
||||
"next": {
|
||||
"rootDir": "."
|
||||
},
|
||||
"react": {
|
||||
"version": "19.2.8",
|
||||
"linkComponents": [{ "name": "Link", "attribute": "href" }]
|
||||
},
|
||||
"jsx-a11y": {
|
||||
"components": {
|
||||
"Link": "a",
|
||||
"Button": "button"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,10 +23,10 @@ export function LoginModalClient() {
|
||||
|
||||
function AuthorizedLoginContent({ onNavigate }: { onNavigate: () => void }) {
|
||||
const { data: session } = useSession();
|
||||
const { replace } = useRouter();
|
||||
const router = useRouter();
|
||||
|
||||
if (session?.user) {
|
||||
replace("/dashboard");
|
||||
router.replace("/dashboard");
|
||||
}
|
||||
|
||||
return <LoginContent onNavigate={onNavigate} />;
|
||||
|
||||
@@ -86,7 +86,13 @@ export async function GET(
|
||||
return new NextResponse("Avatar host not allowed", { status: 403 });
|
||||
case "size_exceeded":
|
||||
return new NextResponse("Avatar too large", { status: 413 });
|
||||
default:
|
||||
case "connection_error":
|
||||
case "dns_error":
|
||||
case "invalid_response":
|
||||
case "invalid_url":
|
||||
case "protocol_not_allowed":
|
||||
case "redirect_limit":
|
||||
case "timeout":
|
||||
return new NextResponse("Failed to fetch avatar", { status: 502 });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,8 +87,7 @@ export async function POST(
|
||||
// Only treat as cache hit if we have a definitive result:
|
||||
// - url is present (string), OR
|
||||
// - url is null but marked as permanently not found
|
||||
const isDefinitiveResult =
|
||||
cachedScreenshot.url !== null || cachedScreenshot.notFound === true;
|
||||
const isDefinitiveResult = cachedScreenshot.url !== null || cachedScreenshot.notFound;
|
||||
|
||||
if (isDefinitiveResult) {
|
||||
// Check current block status dynamically
|
||||
@@ -186,7 +185,7 @@ export async function GET(
|
||||
cached: false,
|
||||
success: result.success,
|
||||
data: result.data,
|
||||
...(result.success === false && { error: result.error }),
|
||||
...(!result.success && { error: result.error }),
|
||||
} as ScreenshotStatusResponse,
|
||||
{ headers: rateLimit.headers },
|
||||
);
|
||||
|
||||
@@ -11,10 +11,11 @@ const handler = async (req: Request) => {
|
||||
req,
|
||||
router: appRouter,
|
||||
createContext: () => ctx,
|
||||
onError: async ({ path, error }) => {
|
||||
// Use logger for unhandled errors
|
||||
const { logger } = await import("@domainstack/logger");
|
||||
logger.error({ err: error, source: "trpc", path });
|
||||
onError: ({ path, error }) => {
|
||||
void (async () => {
|
||||
const { logger } = await import("@domainstack/logger");
|
||||
logger.error({ err: error, source: "trpc", path });
|
||||
})();
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
@@ -51,7 +51,7 @@ export const PromptInput = ({ className, onSubmit, children, ...props }: PromptI
|
||||
form.reset();
|
||||
|
||||
try {
|
||||
onSubmit({ text }, event);
|
||||
void onSubmit({ text }, event);
|
||||
} catch (error) {
|
||||
console.warn("Message submission failed:", error);
|
||||
}
|
||||
|
||||
@@ -223,7 +223,7 @@ function CloudChatSession({
|
||||
(msgParams: { text: string }) => {
|
||||
const text = msgParams.text.trim();
|
||||
if (!text) return;
|
||||
chat.sendMessage({ text });
|
||||
void chat.sendMessage({ text });
|
||||
onActiveChange(true);
|
||||
},
|
||||
[chat, onActiveChange],
|
||||
|
||||
@@ -43,7 +43,8 @@ function getStatusLabel(status: BrowserAIStatus, downloadProgress?: number): str
|
||||
return `Downloading… ${Math.round((downloadProgress ?? 0) * 100)}%`;
|
||||
case "error":
|
||||
return "Error";
|
||||
default:
|
||||
case "ready":
|
||||
case "unavailable":
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -125,7 +125,7 @@ export function AddDomainContent({
|
||||
|
||||
<div className="flex flex-col gap-2">
|
||||
<Button
|
||||
onClick={() => void refetchSubscription()}
|
||||
onClick={() => refetchSubscription()}
|
||||
disabled={isSubscriptionLoading}
|
||||
className="w-full"
|
||||
>
|
||||
|
||||
@@ -49,7 +49,7 @@ type DashboardBannerProps = {
|
||||
};
|
||||
|
||||
export function DashboardBanner({
|
||||
variant = "info",
|
||||
variant,
|
||||
icon: Icon,
|
||||
title,
|
||||
description,
|
||||
|
||||
@@ -291,7 +291,7 @@ export function DashboardClient() {
|
||||
|
||||
const handleRetry = useCallback(() => {
|
||||
refetchSubscription();
|
||||
domainsQuery.refetch();
|
||||
void domainsQuery.refetch();
|
||||
}, [refetchSubscription, domainsQuery]);
|
||||
|
||||
if (isLoading) {
|
||||
|
||||
@@ -43,7 +43,7 @@ function domainNames() {
|
||||
}
|
||||
|
||||
function getFilterTrigger(name: RegExp) {
|
||||
return screen.getAllByRole("combobox", { name })[0]!;
|
||||
return screen.getAllByRole("combobox", { name })[0];
|
||||
}
|
||||
|
||||
async function waitForCatalog() {
|
||||
@@ -320,7 +320,7 @@ describe("dashboard shell", () => {
|
||||
|
||||
await user.click(screen.getByRole("button", { name: "Grid view" }));
|
||||
// "s" still matches all 12 sites, so clamp alone would leave page 2 empty of site00.
|
||||
await user.type(screen.getAllByRole("textbox", { name: "Search domains" })[0]!, "s");
|
||||
await user.type(screen.getAllByRole("textbox", { name: "Search domains" })[0], "s");
|
||||
await waitFor(() => {
|
||||
expect(domainNames()).toEqual(expect.arrayContaining(["site00.com", "site11.com"]));
|
||||
});
|
||||
@@ -383,7 +383,7 @@ describe("dashboard shell", () => {
|
||||
|
||||
expect(screen.getByRole("button", { name: /^Registrar$/ })).toBeInTheDocument();
|
||||
|
||||
await user.click(screen.getAllByRole("button", { name: "Toggle columns" })[0]!);
|
||||
await user.click(screen.getAllByRole("button", { name: "Toggle columns" })[0]);
|
||||
await user.click(await screen.findByRole("menuitemcheckbox", { name: /Registrar/ }));
|
||||
|
||||
await waitFor(() => {
|
||||
|
||||
@@ -20,7 +20,8 @@ export function DashboardTableColumnMenu() {
|
||||
const columnVisibility = useDashboardColumnVisibility();
|
||||
const setColumnVisibility = usePreferencesStore((s) => s.setColumnVisibility);
|
||||
|
||||
const isColumnVisible = (columnId: string) => columnVisibility[columnId] !== false;
|
||||
// Missing keys default to visible — `{}` means show every column.
|
||||
const isColumnVisible = (columnId: string) => columnVisibility[columnId] ?? true;
|
||||
|
||||
const hiddenCount = HIDEABLE_COLUMNS.filter((column) => !isColumnVisible(column.id)).length;
|
||||
|
||||
|
||||
@@ -51,7 +51,7 @@ export function DashboardTable({ domains }: DashboardTableProps) {
|
||||
(updater: SortingState | ((old: SortingState) => SortingState)) => {
|
||||
startSortTransition(() => {
|
||||
const newSorting = typeof updater === "function" ? updater(sorting) : updater;
|
||||
setSortParam(serializeSortState(newSorting));
|
||||
void setSortParam(serializeSortState(newSorting));
|
||||
resetPage();
|
||||
});
|
||||
},
|
||||
|
||||
@@ -87,7 +87,7 @@ function getStatusConfig(status: HealthStatus): {
|
||||
colorClass: "border-danger-border bg-danger/20 text-danger-foreground",
|
||||
icon: IconAlertOctagon,
|
||||
};
|
||||
default:
|
||||
case "unknown":
|
||||
return {
|
||||
label: "Unknown",
|
||||
colorClass: "border-muted-border bg-muted/20 text-muted-foreground",
|
||||
@@ -118,7 +118,7 @@ export function getHealthAccent(
|
||||
return "orange";
|
||||
case "critical":
|
||||
return "red";
|
||||
default:
|
||||
case "unknown":
|
||||
return "slate";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ const EXPLICIT_COLUMNS = ["select", "domainName", "verified", "actions"];
|
||||
|
||||
type UnverifiedTableRowProps = {
|
||||
rowId: string;
|
||||
cells: Cell<DashboardTableFeatures, TrackedDomainWithDetails, unknown>[];
|
||||
cells: Cell<DashboardTableFeatures, TrackedDomainWithDetails>[];
|
||||
original: TrackedDomainWithDetails;
|
||||
};
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ import { cn } from "@domainstack/ui/utils";
|
||||
|
||||
type VerifiedTableRowProps = {
|
||||
rowId: string;
|
||||
cells: Cell<DashboardTableFeatures, TrackedDomainWithDetails, unknown>[];
|
||||
cells: Cell<DashboardTableFeatures, TrackedDomainWithDetails>[];
|
||||
original: TrackedDomainWithDetails;
|
||||
};
|
||||
|
||||
|
||||
@@ -49,9 +49,7 @@ export function ExportButton({ domain, enabled = true }: { domain: string; enabl
|
||||
try {
|
||||
const exportData: Record<string, unknown> = {};
|
||||
for (const key of Object.keys(queryKeys)) {
|
||||
const response = queryClient.getQueryData(queryKeys[key as keyof typeof queryKeys]) as
|
||||
| { success?: boolean; data?: unknown }
|
||||
| undefined;
|
||||
const response = queryClient.getQueryData(queryKeys[key as keyof typeof queryKeys]);
|
||||
|
||||
if (response?.data) {
|
||||
exportData[key] = response.data;
|
||||
|
||||
@@ -171,6 +171,8 @@ export function RawDataDialog({ domain, format, data, serverName, serverUrl }: R
|
||||
</DialogTitle>
|
||||
</DialogHeader>
|
||||
<div
|
||||
// Scrollable region needs a tab stop so keyboard users can pan the raw dump.
|
||||
// oxlint-disable-next-line jsx-a11y/no-noninteractive-tabindex
|
||||
tabIndex={0}
|
||||
className="min-h-0 flex-1 overflow-auto overscroll-contain bg-popover/10 outline-none focus-visible:ring-[3px] focus-visible:ring-ring/50 focus-visible:outline-1"
|
||||
aria-label={`Raw ${format} data`}
|
||||
|
||||
@@ -148,7 +148,7 @@ export function DomainReportClient({ domain }: { domain: string }) {
|
||||
error: registrationError,
|
||||
} = useQuery(trpc.domain.getRegistration.queryOptions({ domain }, staticQueryOptions));
|
||||
const domainId = registration?.data?.domainId;
|
||||
const lookupFailed = Boolean(registration && registration.success === false);
|
||||
const lookupFailed = Boolean(registration && !registration.success);
|
||||
const isRegistered = registration?.success === true && registration.data?.isRegistered === true;
|
||||
const isUnregistered =
|
||||
registration?.success === true && registration.data?.isRegistered === false;
|
||||
|
||||
@@ -39,7 +39,10 @@ function parseStartResponse(raw: unknown): ScreenshotStartResponse {
|
||||
const obj = raw as Record<string, unknown>;
|
||||
|
||||
if ("error" in obj && !("status" in obj)) {
|
||||
return { status: "error", error: String(obj.error) };
|
||||
return {
|
||||
status: "error",
|
||||
error: typeof obj.error === "string" ? obj.error : "Invalid response",
|
||||
};
|
||||
}
|
||||
|
||||
if (obj.status === "running" && typeof obj.runId === "string") {
|
||||
@@ -68,7 +71,10 @@ function parseStatusResponse(raw: unknown): ScreenshotStatusResponse {
|
||||
const obj = raw as Record<string, unknown>;
|
||||
|
||||
if ("error" in obj && !("status" in obj)) {
|
||||
return { status: "error", error: String(obj.error) };
|
||||
return {
|
||||
status: "error",
|
||||
error: typeof obj.error === "string" ? obj.error : "Invalid response",
|
||||
};
|
||||
}
|
||||
|
||||
if (obj.status === "running") {
|
||||
@@ -76,7 +82,10 @@ function parseStatusResponse(raw: unknown): ScreenshotStatusResponse {
|
||||
}
|
||||
|
||||
if (obj.status === "failed") {
|
||||
return { status: "failed", error: String(obj.error ?? "Workflow failed") };
|
||||
return {
|
||||
status: "failed",
|
||||
error: typeof obj.error === "string" ? obj.error : "Workflow failed",
|
||||
};
|
||||
}
|
||||
|
||||
if (obj.status === "completed" && obj.data) {
|
||||
|
||||
@@ -195,7 +195,7 @@ export function RobotsSummary({
|
||||
<span>robots.txt</span>
|
||||
<IconExternalLink className="relative bottom-px inline-flex size-3" aria-hidden />
|
||||
</a>
|
||||
<PillCount count={(counts.allows + counts.disallows) as number} color="blue" />
|
||||
<PillCount count={counts.allows + counts.disallows} color="blue" />
|
||||
</div>
|
||||
|
||||
<div className="space-y-4">
|
||||
@@ -244,7 +244,7 @@ export function RobotsSummary({
|
||||
<ToggleGroupItem value="all" className="h-full">
|
||||
<IconCircleHalf2 className="size-3.5 text-accent-blue" aria-hidden />
|
||||
<span className="text-[13px]">All</span>
|
||||
<PillCount count={(counts.allows + counts.disallows) as number} color="slate" />
|
||||
<PillCount count={counts.allows + counts.disallows} color="slate" />
|
||||
</ToggleGroupItem>
|
||||
<ToggleGroupItem value="allow" className="h-full">
|
||||
<IconCircleCheck className="size-3.5 text-accent-green" aria-hidden />
|
||||
|
||||
@@ -165,6 +165,8 @@ export function AppFooter() {
|
||||
size="lg"
|
||||
nativeButton={false}
|
||||
render={
|
||||
// Bookmarklet drag target; href is filled in on mount.
|
||||
// oxlint-disable-next-line jsx-a11y/anchor-is-valid
|
||||
<a ref={hrefScript} href="#">
|
||||
<IconWorld />
|
||||
Inspect Domain
|
||||
|
||||
@@ -37,7 +37,7 @@ export function UserMenu() {
|
||||
const { user } = session;
|
||||
const avatarUrl = `/api/avatar/${user.id}`;
|
||||
const { props: imageProps } = getImageProps({
|
||||
src: avatarUrl as string,
|
||||
src: avatarUrl,
|
||||
alt: user.name ?? "User avatar",
|
||||
width: 32,
|
||||
height: 32,
|
||||
|
||||
@@ -7,7 +7,7 @@ import { HeaderSearchClient } from "./header-search-client";
|
||||
|
||||
const nav = vi.hoisted(() => ({
|
||||
push: vi.fn<(href: string) => void | Promise<void>>(),
|
||||
params: { domain: "Test.INVALID" as string | undefined },
|
||||
params: { domain: "Test.INVALID" },
|
||||
}));
|
||||
|
||||
vi.mock("@/hooks/use-router", () => ({
|
||||
@@ -53,7 +53,7 @@ describe("HeaderSearch", () => {
|
||||
});
|
||||
|
||||
it("does nothing on invalid domain", async () => {
|
||||
nav.params = { domain: "invalid domain" } as { domain: string };
|
||||
nav.params = { domain: "invalid domain" };
|
||||
render(<HeaderSearchClient />);
|
||||
const input = screen.getByLabelText(/Search any domain/i);
|
||||
await userEvent.type(input, "{Enter}");
|
||||
|
||||
@@ -85,7 +85,7 @@ describe("DomainSearch (form variant)", () => {
|
||||
await userEvent.type(input, "test.invalid{Enter}");
|
||||
expect(nav.push).toHaveBeenCalledWith("/test.invalid");
|
||||
// Input and button should be disabled while loading/submitting
|
||||
expect((screen.getByLabelText(/Search any domain/i) as HTMLInputElement).disabled).toBe(true);
|
||||
expect(screen.getByLabelText(/Search any domain/i)).toBeDisabled();
|
||||
// Submit button shows a loading spinner with accessible name "Loading"
|
||||
expect(screen.getByRole("button", { name: /loading/i })).toBeDisabled();
|
||||
|
||||
@@ -146,8 +146,8 @@ describe("DomainSearch (form variant)", () => {
|
||||
rerender(<SearchClient variant="lg" />);
|
||||
|
||||
// Wait for input to reflect the triggered domain (async due to useEffect)
|
||||
const input = (await screen.findByLabelText(/Search any domain/i)) as HTMLInputElement;
|
||||
expect(input.value).toBe("test.invalid");
|
||||
const input = await screen.findByLabelText(/Search any domain/i);
|
||||
expect(input).toHaveValue("test.invalid");
|
||||
|
||||
// Wait for navigation and store clear to be triggered
|
||||
await waitFor(() => {
|
||||
@@ -181,8 +181,8 @@ describe("DomainSearch (header variant)", () => {
|
||||
|
||||
render(<SearchClient variant="sm" />);
|
||||
|
||||
const input = screen.getByLabelText(/Search any domain/i) as HTMLInputElement;
|
||||
expect(input.placeholder).toBe("Search any domain\u2026");
|
||||
const input = screen.getByLabelText(/Search any domain/i);
|
||||
expect(input).toHaveAttribute("placeholder", "Search any domain\u2026");
|
||||
});
|
||||
|
||||
it("shows short placeholder on mobile screens", async () => {
|
||||
@@ -190,8 +190,8 @@ describe("DomainSearch (header variant)", () => {
|
||||
|
||||
render(<SearchClient variant="sm" />);
|
||||
|
||||
const input = screen.getByLabelText(/Search any domain/i) as HTMLInputElement;
|
||||
expect(input.placeholder).toBe("Search\u2026");
|
||||
const input = screen.getByLabelText(/Search any domain/i);
|
||||
expect(input).toHaveAttribute("placeholder", "Search\u2026");
|
||||
});
|
||||
|
||||
it("updates placeholder when window is resized", async () => {
|
||||
@@ -200,15 +200,15 @@ describe("DomainSearch (header variant)", () => {
|
||||
const { rerender } = render(<SearchClient variant="sm" />);
|
||||
|
||||
// Verify desktop placeholder
|
||||
let input = screen.getByLabelText(/Search any domain/i) as HTMLInputElement;
|
||||
expect(input.placeholder).toBe("Search any domain\u2026");
|
||||
let input = screen.getByLabelText(/Search any domain/i);
|
||||
expect(input).toHaveAttribute("placeholder", "Search any domain\u2026");
|
||||
|
||||
// Simulate resize to mobile
|
||||
useIsMobile.mockReturnValue(true);
|
||||
rerender(<SearchClient variant="sm" />);
|
||||
|
||||
// Verify mobile placeholder
|
||||
input = screen.getByLabelText(/Search any domain/i) as HTMLInputElement;
|
||||
expect(input.placeholder).toBe("Search\u2026");
|
||||
input = screen.getByLabelText(/Search any domain/i);
|
||||
expect(input).toHaveAttribute("placeholder", "Search\u2026");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -78,7 +78,7 @@ export function NotificationMatrix({
|
||||
<div className="flex w-14 items-center justify-center py-1">
|
||||
<Checkbox
|
||||
checked={pref.inApp}
|
||||
onCheckedChange={(checked) => onToggle(category, "inApp", checked === true)}
|
||||
onCheckedChange={(checked) => onToggle(category, "inApp", checked)}
|
||||
disabled={disabled}
|
||||
aria-label={`Web notifications for ${info.label}`}
|
||||
/>
|
||||
@@ -86,7 +86,7 @@ export function NotificationMatrix({
|
||||
<div className="flex w-14 items-center justify-center py-1">
|
||||
<Checkbox
|
||||
checked={pref.email}
|
||||
onCheckedChange={(checked) => onToggle(category, "email", checked === true)}
|
||||
onCheckedChange={(checked) => onToggle(category, "email", checked)}
|
||||
disabled={disabled}
|
||||
aria-label={`Email notifications for ${info.label}`}
|
||||
/>
|
||||
|
||||
@@ -729,9 +729,9 @@ function MapControls({
|
||||
const container = map?.getContainer();
|
||||
if (!container) return;
|
||||
if (document.fullscreenElement) {
|
||||
document.exitFullscreen();
|
||||
void document.exitFullscreen();
|
||||
} else {
|
||||
container.requestFullscreen();
|
||||
void container.requestFullscreen();
|
||||
}
|
||||
}, [map]);
|
||||
|
||||
@@ -1007,7 +1007,7 @@ function MapRoute({
|
||||
|
||||
const source = map.getSource(sourceId) as GeoJSONSource;
|
||||
if (source) {
|
||||
source.setData({
|
||||
void source.setData({
|
||||
type: "Feature",
|
||||
properties: {},
|
||||
geometry: { type: "LineString", coordinates },
|
||||
|
||||
@@ -16,7 +16,7 @@ const Toaster = ({ ...props }: ToasterProps) => {
|
||||
|
||||
return (
|
||||
<Sonner
|
||||
theme={theme as ToasterProps["theme"]}
|
||||
theme={theme}
|
||||
className="toaster group"
|
||||
icons={{
|
||||
success: <IconCircleCheck className="size-4" />,
|
||||
|
||||
@@ -151,7 +151,7 @@ export function useDashboardFilters(
|
||||
const setSearch = useCallback(
|
||||
(value: string) => {
|
||||
startTransition(() => {
|
||||
setFilters({ search: value || null, domainId: null });
|
||||
void setFilters({ search: value || null, domainId: null });
|
||||
});
|
||||
},
|
||||
[setFilters],
|
||||
@@ -160,7 +160,7 @@ export function useDashboardFilters(
|
||||
const setStatus = useCallback(
|
||||
(values: StatusFilter[]) => {
|
||||
startTransition(() => {
|
||||
setFilters({
|
||||
void setFilters({
|
||||
status: values.length > 0 ? values : null,
|
||||
domainId: null,
|
||||
});
|
||||
@@ -172,7 +172,7 @@ export function useDashboardFilters(
|
||||
const setHealth = useCallback(
|
||||
(values: HealthFilter[]) => {
|
||||
startTransition(() => {
|
||||
setFilters({
|
||||
void setFilters({
|
||||
health: values.length > 0 ? values : null,
|
||||
domainId: null,
|
||||
});
|
||||
@@ -184,7 +184,7 @@ export function useDashboardFilters(
|
||||
const setTlds = useCallback(
|
||||
(values: string[]) => {
|
||||
startTransition(() => {
|
||||
setFilters({ tlds: values.length > 0 ? values : null, domainId: null });
|
||||
void setFilters({ tlds: values.length > 0 ? values : null, domainId: null });
|
||||
});
|
||||
},
|
||||
[setFilters],
|
||||
@@ -193,7 +193,7 @@ export function useDashboardFilters(
|
||||
const setProviders = useCallback(
|
||||
(values: string[]) => {
|
||||
startTransition(() => {
|
||||
setFilters({
|
||||
void setFilters({
|
||||
providers: values.length > 0 ? values : null,
|
||||
domainId: null,
|
||||
});
|
||||
@@ -204,7 +204,7 @@ export function useDashboardFilters(
|
||||
|
||||
const clearFilters = useCallback(() => {
|
||||
startTransition(() => {
|
||||
setFilters({
|
||||
void setFilters({
|
||||
search: null,
|
||||
status: null,
|
||||
health: null,
|
||||
@@ -219,9 +219,9 @@ export function useDashboardFilters(
|
||||
(filter: HealthFilter | "pending") => {
|
||||
startTransition(() => {
|
||||
if (filter === "pending") {
|
||||
setFilters({ status: ["pending"], health: null, domainId: null });
|
||||
void setFilters({ status: ["pending"], health: null, domainId: null });
|
||||
} else {
|
||||
setFilters({ status: null, health: [filter], domainId: null });
|
||||
void setFilters({ status: null, health: [filter], domainId: null });
|
||||
}
|
||||
});
|
||||
},
|
||||
@@ -230,7 +230,7 @@ export function useDashboardFilters(
|
||||
|
||||
const clearDomainId = useCallback(() => {
|
||||
startTransition(() => {
|
||||
setFilters({ domainId: null });
|
||||
void setFilters({ domainId: null });
|
||||
});
|
||||
}, [setFilters]);
|
||||
|
||||
|
||||
@@ -135,9 +135,7 @@ export function useDashboardMutations(): UseDashboardMutationsReturn {
|
||||
const previousSubscription =
|
||||
queryClient.getQueryData<SubscriptionData>(subscriptionQueryKey);
|
||||
|
||||
const { active, archived } = affectedCounts(previousDomains as [unknown, unknown][], [
|
||||
trackedDomainId,
|
||||
]);
|
||||
const { active, archived } = affectedCounts(previousDomains, [trackedDomainId]);
|
||||
|
||||
queryClient.setQueriesData(domainsFilter, (old: DomainsData) =>
|
||||
old?.filter((d) => d.id !== trackedDomainId),
|
||||
@@ -155,7 +153,7 @@ export function useDashboardMutations(): UseDashboardMutationsReturn {
|
||||
});
|
||||
|
||||
return {
|
||||
previousDomains: previousDomains as [unknown, unknown][],
|
||||
previousDomains,
|
||||
previousSubscription,
|
||||
};
|
||||
},
|
||||
@@ -187,9 +185,7 @@ export function useDashboardMutations(): UseDashboardMutationsReturn {
|
||||
const previousSubscription =
|
||||
queryClient.getQueryData<SubscriptionData>(subscriptionQueryKey);
|
||||
|
||||
const { active: toArchive } = affectedCounts(previousDomains as [unknown, unknown][], [
|
||||
trackedDomainId,
|
||||
]);
|
||||
const { active: toArchive } = affectedCounts(previousDomains, [trackedDomainId]);
|
||||
|
||||
queryClient.setQueriesData(domainsFilter, (old: DomainsData) =>
|
||||
old?.map((d) => (d.id === trackedDomainId ? { ...d, archivedAt: new Date() } : d)),
|
||||
@@ -206,7 +202,7 @@ export function useDashboardMutations(): UseDashboardMutationsReturn {
|
||||
});
|
||||
|
||||
return {
|
||||
previousDomains: previousDomains as [unknown, unknown][],
|
||||
previousDomains,
|
||||
previousSubscription,
|
||||
};
|
||||
},
|
||||
@@ -238,9 +234,7 @@ export function useDashboardMutations(): UseDashboardMutationsReturn {
|
||||
const previousSubscription =
|
||||
queryClient.getQueryData<SubscriptionData>(subscriptionQueryKey);
|
||||
|
||||
const { archived: toActivate } = affectedCounts(previousDomains as [unknown, unknown][], [
|
||||
trackedDomainId,
|
||||
]);
|
||||
const { archived: toActivate } = affectedCounts(previousDomains, [trackedDomainId]);
|
||||
|
||||
queryClient.setQueriesData(domainsFilter, (old: DomainsData) =>
|
||||
old?.map((d) => (d.id === trackedDomainId ? { ...d, archivedAt: null } : d)),
|
||||
@@ -257,7 +251,7 @@ export function useDashboardMutations(): UseDashboardMutationsReturn {
|
||||
});
|
||||
|
||||
return {
|
||||
previousDomains: previousDomains as [unknown, unknown][],
|
||||
previousDomains,
|
||||
previousSubscription,
|
||||
};
|
||||
},
|
||||
@@ -290,7 +284,7 @@ export function useDashboardMutations(): UseDashboardMutationsReturn {
|
||||
old?.map((d) => (d.id === trackedDomainId ? { ...d, muted } : d)),
|
||||
);
|
||||
|
||||
return { previousDomains: previousDomains as [unknown, unknown][] };
|
||||
return { previousDomains };
|
||||
},
|
||||
onError: (_err, _vars, context: { previousDomains: [unknown, unknown][] } | undefined) => {
|
||||
if (context?.previousDomains) {
|
||||
@@ -318,10 +312,7 @@ export function useDashboardMutations(): UseDashboardMutationsReturn {
|
||||
queryClient.getQueryData<SubscriptionData>(subscriptionQueryKey);
|
||||
|
||||
const idsSet = new Set(trackedDomainIds);
|
||||
const { active: archiveCount } = affectedCounts(
|
||||
previousDomains as [unknown, unknown][],
|
||||
idsSet,
|
||||
);
|
||||
const { active: archiveCount } = affectedCounts(previousDomains, idsSet);
|
||||
|
||||
queryClient.setQueriesData(domainsFilter, (old: DomainsData) =>
|
||||
old?.map((d) =>
|
||||
@@ -340,7 +331,7 @@ export function useDashboardMutations(): UseDashboardMutationsReturn {
|
||||
});
|
||||
|
||||
return {
|
||||
previousDomains: previousDomains as [unknown, unknown][],
|
||||
previousDomains,
|
||||
previousSubscription,
|
||||
};
|
||||
},
|
||||
@@ -373,7 +364,7 @@ export function useDashboardMutations(): UseDashboardMutationsReturn {
|
||||
|
||||
const idsSet = new Set(trackedDomainIds);
|
||||
const { active: activeDeleted, archived: archivedDeleted } = affectedCounts(
|
||||
previousDomains as [unknown, unknown][],
|
||||
previousDomains,
|
||||
idsSet,
|
||||
);
|
||||
|
||||
@@ -393,7 +384,7 @@ export function useDashboardMutations(): UseDashboardMutationsReturn {
|
||||
});
|
||||
|
||||
return {
|
||||
previousDomains: previousDomains as [unknown, unknown][],
|
||||
previousDomains,
|
||||
previousSubscription,
|
||||
};
|
||||
},
|
||||
@@ -432,7 +423,7 @@ export function useDashboardMutations(): UseDashboardMutationsReturn {
|
||||
old?.map((d) => (idsSet.has(d.id) ? { ...d, muted } : d)),
|
||||
);
|
||||
|
||||
return { previousDomains: previousDomains as [unknown, unknown][] };
|
||||
return { previousDomains };
|
||||
},
|
||||
onError: (
|
||||
_err,
|
||||
|
||||
@@ -59,7 +59,7 @@ export function useDashboardPagination(): UseDashboardPaginationReturn {
|
||||
|
||||
const setPageIndex = useCallback(
|
||||
(newIndex: number) => {
|
||||
setPageParam(newIndex + 1);
|
||||
void setPageParam(newIndex + 1);
|
||||
},
|
||||
[setPageParam],
|
||||
);
|
||||
@@ -68,13 +68,13 @@ export function useDashboardPagination(): UseDashboardPaginationReturn {
|
||||
(newSize: DashboardPageSizeOptions) => {
|
||||
setPageSizePreference(newSize);
|
||||
// Reset to first page when changing page size
|
||||
setPageParam(1);
|
||||
void setPageParam(1);
|
||||
},
|
||||
[setPageSizePreference, setPageParam],
|
||||
);
|
||||
|
||||
const resetPage = useCallback(() => {
|
||||
setPageParam(1);
|
||||
void setPageParam(1);
|
||||
}, [setPageParam]);
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
@@ -162,11 +162,17 @@ export function useSubscription(options: UseSubscriptionOptions = {}): UseSubscr
|
||||
isPro: query.data?.plan === "pro",
|
||||
isSubscriptionLoading: query.isLoading,
|
||||
isSubscriptionError: query.isError,
|
||||
refetchSubscription: query.refetch,
|
||||
refetchSubscription: () => {
|
||||
void query.refetch();
|
||||
},
|
||||
invalidateSubscription: invalidate,
|
||||
handleCheckout,
|
||||
handleCheckout: () => {
|
||||
void handleCheckout();
|
||||
},
|
||||
isCheckoutLoading,
|
||||
handleCustomerPortal,
|
||||
handleCustomerPortal: () => {
|
||||
void handleCustomerPortal();
|
||||
},
|
||||
isCustomerPortalLoading,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -57,7 +57,7 @@ export function getAssistantRenderItems(message: UIMessage): AssistantRenderItem
|
||||
const texts: string[] = [];
|
||||
let last: ReasoningUIPart = part;
|
||||
|
||||
while (i < parts.length && isReasoningUIPart(parts[i]!)) {
|
||||
while (i < parts.length && isReasoningUIPart(parts[i])) {
|
||||
last = parts[i] as ReasoningUIPart;
|
||||
texts.push(last.text);
|
||||
i += 1;
|
||||
|
||||
@@ -27,7 +27,7 @@ export interface SectionDef {
|
||||
export const sections: Record<Section, SectionDef> = {
|
||||
registration: {
|
||||
title: "Registration",
|
||||
accent: "purple" as SectionAccent,
|
||||
accent: "purple",
|
||||
icon: IconIdBadge2,
|
||||
description: "Registrar and registrant details",
|
||||
help: "RDAP/WHOIS shows registrar, registration dates, and registrant details.",
|
||||
@@ -35,7 +35,7 @@ export const sections: Record<Section, SectionDef> = {
|
||||
},
|
||||
hosting: {
|
||||
title: "Hosting & Email",
|
||||
accent: "blue" as SectionAccent,
|
||||
accent: "blue",
|
||||
icon: IconCloudComputing,
|
||||
description: "Providers and IP geolocation",
|
||||
help: "Hosting provider serves a site; email provider handles a domain's email.",
|
||||
@@ -43,7 +43,7 @@ export const sections: Record<Section, SectionDef> = {
|
||||
},
|
||||
dns: {
|
||||
title: "DNS Records",
|
||||
accent: "green" as SectionAccent,
|
||||
accent: "green",
|
||||
icon: IconRoute,
|
||||
description: "A, AAAA, MX, CNAME, TXT, NS",
|
||||
help: "DNS records map the domain to services like web (A/AAAA), mail (MX), and aliases (CNAME).",
|
||||
@@ -51,7 +51,7 @@ export const sections: Record<Section, SectionDef> = {
|
||||
},
|
||||
certificates: {
|
||||
title: "SSL Certificates",
|
||||
accent: "orange" as SectionAccent,
|
||||
accent: "orange",
|
||||
icon: IconCertificate,
|
||||
description: "Issuer and validity",
|
||||
help: "SSL/TLS certificates encrypt traffic and verify a domain's identity.",
|
||||
@@ -59,7 +59,7 @@ export const sections: Record<Section, SectionDef> = {
|
||||
},
|
||||
headers: {
|
||||
title: "HTTP Headers",
|
||||
accent: "pink" as SectionAccent,
|
||||
accent: "pink",
|
||||
icon: IconList,
|
||||
description: "Server, security, caching",
|
||||
help: "Headers include server info and security/caching directives returned by a site.",
|
||||
@@ -67,7 +67,7 @@ export const sections: Record<Section, SectionDef> = {
|
||||
},
|
||||
seo: {
|
||||
title: "SEO & Social",
|
||||
accent: "cyan" as SectionAccent,
|
||||
accent: "cyan",
|
||||
icon: IconShare,
|
||||
description: "Meta tags, previews, robots.txt",
|
||||
help: "Open Graph, Twitter, and standard meta inform social previews and search engines.",
|
||||
|
||||
@@ -75,7 +75,7 @@ export function getSeverityIconColor(
|
||||
return "destructive";
|
||||
case "warning":
|
||||
return "warning";
|
||||
default:
|
||||
case "info":
|
||||
return "default";
|
||||
}
|
||||
}
|
||||
@@ -136,7 +136,7 @@ export function getUnreadIndicatorColor(severity: NotificationSeverity) {
|
||||
return "bg-destructive";
|
||||
case "warning":
|
||||
return "bg-amber-500";
|
||||
default:
|
||||
case "info":
|
||||
return "bg-blue-500";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,7 +102,7 @@ export function classifyFetchError(
|
||||
const retryAfterMatch = message.match(/retry[- ]after[:\s]+(\d+)/i);
|
||||
const retrySeconds = retryAfterMatch ? Number.parseInt(retryAfterMatch[1], 10) : 60; // Default to 1 minute for rate limits
|
||||
return new RetryableError(`${context}: rate limited`, {
|
||||
retryAfter: `${retrySeconds}s` as `${number}s`,
|
||||
retryAfter: `${retrySeconds}s`,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -8,9 +8,8 @@
|
||||
"build": "next build",
|
||||
"start": "next start",
|
||||
"analyze": "next experimental-analyze",
|
||||
"check-types": "next typegen && tsc --noEmit",
|
||||
"lint": "oxlint",
|
||||
"lint:fix": "oxlint --fix",
|
||||
"lint": "next typegen && oxlint",
|
||||
"lint:fix": "next typegen && oxlint --fix",
|
||||
"fmt": "oxfmt --config ../../.oxfmtrc.json",
|
||||
"fmt:check": "oxfmt --check --config ../../.oxfmtrc.json",
|
||||
"test": "vitest run",
|
||||
|
||||
@@ -61,7 +61,7 @@ export const domainRouter = createTRPCRouter({
|
||||
// Fetch fresh data
|
||||
try {
|
||||
const result = await fetchRegistration(input.domain);
|
||||
if (result.success === false) {
|
||||
if (!result.success) {
|
||||
return {
|
||||
success: false,
|
||||
cached: false,
|
||||
@@ -168,7 +168,7 @@ export const domainRouter = createTRPCRouter({
|
||||
// Fetch fresh data
|
||||
try {
|
||||
const result = await fetchCertificates(input.domain);
|
||||
if (result.success === false) {
|
||||
if (!result.success) {
|
||||
return {
|
||||
success: false,
|
||||
cached: false,
|
||||
@@ -216,7 +216,7 @@ export const domainRouter = createTRPCRouter({
|
||||
// Fetch fresh data
|
||||
try {
|
||||
const result = await fetchHeaders(input.domain);
|
||||
if (result.success === false) {
|
||||
if (!result.success) {
|
||||
return {
|
||||
success: false,
|
||||
cached: false,
|
||||
|
||||
@@ -25,7 +25,7 @@ const SORTED_THRESHOLDS = [...SUBSCRIPTION_EXPIRY_THRESHOLDS].sort((a, b) => a -
|
||||
function getSubscriptionExpiryThreshold(daysRemaining: number): SubscriptionExpiryThreshold | null {
|
||||
for (const threshold of SORTED_THRESHOLDS) {
|
||||
if (daysRemaining <= threshold) {
|
||||
return threshold as SubscriptionExpiryThreshold;
|
||||
return threshold;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
|
||||
+1
-1
@@ -15,7 +15,6 @@
|
||||
"dev": "turbo run dev",
|
||||
"build": "turbo run build",
|
||||
"start": "turbo run start",
|
||||
"check-types": "turbo run check-types",
|
||||
"lint": "turbo run lint",
|
||||
"lint:fix": "turbo run lint:fix",
|
||||
"fmt": "turbo run fmt",
|
||||
@@ -31,6 +30,7 @@
|
||||
"devDependencies": {
|
||||
"oxfmt": "^0.66.0",
|
||||
"oxlint": "^1.81.0",
|
||||
"oxlint-tsgolint": "^7.0.2001",
|
||||
"shx": "^0.4.0",
|
||||
"turbo": "^2.10.12",
|
||||
"typescript": "^7.0.2"
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
"./middleware": "./src/middleware/index.ts"
|
||||
},
|
||||
"scripts": {
|
||||
"check-types": "tsc --noEmit",
|
||||
"lint": "oxlint",
|
||||
"lint:fix": "oxlint --fix",
|
||||
"fmt": "oxfmt --config ../../.oxfmtrc.json",
|
||||
|
||||
@@ -41,9 +41,10 @@ describe("scheduleBackground", () => {
|
||||
const work = deferred<boolean>();
|
||||
|
||||
let settled = false;
|
||||
const pending = scheduleBackground(work.promise).then(() => {
|
||||
const pending = (async () => {
|
||||
await scheduleBackground(work.promise);
|
||||
settled = true;
|
||||
});
|
||||
})();
|
||||
|
||||
expect(waitUntil).not.toHaveBeenCalled();
|
||||
expect(settled).toBe(false);
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
"./types": "./src/types.ts"
|
||||
},
|
||||
"scripts": {
|
||||
"check-types": "tsc --noEmit",
|
||||
"lint": "oxlint",
|
||||
"lint:fix": "oxlint --fix",
|
||||
"fmt": "oxfmt --config ../../.oxfmtrc.json",
|
||||
|
||||
@@ -2,7 +2,7 @@ import { drizzleAdapter } from "@better-auth/drizzle-adapter";
|
||||
import { dash } from "@better-auth/infra";
|
||||
import { waitUntil } from "@vercel/functions";
|
||||
import { getSessionCookie } from "better-auth/cookies";
|
||||
import { type BetterAuthOptions, betterAuth } from "better-auth/minimal";
|
||||
import { betterAuth } from "better-auth/minimal";
|
||||
import { nextCookies, toNextJsHandler } from "better-auth/next-js";
|
||||
|
||||
import { db } from "@domainstack/db/client";
|
||||
@@ -116,7 +116,7 @@ export const auth = betterAuth({
|
||||
logger: {
|
||||
log: (level, message, ...args) => {
|
||||
const logFn = logger[level].bind(logger);
|
||||
logFn({ ...args }, message);
|
||||
logFn({ extra: args }, message);
|
||||
},
|
||||
},
|
||||
databaseHooks: {
|
||||
@@ -249,7 +249,7 @@ export const auth = betterAuth({
|
||||
// must be last: https://www.better-auth.com/docs/integrations/next#server-action-cookies
|
||||
nextCookies(),
|
||||
],
|
||||
} as BetterAuthOptions);
|
||||
});
|
||||
|
||||
export type Session = typeof auth.$Infer.Session;
|
||||
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
".": "./src/index.ts"
|
||||
},
|
||||
"scripts": {
|
||||
"check-types": "tsc --noEmit",
|
||||
"lint": "oxlint",
|
||||
"lint:fix": "oxlint --fix",
|
||||
"fmt": "oxfmt --config ../../.oxfmtrc.json",
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
".": "./src/index.ts"
|
||||
},
|
||||
"scripts": {
|
||||
"check-types": "tsc --noEmit",
|
||||
"lint": "oxlint",
|
||||
"lint:fix": "oxlint --fix",
|
||||
"fmt": "oxfmt --config ../../.oxfmtrc.json",
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
"./testing": "./src/testing.ts"
|
||||
},
|
||||
"scripts": {
|
||||
"check-types": "tsc --noEmit",
|
||||
"lint": "oxlint",
|
||||
"lint:fix": "oxlint --fix",
|
||||
"fmt": "oxfmt --config ../../.oxfmtrc.json",
|
||||
|
||||
@@ -42,7 +42,7 @@ export async function getFaviconById(domainId: string): Promise<CacheResult<Favi
|
||||
return { data: null, stale: false, fetchedAt: null, expiresAt: null };
|
||||
}
|
||||
|
||||
const isDefinitiveResult = row.url !== null || row.notFound === true;
|
||||
const isDefinitiveResult = row.url !== null || row.notFound;
|
||||
|
||||
if (!isDefinitiveResult) {
|
||||
return { data: null, stale: false, fetchedAt: null, expiresAt: null };
|
||||
@@ -80,7 +80,7 @@ export async function getFavicon(domainName: string): Promise<CacheResult<Favico
|
||||
return { data: null, stale: false, fetchedAt: null, expiresAt: null };
|
||||
}
|
||||
|
||||
const isDefinitiveResult = row.url !== null || row.notFound === true;
|
||||
const isDefinitiveResult = row.url !== null || row.notFound;
|
||||
|
||||
if (!isDefinitiveResult) {
|
||||
return { data: null, stale: false, fetchedAt: null, expiresAt: null };
|
||||
|
||||
@@ -81,7 +81,7 @@ export async function getCachedHeaders(domain: string): Promise<CacheResult<Head
|
||||
// Status messages are derived from the numeric code at the call site.
|
||||
return {
|
||||
data: {
|
||||
headers: row.headers as Header[],
|
||||
headers: row.headers,
|
||||
status: row.status,
|
||||
statusMessage: undefined,
|
||||
},
|
||||
|
||||
@@ -44,7 +44,7 @@ export async function getProviderLogo(
|
||||
return { data: null, stale: false, fetchedAt: null, expiresAt: null };
|
||||
}
|
||||
|
||||
const isDefinitiveResult = row.url !== null || row.notFound === true;
|
||||
const isDefinitiveResult = row.url !== null || row.notFound;
|
||||
|
||||
if (!isDefinitiveResult) {
|
||||
return { data: null, stale: false, fetchedAt: null, expiresAt: null };
|
||||
|
||||
@@ -1,13 +1,7 @@
|
||||
import type { InferInsertModel } from "drizzle-orm";
|
||||
import { eq } from "drizzle-orm";
|
||||
|
||||
import type {
|
||||
GeneralMeta,
|
||||
OpenGraphMeta,
|
||||
RobotsTxt,
|
||||
SeoResponse,
|
||||
TwitterMeta,
|
||||
} from "@domainstack/types";
|
||||
import type { RobotsTxt, SeoResponse } from "@domainstack/types";
|
||||
|
||||
import { db } from "../client";
|
||||
import { blockedDomains, domains, seo as seoTable } from "../schema";
|
||||
@@ -82,7 +76,7 @@ export async function getCachedSeo(domain: string): Promise<CacheResult<SeoRespo
|
||||
: null;
|
||||
|
||||
// Normalize robots
|
||||
const robotsData = row.robots as RobotsTxt;
|
||||
const robotsData = row.robots;
|
||||
const normalizedRobots: RobotsTxt =
|
||||
robotsData && "fetched" in robotsData
|
||||
? robotsData
|
||||
@@ -90,9 +84,9 @@ export async function getCachedSeo(domain: string): Promise<CacheResult<SeoRespo
|
||||
|
||||
const response: SeoResponse = {
|
||||
meta: {
|
||||
openGraph: row.metaOpenGraph as OpenGraphMeta,
|
||||
twitter: row.metaTwitter as TwitterMeta,
|
||||
general: row.metaGeneral as GeneralMeta,
|
||||
openGraph: row.metaOpenGraph,
|
||||
twitter: row.metaTwitter,
|
||||
general: row.metaGeneral,
|
||||
},
|
||||
robots: normalizedRobots,
|
||||
preview,
|
||||
@@ -100,7 +94,7 @@ export async function getCachedSeo(domain: string): Promise<CacheResult<SeoRespo
|
||||
finalUrl: row.sourceFinalUrl ?? null,
|
||||
status: row.sourceStatus ?? null,
|
||||
},
|
||||
errors: row.errors as { html?: string; robots?: string },
|
||||
errors: row.errors,
|
||||
};
|
||||
|
||||
return { data: response, stale, fetchedAt, expiresAt };
|
||||
|
||||
@@ -32,9 +32,9 @@ export async function makePGliteDb(): Promise<DbBundle> {
|
||||
const origLog = consoleObj.log;
|
||||
try {
|
||||
consoleObj.log = (...args: unknown[]) => {
|
||||
const s = String(args[0] ?? "");
|
||||
const s = typeof args[0] === "string" ? args[0] : "";
|
||||
if (s.includes("Pulling schema from database")) return;
|
||||
origLog.apply(consoleObj, args as unknown[]);
|
||||
origLog.apply(consoleObj, args);
|
||||
};
|
||||
await apply();
|
||||
} finally {
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
".": "./src/index.ts"
|
||||
},
|
||||
"scripts": {
|
||||
"check-types": "tsc --noEmit",
|
||||
"lint": "oxlint",
|
||||
"lint:fix": "oxlint --fix",
|
||||
"fmt": "oxfmt --config ../../.oxfmtrc.json",
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
{
|
||||
"$schema": "../../node_modules/oxlint/configuration_schema.json",
|
||||
"extends": ["../../.oxlintrc.json"]
|
||||
"extends": ["../../.oxlintrc.json"],
|
||||
"settings": {
|
||||
"react": {
|
||||
"version": "19.2.8"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
"./components/*": "./src/components/*.tsx"
|
||||
},
|
||||
"scripts": {
|
||||
"check-types": "tsc --noEmit",
|
||||
"lint": "oxlint",
|
||||
"lint:fix": "oxlint --fix",
|
||||
"fmt": "oxfmt --config ../../.oxfmtrc.json",
|
||||
|
||||
@@ -98,7 +98,7 @@ CertificateExpiryEmail.PreviewProps = {
|
||||
daysRemaining: 7,
|
||||
issuer: "Let's Encrypt",
|
||||
baseUrl: "https://domainstack.io",
|
||||
} as CertificateExpiryEmailProps;
|
||||
};
|
||||
|
||||
export default CertificateExpiryEmail;
|
||||
|
||||
|
||||
@@ -70,6 +70,6 @@ DeleteAccountVerifyEmail.PreviewProps = {
|
||||
userName: "Jake",
|
||||
confirmUrl: "https://domainstack.io/api/auth/delete-user?token=abc123",
|
||||
baseUrl: "https://domainstack.io",
|
||||
} as DeleteAccountVerifyEmailProps;
|
||||
};
|
||||
|
||||
export default DeleteAccountVerifyEmail;
|
||||
|
||||
@@ -64,6 +64,6 @@ function ProUpgradeSuccessEmail({ userName, baseUrl }: ProUpgradeSuccessEmailPro
|
||||
ProUpgradeSuccessEmail.PreviewProps = {
|
||||
userName: "Jake",
|
||||
baseUrl: "https://domainstack.io",
|
||||
} as ProUpgradeSuccessEmailProps;
|
||||
};
|
||||
|
||||
export default ProUpgradeSuccessEmail;
|
||||
|
||||
@@ -82,6 +82,6 @@ function ProWelcomeEmail({ userName, baseUrl }: ProWelcomeEmailProps) {
|
||||
ProWelcomeEmail.PreviewProps = {
|
||||
userName: "Jake",
|
||||
baseUrl: "https://domainstack.io",
|
||||
} as ProWelcomeEmailProps;
|
||||
};
|
||||
|
||||
export default ProWelcomeEmail;
|
||||
|
||||
@@ -72,6 +72,6 @@ SubscriptionCancelingEmail.PreviewProps = {
|
||||
userName: "Jake",
|
||||
endDate: "January 15, 2025",
|
||||
baseUrl: "https://domainstack.io",
|
||||
} as SubscriptionCancelingEmailProps;
|
||||
};
|
||||
|
||||
export default SubscriptionCancelingEmail;
|
||||
|
||||
@@ -86,6 +86,6 @@ SubscriptionExpiredEmail.PreviewProps = {
|
||||
userName: "Jake",
|
||||
archivedCount: 12,
|
||||
baseUrl: "https://domainstack.io",
|
||||
} as SubscriptionExpiredEmailProps;
|
||||
};
|
||||
|
||||
export default SubscriptionExpiredEmail;
|
||||
|
||||
@@ -143,6 +143,6 @@ VerificationInstructionsEmail.PreviewProps = {
|
||||
htmlFileContent: "domainstack-verify=abc123xyz",
|
||||
metaTag: '<meta name="domainstack-verify" content="abc123xyz">',
|
||||
baseUrl: "https://domainstack.io",
|
||||
} as VerificationInstructionsEmailProps;
|
||||
};
|
||||
|
||||
export default VerificationInstructionsEmail;
|
||||
|
||||
@@ -64,6 +64,6 @@ VerificationRevokedEmail.PreviewProps = {
|
||||
userName: "Jake",
|
||||
domainName: "example.com",
|
||||
baseUrl: "https://domainstack.io",
|
||||
} as VerificationRevokedEmailProps;
|
||||
};
|
||||
|
||||
export default VerificationRevokedEmail;
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"$schema": "../../node_modules/oxlint/configuration_schema.json",
|
||||
"extends": ["../../.oxlintrc.json"]
|
||||
}
|
||||
@@ -8,7 +8,6 @@
|
||||
".": "./src/index.ts"
|
||||
},
|
||||
"scripts": {
|
||||
"check-types": "tsc --noEmit",
|
||||
"lint": "oxlint",
|
||||
"lint:fix": "oxlint --fix",
|
||||
"fmt": "oxfmt --config ../../.oxfmtrc.json",
|
||||
|
||||
@@ -99,7 +99,7 @@ export async function optimizeImage(input: Buffer, options: OptimizeImageOptions
|
||||
return pipeline.png({ quality, compressionLevel: 9 }).toBuffer();
|
||||
case "jpeg":
|
||||
return pipeline.jpeg({ quality, mozjpeg: true }).toBuffer();
|
||||
default:
|
||||
case "webp":
|
||||
return pipeline.webp({ quality }).toBuffer();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
".": "./src/index.ts"
|
||||
},
|
||||
"scripts": {
|
||||
"check-types": "tsc --noEmit",
|
||||
"lint": "oxlint",
|
||||
"lint:fix": "oxlint --fix",
|
||||
"fmt": "oxfmt --config ../../.oxfmtrc.json",
|
||||
|
||||
@@ -149,8 +149,7 @@ function toAttributes(record: Record<string, unknown>): Record<string, string |
|
||||
*/
|
||||
export function toLogRecord(record: Record<string, unknown>): LogRecord {
|
||||
const timestamp = toTimestamp(record.time);
|
||||
const body =
|
||||
typeof record.msg === "string" ? record.msg : record.msg != null ? String(record.msg) : "";
|
||||
const body = typeof record.msg === "string" ? record.msg : "";
|
||||
|
||||
return {
|
||||
body,
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
"./server": "./src/server.ts"
|
||||
},
|
||||
"scripts": {
|
||||
"check-types": "tsc --noEmit",
|
||||
"lint": "oxlint",
|
||||
"lint:fix": "oxlint --fix",
|
||||
"fmt": "oxfmt --config ../../.oxfmtrc.json",
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
"./ratelimit": "./src/ratelimit.ts"
|
||||
},
|
||||
"scripts": {
|
||||
"check-types": "tsc --noEmit",
|
||||
"lint": "oxlint",
|
||||
"lint:fix": "oxlint --fix",
|
||||
"fmt": "oxfmt --config ../../.oxfmtrc.json",
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
".": "./src/index.ts"
|
||||
},
|
||||
"scripts": {
|
||||
"check-types": "tsc --noEmit",
|
||||
"lint": "oxlint",
|
||||
"lint:fix": "oxlint --fix",
|
||||
"fmt": "oxfmt --config ../../.oxfmtrc.json",
|
||||
|
||||
@@ -625,7 +625,7 @@ describe("safeFetch", () => {
|
||||
|
||||
it("times out DNS lookup before fetch starts", async () => {
|
||||
vi.useFakeTimers();
|
||||
mockLookup.mockReturnValue(new Promise(() => {}) as ReturnType<typeof lookup>);
|
||||
mockLookup.mockReturnValue(new Promise(() => {}));
|
||||
const mockFetch = createMockFetch(mockResponse("OK", { status: 200 }));
|
||||
|
||||
const errorPromise = safeFetch({
|
||||
|
||||
@@ -301,7 +301,7 @@ async function readBodyWithLimit(
|
||||
if (partial.length > 0) chunks.push(partial);
|
||||
|
||||
try {
|
||||
reader.cancel();
|
||||
void reader.cancel();
|
||||
} catch {
|
||||
// Ignore cancel errors
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
".": "./src/index.ts"
|
||||
},
|
||||
"scripts": {
|
||||
"check-types": "tsc --noEmit",
|
||||
"lint": "oxlint",
|
||||
"lint:fix": "oxlint --fix",
|
||||
"fmt": "oxfmt --config ../../.oxfmtrc.json",
|
||||
|
||||
@@ -39,7 +39,7 @@ async function createBrowser(): Promise<import("puppeteer-core").Browser> {
|
||||
const executablePath = await chromium.executablePath();
|
||||
|
||||
const baseArgs = Array.isArray((chromium as unknown as { args?: unknown }).args)
|
||||
? (chromium.args as string[])
|
||||
? chromium.args
|
||||
: [];
|
||||
|
||||
return launch({
|
||||
@@ -60,8 +60,8 @@ async function createBrowser(): Promise<import("puppeteer-core").Browser> {
|
||||
headless: true,
|
||||
args: mergeArgs(STABILITY_ARGS),
|
||||
defaultViewport: null,
|
||||
} as never);
|
||||
return browser as unknown as import("puppeteer-core").Browser;
|
||||
});
|
||||
return browser;
|
||||
} catch {
|
||||
// Fallback: require an explicit executable path for a locally installed Chrome/Chromium
|
||||
const { launch } = await import("puppeteer-core");
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
"./cloudflare": "./src/cloudflare.ts"
|
||||
},
|
||||
"scripts": {
|
||||
"check-types": "tsc --noEmit",
|
||||
"lint": "oxlint",
|
||||
"lint:fix": "oxlint --fix",
|
||||
"fmt": "oxfmt --config ../../.oxfmtrc.json",
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
* and stripping invisible formatting characters.
|
||||
*/
|
||||
export function sanitizeText(input: unknown): string {
|
||||
let out = String(input ?? "");
|
||||
let out = typeof input === "string" ? input : "";
|
||||
out = out.trim().replace(/\s+/g, " ");
|
||||
let res = "";
|
||||
for (let i = 0; i < out.length; i++) {
|
||||
@@ -20,7 +20,7 @@ export function sanitizeText(input: unknown): string {
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
res += out[i] as string;
|
||||
res += out[i];
|
||||
}
|
||||
// Strip invisible formatting chars (ZWSP, bidi marks, BOM)
|
||||
return res.replace(/[\u200B-\u200F\u202A-\u202E\u2066-\u2069\uFEFF]/g, "");
|
||||
|
||||
@@ -45,7 +45,7 @@ export async function fetchCertificateChain(
|
||||
},
|
||||
() => {
|
||||
socket.setTimeout(0);
|
||||
const peer = socket.getPeerCertificate(true) as DetailedPeerCertificate;
|
||||
const peer = socket.getPeerCertificate(true);
|
||||
|
||||
const rawChain: RawCertificate[] = [];
|
||||
let current: DetailedPeerCertificate | null = peer;
|
||||
|
||||
@@ -7,13 +7,19 @@ import { type BootstrapData, lookup } from "rdapper";
|
||||
import type { RdapLookupResult, WhoisLookupOptions } from "./types";
|
||||
import { RDAP_BOOTSTRAP_URL } from "./types";
|
||||
|
||||
function errorText(error: unknown): string {
|
||||
if (typeof error === "string") return error;
|
||||
if (error instanceof Error) return error.message;
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if error indicates an unsupported TLD.
|
||||
*/
|
||||
function isExpectedRegistrationError(error: unknown): boolean {
|
||||
if (!error) return false;
|
||||
|
||||
const errorStr = String(error).toLowerCase();
|
||||
const errorStr = errorText(error).toLowerCase();
|
||||
|
||||
return (
|
||||
errorStr.includes("no whois server discovered") ||
|
||||
@@ -30,7 +36,7 @@ function isExpectedRegistrationError(error: unknown): boolean {
|
||||
function isTimeoutError(error: unknown): boolean {
|
||||
if (!error) return false;
|
||||
|
||||
const errorStr = String(error).toLowerCase();
|
||||
const errorStr = errorText(error).toLowerCase();
|
||||
return (
|
||||
errorStr.includes("whois socket timeout") ||
|
||||
errorStr.includes("whois timeout") ||
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
".": "./src/index.ts"
|
||||
},
|
||||
"scripts": {
|
||||
"check-types": "tsc --noEmit",
|
||||
"lint": "oxlint",
|
||||
"lint:fix": "oxlint --fix",
|
||||
"fmt": "oxfmt --config ../../.oxfmtrc.json",
|
||||
|
||||
@@ -1,4 +1,18 @@
|
||||
{
|
||||
"$schema": "../../node_modules/oxlint/configuration_schema.json",
|
||||
"extends": ["../../.oxlintrc.json"]
|
||||
"extends": ["../../.oxlintrc.json"],
|
||||
"env": {
|
||||
"browser": true
|
||||
},
|
||||
"settings": {
|
||||
"react": {
|
||||
"version": "19.2.8"
|
||||
},
|
||||
"jsx-a11y": {
|
||||
"components": {
|
||||
"Link": "a",
|
||||
"Button": "button"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
"./*": "./src/components/*.tsx"
|
||||
},
|
||||
"scripts": {
|
||||
"check-types": "tsc --noEmit",
|
||||
"lint": "oxlint",
|
||||
"lint:fix": "oxlint --fix",
|
||||
"fmt": "oxfmt --config ../../.oxfmtrc.json",
|
||||
|
||||
@@ -63,6 +63,8 @@ function PaginationLink({
|
||||
className={className}
|
||||
nativeButton={false}
|
||||
render={
|
||||
// Children are passed through `...props` by PaginationLink callers.
|
||||
// oxlint-disable-next-line jsx-a11y/anchor-has-content
|
||||
<a
|
||||
aria-current={isActive ? "page" : undefined}
|
||||
data-slot="pagination-link"
|
||||
|
||||
@@ -35,11 +35,8 @@ function ResponsiveTooltipTrigger({
|
||||
nativeButton,
|
||||
closeDelay,
|
||||
...props
|
||||
}: Omit<
|
||||
TooltipPrimitive.Trigger.Props<unknown> & PopoverPrimitive.Trigger.Props<unknown>,
|
||||
"handle"
|
||||
> &
|
||||
Pick<PopoverPrimitive.Trigger.Props<unknown>, "nativeButton">) {
|
||||
}: Omit<TooltipPrimitive.Trigger.Props & PopoverPrimitive.Trigger.Props, "handle"> &
|
||||
Pick<PopoverPrimitive.Trigger.Props, "nativeButton">) {
|
||||
const ctx = useContext(ResponsiveTooltipContext);
|
||||
if (!ctx) {
|
||||
throw new Error("ResponsiveTooltipTrigger must be used within <ResponsiveTooltip>.");
|
||||
|
||||
@@ -347,7 +347,7 @@ function StepperTrigger({ render, className, children, tabIndex, ...props }: Ste
|
||||
|
||||
return useRender({
|
||||
defaultTagName: "button",
|
||||
render: render as useRender.RenderProp<Record<string, unknown>> | undefined,
|
||||
render: render as useRender.RenderProp | undefined,
|
||||
ref: btnRef,
|
||||
state: triggerState as unknown as Record<string, unknown>,
|
||||
props: mergeProps(defaultProps, props),
|
||||
|
||||
@@ -104,6 +104,8 @@ function VideoPlayerMuteButton({
|
||||
}
|
||||
|
||||
function VideoPlayerContent({ className, ...props }: React.ComponentProps<"video">) {
|
||||
// Captions are supplied by the consumer via `children` / `<track>`.
|
||||
// oxlint-disable-next-line jsx-a11y/media-has-caption
|
||||
return <video className={cn("mt-0 mb-0", className)} {...props} />;
|
||||
}
|
||||
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
"./verification": "./src/verification.ts"
|
||||
},
|
||||
"scripts": {
|
||||
"check-types": "tsc --noEmit",
|
||||
"lint": "oxlint",
|
||||
"lint:fix": "oxlint --fix",
|
||||
"fmt": "oxfmt --config ../../.oxfmtrc.json",
|
||||
|
||||
@@ -155,7 +155,8 @@ describe("queryDohProvider", () => {
|
||||
it("adds cacheBust parameter when option is true", async () => {
|
||||
let capturedUrl = "";
|
||||
mockFetchImplementation((input) => {
|
||||
capturedUrl = input.toString();
|
||||
capturedUrl =
|
||||
typeof input === "string" ? input : input instanceof URL ? input.href : input.url;
|
||||
return Promise.resolve({
|
||||
ok: true,
|
||||
json: () => Promise.resolve({ Status: 0 }),
|
||||
@@ -172,7 +173,8 @@ describe("queryDohProvider", () => {
|
||||
it("does not add cacheBust parameter by default", async () => {
|
||||
let capturedUrl = "";
|
||||
mockFetchImplementation((input) => {
|
||||
capturedUrl = input.toString();
|
||||
capturedUrl =
|
||||
typeof input === "string" ? input : input instanceof URL ? input.href : input.url;
|
||||
return Promise.resolve({
|
||||
ok: true,
|
||||
json: () => Promise.resolve({ Status: 0 }),
|
||||
|
||||
Generated
+70
-5
@@ -41,7 +41,10 @@ importers:
|
||||
version: 0.66.0
|
||||
oxlint:
|
||||
specifier: ^1.81.0
|
||||
version: 1.81.0
|
||||
version: 1.81.0(oxlint-tsgolint@7.0.2001)
|
||||
oxlint-tsgolint:
|
||||
specifier: ^7.0.2001
|
||||
version: 7.0.2001
|
||||
shx:
|
||||
specifier: ^0.4.0
|
||||
version: 0.4.0
|
||||
@@ -2325,6 +2328,36 @@ packages:
|
||||
cpu: [x64]
|
||||
os: [win32]
|
||||
|
||||
'@oxlint-tsgolint/darwin-arm64@7.0.2001':
|
||||
resolution: {integrity: sha512-CUJEdbSZ54+Xy9OXqOhWLTKZKV0BBiV7C2i/ygyVmXtkUNXx5YCzN8DpSSshTAKktoL7S+tnQ/ftFG/i7X896w==}
|
||||
cpu: [arm64]
|
||||
os: [darwin]
|
||||
|
||||
'@oxlint-tsgolint/darwin-x64@7.0.2001':
|
||||
resolution: {integrity: sha512-pXfBb5BqONCcgrXQNUZWXgiYmRSWJzd97S8i41VVOh6ut0tyo+cJ5FKFpczDHxiVNfj/3e7c9B4MtztNdpIVCw==}
|
||||
cpu: [x64]
|
||||
os: [darwin]
|
||||
|
||||
'@oxlint-tsgolint/linux-arm64@7.0.2001':
|
||||
resolution: {integrity: sha512-roP7zujb/QDPzDwEKsFFpzNHHy91/Y7oX9vQXk78ekyZtcQj1QXDIMH33gjDdHBfRl4K9pZ36xhRgrP4Zr+R8A==}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
|
||||
'@oxlint-tsgolint/linux-x64@7.0.2001':
|
||||
resolution: {integrity: sha512-UDezNqdECVmngu2TPnjaS1YoAmcTaBoI5lV9vk3VahBxoi+I5r9k3iJTT7qZoYWOXTD/7T7bNcwRgrocR6BscQ==}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
|
||||
'@oxlint-tsgolint/win32-arm64@7.0.2001':
|
||||
resolution: {integrity: sha512-uJZhqB6pdXLuN+AD1F5082byyQti/NPmJA77GtcFlmT2HzRelqbNls3SaIqxpjdFgvSBF9g0yOKGBkGFg7kX8Q==}
|
||||
cpu: [arm64]
|
||||
os: [win32]
|
||||
|
||||
'@oxlint-tsgolint/win32-x64@7.0.2001':
|
||||
resolution: {integrity: sha512-FkDRm8hx9OwzGQqyWG1tO5QrTLRApff9DzSgpz9QZau37BR8d1VYKOxMLGf6shPZntJFoTwIIJYT68VndYDCog==}
|
||||
cpu: [x64]
|
||||
os: [win32]
|
||||
|
||||
'@oxlint/binding-android-arm-eabi@1.81.0':
|
||||
resolution: {integrity: sha512-IcCRsXiedJoJopY6mpZUBEeVFsUrutmrG7dZ87zMuKJlhg70Ora9bBl1WcCxZQtyI10YpnVdEso5oCg7YcfSHw==}
|
||||
engines: {node: ^20.19.0 || >=22.12.0}
|
||||
@@ -6057,6 +6090,10 @@ packages:
|
||||
vite-plus:
|
||||
optional: true
|
||||
|
||||
oxlint-tsgolint@7.0.2001:
|
||||
resolution: {integrity: sha512-KjK/XLcXr1DSyonKhsuFqJRiuKqcyG9j3LJ8nkOsrLzGvodBPqzHOKauy10asLMDI0sUpvb+1sxlzff3udZvfg==}
|
||||
hasBin: true
|
||||
|
||||
oxlint@1.81.0:
|
||||
resolution: {integrity: sha512-HyrJYqeoOCL0iqaLEzGewGT48ZX99P3hxYh8udAF9RGGIghSamkXE4ClUyBpEDNqasamThgmlPbuMOe7SAZmHg==}
|
||||
engines: {node: ^20.19.0 || >=22.12.0}
|
||||
@@ -8690,6 +8727,24 @@ snapshots:
|
||||
'@oxfmt/binding-win32-x64-msvc@0.66.0':
|
||||
optional: true
|
||||
|
||||
'@oxlint-tsgolint/darwin-arm64@7.0.2001':
|
||||
optional: true
|
||||
|
||||
'@oxlint-tsgolint/darwin-x64@7.0.2001':
|
||||
optional: true
|
||||
|
||||
'@oxlint-tsgolint/linux-arm64@7.0.2001':
|
||||
optional: true
|
||||
|
||||
'@oxlint-tsgolint/linux-x64@7.0.2001':
|
||||
optional: true
|
||||
|
||||
'@oxlint-tsgolint/win32-arm64@7.0.2001':
|
||||
optional: true
|
||||
|
||||
'@oxlint-tsgolint/win32-x64@7.0.2001':
|
||||
optional: true
|
||||
|
||||
'@oxlint/binding-android-arm-eabi@1.81.0':
|
||||
optional: true
|
||||
|
||||
@@ -9454,7 +9509,7 @@ snapshots:
|
||||
|
||||
'@types/set-cookie-parser@2.4.10':
|
||||
dependencies:
|
||||
'@types/node': 24.13.3
|
||||
'@types/node': 26.4.1
|
||||
|
||||
'@types/statuses@2.0.6': {}
|
||||
|
||||
@@ -9683,9 +9738,9 @@ snapshots:
|
||||
obug: 2.1.4
|
||||
std-env: 4.2.0
|
||||
tinyrainbow: 3.1.1
|
||||
vitest: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@24.13.3)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(msw@2.15.0(@types/node@24.13.3)(typescript@7.0.2))(vite@8.2.2(@types/node@24.13.3)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.51.2)(tsx@4.23.13))
|
||||
vitest: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.4.1)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(msw@2.15.0(@types/node@26.4.1)(typescript@7.0.2))(vite@8.2.2(@types/node@26.4.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.51.2)(tsx@4.23.13))
|
||||
optionalDependencies:
|
||||
'@vitest/browser': 4.1.11(bufferutil@4.1.0)(msw@2.15.0(@types/node@24.13.3)(typescript@7.0.2))(vite@8.2.2(@types/node@24.13.3)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.51.2)(tsx@4.23.13))(vitest@4.1.11)
|
||||
'@vitest/browser': 4.1.11(bufferutil@4.1.0)(msw@2.15.0(@types/node@26.4.1)(typescript@7.0.2))(vite@8.2.2(@types/node@26.4.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.51.2)(tsx@4.23.13))(vitest@4.1.11)
|
||||
|
||||
'@vitest/expect@4.1.11':
|
||||
dependencies:
|
||||
@@ -12602,7 +12657,16 @@ snapshots:
|
||||
'@oxfmt/binding-win32-ia32-msvc': 0.66.0
|
||||
'@oxfmt/binding-win32-x64-msvc': 0.66.0
|
||||
|
||||
oxlint@1.81.0:
|
||||
oxlint-tsgolint@7.0.2001:
|
||||
optionalDependencies:
|
||||
'@oxlint-tsgolint/darwin-arm64': 7.0.2001
|
||||
'@oxlint-tsgolint/darwin-x64': 7.0.2001
|
||||
'@oxlint-tsgolint/linux-arm64': 7.0.2001
|
||||
'@oxlint-tsgolint/linux-x64': 7.0.2001
|
||||
'@oxlint-tsgolint/win32-arm64': 7.0.2001
|
||||
'@oxlint-tsgolint/win32-x64': 7.0.2001
|
||||
|
||||
oxlint@1.81.0(oxlint-tsgolint@7.0.2001):
|
||||
optionalDependencies:
|
||||
'@oxlint/binding-android-arm-eabi': 1.81.0
|
||||
'@oxlint/binding-android-arm64': 1.81.0
|
||||
@@ -12623,6 +12687,7 @@ snapshots:
|
||||
'@oxlint/binding-win32-arm64-msvc': 1.81.0
|
||||
'@oxlint/binding-win32-ia32-msvc': 1.81.0
|
||||
'@oxlint/binding-win32-x64-msvc': 1.81.0
|
||||
oxlint-tsgolint: 7.0.2001
|
||||
|
||||
p-cancelable@4.0.1: {}
|
||||
|
||||
|
||||
@@ -42,10 +42,6 @@
|
||||
"persistent": true,
|
||||
"cache": false
|
||||
},
|
||||
"check-types": {
|
||||
"dependsOn": ["^check-types"],
|
||||
"outputs": []
|
||||
},
|
||||
"lint": {
|
||||
"dependsOn": ["^lint"],
|
||||
"outputs": []
|
||||
|
||||
Reference in New Issue
Block a user