diff --git a/.vscode/settings.json b/.vscode/settings.json index 9bdb023..04b90c7 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,6 +1,14 @@ { "[json]": { "editor.formatOnSave": true, "editor.defaultFormatter": "dprint.dprint" }, "[jsonc]": { "editor.formatOnSave": true, "editor.defaultFormatter": "dprint.dprint" }, - "[javascript]": { "editor.formatOnSave": true, "editor.defaultFormatter": "dprint.dprint" }, - "[typescript]": { "editor.formatOnSave": true, "editor.defaultFormatter": "dprint.dprint" } + "[javascript]": { + "editor.formatOnSave": true, + "editor.defaultFormatter": "dprint.dprint", + "editor.codeActionsOnSave": { "source.fixAll.eslint": "always" } + }, + "[typescript]": { + "editor.formatOnSave": true, + "editor.defaultFormatter": "dprint.dprint", + "editor.codeActionsOnSave": { "source.fixAll.eslint": "always" } + } } diff --git a/TODO.txt b/TODO.txt index 686b469..6728032 100644 --- a/TODO.txt +++ b/TODO.txt @@ -47,14 +47,12 @@ Goal: a monorepo that can run a blank canvas in web + desktop. Goal: a correct, testable camera transform (world <-> screen). -Core primitives (/packages/core/src/math): -[ ] Define Vec2 { x, y } + helpers: +Core primitives (/packages/core/src/math.ts): +[x] Define Vec2 { x, y } + helpers: - add, sub, mulScalar, len, normalize, dot - -[ ] Define Box2 { min: Vec2, max: Vec2 }: +[x] Define Box2 { min: Vec2, max: Vec2 }: - fromPoints, containsPoint, intersectsBox - -[ ] Define Mat3 (2D affine) or equivalent: +[x] Define Mat3 (2D affine) or equivalent: - identity - translate(tx, ty) - scale(sx, sy) diff --git a/eslint.config.js b/eslint.config.js new file mode 100644 index 0000000..6cd6dec --- /dev/null +++ b/eslint.config.js @@ -0,0 +1,15 @@ +// @ts-check + +import eslint from "@eslint/js"; +import eslintPluginUnicorn from "eslint-plugin-unicorn"; +import { defineConfig } from "eslint/config"; +import tseslint from "typescript-eslint"; + +export default defineConfig( + eslint.configs.recommended, + tseslint.configs.recommended, + eslintPluginUnicorn.configs.recommended, + [{ + rules: { "unicorn/no-null": "off", "unicorn/prevent-abbreviations": ["error", { "replacements": { "i": false } }] }, + }], +); diff --git a/package.json b/package.json index 2bc5dc7..01914dd 100644 --- a/package.json +++ b/package.json @@ -3,9 +3,22 @@ "version": "1.0.0", "private": true, "type": "module", - "workspaces": ["packages/*"], + "workspaces": [ + "packages/*" + ], "scripts": {}, - "devDependencies": { "dprint": "^0.50.2" }, - "engines": { "node": ">=18.0.0", "pnpm": ">=8.0.0" }, + "devDependencies": { + "@eslint/js": "^9.39.2", + "dprint": "^0.50.2", + "eslint": "^9.39.2", + "eslint-plugin-unicorn": "^62.0.0", + "globals": "^16.5.0", + "typescript": "^5.9.3", + "typescript-eslint": "^8.50.0" + }, + "engines": { + "node": ">=18.0.0", + "pnpm": ">=8.0.0" + }, "packageManager": "pnpm@10.26.1" } diff --git a/packages/core/package.json b/packages/core/package.json index 6612461..8ecd345 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,28 +1,18 @@ { - "name": "tsdown-starter", + "name": "inkfinite-core", "type": "module", "version": "0.0.0", "description": "A starter for creating a TypeScript package.", "author": "Author Name ", "license": "MIT", "homepage": "https://github.com/author/library#readme", - "repository": { - "type": "git", - "url": "git+https://github.com/author/library.git" - }, - "bugs": { - "url": "https://github.com/author/library/issues" - }, - "exports": { - ".": "./dist/index.mjs", - "./package.json": "./package.json" - }, + "repository": { "type": "git", "url": "git+https://github.com/author/library.git" }, + "bugs": { "url": "https://github.com/author/library/issues" }, + "exports": { ".": "./dist/index.mjs", "./package.json": "./package.json" }, "main": "./dist/index.mjs", "module": "./dist/index.mjs", "types": "./dist/index.d.mts", - "files": [ - "dist" - ], + "files": ["dist"], "scripts": { "build": "tsdown", "dev": "tsdown --watch", diff --git a/packages/core/src/math.ts b/packages/core/src/math.ts new file mode 100644 index 0000000..0c1e754 --- /dev/null +++ b/packages/core/src/math.ts @@ -0,0 +1,360 @@ +export type Vec2 = { x: number; y: number }; + +export const Vec2 = { + /** + * Add two vectors + */ + add(a: Vec2, b: Vec2): Vec2 { + return { x: a.x + b.x, y: a.y + b.y }; + }, + + /** + * Subtract vector b from vector a + */ + sub(a: Vec2, b: Vec2): Vec2 { + return { x: a.x - b.x, y: a.y - b.y }; + }, + + /** + * Multiply vector by scalar + */ + mulScalar(v: Vec2, s: number): Vec2 { + return { x: v.x * s, y: v.y * s }; + }, + + /** + * Calculate length (magnitude) of vector + */ + len(v: Vec2): number { + return Math.hypot(v.x, v.y); + }, + + /** + * Calculate squared length (faster, no sqrt) + */ + lenSq(v: Vec2): number { + return v.x * v.x + v.y * v.y; + }, + + /** + * Normalize vector to unit length + * Returns zero vector if input length is zero + */ + normalize(v: Vec2): Vec2 { + const length = Vec2.len(v); + if (length === 0) { + return { x: 0, y: 0 }; + } + return { x: v.x / length, y: v.y / length }; + }, + + /** + * Calculate dot product of two vectors + */ + dot(a: Vec2, b: Vec2): number { + return a.x * b.x + a.y * b.y; + }, + + /** + * Calculate distance between two points + */ + dist(a: Vec2, b: Vec2): number { + return Vec2.len(Vec2.sub(a, b)); + }, + + /** + * Calculate squared distance (faster, no sqrt) + */ + distSq(a: Vec2, b: Vec2): number { + return Vec2.lenSq(Vec2.sub(a, b)); + }, + + /** + * Check if two vectors are approximately equal + */ + equals(a: Vec2, b: Vec2, epsilon = 1e-10): boolean { + return Math.abs(a.x - b.x) <= epsilon && Math.abs(a.y - b.y) <= epsilon; + }, + + /** + * Create a new vector + */ + create(x: number, y: number): Vec2 { + return { x, y }; + }, + + /** + * Clone a vector + */ + clone(v: Vec2): Vec2 { + return { x: v.x, y: v.y }; + }, +}; + +export type Box2 = { min: Vec2; max: Vec2 }; + +export const Box2 = { + /** + * Create a bounding box from an array of points + */ + fromPoints(points: Vec2[]): Box2 { + if (points.length === 0) { + return { min: { x: 0, y: 0 }, max: { x: 0, y: 0 } }; + } + + let minX = points[0].x; + let minY = points[0].y; + let maxX = points[0].x; + let maxY = points[0].y; + + for (let index = 1; index < points.length; index++) { + const p = points[index]; + if (p.x < minX) minX = p.x; + if (p.y < minY) minY = p.y; + if (p.x > maxX) maxX = p.x; + if (p.y > maxY) maxY = p.y; + } + + return { min: { x: minX, y: minY }, max: { x: maxX, y: maxY } }; + }, + + /** + * Create a box from center and size + */ + fromCenterSize(center: Vec2, width: number, height: number): Box2 { + const halfW = width / 2; + const halfH = height / 2; + return { min: { x: center.x - halfW, y: center.y - halfH }, max: { x: center.x + halfW, y: center.y + halfH } }; + }, + + /** + * Create a box from min/max coordinates + */ + create(minX: number, minY: number, maxX: number, maxY: number): Box2 { + return { min: { x: minX, y: minY }, max: { x: maxX, y: maxY } }; + }, + + /** + * Check if a point is inside the box + */ + containsPoint(box: Box2, point: Vec2): boolean { + return (point.x >= box.min.x && point.x <= box.max.x && point.y >= box.min.y && point.y <= box.max.y); + }, + + /** + * Check if two boxes intersect + */ + intersectsBox(a: Box2, b: Box2): boolean { + return !(a.max.x < b.min.x || a.min.x > b.max.x || a.max.y < b.min.y || a.min.y > b.max.y); + }, + + /** + * Check if box a completely contains box b + */ + containsBox(a: Box2, b: Box2): boolean { + return (b.min.x >= a.min.x && b.max.x <= a.max.x && b.min.y >= a.min.y && b.max.y <= a.max.y); + }, + + /** + * Get the width of the box + */ + width(box: Box2): number { + return box.max.x - box.min.x; + }, + + /** + * Get the height of the box + */ + height(box: Box2): number { + return box.max.y - box.min.y; + }, + + /** + * Get the center point of the box + */ + center(box: Box2): Vec2 { + return { x: (box.min.x + box.max.x) / 2, y: (box.min.y + box.max.y) / 2 }; + }, + + /** + * Get the area of the box + */ + area(box: Box2): number { + return Box2.width(box) * Box2.height(box); + }, + + /** + * Expand box to include a point + */ + expandToPoint(box: Box2, point: Vec2): Box2 { + return { + min: { x: Math.min(box.min.x, point.x), y: Math.min(box.min.y, point.y) }, + max: { x: Math.max(box.max.x, point.x), y: Math.max(box.max.y, point.y) }, + }; + }, + + /** + * Clone a box + */ + clone(box: Box2): Box2 { + return { min: { ...box.min }, max: { ...box.max } }; + }, +}; + +/** + * 3x3 matrix stored in column-major order for 2D affine transforms + * Layout: + * [a c tx] + * [b d ty] + * [0 0 1] + * + * Stored as: [a, b, 0, c, d, 0, tx, ty, 1] + */ +export type Mat3 = [number, number, number, number, number, number, number, number, number]; + +export const Mat3 = { + /** + * Create an identity matrix + */ + identity(): Mat3 { + return [1, 0, 0, 0, 1, 0, 0, 0, 1]; + }, + + /** + * Create a translation matrix + */ + translate(tx: number, ty: number): Mat3 { + return [1, 0, 0, 0, 1, 0, tx, ty, 1]; + }, + + /** + * Create a scale matrix + */ + scale(sx: number, sy: number): Mat3 { + return [sx, 0, 0, 0, sy, 0, 0, 0, 1]; + }, + + /** + * Create a rotation matrix + * @param theta - angle in radians + */ + rotate(theta: number): Mat3 { + const c = Math.cos(theta); + const s = Math.sin(theta); + return [c, s, 0, -s, c, 0, 0, 0, 1]; + }, + + /** + * Multiply two matrices: result = a * b + * Order matters: transformations are applied right to left + */ + multiply(a: Mat3, b: Mat3): Mat3 { + const a00 = a[0], a01 = a[1], a02 = a[2]; + const a10 = a[3], a11 = a[4], a12 = a[5]; + const a20 = a[6], a21 = a[7], a22 = a[8]; + + const b00 = b[0], b01 = b[1], b02 = b[2]; + const b10 = b[3], b11 = b[4], b12 = b[5]; + const b20 = b[6], b21 = b[7], b22 = b[8]; + + return [ + a00 * b00 + a10 * b01 + a20 * b02, + a01 * b00 + a11 * b01 + a21 * b02, + a02 * b00 + a12 * b01 + a22 * b02, + + a00 * b10 + a10 * b11 + a20 * b12, + a01 * b10 + a11 * b11 + a21 * b12, + a02 * b10 + a12 * b11 + a22 * b12, + + a00 * b20 + a10 * b21 + a20 * b22, + a01 * b20 + a11 * b21 + a21 * b22, + a02 * b20 + a12 * b21 + a22 * b22, + ]; + }, + + /** + * Transform a point by a matrix + */ + transformPoint(m: Mat3, p: Vec2): Vec2 { + const x = m[0] * p.x + m[3] * p.y + m[6]; + const y = m[1] * p.x + m[4] * p.y + m[7]; + return { x, y }; + }, + + /** + * Invert a matrix + * Returns null if matrix is not invertible + */ + invert(m: Mat3): Mat3 | null { + const a00 = m[0], a01 = m[1], a02 = m[2]; + const a10 = m[3], a11 = m[4], a12 = m[5]; + const a20 = m[6], a21 = m[7], a22 = m[8]; + + const b01 = a22 * a11 - a12 * a21; + const b11 = -a22 * a10 + a12 * a20; + const b21 = a21 * a10 - a11 * a20; + + const det = a00 * b01 + a01 * b11 + a02 * b21; + + if (Math.abs(det) < 1e-10) { + return null; + } + + const invDet = 1 / det; + + return [ + b01 * invDet, + (-a22 * a01 + a02 * a21) * invDet, + (a12 * a01 - a02 * a11) * invDet, + + b11 * invDet, + (a22 * a00 - a02 * a20) * invDet, + (-a12 * a00 + a02 * a10) * invDet, + + b21 * invDet, + (-a21 * a00 + a01 * a20) * invDet, + (a11 * a00 - a01 * a10) * invDet, + ]; + }, + + /** + * Get the determinant of a matrix + */ + determinant(m: Mat3): number { + const a00 = m[0], a01 = m[1], a02 = m[2]; + const a10 = m[3], a11 = m[4], a12 = m[5]; + const a20 = m[6], a21 = m[7], a22 = m[8]; + + return (a00 * (a22 * a11 - a12 * a21) + a01 * (-a22 * a10 + a12 * a20) + a02 * (a21 * a10 - a11 * a20)); + }, + + /** + * Clone a matrix + */ + clone(m: Mat3): Mat3 { + return [...m] as Mat3; + }, + + /** + * Check if two matrices are approximately equal + */ + equals(a: Mat3, b: Mat3, epsilon = 1e-10): boolean { + for (let index = 0; index < 9; index++) { + if (Math.abs(a[index] - b[index]) >= epsilon) { + return false; + } + } + return true; + }, + + /** + * Create a combined transform matrix + * Applies in order: translate -> rotate -> scale + */ + fromTransform(tx: number, ty: number, rotation: number, sx: number, sy: number): Mat3 { + const c = Math.cos(rotation); + const s = Math.sin(rotation); + + return [c * sx, s * sx, 0, -s * sy, c * sy, 0, tx, ty, 1]; + }, +}; diff --git a/packages/core/tests/math.test.ts b/packages/core/tests/math.test.ts new file mode 100644 index 0000000..5b6cb9c --- /dev/null +++ b/packages/core/tests/math.test.ts @@ -0,0 +1,770 @@ +import { describe, expect, it } from "vitest"; +import { Box2, Mat3, Vec2 } from "../src/math"; + +describe("Vec2", () => { + describe("add", () => { + it.each([{ + description: "should add two positive vectors", + a: { x: 1, y: 2 }, + b: { x: 3, y: 4 }, + expected: { x: 4, y: 6 }, + }, { + description: "should handle negative values", + a: { x: -1, y: -2 }, + b: { x: 3, y: 4 }, + expected: { x: 2, y: 2 }, + }, { + description: "should handle zero vectors", + a: { x: 0, y: 0 }, + b: { x: 5, y: 10 }, + expected: { x: 5, y: 10 }, + }])("$description", ({ a, b, expected }) => { + expect(Vec2.add(a, b)).toEqual(expected); + }); + + it("should be commutative", () => { + const a = { x: 1, y: 2 }; + const b = { x: 3, y: 4 }; + expect(Vec2.add(a, b)).toEqual(Vec2.add(b, a)); + }); + }); + + describe("sub", () => { + it.each([{ + description: "should subtract two vectors", + a: { x: 5, y: 7 }, + b: { x: 2, y: 3 }, + expected: { x: 3, y: 4 }, + }, { + description: "should handle negative results", + a: { x: 1, y: 2 }, + b: { x: 3, y: 4 }, + expected: { x: -2, y: -2 }, + }, { + description: "should handle zero vectors", + a: { x: 5, y: 10 }, + b: { x: 0, y: 0 }, + expected: { x: 5, y: 10 }, + }])("$description", ({ a, b, expected }) => { + expect(Vec2.sub(a, b)).toEqual(expected); + }); + + it("should return zero vector when subtracting identical vectors", () => { + const a = { x: 5, y: 10 }; + expect(Vec2.sub(a, a)).toEqual({ x: 0, y: 0 }); + }); + }); + + describe("mulScalar", () => { + it.each([ + { description: "should multiply by positive scalar", v: { x: 2, y: 3 }, scalar: 4, expected: { x: 8, y: 12 } }, + { description: "should multiply by negative scalar", v: { x: 2, y: 3 }, scalar: -2, expected: { x: -4, y: -6 } }, + { description: "should multiply by zero", v: { x: 5, y: 10 }, scalar: 0, expected: { x: 0, y: 0 } }, + { description: "should multiply by one (identity)", v: { x: 5, y: 10 }, scalar: 1, expected: { x: 5, y: 10 } }, + { description: "should handle fractional scalars", v: { x: 10, y: 20 }, scalar: 0.5, expected: { x: 5, y: 10 } }, + ])("$description", ({ v, scalar, expected }) => { + expect(Vec2.mulScalar(v, scalar)).toEqual(expected); + }); + }); + + describe("len", () => { + it.each([ + { description: "should calculate length of 3-4-5 triangle", v: { x: 3, y: 4 }, expected: 5 }, + { description: "should return zero for zero vector", v: { x: 0, y: 0 }, expected: 0 }, + { description: "should handle negative components", v: { x: -3, y: -4 }, expected: 5 }, + { description: "should calculate length for unit vector X", v: { x: 1, y: 0 }, expected: 1 }, + { description: "should calculate length for unit vector Y", v: { x: 0, y: 1 }, expected: 1 }, + ])("$description", ({ v, expected }) => { + expect(Vec2.len(v)).toBe(expected); + }); + + it("should handle very small vectors", () => { + const v = { x: 1e-10, y: 1e-10 }; + expect(Vec2.len(v)).toBeCloseTo(Math.sqrt(2) * 1e-10, 20); + }); + }); + + describe("lenSq", () => { + it.each([{ description: "should calculate squared length", v: { x: 3, y: 4 }, expected: 25 }, { + description: "should return zero for zero vector", + v: { x: 0, y: 0 }, + expected: 0, + }])("$description", ({ v, expected }) => { + expect(Vec2.lenSq(v)).toBe(expected); + }); + + it("should match len squared", () => { + const v = { x: 3, y: 4 }; + expect(Vec2.lenSq(v)).toBe(Vec2.len(v) ** 2); + }); + }); + + describe("normalize", () => { + it("should normalize vector to unit length", () => { + const v = { x: 3, y: 4 }; + const result = Vec2.normalize(v); + expect(result.x).toBeCloseTo(0.6); + expect(result.y).toBeCloseTo(0.8); + expect(Vec2.len(result)).toBeCloseTo(1); + }); + + it("should handle zero vector (return zero)", () => { + const v = { x: 0, y: 0 }; + expect(Vec2.normalize(v)).toEqual({ x: 0, y: 0 }); + }); + + it("should handle already normalized vectors", () => { + const v = { x: 1, y: 0 }; + const result = Vec2.normalize(v); + expect(result.x).toBeCloseTo(1); + expect(result.y).toBeCloseTo(0); + }); + + it("should handle negative components", () => { + const v = { x: -3, y: -4 }; + const result = Vec2.normalize(v); + expect(result.x).toBeCloseTo(-0.6); + expect(result.y).toBeCloseTo(-0.8); + expect(Vec2.len(result)).toBeCloseTo(1); + }); + + it("should handle very small vectors", () => { + const v = { x: 1e-100, y: 1e-100 }; + const result = Vec2.normalize(v); + const expectedX = 1 / Math.sqrt(2); + expect(result.x).toBeCloseTo(expectedX, 5); + expect(result.y).toBeCloseTo(expectedX, 5); + }); + }); + + describe("dot", () => { + it.each([ + { description: "should calculate dot product", a: { x: 2, y: 3 }, b: { x: 4, y: 5 }, expected: 23 }, + { + description: "should return zero for perpendicular vectors", + a: { x: 1, y: 0 }, + b: { x: 0, y: 1 }, + expected: 0, + }, + { description: "should handle negative values", a: { x: 2, y: 3 }, b: { x: -4, y: -5 }, expected: -23 }, + { description: "should return zero with zero vector", a: { x: 5, y: 10 }, b: { x: 0, y: 0 }, expected: 0 }, + { + description: "should calculate dot product of parallel vectors", + a: { x: 2, y: 3 }, + b: { x: 4, y: 6 }, + expected: 26, + }, + ])("$description", ({ a, b, expected }) => { + expect(Vec2.dot(a, b)).toBe(expected); + }); + + it("should be commutative", () => { + const a = { x: 2, y: 3 }; + const b = { x: 4, y: 5 }; + expect(Vec2.dot(a, b)).toBe(Vec2.dot(b, a)); + }); + }); + + describe("dist and distSq", () => { + it.each([{ + description: "should calculate distance between two points", + a: { x: 0, y: 0 }, + b: { x: 3, y: 4 }, + expectedDist: 5, + expectedDistSq: 25, + }, { + description: "should return zero for identical points", + a: { x: 5, y: 10 }, + b: { x: 5, y: 10 }, + expectedDist: 0, + expectedDistSq: 0, + }, { + description: "should handle negative coordinates", + a: { x: -3, y: -4 }, + b: { x: 0, y: 0 }, + expectedDist: 5, + expectedDistSq: 25, + }])("$description", ({ a, b, expectedDist, expectedDistSq }) => { + expect(Vec2.dist(a, b)).toBe(expectedDist); + expect(Vec2.distSq(a, b)).toBe(expectedDistSq); + }); + + it("should be symmetric", () => { + const a = { x: 1, y: 2 }; + const b = { x: 4, y: 6 }; + expect(Vec2.dist(a, b)).toBe(Vec2.dist(b, a)); + }); + + it("distSq should match dist squared", () => { + const a = { x: 1, y: 2 }; + const b = { x: 4, y: 6 }; + expect(Vec2.distSq(a, b)).toBe(Vec2.dist(a, b) ** 2); + }); + }); + + describe("equals", () => { + it.each([{ + description: "should return true for identical vectors", + a: { x: 1.5, y: 2.5 }, + b: { x: 1.5, y: 2.5 }, + expected: true, + }, { + description: "should return false for different vectors", + a: { x: 1, y: 2 }, + b: { x: 3, y: 4 }, + expected: false, + }])("$description", ({ a, b, expected }) => { + expect(Vec2.equals(a, b)).toBe(expected); + }); + + it("should use epsilon for floating point comparison", () => { + const a = { x: 1 + 5e-11, y: 2 + 5e-11 }; + const b = { x: 1, y: 2 }; + expect(Vec2.equals(a, b)).toBe(true); + }); + + it("should allow custom epsilon", () => { + const a = { x: 1.001, y: 2.001 }; + const b = { x: 1, y: 2 }; + expect(Vec2.equals(a, b, 0.01)).toBe(true); + expect(Vec2.equals(a, b, 0.0001)).toBe(false); + }); + }); + + describe("create and clone", () => { + it("should create a vector", () => { + expect(Vec2.create(3, 4)).toEqual({ x: 3, y: 4 }); + }); + + it("should clone a vector", () => { + const original = { x: 5, y: 10 }; + const cloned = Vec2.clone(original); + expect(cloned).toEqual(original); + expect(cloned).not.toBe(original); + }); + }); +}); + +describe("Box2", () => { + describe("fromPoints", () => { + it.each([ + { + description: "should create box from multiple points", + points: [{ x: 1, y: 2 }, { x: 5, y: 8 }, { x: 3, y: 4 }], + expected: { min: { x: 1, y: 2 }, max: { x: 5, y: 8 } }, + }, + { + description: "should handle single point", + points: [{ x: 3, y: 4 }], + expected: { min: { x: 3, y: 4 }, max: { x: 3, y: 4 } }, + }, + { description: "should handle empty array", points: [], expected: { min: { x: 0, y: 0 }, max: { x: 0, y: 0 } } }, + { + description: "should handle negative coordinates", + points: [{ x: -5, y: -3 }, { x: 2, y: 4 }, { x: -1, y: 0 }], + expected: { min: { x: -5, y: -3 }, max: { x: 2, y: 4 } }, + }, + ])("$description", ({ points, expected }) => { + expect(Box2.fromPoints(points)).toEqual(expected); + }); + + it("should handle points in any order", () => { + const points1 = [{ x: 0, y: 0 }, { x: 10, y: 10 }]; + const points2 = [{ x: 10, y: 10 }, { x: 0, y: 0 }]; + expect(Box2.fromPoints(points1)).toEqual(Box2.fromPoints(points2)); + }); + }); + + describe("fromCenterSize", () => { + it("should create box from center and size", () => { + const center = { x: 5, y: 5 }; + const box = Box2.fromCenterSize(center, 10, 6); + expect(box).toEqual({ min: { x: 0, y: 2 }, max: { x: 10, y: 8 } }); + }); + + it("should handle zero size", () => { + const center = { x: 5, y: 5 }; + const box = Box2.fromCenterSize(center, 0, 0); + expect(box).toEqual({ min: { x: 5, y: 5 }, max: { x: 5, y: 5 } }); + }); + + it("should handle odd dimensions", () => { + const center = { x: 0, y: 0 }; + const box = Box2.fromCenterSize(center, 5, 3); + expect(box.min.x).toBeCloseTo(-2.5); + expect(box.min.y).toBeCloseTo(-1.5); + expect(box.max.x).toBeCloseTo(2.5); + expect(box.max.y).toBeCloseTo(1.5); + }); + }); + + describe("create", () => { + it("should create box from coordinates", () => { + expect(Box2.create(0, 0, 10, 10)).toEqual({ min: { x: 0, y: 0 }, max: { x: 10, y: 10 } }); + }); + }); + + describe("containsPoint", () => { + const box = Box2.create(0, 0, 10, 10); + + it.each([ + { description: "point inside box", point: { x: 5, y: 5 }, expected: true }, + { description: "point on left edge", point: { x: 0, y: 5 }, expected: true }, + { description: "point on right edge", point: { x: 10, y: 5 }, expected: true }, + { description: "point on top edge", point: { x: 5, y: 0 }, expected: true }, + { description: "point on bottom edge", point: { x: 5, y: 10 }, expected: true }, + { description: "top-left corner", point: { x: 0, y: 0 }, expected: true }, + { description: "bottom-right corner", point: { x: 10, y: 10 }, expected: true }, + { description: "point left of box", point: { x: -1, y: 5 }, expected: false }, + { description: "point right of box", point: { x: 11, y: 5 }, expected: false }, + { description: "point above box", point: { x: 5, y: -1 }, expected: false }, + { description: "point below box", point: { x: 5, y: 11 }, expected: false }, + ])("should handle $description", ({ point, expected }) => { + expect(Box2.containsPoint(box, point)).toBe(expected); + }); + + it("should handle negative coordinates", () => { + const negBox = Box2.create(-10, -10, 10, 10); + expect(Box2.containsPoint(negBox, { x: 0, y: 0 })).toBe(true); + expect(Box2.containsPoint(negBox, { x: -5, y: -5 })).toBe(true); + expect(Box2.containsPoint(negBox, { x: -11, y: 0 })).toBe(false); + }); + + it("should handle zero-size box", () => { + const pointBox = Box2.create(5, 5, 5, 5); + expect(Box2.containsPoint(pointBox, { x: 5, y: 5 })).toBe(true); + expect(Box2.containsPoint(pointBox, { x: 5.1, y: 5 })).toBe(false); + }); + }); + + describe("intersectsBox", () => { + it.each([{ + description: "overlapping boxes", + a: Box2.create(0, 0, 10, 10), + b: Box2.create(5, 5, 15, 15), + expected: true, + }, { + description: "boxes touching at edge", + a: Box2.create(0, 0, 10, 10), + b: Box2.create(10, 0, 20, 10), + expected: true, + }, { + description: "one box contains another", + a: Box2.create(0, 0, 10, 10), + b: Box2.create(2, 2, 8, 8), + expected: true, + }, { + description: "non-overlapping boxes", + a: Box2.create(0, 0, 10, 10), + b: Box2.create(11, 11, 20, 20), + expected: false, + }, { + description: "boxes separated horizontally", + a: Box2.create(0, 0, 10, 10), + b: Box2.create(11, 0, 20, 10), + expected: false, + }, { + description: "boxes separated vertically", + a: Box2.create(0, 0, 10, 10), + b: Box2.create(0, 11, 10, 20), + expected: false, + }])("should handle $description", ({ a, b, expected }) => { + expect(Box2.intersectsBox(a, b)).toBe(expected); + }); + + it("should handle negative coordinates", () => { + const a = Box2.create(-10, -10, 0, 0); + const b = Box2.create(-5, -5, 5, 5); + expect(Box2.intersectsBox(a, b)).toBe(true); + }); + + it("should be symmetric", () => { + const a = Box2.create(0, 0, 10, 10); + const b = Box2.create(5, 5, 15, 15); + expect(Box2.intersectsBox(a, b)).toBe(Box2.intersectsBox(b, a)); + }); + }); + + describe("containsBox", () => { + it.each([ + { description: "a contains b", a: Box2.create(0, 0, 10, 10), b: Box2.create(2, 2, 8, 8), expected: true }, + { + description: "b contains a (should be false)", + a: Box2.create(2, 2, 8, 8), + b: Box2.create(0, 0, 10, 10), + expected: false, + }, + { description: "identical boxes", a: Box2.create(0, 0, 10, 10), b: Box2.create(0, 0, 10, 10), expected: true }, + { + description: "overlapping but not contained", + a: Box2.create(0, 0, 10, 10), + b: Box2.create(5, 5, 15, 15), + expected: false, + }, + { + description: "boxes just touching", + a: Box2.create(0, 0, 10, 10), + b: Box2.create(10, 0, 20, 10), + expected: false, + }, + ])("should handle $description", ({ a, b, expected }) => { + expect(Box2.containsBox(a, b)).toBe(expected); + }); + }); + + describe("width, height, center, area", () => { + it.each([{ + description: "standard box", + box: Box2.create(0, 0, 10, 5), + width: 10, + height: 5, + center: { x: 5, y: 2.5 }, + area: 50, + }, { + description: "zero-width box", + box: Box2.create(5, 5, 5, 10), + width: 0, + height: 5, + center: { x: 5, y: 7.5 }, + area: 0, + }, { + description: "negative coordinate box", + box: Box2.create(-10, -5, 10, 5), + width: 20, + height: 10, + center: { x: 0, y: 0 }, + area: 200, + }, { + description: "zero-size box", + box: Box2.create(5, 5, 5, 5), + width: 0, + height: 0, + center: { x: 5, y: 5 }, + area: 0, + }])("should calculate properties for $description", ({ box, width, height, center, area }) => { + expect(Box2.width(box)).toBe(width); + expect(Box2.height(box)).toBe(height); + expect(Box2.center(box)).toEqual(center); + expect(Box2.area(box)).toBe(area); + }); + }); + + describe("expandToPoint", () => { + it.each([{ + description: "expand to point outside (right)", + box: Box2.create(0, 0, 10, 10), + point: { x: 15, y: 5 }, + expected: { min: { x: 0, y: 0 }, max: { x: 15, y: 10 } }, + }, { + description: "point inside (no change)", + box: Box2.create(0, 0, 10, 10), + point: { x: 5, y: 5 }, + expected: { min: { x: 0, y: 0 }, max: { x: 10, y: 10 } }, + }, { + description: "expand in multiple directions", + box: Box2.create(5, 5, 10, 10), + point: { x: 0, y: 15 }, + expected: { min: { x: 0, y: 5 }, max: { x: 10, y: 15 } }, + }, { + description: "expand to negative coordinates", + box: Box2.create(0, 0, 10, 10), + point: { x: -5, y: -5 }, + expected: { min: { x: -5, y: -5 }, max: { x: 10, y: 10 } }, + }])("should $description", ({ box, point, expected }) => { + expect(Box2.expandToPoint(box, point)).toEqual(expected); + }); + }); + + describe("clone", () => { + it("should create a copy of the box", () => { + const box = Box2.create(0, 0, 10, 10); + const cloned = Box2.clone(box); + expect(cloned).toEqual(box); + expect(cloned).not.toBe(box); + expect(cloned.min).not.toBe(box.min); + expect(cloned.max).not.toBe(box.max); + }); + }); +}); + +describe("Mat3", () => { + describe("identity", () => { + it("should create identity matrix", () => { + expect(Mat3.identity()).toEqual([1, 0, 0, 0, 1, 0, 0, 0, 1]); + }); + + it("should not transform points", () => { + const m = Mat3.identity(); + const p = { x: 5, y: 10 }; + expect(Mat3.transformPoint(m, p)).toEqual(p); + }); + }); + + describe("translate", () => { + it.each([{ description: "positive translation", tx: 5, ty: 10, point: { x: 0, y: 0 }, expected: { x: 5, y: 10 } }, { + description: "negative translation", + tx: -5, + ty: -10, + point: { x: 5, y: 10 }, + expected: { x: 0, y: 0 }, + }, { + description: "zero translation (identity)", + tx: 0, + ty: 0, + point: { x: 5, y: 10 }, + expected: { x: 5, y: 10 }, + }])("should handle $description", ({ tx, ty, point, expected }) => { + const m = Mat3.translate(tx, ty); + expect(Mat3.transformPoint(m, point)).toEqual(expected); + }); + + it("should create correct matrix structure", () => { + expect(Mat3.translate(5, 10)).toEqual([1, 0, 0, 0, 1, 0, 5, 10, 1]); + }); + }); + + describe("scale", () => { + it.each([ + { description: "non-uniform scale", sx: 2, sy: 3, point: { x: 5, y: 10 }, expected: { x: 10, y: 30 } }, + { description: "uniform scale", sx: 2, sy: 2, point: { x: 5, y: 10 }, expected: { x: 10, y: 20 } }, + { description: "scale by 1 (identity)", sx: 1, sy: 1, point: { x: 5, y: 10 }, expected: { x: 5, y: 10 } }, + { description: "scale by 0 (collapse)", sx: 0, sy: 0, point: { x: 5, y: 10 }, expected: { x: 0, y: 0 } }, + ])("should handle $description", ({ sx, sy, point, expected }) => { + const m = Mat3.scale(sx, sy); + expect(Mat3.transformPoint(m, point)).toEqual(expected); + }); + + it("should handle negative scale (flip)", () => { + const m = Mat3.scale(-1, 1); + const result = Mat3.transformPoint(m, { x: 5, y: 10 }); + expect(result).toEqual({ x: -5, y: 10 }); + }); + }); + + describe("rotate", () => { + it.each([ + { + description: "90 degrees counterclockwise", + angle: Math.PI / 2, + point: { x: 1, y: 0 }, + expected: { x: 0, y: 1 }, + }, + { description: "180 degrees", angle: Math.PI, point: { x: 1, y: 0 }, expected: { x: -1, y: 0 } }, + { description: "zero rotation", angle: 0, point: { x: 5, y: 10 }, expected: { x: 5, y: 10 } }, + { + description: "360 degrees (full rotation)", + angle: 2 * Math.PI, + point: { x: 5, y: 10 }, + expected: { x: 5, y: 10 }, + }, + { description: "-90 degrees (clockwise)", angle: -Math.PI / 2, point: { x: 1, y: 0 }, expected: { x: 0, y: -1 } }, + ])("should handle $description", ({ angle, point, expected }) => { + const m = Mat3.rotate(angle); + const result = Mat3.transformPoint(m, point); + expect(result.x).toBeCloseTo(expected.x, 10); + expect(result.y).toBeCloseTo(expected.y, 10); + }); + }); + + describe("multiply", () => { + it("should combine translations", () => { + const a = Mat3.translate(5, 0); + const b = Mat3.translate(0, 10); + const result = Mat3.multiply(a, b); + const transformed = Mat3.transformPoint(result, { x: 0, y: 0 }); + expect(transformed).toEqual({ x: 5, y: 10 }); + }); + + it("should apply transforms right to left", () => { + const translate = Mat3.translate(10, 0); + const scale = Mat3.scale(2, 1); + const result = Mat3.multiply(translate, scale); + const transformed = Mat3.transformPoint(result, { x: 5, y: 0 }); + expect(transformed.x).toBeCloseTo(20); + }); + + it("should handle identity multiplication", () => { + const m = Mat3.translate(5, 10); + const identity = Mat3.identity(); + expect(Mat3.equals(Mat3.multiply(m, identity), m)).toBe(true); + expect(Mat3.equals(Mat3.multiply(identity, m), m)).toBe(true); + }); + + it("should be associative", () => { + const a = Mat3.translate(1, 2); + const b = Mat3.scale(2, 3); + const c = Mat3.rotate(Math.PI / 4); + const result1 = Mat3.multiply(Mat3.multiply(a, b), c); + const result2 = Mat3.multiply(a, Mat3.multiply(b, c)); + expect(Mat3.equals(result1, result2)).toBe(true); + }); + + it("should not be commutative", () => { + const a = Mat3.translate(10, 0); + const b = Mat3.scale(2, 1); + const result1 = Mat3.multiply(a, b); + const result2 = Mat3.multiply(b, a); + expect(Mat3.equals(result1, result2)).toBe(false); + }); + }); + + describe("transformPoint", () => { + it("should transform through combined transforms", () => { + const m = Mat3.multiply(Mat3.translate(10, 20), Mat3.multiply(Mat3.rotate(Math.PI / 2), Mat3.scale(2, 2))); + const result = Mat3.transformPoint(m, { x: 1, y: 0 }); + expect(result.x).toBeCloseTo(10); + expect(result.y).toBeCloseTo(22); + }); + + it("should handle origin point", () => { + const m = Mat3.translate(5, 10); + expect(Mat3.transformPoint(m, { x: 0, y: 0 })).toEqual({ x: 5, y: 10 }); + }); + }); + + describe("invert", () => { + it.each([{ description: "translation", matrix: Mat3.translate(5, 10) }, { + description: "scale", + matrix: Mat3.scale(2, 3), + }, { description: "rotation", matrix: Mat3.rotate(Math.PI / 3) }])("should invert $description", ({ matrix }) => { + const inv = Mat3.invert(matrix); + expect(inv).not.toBeNull(); + + const p = { x: 10, y: 20 }; + const transformed = Mat3.transformPoint(matrix, p); + const restored = Mat3.transformPoint(inv!, transformed); + + expect(restored.x).toBeCloseTo(p.x); + expect(restored.y).toBeCloseTo(p.y); + }); + + it("should invert identity to identity", () => { + const m = Mat3.identity(); + const inv = Mat3.invert(m); + expect(inv).not.toBeNull(); + expect(Mat3.equals(inv!, m)).toBe(true); + }); + + it("should return null for non-invertible matrix", () => { + const m = Mat3.scale(0, 0); + expect(Mat3.invert(m)).toBeNull(); + }); + + it("should satisfy M * M^-1 = I", () => { + const m = Mat3.multiply(Mat3.translate(5, 10), Mat3.multiply(Mat3.rotate(0.5), Mat3.scale(2, 3))); + const inv = Mat3.invert(m); + expect(inv).not.toBeNull(); + + const identity = Mat3.multiply(m, inv!); + expect(Mat3.equals(identity, Mat3.identity(), 1e-10)).toBe(true); + }); + }); + + describe("determinant", () => { + it.each([ + { description: "identity", matrix: Mat3.identity(), expected: 1 }, + { description: "translation", matrix: Mat3.translate(5, 10), expected: 1 }, + { description: "scale 2x3", matrix: Mat3.scale(2, 3), expected: 6 }, + { description: "rotation", matrix: Mat3.rotate(Math.PI / 4), expected: 1 }, + { description: "singular matrix", matrix: Mat3.scale(0, 5), expected: 0 }, + { description: "reflection", matrix: Mat3.scale(-1, 1), expected: -1 }, + ])("should calculate determinant for $description", ({ matrix, expected }) => { + expect(Mat3.determinant(matrix)).toBeCloseTo(expected, 10); + }); + }); + + describe("clone and equals", () => { + it("should clone matrix", () => { + const m = Mat3.translate(5, 10); + const cloned = Mat3.clone(m); + expect(cloned).toEqual(m); + expect(cloned).not.toBe(m); + }); + + it.each([ + { description: "identical matrices", a: Mat3.translate(5, 10), b: Mat3.translate(5, 10), expected: true }, + { description: "different matrices", a: Mat3.translate(5, 10), b: Mat3.translate(10, 5), expected: false }, + ])("should compare $description", ({ a, b, expected }) => { + expect(Mat3.equals(a, b)).toBe(expected); + }); + + it("should use epsilon for floating point comparison", () => { + const a = Mat3.rotate(Math.PI / 4); + const b = Mat3.clone(a); + b[0] += 1e-15; + expect(Mat3.equals(a, b)).toBe(true); + }); + + it("should allow custom epsilon", () => { + const a = Mat3.identity(); + const b = Mat3.identity(); + b[0] = 1.001; + expect(Mat3.equals(a, b, 0.01)).toBe(true); + expect(Mat3.equals(a, b, 0.0001)).toBe(false); + }); + }); + + describe("fromTransform", () => { + it("should create combined transform matrix", () => { + const m = Mat3.fromTransform(10, 20, 0, 1, 1); + const expected = Mat3.translate(10, 20); + expect(Mat3.equals(m, expected)).toBe(true); + }); + + it("should handle all transforms", () => { + const tx = 10, ty = 20; + const rotation = Math.PI / 4; + const sx = 2, sy = 3; + + const m = Mat3.fromTransform(tx, ty, rotation, sx, sy); + const p = { x: 1, y: 0 }; + const result = Mat3.transformPoint(m, p); + + const scaled = { x: 2, y: 0 }; + const rotated = { x: scaled.x * Math.cos(rotation), y: scaled.x * Math.sin(rotation) }; + const translated = { x: rotated.x + tx, y: rotated.y + ty }; + + expect(result.x).toBeCloseTo(translated.x); + expect(result.y).toBeCloseTo(translated.y); + }); + + it("should match manual composition", () => { + const tx = 5, ty = 10; + const rotation = Math.PI / 6; + const sx = 2, sy = 3; + + const m1 = Mat3.fromTransform(tx, ty, rotation, sx, sy); + const m2 = Mat3.multiply(Mat3.translate(tx, ty), Mat3.multiply(Mat3.rotate(rotation), Mat3.scale(sx, sy))); + + expect(Mat3.equals(m1, m2, 1e-10)).toBe(true); + }); + }); + + describe("edge cases and numerical stability", () => { + it.each([{ + description: "very large values", + matrix: Mat3.translate(1e10, 1e10), + point: { x: 0, y: 0 }, + expected: { x: 1e10, y: 1e10 }, + }, { + description: "very small values", + matrix: Mat3.scale(1e-10, 1e-10), + point: { x: 1e10, y: 1e10 }, + expected: { x: 1, y: 1 }, + }])("should handle $description", ({ matrix, point, expected }) => { + const result = Mat3.transformPoint(matrix, point); + expect(result.x).toBeCloseTo(expected.x); + expect(result.y).toBeCloseTo(expected.y); + }); + + it("should handle accumulated rotations", () => { + let m = Mat3.identity(); + for (let i = 0; i < 100; i++) { + m = Mat3.multiply(m, Mat3.rotate((2 * Math.PI) / 100)); + } + const result = Mat3.transformPoint(m, { x: 1, y: 0 }); + expect(result.x).toBeCloseTo(1, 5); + expect(result.y).toBeCloseTo(0, 5); + }); + }); +}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index cb84a39..ff39b91 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -8,9 +8,27 @@ importers: .: devDependencies: + '@eslint/js': + specifier: ^9.39.2 + version: 9.39.2 dprint: specifier: ^0.50.2 version: 0.50.2 + eslint: + specifier: ^9.39.2 + version: 9.39.2(jiti@2.6.1) + eslint-plugin-unicorn: + specifier: ^62.0.0 + version: 62.0.0(eslint@9.39.2(jiti@2.6.1)) + globals: + specifier: ^16.5.0 + version: 16.5.0 + typescript: + specifier: ^5.9.3 + version: 5.9.3 + typescript-eslint: + specifier: ^8.50.0 + version: 8.50.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) packages/core: devDependencies: @@ -281,6 +299,60 @@ packages: cpu: [x64] os: [win32] + '@eslint-community/eslint-utils@4.9.0': + resolution: {integrity: sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + + '@eslint-community/regexpp@4.12.2': + resolution: {integrity: sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==} + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + + '@eslint/config-array@0.21.1': + resolution: {integrity: sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/config-helpers@0.4.2': + resolution: {integrity: sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/core@0.17.0': + resolution: {integrity: sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/eslintrc@3.3.3': + resolution: {integrity: sha512-Kr+LPIUVKz2qkx1HAMH8q1q6azbqBAsXJUxBl/ODDuVPX45Z9DfwB8tPjTi6nNZ8BuM3nbJxC5zCAg5elnBUTQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/js@9.39.2': + resolution: {integrity: sha512-q1mjIoW1VX4IvSocvM/vbTiveKC4k9eLrajNEuSsmjymSDEbpGddtpfOoN7YGAqBK3NG+uqo8ia4PDTt8buCYA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/object-schema@2.1.7': + resolution: {integrity: sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/plugin-kit@0.4.1': + resolution: {integrity: sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@humanfs/core@0.19.1': + resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} + engines: {node: '>=18.18.0'} + + '@humanfs/node@0.16.7': + resolution: {integrity: sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==} + engines: {node: '>=18.18.0'} + + '@humanwhocodes/module-importer@1.0.1': + resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} + engines: {node: '>=12.22'} + + '@humanwhocodes/retry@0.4.3': + resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} + engines: {node: '>=18.18'} + '@jridgewell/gen-mapping@0.3.13': resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} @@ -508,9 +580,71 @@ packages: '@types/estree@1.0.8': resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} + '@types/json-schema@7.0.15': + resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} + '@types/node@25.0.3': resolution: {integrity: sha512-W609buLVRVmeW693xKfzHeIV6nJGGz98uCPfeXI1ELMLXVeKYZ9m15fAMSaUPBHYLGFsVRcMmSCksQOrZV9BYA==} + '@typescript-eslint/eslint-plugin@8.50.0': + resolution: {integrity: sha512-O7QnmOXYKVtPrfYzMolrCTfkezCJS9+ljLdKW/+DCvRsc3UAz+sbH6Xcsv7p30+0OwUbeWfUDAQE0vpabZ3QLg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + '@typescript-eslint/parser': ^8.50.0 + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/parser@8.50.0': + resolution: {integrity: sha512-6/cmF2piao+f6wSxUsJLZjck7OQsYyRtcOZS02k7XINSNlz93v6emM8WutDQSXnroG2xwYlEVHJI+cPA7CPM3Q==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/project-service@8.50.0': + resolution: {integrity: sha512-Cg/nQcL1BcoTijEWyx4mkVC56r8dj44bFDvBdygifuS20f3OZCHmFbjF34DPSi07kwlFvqfv/xOLnJ5DquxSGQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/scope-manager@8.50.0': + resolution: {integrity: sha512-xCwfuCZjhIqy7+HKxBLrDVT5q/iq7XBVBXLn57RTIIpelLtEIZHXAF/Upa3+gaCpeV1NNS5Z9A+ID6jn50VD4A==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/tsconfig-utils@8.50.0': + resolution: {integrity: sha512-vxd3G/ybKTSlm31MOA96gqvrRGv9RJ7LGtZCn2Vrc5htA0zCDvcMqUkifcjrWNNKXHUU3WCkYOzzVSFBd0wa2w==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/type-utils@8.50.0': + resolution: {integrity: sha512-7OciHT2lKCewR0mFoBrvZJ4AXTMe/sYOe87289WAViOocEmDjjv8MvIOT2XESuKj9jp8u3SZYUSh89QA4S1kQw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/types@8.50.0': + resolution: {integrity: sha512-iX1mgmGrXdANhhITbpp2QQM2fGehBse9LbTf0sidWK6yg/NE+uhV5dfU1g6EYPlcReYmkE9QLPq/2irKAmtS9w==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/typescript-estree@8.50.0': + resolution: {integrity: sha512-W7SVAGBR/IX7zm1t70Yujpbk+zdPq/u4soeFSknWFdXIFuWsBGBOUu/Tn/I6KHSKvSh91OiMuaSnYp3mtPt5IQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/utils@8.50.0': + resolution: {integrity: sha512-87KgUXET09CRjGCi2Ejxy3PULXna63/bMYv72tCAlDJC3Yqwln0HiFJ3VJMst2+mEtNtZu5oFvX4qJGjKsnAgg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/visitor-keys@8.50.0': + resolution: {integrity: sha512-Xzmnb58+Db78gT/CCj/PVCvK+zxbnsw6F+O1oheYszJbBSdEjVhQi3C/Xttzxgi/GLmpvOggRs1RFpiJ8+c34Q==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@vitest/expect@4.0.16': resolution: {integrity: sha512-eshqULT2It7McaJkQGLkPjPjNph+uevROGuIMJdG3V+0BSR2w9u6J9Lwu+E8cK5TETlfou8GRijhafIMhXsimA==} @@ -540,10 +674,30 @@ packages: '@vitest/utils@4.0.16': resolution: {integrity: sha512-h8z9yYhV3e1LEfaQ3zdypIrnAg/9hguReGZoS7Gl0aBG5xgA410zBqECqmaF/+RkTggRsfnzc1XaAHA6bmUufA==} + acorn-jsx@5.3.2: + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + + acorn@8.15.0: + resolution: {integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==} + engines: {node: '>=0.4.0'} + hasBin: true + + ajv@6.12.6: + resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} + + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + ansis@4.2.0: resolution: {integrity: sha512-HqZ5rWlFjGiV0tDm3UxxgNRqsOTniqoKZu0pIAfh7TZQMGuZK+hH0drySty0si0QXj1ieop4+SkSfPZBPPkHig==} engines: {node: '>=14'} + argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + args-tokenizer@0.3.0: resolution: {integrity: sha512-xXAd7G2Mll5W8uo37GETpQ2VrE84M181Z7ugHFGQnJZ50M2mbOv0osSZ9VsSgPfJQ+LVG0prSi0th+ELMsno7Q==} @@ -555,9 +709,31 @@ packages: resolution: {integrity: sha512-m1Q/RaVOnTp9JxPX+F+Zn7IcLYMzM8kZofDImfsKZd8MbR+ikdOzTeztStWqfrqIxZnYWryyI9ePm3NGjnZgGw==} engines: {node: '>=20.19.0'} + balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + + baseline-browser-mapping@2.9.11: + resolution: {integrity: sha512-Sg0xJUNDU1sJNGdfGWhVHX0kkZ+HWcvmVymJbj6NSgZZmW/8S9Y2HQ5euytnIgakgxN6papOAWiwDo1ctFDcoQ==} + hasBin: true + birpc@4.0.0: resolution: {integrity: sha512-LShSxJP0KTmd101b6DRyGBj57LZxSDYWKitQNW/mi8GRMvZb078Uf9+pveax1DrVL89vm7mWe+TovdI/UDOuPw==} + brace-expansion@1.1.12: + resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==} + + brace-expansion@2.0.2: + resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==} + + browserslist@4.28.1: + resolution: {integrity: sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + + builtin-modules@5.0.0: + resolution: {integrity: sha512-bkXY9WsVpY7CvMhKSR6pZilZu9Ln5WDrKVBUXf2S443etkmEO4V58heTecXcUIsNsi4Rx8JUO4NfX1IcQl4deg==} + engines: {node: '>=18.20'} + bumpp@10.3.2: resolution: {integrity: sha512-yUUkVx5zpTywLNX97MlrqtpanI7eMMwFwLntWR2EBVDw3/Pm3aRIzCoDEGHATLIiHK9PuJC7xWI4XNWqXItSPg==} engines: {node: '>=18'} @@ -575,17 +751,49 @@ packages: resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} engines: {node: '>=8'} + callsites@3.1.0: + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} + + caniuse-lite@1.0.30001761: + resolution: {integrity: sha512-JF9ptu1vP2coz98+5051jZ4PwQgd2ni8A+gYSN7EA7dPKIMf0pDlSUxhdmVOaV3/fYK5uWBkgSXJaRLr4+3A6g==} + chai@6.2.1: resolution: {integrity: sha512-p4Z49OGG5W/WBCPSS/dH3jQ73kD6tiMmUM+bckNK6Jr5JHMG3k9bg/BvKR8lKmtVBKmOiuVaV2ws8s9oSbwysg==} engines: {node: '>=18'} + chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + + change-case@5.4.4: + resolution: {integrity: sha512-HRQyTk2/YPEkt9TnUPbOpr64Uw3KOicFWPVBb+xiHvd6eBx/qPr9xqfBFDT8P2vWsvvz4jbEkfDe71W3VyNu2w==} + chokidar@5.0.0: resolution: {integrity: sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==} engines: {node: '>= 20.19.0'} + ci-info@4.3.1: + resolution: {integrity: sha512-Wdy2Igu8OcBpI2pZePZ5oWjPC38tmDVx5WKUXKwlLYkA0ozo85sLsLvkBbBn/sZaSCMFOGZJ14fvW9t5/d7kdA==} + engines: {node: '>=8'} + citty@0.1.6: resolution: {integrity: sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ==} + clean-regexp@1.0.0: + resolution: {integrity: sha512-GfisEZEJvzKrmGWkvfhgzcz/BllN1USeqD2V6tg14OAOgaCD2Z/PUEuxnAZ/nPvmaHRG7a8y77p1T/IRQ4D1Hw==} + engines: {node: '>=4'} + + color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + + color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + + concat-map@0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + confbox@0.2.2: resolution: {integrity: sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ==} @@ -593,6 +801,25 @@ packages: resolution: {integrity: sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==} engines: {node: ^14.18.0 || >=16.10.0} + core-js-compat@3.47.0: + resolution: {integrity: sha512-IGfuznZ/n7Kp9+nypamBhvwdwLsW6KC8IOaURw2doAK5e98AG3acVLdh0woOnEqCfUtS+Vu882JE4k/DAm3ItQ==} + + cross-spawn@7.0.6: + resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} + engines: {node: '>= 8'} + + debug@4.4.3: + resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + deep-is@0.1.4: + resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + defu@6.1.4: resolution: {integrity: sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==} @@ -616,6 +843,9 @@ packages: oxc-resolver: optional: true + electron-to-chromium@1.5.267: + resolution: {integrity: sha512-0Drusm6MVRXSOJpGbaSVgcQsuB4hEkMpHXaVstcPmhu5LIedxs1xNK/nIxmQIU/RPC0+1/o0AVZfBTkTNJOdUw==} + empathic@2.0.0: resolution: {integrity: sha512-i6UzDscO/XfAcNYD75CfICkmfLedpyPDdozrLMmQc5ORaQcdMoc21OnlEylMIqI7U8eniKrPMxxtj8k0vhmJhA==} engines: {node: '>=14'} @@ -632,9 +862,65 @@ packages: resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} engines: {node: '>=6'} + escape-string-regexp@1.0.5: + resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} + engines: {node: '>=0.8.0'} + + escape-string-regexp@4.0.0: + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} + + eslint-plugin-unicorn@62.0.0: + resolution: {integrity: sha512-HIlIkGLkvf29YEiS/ImuDZQbP12gWyx5i3C6XrRxMvVdqMroCI9qoVYCoIl17ChN+U89pn9sVwLxhIWj5nEc7g==} + engines: {node: ^20.10.0 || >=21.0.0} + peerDependencies: + eslint: '>=9.38.0' + + eslint-scope@8.4.0: + resolution: {integrity: sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + eslint-visitor-keys@3.4.3: + resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + eslint-visitor-keys@4.2.1: + resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + eslint@9.39.2: + resolution: {integrity: sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + hasBin: true + peerDependencies: + jiti: '*' + peerDependenciesMeta: + jiti: + optional: true + + espree@10.4.0: + resolution: {integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + esquery@1.6.0: + resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==} + engines: {node: '>=0.10'} + + esrecurse@4.3.0: + resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} + engines: {node: '>=4.0'} + + estraverse@5.3.0: + resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} + engines: {node: '>=4.0'} + estree-walker@3.0.3: resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} + esutils@2.0.3: + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} + engines: {node: '>=0.10.0'} + expect-type@1.3.0: resolution: {integrity: sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==} engines: {node: '>=12.0.0'} @@ -642,6 +928,15 @@ packages: exsolve@1.0.8: resolution: {integrity: sha512-LmDxfWXwcTArk8fUEnOfSZpHOJ6zOMUJKOtFLFqJLoKJetuQG874Uc7/Kki7zFLzYybmZhp1M7+98pfMqeX8yA==} + fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + + fast-json-stable-stringify@2.1.0: + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + + fast-levenshtein@2.0.6: + resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + fdir@6.5.0: resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} engines: {node: '>=12.0.0'} @@ -651,6 +946,25 @@ packages: picomatch: optional: true + file-entry-cache@8.0.0: + resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} + engines: {node: '>=16.0.0'} + + find-up-simple@1.0.1: + resolution: {integrity: sha512-afd4O7zpqHeRyg4PfDQsXmlDe2PfdHtJt6Akt8jOWaApLOZk5JXs6VMR29lz03pRe9mpykrRCYIYxaJYcfpncQ==} + engines: {node: '>=18'} + + find-up@5.0.0: + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} + + flat-cache@4.0.1: + resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} + engines: {node: '>=16'} + + flatted@3.3.3: + resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==} + fsevents@2.3.3: resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} @@ -663,36 +977,130 @@ packages: resolution: {integrity: sha512-L5bGsVkxJbJgdnwyuheIunkGatUF/zssUoxxjACCseZYAVbaqdh9Tsmmlkl8vYan09H7sbvKt4pS8GqKLBrEzA==} hasBin: true + glob-parent@6.0.2: + resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} + engines: {node: '>=10.13.0'} + + globals@14.0.0: + resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} + engines: {node: '>=18'} + + globals@16.5.0: + resolution: {integrity: sha512-c/c15i26VrJ4IRt5Z89DnIzCGDn9EcebibhAOjw5ibqEHsE1wLUgkPn9RDmNcUKyU87GeaL633nyJ+pplFR2ZQ==} + engines: {node: '>=18'} + + has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + hookable@5.5.3: resolution: {integrity: sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==} + ignore@5.3.2: + resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} + engines: {node: '>= 4'} + + ignore@7.0.5: + resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==} + engines: {node: '>= 4'} + + import-fresh@3.3.1: + resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} + engines: {node: '>=6'} + import-without-cache@0.2.4: resolution: {integrity: sha512-b/Ke0y4n26ffQhkLvgBxV/NVO/QEE6AZlrMj8DYuxBWNAAu4iMQWZTFWzKcCTEmv7VQ0ae0j8KwrlGzSy8sYQQ==} engines: {node: '>=20.19.0'} + imurmurhash@0.1.4: + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + engines: {node: '>=0.8.19'} + + indent-string@5.0.0: + resolution: {integrity: sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==} + engines: {node: '>=12'} + + is-builtin-module@5.0.0: + resolution: {integrity: sha512-f4RqJKBUe5rQkJ2eJEJBXSticB3hGbN9j0yxxMQFqIW89Jp9WYFtzfTcRlstDKVUTRzSOTLKRfO9vIztenwtxA==} + engines: {node: '>=18.20'} + + is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + + is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + + isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + jiti@2.6.1: resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==} hasBin: true + js-yaml@4.1.1: + resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==} + hasBin: true + jsesc@3.1.0: resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} engines: {node: '>=6'} hasBin: true + json-buffer@3.0.1: + resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + + json-schema-traverse@0.4.1: + resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + + json-stable-stringify-without-jsonify@1.0.1: + resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + jsonc-parser@3.3.1: resolution: {integrity: sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==} + keyv@4.5.4: + resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + + levn@0.4.1: + resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} + engines: {node: '>= 0.8.0'} + + locate-path@6.0.0: + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} + + lodash.merge@4.6.2: + resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + magic-string@0.30.21: resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + minimatch@3.1.2: + resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} + + minimatch@9.0.5: + resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} + engines: {node: '>=16 || 14 >=14.17'} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + nanoid@3.3.11: resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true + natural-compare@1.4.0: + resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + node-fetch-native@1.6.7: resolution: {integrity: sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==} + node-releases@2.0.27: + resolution: {integrity: sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==} + nypm@0.6.2: resolution: {integrity: sha512-7eM+hpOtrKrBDCh7Ypu2lJ9Z7PNZBdi/8AT3AX8xoCj43BBVHD0hPSTEvMtkMpfs8FCqBGhxB+uToIQimA111g==} engines: {node: ^14.16.0 || >=16.10.0} @@ -704,9 +1112,33 @@ packages: ohash@2.0.11: resolution: {integrity: sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==} + optionator@0.9.4: + resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} + engines: {node: '>= 0.8.0'} + + p-limit@3.1.0: + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} + + p-locate@5.0.0: + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} + engines: {node: '>=10'} + package-manager-detector@1.6.0: resolution: {integrity: sha512-61A5ThoTiDG/C8s8UMZwSorAGwMJ0ERVGj2OjoW5pAalsNOg15+iQiPzrLJ4jhZ1HJzmC2PIHT2oEiH3R5fzNA==} + parent-module@1.0.1: + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} + engines: {node: '>=6'} + + path-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + + path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + pathe@2.0.3: resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} @@ -723,10 +1155,22 @@ packages: pkg-types@2.3.0: resolution: {integrity: sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig==} + pluralize@8.0.0: + resolution: {integrity: sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==} + engines: {node: '>=4'} + postcss@8.5.6: resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} engines: {node: ^10 || ^12 || >=14} + prelude-ls@1.2.1: + resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} + engines: {node: '>= 0.8.0'} + + punycode@2.3.1: + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + engines: {node: '>=6'} + quansync@1.0.0: resolution: {integrity: sha512-5xZacEEufv3HSTPQuchrvV6soaiACMFnq1H8wkVioctoH3TRha9Sz66lOxRwPK/qZj7HPiSveih9yAyh98gvqA==} @@ -737,6 +1181,18 @@ packages: resolution: {integrity: sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ==} engines: {node: '>= 20.19.0'} + regexp-tree@0.1.27: + resolution: {integrity: sha512-iETxpjK6YoRWJG5o6hXLwvjYAoW+FEZn9os0PD/b6AP6xQwsa/Y7lCVgIixBbUPMfhu+i2LtdeAqVTgGlQarfA==} + hasBin: true + + regjsparser@0.13.0: + resolution: {integrity: sha512-NZQZdC5wOE/H3UT28fVGL+ikOZcEzfMGk/c3iN9UGxzWHMa1op7274oyiUVrAG4B2EuFhus8SvkaYnhvW92p9Q==} + hasBin: true + + resolve-from@4.0.0: + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} + engines: {node: '>=4'} + resolve-pkg-maps@1.0.0: resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} @@ -774,6 +1230,14 @@ packages: engines: {node: '>=10'} hasBin: true + shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + + shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + siginfo@2.0.0: resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} @@ -787,6 +1251,18 @@ packages: std-env@3.10.0: resolution: {integrity: sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==} + strip-indent@4.1.1: + resolution: {integrity: sha512-SlyRoSkdh1dYP0PzclLE7r0M9sgbFKKMFXpFRUMNuKhQSbC6VQIGzq3E0qsfvGJaUFJPGv6Ws1NZ/haTAjfbMA==} + engines: {node: '>=12'} + + strip-json-comments@3.1.1: + resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} + engines: {node: '>=8'} + + supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + tinybench@2.9.0: resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} @@ -806,6 +1282,12 @@ packages: resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} hasBin: true + ts-api-utils@2.1.0: + resolution: {integrity: sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==} + engines: {node: '>=18.12'} + peerDependencies: + typescript: '>=4.8.4' + tsdown@0.18.1: resolution: {integrity: sha512-na4MdVA8QS9Zw++0KovGpjvw1BY5WvoCWcE4Aw0dyfff9nWK8BPzniQEVs+apGUg3DHaYMDfs+XiFaDDgqDDzQ==} engines: {node: '>=20.19.0'} @@ -834,6 +1316,17 @@ packages: tslib@2.8.1: resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + type-check@0.4.0: + resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} + engines: {node: '>= 0.8.0'} + + typescript-eslint@8.50.0: + resolution: {integrity: sha512-Q1/6yNUmCpH94fbgMUMg2/BSAr/6U7GBk61kZTv1/asghQOWOjTlp9K8mixS5NcJmm2creY+UFfGeW/+OcA64A==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <6.0.0' + typescript@5.9.3: resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} engines: {node: '>=14.17'} @@ -855,6 +1348,15 @@ packages: synckit: optional: true + update-browserslist-db@1.2.3: + resolution: {integrity: sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + + uri-js@4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + vite@7.3.0: resolution: {integrity: sha512-dZwN5L1VlUBewiP6H9s2+B3e3Jg96D0vzN+Ry73sOefebhYr9f94wwkMNN/9ouoU8pV1BqA1d1zGk8928cx0rg==} engines: {node: ^20.19.0 || >=22.12.0} @@ -929,16 +1431,29 @@ packages: jsdom: optional: true + which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + why-is-node-running@2.3.0: resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} engines: {node: '>=8'} hasBin: true + word-wrap@1.2.5: + resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} + engines: {node: '>=0.10.0'} + yaml@2.8.2: resolution: {integrity: sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==} engines: {node: '>= 14.6'} hasBin: true + yocto-queue@0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + snapshots: '@babel/generator@7.28.5': @@ -1083,6 +1598,63 @@ snapshots: '@esbuild/win32-x64@0.27.2': optional: true + '@eslint-community/eslint-utils@4.9.0(eslint@9.39.2(jiti@2.6.1))': + dependencies: + eslint: 9.39.2(jiti@2.6.1) + eslint-visitor-keys: 3.4.3 + + '@eslint-community/regexpp@4.12.2': {} + + '@eslint/config-array@0.21.1': + dependencies: + '@eslint/object-schema': 2.1.7 + debug: 4.4.3 + minimatch: 3.1.2 + transitivePeerDependencies: + - supports-color + + '@eslint/config-helpers@0.4.2': + dependencies: + '@eslint/core': 0.17.0 + + '@eslint/core@0.17.0': + dependencies: + '@types/json-schema': 7.0.15 + + '@eslint/eslintrc@3.3.3': + dependencies: + ajv: 6.12.6 + debug: 4.4.3 + espree: 10.4.0 + globals: 14.0.0 + ignore: 5.3.2 + import-fresh: 3.3.1 + js-yaml: 4.1.1 + minimatch: 3.1.2 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color + + '@eslint/js@9.39.2': {} + + '@eslint/object-schema@2.1.7': {} + + '@eslint/plugin-kit@0.4.1': + dependencies: + '@eslint/core': 0.17.0 + levn: 0.4.1 + + '@humanfs/core@0.19.1': {} + + '@humanfs/node@0.16.7': + dependencies: + '@humanfs/core': 0.19.1 + '@humanwhocodes/retry': 0.4.3 + + '@humanwhocodes/module-importer@1.0.1': {} + + '@humanwhocodes/retry@0.4.3': {} + '@jridgewell/gen-mapping@0.3.13': dependencies: '@jridgewell/sourcemap-codec': 1.5.5 @@ -1235,10 +1807,103 @@ snapshots: '@types/estree@1.0.8': {} + '@types/json-schema@7.0.15': {} + '@types/node@25.0.3': dependencies: undici-types: 7.16.0 + '@typescript-eslint/eslint-plugin@8.50.0(@typescript-eslint/parser@8.50.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)': + dependencies: + '@eslint-community/regexpp': 4.12.2 + '@typescript-eslint/parser': 8.50.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/scope-manager': 8.50.0 + '@typescript-eslint/type-utils': 8.50.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/utils': 8.50.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.50.0 + eslint: 9.39.2(jiti@2.6.1) + ignore: 7.0.5 + natural-compare: 1.4.0 + ts-api-utils: 2.1.0(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/parser@8.50.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)': + dependencies: + '@typescript-eslint/scope-manager': 8.50.0 + '@typescript-eslint/types': 8.50.0 + '@typescript-eslint/typescript-estree': 8.50.0(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.50.0 + debug: 4.4.3 + eslint: 9.39.2(jiti@2.6.1) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/project-service@8.50.0(typescript@5.9.3)': + dependencies: + '@typescript-eslint/tsconfig-utils': 8.50.0(typescript@5.9.3) + '@typescript-eslint/types': 8.50.0 + debug: 4.4.3 + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/scope-manager@8.50.0': + dependencies: + '@typescript-eslint/types': 8.50.0 + '@typescript-eslint/visitor-keys': 8.50.0 + + '@typescript-eslint/tsconfig-utils@8.50.0(typescript@5.9.3)': + dependencies: + typescript: 5.9.3 + + '@typescript-eslint/type-utils@8.50.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)': + dependencies: + '@typescript-eslint/types': 8.50.0 + '@typescript-eslint/typescript-estree': 8.50.0(typescript@5.9.3) + '@typescript-eslint/utils': 8.50.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) + debug: 4.4.3 + eslint: 9.39.2(jiti@2.6.1) + ts-api-utils: 2.1.0(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/types@8.50.0': {} + + '@typescript-eslint/typescript-estree@8.50.0(typescript@5.9.3)': + dependencies: + '@typescript-eslint/project-service': 8.50.0(typescript@5.9.3) + '@typescript-eslint/tsconfig-utils': 8.50.0(typescript@5.9.3) + '@typescript-eslint/types': 8.50.0 + '@typescript-eslint/visitor-keys': 8.50.0 + debug: 4.4.3 + minimatch: 9.0.5 + semver: 7.7.3 + tinyglobby: 0.2.15 + ts-api-utils: 2.1.0(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/utils@8.50.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)': + dependencies: + '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.2(jiti@2.6.1)) + '@typescript-eslint/scope-manager': 8.50.0 + '@typescript-eslint/types': 8.50.0 + '@typescript-eslint/typescript-estree': 8.50.0(typescript@5.9.3) + eslint: 9.39.2(jiti@2.6.1) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/visitor-keys@8.50.0': + dependencies: + '@typescript-eslint/types': 8.50.0 + eslint-visitor-keys: 4.2.1 + '@vitest/expect@4.0.16': dependencies: '@standard-schema/spec': 1.1.0 @@ -1278,8 +1943,27 @@ snapshots: '@vitest/pretty-format': 4.0.16 tinyrainbow: 3.0.3 + acorn-jsx@5.3.2(acorn@8.15.0): + dependencies: + acorn: 8.15.0 + + acorn@8.15.0: {} + + ajv@6.12.6: + dependencies: + fast-deep-equal: 3.1.3 + fast-json-stable-stringify: 2.1.0 + json-schema-traverse: 0.4.1 + uri-js: 4.4.1 + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + ansis@4.2.0: {} + argparse@2.0.1: {} + args-tokenizer@0.3.0: {} assertion-error@2.0.1: {} @@ -1289,8 +1973,31 @@ snapshots: '@babel/parser': 7.28.5 pathe: 2.0.3 + balanced-match@1.0.2: {} + + baseline-browser-mapping@2.9.11: {} + birpc@4.0.0: {} + brace-expansion@1.1.12: + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + + brace-expansion@2.0.2: + dependencies: + balanced-match: 1.0.2 + + browserslist@4.28.1: + dependencies: + baseline-browser-mapping: 2.9.11 + caniuse-lite: 1.0.30001761 + electron-to-chromium: 1.5.267 + node-releases: 2.0.27 + update-browserslist-db: 1.2.3(browserslist@4.28.1) + + builtin-modules@5.0.0: {} + bumpp@10.3.2: dependencies: ansis: 4.2.0 @@ -1324,20 +2031,61 @@ snapshots: cac@6.7.14: {} + callsites@3.1.0: {} + + caniuse-lite@1.0.30001761: {} + chai@6.2.1: {} + chalk@4.1.2: + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + + change-case@5.4.4: {} + chokidar@5.0.0: dependencies: readdirp: 5.0.0 + ci-info@4.3.1: {} + citty@0.1.6: dependencies: consola: 3.4.2 + clean-regexp@1.0.0: + dependencies: + escape-string-regexp: 1.0.5 + + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.4: {} + + concat-map@0.0.1: {} + confbox@0.2.2: {} consola@3.4.2: {} + core-js-compat@3.47.0: + dependencies: + browserslist: 4.28.1 + + cross-spawn@7.0.6: + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + + debug@4.4.3: + dependencies: + ms: 2.1.3 + + deep-is@0.1.4: {} + defu@6.1.4: {} destr@2.0.5: {} @@ -1358,6 +2106,8 @@ snapshots: dts-resolver@2.1.3: {} + electron-to-chromium@1.5.267: {} + empathic@2.0.0: {} es-module-lexer@1.7.0: {} @@ -1393,18 +2143,136 @@ snapshots: escalade@3.2.0: {} + escape-string-regexp@1.0.5: {} + + escape-string-regexp@4.0.0: {} + + eslint-plugin-unicorn@62.0.0(eslint@9.39.2(jiti@2.6.1)): + dependencies: + '@babel/helper-validator-identifier': 7.28.5 + '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.2(jiti@2.6.1)) + '@eslint/plugin-kit': 0.4.1 + change-case: 5.4.4 + ci-info: 4.3.1 + clean-regexp: 1.0.0 + core-js-compat: 3.47.0 + eslint: 9.39.2(jiti@2.6.1) + esquery: 1.6.0 + find-up-simple: 1.0.1 + globals: 16.5.0 + indent-string: 5.0.0 + is-builtin-module: 5.0.0 + jsesc: 3.1.0 + pluralize: 8.0.0 + regexp-tree: 0.1.27 + regjsparser: 0.13.0 + semver: 7.7.3 + strip-indent: 4.1.1 + + eslint-scope@8.4.0: + dependencies: + esrecurse: 4.3.0 + estraverse: 5.3.0 + + eslint-visitor-keys@3.4.3: {} + + eslint-visitor-keys@4.2.1: {} + + eslint@9.39.2(jiti@2.6.1): + dependencies: + '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.2(jiti@2.6.1)) + '@eslint-community/regexpp': 4.12.2 + '@eslint/config-array': 0.21.1 + '@eslint/config-helpers': 0.4.2 + '@eslint/core': 0.17.0 + '@eslint/eslintrc': 3.3.3 + '@eslint/js': 9.39.2 + '@eslint/plugin-kit': 0.4.1 + '@humanfs/node': 0.16.7 + '@humanwhocodes/module-importer': 1.0.1 + '@humanwhocodes/retry': 0.4.3 + '@types/estree': 1.0.8 + ajv: 6.12.6 + chalk: 4.1.2 + cross-spawn: 7.0.6 + debug: 4.4.3 + escape-string-regexp: 4.0.0 + eslint-scope: 8.4.0 + eslint-visitor-keys: 4.2.1 + espree: 10.4.0 + esquery: 1.6.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 8.0.0 + find-up: 5.0.0 + glob-parent: 6.0.2 + ignore: 5.3.2 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + json-stable-stringify-without-jsonify: 1.0.1 + lodash.merge: 4.6.2 + minimatch: 3.1.2 + natural-compare: 1.4.0 + optionator: 0.9.4 + optionalDependencies: + jiti: 2.6.1 + transitivePeerDependencies: + - supports-color + + espree@10.4.0: + dependencies: + acorn: 8.15.0 + acorn-jsx: 5.3.2(acorn@8.15.0) + eslint-visitor-keys: 4.2.1 + + esquery@1.6.0: + dependencies: + estraverse: 5.3.0 + + esrecurse@4.3.0: + dependencies: + estraverse: 5.3.0 + + estraverse@5.3.0: {} + estree-walker@3.0.3: dependencies: '@types/estree': 1.0.8 + esutils@2.0.3: {} + expect-type@1.3.0: {} exsolve@1.0.8: {} + fast-deep-equal@3.1.3: {} + + fast-json-stable-stringify@2.1.0: {} + + fast-levenshtein@2.0.6: {} + fdir@6.5.0(picomatch@4.0.3): optionalDependencies: picomatch: 4.0.3 + file-entry-cache@8.0.0: + dependencies: + flat-cache: 4.0.1 + + find-up-simple@1.0.1: {} + + find-up@5.0.0: + dependencies: + locate-path: 6.0.0 + path-exists: 4.0.0 + + flat-cache@4.0.1: + dependencies: + flatted: 3.3.3 + keyv: 4.5.4 + + flatted@3.3.3: {} + fsevents@2.3.3: optional: true @@ -1421,24 +2289,98 @@ snapshots: nypm: 0.6.2 pathe: 2.0.3 + glob-parent@6.0.2: + dependencies: + is-glob: 4.0.3 + + globals@14.0.0: {} + + globals@16.5.0: {} + + has-flag@4.0.0: {} + hookable@5.5.3: {} + ignore@5.3.2: {} + + ignore@7.0.5: {} + + import-fresh@3.3.1: + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + import-without-cache@0.2.4: {} + imurmurhash@0.1.4: {} + + indent-string@5.0.0: {} + + is-builtin-module@5.0.0: + dependencies: + builtin-modules: 5.0.0 + + is-extglob@2.1.1: {} + + is-glob@4.0.3: + dependencies: + is-extglob: 2.1.1 + + isexe@2.0.0: {} + jiti@2.6.1: {} + js-yaml@4.1.1: + dependencies: + argparse: 2.0.1 + jsesc@3.1.0: {} + json-buffer@3.0.1: {} + + json-schema-traverse@0.4.1: {} + + json-stable-stringify-without-jsonify@1.0.1: {} + jsonc-parser@3.3.1: {} + keyv@4.5.4: + dependencies: + json-buffer: 3.0.1 + + levn@0.4.1: + dependencies: + prelude-ls: 1.2.1 + type-check: 0.4.0 + + locate-path@6.0.0: + dependencies: + p-locate: 5.0.0 + + lodash.merge@4.6.2: {} + magic-string@0.30.21: dependencies: '@jridgewell/sourcemap-codec': 1.5.5 + minimatch@3.1.2: + dependencies: + brace-expansion: 1.1.12 + + minimatch@9.0.5: + dependencies: + brace-expansion: 2.0.2 + + ms@2.1.3: {} + nanoid@3.3.11: {} + natural-compare@1.4.0: {} + node-fetch-native@1.6.7: {} + node-releases@2.0.27: {} + nypm@0.6.2: dependencies: citty: 0.1.6 @@ -1451,8 +2393,33 @@ snapshots: ohash@2.0.11: {} + optionator@0.9.4: + dependencies: + deep-is: 0.1.4 + fast-levenshtein: 2.0.6 + levn: 0.4.1 + prelude-ls: 1.2.1 + type-check: 0.4.0 + word-wrap: 1.2.5 + + p-limit@3.1.0: + dependencies: + yocto-queue: 0.1.0 + + p-locate@5.0.0: + dependencies: + p-limit: 3.1.0 + package-manager-detector@1.6.0: {} + parent-module@1.0.1: + dependencies: + callsites: 3.1.0 + + path-exists@4.0.0: {} + + path-key@3.1.1: {} + pathe@2.0.3: {} perfect-debounce@2.0.0: {} @@ -1467,12 +2434,18 @@ snapshots: exsolve: 1.0.8 pathe: 2.0.3 + pluralize@8.0.0: {} + postcss@8.5.6: dependencies: nanoid: 3.3.11 picocolors: 1.1.1 source-map-js: 1.2.1 + prelude-ls@1.2.1: {} + + punycode@2.3.1: {} + quansync@1.0.0: {} rc9@2.1.2: @@ -1482,6 +2455,14 @@ snapshots: readdirp@5.0.0: {} + regexp-tree@0.1.27: {} + + regjsparser@0.13.0: + dependencies: + jsesc: 3.1.0 + + resolve-from@4.0.0: {} + resolve-pkg-maps@1.0.0: {} rolldown-plugin-dts@0.19.1(rolldown@1.0.0-beta.55)(typescript@5.9.3): @@ -1549,6 +2530,12 @@ snapshots: semver@7.7.3: {} + shebang-command@2.0.0: + dependencies: + shebang-regex: 3.0.0 + + shebang-regex@3.0.0: {} + siginfo@2.0.0: {} source-map-js@1.2.1: {} @@ -1557,6 +2544,14 @@ snapshots: std-env@3.10.0: {} + strip-indent@4.1.1: {} + + strip-json-comments@3.1.1: {} + + supports-color@7.2.0: + dependencies: + has-flag: 4.0.0 + tinybench@2.9.0: {} tinyexec@1.0.2: {} @@ -1570,6 +2565,10 @@ snapshots: tree-kill@1.2.2: {} + ts-api-utils@2.1.0(typescript@5.9.3): + dependencies: + typescript: 5.9.3 + tsdown@0.18.1(typescript@5.9.3): dependencies: ansis: 4.2.0 @@ -1600,6 +2599,21 @@ snapshots: tslib@2.8.1: optional: true + type-check@0.4.0: + dependencies: + prelude-ls: 1.2.1 + + typescript-eslint@8.50.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3): + dependencies: + '@typescript-eslint/eslint-plugin': 8.50.0(@typescript-eslint/parser@8.50.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/parser': 8.50.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/typescript-estree': 8.50.0(typescript@5.9.3) + '@typescript-eslint/utils': 8.50.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) + eslint: 9.39.2(jiti@2.6.1) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + typescript@5.9.3: {} unconfig-core@7.4.2: @@ -1613,6 +2627,16 @@ snapshots: dependencies: rolldown: 1.0.0-beta.55 + update-browserslist-db@1.2.3(browserslist@4.28.1): + dependencies: + browserslist: 4.28.1 + escalade: 3.2.0 + picocolors: 1.1.1 + + uri-js@4.4.1: + dependencies: + punycode: 2.3.1 + vite@7.3.0(@types/node@25.0.3)(jiti@2.6.1)(yaml@2.8.2): dependencies: esbuild: 0.27.2 @@ -1664,9 +2688,17 @@ snapshots: - tsx - yaml + which@2.0.2: + dependencies: + isexe: 2.0.0 + why-is-node-running@2.3.0: dependencies: siginfo: 2.0.0 stackback: 0.0.2 + word-wrap@1.2.5: {} + yaml@2.8.2: {} + + yocto-queue@0.1.0: {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index abc3af1..ca95b10 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -2,4 +2,5 @@ packages: - packages/* onlyBuiltDependencies: + - dprint - esbuild