From f5ada98f91f7ae174d13358fc02f7ee8d453d568 Mon Sep 17 00:00:00 2001 From: Ayush Date: Fri, 25 Oct 2024 06:02:03 +0530 Subject: [PATCH 1/7] added testing for cookbook wallet section --- .github/workflows/code:test.yml | 63 + .gitignore | 2 + .../cookbook/wallets/check-publickey.ts} | 15 +- .../cookbook/wallets/create-keypair.ts | 5 + .../cookbook/wallets/generate-mnemonic.ts | 5 + .../wallets/generate-vanity-address.sh | 1 + .../wallets/restore-bip39-mnemonic.ts | 15 + .../wallets/restore-bip44-mnemonic.ts | 29 + .../wallets/restore-keypair-from-bs58.ts | 10 + .../wallets/restore-keypair-from-bytes.ts | 12 + code/content/cookbook/wallets/sign-message.ts | 27 + .../wallets/tests/check-publick.test.ts | 121 ++ .../wallets/tests/create-keypair.test.ts | 103 ++ .../wallets/tests/generate-mnemonic.test.ts | 49 + .../tests/restore-from-mnemonic.test.ts | 118 ++ .../wallets/tests/restore-keypair.test.ts | 68 ++ .../wallets/tests/sign-message.test.ts | 122 ++ .../wallets/tests/verify-keypair.test.ts | 26 + .../cookbook/wallets/verify-keypair.ts | 17 + code/package.json | 15 +- code/tsconfig.json | 15 + content/cookbook/wallets/check-publickey.md | 13 +- content/cookbook/wallets/create-keypair.md | 2 +- content/cookbook/wallets/generate-mnemonic.md | 2 +- .../wallets/generate-vanity-address.md | 2 +- .../cookbook/wallets/restore-from-mnemonic.md | 27 +- content/cookbook/wallets/restore-keypair.md | 8 +- content/cookbook/wallets/sign-message.md | 23 +- content/cookbook/wallets/verify-keypair.md | 2 +- package.json | 9 +- pnpm-lock.yaml | 1024 ++++++++++++++--- pnpm-workspace.yaml | 3 + src/utils/code-import.ts | 20 +- turbo.json | 44 + 34 files changed, 1795 insertions(+), 222 deletions(-) create mode 100644 .github/workflows/code:test.yml rename code/{cookbook/wallets/check-public-key.ts => content/cookbook/wallets/check-publickey.ts} (63%) create mode 100644 code/content/cookbook/wallets/create-keypair.ts create mode 100644 code/content/cookbook/wallets/generate-mnemonic.ts create mode 100644 code/content/cookbook/wallets/generate-vanity-address.sh create mode 100644 code/content/cookbook/wallets/restore-bip39-mnemonic.ts create mode 100644 code/content/cookbook/wallets/restore-bip44-mnemonic.ts create mode 100644 code/content/cookbook/wallets/restore-keypair-from-bs58.ts create mode 100644 code/content/cookbook/wallets/restore-keypair-from-bytes.ts create mode 100644 code/content/cookbook/wallets/sign-message.ts create mode 100644 code/content/cookbook/wallets/tests/check-publick.test.ts create mode 100644 code/content/cookbook/wallets/tests/create-keypair.test.ts create mode 100644 code/content/cookbook/wallets/tests/generate-mnemonic.test.ts create mode 100644 code/content/cookbook/wallets/tests/restore-from-mnemonic.test.ts create mode 100644 code/content/cookbook/wallets/tests/restore-keypair.test.ts create mode 100644 code/content/cookbook/wallets/tests/sign-message.test.ts create mode 100644 code/content/cookbook/wallets/tests/verify-keypair.test.ts create mode 100644 code/content/cookbook/wallets/verify-keypair.ts create mode 100644 code/tsconfig.json create mode 100644 pnpm-workspace.yaml create mode 100644 turbo.json diff --git a/.github/workflows/code:test.yml b/.github/workflows/code:test.yml new file mode 100644 index 000000000..7da73a0ff --- /dev/null +++ b/.github/workflows/code:test.yml @@ -0,0 +1,63 @@ +name: Code Snippets testing + +on: + pull_request: + branches: [main] + schedule: + - cron: "0 0 * * *" # Run daily at midnight UTC + +jobs: + test: + runs-on: ubuntu-latest + + strategy: + matrix: + node-version: [20] + + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 # Fetch all history for all branches and tags + + - name: Get changed files + id: changed-files + uses: tj-actions/changed-files@v39 + with: + files: "**/*.ts" + + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node-version }} + + - name: Install pnpm + uses: pnpm/action-setup@v2 + with: + version: 9 + + - run: cd code + + - name: Install dependencies + run: pnpm install + + - name: Run tests on changed files + if: github.event_name == 'pull_request' + run: | + for file in ${{ steps.changed-files.outputs.all_changed_files }}; do + if [[ $file == *.test.ts ]]; then + echo "Running tests for $file" + node --import tsx --test $file + elif [[ $file == *.ts ]]; then + test_file="${file%.ts}.test.ts" + if [ -f "$test_file" ]; then + echo "Running tests for $test_file" + node --import tsx --test $test_file + else + echo "No test file found for $file" + fi + fi + done + + - name: Run all tests + if: github.event_name == 'schedule' + run: pnpm turbo test diff --git a/.gitignore b/.gitignore index ac91c0ca7..3c30833e7 100644 --- a/.gitignore +++ b/.gitignore @@ -106,6 +106,8 @@ package-lock.json # translations are stored in the `i18n` via crowdin i18n +# turborepo +.turbo # code-import code/node_modules diff --git a/code/cookbook/wallets/check-public-key.ts b/code/content/cookbook/wallets/check-publickey.ts similarity index 63% rename from code/cookbook/wallets/check-public-key.ts rename to code/content/cookbook/wallets/check-publickey.ts index f5b4c8392..0151a9703 100644 --- a/code/cookbook/wallets/check-public-key.ts +++ b/code/content/cookbook/wallets/check-publickey.ts @@ -1,4 +1,4 @@ -import { PublicKey } from "@solana/web3.js"; +import { PublicKey, Keypair } from "@solana/web3.js"; // Note that Keypair.generate() will always give a public key that is valid for users @@ -15,5 +15,14 @@ const offCurveAddress = new PublicKey( // Not on the ed25519 curve, therefore not suitable for users console.log(PublicKey.isOnCurve(offCurveAddress.toBytes())); -// Not a valid public key -const errorPubkey = new PublicKey("testPubkey"); +let errorPubkey; +try { + // Not a valid public key + errorPubkey = new PublicKey("testPubkey"); +} catch (err) { + // Error will be caught here +} + +const onCurve = PublicKey.isOnCurve(key.toBytes()); + +export { key, onCurve, offCurveAddress, errorPubkey }; diff --git a/code/content/cookbook/wallets/create-keypair.ts b/code/content/cookbook/wallets/create-keypair.ts new file mode 100644 index 000000000..d08330522 --- /dev/null +++ b/code/content/cookbook/wallets/create-keypair.ts @@ -0,0 +1,5 @@ +import { Keypair } from "@solana/web3.js"; + +const keypair = Keypair.generate(); + +export { keypair }; diff --git a/code/content/cookbook/wallets/generate-mnemonic.ts b/code/content/cookbook/wallets/generate-mnemonic.ts new file mode 100644 index 000000000..03d398d7b --- /dev/null +++ b/code/content/cookbook/wallets/generate-mnemonic.ts @@ -0,0 +1,5 @@ +import * as bip39 from "bip39"; + +const mnemonic = bip39.generateMnemonic(); + +export { mnemonic }; diff --git a/code/content/cookbook/wallets/generate-vanity-address.sh b/code/content/cookbook/wallets/generate-vanity-address.sh new file mode 100644 index 000000000..8dfe31ecf --- /dev/null +++ b/code/content/cookbook/wallets/generate-vanity-address.sh @@ -0,0 +1 @@ +solana-keygen grind --starts-with e1v1s:1 \ No newline at end of file diff --git a/code/content/cookbook/wallets/restore-bip39-mnemonic.ts b/code/content/cookbook/wallets/restore-bip39-mnemonic.ts new file mode 100644 index 000000000..38a2f6af3 --- /dev/null +++ b/code/content/cookbook/wallets/restore-bip39-mnemonic.ts @@ -0,0 +1,15 @@ +import { Keypair } from "@solana/web3.js"; +import * as bip39 from "bip39"; + +const mnemonic = + "pill tomorrow foster begin walnut borrow virtual kick shift mutual shoe scatter"; + +// arguments: (mnemonic, password) +const seed = bip39.mnemonicToSeedSync(mnemonic, ""); +const keypair = Keypair.fromSeed(seed.slice(0, 32)); + +console.log(`${keypair.publicKey.toBase58()}`); + +// output: 5ZWj7a1f8tWkjBESHKgrLmXshuXxqeY9SYcfbshpAqPG + +export { keypair }; diff --git a/code/content/cookbook/wallets/restore-bip44-mnemonic.ts b/code/content/cookbook/wallets/restore-bip44-mnemonic.ts new file mode 100644 index 000000000..2e04f446c --- /dev/null +++ b/code/content/cookbook/wallets/restore-bip44-mnemonic.ts @@ -0,0 +1,29 @@ +import { Keypair } from "@solana/web3.js"; +import { HDKey } from "micro-key-producer/slip10.js"; +import * as bip39 from "bip39"; + +type Wallet = { + path: string; + keypair: Keypair; + publicKey: string; +}; + +const mnemonic = + "neither lonely flavor argue grass remind eye tag avocado spot unusual intact"; + +const seed = bip39.mnemonicToSeedSync(mnemonic, ""); +const hd = HDKey.fromMasterSeed(seed.toString("hex")); + +const wallets: Wallet[] = []; + +for (let i = 0; i < 10; i++) { + const path = `m/44'/501'/${i}'/0'`; + const keypair = Keypair.fromSeed(hd.derive(path).privateKey); + wallets.push({ + path, + keypair, + publicKey: keypair.publicKey.toBase58(), + }); +} + +export { mnemonic, wallets }; diff --git a/code/content/cookbook/wallets/restore-keypair-from-bs58.ts b/code/content/cookbook/wallets/restore-keypair-from-bs58.ts new file mode 100644 index 000000000..ef8ac59e8 --- /dev/null +++ b/code/content/cookbook/wallets/restore-keypair-from-bs58.ts @@ -0,0 +1,10 @@ +import { Keypair } from "@solana/web3.js"; +import bs58 from "bs58"; + +const keypair = Keypair.fromSecretKey( + bs58.decode( + "4UzFMkVbk1q6ApxvDS8inUxg4cMBxCQRVXRx5msqQyktbi1QkJkt574Jda6BjZThSJi54CHfVoLFdVFX8XFn233L", + ), +); + +export { keypair as bs58Keypair }; diff --git a/code/content/cookbook/wallets/restore-keypair-from-bytes.ts b/code/content/cookbook/wallets/restore-keypair-from-bytes.ts new file mode 100644 index 000000000..0ef68f10d --- /dev/null +++ b/code/content/cookbook/wallets/restore-keypair-from-bytes.ts @@ -0,0 +1,12 @@ +import { Keypair } from "@solana/web3.js"; + +const keypair = Keypair.fromSecretKey( + Uint8Array.from([ + 174, 47, 154, 16, 202, 193, 206, 113, 199, 190, 53, 133, 169, 175, 31, 56, + 222, 53, 138, 189, 224, 216, 117, 173, 10, 149, 53, 45, 73, 251, 237, 246, + 15, 185, 186, 82, 177, 240, 148, 69, 241, 227, 167, 80, 141, 89, 240, 121, + 121, 35, 172, 247, 68, 251, 226, 218, 48, 63, 176, 109, 168, 89, 238, 135, + ]), +); + +export { keypair as bytesKeypair }; diff --git a/code/content/cookbook/wallets/sign-message.ts b/code/content/cookbook/wallets/sign-message.ts new file mode 100644 index 000000000..803fa59cc --- /dev/null +++ b/code/content/cookbook/wallets/sign-message.ts @@ -0,0 +1,27 @@ +import { Keypair } from "@solana/web3.js"; +import * as ed from "@noble/ed25519"; +import { sha512 } from "@noble/hashes/sha512"; +import { utf8ToBytes } from "@noble/hashes/utils"; + +// Enable synchronous methods for noble-ed25519 +ed.etc.sha512Sync = (...m) => sha512(ed.etc.concatBytes(...m)); + +const keypair = Keypair.fromSecretKey( + Uint8Array.from([ + 174, 47, 154, 16, 202, 193, 206, 113, 199, 190, 53, 133, 169, 175, 31, 56, + 222, 53, 138, 189, 224, 216, 117, 173, 10, 149, 53, 45, 73, 251, 237, 246, + 15, 185, 186, 82, 177, 240, 148, 69, 241, 227, 167, 80, 141, 89, 240, 121, + 121, 35, 172, 247, 68, 251, 226, 218, 48, 63, 176, 109, 168, 89, 238, 135, + ]), +); + +const message = "The quick brown fox jumps over the lazy dog"; +const messageBytes = utf8ToBytes(message); + +// Sign using noble-ed25519 +const signature = ed.sign(messageBytes, keypair.secretKey.slice(0, 32)); + +// Verify using noble-ed25519 +const result = ed.verify(signature, messageBytes, keypair.publicKey.toBytes()); + +export { keypair, message, messageBytes, signature, result }; diff --git a/code/content/cookbook/wallets/tests/check-publick.test.ts b/code/content/cookbook/wallets/tests/check-publick.test.ts new file mode 100644 index 000000000..c2f033c59 --- /dev/null +++ b/code/content/cookbook/wallets/tests/check-publick.test.ts @@ -0,0 +1,121 @@ +import { describe, test } from "node:test"; +import { strict as assert } from "node:assert"; +import { PublicKey } from "@solana/web3.js"; +import { key, onCurve, offCurveAddress } from "../check-publickey"; + +describe("Check PublicKey", async () => { + test("should properly instantiate valid key", () => { + assert.ok(key instanceof PublicKey, "key should be a PublicKey instance"); + assert.equal( + key.toString(), + "5oNDL3swdJJF1g9DzJiZ4ynHXgszjAEpUkxVYejchzrY", + "key should match the expected value", + ); + }); + + test("should verify key is on ed25519 curve", () => { + assert.equal(onCurve, true, "key should be on the ed25519 curve"); + // Double-check the curve status directly + assert.equal( + PublicKey.isOnCurve(key.toBytes()), + true, + "key should be verified on curve through direct check", + ); + }); + + test("should identify valid address not on curve", () => { + assert.ok( + offCurveAddress instanceof PublicKey, + "offCurveAddress should be a PublicKey instance", + ); + assert.equal( + offCurveAddress.toString(), + "4BJXYkfvg37zEmBbsacZjeQDpTNx91KppxFJxRqrz48e", + "offCurveAddress should match the expected value", + ); + assert.equal( + PublicKey.isOnCurve(offCurveAddress.toBytes()), + false, + "offCurveAddress should not be on the curve", + ); + }); + + test("should throw error for invalid public key format", () => { + assert.throws( + () => { + new PublicKey("testPubkey"); + }, + { + name: "Error", + message: /Invalid public key/, + }, + "Should throw an error for invalid public key format", + ); + }); + + test("should throw error for empty string public key", () => { + assert.throws( + () => { + new PublicKey(""); + }, + { + name: "Error", + message: /Invalid public key/, + }, + "Should throw an error for empty string", + ); + }); + + test("should handle conversion between different formats", () => { + const keyString = key.toString(); + const keyBytes = key.toBytes(); + const keyBase58 = key.toBase58(); + + // Test string conversion + assert.equal( + new PublicKey(keyString).toString(), + keyString, + "should maintain equality when converting through string", + ); + + // Test bytes conversion + assert.deepEqual( + new PublicKey(keyBytes).toBytes(), + keyBytes, + "should maintain equality when converting through bytes", + ); + + // Test base58 conversion + assert.equal( + new PublicKey(keyBase58).toBase58(), + keyBase58, + "should maintain equality when converting through base58", + ); + }); + + test("should verify key bytes are correct length", () => { + const keyBytes = key.toBytes(); + assert.equal( + keyBytes.length, + 32, + "public key bytes should be exactly 32 bytes", + ); + }); + + test("should maintain equality for same public key", () => { + const sameKey = new PublicKey(key.toString()); + assert.ok( + key.equals(sameKey), + "same public key should be equal when compared", + ); + }); + + test("should identify different public keys as not equal", () => { + const differentKey = new PublicKey(offCurveAddress.toString()); + assert.equal( + key.equals(differentKey), + false, + "different public keys should not be equal", + ); + }); +}); diff --git a/code/content/cookbook/wallets/tests/create-keypair.test.ts b/code/content/cookbook/wallets/tests/create-keypair.test.ts new file mode 100644 index 000000000..cccf116e1 --- /dev/null +++ b/code/content/cookbook/wallets/tests/create-keypair.test.ts @@ -0,0 +1,103 @@ +import { describe, test } from "node:test"; +import assert from "node:assert"; +import { keypair } from "../create-keypair"; +import { Keypair, PublicKey } from "@solana/web3.js"; + +describe("Create Keypair", async () => { + test("should be a valid Keypair instance", () => { + assert.ok( + keypair instanceof Keypair, + "keypair should be instance of Keypair", + ); + }); + + test("should have correct secretKey length", () => { + assert.equal(keypair.secretKey.length, 64, "secretKey should be 64 bytes"); + }); + + test("should have correct publicKey length", () => { + assert.equal( + keypair.publicKey.toBytes().length, + 32, + "publicKey should be 32 bytes", + ); + }); + + test("should have valid public key instance", () => { + assert.ok( + keypair.publicKey instanceof PublicKey, + "publicKey should be instance of PublicKey", + ); + }); + + test("should derive same public key from secret key", () => { + const derivedKeypair = Keypair.fromSecretKey(keypair.secretKey); + assert.ok( + keypair.publicKey.equals(derivedKeypair.publicKey), + "public key should match derived public key", + ); + }); + + test("should generate correct base58 public key string", () => { + const pubkeyStr = keypair.publicKey.toBase58(); + assert.ok( + pubkeyStr.length === 44 || pubkeyStr.length === 43, + "public key string should be 43 or 44 characters", + ); + }); + + test("should have valid secret key format", () => { + assert.ok( + keypair.secretKey instanceof Uint8Array, + "secretKey should be Uint8Array", + ); + }); + + test("should have matching public key in last 32 bytes of secret key", () => { + const pubkeyFromSecret = keypair.secretKey.slice(32); + assert.deepEqual( + pubkeyFromSecret, + keypair.publicKey.toBytes(), + "last 32 bytes of secret key should match public key", + ); + }); + + test("should generate unique keypairs", () => { + const anotherKeypair = Keypair.generate(); + assert.ok( + !keypair.publicKey.equals(anotherKeypair.publicKey), + "different keypairs should have different public keys", + ); + assert.ok( + !Buffer.from(keypair.secretKey).equals( + Buffer.from(anotherKeypair.secretKey), + ), + "different keypairs should have different secret keys", + ); + }); + + test("should maintain consistency when serializing and deserializing", () => { + const secretKeyBytes = keypair.secretKey; + const restoredKeypair = Keypair.fromSecretKey(secretKeyBytes); + + assert.ok( + keypair.publicKey.equals(restoredKeypair.publicKey), + "restored keypair should have same public key", + ); + assert.deepEqual( + keypair.secretKey, + restoredKeypair.secretKey, + "restored keypair should have same secret key", + ); + }); + + test("should correctly handle base58 encoding/decoding of public key", () => { + const base58Pubkey = keypair.publicKey.toBase58(); + const decodedPubkey = new PublicKey(base58Pubkey); + + assert.ok( + keypair.publicKey.equals(decodedPubkey), + "public key should maintain equality after base58 encoding/decoding", + ); + }); +}); diff --git a/code/content/cookbook/wallets/tests/generate-mnemonic.test.ts b/code/content/cookbook/wallets/tests/generate-mnemonic.test.ts new file mode 100644 index 000000000..5a3a4220f --- /dev/null +++ b/code/content/cookbook/wallets/tests/generate-mnemonic.test.ts @@ -0,0 +1,49 @@ +import { strict as assert } from "node:assert"; +import { describe, it } from "node:test"; +import * as bip39 from "bip39"; +import { mnemonic } from "../generate-mnemonic"; + +describe("Mnemonic Generation", async () => { + it("should generate a valid mnemonic phrase", () => { + assert.ok( + bip39.validateMnemonic(mnemonic), + "Generated mnemonic should be valid", + ); + }); + + it("should generate a 12-word mnemonic by default", () => { + const words = mnemonic.split(" "); + assert.equal(words.length, 12, "Mnemonic should contain exactly 12 words"); + }); + + it("should use only valid BIP39 words", () => { + const wordList = bip39.wordlists.english; + const mnemonicWords = mnemonic.split(" "); + + mnemonicWords.forEach(word => { + assert.ok( + wordList.includes(word), + `Word "${word}" should be in the BIP39 wordlist`, + ); + }); + }); + + it("should generate unique mnemonics on each call", () => { + const anotherMnemonic = bip39.generateMnemonic(); + assert.notEqual( + mnemonic, + anotherMnemonic, + "Generated mnemonics should be unique", + ); + }); + + it("should generate mnemonic with correct entropy", () => { + // BIP39 mnemonics are generated from 128 bits of entropy for 12 words + const entropy = bip39.mnemonicToEntropy(mnemonic); + assert.equal( + entropy.length, + 32, // 128 bits = 32 hex characters + "Mnemonic should be generated from 128 bits of entropy", + ); + }); +}); diff --git a/code/content/cookbook/wallets/tests/restore-from-mnemonic.test.ts b/code/content/cookbook/wallets/tests/restore-from-mnemonic.test.ts new file mode 100644 index 000000000..19712cab3 --- /dev/null +++ b/code/content/cookbook/wallets/tests/restore-from-mnemonic.test.ts @@ -0,0 +1,118 @@ +import { strict as assert } from "node:assert"; +import { describe, test } from "node:test"; +import { mnemonic, wallets } from "../restore-bip44-mnemonic"; +import { keypair } from "../restore-bip39-mnemonic"; +import * as bip39 from "bip39"; +import { Keypair } from "@solana/web3.js"; + +describe("Restore Keypair from Mnemonic words", async () => { + test("should generate correct deterministic keypair from bip 39", () => { + // Known correct public key for the given mnemonic + const expectedPublicKey = "5ZWj7a1f8tWkjBESHKgrLmXshuXxqeY9SYcfbshpAqPG"; + const mnemonic = + "pill tomorrow foster begin walnut borrow virtual kick shift mutual shoe scatter"; + + // Test generated public key matches expected + assert.equal( + keypair.publicKey.toBase58(), + expectedPublicKey, + "Generated public key should match expected value", + ); + + // Verify mnemonic is valid + assert.ok( + bip39.validateMnemonic(mnemonic), + "Mnemonic should be valid BIP39 phrase", + ); + + // Verify keypair secret key properties + assert.equal(keypair.secretKey.length, 64, "Secret key should be 64 bytes"); + + // Regenerate keypair to verify deterministic generation + const seedVerify = bip39.mnemonicToSeedSync(mnemonic, ""); + const keypairVerify = Keypair.fromSeed( + new Uint8Array(seedVerify).slice(0, 32), + ); + + assert.equal( + keypairVerify.publicKey.toBase58(), + keypair.publicKey.toBase58(), + "Keypair generation should be deterministic", + ); + + // Verify secret key is Uint8Array + assert.ok( + keypair.secretKey instanceof Uint8Array, + "Secret key should be Uint8Array", + ); + + // Verify first 32 bytes of secret key match seed slice + const seedSlice = new Uint8Array( + bip39.mnemonicToSeedSync(mnemonic, ""), + ).slice(0, 32); + assert.deepEqual( + keypair.secretKey.slice(0, 32), + seedSlice, + "First 32 bytes of secret key should match seed slice", + ); + }); + + test("should generate correct set of deterministic wallets from bip 44", () => { + // Known correct public keys for the first three wallets + const expectedPublicKeys = [ + "5vftMkHL72JaJG6ExQfGAsT2uGVHpRR7oTNUPMs68Y2N", + "GcXbfQ5yY3uxCyBNDPBbR5FjumHf89E7YHXuULfGDBBv", + "7QPgyQwNLqnoSwHEuK8wKy2Y3Ani6EHoZRihTuWkwxbc", + ]; + + // Test the total number of wallets + assert.equal(wallets.length, 10, "Should generate exactly 10 wallets"); + + // Verify mnemonic is valid + assert.ok( + bip39.validateMnemonic(mnemonic), + "Mnemonic should be valid BIP39 phrase", + ); + + // Test the first three wallets match expected public keys + expectedPublicKeys.forEach((expectedKey, index) => { + assert.equal( + wallets[index].publicKey, + expectedKey, + `Wallet ${index} should have correct public key`, + ); + }); + + // Test each wallet's properties + wallets.forEach((wallet, index) => { + // Verify derivation path format + assert.equal( + wallet.path, + `m/44'/501'/${index}'/0'`, + `Wallet ${index} should have correct derivation path`, + ); + + // Verify keypair secret key length + assert.equal( + wallet.keypair.secretKey.length, + 64, + `Wallet ${index} secret key should be 64 bytes`, + ); + + // Verify public key matches keypair + assert.equal( + wallet.keypair.publicKey.toBase58(), + wallet.publicKey, + `Wallet ${index} public key should match keypair`, + ); + + // Verify public keys are unique + const duplicates = wallets.filter(w => w.publicKey === wallet.publicKey); + assert.equal( + duplicates.length, + 1, + `Wallet ${index} public key should be unique`, + ); + }); + }); +}); diff --git a/code/content/cookbook/wallets/tests/restore-keypair.test.ts b/code/content/cookbook/wallets/tests/restore-keypair.test.ts new file mode 100644 index 000000000..3a78e2bc5 --- /dev/null +++ b/code/content/cookbook/wallets/tests/restore-keypair.test.ts @@ -0,0 +1,68 @@ +import { strict as assert } from "node:assert"; +import { describe, it } from "node:test"; +import { bytesKeypair } from "../restore-keypair-from-bytes"; +import { bs58Keypair } from "../restore-keypair-from-bs58"; +import { PublicKey } from "@solana/web3.js"; + +describe("Restore Keypair", async () => { + it("should correctly generate keypairs from both byte array and base58", () => { + // Expected values + const expectedPublicKey = new PublicKey( + "24PNhTaNtomHhoy3fTRaMhAFCRj4uHqhZEEoWrKDbR5p", + ); + const expectedSecretKeyLength = 64; + + // Test byte array keypair + assert.equal( + bytesKeypair.publicKey.toBase58(), + expectedPublicKey.toBase58(), + "Byte array keypair should have correct public key", + ); + + assert.equal( + bytesKeypair.secretKey.length, + expectedSecretKeyLength, + "Byte array keypair secret key should be 64 bytes", + ); + + // Verify byte array matches original + const originalBytes = [ + 174, 47, 154, 16, 202, 193, 206, 113, 199, 190, 53, 133, 169, 175, 31, 56, + 222, 53, 138, 189, 224, 216, 117, 173, 10, 149, 53, 45, 73, 251, 237, 246, + 15, 185, 186, 82, 177, 240, 148, 69, 241, 227, 167, 80, 141, 89, 240, 121, + 121, 35, 172, 247, 68, 251, 226, 218, 48, 63, 176, 109, 168, 89, 238, 135, + ]; + assert.deepEqual( + Array.from(bytesKeypair.secretKey), + originalBytes, + "Byte array keypair should match original bytes", + ); + + // Test base58 keypair + assert.equal( + bs58Keypair.publicKey.toBase58(), + expectedPublicKey.toBase58(), + "Base58 keypair should have correct public key", + ); + + assert.equal( + bs58Keypair.secretKey.length, + expectedSecretKeyLength, + "Base58 keypair secret key should be 64 bytes", + ); + + // Verify both keypairs generate the same key + assert.equal( + bytesKeypair.publicKey.toBase58(), + bs58Keypair.publicKey.toBase58(), + "Both keypairs should generate identical public keys", + ); + + // Verify secret keys match + assert.deepEqual( + bytesKeypair.secretKey, + bs58Keypair.secretKey, + "Both keypairs should have identical secret keys", + ); + }); +}); diff --git a/code/content/cookbook/wallets/tests/sign-message.test.ts b/code/content/cookbook/wallets/tests/sign-message.test.ts new file mode 100644 index 000000000..d9b06120a --- /dev/null +++ b/code/content/cookbook/wallets/tests/sign-message.test.ts @@ -0,0 +1,122 @@ +import { describe, test } from "node:test"; +import assert from "node:assert"; +import * as ed from "@noble/ed25519"; +import { sha512 } from "@noble/hashes/sha512"; +import { utf8ToBytes } from "@noble/hashes/utils"; +import { + keypair, + message, + messageBytes, + signature, + result, +} from "../sign-message"; + +// Enable synchronous methods for noble-ed25519 +ed.etc.sha512Sync = (...m) => sha512(ed.etc.concatBytes(...m)); + +describe("Sign and Verify Message", async () => { + test("keypair should have valid secretKey format and length", () => { + assert( + keypair.secretKey instanceof Uint8Array, + "secretKey should be Uint8Array", + ); + assert.equal(keypair.secretKey.length, 64, "secretKey should be 64 bytes"); + }); + + test("keypair should have valid publicKey format and length", () => { + assert( + keypair.publicKey.toBytes() instanceof Uint8Array, + "publicKey should convert to Uint8Array", + ); + assert.equal( + keypair.publicKey.toBytes().length, + 32, + "publicKey should be 32 bytes", + ); + }); + + test("message should convert to correct byte format", () => { + const testMessageBytes = utf8ToBytes(message); + assert.deepStrictEqual( + messageBytes, + testMessageBytes, + "messageBytes should match expected conversion", + ); + }); + + test("signature should have correct format and length", () => { + assert(signature instanceof Uint8Array, "signature should be Uint8Array"); + assert.equal(signature.length, 64, "signature should be 64 bytes"); + }); + + test("signature should verify successfully with correct inputs", () => { + const verificationResult = ed.verify( + signature, + messageBytes, + keypair.publicKey.toBytes(), + ); + assert.strictEqual( + verificationResult, + true, + "signature should verify successfully", + ); + assert.strictEqual(result, true, "exported result should be true"); + }); + + test("signature should fail verification with wrong message", () => { + const wrongMessage = utf8ToBytes("Wrong message"); + assert.strictEqual( + ed.verify(signature, wrongMessage, keypair.publicKey.toBytes()), + false, + "should fail with wrong message", + ); + }); + + test("signature should fail verification with wrong public key", () => { + const wrongPubkey = ed.getPublicKey(ed.utils.randomPrivateKey()); + assert.strictEqual( + ed.verify(signature, messageBytes, wrongPubkey), + false, + "should fail with wrong public key", + ); + }); + + test("signature should fail verification with wrong signature", () => { + const wrongSignature = new Uint8Array(64).fill(1); + assert.strictEqual( + ed.verify(wrongSignature, messageBytes, keypair.publicKey.toBytes()), + false, + "should fail with wrong signature", + ); + }); + + test("signature generation should be deterministic", () => { + const newSignature = ed.sign(messageBytes, keypair.secretKey.slice(0, 32)); + assert.deepStrictEqual( + signature, + newSignature, + "regenerated signature should match original", + ); + }); + + test("should throw error for invalid signature length", () => { + assert.throws( + () => + ed.verify( + new Uint8Array(63), + messageBytes, + keypair.publicKey.toBytes(), + ), + /Uint8Array/i, + "should throw on invalid signature length", + ); + }); + + test("should throw error for invalid message format", () => { + assert.throws( + () => ed.sign(null as any, keypair.secretKey.slice(0, 32)), + /expected/i, + "should throw on invalid message format", + ); + }); +}); diff --git a/code/content/cookbook/wallets/tests/verify-keypair.test.ts b/code/content/cookbook/wallets/tests/verify-keypair.test.ts new file mode 100644 index 000000000..cb16168a6 --- /dev/null +++ b/code/content/cookbook/wallets/tests/verify-keypair.test.ts @@ -0,0 +1,26 @@ +import { strict as assert } from "node:assert"; +import { describe, it } from "node:test"; +import { PublicKey } from "@solana/web3.js"; +import { keypair } from "../verify-keypair"; + +describe("Verify Keypair", async () => { + it("keypair should match public key", () => { + const expectedPublicKey = new PublicKey( + "24PNhTaNtomHhoy3fTRaMhAFCRj4uHqhZEEoWrKDbR5p", + ); + + // Verify the keypair's public key matches the expected public key + assert.equal( + keypair.publicKey.toBase58(), + expectedPublicKey.toBase58(), + "Generated keypair should have the expected public key", + ); + + // Additional verification of the secret key length + assert.equal( + keypair.secretKey.length, + 64, + "Secret key should be 64 bytes long", + ); + }); +}); diff --git a/code/content/cookbook/wallets/verify-keypair.ts b/code/content/cookbook/wallets/verify-keypair.ts new file mode 100644 index 000000000..675584412 --- /dev/null +++ b/code/content/cookbook/wallets/verify-keypair.ts @@ -0,0 +1,17 @@ +import { Keypair, PublicKey } from "@solana/web3.js"; + +const publicKey = new PublicKey("24PNhTaNtomHhoy3fTRaMhAFCRj4uHqhZEEoWrKDbR5p"); + +const keypair = Keypair.fromSecretKey( + Uint8Array.from([ + 174, 47, 154, 16, 202, 193, 206, 113, 199, 190, 53, 133, 169, 175, 31, 56, + 222, 53, 138, 189, 224, 216, 117, 173, 10, 149, 53, 45, 73, 251, 237, 246, + 15, 185, 186, 82, 177, 240, 148, 69, 241, 227, 167, 80, 141, 89, 240, 121, + 121, 35, 172, 247, 68, 251, 226, 218, 48, 63, 176, 109, 168, 89, 238, 135, + ]), +); + +console.log(keypair.publicKey.toBase58() === publicKey.toBase58()); +// output: true + +export { keypair }; diff --git a/code/package.json b/code/package.json index 4429cdec8..7ea3e0532 100644 --- a/code/package.json +++ b/code/package.json @@ -1,15 +1,24 @@ { - "name": "code", + "name": "solana-developer-content-code", "version": "1.0.0", + "private": true, "description": "", "main": "index.js", "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" + "test": "node --import tsx --test '**/*.ts' --trace-deprecation" }, "keywords": [], "author": "", "license": "ISC", "dependencies": { - "@solana/web3.js": "^1.95.2" + "@noble/ed25519": "^2.1.0", + "@noble/hashes": "^1.5.0", + "@solana/web3.js": "^1.95.4", + "bip39": "^3.1.0", + "bs58": "^6.0.0", + "micro-key-producer": "^0.7.1" + }, + "devDependencies": { + "tsx": "^4.19.1" } } diff --git a/code/tsconfig.json b/code/tsconfig.json new file mode 100644 index 000000000..03cc08ce3 --- /dev/null +++ b/code/tsconfig.json @@ -0,0 +1,15 @@ +{ + "compilerOptions": { + "baseUrl": ".", + "declaration": true, + "esModuleInterop": true, + "lib": ["es2022", "dom"], + "module": "commonjs", + "outDir": "build", + "resolveJsonModule": true, + "strict": true, + "target": "es2022" + }, + "include": ["./**/*.ts"], + "exclude": ["./build"] +} diff --git a/content/cookbook/wallets/check-publickey.md b/content/cookbook/wallets/check-publickey.md index 287f64c2e..622fb190e 100644 --- a/content/cookbook/wallets/check-publickey.md +++ b/content/cookbook/wallets/check-publickey.md @@ -11,8 +11,8 @@ have a private key associated with them. You can check this by looking to see if the public key lies on the ed25519 curve. Only public keys that lie on the curve can be controlled by users with wallets. -```javascript file=/code/cookbook/wallets/check-public-key.ts#L1-L2,#L3-L19 -import { PublicKey } from "@solana/web3.js"; +```typescript file=/code/content/cookbook/wallets/check-publickey.ts#L1-L24 +import { PublicKey, Keypair } from "@solana/web3.js"; // Note that Keypair.generate() will always give a public key that is valid for users @@ -29,6 +29,11 @@ const offCurveAddress = new PublicKey( // Not on the ed25519 curve, therefore not suitable for users console.log(PublicKey.isOnCurve(offCurveAddress.toBytes())); -// Not a valid public key -const errorPubkey = new PublicKey("testPubkey"); +let errorPubkey; +try { + // Not a valid public key + errorPubkey = new PublicKey("testPubkey"); +} catch (err) { + // Error will be caught here +} ``` diff --git a/content/cookbook/wallets/create-keypair.md b/content/cookbook/wallets/create-keypair.md index 9391c2b6b..ca59e98b6 100644 --- a/content/cookbook/wallets/create-keypair.md +++ b/content/cookbook/wallets/create-keypair.md @@ -11,7 +11,7 @@ are [connecting to a wallet](/content/cookbook/wallets/connect-wallet-react), you do not need to worry about the keypair. Otherwise a keypair must be generated for signing transactions. -```javascript +```javascript file=/code/content/cookbook/wallets/create-keypair.ts#L1-L3 import { Keypair } from "@solana/web3.js"; const keypair = Keypair.generate(); diff --git a/content/cookbook/wallets/generate-mnemonic.md b/content/cookbook/wallets/generate-mnemonic.md index baa48397a..279b4c01f 100644 --- a/content/cookbook/wallets/generate-mnemonic.md +++ b/content/cookbook/wallets/generate-mnemonic.md @@ -11,7 +11,7 @@ generally used to make the user experience within wallets better than a Keypair file by using a list of readable words (instead of a shorter string of random numbers and letters). -```typescript filename="generate-mnemonic.ts" +```typescript filename="generate-mnemonic.ts" file=/code/content/cookbook/wallets/generate-mnemonic.ts#L1-L3 import * as bip39 from "bip39"; const mnemonic = bip39.generateMnemonic(); diff --git a/content/cookbook/wallets/generate-vanity-address.md b/content/cookbook/wallets/generate-vanity-address.md index c8663ec22..04a59453e 100644 --- a/content/cookbook/wallets/generate-vanity-address.md +++ b/content/cookbook/wallets/generate-vanity-address.md @@ -18,6 +18,6 @@ key more easily identifiable. You can generate a vanity address using the [Solana CLI](/docs/intro/installation.md): -```bash +```bash file=/code/content/cookbook/wallets/generate-vanity-address.sh solana-keygen grind --starts-with e1v1s:1 ``` diff --git a/content/cookbook/wallets/restore-from-mnemonic.md b/content/cookbook/wallets/restore-from-mnemonic.md index 6477c5bb9..032aa6dfa 100644 --- a/content/cookbook/wallets/restore-from-mnemonic.md +++ b/content/cookbook/wallets/restore-from-mnemonic.md @@ -9,39 +9,32 @@ convert the mnemonic to Keypairs for local testing. ## Restoring BIP39 format mnemonics -```typescript filename="restore-bip39-mnemonic.ts" +```typescript filename="restore-bip39-mnemonic.ts" file=/code/content/cookbook/wallets/restore-bip39-mnemonic.ts#L1-#L13 import { Keypair } from "@solana/web3.js"; -import * as bip39 from "bip39"; - -const mnemonic = - "pill tomorrow foster begin walnut borrow virtual kick shift mutual shoe scatter"; - -// arguments: (mnemonic, password) -const seed = bip39.mnemonicToSeedSync(mnemonic, ""); -const keypair = Keypair.fromSeed(seed.slice(0, 32)); - -console.log(`${keypair.publicKey.toBase58()}`); - // output: 5ZWj7a1f8tWkjBESHKgrLmXshuXxqeY9SYcfbshpAqPG ``` ## Restoring BIP44 formant mnemonics -```typescript filename="restore-bip44-mnemonic.ts" +```typescript filename="restore-bip44-mnemonic.ts" file=/code/content/cookbook/wallets/restore-bip44-mnemonic.ts#L1-L3,#L11-L27 import { Keypair } from "@solana/web3.js"; -import { HDKey } from "micro-ed25519-hdkey"; +import { HDKey } from "micro-key-producer/slip10.js"; import * as bip39 from "bip39"; - const mnemonic = "neither lonely flavor argue grass remind eye tag avocado spot unusual intact"; -// arguments: (mnemonic, password) const seed = bip39.mnemonicToSeedSync(mnemonic, ""); const hd = HDKey.fromMasterSeed(seed.toString("hex")); +const wallets: Wallet[] = []; + for (let i = 0; i < 10; i++) { const path = `m/44'/501'/${i}'/0'`; const keypair = Keypair.fromSeed(hd.derive(path).privateKey); - console.log(`${path} => ${keypair.publicKey.toBase58()}`); + wallets.push({ + path, + keypair, + publicKey: keypair.publicKey.toBase58(), + }); } ``` diff --git a/content/cookbook/wallets/restore-keypair.md b/content/cookbook/wallets/restore-keypair.md index 7cb5d570f..fd9755e22 100644 --- a/content/cookbook/wallets/restore-keypair.md +++ b/content/cookbook/wallets/restore-keypair.md @@ -9,7 +9,7 @@ secret to test out your dApp. ## From Bytes -```typescript filename="restore-keypair-from-bytes.ts" +```typescript filename="restore-keypair-from-bytes.ts" file=/code/content/cookbook/wallets/restore-keypair-from-bytes.ts#L1-L10 import { Keypair } from "@solana/web3.js"; const keypair = Keypair.fromSecretKey( @@ -24,13 +24,13 @@ const keypair = Keypair.fromSecretKey( ## From base58 String -```typescript filename="restore-keypair-from-base58.ts +```typescript filename="restore-keypair-from-base58.ts file=/code/content/cookbook/wallets/restore-keypair-from-bs58.ts#L1-L8 import { Keypair } from "@solana/web3.js"; -import * as bs58 from "bs58"; +import bs58 from "bs58"; const keypair = Keypair.fromSecretKey( bs58.decode( - "5MaiiCavjCmn9Hs1o3eznqDEhRwxo7pXiAYez7keQUviUkauRiTMD8DrESdrNjN8zd9mTmVhRvBJeg5vhyvgrAhG", + "4UzFMkVbk1q6ApxvDS8inUxg4cMBxCQRVXRx5msqQyktbi1QkJkt574Jda6BjZThSJi54CHfVoLFdVFX8XFn233L", ), ); ``` diff --git a/content/cookbook/wallets/sign-message.md b/content/cookbook/wallets/sign-message.md index d491d7a97..f903edd99 100644 --- a/content/cookbook/wallets/sign-message.md +++ b/content/cookbook/wallets/sign-message.md @@ -11,10 +11,14 @@ the data was signed by the owner of a specific private key. To do so, we can use the [TweetNaCl](https://www.npmjs.com/package/tweetnacl) crypto library: -```typescript filename="sign-message.ts" +```typescript filename="sign-message.ts" file=/code/content/cookbook/wallets/sign-message.ts#L1-L25 import { Keypair } from "@solana/web3.js"; -import nacl from "tweetnacl"; -import { decodeUTF8 } from "tweetnacl-util"; +import * as ed from "@noble/ed25519"; +import { sha512 } from "@noble/hashes/sha512"; +import { utf8ToBytes } from "@noble/hashes/utils"; + +// Enable synchronous methods for noble-ed25519 +ed.etc.sha512Sync = (...m) => sha512(ed.etc.concatBytes(...m)); const keypair = Keypair.fromSecretKey( Uint8Array.from([ @@ -26,14 +30,11 @@ const keypair = Keypair.fromSecretKey( ); const message = "The quick brown fox jumps over the lazy dog"; -const messageBytes = decodeUTF8(message); +const messageBytes = utf8ToBytes(message); -const signature = nacl.sign.detached(messageBytes, keypair.secretKey); -const result = nacl.sign.detached.verify( - messageBytes, - signature, - keypair.publicKey.toBytes(), -); +// Sign using noble-ed25519 +const signature = ed.sign(messageBytes, keypair.secretKey.slice(0, 32)); -console.log(result); +// Verify using noble-ed25519 +const result = ed.verify(signature, messageBytes, keypair.publicKey.toBytes()); ``` diff --git a/content/cookbook/wallets/verify-keypair.md b/content/cookbook/wallets/verify-keypair.md index 6a812be52..de8916a10 100644 --- a/content/cookbook/wallets/verify-keypair.md +++ b/content/cookbook/wallets/verify-keypair.md @@ -7,7 +7,7 @@ description: "Learn how to verify keypairs on Solana." If you are given a keypair, you can verify whether or not the secret matches the given public key -```typescript filename="verify-keypair.ts" +```typescript filename="verify-keypair.ts" file=/code/content/cookbook/wallets/verify-keypair.ts#L1-L15 import { Keypair, PublicKey } from "@solana/web3.js"; const publicKey = new PublicKey("24PNhTaNtomHhoy3fTRaMhAFCRj4uHqhZEEoWrKDbR5p"); diff --git a/package.json b/package.json index 7b05ad619..a4426924f 100644 --- a/package.json +++ b/package.json @@ -1,8 +1,9 @@ { - "name": "solana-developer-content", + "name": "solana-developer-content-api", "private": true, "description": "Solana developer content", "version": "0.0.1", + "packageManager": "pnpm@9.2.0", "repository": { "type": "git", "url": "https://github.com/solana-foundation/developer-content" @@ -14,7 +15,6 @@ "dev": "pnpm code-import && pnpm contentlayer:build && concurrently -p \"[{name}]\" -n \"code import,next dev\" -c \"bgBlue.bold,bgGreen.bold\" \"pnpm code-import --watch\" \"next dev -p 3001\"", "build": "pnpm code-import && pnpm prettier:i18n && pnpm contentlayer:build && next build", "start": "next start -p 3001", - "test": "pnpm code-import && pnpm prettier && pnpm contentlayer:build", "lint": "next lint", "prettier:i18n": "prettier -cw \"i18n/**/*.{js,jsx,ts,tsx,md,css,md,mdx}\" --ignore-path \"[]\"", "prettier": "prettier -c \"./**/*.{js,jsx,ts,tsx,md,css,md,mdx}\"", @@ -30,6 +30,7 @@ "@types/node": "20.11.17", "@types/react": "18.2.15", "@types/react-dom": "18.2.7", + "@types/unist": "^3.0.3", "autoprefixer": "10.4.14", "eslint": "8.45.0", "eslint-config-next": "14.1.0", @@ -49,16 +50,16 @@ "husky": "^9.1.4", "ignore": "^5.3.1", "lint-staged": "^15.2.7", - "mdast": "^3.0.0", "prettier": "^3.2.4", "remark": "^15.0.1", "remark-frontmatter": "^5.0.0", "remark-parse": "^11.0.0", "remark-stringify": "^11.0.0", "strip-indent": "^4.0.0", + "turbo": "^2.2.3", "unified": "^11.0.5", "unist-util-visit": "^5.0.0", - "vfile": "^6.0.2" + "vfile": "^6.0.3" }, "lint-staged": { "*.{js,jsx,json,ts,tsx,md,css,md,mdx,yml,yaml}": "pnpm prettier:fix" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 627a7b48e..7a4c06768 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -19,6 +19,9 @@ importers: "@types/react-dom": specifier: 18.2.7 version: 18.2.7 + "@types/unist": + specifier: ^3.0.3 + version: 3.0.3 autoprefixer: specifier: 10.4.14 version: 10.4.14(postcss@8.4.26) @@ -33,7 +36,7 @@ importers: version: 14.1.0(@opentelemetry/api@1.9.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) next-contentlayer2: specifier: ^0.4.6 - version: 0.4.6(contentlayer2@0.4.6(esbuild@0.20.2))(esbuild@0.20.2)(next@14.1.0(@opentelemetry/api@1.9.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + version: 0.4.6(contentlayer2@0.4.6(esbuild@0.23.1))(esbuild@0.23.1)(next@14.1.0(@opentelemetry/api@1.9.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(react-dom@18.2.0(react@18.2.0))(react@18.2.0) postcss: specifier: 8.4.26 version: 8.4.26 @@ -61,7 +64,7 @@ importers: version: 8.2.2 contentlayer2: specifier: ^0.4.6 - version: 0.4.6(esbuild@0.20.2) + version: 0.4.6(esbuild@0.23.1) husky: specifier: ^9.1.4 version: 9.1.6 @@ -71,9 +74,6 @@ importers: lint-staged: specifier: ^15.2.7 version: 15.2.10 - mdast: - specifier: ^3.0.0 - version: 3.0.0 prettier: specifier: ^3.2.4 version: 3.3.3 @@ -92,6 +92,9 @@ importers: strip-indent: specifier: ^4.0.0 version: 4.0.0 + turbo: + specifier: ^2.2.3 + version: 2.2.3 unified: specifier: ^11.0.5 version: 11.0.5 @@ -99,9 +102,34 @@ importers: specifier: ^5.0.0 version: 5.0.0 vfile: - specifier: ^6.0.2 + specifier: ^6.0.3 version: 6.0.3 + code: + dependencies: + "@noble/ed25519": + specifier: ^2.1.0 + version: 2.1.0 + "@noble/hashes": + specifier: ^1.5.0 + version: 1.5.0 + "@solana/web3.js": + specifier: ^1.95.4 + version: 1.95.4(bufferutil@4.0.8)(utf-8-validate@5.0.10) + bip39: + specifier: ^3.1.0 + version: 3.1.0 + bs58: + specifier: ^6.0.0 + version: 6.0.0 + micro-key-producer: + specifier: ^0.7.1 + version: 0.7.1 + devDependencies: + tsx: + specifier: ^4.19.1 + version: 4.19.1 + packages: "@alloc/quick-lru@5.2.0": resolution: @@ -216,210 +244,219 @@ packages: peerDependencies: esbuild: "*" - "@esbuild/aix-ppc64@0.20.2": + "@esbuild/aix-ppc64@0.23.1": resolution: { - integrity: sha512-D+EBOJHXdNZcLJRBkhENNG8Wji2kgc9AZ9KiPr1JuZjsNtyHzrsfLRrY0tk2H2aoFu6RANO1y1iPPUCDYWkb5g==, + integrity: sha512-6VhYk1diRqrhBAqpJEdjASR/+WVRtfjpqKuNw11cLiaWpAT/Uu+nokB+UJnevzy/P9C/ty6AOe0dwueMrGh/iQ==, } - engines: { node: ">=12" } + engines: { node: ">=18" } cpu: [ppc64] os: [aix] - "@esbuild/android-arm64@0.20.2": + "@esbuild/android-arm64@0.23.1": resolution: { - integrity: sha512-mRzjLacRtl/tWU0SvD8lUEwb61yP9cqQo6noDZP/O8VkwafSYwZ4yWy24kan8jE/IMERpYncRt2dw438LP3Xmg==, + integrity: sha512-xw50ipykXcLstLeWH7WRdQuysJqejuAGPd30vd1i5zSyKK3WE+ijzHmLKxdiCMtH1pHz78rOg0BKSYOSB/2Khw==, } - engines: { node: ">=12" } + engines: { node: ">=18" } cpu: [arm64] os: [android] - "@esbuild/android-arm@0.20.2": + "@esbuild/android-arm@0.23.1": resolution: { - integrity: sha512-t98Ra6pw2VaDhqNWO2Oph2LXbz/EJcnLmKLGBJwEwXX/JAN83Fym1rU8l0JUWK6HkIbWONCSSatf4sf2NBRx/w==, + integrity: sha512-uz6/tEy2IFm9RYOyvKl88zdzZfwEfKZmnX9Cj1BHjeSGNuGLuMD1kR8y5bteYmwqKm1tj8m4cb/aKEorr6fHWQ==, } - engines: { node: ">=12" } + engines: { node: ">=18" } cpu: [arm] os: [android] - "@esbuild/android-x64@0.20.2": + "@esbuild/android-x64@0.23.1": resolution: { - integrity: sha512-btzExgV+/lMGDDa194CcUQm53ncxzeBrWJcncOBxuC6ndBkKxnHdFJn86mCIgTELsooUmwUm9FkhSp5HYu00Rg==, + integrity: sha512-nlN9B69St9BwUoB+jkyU090bru8L0NA3yFvAd7k8dNsVH8bi9a8cUAUSEcEEgTp2z3dbEDGJGfP6VUnkQnlReg==, } - engines: { node: ">=12" } + engines: { node: ">=18" } cpu: [x64] os: [android] - "@esbuild/darwin-arm64@0.20.2": + "@esbuild/darwin-arm64@0.23.1": resolution: { - integrity: sha512-4J6IRT+10J3aJH3l1yzEg9y3wkTDgDk7TSDFX+wKFiWjqWp/iCfLIYzGyasx9l0SAFPT1HwSCR+0w/h1ES/MjA==, + integrity: sha512-YsS2e3Wtgnw7Wq53XXBLcV6JhRsEq8hkfg91ESVadIrzr9wO6jJDMZnCQbHm1Guc5t/CdDiFSSfWP58FNuvT3Q==, } - engines: { node: ">=12" } + engines: { node: ">=18" } cpu: [arm64] os: [darwin] - "@esbuild/darwin-x64@0.20.2": + "@esbuild/darwin-x64@0.23.1": resolution: { - integrity: sha512-tBcXp9KNphnNH0dfhv8KYkZhjc+H3XBkF5DKtswJblV7KlT9EI2+jeA8DgBjp908WEuYll6pF+UStUCfEpdysA==, + integrity: sha512-aClqdgTDVPSEGgoCS8QDG37Gu8yc9lTHNAQlsztQ6ENetKEO//b8y31MMu2ZaPbn4kVsIABzVLXYLhCGekGDqw==, } - engines: { node: ">=12" } + engines: { node: ">=18" } cpu: [x64] os: [darwin] - "@esbuild/freebsd-arm64@0.20.2": + "@esbuild/freebsd-arm64@0.23.1": resolution: { - integrity: sha512-d3qI41G4SuLiCGCFGUrKsSeTXyWG6yem1KcGZVS+3FYlYhtNoNgYrWcvkOoaqMhwXSMrZRl69ArHsGJ9mYdbbw==, + integrity: sha512-h1k6yS8/pN/NHlMl5+v4XPfikhJulk4G+tKGFIOwURBSFzE8bixw1ebjluLOjfwtLqY0kewfjLSrO6tN2MgIhA==, } - engines: { node: ">=12" } + engines: { node: ">=18" } cpu: [arm64] os: [freebsd] - "@esbuild/freebsd-x64@0.20.2": + "@esbuild/freebsd-x64@0.23.1": resolution: { - integrity: sha512-d+DipyvHRuqEeM5zDivKV1KuXn9WeRX6vqSqIDgwIfPQtwMP4jaDsQsDncjTDDsExT4lR/91OLjRo8bmC1e+Cw==, + integrity: sha512-lK1eJeyk1ZX8UklqFd/3A60UuZ/6UVfGT2LuGo3Wp4/z7eRTRYY+0xOu2kpClP+vMTi9wKOfXi2vjUpO1Ro76g==, } - engines: { node: ">=12" } + engines: { node: ">=18" } cpu: [x64] os: [freebsd] - "@esbuild/linux-arm64@0.20.2": + "@esbuild/linux-arm64@0.23.1": resolution: { - integrity: sha512-9pb6rBjGvTFNira2FLIWqDk/uaf42sSyLE8j1rnUpuzsODBq7FvpwHYZxQ/It/8b+QOS1RYfqgGFNLRI+qlq2A==, + integrity: sha512-/93bf2yxencYDnItMYV/v116zff6UyTjo4EtEQjUBeGiVpMmffDNUyD9UN2zV+V3LRV3/on4xdZ26NKzn6754g==, } - engines: { node: ">=12" } + engines: { node: ">=18" } cpu: [arm64] os: [linux] - "@esbuild/linux-arm@0.20.2": + "@esbuild/linux-arm@0.23.1": resolution: { - integrity: sha512-VhLPeR8HTMPccbuWWcEUD1Az68TqaTYyj6nfE4QByZIQEQVWBB8vup8PpR7y1QHL3CpcF6xd5WVBU/+SBEvGTg==, + integrity: sha512-CXXkzgn+dXAPs3WBwE+Kvnrf4WECwBdfjfeYHpMeVxWE0EceB6vhWGShs6wi0IYEqMSIzdOF1XjQ/Mkm5d7ZdQ==, } - engines: { node: ">=12" } + engines: { node: ">=18" } cpu: [arm] os: [linux] - "@esbuild/linux-ia32@0.20.2": + "@esbuild/linux-ia32@0.23.1": resolution: { - integrity: sha512-o10utieEkNPFDZFQm9CoP7Tvb33UutoJqg3qKf1PWVeeJhJw0Q347PxMvBgVVFgouYLGIhFYG0UGdBumROyiig==, + integrity: sha512-VTN4EuOHwXEkXzX5nTvVY4s7E/Krz7COC8xkftbbKRYAl96vPiUssGkeMELQMOnLOJ8k3BY1+ZY52tttZnHcXQ==, } - engines: { node: ">=12" } + engines: { node: ">=18" } cpu: [ia32] os: [linux] - "@esbuild/linux-loong64@0.20.2": + "@esbuild/linux-loong64@0.23.1": resolution: { - integrity: sha512-PR7sp6R/UC4CFVomVINKJ80pMFlfDfMQMYynX7t1tNTeivQ6XdX5r2XovMmha/VjR1YN/HgHWsVcTRIMkymrgQ==, + integrity: sha512-Vx09LzEoBa5zDnieH8LSMRToj7ir/Jeq0Gu6qJ/1GcBq9GkfoEAoXvLiW1U9J1qE/Y/Oyaq33w5p2ZWrNNHNEw==, } - engines: { node: ">=12" } + engines: { node: ">=18" } cpu: [loong64] os: [linux] - "@esbuild/linux-mips64el@0.20.2": + "@esbuild/linux-mips64el@0.23.1": resolution: { - integrity: sha512-4BlTqeutE/KnOiTG5Y6Sb/Hw6hsBOZapOVF6njAESHInhlQAghVVZL1ZpIctBOoTFbQyGW+LsVYZ8lSSB3wkjA==, + integrity: sha512-nrFzzMQ7W4WRLNUOU5dlWAqa6yVeI0P78WKGUo7lg2HShq/yx+UYkeNSE0SSfSure0SqgnsxPvmAUu/vu0E+3Q==, } - engines: { node: ">=12" } + engines: { node: ">=18" } cpu: [mips64el] os: [linux] - "@esbuild/linux-ppc64@0.20.2": + "@esbuild/linux-ppc64@0.23.1": resolution: { - integrity: sha512-rD3KsaDprDcfajSKdn25ooz5J5/fWBylaaXkuotBDGnMnDP1Uv5DLAN/45qfnf3JDYyJv/ytGHQaziHUdyzaAg==, + integrity: sha512-dKN8fgVqd0vUIjxuJI6P/9SSSe/mB9rvA98CSH2sJnlZ/OCZWO1DJvxj8jvKTfYUdGfcq2dDxoKaC6bHuTlgcw==, } - engines: { node: ">=12" } + engines: { node: ">=18" } cpu: [ppc64] os: [linux] - "@esbuild/linux-riscv64@0.20.2": + "@esbuild/linux-riscv64@0.23.1": resolution: { - integrity: sha512-snwmBKacKmwTMmhLlz/3aH1Q9T8v45bKYGE3j26TsaOVtjIag4wLfWSiZykXzXuE1kbCE+zJRmwp+ZbIHinnVg==, + integrity: sha512-5AV4Pzp80fhHL83JM6LoA6pTQVWgB1HovMBsLQ9OZWLDqVY8MVobBXNSmAJi//Csh6tcY7e7Lny2Hg1tElMjIA==, } - engines: { node: ">=12" } + engines: { node: ">=18" } cpu: [riscv64] os: [linux] - "@esbuild/linux-s390x@0.20.2": + "@esbuild/linux-s390x@0.23.1": resolution: { - integrity: sha512-wcWISOobRWNm3cezm5HOZcYz1sKoHLd8VL1dl309DiixxVFoFe/o8HnwuIwn6sXre88Nwj+VwZUvJf4AFxkyrQ==, + integrity: sha512-9ygs73tuFCe6f6m/Tb+9LtYxWR4c9yg7zjt2cYkjDbDpV/xVn+68cQxMXCjUpYwEkze2RcU/rMnfIXNRFmSoDw==, } - engines: { node: ">=12" } + engines: { node: ">=18" } cpu: [s390x] os: [linux] - "@esbuild/linux-x64@0.20.2": + "@esbuild/linux-x64@0.23.1": resolution: { - integrity: sha512-1MdwI6OOTsfQfek8sLwgyjOXAu+wKhLEoaOLTjbijk6E2WONYpH9ZU2mNtR+lZ2B4uwr+usqGuVfFT9tMtGvGw==, + integrity: sha512-EV6+ovTsEXCPAp58g2dD68LxoP/wK5pRvgy0J/HxPGB009omFPv3Yet0HiaqvrIrgPTBuC6wCH1LTOY91EO5hQ==, } - engines: { node: ">=12" } + engines: { node: ">=18" } cpu: [x64] os: [linux] - "@esbuild/netbsd-x64@0.20.2": + "@esbuild/netbsd-x64@0.23.1": resolution: { - integrity: sha512-K8/DhBxcVQkzYc43yJXDSyjlFeHQJBiowJ0uVL6Tor3jGQfSGHNNJcWxNbOI8v5k82prYqzPuwkzHt3J1T1iZQ==, + integrity: sha512-aevEkCNu7KlPRpYLjwmdcuNz6bDFiE7Z8XC4CPqExjTvrHugh28QzUXVOZtiYghciKUacNktqxdpymplil1beA==, } - engines: { node: ">=12" } + engines: { node: ">=18" } cpu: [x64] os: [netbsd] - "@esbuild/openbsd-x64@0.20.2": + "@esbuild/openbsd-arm64@0.23.1": resolution: { - integrity: sha512-eMpKlV0SThJmmJgiVyN9jTPJ2VBPquf6Kt/nAoo6DgHAoN57K15ZghiHaMvqjCye/uU4X5u3YSMgVBI1h3vKrQ==, + integrity: sha512-3x37szhLexNA4bXhLrCC/LImN/YtWis6WXr1VESlfVtVeoFJBRINPJ3f0a/6LV8zpikqoUg4hyXw0sFBt5Cr+Q==, } - engines: { node: ">=12" } + engines: { node: ">=18" } + cpu: [arm64] + os: [openbsd] + + "@esbuild/openbsd-x64@0.23.1": + resolution: + { + integrity: sha512-aY2gMmKmPhxfU+0EdnN+XNtGbjfQgwZj43k8G3fyrDM/UdZww6xrWxmDkuz2eCZchqVeABjV5BpildOrUbBTqA==, + } + engines: { node: ">=18" } cpu: [x64] os: [openbsd] - "@esbuild/sunos-x64@0.20.2": + "@esbuild/sunos-x64@0.23.1": resolution: { - integrity: sha512-2UyFtRC6cXLyejf/YEld4Hajo7UHILetzE1vsRcGL3earZEW77JxrFjH4Ez2qaTiEfMgAXxfAZCm1fvM/G/o8w==, + integrity: sha512-RBRT2gqEl0IKQABT4XTj78tpk9v7ehp+mazn2HbUeZl1YMdaGAQqhapjGTCe7uw7y0frDi4gS0uHzhvpFuI1sA==, } - engines: { node: ">=12" } + engines: { node: ">=18" } cpu: [x64] os: [sunos] - "@esbuild/win32-arm64@0.20.2": + "@esbuild/win32-arm64@0.23.1": resolution: { - integrity: sha512-GRibxoawM9ZCnDxnP3usoUDO9vUkpAxIIZ6GQI+IlVmr5kP3zUq+l17xELTHMWTWzjxa2guPNyrpq1GWmPvcGQ==, + integrity: sha512-4O+gPR5rEBe2FpKOVyiJ7wNDPA8nGzDuJ6gN4okSA1gEOYZ67N8JPk58tkWtdtPeLz7lBnY6I5L3jdsr3S+A6A==, } - engines: { node: ">=12" } + engines: { node: ">=18" } cpu: [arm64] os: [win32] - "@esbuild/win32-ia32@0.20.2": + "@esbuild/win32-ia32@0.23.1": resolution: { - integrity: sha512-HfLOfn9YWmkSKRQqovpnITazdtquEW8/SoHW7pWpuEeguaZI4QnCRW6b+oZTztdBnZOS2hqJ6im/D5cPzBTTlQ==, + integrity: sha512-BcaL0Vn6QwCwre3Y717nVHZbAa4UBEigzFm6VdsVdT/MbZ38xoj1X9HPkZhbmaBGUD1W8vxAfffbDe8bA6AKnQ==, } - engines: { node: ">=12" } + engines: { node: ">=18" } cpu: [ia32] os: [win32] - "@esbuild/win32-x64@0.20.2": + "@esbuild/win32-x64@0.23.1": resolution: { - integrity: sha512-N49X4lJX27+l9jbLKSqZ6bKNjzQvHaT8IIFUy+YIqmXQdjYCToGWwOItDrfby14c78aDd5NHQl29xingXfCdLQ==, + integrity: sha512-BHpFFeslkWrXWyUPnbKm+xYYVYruCinGcftSBaa8zoF9hZO4BcSCFUvHVTtzpIY6YzUnYtuEhZ+C9iEXjxnasg==, } - engines: { node: ">=12" } + engines: { node: ">=18" } cpu: [x64] os: [win32] @@ -683,6 +720,33 @@ packages: cpu: [x64] os: [win32] + "@noble/ciphers@1.0.0": + resolution: + { + integrity: sha512-wH5EHOmLi0rEazphPbecAzmjd12I6/Yv/SiHdkA9LSycsQk7RuuTp7am5/o62qYr0RScE7Pc9icXGBbsr6cesA==, + } + engines: { node: ^14.21.3 || >=16 } + + "@noble/curves@1.6.0": + resolution: + { + integrity: sha512-TlaHRXDehJuRNR9TfZDNQ45mMEd5dwUwmicsafcIX4SsNiqnCHKjE/1alYPd/lDRVhxdhUAlv8uEhMCI5zjIJQ==, + } + engines: { node: ^14.21.3 || >=16 } + + "@noble/ed25519@2.1.0": + resolution: + { + integrity: sha512-KM4qTyXPinyCgMzeYJH/UudpdL+paJXtY3CHtHYZQtBkS8MZoPr4rOikZllIutJe0d06QDQKisyn02gxZ8TcQA==, + } + + "@noble/hashes@1.5.0": + resolution: + { + integrity: sha512-1j6kQFb7QRru7eKN3ZDvRcP13rugwdxZqCjbiAVZfIJwgj2A65UmT4TgARXGlXgnRkORLTDTrO19ZErt7+QXgA==, + } + engines: { node: ^14.21.3 || >=16 } + "@nodelib/fs.scandir@2.1.5": resolution: { @@ -963,6 +1027,31 @@ packages: integrity: sha512-WJgX9nzTqknM393q1QJDJmoW28kUfEnybeTfVNcNAPnIx210RXm2DiXiHzfNPJNIUUb1tJnz/l4QGtJ30PgWmA==, } + "@scure/base@1.1.9": + resolution: + { + integrity: sha512-8YKhl8GHiNI/pU2VMaofa2Tor7PJRAjwQLBBuilkJ9L5+13yVbC7JO/wS7piioAvPSwR3JKM1IJ/u4xQzbcXKg==, + } + + "@solana/buffer-layout@4.0.1": + resolution: + { + integrity: sha512-E1ImOIAD1tBZFRdjeM4/pzTiTApC0AOBGwyAMS4fwIodCWArzJ3DWdoh8cKxeFM2fElkxBh2Aqts1BPC373rHA==, + } + engines: { node: ">=5.10" } + + "@solana/web3.js@1.95.4": + resolution: + { + integrity: sha512-sdewnNEA42ZSMxqkzdwEWi6fDgzwtJHaQa5ndUGEJYtoOnM6X5cvPmjoTUp7/k7bRrVAxfBgDnvQQHD6yhlLYw==, + } + + "@swc/helpers@0.5.13": + resolution: + { + integrity: sha512-UoKGxQ3r5kYI9dALKJapMmuK+1zWM/H17Z1+iwnNmzcJRnfFuevZs375TA5rW31pu4BS4NoSy1fRsexDXfWn5w==, + } + "@swc/helpers@0.5.2": resolution: { @@ -975,6 +1064,12 @@ packages: integrity: sha512-veQTnWP+1D/xbxVrPC3zHnCZRjSrKfhbMUlEA43iMZLu7EsnTtkJklIuwrCPbOi8YkvDQAiW05VQQFvvz9oieQ==, } + "@types/connect@3.4.38": + resolution: + { + integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==, + } + "@types/debug@4.1.12": resolution: { @@ -1023,6 +1118,12 @@ packages: integrity: sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==, } + "@types/node@12.20.55": + resolution: + { + integrity: sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==, + } + "@types/node@20.11.17": resolution: { @@ -1071,6 +1172,24 @@ packages: integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==, } + "@types/uuid@8.3.4": + resolution: + { + integrity: sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw==, + } + + "@types/ws@7.4.7": + resolution: + { + integrity: sha512-JQbbmxZTZehdc2iszGKs5oC3NFnjeay7mtAWrdt7qNtAVK0g19muApzAy4bm9byz79xa2ZnO/BOBC2R8RC5Lww==, + } + + "@types/ws@8.5.12": + resolution: + { + integrity: sha512-3tPRkv1EtkDpzlgyKyI8pGsGZAGPEaXeu0DOj5DI25Ja91bdAYddYHbADRYVrZMRbfW+1l5YwXVDKohDJNQxkQ==, + } + "@typescript-eslint/parser@6.21.0": resolution: { @@ -1123,6 +1242,13 @@ packages: integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==, } + JSONStream@1.3.5: + resolution: + { + integrity: sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==, + } + hasBin: true + acorn-jsx@5.3.2: resolution: { @@ -1139,6 +1265,13 @@ packages: engines: { node: ">=0.4.0" } hasBin: true + agentkeepalive@4.5.0: + resolution: + { + integrity: sha512-5GG/5IbQQpC9FpkRGsSvZI5QYeSCzlJHdpBQntCsuTOxhKD8lqKhrleg2Yi7yvMIf82Ycmmqln9U8V9qwEiJew==, + } + engines: { node: ">= 8.0.0" } + ajv@6.12.6: resolution: { @@ -1342,6 +1475,31 @@ packages: integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==, } + base-x@3.0.10: + resolution: + { + integrity: sha512-7d0s06rR9rYaIWHkpfLIFICM/tkSVdoPC9qYAQRpxn9DdKNWNsKC0uk++akckyLq16Tx2WIinnZ6WRriAt6njQ==, + } + + base-x@5.0.0: + resolution: + { + integrity: sha512-sMW3VGSX1QWVFA6l8U62MLKz29rRfpTlYdCqLdpLo1/Yd4zZwSbnUaDfciIAowAqvq7YFnWq9hrhdg1KYgc1lQ==, + } + + base64-js@1.5.1: + resolution: + { + integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==, + } + + bigint-buffer@1.1.5: + resolution: + { + integrity: sha512-trfYco6AoZ+rKhKnxA0hgX0HAbVP/s808/EuDSe2JDzUnCp/xAsli35Orvk67UrTEcwuxZqYZDmfA2RXJgxVvA==, + } + engines: { node: ">= 10.0.0" } + binary-extensions@2.3.0: resolution: { @@ -1349,6 +1507,30 @@ packages: } engines: { node: ">=8" } + bindings@1.5.0: + resolution: + { + integrity: sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==, + } + + bip39@3.1.0: + resolution: + { + integrity: sha512-c9kiwdk45Do5GL0vJMe7tS95VjCii65mYAH7DfWl3uW8AVzXKQVUm64i3hzVybBDMp9r7j9iNxR85+ul8MdN/A==, + } + + bn.js@5.2.1: + resolution: + { + integrity: sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==, + } + + borsh@0.7.0: + resolution: + { + integrity: sha512-CLCsZGIBCFnPtkNnieW/a8wmreDmfUtjU2m9yHrzPXIlNbqVs0AQrSatSG6vdNYUqdc83tkQi2eHfF98ubzQLA==, + } + brace-expansion@1.1.11: resolution: { @@ -1376,6 +1558,18 @@ packages: engines: { node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7 } hasBin: true + bs58@4.0.1: + resolution: + { + integrity: sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw==, + } + + bs58@6.0.0: + resolution: + { + integrity: sha512-PD0wEnEYg6ijszw/u8s+iI3H17cTymlrwkKhDhPZq+Sokl3AU4htyBFTjAeNAlCCmg0f53g6ih3jATyCKftTfw==, + } + buffer-crc32@0.2.13: resolution: { @@ -1388,6 +1582,19 @@ packages: integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==, } + buffer@6.0.3: + resolution: + { + integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==, + } + + bufferutil@4.0.8: + resolution: + { + integrity: sha512-4T53u4PdgsXqKaIctwF8ifXlRTTmEPJ8iEPWFdGZvcf7sbwYo6FKFEX9eNNAnzFZ7EzJAQ3CJeOtCRA4rDp7Pw==, + } + engines: { node: ">=6.14.2" } + busboy@1.6.0: resolution: { @@ -1566,6 +1773,12 @@ packages: } engines: { node: ">=18" } + commander@2.20.3: + resolution: + { + integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==, + } + commander@4.1.1: resolution: { @@ -1716,6 +1929,13 @@ packages: } engines: { node: ">= 0.4" } + delay@5.0.0: + resolution: + { + integrity: sha512-ReEBKkIfe4ya47wlPYf/gu5ib6yUG0/Aez0JQZQz94kiWtRQvZIQbTiehsnwHvLSWJnQdhVeqYue7Id1dKr0qw==, + } + engines: { node: ">=10" } + dequal@2.0.3: resolution: { @@ -1867,12 +2087,24 @@ packages: } engines: { node: ">= 0.4" } - esbuild@0.20.2: + es6-promise@4.2.8: resolution: { - integrity: sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g==, + integrity: sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==, } - engines: { node: ">=12" } + + es6-promisify@5.0.0: + resolution: + { + integrity: sha512-C+d6UdsYDk0lMebHNR4S2NybQMMngAOnOwYBQjTOiv0MkoJMP0Myw2mgpDLBcpfCmRLxyFqYhS/CfOENq4SJhQ==, + } + + esbuild@0.23.1: + resolution: + { + integrity: sha512-VVNz/9Sa0bs5SELtn3f7qhJCDPCF5oMEl5cO9/SSinpE9hbPVvxbd572HH5AKiP7WD8INO53GgfDDhRjkylHEg==, + } + engines: { node: ">=18" } hasBin: true escalade@3.2.0: @@ -2127,6 +2359,13 @@ packages: integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==, } + eyes@0.1.8: + resolution: + { + integrity: sha512-GipyPsXO1anza0AOZdy69Im7hGFCNB7Y/NGjDlZGJ3GJJLtwNSb2vrzYrTYJRrRloVx7pl+bhUaTB8yiccPvFQ==, + } + engines: { node: "> 0.1.90" } + fast-deep-equal@3.1.3: resolution: { @@ -2152,6 +2391,12 @@ packages: integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==, } + fast-stable-stringify@1.0.0: + resolution: + { + integrity: sha512-wpYMUmFu5f00Sm0cj2pfivpmawLZ0NKdviQ4w9zJeR8JVtOpOxHmLaJuj0vxvGqMJQWyP/COUkF75/57OKyRag==, + } + fastq@1.17.1: resolution: { @@ -2171,6 +2416,12 @@ packages: } engines: { node: ^10.12.0 || >=12.0.0 } + file-uri-to-path@1.0.0: + resolution: + { + integrity: sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==, + } + fill-range@7.1.1: resolution: { @@ -2484,6 +2735,12 @@ packages: } engines: { node: ">=16.17.0" } + humanize-ms@1.2.1: + resolution: + { + integrity: sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==, + } + husky@9.1.6: resolution: { @@ -2499,6 +2756,12 @@ packages: } engines: { node: ">=10.18" } + ieee754@1.2.1: + resolution: + { + integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==, + } + ignore@5.3.2: resolution: { @@ -2859,6 +3122,14 @@ packages: integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==, } + isomorphic-ws@4.0.1: + resolution: + { + integrity: sha512-BhBvN2MBpWTaSHdWRb/bwdZJ1WaehQ2L1KngkCkfLUGF0mAWAT1sQUQacEmQ0jXkFw/czDXPNQSL5u2/Krsz1w==, + } + peerDependencies: + ws: "*" + iterator.prototype@1.1.2: resolution: { @@ -2878,6 +3149,14 @@ packages: integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==, } + jayson@4.1.2: + resolution: + { + integrity: sha512-5nzMWDHy6f+koZOuYsArh2AXs73NfWYVlFyJJuCedr93GpY+Ku8qq10ropSXVfHK+H0T6paA88ww+/dV+1fBNA==, + } + engines: { node: ">=8" } + hasBin: true + jiti@1.21.6: resolution: { @@ -2929,6 +3208,12 @@ packages: integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==, } + json-stringify-safe@5.0.1: + resolution: + { + integrity: sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==, + } + json5@1.0.2: resolution: { @@ -2936,6 +3221,13 @@ packages: } hasBin: true + jsonparse@1.3.1: + resolution: + { + integrity: sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==, + } + engines: { "0": node >= 0.2.0 } + jsx-ast-utils@3.3.5: resolution: { @@ -3141,13 +3433,6 @@ packages: integrity: sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==, } - mdast@3.0.0: - resolution: - { - integrity: sha512-xySmf8g4fPKMeC07jXGz971EkLbWAJ83s4US2Tj9lEdnZ142UP5grN73H1Xd3HzrdbU5o9GYYP/y8F9ZSwLE9g==, - } - deprecated: "`mdast` was renamed to `remark`" - mdx-bundler@10.0.3: resolution: { @@ -3177,6 +3462,18 @@ packages: } engines: { node: ">= 8" } + micro-key-producer@0.7.1: + resolution: + { + integrity: sha512-2BdtXMha712vZ57PfWaJokLSNl5HQTAVUvIw7oge/3gNSpaQfiYbLrZZGPUSPE3EqZMyOhydSVifrBkXQHVHcg==, + } + + micro-packed@0.6.3: + resolution: + { + integrity: sha512-VmVkyc7lIzAq/XCPFuLc/CwQ7Ehs5XDK3IwqsZHiBIDttAI9Gs7go6Lv4lNRuAIKrGKcRTtthFKUNyHS0S4wJQ==, + } + micromark-core-commonmark@2.0.1: resolution: { @@ -3514,6 +3811,13 @@ packages: encoding: optional: true + node-gyp-build@4.8.2: + resolution: + { + integrity: sha512-IRUxE4BVsHWXkV/SFOut4qTlagw2aM8T5/vnTsmrHJvVoKueJHRc/JaFND7QDDc61kLYUJ6qlZM3sqTSyx2dTw==, + } + hasBin: true + node-releases@2.0.18: resolution: { @@ -4080,6 +4384,12 @@ packages: deprecated: Rimraf versions prior to v4 are no longer supported hasBin: true + rpc-websockets@9.0.4: + resolution: + { + integrity: sha512-yWZWN0M+bivtoNLnaDbtny4XchdAIF5Q4g/ZsC5UC61Ckbp0QczwO8fg44rV3uYmY4WHd+EZQbn90W1d8ojzqQ==, + } + run-parallel@1.2.0: resolution: { @@ -4099,6 +4409,12 @@ packages: } engines: { node: ">=0.4" } + safe-buffer@5.2.1: + resolution: + { + integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==, + } + safe-regex-test@1.0.3: resolution: { @@ -4428,6 +4744,13 @@ packages: engines: { node: ">=16 || 14 >=14.17" } hasBin: true + superstruct@2.0.2: + resolution: + { + integrity: sha512-uV+TFRZdXsqXTL2pRvujROjdZQ4RAlBUS5BTh9IGm+jTqQntYThciG/qu57Gs69yjnVUSqdxF9YLmSnpupBW9A==, + } + engines: { node: ">=14.0.0" } + supports-color@7.2.0: resolution: { @@ -4471,6 +4794,12 @@ packages: } engines: { node: ">=10" } + text-encoding-utf-8@1.0.2: + resolution: + { + integrity: sha512-8bw4MY9WjdsD2aMtO0OzOCY3pXGYNx2d2FfHRVUKkiCPDWjKuOlhLVASS+pD7VkLTVjW268LYJHwsnPFlBpbAg==, + } + text-table@0.2.0: resolution: { @@ -4499,6 +4828,12 @@ packages: peerDependencies: tslib: ^2 + through@2.3.8: + resolution: + { + integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==, + } + to-regex-range@5.0.1: resolution: { @@ -4579,6 +4914,69 @@ packages: integrity: sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==, } + tsx@4.19.1: + resolution: + { + integrity: sha512-0flMz1lh74BR4wOvBjuh9olbnwqCPc35OOlfyzHba0Dc+QNUeWX/Gq2YTbnwcWPO3BMd8fkzRVrHcsR+a7z7rA==, + } + engines: { node: ">=18.0.0" } + hasBin: true + + turbo-darwin-64@2.2.3: + resolution: + { + integrity: sha512-Rcm10CuMKQGcdIBS3R/9PMeuYnv6beYIHqfZFeKWVYEWH69sauj4INs83zKMTUiZJ3/hWGZ4jet9AOwhsssLyg==, + } + cpu: [x64] + os: [darwin] + + turbo-darwin-arm64@2.2.3: + resolution: + { + integrity: sha512-+EIMHkuLFqUdJYsA3roj66t9+9IciCajgj+DVek+QezEdOJKcRxlvDOS2BUaeN8kEzVSsNiAGnoysFWYw4K0HA==, + } + cpu: [arm64] + os: [darwin] + + turbo-linux-64@2.2.3: + resolution: + { + integrity: sha512-UBhJCYnqtaeOBQLmLo8BAisWbc9v9daL9G8upLR+XGj6vuN/Nz6qUAhverN4Pyej1g4Nt1BhROnj6GLOPYyqxQ==, + } + cpu: [x64] + os: [linux] + + turbo-linux-arm64@2.2.3: + resolution: + { + integrity: sha512-hJYT9dN06XCQ3jBka/EWvvAETnHRs3xuO/rb5bESmDfG+d9yQjeTMlhRXKrr4eyIMt6cLDt1LBfyi+6CQ+VAwQ==, + } + cpu: [arm64] + os: [linux] + + turbo-windows-64@2.2.3: + resolution: + { + integrity: sha512-NPrjacrZypMBF31b4HE4ROg4P3nhMBPHKS5WTpMwf7wydZ8uvdEHpESVNMOtqhlp857zbnKYgP+yJF30H3N2dQ==, + } + cpu: [x64] + os: [win32] + + turbo-windows-arm64@2.2.3: + resolution: + { + integrity: sha512-fnNrYBCqn6zgKPKLHu4sOkihBI/+0oYFr075duRxqUZ+1aLWTAGfHZLgjVeLh3zR37CVzuerGIPWAEkNhkWEIw==, + } + cpu: [arm64] + os: [win32] + + turbo@2.2.3: + resolution: + { + integrity: sha512-5lDvSqIxCYJ/BAd6rQGK/AzFRhBkbu4JHVMLmGh/hCb7U3CqSnr5Tjwfy9vc+/5wG2DJ6wttgAaA7MoCgvBKZQ==, + } + hasBin: true + typanion@3.14.0: resolution: { @@ -4711,12 +5109,26 @@ packages: integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==, } + utf-8-validate@5.0.10: + resolution: + { + integrity: sha512-Z6czzLq4u8fPOyx7TU6X3dvUZVvoJmxSQ+IcrlmagKhilxlhZgxPK6C5Jqbkw1IDUmFTM+cz9QDnnLTwDz/2gQ==, + } + engines: { node: ">=6.14.2" } + util-deprecate@1.0.2: resolution: { integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==, } + uuid@8.3.2: + resolution: + { + integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==, + } + hasBin: true + uuid@9.0.1: resolution: { @@ -4817,6 +5229,36 @@ packages: integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==, } + ws@7.5.10: + resolution: + { + integrity: sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==, + } + engines: { node: ">=8.3.0" } + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: ^5.0.2 + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + + ws@8.18.0: + resolution: + { + integrity: sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==, + } + engines: { node: ">=10.0.0" } + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: ">=5.0.2" + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + y18n@5.0.8: resolution: { @@ -4885,9 +5327,9 @@ snapshots: dependencies: regenerator-runtime: 0.14.1 - "@contentlayer2/cli@0.4.3(esbuild@0.20.2)": + "@contentlayer2/cli@0.4.3(esbuild@0.23.1)": dependencies: - "@contentlayer2/core": 0.4.3(esbuild@0.20.2) + "@contentlayer2/core": 0.4.3(esbuild@0.23.1) "@contentlayer2/utils": 0.4.3 clipanion: 3.2.1(typanion@3.14.0) typanion: 3.14.0 @@ -4897,22 +5339,22 @@ snapshots: - markdown-wasm - supports-color - "@contentlayer2/client@0.4.3(esbuild@0.20.2)": + "@contentlayer2/client@0.4.3(esbuild@0.23.1)": dependencies: - "@contentlayer2/core": 0.4.3(esbuild@0.20.2) + "@contentlayer2/core": 0.4.3(esbuild@0.23.1) transitivePeerDependencies: - "@effect-ts/otel-node" - esbuild - markdown-wasm - supports-color - "@contentlayer2/core@0.4.3(esbuild@0.20.2)": + "@contentlayer2/core@0.4.3(esbuild@0.23.1)": dependencies: "@contentlayer2/utils": 0.4.3 camel-case: 4.1.2 comment-json: 4.2.5 gray-matter: 4.0.3 - mdx-bundler: 10.0.3(esbuild@0.20.2) + mdx-bundler: 10.0.3(esbuild@0.23.1) rehype-stringify: 10.0.0 remark-frontmatter: 5.0.0 remark-parse: 11.0.0 @@ -4921,14 +5363,14 @@ snapshots: type-fest: 4.26.1 unified: 11.0.5 optionalDependencies: - esbuild: 0.20.2 + esbuild: 0.23.1 transitivePeerDependencies: - "@effect-ts/otel-node" - supports-color - "@contentlayer2/source-files@0.4.3(esbuild@0.20.2)": + "@contentlayer2/source-files@0.4.3(esbuild@0.23.1)": dependencies: - "@contentlayer2/core": 0.4.3(esbuild@0.20.2) + "@contentlayer2/core": 0.4.3(esbuild@0.23.1) "@contentlayer2/utils": 0.4.3 chokidar: 3.6.0 fast-glob: 3.3.2 @@ -4945,10 +5387,10 @@ snapshots: - markdown-wasm - supports-color - "@contentlayer2/source-remote-files@0.4.3(esbuild@0.20.2)": + "@contentlayer2/source-remote-files@0.4.3(esbuild@0.23.1)": dependencies: - "@contentlayer2/core": 0.4.3(esbuild@0.20.2) - "@contentlayer2/source-files": 0.4.3(esbuild@0.20.2) + "@contentlayer2/core": 0.4.3(esbuild@0.23.1) + "@contentlayer2/source-files": 0.4.3(esbuild@0.23.1) "@contentlayer2/utils": 0.4.3 transitivePeerDependencies: - "@effect-ts/otel-node" @@ -5009,83 +5451,86 @@ snapshots: "@effect-ts/system@0.57.5": {} - "@esbuild-plugins/node-resolve@0.2.2(esbuild@0.20.2)": + "@esbuild-plugins/node-resolve@0.2.2(esbuild@0.23.1)": dependencies: "@types/resolve": 1.20.6 debug: 4.3.7 - esbuild: 0.20.2 + esbuild: 0.23.1 escape-string-regexp: 4.0.0 resolve: 1.22.8 transitivePeerDependencies: - supports-color - "@esbuild/aix-ppc64@0.20.2": + "@esbuild/aix-ppc64@0.23.1": optional: true - "@esbuild/android-arm64@0.20.2": + "@esbuild/android-arm64@0.23.1": optional: true - "@esbuild/android-arm@0.20.2": + "@esbuild/android-arm@0.23.1": optional: true - "@esbuild/android-x64@0.20.2": + "@esbuild/android-x64@0.23.1": optional: true - "@esbuild/darwin-arm64@0.20.2": + "@esbuild/darwin-arm64@0.23.1": optional: true - "@esbuild/darwin-x64@0.20.2": + "@esbuild/darwin-x64@0.23.1": optional: true - "@esbuild/freebsd-arm64@0.20.2": + "@esbuild/freebsd-arm64@0.23.1": optional: true - "@esbuild/freebsd-x64@0.20.2": + "@esbuild/freebsd-x64@0.23.1": optional: true - "@esbuild/linux-arm64@0.20.2": + "@esbuild/linux-arm64@0.23.1": optional: true - "@esbuild/linux-arm@0.20.2": + "@esbuild/linux-arm@0.23.1": optional: true - "@esbuild/linux-ia32@0.20.2": + "@esbuild/linux-ia32@0.23.1": optional: true - "@esbuild/linux-loong64@0.20.2": + "@esbuild/linux-loong64@0.23.1": optional: true - "@esbuild/linux-mips64el@0.20.2": + "@esbuild/linux-mips64el@0.23.1": optional: true - "@esbuild/linux-ppc64@0.20.2": + "@esbuild/linux-ppc64@0.23.1": optional: true - "@esbuild/linux-riscv64@0.20.2": + "@esbuild/linux-riscv64@0.23.1": optional: true - "@esbuild/linux-s390x@0.20.2": + "@esbuild/linux-s390x@0.23.1": optional: true - "@esbuild/linux-x64@0.20.2": + "@esbuild/linux-x64@0.23.1": optional: true - "@esbuild/netbsd-x64@0.20.2": + "@esbuild/netbsd-x64@0.23.1": optional: true - "@esbuild/openbsd-x64@0.20.2": + "@esbuild/openbsd-arm64@0.23.1": optional: true - "@esbuild/sunos-x64@0.20.2": + "@esbuild/openbsd-x64@0.23.1": optional: true - "@esbuild/win32-arm64@0.20.2": + "@esbuild/sunos-x64@0.23.1": optional: true - "@esbuild/win32-ia32@0.20.2": + "@esbuild/win32-arm64@0.23.1": optional: true - "@esbuild/win32-x64@0.20.2": + "@esbuild/win32-ia32@0.23.1": + optional: true + + "@esbuild/win32-x64@0.23.1": optional: true "@eslint-community/eslint-utils@4.4.0(eslint@8.45.0)": @@ -5186,11 +5631,11 @@ snapshots: dependencies: tslib: 2.7.0 - "@mdx-js/esbuild@3.0.1(esbuild@0.20.2)": + "@mdx-js/esbuild@3.0.1(esbuild@0.23.1)": dependencies: "@mdx-js/mdx": 3.0.1 "@types/unist": 3.0.3 - esbuild: 0.20.2 + esbuild: 0.23.1 vfile: 6.0.3 vfile-message: 4.0.2 transitivePeerDependencies: @@ -5257,6 +5702,16 @@ snapshots: "@next/swc-win32-x64-msvc@14.1.0": optional: true + "@noble/ciphers@1.0.0": {} + + "@noble/curves@1.6.0": + dependencies: + "@noble/hashes": 1.5.0 + + "@noble/ed25519@2.1.0": {} + + "@noble/hashes@1.5.0": {} + "@nodelib/fs.scandir@2.1.5": dependencies: "@nodelib/fs.stat": 2.0.5 @@ -5418,6 +5873,38 @@ snapshots: "@rushstack/eslint-patch@1.10.4": {} + "@scure/base@1.1.9": {} + + "@solana/buffer-layout@4.0.1": + dependencies: + buffer: 6.0.3 + + "@solana/web3.js@1.95.4(bufferutil@4.0.8)(utf-8-validate@5.0.10)": + dependencies: + "@babel/runtime": 7.25.6 + "@noble/curves": 1.6.0 + "@noble/hashes": 1.5.0 + "@solana/buffer-layout": 4.0.1 + agentkeepalive: 4.5.0 + bigint-buffer: 1.1.5 + bn.js: 5.2.1 + borsh: 0.7.0 + bs58: 4.0.1 + buffer: 6.0.3 + fast-stable-stringify: 1.0.0 + jayson: 4.1.2(bufferutil@4.0.8)(utf-8-validate@5.0.10) + node-fetch: 2.7.0 + rpc-websockets: 9.0.4 + superstruct: 2.0.2 + transitivePeerDependencies: + - bufferutil + - encoding + - utf-8-validate + + "@swc/helpers@0.5.13": + dependencies: + tslib: 2.7.0 + "@swc/helpers@0.5.2": dependencies: tslib: 2.7.0 @@ -5426,6 +5913,10 @@ snapshots: dependencies: "@types/estree": 1.0.6 + "@types/connect@3.4.38": + dependencies: + "@types/node": 20.11.17 + "@types/debug@4.1.12": dependencies: "@types/ms": 0.7.34 @@ -5450,6 +5941,8 @@ snapshots: "@types/ms@0.7.34": {} + "@types/node@12.20.55": {} + "@types/node@20.11.17": dependencies: undici-types: 5.26.5 @@ -5474,6 +5967,16 @@ snapshots: "@types/unist@3.0.3": {} + "@types/uuid@8.3.4": {} + + "@types/ws@7.4.7": + dependencies: + "@types/node": 20.11.17 + + "@types/ws@8.5.12": + dependencies: + "@types/node": 20.11.17 + "@typescript-eslint/parser@6.21.0(eslint@8.45.0)(typescript@5.3.3)": dependencies: "@typescript-eslint/scope-manager": 6.21.0 @@ -5516,12 +6019,21 @@ snapshots: "@ungap/structured-clone@1.2.0": {} + JSONStream@1.3.5: + dependencies: + jsonparse: 1.3.1 + through: 2.3.8 + acorn-jsx@5.3.2(acorn@8.12.1): dependencies: acorn: 8.12.1 acorn@8.12.1: {} + agentkeepalive@4.5.0: + dependencies: + humanize-ms: 1.2.1 + ajv@6.12.6: dependencies: fast-deep-equal: 3.1.3 @@ -5657,8 +6169,36 @@ snapshots: balanced-match@1.0.2: {} + base-x@3.0.10: + dependencies: + safe-buffer: 5.2.1 + + base-x@5.0.0: {} + + base64-js@1.5.1: {} + + bigint-buffer@1.1.5: + dependencies: + bindings: 1.5.0 + binary-extensions@2.3.0: {} + bindings@1.5.0: + dependencies: + file-uri-to-path: 1.0.0 + + bip39@3.1.0: + dependencies: + "@noble/hashes": 1.5.0 + + bn.js@5.2.1: {} + + borsh@0.7.0: + dependencies: + bn.js: 5.2.1 + bs58: 4.0.1 + text-encoding-utf-8: 1.0.2 + brace-expansion@1.1.11: dependencies: balanced-match: 1.0.2 @@ -5679,10 +6219,28 @@ snapshots: node-releases: 2.0.18 update-browserslist-db: 1.1.0(browserslist@4.24.0) + bs58@4.0.1: + dependencies: + base-x: 3.0.10 + + bs58@6.0.0: + dependencies: + base-x: 5.0.0 + buffer-crc32@0.2.13: {} buffer-from@1.1.2: {} + buffer@6.0.3: + dependencies: + base64-js: 1.5.1 + ieee754: 1.2.1 + + bufferutil@4.0.8: + dependencies: + node-gyp-build: 4.8.2 + optional: true + busboy@1.6.0: dependencies: streamsearch: 1.1.0 @@ -5774,6 +6332,8 @@ snapshots: commander@12.1.0: {} + commander@2.20.3: {} + commander@4.1.1: {} comment-json@4.2.5: @@ -5798,13 +6358,13 @@ snapshots: tree-kill: 1.2.2 yargs: 17.7.2 - contentlayer2@0.4.6(esbuild@0.20.2): + contentlayer2@0.4.6(esbuild@0.23.1): dependencies: - "@contentlayer2/cli": 0.4.3(esbuild@0.20.2) - "@contentlayer2/client": 0.4.3(esbuild@0.20.2) - "@contentlayer2/core": 0.4.3(esbuild@0.20.2) - "@contentlayer2/source-files": 0.4.3(esbuild@0.20.2) - "@contentlayer2/source-remote-files": 0.4.3(esbuild@0.20.2) + "@contentlayer2/cli": 0.4.3(esbuild@0.23.1) + "@contentlayer2/client": 0.4.3(esbuild@0.23.1) + "@contentlayer2/core": 0.4.3(esbuild@0.23.1) + "@contentlayer2/source-files": 0.4.3(esbuild@0.23.1) + "@contentlayer2/source-remote-files": 0.4.3(esbuild@0.23.1) "@contentlayer2/utils": 0.4.3 transitivePeerDependencies: - "@effect-ts/otel-node" @@ -5895,6 +6455,8 @@ snapshots: has-property-descriptors: 1.0.2 object-keys: 1.1.1 + delay@5.0.0: {} + dequal@2.0.3: {} devlop@1.1.0: @@ -6038,31 +6600,38 @@ snapshots: is-date-object: 1.0.5 is-symbol: 1.0.4 - esbuild@0.20.2: + es6-promise@4.2.8: {} + + es6-promisify@5.0.0: + dependencies: + es6-promise: 4.2.8 + + esbuild@0.23.1: optionalDependencies: - "@esbuild/aix-ppc64": 0.20.2 - "@esbuild/android-arm": 0.20.2 - "@esbuild/android-arm64": 0.20.2 - "@esbuild/android-x64": 0.20.2 - "@esbuild/darwin-arm64": 0.20.2 - "@esbuild/darwin-x64": 0.20.2 - "@esbuild/freebsd-arm64": 0.20.2 - "@esbuild/freebsd-x64": 0.20.2 - "@esbuild/linux-arm": 0.20.2 - "@esbuild/linux-arm64": 0.20.2 - "@esbuild/linux-ia32": 0.20.2 - "@esbuild/linux-loong64": 0.20.2 - "@esbuild/linux-mips64el": 0.20.2 - "@esbuild/linux-ppc64": 0.20.2 - "@esbuild/linux-riscv64": 0.20.2 - "@esbuild/linux-s390x": 0.20.2 - "@esbuild/linux-x64": 0.20.2 - "@esbuild/netbsd-x64": 0.20.2 - "@esbuild/openbsd-x64": 0.20.2 - "@esbuild/sunos-x64": 0.20.2 - "@esbuild/win32-arm64": 0.20.2 - "@esbuild/win32-ia32": 0.20.2 - "@esbuild/win32-x64": 0.20.2 + "@esbuild/aix-ppc64": 0.23.1 + "@esbuild/android-arm": 0.23.1 + "@esbuild/android-arm64": 0.23.1 + "@esbuild/android-x64": 0.23.1 + "@esbuild/darwin-arm64": 0.23.1 + "@esbuild/darwin-x64": 0.23.1 + "@esbuild/freebsd-arm64": 0.23.1 + "@esbuild/freebsd-x64": 0.23.1 + "@esbuild/linux-arm": 0.23.1 + "@esbuild/linux-arm64": 0.23.1 + "@esbuild/linux-ia32": 0.23.1 + "@esbuild/linux-loong64": 0.23.1 + "@esbuild/linux-mips64el": 0.23.1 + "@esbuild/linux-ppc64": 0.23.1 + "@esbuild/linux-riscv64": 0.23.1 + "@esbuild/linux-s390x": 0.23.1 + "@esbuild/linux-x64": 0.23.1 + "@esbuild/netbsd-x64": 0.23.1 + "@esbuild/openbsd-arm64": 0.23.1 + "@esbuild/openbsd-x64": 0.23.1 + "@esbuild/sunos-x64": 0.23.1 + "@esbuild/win32-arm64": 0.23.1 + "@esbuild/win32-ia32": 0.23.1 + "@esbuild/win32-x64": 0.23.1 escalade@3.2.0: {} @@ -6078,7 +6647,7 @@ snapshots: eslint: 8.45.0 eslint-import-resolver-node: 0.3.9 eslint-import-resolver-typescript: 3.6.3(@typescript-eslint/parser@6.21.0(eslint@8.45.0)(typescript@5.3.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.30.0(eslint@8.45.0))(eslint@8.45.0) - eslint-plugin-import: 2.30.0(@typescript-eslint/parser@6.21.0(eslint@8.45.0)(typescript@5.3.3))(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@6.21.0(eslint@8.45.0)(typescript@5.3.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.30.0(eslint@8.45.0))(eslint@8.45.0))(eslint@8.45.0) + eslint-plugin-import: 2.30.0(@typescript-eslint/parser@6.21.0(eslint@8.45.0)(typescript@5.3.3))(eslint-import-resolver-typescript@3.6.3)(eslint@8.45.0) eslint-plugin-jsx-a11y: 6.10.0(eslint@8.45.0) eslint-plugin-react: 7.36.1(eslint@8.45.0) eslint-plugin-react-hooks: 4.6.2(eslint@8.45.0) @@ -6109,7 +6678,7 @@ snapshots: is-bun-module: 1.2.1 is-glob: 4.0.3 optionalDependencies: - eslint-plugin-import: 2.30.0(@typescript-eslint/parser@6.21.0(eslint@8.45.0)(typescript@5.3.3))(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@6.21.0(eslint@8.45.0)(typescript@5.3.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.30.0(eslint@8.45.0))(eslint@8.45.0))(eslint@8.45.0) + eslint-plugin-import: 2.30.0(@typescript-eslint/parser@6.21.0(eslint@8.45.0)(typescript@5.3.3))(eslint-import-resolver-typescript@3.6.3)(eslint@8.45.0) transitivePeerDependencies: - "@typescript-eslint/parser" - eslint-import-resolver-node @@ -6127,7 +6696,7 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-plugin-import@2.30.0(@typescript-eslint/parser@6.21.0(eslint@8.45.0)(typescript@5.3.3))(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@6.21.0(eslint@8.45.0)(typescript@5.3.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.30.0(eslint@8.45.0))(eslint@8.45.0))(eslint@8.45.0): + eslint-plugin-import@2.30.0(@typescript-eslint/parser@6.21.0(eslint@8.45.0)(typescript@5.3.3))(eslint-import-resolver-typescript@3.6.3)(eslint@8.45.0): dependencies: "@rtsao/scc": 1.1.0 array-includes: 3.1.8 @@ -6322,6 +6891,8 @@ snapshots: extend@3.0.2: {} + eyes@0.1.8: {} + fast-deep-equal@3.1.3: {} fast-glob@3.3.2: @@ -6336,6 +6907,8 @@ snapshots: fast-levenshtein@2.0.6: {} + fast-stable-stringify@1.0.0: {} + fastq@1.17.1: dependencies: reusify: 1.0.4 @@ -6348,6 +6921,8 @@ snapshots: dependencies: flat-cache: 3.2.0 + file-uri-to-path@1.0.0: {} + fill-range@7.1.1: dependencies: to-regex-range: 5.0.1 @@ -6576,10 +7151,16 @@ snapshots: human-signals@5.0.0: {} + humanize-ms@1.2.1: + dependencies: + ms: 2.1.3 + husky@9.1.6: {} hyperdyperid@1.2.0: {} + ieee754@1.2.1: {} + ignore@5.3.2: {} imagescript@1.3.0: {} @@ -6750,6 +7331,10 @@ snapshots: isexe@2.0.0: {} + isomorphic-ws@4.0.1(ws@7.5.10(bufferutil@4.0.8)(utf-8-validate@5.0.10)): + dependencies: + ws: 7.5.10(bufferutil@4.0.8)(utf-8-validate@5.0.10) + iterator.prototype@1.1.2: dependencies: define-properties: 1.2.1 @@ -6770,6 +7355,24 @@ snapshots: optionalDependencies: "@pkgjs/parseargs": 0.11.0 + jayson@4.1.2(bufferutil@4.0.8)(utf-8-validate@5.0.10): + dependencies: + "@types/connect": 3.4.38 + "@types/node": 12.20.55 + "@types/ws": 7.4.7 + JSONStream: 1.3.5 + commander: 2.20.3 + delay: 5.0.0 + es6-promisify: 5.0.0 + eyes: 0.1.8 + isomorphic-ws: 4.0.1(ws@7.5.10(bufferutil@4.0.8)(utf-8-validate@5.0.10)) + json-stringify-safe: 5.0.1 + uuid: 8.3.2 + ws: 7.5.10(bufferutil@4.0.8)(utf-8-validate@5.0.10) + transitivePeerDependencies: + - bufferutil + - utf-8-validate + jiti@1.21.6: {} js-tokens@4.0.0: {} @@ -6791,10 +7394,14 @@ snapshots: json-stable-stringify-without-jsonify@1.0.1: {} + json-stringify-safe@5.0.1: {} + json5@1.0.2: dependencies: minimist: 1.2.8 + jsonparse@1.3.1: {} + jsx-ast-utils@3.3.5: dependencies: array-includes: 3.1.8 @@ -6992,15 +7599,13 @@ snapshots: dependencies: "@types/mdast": 4.0.4 - mdast@3.0.0: {} - - mdx-bundler@10.0.3(esbuild@0.20.2): + mdx-bundler@10.0.3(esbuild@0.23.1): dependencies: "@babel/runtime": 7.25.6 - "@esbuild-plugins/node-resolve": 0.2.2(esbuild@0.20.2) + "@esbuild-plugins/node-resolve": 0.2.2(esbuild@0.23.1) "@fal-works/esbuild-plugin-global-externals": 2.1.2 - "@mdx-js/esbuild": 3.0.1(esbuild@0.20.2) - esbuild: 0.20.2 + "@mdx-js/esbuild": 3.0.1(esbuild@0.23.1) + esbuild: 0.23.1 gray-matter: 4.0.3 remark-frontmatter: 5.0.0 remark-mdx-frontmatter: 4.0.0 @@ -7020,6 +7625,18 @@ snapshots: merge2@1.4.1: {} + micro-key-producer@0.7.1: + dependencies: + "@noble/ciphers": 1.0.0 + "@noble/curves": 1.6.0 + "@noble/hashes": 1.5.0 + "@scure/base": 1.1.9 + micro-packed: 0.6.3 + + micro-packed@0.6.3: + dependencies: + "@scure/base": 1.1.9 + micromark-core-commonmark@2.0.1: dependencies: decode-named-character-reference: 1.0.2 @@ -7287,11 +7904,11 @@ snapshots: natural-compare@1.4.0: {} - next-contentlayer2@0.4.6(contentlayer2@0.4.6(esbuild@0.20.2))(esbuild@0.20.2)(next@14.1.0(@opentelemetry/api@1.9.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(react-dom@18.2.0(react@18.2.0))(react@18.2.0): + next-contentlayer2@0.4.6(contentlayer2@0.4.6(esbuild@0.23.1))(esbuild@0.23.1)(next@14.1.0(@opentelemetry/api@1.9.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(react-dom@18.2.0(react@18.2.0))(react@18.2.0): dependencies: - "@contentlayer2/core": 0.4.3(esbuild@0.20.2) + "@contentlayer2/core": 0.4.3(esbuild@0.23.1) "@contentlayer2/utils": 0.4.3 - contentlayer2: 0.4.6(esbuild@0.20.2) + contentlayer2: 0.4.6(esbuild@0.23.1) next: 14.1.0(@opentelemetry/api@1.9.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) react: 18.2.0 react-dom: 18.2.0(react@18.2.0) @@ -7336,6 +7953,9 @@ snapshots: dependencies: whatwg-url: 5.0.0 + node-gyp-build@4.8.2: + optional: true + node-releases@2.0.18: {} normalize-path@3.0.0: {} @@ -7694,6 +8314,19 @@ snapshots: dependencies: glob: 7.2.3 + rpc-websockets@9.0.4: + dependencies: + "@swc/helpers": 0.5.13 + "@types/uuid": 8.3.4 + "@types/ws": 8.5.12 + buffer: 6.0.3 + eventemitter3: 5.0.1 + uuid: 8.3.2 + ws: 8.18.0(bufferutil@4.0.8)(utf-8-validate@5.0.10) + optionalDependencies: + bufferutil: 4.0.8 + utf-8-validate: 5.0.10 + run-parallel@1.2.0: dependencies: queue-microtask: 1.2.3 @@ -7709,6 +8342,8 @@ snapshots: has-symbols: 1.0.3 isarray: 2.0.5 + safe-buffer@5.2.1: {} + safe-regex-test@1.0.3: dependencies: call-bind: 1.0.7 @@ -7914,6 +8549,8 @@ snapshots: pirates: 4.0.6 ts-interface-checker: 0.1.13 + superstruct@2.0.2: {} + supports-color@7.2.0: dependencies: has-flag: 4.0.0 @@ -7962,6 +8599,8 @@ snapshots: mkdirp: 1.0.4 yallist: 4.0.0 + text-encoding-utf-8@1.0.2: {} + text-table@0.2.0: {} thenify-all@1.6.0: @@ -7976,6 +8615,8 @@ snapshots: dependencies: tslib: 2.7.0 + through@2.3.8: {} + to-regex-range@5.0.1: dependencies: is-number: 7.0.0 @@ -8011,6 +8652,40 @@ snapshots: tslib@2.7.0: {} + tsx@4.19.1: + dependencies: + esbuild: 0.23.1 + get-tsconfig: 4.8.1 + optionalDependencies: + fsevents: 2.3.3 + + turbo-darwin-64@2.2.3: + optional: true + + turbo-darwin-arm64@2.2.3: + optional: true + + turbo-linux-64@2.2.3: + optional: true + + turbo-linux-arm64@2.2.3: + optional: true + + turbo-windows-64@2.2.3: + optional: true + + turbo-windows-arm64@2.2.3: + optional: true + + turbo@2.2.3: + optionalDependencies: + turbo-darwin-64: 2.2.3 + turbo-darwin-arm64: 2.2.3 + turbo-linux-64: 2.2.3 + turbo-linux-arm64: 2.2.3 + turbo-windows-64: 2.2.3 + turbo-windows-arm64: 2.2.3 + typanion@3.14.0: {} type-check@0.4.0: @@ -8111,8 +8786,15 @@ snapshots: dependencies: punycode: 2.3.1 + utf-8-validate@5.0.10: + dependencies: + node-gyp-build: 4.8.2 + optional: true + util-deprecate@1.0.2: {} + uuid@8.3.2: {} + uuid@9.0.1: {} vfile-message@4.0.2: @@ -8196,6 +8878,16 @@ snapshots: wrappy@1.0.2: {} + ws@7.5.10(bufferutil@4.0.8)(utf-8-validate@5.0.10): + optionalDependencies: + bufferutil: 4.0.8 + utf-8-validate: 5.0.10 + + ws@8.18.0(bufferutil@4.0.8)(utf-8-validate@5.0.10): + optionalDependencies: + bufferutil: 4.0.8 + utf-8-validate: 5.0.10 + y18n@5.0.8: {} yallist@4.0.0: {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml new file mode 100644 index 000000000..dc6e92d2d --- /dev/null +++ b/pnpm-workspace.yaml @@ -0,0 +1,3 @@ +packages: + - "." + - "code" diff --git a/src/utils/code-import.ts b/src/utils/code-import.ts index b31e0277a..56dd211f0 100644 --- a/src/utils/code-import.ts +++ b/src/utils/code-import.ts @@ -66,13 +66,21 @@ function extractLines( const lines = content.split(EOL); let result: string[] = []; - for (const range of ranges) { - if (range.to > lines.length) { - throw new Error( - `Line range exceeds file length of ${lines.length} lines`, - ); + if ( + ranges.length === 1 && + ranges[0].from === 1 && + ranges[0].to === Infinity + ) { + result = result.concat(lines); + } else { + for (const range of ranges) { + if (range.to > lines.length) { + throw new Error( + `Line range exceeds file length of ${lines.length} lines`, + ); + } + result = result.concat(lines.slice(range.from - 1, range.to)); } - result = result.concat(lines.slice(range.from - 1, range.to)); } let finalResult = result.join("\n"); diff --git a/turbo.json b/turbo.json new file mode 100644 index 000000000..33d386806 --- /dev/null +++ b/turbo.json @@ -0,0 +1,44 @@ +{ + "$schema": "https://turbo.build/schema.json", + "globalDependencies": ["**/.env.*local"], + "tasks": { + "build": { + "dependsOn": ["^build"], + "outputs": [".next/**", "!.next/cache/**", "dist/**"] + }, + "contentlayer:build": { + "dependsOn": ["code-import"], + "inputs": [ + "content/**/*.md", + "content/**/*.mdx", + "content/**/*.yaml", + "content/**/*.yml", + "docs/**/*.md", + "docs/**/*.mdx", + "docs/**/*.yaml", + "docs/**/*.yml", + "i8n/**/*.md", + "i8n/**/*.mdx", + "i8n/**/*.yaml", + "i8n/**/*.yml", + "src/**/**", + "contentlayer.config.ts" + ], + "outputs": [".contentlayer/**"], + "cache": true + }, + "lint": {}, + "dev": { + "cache": false, + "persistent": true + }, + "test": { + "dependsOn": ["build"], + "inputs": ["src/**/*.tsx", "src/**/*.ts", "test/**/*.ts", "test/**/*.tsx"] + }, + "code-import": { + "outputs": ["code/**/*"] + } + }, + "extends": ["//"] +} From b10332a4b24bcd1391d246429dad636e0b66e79e Mon Sep 17 00:00:00 2001 From: Ayush Date: Fri, 25 Oct 2024 06:10:35 +0530 Subject: [PATCH 2/7] updated turbo with relevant file patterns --- turbo.json | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/turbo.json b/turbo.json index 33d386806..7c24c0650 100644 --- a/turbo.json +++ b/turbo.json @@ -34,10 +34,18 @@ }, "test": { "dependsOn": ["build"], - "inputs": ["src/**/*.tsx", "src/**/*.ts", "test/**/*.ts", "test/**/*.tsx"] + "inputs": ["code/**/*.test.ts"] }, "code-import": { - "outputs": ["code/**/*"] + "inputs": ["code/**/*"], + "outputs": [ + "content/**/*.md", + "content/**/*.mdx", + "docs/**/*.md", + "docs/**/*.mdx", + "i8n/**/*.md", + "i8n/**/*.mdx" + ] } }, "extends": ["//"] From 6560bc278bd0fbe426e23506347da6444de5e8ed Mon Sep 17 00:00:00 2001 From: Ayush Date: Fri, 25 Oct 2024 06:20:42 +0530 Subject: [PATCH 3/7] added tsx, removed pnpm version from actions in support of package.json --- .github/workflows/code:test.yml | 4 +--- .github/workflows/contentlayer.yml | 2 -- .github/workflows/deploy.yml | 2 -- .github/workflows/formatting.yml | 2 -- package.json | 3 ++- pnpm-lock.yaml | 3 +++ 6 files changed, 6 insertions(+), 10 deletions(-) diff --git a/.github/workflows/code:test.yml b/.github/workflows/code:test.yml index 7da73a0ff..a7791abf7 100644 --- a/.github/workflows/code:test.yml +++ b/.github/workflows/code:test.yml @@ -7,7 +7,7 @@ on: - cron: "0 0 * * *" # Run daily at midnight UTC jobs: - test: + code-import-test: runs-on: ubuntu-latest strategy: @@ -32,8 +32,6 @@ jobs: - name: Install pnpm uses: pnpm/action-setup@v2 - with: - version: 9 - run: cd code diff --git a/.github/workflows/contentlayer.yml b/.github/workflows/contentlayer.yml index 962162710..356f76089 100644 --- a/.github/workflows/contentlayer.yml +++ b/.github/workflows/contentlayer.yml @@ -18,8 +18,6 @@ jobs: - uses: actions/checkout@v4 - name: Install pnpm uses: pnpm/action-setup@v4 - with: - version: 9 - name: use Node.js ${{ matrix.node-version }} uses: actions/setup-node@v3 with: diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 906dd536d..69569cbf7 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -18,8 +18,6 @@ jobs: - uses: actions/checkout@v4 - name: Install pnpm uses: pnpm/action-setup@v4 - with: - version: 9 - name: Install Vercel CLI and pnpm run: npm install --global vercel@latest - name: Pull Vercel Environment Information diff --git a/.github/workflows/formatting.yml b/.github/workflows/formatting.yml index b9c8ad1dc..8cd048900 100644 --- a/.github/workflows/formatting.yml +++ b/.github/workflows/formatting.yml @@ -16,8 +16,6 @@ jobs: uses: actions/checkout@v4 - name: Install pnpm uses: pnpm/action-setup@v4 - with: - version: 9 - name: Use Node.js ${{ matrix.node-version }} uses: actions/setup-node@v4 with: diff --git a/package.json b/package.json index a4426924f..11bb9a50c 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,7 @@ "scripts": { "runner": "npx ts-node -r tsconfig-paths/register", "contentlayer:build": "npx contentlayer2 build --clearCache", - "code-import": "npx esrun coder.ts", + "code-import": "npx tsx coder.ts", "dev": "pnpm code-import && pnpm contentlayer:build && concurrently -p \"[{name}]\" -n \"code import,next dev\" -c \"bgBlue.bold,bgGreen.bold\" \"pnpm code-import --watch\" \"next dev -p 3001\"", "build": "pnpm code-import && pnpm prettier:i18n && pnpm contentlayer:build && next build", "start": "next start -p 3001", @@ -56,6 +56,7 @@ "remark-parse": "^11.0.0", "remark-stringify": "^11.0.0", "strip-indent": "^4.0.0", + "tsx": "^4.19.1", "turbo": "^2.2.3", "unified": "^11.0.5", "unist-util-visit": "^5.0.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7a4c06768..db5345010 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -92,6 +92,9 @@ importers: strip-indent: specifier: ^4.0.0 version: 4.0.0 + tsx: + specifier: ^4.19.1 + version: 4.19.1 turbo: specifier: ^2.2.3 version: 2.2.3 From f35aa0c76424b2709eb290e6793fc9329f4b07d3 Mon Sep 17 00:00:00 2001 From: Ayush Date: Fri, 25 Oct 2024 06:37:59 +0530 Subject: [PATCH 4/7] improve gh action for tests, report test results, add coverage --- .github/workflows/code:test.yml | 92 +++++++++++++++++++++++++-------- code/package.json | 3 +- 2 files changed, 72 insertions(+), 23 deletions(-) diff --git a/.github/workflows/code:test.yml b/.github/workflows/code:test.yml index a7791abf7..67b941632 100644 --- a/.github/workflows/code:test.yml +++ b/.github/workflows/code:test.yml @@ -3,37 +3,48 @@ name: Code Snippets testing on: pull_request: branches: [main] + paths: + - "code/**/*.test.ts" # Only trigger on test file changes schedule: - cron: "0 0 * * *" # Run daily at midnight UTC jobs: - code-import-test: + snippets-tests: runs-on: ubuntu-latest + defaults: + run: + working-directory: ./code # Set working directory to code folder + strategy: matrix: node-version: [20] + fail-fast: false steps: - - uses: actions/checkout@v4 + - name: Checkout repository + uses: actions/checkout@v4 with: - fetch-depth: 0 # Fetch all history for all branches and tags + fetch-depth: 0 - - name: Get changed files + - name: Get changed test files id: changed-files uses: tj-actions/changed-files@v39 with: - files: "**/*.ts" + files: | + code/**/*.test.ts - name: Use Node.js ${{ matrix.node-version }} uses: actions/setup-node@v4 with: node-version: ${{ matrix.node-version }} + cache: "pnpm" - name: Install pnpm uses: pnpm/action-setup@v2 - - - run: cd code + with: + version: 8 + run_install: false - name: Install dependencies run: pnpm install @@ -41,21 +52,58 @@ jobs: - name: Run tests on changed files if: github.event_name == 'pull_request' run: | - for file in ${{ steps.changed-files.outputs.all_changed_files }}; do - if [[ $file == *.test.ts ]]; then - echo "Running tests for $file" - node --import tsx --test $file - elif [[ $file == *.ts ]]; then - test_file="${file%.ts}.test.ts" - if [ -f "$test_file" ]; then - echo "Running tests for $test_file" - node --import tsx --test $test_file - else - echo "No test file found for $file" - fi - fi - done + CHANGED_FILES="${{ steps.changed-files.outputs.all_changed_files }}" + + if [ -z "$CHANGED_FILES" ]; then + echo "No test files were changed" + exit 0 + fi + + # Convert the space-separated list to an array + readarray -t test_files <<< "$CHANGED_FILES" + + # Print the files we're going to test + echo "Running tests for the following files:" + printf '%s\n' "${test_files[@]}" + + # Run all test files in a single command + node --import tsx --test "${test_files[@]}" || exit 1 - name: Run all tests if: github.event_name == 'schedule' - run: pnpm turbo test + run: | + echo "Running all tests in code directory" + pnpm turbo test + + - name: Report test results + if: always() && github.event_name == 'pull_request' + uses: actions/github-script@v7 + with: + script: | + const { context, github } = require('@actions/github'); + + const testStatus = process.env.TEST_SUCCESS === 'true' ? '✅' : '❌'; + const summary = []; + + summary.push(`# Test Results ${testStatus}`); + summary.push(''); + + const changedFiles = '${{ steps.changed-files.outputs.all_changed_files }}'.split(' '); + if (changedFiles.length > 0 && changedFiles[0] !== '') { + summary.push('## Changed Test Files:'); + changedFiles.forEach(file => { + summary.push(`- \`${file}\``); + }); + } else { + summary.push('No test files were changed in this PR.'); + } + + const body = summary.join('\n'); + + + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.issue.number, + body: body + }); diff --git a/code/package.json b/code/package.json index 7ea3e0532..b1e4529c4 100644 --- a/code/package.json +++ b/code/package.json @@ -5,7 +5,8 @@ "description": "", "main": "index.js", "scripts": { - "test": "node --import tsx --test '**/*.ts' --trace-deprecation" + "test": "node --import tsx --test '**/*.test.ts' --trace-deprecation", + "test:coverage": "node --import tsx --experimental-test-coverage --test '**/*.test.ts'" }, "keywords": [], "author": "", From 7e5c749c01f59a941de52a93520120b4352a6595 Mon Sep 17 00:00:00 2001 From: Ayush Date: Fri, 25 Oct 2024 06:48:55 +0530 Subject: [PATCH 5/7] gh Action --- .github/workflows/code:test.yml | 63 +++++++++++---------------------- 1 file changed, 21 insertions(+), 42 deletions(-) diff --git a/.github/workflows/code:test.yml b/.github/workflows/code:test.yml index 67b941632..8387b6530 100644 --- a/.github/workflows/code:test.yml +++ b/.github/workflows/code:test.yml @@ -59,51 +59,30 @@ jobs: exit 0 fi - # Convert the space-separated list to an array - readarray -t test_files <<< "$CHANGED_FILES" - - # Print the files we're going to test - echo "Running tests for the following files:" - printf '%s\n' "${test_files[@]}" - - # Run all test files in a single command - node --import tsx --test "${test_files[@]}" || exit 1 + # Create array for test files + declare -a test_files=() + + # Collect only .test.ts files + for file in $CHANGED_FILES; do + if [[ $file == *.test.ts ]]; then + test_files+=("$file") + echo "Added test file: $file" + fi + done + + # If we found test files, run them together + if [ ${#test_files[@]} -gt 0 ]; then + echo "Running tests for the following files:" + printf '%s\n' "${test_files[@]}" + + # Run all test files in a single command + node --import tsx --test "${test_files[@]}" || exit 1 + else + echo "No test files to run" + fi - name: Run all tests if: github.event_name == 'schedule' run: | echo "Running all tests in code directory" pnpm turbo test - - - name: Report test results - if: always() && github.event_name == 'pull_request' - uses: actions/github-script@v7 - with: - script: | - const { context, github } = require('@actions/github'); - - const testStatus = process.env.TEST_SUCCESS === 'true' ? '✅' : '❌'; - const summary = []; - - summary.push(`# Test Results ${testStatus}`); - summary.push(''); - - const changedFiles = '${{ steps.changed-files.outputs.all_changed_files }}'.split(' '); - if (changedFiles.length > 0 && changedFiles[0] !== '') { - summary.push('## Changed Test Files:'); - changedFiles.forEach(file => { - summary.push(`- \`${file}\``); - }); - } else { - summary.push('No test files were changed in this PR.'); - } - - const body = summary.join('\n'); - - - await github.rest.issues.createComment({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: context.issue.number, - body: body - }); From 20710fb656db337bd25ba4017e725af7f88de5bd Mon Sep 17 00:00:00 2001 From: Ayush Date: Fri, 25 Oct 2024 06:53:58 +0530 Subject: [PATCH 6/7] ghAction returns --- .github/workflows/code:test.yml | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/.github/workflows/code:test.yml b/.github/workflows/code:test.yml index 8387b6530..d641fb587 100644 --- a/.github/workflows/code:test.yml +++ b/.github/workflows/code:test.yml @@ -12,10 +12,6 @@ jobs: snippets-tests: runs-on: ubuntu-latest - defaults: - run: - working-directory: ./code # Set working directory to code folder - strategy: matrix: node-version: [20] @@ -34,21 +30,21 @@ jobs: files: | code/**/*.test.ts + - name: Install pnpm + uses: pnpm/action-setup@v4 + - name: Use Node.js ${{ matrix.node-version }} uses: actions/setup-node@v4 with: node-version: ${{ matrix.node-version }} cache: "pnpm" - - name: Install pnpm - uses: pnpm/action-setup@v2 - with: - version: 8 - run_install: false - - name: Install dependencies run: pnpm install + - name: Change Directory + run: cd code + - name: Run tests on changed files if: github.event_name == 'pull_request' run: | From ba64c83acb239fa6be4488ea9991ccdf54bee517 Mon Sep 17 00:00:00 2001 From: Ayush Date: Wed, 6 Nov 2024 19:03:26 +0530 Subject: [PATCH 7/7] add section for testing in contributing --- CONTRIBUTING.md | 70 ++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 67 insertions(+), 3 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index bb8c7c0f7..1fb34954e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -23,7 +23,7 @@ transparent as possible, whether it's: - content translations are supported via Crowdin - code blocks must use code-import for file snippets (via filesystem) - code file should be [tests](https://nodejs.org/api/test.html) and should add - code ranges instead of whole test file + code ranges instead of whole file ## Style guidelines @@ -285,8 +285,8 @@ source files. To use code-import, follow these steps: -Ensure your code file is a test file located in the appropriate directory within -the repo. Use the following syntax to import code snippets: +Ensure your code file is located in the appropriate directory within the repo. +Use the following syntax to import code snippets: ```javascript file="/path/to/your/file.ts#L1-L10,#L15-L20" @@ -1012,3 +1012,67 @@ pnpm dev ``` > Note: The developer content API normally runs locally on port `3001` + +## Testing + +The repository uses Node's native test runner for testing code examples. This +ensures all code snippets are correct and working as expected. + +### Test Structure + +There are two supported ways to structure your tests: + +1. **Adjacent test files** - For simpler modules with a single code file: + + ``` + pda/ + ├── index.ts + └── index.test.ts + ``` + +2. **Tests directory** - For modules with multiple code files or complex test + cases: + ``` + pda/ + ├── index.ts + ├── utils.ts + └── tests/ + ├── index.test.ts + └── utils.test.ts + ``` + +Choose the structure that best fits your needs: + +- Use adjacent test files (`index.test.ts`) when you have a simple 1:1 + relationship between code and test files +- Use a `tests` directory when you have multiple code files that need testing or + need multiple test files for a single code file + +### Running Tests + +You can run tests in two ways: + +1. Test the entire codebase: + + ```shell + pnpm install + pnpm turbo test + ``` + +2. Test specific code imports: + ```shell + cd code + pnpm install + pnpm turbo test + ``` + +### Writing Tests + +We use [Behavior-Driven Development (BDD)](https://cucumber.io/docs/bdd/) style +testing, which uses `describe` and `it` blocks to create readable test +descriptions. When tests run, they read like natural sentences. + +Example: +[generate-mnemonic.test.ts](https://github.com/solana-foundation/developer-content/blob/master/code/content/cookbook/wallets/tests/generate-mnemonic.test.ts) + +All tests should handle both success and failure cases appropriately.