From 268f4116332140d3fab93c6c03e3278d21b9f889 Mon Sep 17 00:00:00 2001 From: Jay Date: Fri, 24 Oct 2025 12:58:28 -0400 Subject: [PATCH] Add prettier, formatted files. Use bytes/hex conversions from noble. --- .prettierignore | 1 + package-lock.json | 245 +++++++++++++++++++++++++++++++++++++++++++ package.json | 21 ++++ src/constants.ts | 11 -- src/errors.ts | 40 ------- src/event.test.ts | 1 + src/filter.test.ts | 1 + src/id.test.ts | 5 +- src/id.ts | 4 +- src/index.ts | 1 - src/keys.test.ts | 20 ++-- src/keys.ts | 13 +-- src/sign.test.ts | 9 +- src/sign.ts | 19 ++-- src/util.test.ts | 1 + src/validate.test.ts | 1 + 16 files changed, 299 insertions(+), 94 deletions(-) create mode 100644 .prettierignore delete mode 100644 src/constants.ts diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..d383c56 --- /dev/null +++ b/.prettierignore @@ -0,0 +1 @@ +testdata diff --git a/package-lock.json b/package-lock.json index 8e07c2f..a453df0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,12 +12,140 @@ "@noble/secp256k1": "^3.0.0" }, "devDependencies": { + "@trivago/prettier-plugin-sort-imports": "^5.2.2", "@types/node": "^24.9.1", + "prettier": "^3.5.3", "typescript": "^5.9.3", "vite": "^7.1.12", "vitest": "^4.0.2" } }, + "node_modules/@babel/code-frame": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.27.1", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/generator": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.5.tgz", + "integrity": "sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.28.5", + "@babel/types": "^7.28.5", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.5.tgz", + "integrity": "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.5" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/template": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", + "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/parser": "^7.27.2", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.5.tgz", + "integrity": "sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.5", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.28.5", + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.5", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.5.tgz", + "integrity": "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@esbuild/aix-ppc64": { "version": "0.25.11", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.11.tgz", @@ -460,6 +588,27 @@ "node": ">=18" } }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/@jridgewell/sourcemap-codec": { "version": "1.5.5", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", @@ -467,6 +616,17 @@ "dev": true, "license": "MIT" }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, "node_modules/@noble/hashes": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-2.0.1.tgz", @@ -803,6 +963,41 @@ "dev": true, "license": "MIT" }, + "node_modules/@trivago/prettier-plugin-sort-imports": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/@trivago/prettier-plugin-sort-imports/-/prettier-plugin-sort-imports-5.2.2.tgz", + "integrity": "sha512-fYDQA9e6yTNmA13TLVSA+WMQRc5Bn/c0EUBditUHNfMMxN7M82c38b1kEggVE3pLpZ0FwkwJkUEKMiOi52JXFA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@babel/generator": "^7.26.5", + "@babel/parser": "^7.26.7", + "@babel/traverse": "^7.26.7", + "@babel/types": "^7.26.7", + "javascript-natural-sort": "^0.7.1", + "lodash": "^4.17.21" + }, + "engines": { + "node": ">18.12" + }, + "peerDependencies": { + "@vue/compiler-sfc": "3.x", + "prettier": "2.x - 3.x", + "prettier-plugin-svelte": "3.x", + "svelte": "4.x || 5.x" + }, + "peerDependenciesMeta": { + "@vue/compiler-sfc": { + "optional": true + }, + "prettier-plugin-svelte": { + "optional": true + }, + "svelte": { + "optional": true + } + } + }, "node_modules/@types/chai": { "version": "5.2.3", "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz", @@ -1089,6 +1284,40 @@ "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, + "node_modules/javascript-natural-sort": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/javascript-natural-sort/-/javascript-natural-sort-0.7.1.tgz", + "integrity": "sha512-nO6jcEfZWQXDhOiBtG2KvKyEptz7RVbpGP4vTD2hLBdmNQSsCiicO2Ioinv6UI4y9ukqnBpy+XZ9H6uLNgJTlw==", + "dev": true, + "license": "MIT" + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true, + "license": "MIT" + }, "node_modules/magic-string": { "version": "0.30.21", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", @@ -1181,6 +1410,22 @@ "node": "^10 || ^12 || >=14" } }, + "node_modules/prettier": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz", + "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, "node_modules/rollup": { "version": "4.52.5", "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.52.5.tgz", diff --git a/package.json b/package.json index 297e051..d33ce5f 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,8 @@ ], "scripts": { "build": "tsc", + "lint": "tsc --noEmit", + "format": "prettier -w src", "test": "vitest", "test:run": "vitest run" }, @@ -23,9 +25,28 @@ "@noble/secp256k1": "^3.0.0" }, "devDependencies": { + "@trivago/prettier-plugin-sort-imports": "^5.2.2", "@types/node": "^24.9.1", + "prettier": "^3.5.3", "typescript": "^5.9.3", "vite": "^7.1.12", "vitest": "^4.0.2" + }, + "prettier": { + "importOrder": [ + "^react$", + "^react-dom(.*)$", + "^react(.*)$", + "^@(?!(/))(.*)$", + "^(?!@|[.])(.*)$", + "^@(/)(.*)$", + "^[./]" + ], + "importOrderSeparation": true, + "importOrderSortSpecifiers": true, + "plugins": [ + "@trivago/prettier-plugin-sort-imports" + ] } + } diff --git a/src/constants.ts b/src/constants.ts deleted file mode 100644 index 0bf192d..0000000 --- a/src/constants.ts +++ /dev/null @@ -1,11 +0,0 @@ -/** - * Matches 64-character lowercase hexadecimal strings. - * Used for validating event IDs and cryptographic keys. - */ -export const HEX_64_PATTERN = /^[a-f0-9]{64}$/; - -/** - * Matches 128-character lowercase hexadecimal strings. - * Used for validating signatures. - */ -export const HEX_128_PATTERN = /^[a-f0-9]{128}$/; diff --git a/src/errors.ts b/src/errors.ts index 6818dfb..a9c1880 100644 --- a/src/errors.ts +++ b/src/errors.ts @@ -1,43 +1,3 @@ -/** - * Public key is not 64 lowercase hex characters. - */ -export class MalformedPubKeyError extends Error { - constructor() { - super("public key must be 64 lowercase hex characters"); - this.name = "MalformedPubKeyError"; - } -} - -/** - * Private key is not 64 lowercase hex characters. - */ -export class MalformedPrivKeyError extends Error { - constructor() { - super("private key must be 64 lowercase hex characters"); - this.name = "MalformedPrivKeyError"; - } -} - -/** - * Event ID is not 64 hex characters. - */ -export class MalformedIDError extends Error { - constructor() { - super("event id must be 64 hex characters"); - this.name = "MalformedIDError"; - } -} - -/** - * Event signature is not 128 hex characters. - */ -export class MalformedSigError extends Error { - constructor() { - super("event signature must be 128 hex characters"); - this.name = "MalformedSigError"; - } -} - /** * Event tag contains fewer than two elements. */ diff --git a/src/event.test.ts b/src/event.test.ts index b0b0462..31d1451 100644 --- a/src/event.test.ts +++ b/src/event.test.ts @@ -1,2 +1,3 @@ import { test } from "vitest"; + test("placeholder", () => {}); diff --git a/src/filter.test.ts b/src/filter.test.ts index b0b0462..31d1451 100644 --- a/src/filter.test.ts +++ b/src/filter.test.ts @@ -1,2 +1,3 @@ import { test } from "vitest"; + test("placeholder", () => {}); diff --git a/src/id.test.ts b/src/id.test.ts index 743eb5b..5497383 100644 --- a/src/id.test.ts +++ b/src/id.test.ts @@ -1,7 +1,8 @@ -import { describe, test, expect } from "vitest"; +import { describe, expect, test } from "vitest"; + import { EventID } from "./id"; -import { testEvent, testPK } from "./util.test"; import type { EventData } from "./types"; +import { testEvent, testPK } from "./util.test"; interface IDTestCase { name: string; diff --git a/src/id.ts b/src/id.ts index 0721980..e2a4a49 100644 --- a/src/id.ts +++ b/src/id.ts @@ -1,4 +1,6 @@ import { sha256 } from "@noble/hashes/sha2.js"; +import { bytesToHex } from "@noble/hashes/utils.js"; + import type { EventData } from "./types"; /** @@ -27,7 +29,7 @@ function serialize(event: EventData): string { function getID(event: EventData): string { const serialized = serialize(event); const hash = sha256(new TextEncoder().encode(serialized)); - return Buffer.from(hash).toString("hex"); + return bytesToHex(hash); } export const EventID = { diff --git a/src/index.ts b/src/index.ts index 528df97..82e8768 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,5 +1,4 @@ export * from "./types"; -export * from "./constants"; export * from "./errors"; export { Event } from "./event"; export { Keys } from "./keys"; diff --git a/src/keys.test.ts b/src/keys.test.ts index f1f118f..195b5b8 100644 --- a/src/keys.test.ts +++ b/src/keys.test.ts @@ -1,7 +1,9 @@ -import { describe, test, expect } from "vitest"; +import { describe, expect, test } from "vitest"; + import { Keys } from "./keys"; -import { HEX_64_PATTERN } from "./constants"; -import { testSK, testPK } from "./util.test"; +import { testPK, testSK } from "./util.test"; + +const HEX_64_PATTERN = /^[a-f0-9]{64}$/; describe("Keys.generatePrivate", () => { test("returns 64 hex characters", () => { @@ -24,7 +26,7 @@ describe("Keys.getPublic", () => { test("throws on invalid private key - too short", () => { expect(() => Keys.getPublicKey("abc123")).toThrow( - "private key must be 64 lowercase hex characters", + /"secret key" expected.*/, ); }); @@ -33,14 +35,6 @@ describe("Keys.getPublic", () => { Keys.getPublicKey( "zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz", ), - ).toThrow("private key must be 64 lowercase hex characters"); - }); - - test("throws on invalid private key - uppercase", () => { - expect(() => - Keys.getPublicKey( - "F43A0435F69529F310BBD1D6263D2FBF0977F54BFE2310CC37AE5904B83BB167", - ), - ).toThrow("private key must be 64 lowercase hex characters"); + ).toThrow(/hex string expected,.*/); }); }); diff --git a/src/keys.ts b/src/keys.ts index b5795f1..e2622e6 100644 --- a/src/keys.ts +++ b/src/keys.ts @@ -1,6 +1,5 @@ +import { bytesToHex, hexToBytes } from "@noble/hashes/utils.js"; import { schnorr } from "@noble/secp256k1"; -import { HEX_64_PATTERN } from "./constants"; -import { MalformedPrivKeyError } from "./errors"; /** * Generates a new random secp256k1 private key. @@ -8,7 +7,7 @@ import { MalformedPrivKeyError } from "./errors"; */ function generatePrivateKey(): string { const { secretKey } = schnorr.keygen(); - return Buffer.from(secretKey).toString("hex"); + return bytesToHex(secretKey); } /** @@ -18,14 +17,10 @@ function generatePrivateKey(): string { * @throws {MalformedPrivKeyError} If private key is not 64 lowercase hex characters */ function getPublicKey(privateKey: string): string { - if (!HEX_64_PATTERN.test(privateKey)) { - throw new MalformedPrivKeyError(); - } - - const privateKeyBytes = Buffer.from(privateKey, "hex"); + const privateKeyBytes = hexToBytes(privateKey); const publicKeyBytes = schnorr.getPublicKey(privateKeyBytes); - return Buffer.from(publicKeyBytes).toString("hex"); + return bytesToHex(publicKeyBytes); } export const Keys = { diff --git a/src/sign.test.ts b/src/sign.test.ts index 4e51209..35ab625 100644 --- a/src/sign.test.ts +++ b/src/sign.test.ts @@ -1,6 +1,7 @@ -import { describe, test, expect } from "vitest"; +import { describe, expect, test } from "vitest"; + import { Sign } from "./sign"; -import { testSK, testEvent } from "./util.test"; +import { testEvent, testSK } from "./util.test"; describe("Sign.sign", () => { test("produces correct signature", () => { @@ -10,13 +11,13 @@ describe("Sign.sign", () => { test("throws on invalid event ID", () => { expect(() => Sign.sign("thisisabadeventid", testSK)).toThrow( - "event id must be 64 hex characters", + /hex string expected,.*/, ); }); test("throws on invalid private key", () => { expect(() => Sign.sign(testEvent.id, "thisisabadsecretkey")).toThrow( - "private key must be 64 lowercase hex characters", + /hex string expected,.*/, ); }); }); diff --git a/src/sign.ts b/src/sign.ts index 61c9c31..2304535 100644 --- a/src/sign.ts +++ b/src/sign.ts @@ -1,8 +1,8 @@ -import { hashes as secp_hashes } from "@noble/secp256k1"; -import { schnorr } from "@noble/secp256k1"; import { hmac } from "@noble/hashes/hmac.js"; import { sha256 } from "@noble/hashes/sha2.js"; -import { MalformedIDError, MalformedPrivKeyError } from "./errors"; +import { bytesToHex, hexToBytes } from "@noble/hashes/utils.js"; +import { hashes as secp_hashes } from "@noble/secp256k1"; +import { schnorr } from "@noble/secp256k1"; secp_hashes.hmacSha256 = (key, msg) => hmac(sha256, key, msg); secp_hashes.sha256 = sha256; @@ -16,19 +16,12 @@ secp_hashes.sha256 = sha256; * @throws {MalformedPrivKeyError} If private key is not 64 lowercase hex characters */ function sign(eventID: string, privateKey: string): string { - const privateKeyBytes = Buffer.from(privateKey, "hex"); - if (privateKeyBytes.length !== 32) { - throw new MalformedPrivKeyError(); - } - - const idBytes = Buffer.from(eventID, "hex"); - if (idBytes.length !== 32) { - throw new MalformedIDError(); - } + const privateKeyBytes = hexToBytes(privateKey); + const idBytes = hexToBytes(eventID); const auxRand = sha256(privateKeyBytes); const signature = schnorr.sign(idBytes, privateKeyBytes, auxRand); - return Buffer.from(signature).toString("hex"); + return bytesToHex(signature); } export const Sign = { diff --git a/src/util.test.ts b/src/util.test.ts index 37975db..c05524f 100644 --- a/src/util.test.ts +++ b/src/util.test.ts @@ -1,4 +1,5 @@ import { test } from "vitest"; + import type { EventData } from "./types"; test("placeholder", () => {}); diff --git a/src/validate.test.ts b/src/validate.test.ts index b0b0462..31d1451 100644 --- a/src/validate.test.ts +++ b/src/validate.test.ts @@ -1,2 +1,3 @@ import { test } from "vitest"; + test("placeholder", () => {});