Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/pull_request.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ jobs:
- run: yarn run format:check
if: runner.os == 'Linux'
- run: yarn run build
- run: yarn run test
# Sanity-check `npm pack` only once (Linux + Node 24) to avoid
# 6-way artifact-name collisions; lint/build coverage is what
# the cross-OS matrix is actually for.
Expand Down
6 changes: 4 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,10 @@
"build": "vite build && vue-tsc -p tsconfig.build.json --emitDeclarationOnly",
"typecheck": "vue-tsc --noEmit",
"lint": "eslint src demo",
"test": "tsx --test tests/*.test.ts",
"quality": "scoria",
"format": "prettier --write \"{src,demo}/**/*.{ts,tsx,vue,json,css}\" \"*.{ts,js,json}\"",
"format:check": "prettier --check \"{src,demo}/**/*.{ts,tsx,vue,json,css}\" \"*.{ts,js,json}\""
"format": "prettier --write \"{src,demo,tests}/**/*.{ts,tsx,vue,json,css}\" \"*.{ts,js,json}\"",
"format:check": "prettier --check \"{src,demo,tests}/**/*.{ts,tsx,vue,json,css}\" \"*.{ts,js,json}\""
},
"peerDependencies": {
"gui-chat-protocol": "^2.3.0",
Expand All @@ -53,6 +54,7 @@
"prettier": "^3.9.10",
"scoria": "^0.4.1",
"tailwindcss": "^4.3.3",
"tsx": "^4.23.15",
"typescript": "~6.0.3",
"vite": "^8.3.4",
"vue": "^3.5.44",
Expand Down
11 changes: 11 additions & 0 deletions plans/fix-34-move-position-bounds.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# #34 — reject fractional move coordinates

The bounds check (`row < 0 || row > 2 || …`) let a fractional value through, so `row: 1.5` threw a `TypeError` and `col: 0.5` was reported as an occupied square.

## How
- `src/core/position.ts`: `isOnBoard(row, col, size)` — both are whole numbers inside the board. Pure, so it is tested on its own.
- `playTicTacToe` uses it in place of the range comparison, so these moves get the out-of-bounds response.
- `tests/position.test.ts` covers the check in both directions and `playTicTacToe` with off-board and fractional moves. Run with `yarn test`, the same `tsx --test` setup the other plugins use; CI now runs it.

## Left out
- Validating the shape of a caller-supplied `board`.
7 changes: 5 additions & 2 deletions src/core/logic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
*/

import type { Cell, Side, TicTacToeBoard, Command, TicTacToeState } from "./types";
import { isOnBoard } from "./position";

const BOARD_SIZE = 3;

// All possible winning lines (rows, columns, diagonals)
const WINNING_LINES: { row: number; col: number }[][] = [
Expand Down Expand Up @@ -54,7 +57,7 @@
function createEmptyBoard(): TicTacToeBoard {
const board: TicTacToeBoard = [];
for (let i = 0; i < 3; i++) {
board[i] = new Array(3).fill(".") as Cell[];

Check warning on line 60 in src/core/logic.ts

View workflow job for this annotation

GitHub Actions / lint_test (26.x, ubuntu-latest)

Do not use any type assertions

Check warning on line 60 in src/core/logic.ts

View workflow job for this annotation

GitHub Actions / lint_test (24.x, windows-latest)

Do not use any type assertions

Check warning on line 60 in src/core/logic.ts

View workflow job for this annotation

GitHub Actions / lint_test (26.x, windows-latest)

Do not use any type assertions

Check warning on line 60 in src/core/logic.ts

View workflow job for this annotation

GitHub Actions / lint_test (24.x, ubuntu-latest)

Do not use any type assertions

Check warning on line 60 in src/core/logic.ts

View workflow job for this annotation

GitHub Actions / lint_test (26.x, macos-latest)

Do not use any type assertions

Check warning on line 60 in src/core/logic.ts

View workflow job for this annotation

GitHub Actions / lint_test (24.x, macos-latest)

Do not use any type assertions
}
return board;
}
Expand Down Expand Up @@ -112,7 +115,7 @@
for (const line of WINNING_LINES) {
const cells = line.map((pos) => board[pos.row][pos.col]);
if (cells[0] !== "." && cells[0] === cells[1] && cells[1] === cells[2]) {
return { winner: cells[0] as Side, winningLine: line };

Check warning on line 118 in src/core/logic.ts

View workflow job for this annotation

GitHub Actions / lint_test (26.x, ubuntu-latest)

Do not use any type assertions

Check warning on line 118 in src/core/logic.ts

View workflow job for this annotation

GitHub Actions / lint_test (24.x, windows-latest)

Do not use any type assertions

Check warning on line 118 in src/core/logic.ts

View workflow job for this annotation

GitHub Actions / lint_test (26.x, windows-latest)

Do not use any type assertions

Check warning on line 118 in src/core/logic.ts

View workflow job for this annotation

GitHub Actions / lint_test (24.x, ubuntu-latest)

Do not use any type assertions

Check warning on line 118 in src/core/logic.ts

View workflow job for this annotation

GitHub Actions / lint_test (26.x, macos-latest)

Do not use any type assertions

Check warning on line 118 in src/core/logic.ts

View workflow job for this annotation

GitHub Actions / lint_test (24.x, macos-latest)

Do not use any type assertions
}
}
return { winner: null, winningLine: null };
Expand Down Expand Up @@ -157,7 +160,7 @@
const { row, col, board, currentSide, playerNames } = cmd;

// Validate the move
if (row < 0 || row > 2 || col < 0 || col > 2) {
if (!isOnBoard(row, col, BOARD_SIZE)) {
const legalMoves = getLegalMoves(board);
const counts = countPieces(board);

Expand All @@ -171,7 +174,7 @@
winner: null,
winningLine: null,
lastAction: { type: "move", row, col },
error: `Invalid move: (${row}, ${col}) is out of bounds. Row and column must be 0-2.`,
error: `Invalid move: (${row}, ${col}) is out of bounds. Row and column must be whole numbers from 0 to 2.`,
};
}

Expand Down
4 changes: 4 additions & 0 deletions src/core/position.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
/** Whether (row, col) names a square of a size×size board: whole numbers, inside it. */
export function isOnBoard(row: number, col: number, size: number): boolean {
return [row, col].every((index) => Number.isInteger(index) && index >= 0 && index < size);
}
60 changes: 60 additions & 0 deletions tests/position.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { test, describe } from "node:test";
import assert from "node:assert";
import { isOnBoard } from "../src/core/position.js";
import { playTicTacToe } from "../src/core/logic.js";

const SIZE = 3;
const playerNames = { X: "user", O: "computer" };
const opening = () => playTicTacToe({ action: "new_game", playerNames });
const moveTo = (row: number, col: number) => playTicTacToe({ action: "move", row, col, board: opening().board, currentSide: "X", playerNames });

describe("isOnBoard", () => {
test("accepts every square", () => {
[0, 1, 2].forEach((row) => [0, 1, 2].forEach((col) => assert.equal(isOnBoard(row, col, SIZE), true)));
});

test("rejects positions just outside each edge", () => {
[
[-1, 0],
[0, -1],
[SIZE, 0],
[0, SIZE],
].forEach(([row, col]) => assert.equal(isOnBoard(row, col, SIZE), false));
});

test("rejects values that are not whole numbers", () => {
[0.5, Number.NaN, Number.POSITIVE_INFINITY, Number.NEGATIVE_INFINITY, -0.5].forEach((value) => {
assert.equal(isOnBoard(value, 0, SIZE), false);
assert.equal(isOnBoard(0, value, SIZE), false);
});
});

test("rejects a non-number from an untyped caller", () => {
const fromJson: { row: number } = JSON.parse('{"row":"__proto__"}');
assert.equal(isOnBoard(fromJson.row, 0, SIZE), false);
});
});

describe("playTicTacToe move coordinates", () => {
test("a square on the board is played", () => {
const state = moveTo(1, 1);
assert.equal(state.error, undefined);
assert.equal(state.board[1][1], "X");
});

test("a square off the board or between squares is out of bounds, not a throw", () => {
[
[-1, 0],
[0, -1],
[SIZE, 0],
[0, SIZE],
[1.5, 0],
[0, 0.5],
[Number.NaN, 0],
].forEach(([row, col]) => {
const state = moveTo(row, col);
assert.match(state.error ?? "", /out of bounds/);
assert.deepEqual(state.board, opening().board);
});
});
});
171 changes: 171 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,136 @@
dependencies:
tslib "^2.4.0"

"@esbuild/aix-ppc64@0.28.2":
version "0.28.2"
resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.28.2.tgz#bf6e10303bcf2e7c686975fa52f937ec2728d8bc"
integrity sha512-XExcO+dvLKvVtNTibSTBej1NCAbaGhWn9Ww1ZPx80qsahhPFe/8jgWP0IchNe0F3HwkU7n8ejhH8bjonqht8mQ==

"@esbuild/android-arm64@0.28.2":
version "0.28.2"
resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.28.2.tgz#0c6246bc8d2c4d172aac2db3fb1190d72bd65504"
integrity sha512-5YfKeeI8qWfBZIX+u2xZC3Zlb3Os/gLS2sbEKM+I4ZOcsWmHS2WLysCcQZDAFRslDUU5Oiq44gf6PYN1vGwG5A==

"@esbuild/android-arm@0.28.2":
version "0.28.2"
resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.28.2.tgz#2d84ece6a4e2684d92be26ee13d42757d831c381"
integrity sha512-kXXoiPVVGQcnIYGOeaovwOURpniDBpSq4A03qkQ+BMQqtGG6HYap3xne9C1O1yo4TR3qxlCX5IqqmX6fFo2Lqg==

"@esbuild/android-x64@0.28.2":
version "0.28.2"
resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.28.2.tgz#fc38d4d6358d8dc1cf53f09f7589fe436eb64801"
integrity sha512-O387ite7SzUyCcy3JQX4P4bLtEA7bLLkx+esve5JHnyYfNTxcVpXZo9jhdB0lTKN44gztELTdU7nS8Nr16Fs1Q==

"@esbuild/darwin-arm64@0.28.2":
version "0.28.2"
resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.28.2.tgz#f83afeeac1d7dac01c7a2fd012b3e451a0591fcc"
integrity sha512-n4KqkOQrraxHJcgjM1RvwbigfQKIKJVpM7xp+KsxiyUSrRdIXnt73VhrPAx0fV44hgfmIVKjxMN9J1t5jySVkw==

"@esbuild/darwin-x64@0.28.2":
version "0.28.2"
resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.28.2.tgz#510147c055a795588dbbe14fd6b1b8ad0a2f30de"
integrity sha512-uq6suIWYP37qzGddBKPw5QEQPi6HiLGsO7UmkpfyaYNQ3D+rN6w6WfwH+nuqcGXWvawGwxOEroO4YGnFh95azw==

"@esbuild/freebsd-arm64@0.28.2":
version "0.28.2"
resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.2.tgz#093b9200ecf0b115ba4e5e248a7485c9c5f8bd5e"
integrity sha512-n+I0BTSRIoy+d6RPKnEVwql5UwBJolytvY4mAOIEJorKlqgPII8ix6slVVrfZ5Tnj7glIZvloylbB/EJPMWEXw==

"@esbuild/freebsd-x64@0.28.2":
version "0.28.2"
resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.28.2.tgz#0be22b6df925d213e841ea87123af5df80b0faf7"
integrity sha512-78XJTJkvPs0kz2w61301PJjXl4g7q3JqiYMZ/M/yVI73EHBrCRTgkhu9oqG7vPqq+a/yadEW8aD+agKlk5xrmg==

"@esbuild/linux-arm64@0.28.2":
version "0.28.2"
resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.28.2.tgz#1bdbc651cda9ba9995c53ed9c71ceaa65094762d"
integrity sha512-pW4AC0P3it8c7do9MVM4p51FzHzdM/TZrerurgRcHJ2WTa1VQ1CIq18xncfpBJw4ojkiZZrKW2yIBWBP92j6Ug==

"@esbuild/linux-arm@0.28.2":
version "0.28.2"
resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.28.2.tgz#beb12ad72b84f72d28488cc1b8ee9f7eb141d753"
integrity sha512-XlDnu2q5yoqems+xay6wSAcg9DDD7K9RLKZEBOMZm3ckNpJBvOX20tSfby8KfrrhINDyv9V2YVZKY/SpoGJI8w==

"@esbuild/linux-ia32@0.28.2":
version "0.28.2"
resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.28.2.tgz#b81f9d55529b45c206a46a138214b1aa6879696b"
integrity sha512-CYbnj78HsIeA+DhgUKgFCfvNsTHFhMMrinUrMZpDXJXKN8T3XViTZ/+wtHeVxEWY8ewSzTFN+nRmSwO2tZaLUQ==

"@esbuild/linux-loong64@0.28.2":
version "0.28.2"
resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.28.2.tgz#598667241a04c99b76ed6ef940ac50038c419f98"
integrity sha512-buwkd8nsph4R+ajRvw0qM5Hja/TXQow3ptzWO2EbG/cqcIkHloRrdlBtQlshyYGTNFvfkfJ5tpPLVkY4DtsPfQ==

"@esbuild/linux-mips64el@0.28.2":
version "0.28.2"
resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.28.2.tgz#1c51eb9cea903f53d97b5af3b1841db70f5596ca"
integrity sha512-ZVykbDyk7519VwiNb9Lcj9m8XM6v5V9uKPvrEMkkEedVewf+0itkhahp4HDpgERXhwLRpWFypsGbG/J8s0QjJA==

"@esbuild/linux-ppc64@0.28.2":
version "0.28.2"
resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.28.2.tgz#63dd61f17ceb31a81227f413feac8a71bc2c51f2"
integrity sha512-CAXl+Dtd9UUuJd8pKKdwh6MLm3MUMiqMPmhZ3tTSXPqfyQ3vDl6R5hZdZ/kYojK4ofXtdfSv1tFq8XzWx3heNQ==

"@esbuild/linux-riscv64@0.28.2":
version "0.28.2"
resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.28.2.tgz#3763b08fde5cf25ab1facb8e7752edfe45fbfc27"
integrity sha512-GeXCej4IQtU1B+QlDV8W/RRvbzI3O/Stss+/bCXv4lZls5WGRtu2a+3JkA3i4qIUlMXpcHebWpF8AkJhATowuA==

"@esbuild/linux-s390x@0.28.2":
version "0.28.2"
resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.28.2.tgz#1a137ff293a82906eb3176385bd7e8e0e5cfb7cb"
integrity sha512-3H1weTYZPxt/WOhByszQZybS9w5lKzUn1FDMsgEChbHWQwHYQQRfBxgCcZvPhjHfKyJjIievvMmEUawJrdY9Dg==

"@esbuild/linux-x64@0.28.2":
version "0.28.2"
resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.28.2.tgz#268b36211c146ca54f8fe12c578a8d6ef8979485"
integrity sha512-4xTZr1FUmSoQW4XIWmit3tzQrUTZM+N3P0XV8xROKYF50XfI7xeO90+1bZvNwxIufQ9hDQVRJH5YhgPVF8A/HQ==

"@esbuild/netbsd-arm64@0.28.2":
version "0.28.2"
resolved "https://registry.yarnpkg.com/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.2.tgz#22571ad951d62bb6accc82d8d1fad5c8c1ac0ba1"
integrity sha512-sSATRjPeDBg3pdgHoQfoYBob11Kk1FGa9lui5RIHZCoCkJa9QKlvl3/vKz2usCmYYjs7ymJR/2Nnsqe+Hjt5nw==

"@esbuild/netbsd-x64@0.28.2":
version "0.28.2"
resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.28.2.tgz#42fcc57297eb0a0ca3f5fc475291f4c1a3f7c0de"
integrity sha512-lqnzCV+mM0gIADaKihiCg6ifgfU2L3h5E33rNQBN1Y4MaVGnzryzmvvf7UHxprpQdE8hpqLolJ9Rl+SkIRDpyw==

"@esbuild/openbsd-arm64@0.28.2":
version "0.28.2"
resolved "https://registry.yarnpkg.com/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.2.tgz#9eb32af104ac3dacf4edca01f596664aab0c73ef"
integrity sha512-AL2qJILH7lNjrDmCQDvdxMfAUIv8KMNZOvrwAQ8i8//ntL9FflhOyMJ8OZSMBb8/AWXe3/5v5S20y3zCoZWKoQ==

"@esbuild/openbsd-x64@0.28.2":
version "0.28.2"
resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.28.2.tgz#febed2402d6088225e91f20fb4ce2522ad0a4efd"
integrity sha512-QtiuPytchRyC4rwUKhexJdQKvDuZ6hWloi3igqPQNUJCS1/v9EiO3UTOXR6A3FoMo4fnAKbWJdqaIwhOzh8qEw==

"@esbuild/openharmony-arm64@0.28.2":
version "0.28.2"
resolved "https://registry.yarnpkg.com/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.2.tgz#85641c3d466428bfbccea5f21c26836663fef5ce"
integrity sha512-WkhYDmpTjLvGlScA1rwjRUmhl4k8oXR3cIbtqWmELgU/dFeHHlEllxDvdWcNJV9rbzCexB5vz8gtNewWLgCT7Q==

"@esbuild/sunos-x64@0.28.2":
version "0.28.2"
resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.28.2.tgz#a736f9d8962481045fc4c3e54f5479f22c870fb4"
integrity sha512-GPMSkTOtMnv2U2F8gxe4Io6qmVs+YKyp832Etqqxr0hFngmXQ3rzwytelm3GIn7T4VviRUlf3sOgBOiTdvaf7g==

"@esbuild/win32-arm64@0.28.2":
version "0.28.2"
resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.28.2.tgz#ee5ab40fad186201b652a33f8a5eb149e9e42532"
integrity sha512-PIhhEkE9uPBleRBrQEJpUn7MBnibZzbGzYWPmY3x+YoVg/95zbjB4CxPPOQ8l5tYYM4mMaCthF8/1DIfBQQyWQ==

"@esbuild/win32-ia32@0.28.2":
version "0.28.2"
resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.28.2.tgz#c40d28a6d99a127da6711f2afd74b11cb63b06a7"
integrity sha512-YmJbfTlvU7Sdn9BB+4PRES4oB6pxgS37MAONj+hBr/cpXS1aBPKXxNnDbu+QCWPj0o9dgyxeq79g6c5P8KeuYA==

"@esbuild/win32-x64@0.28.2":
version "0.28.2"
resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.28.2.tgz#b21affb804cc167c133d95f45b3a1dc1323b9a87"
integrity sha512-5ebpxr3nWMzrL/rnUI755Jkuee0bHL/Gq0WTF9lvcpv73wAp5eu8MfBUgWK9bhWvZjj7yX8etf/8tI8Ney695g==

"@eslint-community/eslint-utils@^4.8.0", "@eslint-community/eslint-utils@^4.9.1":
version "4.10.1"
resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.10.1.tgz#8911bd72b2c3640a543609e0400b8c4d2e7e7cb6"
Expand Down Expand Up @@ -1515,6 +1645,38 @@ es-object-atoms@^1.0.0, es-object-atoms@^1.1.1:
dependencies:
es-errors "^1.3.0"

esbuild@~0.28.0:
version "0.28.2"
resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.28.2.tgz#0f43bd1bad955b72d24e2261e3abe5957ccf0816"
integrity sha512-HKVLS8dvII+xoKW9kmqxbRKrnWEXfJJr/FZhhJmiqIB0e053QNYFqOBouTMO/k5sID4MvCiUCvv8b9M4h32wIA==
optionalDependencies:
"@esbuild/aix-ppc64" "0.28.2"
"@esbuild/android-arm" "0.28.2"
"@esbuild/android-arm64" "0.28.2"
"@esbuild/android-x64" "0.28.2"
"@esbuild/darwin-arm64" "0.28.2"
"@esbuild/darwin-x64" "0.28.2"
"@esbuild/freebsd-arm64" "0.28.2"
"@esbuild/freebsd-x64" "0.28.2"
"@esbuild/linux-arm" "0.28.2"
"@esbuild/linux-arm64" "0.28.2"
"@esbuild/linux-ia32" "0.28.2"
"@esbuild/linux-loong64" "0.28.2"
"@esbuild/linux-mips64el" "0.28.2"
"@esbuild/linux-ppc64" "0.28.2"
"@esbuild/linux-riscv64" "0.28.2"
"@esbuild/linux-s390x" "0.28.2"
"@esbuild/linux-x64" "0.28.2"
"@esbuild/netbsd-arm64" "0.28.2"
"@esbuild/netbsd-x64" "0.28.2"
"@esbuild/openbsd-arm64" "0.28.2"
"@esbuild/openbsd-x64" "0.28.2"
"@esbuild/openharmony-arm64" "0.28.2"
"@esbuild/sunos-x64" "0.28.2"
"@esbuild/win32-arm64" "0.28.2"
"@esbuild/win32-ia32" "0.28.2"
"@esbuild/win32-x64" "0.28.2"

escape-string-regexp@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34"
Expand Down Expand Up @@ -3137,6 +3299,15 @@ tslib@^2.4.0, tslib@^2.8.1:
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f"
integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==

tsx@^4.23.15:
version "4.23.15"
resolved "https://registry.yarnpkg.com/tsx/-/tsx-4.23.15.tgz#f53901695685ad567fed0f08532f7782e631c251"
integrity sha512-Yiex1Ovn8z2xPpOWckIiysV1SSyRMY9BkLF++q0yKiDxCqRhosKfMg3janKkiLBwZ5c/YryloKwGZcrEmtwxKw==
dependencies:
esbuild "~0.28.0"
optionalDependencies:
fsevents "~2.3.3"

type-check@^0.4.0, type-check@~0.4.0:
version "0.4.0"
resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1"
Expand Down
Loading