diff --git a/.oxlintrc.json b/.oxlintrc.json
new file mode 100644
index 0000000..879b145
--- /dev/null
+++ b/.oxlintrc.json
@@ -0,0 +1,26 @@
+{
+ "$schema": "https://raw.githubusercontent.com/nicolo-ribaudo/tc39-proposal-type-annotations/refs/heads/main/packages/oxlint/configuration_file_schema.json",
+ "plugins": ["typescript", "unicorn", "jest"],
+ "categories": {},
+ "rules": {
+ "typescript/no-unnecessary-type-assertion": "error",
+ "typescript/no-deprecated": "warn",
+ "unicorn/prefer-string-replace-all": "error",
+ "unicorn/prefer-string-raw": "error",
+ "jest/expect-expect": [
+ "error",
+ {
+ "assertFunctionNames": ["expect", "expectDomainError"]
+ }
+ ]
+ },
+ "ignorePatterns": [
+ "dist",
+ "coverage",
+ ".claude",
+ ".specify",
+ "specs",
+ ".pnpm-store",
+ "scripts"
+ ]
+}
diff --git a/CLAUDE.md b/CLAUDE.md
index 770fe05..699b6c6 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -5,7 +5,8 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
## Commands
```bash
-pnpm check # Merge gate — must pass before every commit (audit + knip + biome + typecheck + test/coverage + jscpd)
+pnpm check # Merge gate — must pass before every commit (audit + knip + biome + oxlint + typecheck + test/coverage + jscpd)
+pnpm oxlint # Type-aware linting (oxlint — complements Biome)
pnpm knip # Unused code detection (Knip)
pnpm test # Run all tests (Vitest)
pnpm test:watch # Tests in watch mode
@@ -58,12 +59,13 @@ docs/agents/ RPI skill artifacts (research reports, plans)
- React 19, Vite 6, Tailwind CSS v4
- Lucide React (icons)
- `idb` (IndexedDB wrapper for bestiary cache)
-- Biome 2.0 (formatting + linting), Knip (unused code), jscpd (copy-paste detection)
+- Biome 2.4 (formatting + linting), oxlint (type-aware linting), Knip (unused code), jscpd (copy-paste detection)
- Vitest (testing, v8 coverage), Lefthook (pre-commit hooks)
## Conventions
-- **Biome 2.0** for formatting and linting (no Prettier, no ESLint). Tab indentation, 80-char lines. Imports are auto-organized alphabetically.
+- **Biome 2.4** for formatting and linting (no Prettier, no ESLint). Tab indentation, 80-char lines. Imports are auto-organized alphabetically.
+- **oxlint** for type-aware linting that Biome can't do (unnecessary type assertions, deprecated APIs, `replaceAll` preference, `String.raw`). Configured in `.oxlintrc.json`.
- **TypeScript strict mode** with `verbatimModuleSyntax`. Use `.js` extensions in relative imports when required by the repo's ESM settings (e.g., `./types.js`).
- **Branded types** for identity values (e.g., `CombatantId`). Prefer immutability/`readonly` where practical.
- **Domain events** are plain data objects with a `type` discriminant — no classes.
diff --git a/apps/web/src/App.tsx b/apps/web/src/App.tsx
index f1d5717..ef7f90b 100644
--- a/apps/web/src/App.tsx
+++ b/apps/web/src/App.tsx
@@ -157,8 +157,8 @@ export function App() {
(result: SearchResult) => {
const slug = result.name
.toLowerCase()
- .replace(/[^a-z0-9]+/g, "-")
- .replace(/(^-|-$)/g, "");
+ .replaceAll(/[^a-z0-9]+/g, "-")
+ .replaceAll(/(^-|-$)/g, "");
const cId = `${result.source.toLowerCase()}:${slug}` as CreatureId;
sidePanel.showCreature(cId);
},
@@ -205,7 +205,7 @@ export function App() {
if (!globalThis.matchMedia("(min-width: 1024px)").matches) return;
const active = encounter.combatants[encounter.activeIndex];
if (!active?.creatureId || !isLoaded) return;
- sidePanel.showCreature(active.creatureId as CreatureId);
+ sidePanel.showCreature(active.creatureId);
}, [
encounter.activeIndex,
encounter.combatants,
diff --git a/apps/web/src/adapters/__tests__/strip-tags.test.ts b/apps/web/src/adapters/__tests__/strip-tags.test.ts
index fe88001..101b5ca 100644
--- a/apps/web/src/adapters/__tests__/strip-tags.test.ts
+++ b/apps/web/src/adapters/__tests__/strip-tags.test.ts
@@ -30,11 +30,11 @@ describe("stripTags", () => {
expect(stripTags("{@hit 5}")).toBe("+5");
});
- it("strips {@h} to Hit: ", () => {
+ it("strips {@h} to Hit:", () => {
expect(stripTags("{@h}")).toBe("Hit: ");
});
- it("strips {@hom} to Hit or Miss: ", () => {
+ it("strips {@hom} to Hit or Miss:", () => {
expect(stripTags("{@hom}")).toBe("Hit or Miss: ");
});
diff --git a/apps/web/src/adapters/bestiary-adapter.ts b/apps/web/src/adapters/bestiary-adapter.ts
index ba2a5b8..e2fcfb7 100644
--- a/apps/web/src/adapters/bestiary-adapter.ts
+++ b/apps/web/src/adapters/bestiary-adapter.ts
@@ -373,8 +373,8 @@ function extractCr(cr: string | { cr: string } | undefined): string {
function makeCreatureId(source: string, name: string): CreatureId {
const slug = name
.toLowerCase()
- .replace(/[^a-z0-9]+/g, "-")
- .replace(/(^-|-$)/g, "");
+ .replaceAll(/[^a-z0-9]+/g, "-")
+ .replaceAll(/(^-|-$)/g, "");
return creatureId(`${source.toLowerCase()}:${slug}`);
}
diff --git a/apps/web/src/adapters/strip-tags.ts b/apps/web/src/adapters/strip-tags.ts
index 3ab37f9..298e287 100644
--- a/apps/web/src/adapters/strip-tags.ts
+++ b/apps/web/src/adapters/strip-tags.ts
@@ -25,55 +25,58 @@ export function stripTags(text: string): string {
let result = text;
// {@h} → "Hit: "
- result = result.replace(/\{@h\}/g, "Hit: ");
+ result = result.replaceAll("{@h}", "Hit: ");
// {@hom} → "Hit or Miss: "
- result = result.replace(/\{@hom\}/g, "Hit or Miss: ");
+ result = result.replaceAll("{@hom}", "Hit or Miss: ");
// {@actTrigger} → "Trigger:"
- result = result.replace(/\{@actTrigger\}/g, "Trigger:");
+ result = result.replaceAll("{@actTrigger}", "Trigger:");
// {@actResponse} → "Response:"
- result = result.replace(/\{@actResponse\}/g, "Response:");
+ result = result.replaceAll("{@actResponse}", "Response:");
// {@actSaveSuccess} → "Success:"
- result = result.replace(/\{@actSaveSuccess\}/g, "Success:");
+ result = result.replaceAll("{@actSaveSuccess}", "Success:");
// {@actSaveSuccessOrFail} → handled below as parameterized
// {@recharge 5} → "(Recharge 5-6)", {@recharge} → "(Recharge 6)"
- result = result.replace(/\{@recharge\s+(\d)\}/g, "(Recharge $1-6)");
- result = result.replace(/\{@recharge\}/g, "(Recharge 6)");
+ result = result.replaceAll(/\{@recharge\s+(\d)\}/g, "(Recharge $1-6)");
+ result = result.replaceAll("{@recharge}", "(Recharge 6)");
// {@dc N} → "DC N"
- result = result.replace(/\{@dc\s+(\d+)\}/g, "DC $1");
+ result = result.replaceAll(/\{@dc\s+(\d+)\}/g, "DC $1");
// {@hit N} → "+N"
- result = result.replace(/\{@hit\s+(\d+)\}/g, "+$1");
+ result = result.replaceAll(/\{@hit\s+(\d+)\}/g, "+$1");
// {@atkr type} → mapped attack roll text
- result = result.replace(/\{@atkr\s+([^}]+)\}/g, (_, type: string) => {
+ result = result.replaceAll(/\{@atkr\s+([^}]+)\}/g, (_, type: string) => {
return ATKR_MAP[type.trim()] ?? "Attack Roll:";
});
// {@actSave ability} → "Ability saving throw"
- result = result.replace(/\{@actSave\s+([^}]+)\}/g, (_, ability: string) => {
- const name = ABILITY_MAP[ability.trim().toLowerCase()];
- return name ? `${name} saving throw` : `${ability} saving throw`;
- });
+ result = result.replaceAll(
+ /\{@actSave\s+([^}]+)\}/g,
+ (_, ability: string) => {
+ const name = ABILITY_MAP[ability.trim().toLowerCase()];
+ return name ? `${name} saving throw` : `${ability} saving throw`;
+ },
+ );
// {@actSaveFail} → "Failure:" or {@actSaveFail N} → "Failure by N or More:"
- result = result.replace(
+ result = result.replaceAll(
/\{@actSaveFail\s+(\d+)\}/g,
"Failure by $1 or More:",
);
- result = result.replace(/\{@actSaveFail\}/g, "Failure:");
+ result = result.replaceAll("{@actSaveFail}", "Failure:");
// {@actSaveSuccessOrFail} → keep as-is label
- result = result.replace(/\{@actSaveSuccessOrFail\}/g, "Success or Failure:");
+ result = result.replaceAll("{@actSaveSuccessOrFail}", "Success or Failure:");
// {@actSaveFailBy N} → "Failure by N or More:"
- result = result.replace(
+ result = result.replaceAll(
/\{@actSaveFailBy\s+(\d+)\}/g,
"Failure by $1 or More:",
);
@@ -81,7 +84,7 @@ export function stripTags(text: string): string {
// Generic tags: {@tag Display|Source|...} → Display (first segment before |)
// Covers: spell, condition, damage, dice, variantrule, action, skill,
// creature, hazard, status, plus any unknown tags
- result = result.replace(
+ result = result.replaceAll(
/\{@(\w+)\s+([^}]+)\}/g,
(_, tag: string, content: string) => {
// For tags with Display|Source format, extract first segment
diff --git a/apps/web/src/components/action-bar.tsx b/apps/web/src/components/action-bar.tsx
index 3ec7afb..4617c53 100644
--- a/apps/web/src/components/action-bar.tsx
+++ b/apps/web/src/components/action-bar.tsx
@@ -1,4 +1,4 @@
-import type { PlayerCharacter, PlayerIcon } from "@initiative/domain";
+import type { PlayerCharacter } from "@initiative/domain";
import {
Check,
Eye,
@@ -9,12 +9,7 @@ import {
Plus,
Users,
} from "lucide-react";
-import {
- type FormEvent,
- type RefObject,
- useDeferredValue,
- useState,
-} from "react";
+import { type RefObject, useDeferredValue, useState } from "react";
import type { SearchResult } from "../hooks/use-bestiary.js";
import { cn } from "../lib/utils.js";
import { D20Icon } from "./d20-icon.js";
@@ -103,11 +98,9 @@ function AddModeSuggestions({
{pcMatches.map((pc) => {
- const PcIcon = pc.icon
- ? PLAYER_ICON_MAP[pc.icon as PlayerIcon]
- : undefined;
+ const PcIcon = pc.icon ? PLAYER_ICON_MAP[pc.icon] : undefined;
const pcColor = pc.color
- ? PLAYER_COLOR_HEX[pc.color as keyof typeof PLAYER_COLOR_HEX]
+ ? PLAYER_COLOR_HEX[pc.color]
: undefined;
return (
-
@@ -318,7 +311,7 @@ export function ActionBar({
return Number.isNaN(n) ? undefined : n;
};
- const handleAdd = (e: FormEvent) => {
+ const handleAdd = (e: React.SubmitEvent) => {
e.preventDefault();
if (browseMode) return;
if (queued) {
diff --git a/apps/web/src/components/create-player-modal.tsx b/apps/web/src/components/create-player-modal.tsx
index 8adaddc..40fd5cb 100644
--- a/apps/web/src/components/create-player-modal.tsx
+++ b/apps/web/src/components/create-player-modal.tsx
@@ -1,6 +1,6 @@
import type { PlayerCharacter } from "@initiative/domain";
import { X } from "lucide-react";
-import { type FormEvent, useEffect, useState } from "react";
+import { useEffect, useState } from "react";
import { ColorPalette } from "./color-palette";
import { IconGrid } from "./icon-grid";
import { Button } from "./ui/button";
@@ -64,7 +64,7 @@ export function CreatePlayerModal({
if (!open) return null;
- const handleSubmit = (e: FormEvent) => {
+ const handleSubmit = (e: React.SubmitEvent) => {
e.preventDefault();
const trimmed = name.trim();
if (trimmed === "") {
diff --git a/apps/web/src/hooks/use-encounter.ts b/apps/web/src/hooks/use-encounter.ts
index ada9da3..14fa447 100644
--- a/apps/web/src/hooks/use-encounter.ts
+++ b/apps/web/src/hooks/use-encounter.ts
@@ -303,8 +303,8 @@ export function useEncounter() {
// Derive creatureId from source + name
const slug = entry.name
.toLowerCase()
- .replace(/[^a-z0-9]+/g, "-")
- .replace(/(^-|-$)/g, "");
+ .replaceAll(/[^a-z0-9]+/g, "-")
+ .replaceAll(/(^-|-$)/g, "");
const cId = makeCreatureId(`${entry.source.toLowerCase()}:${slug}`);
// Set creatureId on the combatant (use store.save to keep ref in sync for batch calls)
diff --git a/package.json b/package.json
index 028d9e4..733e502 100644
--- a/package.json
+++ b/package.json
@@ -12,6 +12,8 @@
"jscpd": "^4.0.8",
"knip": "^5.85.0",
"lefthook": "^1.11.0",
+ "oxlint": "^1.55.0",
+ "oxlint-tsgolint": "^0.16.0",
"typescript": "^5.8.0",
"vitest": "^3.0.0"
},
@@ -26,6 +28,7 @@
"test:watch": "vitest",
"knip": "knip",
"jscpd": "jscpd",
- "check": "pnpm audit --audit-level=high && knip && biome check . && tsc --build && vitest run && jscpd"
+ "oxlint": "oxlint --tsconfig apps/web/tsconfig.json --type-aware",
+ "check": "pnpm audit --audit-level=high && knip && biome check . && oxlint --tsconfig apps/web/tsconfig.json --type-aware && tsc --build && vitest run && jscpd"
}
}
diff --git a/packages/domain/src/auto-number.ts b/packages/domain/src/auto-number.ts
index 812221b..2ee81ab 100644
--- a/packages/domain/src/auto-number.ts
+++ b/packages/domain/src/auto-number.ts
@@ -51,5 +51,5 @@ export function resolveCreatureName(
}
function escapeRegExp(s: string): string {
- return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
+ return s.replaceAll(/[.*+?^${}()|[\]\\]/g, String.raw`\$&`);
}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index ed636f7..d761cb2 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -26,6 +26,12 @@ importers:
lefthook:
specifier: ^1.11.0
version: 1.13.6
+ oxlint:
+ specifier: ^1.55.0
+ version: 1.55.0(oxlint-tsgolint@0.16.0)
+ oxlint-tsgolint:
+ specifier: ^0.16.0
+ version: 0.16.0
typescript:
specifier: ^5.8.0
version: 5.9.3
@@ -632,6 +638,150 @@ packages:
cpu: [x64]
os: [win32]
+ '@oxlint-tsgolint/darwin-arm64@0.16.0':
+ resolution: {integrity: sha512-WQt5lGwRPJBw7q2KNR0mSPDAaMmZmVvDlEEti96xLO7ONhyomQc6fBZxxwZ4qTFedjJnrHX94sFelZ4OKzS7UQ==}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@oxlint-tsgolint/darwin-x64@0.16.0':
+ resolution: {integrity: sha512-VJo29XOzdkalvCTiE2v6FU3qZlgHaM8x8hUEVJGPU2i5W+FlocPpmn00+Ld2n7Q0pqIjyD5EyvZ5UmoIEJMfqg==}
+ cpu: [x64]
+ os: [darwin]
+
+ '@oxlint-tsgolint/linux-arm64@0.16.0':
+ resolution: {integrity: sha512-MPfqRt1+XRHv9oHomcBMQ3KpTE+CSkZz14wUxDQoqTNdUlV0HWdzwIE9q65I3D9YyxEnqpM7j4qtDQ3apqVvbQ==}
+ cpu: [arm64]
+ os: [linux]
+
+ '@oxlint-tsgolint/linux-x64@0.16.0':
+ resolution: {integrity: sha512-XQSwVUsnwLokMhe1TD6IjgvW5WMTPzOGGkdFDtXWQmlN2YeTw94s/NN0KgDrn2agM1WIgAenEkvnm0u7NgwEyw==}
+ cpu: [x64]
+ os: [linux]
+
+ '@oxlint-tsgolint/win32-arm64@0.16.0':
+ resolution: {integrity: sha512-EWdlspQiiFGsP2AiCYdhg5dTYyAlj6y1nRyNI2dQWq4Q/LITFHiSRVPe+7m7K7lcsZCEz2icN/bCeSkZaORqIg==}
+ cpu: [arm64]
+ os: [win32]
+
+ '@oxlint-tsgolint/win32-x64@0.16.0':
+ resolution: {integrity: sha512-1ufk8cgktXJuJZHKF63zCHAkaLMwZrEXnZ89H2y6NO85PtOXqu4zbdNl0VBpPP3fCUuUBu9RvNqMFiv0VsbXWA==}
+ cpu: [x64]
+ os: [win32]
+
+ '@oxlint/binding-android-arm-eabi@1.55.0':
+ resolution: {integrity: sha512-NhvgAhncTSOhRahQSCnkK/4YIGPjTmhPurQQ2dwt2IvwCMTvZRW5vF2K10UBOxFve4GZDMw6LtXZdC2qeuYIVQ==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm]
+ os: [android]
+
+ '@oxlint/binding-android-arm64@1.55.0':
+ resolution: {integrity: sha512-P9iWRh+Ugqhg+D7rkc7boHX8o3H2h7YPcZHQIgvVBgnua5tk4LR2L+IBlreZs58/95cd2x3/004p5VsQM9z4SA==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm64]
+ os: [android]
+
+ '@oxlint/binding-darwin-arm64@1.55.0':
+ resolution: {integrity: sha512-esakkJIt7WFAhT30P/Qzn96ehFpzdZ1mNuzpOb8SCW7lI4oB8VsyQnkSHREM671jfpuBb/o2ppzBCx5l0jpgMA==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@oxlint/binding-darwin-x64@1.55.0':
+ resolution: {integrity: sha512-xDMFRCCAEK9fOH6As2z8ELsC+VDGSFRHwIKVSilw+xhgLwTDFu37rtmRbmUlx8rRGS6cWKQPTc47AVxAZEVVPQ==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [x64]
+ os: [darwin]
+
+ '@oxlint/binding-freebsd-x64@1.55.0':
+ resolution: {integrity: sha512-mYZqnwUD7ALCRxGenyLd1uuG+rHCL+OTT6S8FcAbVm/ZT2AZMGjvibp3F6k1SKOb2aeqFATmwRykrE41Q0GWVw==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [x64]
+ os: [freebsd]
+
+ '@oxlint/binding-linux-arm-gnueabihf@1.55.0':
+ resolution: {integrity: sha512-LcX6RYcF9vL9ESGwJW3yyIZ/d/ouzdOKXxCdey1q0XJOW1asrHsIg5MmyKdEBR4plQx+shvYeQne7AzW5f3T1w==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm]
+ os: [linux]
+
+ '@oxlint/binding-linux-arm-musleabihf@1.55.0':
+ resolution: {integrity: sha512-C+8GS1rPtK+dI7mJFkqoRBkDuqbrNihnyYQsJPS9ez+8zF9JzfvU19lawqt4l/Y23o5uQswE/DORa8aiXUih3w==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm]
+ os: [linux]
+
+ '@oxlint/binding-linux-arm64-gnu@1.55.0':
+ resolution: {integrity: sha512-ErLE4XbmcCopA4/CIDiH6J1IAaDOMnf/KSx/aFObs4/OjAAM3sFKWGZ57pNOMxhhyBdcmcXwYymph9GwcpcqgQ==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm64]
+ os: [linux]
+
+ '@oxlint/binding-linux-arm64-musl@1.55.0':
+ resolution: {integrity: sha512-/kp65avi6zZfqEng56TTuhiy3P/3pgklKIdf38yvYeJ9/PgEeRA2A2AqKAKbZBNAqUzrzHhz9jF6j/PZvhJzTQ==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm64]
+ os: [linux]
+
+ '@oxlint/binding-linux-ppc64-gnu@1.55.0':
+ resolution: {integrity: sha512-A6pTdXwcEEwL/nmz0eUJ6WxmxcoIS+97GbH96gikAyre3s5deC7sts38ZVVowjS2QQFuSWkpA4ZmQC0jZSNvJQ==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [ppc64]
+ os: [linux]
+
+ '@oxlint/binding-linux-riscv64-gnu@1.55.0':
+ resolution: {integrity: sha512-clj0lnIN+V52G9tdtZl0LbdTSurnZ1NZj92Je5X4lC7gP5jiCSW+Y/oiDiSauBAD4wrHt2S7nN3pA0zfKYK/6Q==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [riscv64]
+ os: [linux]
+
+ '@oxlint/binding-linux-riscv64-musl@1.55.0':
+ resolution: {integrity: sha512-NNu08pllN5x/O94/sgR3DA8lbrGBnTHsINZZR0hcav1sj79ksTiKKm1mRzvZvacwQ0hUnGinFo+JO75ok2PxYg==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [riscv64]
+ os: [linux]
+
+ '@oxlint/binding-linux-s390x-gnu@1.55.0':
+ resolution: {integrity: sha512-BvfQz3PRlWZRoEZ17dZCqgQsMRdpzGZomJkVATwCIGhHVVeHJMQdmdXPSjcT1DCNUrOjXnVyj1RGDj5+/Je2+Q==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [s390x]
+ os: [linux]
+
+ '@oxlint/binding-linux-x64-gnu@1.55.0':
+ resolution: {integrity: sha512-ngSOoFCSBMKVQd24H8zkbcBNc7EHhjnF1sv3mC9NNXQ/4rRjI/4Dj9+9XoDZeFEkF1SX1COSBXF1b2Pr9rqdEw==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [x64]
+ os: [linux]
+
+ '@oxlint/binding-linux-x64-musl@1.55.0':
+ resolution: {integrity: sha512-BDpP7W8GlaG7BR6QjGZAleYzxoyKc/D24spZIF2mB3XsfALQJJT/OBmP8YpeTb1rveFSBHzl8T7l0aqwkWNdGA==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [x64]
+ os: [linux]
+
+ '@oxlint/binding-openharmony-arm64@1.55.0':
+ resolution: {integrity: sha512-PS6GFvmde/pc3fCA2Srt51glr8Lcxhpf6WIBFfLphndjRrD34NEcses4TSxQrEcxYo6qVywGfylM0ZhSCF2gGA==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm64]
+ os: [openharmony]
+
+ '@oxlint/binding-win32-arm64-msvc@1.55.0':
+ resolution: {integrity: sha512-P6JcLJGs/q1UOvDLzN8otd9JsH4tsuuPDv+p7aHqHM3PrKmYdmUvkNj4K327PTd35AYcznOCN+l4ZOaq76QzSw==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm64]
+ os: [win32]
+
+ '@oxlint/binding-win32-ia32-msvc@1.55.0':
+ resolution: {integrity: sha512-gzkk4zE2zsE+WmRxFOiAZHpCpUNDFytEakqNXoNHW+PnYEOTPKDdW6nrzgSeTbGKVPXNAKQnRnMgrh7+n3Xueg==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [ia32]
+ os: [win32]
+
+ '@oxlint/binding-win32-x64-msvc@1.55.0':
+ resolution: {integrity: sha512-ZFALNow2/og75gvYzNP7qe+rREQ5xunktwA+lgykoozHZ6hw9bqg4fn5j2UvG4gIn1FXqrZHkOAXuPf5+GOYTQ==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [x64]
+ os: [win32]
+
'@pkgjs/parseargs@0.11.0':
resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==}
engines: {node: '>=14'}
@@ -1712,6 +1862,20 @@ packages:
oxc-resolver@11.19.1:
resolution: {integrity: sha512-qE/CIg/spwrTBFt5aKmwe3ifeDdLfA2NESN30E42X/lII5ClF8V7Wt6WIJhcGZjp0/Q+nQ+9vgxGk//xZNX2hg==}
+ oxlint-tsgolint@0.16.0:
+ resolution: {integrity: sha512-4RuJK2jP08XwqtUu+5yhCbxEauCm6tv2MFHKEMsjbosK2+vy5us82oI3VLuHwbNyZG7ekZA26U2LLHnGR4frIA==}
+ hasBin: true
+
+ oxlint@1.55.0:
+ resolution: {integrity: sha512-T+FjepiyWpaZMhekqRpH8Z3I4vNM610p6w+Vjfqgj5TZUxHXl7N8N5IPvmOU8U4XdTRxqtNNTh9Y4hLtr7yvFg==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ hasBin: true
+ peerDependencies:
+ oxlint-tsgolint: '>=0.15.0'
+ peerDependenciesMeta:
+ oxlint-tsgolint:
+ optional: true
+
package-json-from-dist@1.0.1:
resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==}
@@ -2614,6 +2778,81 @@ snapshots:
'@oxc-resolver/binding-win32-x64-msvc@11.19.1':
optional: true
+ '@oxlint-tsgolint/darwin-arm64@0.16.0':
+ optional: true
+
+ '@oxlint-tsgolint/darwin-x64@0.16.0':
+ optional: true
+
+ '@oxlint-tsgolint/linux-arm64@0.16.0':
+ optional: true
+
+ '@oxlint-tsgolint/linux-x64@0.16.0':
+ optional: true
+
+ '@oxlint-tsgolint/win32-arm64@0.16.0':
+ optional: true
+
+ '@oxlint-tsgolint/win32-x64@0.16.0':
+ optional: true
+
+ '@oxlint/binding-android-arm-eabi@1.55.0':
+ optional: true
+
+ '@oxlint/binding-android-arm64@1.55.0':
+ optional: true
+
+ '@oxlint/binding-darwin-arm64@1.55.0':
+ optional: true
+
+ '@oxlint/binding-darwin-x64@1.55.0':
+ optional: true
+
+ '@oxlint/binding-freebsd-x64@1.55.0':
+ optional: true
+
+ '@oxlint/binding-linux-arm-gnueabihf@1.55.0':
+ optional: true
+
+ '@oxlint/binding-linux-arm-musleabihf@1.55.0':
+ optional: true
+
+ '@oxlint/binding-linux-arm64-gnu@1.55.0':
+ optional: true
+
+ '@oxlint/binding-linux-arm64-musl@1.55.0':
+ optional: true
+
+ '@oxlint/binding-linux-ppc64-gnu@1.55.0':
+ optional: true
+
+ '@oxlint/binding-linux-riscv64-gnu@1.55.0':
+ optional: true
+
+ '@oxlint/binding-linux-riscv64-musl@1.55.0':
+ optional: true
+
+ '@oxlint/binding-linux-s390x-gnu@1.55.0':
+ optional: true
+
+ '@oxlint/binding-linux-x64-gnu@1.55.0':
+ optional: true
+
+ '@oxlint/binding-linux-x64-musl@1.55.0':
+ optional: true
+
+ '@oxlint/binding-openharmony-arm64@1.55.0':
+ optional: true
+
+ '@oxlint/binding-win32-arm64-msvc@1.55.0':
+ optional: true
+
+ '@oxlint/binding-win32-ia32-msvc@1.55.0':
+ optional: true
+
+ '@oxlint/binding-win32-x64-msvc@1.55.0':
+ optional: true
+
'@pkgjs/parseargs@0.11.0':
optional: true
@@ -3668,6 +3907,38 @@ snapshots:
'@oxc-resolver/binding-win32-ia32-msvc': 11.19.1
'@oxc-resolver/binding-win32-x64-msvc': 11.19.1
+ oxlint-tsgolint@0.16.0:
+ optionalDependencies:
+ '@oxlint-tsgolint/darwin-arm64': 0.16.0
+ '@oxlint-tsgolint/darwin-x64': 0.16.0
+ '@oxlint-tsgolint/linux-arm64': 0.16.0
+ '@oxlint-tsgolint/linux-x64': 0.16.0
+ '@oxlint-tsgolint/win32-arm64': 0.16.0
+ '@oxlint-tsgolint/win32-x64': 0.16.0
+
+ oxlint@1.55.0(oxlint-tsgolint@0.16.0):
+ optionalDependencies:
+ '@oxlint/binding-android-arm-eabi': 1.55.0
+ '@oxlint/binding-android-arm64': 1.55.0
+ '@oxlint/binding-darwin-arm64': 1.55.0
+ '@oxlint/binding-darwin-x64': 1.55.0
+ '@oxlint/binding-freebsd-x64': 1.55.0
+ '@oxlint/binding-linux-arm-gnueabihf': 1.55.0
+ '@oxlint/binding-linux-arm-musleabihf': 1.55.0
+ '@oxlint/binding-linux-arm64-gnu': 1.55.0
+ '@oxlint/binding-linux-arm64-musl': 1.55.0
+ '@oxlint/binding-linux-ppc64-gnu': 1.55.0
+ '@oxlint/binding-linux-riscv64-gnu': 1.55.0
+ '@oxlint/binding-linux-riscv64-musl': 1.55.0
+ '@oxlint/binding-linux-s390x-gnu': 1.55.0
+ '@oxlint/binding-linux-x64-gnu': 1.55.0
+ '@oxlint/binding-linux-x64-musl': 1.55.0
+ '@oxlint/binding-openharmony-arm64': 1.55.0
+ '@oxlint/binding-win32-arm64-msvc': 1.55.0
+ '@oxlint/binding-win32-ia32-msvc': 1.55.0
+ '@oxlint/binding-win32-x64-msvc': 1.55.0
+ oxlint-tsgolint: 0.16.0
+
package-json-from-dist@1.0.1: {}
parse5@8.0.0: