From 04166b6762f0e37f3cb1a0240876e11d461ca568 Mon Sep 17 00:00:00 2001 From: Anurag Date: Tue, 28 Jul 2026 00:54:35 +0530 Subject: [PATCH 01/12] feat(deps): install @noble/ciphers and @noble/hashes --- package-lock.json | 14 ++++++++++++++ package.json | 2 ++ 2 files changed, 16 insertions(+) diff --git a/package-lock.json b/package-lock.json index 2966c08..d8e589e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,6 +13,8 @@ "examples/*" ], "dependencies": { + "@noble/ciphers": "^2.2.0", + "@noble/hashes": "^2.2.0", "aws4fetch": "^1.0.20", "kubo-rpc-client": "^7.1.0", "multiformats": "^14.0.0" @@ -1230,6 +1232,18 @@ "@multiformats/multiaddr": "^13.0.0" } }, + "node_modules/@noble/ciphers": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@noble/ciphers/-/ciphers-2.2.0.tgz", + "integrity": "sha512-Z6pjIZ/8IJcCGzb2S/0Px5J81yij85xASuk1teLNeg75bfT07MV3a/O2Mtn1I2se43k3lkVEcFaR10N4cgQcZA==", + "license": "MIT", + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/@noble/curves": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-2.2.0.tgz", diff --git a/package.json b/package.json index af77db0..4b44b3e 100644 --- a/package.json +++ b/package.json @@ -89,6 +89,8 @@ "access": "public" }, "dependencies": { + "@noble/ciphers": "^2.2.0", + "@noble/hashes": "^2.2.0", "aws4fetch": "^1.0.20", "kubo-rpc-client": "^7.1.0", "multiformats": "^14.0.0" From eb8d35c76236fc846882dc8a60f76495c2ac5b95 Mon Sep 17 00:00:00 2001 From: Anurag Date: Tue, 28 Jul 2026 00:58:06 +0530 Subject: [PATCH 02/12] =?UTF-8?q?feat(core):=20add=20crypto.ts=20=E2=80=94?= =?UTF-8?q?=20AES-256-GCM=20+=20PBKDF2=20utility=20module?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Implement encrypt(data, opts) and decrypt(data, password) using @noble/ciphers (AES-256-GCM) and @noble/hashes (PBKDF2-SHA256) - EMSH wire format: MAGIC(4)|VERSION(1)|ITERATIONS(4)|SALT(16)|NONCE(12)|CT+TAG - Fresh random 128-bit salt + 96-bit nonce on every encrypt() call - isEncryptedPayload() fast structural check on magic/version bytes - DEFAULT_ITERATIONS = 200_000 (OWASP 2023 PBKDF2-SHA256 minimum) - Export encrypt/decrypt/isEncryptedPayload/DEFAULT_ITERATIONS from core index - Add @noble/* to tsup external list (runtime deps, not bundled) --- packages/core/src/crypto.ts | 249 ++++++++++++++++++++++++++++++++++++ packages/core/src/index.ts | 3 + tsup.config.ts | 4 +- 3 files changed, 254 insertions(+), 2 deletions(-) create mode 100644 packages/core/src/crypto.ts diff --git a/packages/core/src/crypto.ts b/packages/core/src/crypto.ts new file mode 100644 index 0000000..369576c --- /dev/null +++ b/packages/core/src/crypto.ts @@ -0,0 +1,249 @@ +/** + * Encryption utilities for IPFS-Meshkit. + * + * Algorithm: AES-256-GCM (authenticated encryption) + * KDF: PBKDF2-SHA256 (password → 256-bit key) + * RNG: globalThis.crypto.getRandomValues (CSPRNG, Node ≥ 20 / browsers / RN) + * + * Wire format (all fields are fixed-width, big-endian where applicable): + * + * ┌────────┬─────────┬────────────┬──────────┬──────────┬──────────────────────┐ + * │ MAGIC │ VERSION │ ITERATIONS │ SALT │ NONCE │ CIPHERTEXT + TAG │ + * │ 4 B │ 1 B │ 4 B uint32 │ 16 B │ 12 B │ n + 16 B │ + * │ "EMSH" │ 0x01 │ big-endian │ random │ random │ AES-256-GCM output │ + * └────────┴─────────┴────────────┴──────────┴──────────┴──────────────────────┘ + * Header = 37 bytes. Minimum valid payload = 53 bytes (header + empty plaintext tag). + * + * Security properties: + * - Every encrypt() call generates a fresh random salt and nonce. + * Same plaintext + same password → always different ciphertext. + * - AES-GCM provides authenticated encryption: any bit-flip in the + * ciphertext or header salt/nonce/iterations causes decryption to throw. + * - PBKDF2 with high iteration count slows offline brute-force attacks. + * - isEncryptedPayload() is a public predicate — it only checks the magic + * bytes, never the password. No timing secrets are involved. + * + * Audited dependencies: + * - @noble/ciphers (Cure53 audit: https://cure53.de/pentest-report_noble-crypto.pdf) + * - @noble/hashes (same audit) + */ + +import { gcm } from '@noble/ciphers/aes.js'; +import { pbkdf2Async } from '@noble/hashes/pbkdf2.js'; +import { sha256 } from '@noble/hashes/sha2.js'; +import { MeshkitError } from './types.js'; + +// --------------------------------------------------------------------------- +// Wire-format constants +// --------------------------------------------------------------------------- + +/** 4-byte magic: ASCII "EMSH" (Encrypted MeSHkit). */ +const MAGIC = new Uint8Array([0x45, 0x4d, 0x53, 0x48]); + +const VERSION = 0x01; + +// Header byte offsets +const OFF_VERSION = 4; // 1 byte +const OFF_ITERATIONS = 5; // 4 bytes, uint32 big-endian +const OFF_SALT = 9; // 16 bytes +const OFF_NONCE = 25; // 12 bytes +const HEADER_LEN = 37; // total header size + +const SALT_LEN = 16; // 128-bit salt +const NONCE_LEN = 12; // 96-bit nonce (GCM standard) +const KEY_LEN = 32; // 256-bit key (AES-256) +const GCM_TAG_LEN = 16; // 128-bit authentication tag + +/** Minimum byte length of a valid encrypted payload (empty plaintext). */ +const MIN_PAYLOAD_LEN = HEADER_LEN + GCM_TAG_LEN; // 53 + +/** Default PBKDF2 iteration count. 200k is the OWASP 2023 minimum for PBKDF2-SHA256. */ +export const DEFAULT_ITERATIONS = 200_000; + +// --------------------------------------------------------------------------- +// Public interface +// --------------------------------------------------------------------------- + +export interface EncryptOptions { + /** + * Passphrase used to derive the AES-256-GCM encryption key via PBKDF2-SHA256. + * Must be a non-empty string. The passphrase is encoded as UTF-8 before hashing. + */ + password: string; + + /** + * PBKDF2 iteration count. Defaults to 200,000. + * Higher values increase brute-force resistance at the cost of encrypt/decrypt time. + * Must be a positive integer ≤ 4,294,967,295 (uint32 max). + */ + iterations?: number; +} + +// --------------------------------------------------------------------------- +// Helpers (not exported — keep the public API surface minimal) +// --------------------------------------------------------------------------- + +/** + * Convert a password string to bytes using UTF-8. + * We never work with the password as a raw string after this point. + */ +function passwordToBytes(password: string): Uint8Array { + return new TextEncoder().encode(password); +} + +/** + * Generate cryptographically secure random bytes using the platform CSPRNG. + * Works identically on Node.js ≥ 20, browsers, and React Native. + */ +function secureRandom(byteLength: number): Uint8Array { + const buf = new Uint8Array(byteLength); + globalThis.crypto.getRandomValues(buf); + return buf; +} + +/** + * Derive a 256-bit AES key from a password + salt using PBKDF2-SHA256. + * Uses the async variant to avoid blocking the event loop during high iteration counts. + */ +async function deriveKey( + password: string, + salt: Uint8Array, + iterations: number, +): Promise { + return pbkdf2Async(sha256, passwordToBytes(password), salt, { + c: iterations, + dkLen: KEY_LEN, + }); +} + +/** Validate the iterations parameter and throw a descriptive error if invalid. */ +function validateIterations(iterations: number): void { + if ( + !Number.isInteger(iterations) || + iterations < 1 || + iterations > 0xffffffff + ) { + throw new MeshkitError( + `iterations must be a positive integer ≤ 4,294,967,295, got: ${iterations}`, + ); + } +} + +// --------------------------------------------------------------------------- +// Public API +// --------------------------------------------------------------------------- + +/** + * Returns `true` when `data` looks like an EMSH encrypted payload. + * + * This is a fast structural check — it only verifies the 4-byte magic prefix + * and the version byte. It does **not** verify the password or authenticate + * the ciphertext. Use it to decide whether to attempt decryption. + */ +export function isEncryptedPayload(data: Uint8Array): boolean { + if (data.length < MIN_PAYLOAD_LEN) return false; + return ( + data[0] === MAGIC[0] && + data[1] === MAGIC[1] && + data[2] === MAGIC[2] && + data[3] === MAGIC[3] && + data[4] === VERSION + ); +} + +/** + * Encrypt `data` using AES-256-GCM with a PBKDF2-SHA256 derived key. + * + * Every invocation generates a fresh random 128-bit salt and 96-bit nonce, + * so the same `data` + `password` will always produce different output. + * + * @returns The encrypted payload in EMSH wire format (header + ciphertext + tag). + * @throws MeshkitError if `options.iterations` is out of range. + */ +export async function encrypt( + data: Uint8Array, + opts: EncryptOptions, +): Promise { + if (!opts.password) { + throw new MeshkitError('password must be a non-empty string'); + } + + const iterations = opts.iterations ?? DEFAULT_ITERATIONS; + validateIterations(iterations); + + // Fresh random material for every encrypt call. + const salt = secureRandom(SALT_LEN); + const nonce = secureRandom(NONCE_LEN); + + const key = await deriveKey(opts.password, salt, iterations); + + // AES-256-GCM encrypt — appends the 16-byte authentication tag to the ciphertext. + const ciphertext = gcm(key, nonce).encrypt(data); + + // Build the 37-byte header. + const header = new Uint8Array(HEADER_LEN); + const view = new DataView(header.buffer); + header.set(MAGIC, 0); + header[OFF_VERSION] = VERSION; + view.setUint32(OFF_ITERATIONS, iterations, false /* big-endian */); + header.set(salt, OFF_SALT); + header.set(nonce, OFF_NONCE); + + // Concatenate header + ciphertext+tag into a single buffer. + const payload = new Uint8Array(HEADER_LEN + ciphertext.length); + payload.set(header, 0); + payload.set(ciphertext, HEADER_LEN); + return payload; +} + +/** + * Decrypt an EMSH payload created by {@link encrypt}. + * + * @returns The original plaintext bytes. + * @throws MeshkitError if the payload is not a valid EMSH blob, + * if the password is wrong, or if the ciphertext has been tampered with. + * The error message intentionally does not distinguish between a wrong + * password and a corrupted payload to avoid oracle attacks. + */ +export async function decrypt( + data: Uint8Array, + password: string, +): Promise { + if (!isEncryptedPayload(data)) { + throw new MeshkitError( + 'Data is not an encrypted meshkit payload (EMSH magic bytes not found)', + ); + } + + if (!password) { + throw new MeshkitError('password must be a non-empty string'); + } + + // Parse the header. Use a DataView that respects a potential non-zero + // byteOffset (e.g. if the caller passed a subarray view). + const view = new DataView(data.buffer, data.byteOffset, data.byteLength); + const iterations = view.getUint32(OFF_ITERATIONS, false /* big-endian */); + + // slice() creates owned copies — safe to hand to noble even if data is a view. + const salt = data.slice(OFF_SALT, OFF_NONCE); + const nonce = data.slice(OFF_NONCE, HEADER_LEN); + const ciphertext = data.slice(HEADER_LEN); + + if (ciphertext.length < GCM_TAG_LEN) { + throw new MeshkitError('Encrypted payload is truncated or corrupt'); + } + + const key = await deriveKey(password, salt, iterations); + + try { + // gcm().decrypt() verifies the GCM authentication tag and throws if it + // does not match — this is the "wrong password" / "tampering" guard. + return gcm(key, nonce).decrypt(ciphertext); + } catch { + // Re-throw as MeshkitError with a generic message to avoid oracle attacks. + // We intentionally do not forward the underlying error message. + throw new MeshkitError( + 'Decryption failed: wrong password or corrupted data', + ); + } +} diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index bcd2a94..fe644fc 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -20,3 +20,6 @@ export type { S3StorageConfig, FilOneConfig } from './create-filone-client.js'; export { IPNS_TTL_DEFAULT, IPNS_TTL_FAST } from './ipns/constants.js'; export { extractCidFromPath, toIpfsPath, toIpnsPath } from './ipns/paths.js'; + +export { encrypt, decrypt, isEncryptedPayload, DEFAULT_ITERATIONS } from './crypto.js'; +export type { EncryptOptions } from './crypto.js'; diff --git a/tsup.config.ts b/tsup.config.ts index 564f7fe..556e194 100644 --- a/tsup.config.ts +++ b/tsup.config.ts @@ -36,7 +36,7 @@ export default defineConfig([ }, dts: sharedDts, noExternal: [/^@ipfs-meshkit\//], - external: ['kubo-rpc-client', 'aws4fetch', 'multiformats'], + external: ['kubo-rpc-client', 'aws4fetch', 'multiformats', '@noble/ciphers', '@noble/hashes'], }, // Browser build — only @ipfs-meshkit/core; no Node.js built-ins. { @@ -51,6 +51,6 @@ export default defineConfig([ }, dts: sharedDts, noExternal: [/^@ipfs-meshkit\//], - external: ['kubo-rpc-client', 'aws4fetch', 'multiformats'], + external: ['kubo-rpc-client', 'aws4fetch', 'multiformats', '@noble/ciphers', '@noble/hashes'], }, ]); From 2ab471d7613e423fd13557b9f59d33aded0f1feb Mon Sep 17 00:00:00 2001 From: Anurag Date: Tue, 28 Jul 2026 00:59:50 +0530 Subject: [PATCH 03/12] test(core): 36 unit tests for crypto.ts Tests cover: - isEncryptedPayload: magic/version detection, edge cases (empty, subarray, wrong version) - encrypt: output size, EMSH validity, random salt/nonce, empty payload, 1MB payload, custom/default iterations in wire format, all error conditions - decrypt: round-trip for text/JSON/binary/empty/1MB, self-describing iterations, subarray byteOffset handling, wrong password, tampered ciphertext, corrupted salt/nonce, truncated payload, generic error message (anti-oracle) --- packages/core/src/crypto.ts | 6 +- packages/core/test/crypto.test.ts | 279 ++++++++++++++++++++++++++++++ 2 files changed, 284 insertions(+), 1 deletion(-) create mode 100644 packages/core/test/crypto.test.ts diff --git a/packages/core/src/crypto.ts b/packages/core/src/crypto.ts index 369576c..782cd07 100644 --- a/packages/core/src/crypto.ts +++ b/packages/core/src/crypto.ts @@ -94,10 +94,14 @@ function passwordToBytes(password: string): Uint8Array { /** * Generate cryptographically secure random bytes using the platform CSPRNG. * Works identically on Node.js ≥ 20, browsers, and React Native. + * Fills in 65,536-byte chunks to respect the Web Crypto API quota per call. */ function secureRandom(byteLength: number): Uint8Array { const buf = new Uint8Array(byteLength); - globalThis.crypto.getRandomValues(buf); + const CHUNK = 65_536; + for (let offset = 0; offset < byteLength; offset += CHUNK) { + globalThis.crypto.getRandomValues(buf.subarray(offset, offset + CHUNK)); + } return buf; } diff --git a/packages/core/test/crypto.test.ts b/packages/core/test/crypto.test.ts new file mode 100644 index 0000000..3d10ad8 --- /dev/null +++ b/packages/core/test/crypto.test.ts @@ -0,0 +1,279 @@ +import { describe, expect, it } from 'vitest'; +import { + DEFAULT_ITERATIONS, + decrypt, + encrypt, + isEncryptedPayload, +} from '../src/crypto.js'; +import { MeshkitError } from '../src/types.js'; + +// --------------------------------------------------------------------------- +// Helpers +// --------------------------------------------------------------------------- + +const PASSWORD = 'correct-horse-battery-staple'; +const PLAINTEXT = new TextEncoder().encode('Hello, IPFS-Meshkit encryption!'); +const EMPTY = new Uint8Array(0); + +/** Encrypt with default options and low iterations so tests run fast. */ +function fastEncrypt(data: Uint8Array, password = PASSWORD) { + return encrypt(data, { password, iterations: 1 }); +} + +// --------------------------------------------------------------------------- +// isEncryptedPayload +// --------------------------------------------------------------------------- + +describe('isEncryptedPayload', () => { + it('returns false for an empty buffer', () => { + expect(isEncryptedPayload(new Uint8Array(0))).toBe(false); + }); + + it('returns false for a short buffer (< 53 bytes)', () => { + expect(isEncryptedPayload(new Uint8Array(52))).toBe(false); + }); + + it('returns false for a buffer with wrong magic bytes', () => { + const buf = new Uint8Array(60); + buf[0] = 0xde; buf[1] = 0xad; buf[2] = 0xbe; buf[3] = 0xef; + buf[4] = 0x01; + expect(isEncryptedPayload(buf)).toBe(false); + }); + + it('returns false for a buffer with correct magic but wrong version byte', () => { + const buf = new Uint8Array(60); + // EMSH magic + buf[0] = 0x45; buf[1] = 0x4d; buf[2] = 0x53; buf[3] = 0x48; + buf[4] = 0x02; // wrong version + expect(isEncryptedPayload(buf)).toBe(false); + }); + + it('returns false for arbitrary text content', () => { + const text = new TextEncoder().encode('{"ipfs":"content","key":"value"}'); + expect(isEncryptedPayload(text)).toBe(false); + }); + + it('returns true for a buffer produced by encrypt()', async () => { + const payload = await fastEncrypt(PLAINTEXT); + expect(isEncryptedPayload(payload)).toBe(true); + }); + + it('returns true when passed a subarray (non-zero byteOffset) of an encrypted payload', async () => { + const wrapper = new Uint8Array(100); + const payload = await fastEncrypt(PLAINTEXT); + // Place encrypted payload at offset 10 inside wrapper + wrapper.set(payload, 10); + expect(isEncryptedPayload(wrapper.subarray(10))).toBe(true); + }); +}); + +// --------------------------------------------------------------------------- +// encrypt +// --------------------------------------------------------------------------- + +describe('encrypt', () => { + it('returns a Uint8Array longer than the plaintext', async () => { + const payload = await fastEncrypt(PLAINTEXT); + // overhead = 37 (header) + 16 (GCM tag) = 53 bytes + expect(payload).toBeInstanceOf(Uint8Array); + expect(payload.length).toBe(PLAINTEXT.length + 53); + }); + + it('always produces a valid EMSH payload', async () => { + const payload = await fastEncrypt(PLAINTEXT); + expect(isEncryptedPayload(payload)).toBe(true); + }); + + it('produces different ciphertexts for the same plaintext + password (random salt/nonce)', async () => { + const a = await fastEncrypt(PLAINTEXT); + const b = await fastEncrypt(PLAINTEXT); + // Payloads must differ (probability of collision is astronomically low) + expect(Buffer.from(a).toString('hex')).not.toBe(Buffer.from(b).toString('hex')); + }); + + it('produces different ciphertexts for different plaintexts', async () => { + const a = await fastEncrypt(new TextEncoder().encode('file-a')); + const b = await fastEncrypt(new TextEncoder().encode('file-b')); + expect(Buffer.from(a).toString('hex')).not.toBe(Buffer.from(b).toString('hex')); + }); + + it('handles an empty plaintext (Uint8Array of length 0)', async () => { + const payload = await fastEncrypt(EMPTY); + expect(payload).toBeInstanceOf(Uint8Array); + // header(37) + tag(16) = 53 bytes + expect(payload.length).toBe(53); + expect(isEncryptedPayload(payload)).toBe(true); + }); + + it('handles a large payload (1 MB)', async () => { + const large = new Uint8Array(1024 * 1024); + // getRandomValues is limited to 65_536 bytes per call — fill in chunks + for (let offset = 0; offset < large.length; offset += 65_536) { + globalThis.crypto.getRandomValues(large.subarray(offset, offset + 65_536)); + } + const payload = await fastEncrypt(large); + expect(payload.length).toBe(large.length + 53); + expect(isEncryptedPayload(payload)).toBe(true); + }); + + it('stores a custom iterations value in the wire format', async () => { + const CUSTOM_ITER = 12_345; + const payload = await encrypt(PLAINTEXT, { password: PASSWORD, iterations: CUSTOM_ITER }); + // Iterations field is at bytes [5..8] as uint32 big-endian + const view = new DataView(payload.buffer); + expect(view.getUint32(5, false)).toBe(CUSTOM_ITER); + }); + + it('stores the default iterations (200_000) when none is provided', async () => { + const payload = await encrypt(PLAINTEXT, { password: PASSWORD }); + const view = new DataView(payload.buffer); + expect(view.getUint32(5, false)).toBe(DEFAULT_ITERATIONS); + }); + + it('throws MeshkitError for an empty password', async () => { + await expect(encrypt(PLAINTEXT, { password: '' })).rejects.toBeInstanceOf(MeshkitError); + }); + + it('throws MeshkitError for iterations = 0', async () => { + await expect( + encrypt(PLAINTEXT, { password: PASSWORD, iterations: 0 }), + ).rejects.toBeInstanceOf(MeshkitError); + }); + + it('throws MeshkitError for non-integer iterations', async () => { + await expect( + encrypt(PLAINTEXT, { password: PASSWORD, iterations: 1.5 }), + ).rejects.toBeInstanceOf(MeshkitError); + }); + + it('throws MeshkitError for iterations > uint32 max', async () => { + await expect( + encrypt(PLAINTEXT, { password: PASSWORD, iterations: 0x1_0000_0000 }), + ).rejects.toBeInstanceOf(MeshkitError); + }); +}); + +// --------------------------------------------------------------------------- +// decrypt +// --------------------------------------------------------------------------- + +describe('decrypt', () => { + it('round-trips plaintext through encrypt → decrypt', async () => { + const payload = await fastEncrypt(PLAINTEXT); + const recovered = await decrypt(payload, PASSWORD); + expect(recovered).toEqual(PLAINTEXT); + }); + + it('round-trips an empty plaintext', async () => { + const payload = await fastEncrypt(EMPTY); + const recovered = await decrypt(payload, PASSWORD); + expect(recovered).toEqual(EMPTY); + }); + + it('round-trips a 1 MB payload', async () => { + const large = new Uint8Array(1024 * 1024); + // getRandomValues is limited to 65_536 bytes per call — fill in chunks + for (let offset = 0; offset < large.length; offset += 65_536) { + globalThis.crypto.getRandomValues(large.subarray(offset, offset + 65_536)); + } + const payload = await fastEncrypt(large); + const recovered = await decrypt(payload, PASSWORD); + expect(recovered).toEqual(large); + }); + + it('round-trips binary data (simulated PDF magic bytes)', async () => { + // %PDF-1.4 header + const pdfLike = new Uint8Array([0x25, 0x50, 0x44, 0x46, 0x2d, 0x31, 0x2e, 0x34]); + const payload = await fastEncrypt(pdfLike); + const recovered = await decrypt(payload, PASSWORD); + expect(recovered).toEqual(pdfLike); + }); + + it('round-trips JSON content', async () => { + const json = new TextEncoder().encode(JSON.stringify({ mscCode: 'MSC1234567', vessel: 'Ever Given' })); + const payload = await fastEncrypt(json); + const recovered = await decrypt(payload, PASSWORD); + expect(new TextDecoder().decode(recovered)).toBe(JSON.stringify({ mscCode: 'MSC1234567', vessel: 'Ever Given' })); + }); + + it('reads the correct iterations from the wire format (self-describing)', async () => { + // Encrypt with custom iterations, then decrypt without knowing iterations up-front. + // The decoder should read iterations from the payload header. + const CUSTOM_ITER = 5_000; + const payload = await encrypt(PLAINTEXT, { password: PASSWORD, iterations: CUSTOM_ITER }); + const recovered = await decrypt(payload, PASSWORD); + expect(recovered).toEqual(PLAINTEXT); + }); + + it('works correctly when passed a subarray with non-zero byteOffset', async () => { + const payload = await fastEncrypt(PLAINTEXT); + // Prepend 8 bytes of garbage to test byteOffset handling + const wrapped = new Uint8Array(8 + payload.length); + wrapped.set(payload, 8); + const recovered = await decrypt(wrapped.subarray(8), PASSWORD); + expect(recovered).toEqual(PLAINTEXT); + }); + + it('throws MeshkitError on wrong password', async () => { + const payload = await fastEncrypt(PLAINTEXT); + await expect(decrypt(payload, 'wrong-password')).rejects.toBeInstanceOf(MeshkitError); + }); + + it('throws MeshkitError with a generic message (no oracle)', async () => { + const payload = await fastEncrypt(PLAINTEXT); + const err = await decrypt(payload, 'wrong-password').catch((e: unknown) => e); + expect(err).toBeInstanceOf(MeshkitError); + // Must not say "wrong password" in isolation — that helps offline attackers + expect((err as MeshkitError).message).toContain('Decryption failed'); + }); + + it('throws MeshkitError for raw (non-encrypted) bytes', async () => { + await expect(decrypt(PLAINTEXT, PASSWORD)).rejects.toBeInstanceOf(MeshkitError); + }); + + it('throws MeshkitError for a truncated payload (missing GCM tag)', async () => { + const payload = await fastEncrypt(PLAINTEXT); + // Slice off the last 20 bytes to break the GCM tag + const truncated = payload.slice(0, payload.length - 20); + await expect(decrypt(truncated, PASSWORD)).rejects.toBeInstanceOf(MeshkitError); + }); + + it('throws MeshkitError for a payload with a single bit flipped in the ciphertext', async () => { + const payload = await fastEncrypt(PLAINTEXT); + const tampered = new Uint8Array(payload); + // Flip a bit in the ciphertext section (after the 37-byte header) + tampered[40] ^= 0x01; + await expect(decrypt(tampered, PASSWORD)).rejects.toBeInstanceOf(MeshkitError); + }); + + it('throws MeshkitError when salt is corrupted', async () => { + const payload = await fastEncrypt(PLAINTEXT); + const tampered = new Uint8Array(payload); + tampered[9] ^= 0xff; // salt starts at byte 9 + await expect(decrypt(tampered, PASSWORD)).rejects.toBeInstanceOf(MeshkitError); + }); + + it('throws MeshkitError when nonce is corrupted', async () => { + const payload = await fastEncrypt(PLAINTEXT); + const tampered = new Uint8Array(payload); + tampered[25] ^= 0xff; // nonce starts at byte 25 + await expect(decrypt(tampered, PASSWORD)).rejects.toBeInstanceOf(MeshkitError); + }); + + it('throws MeshkitError for an empty password', async () => { + const payload = await fastEncrypt(PLAINTEXT); + await expect(decrypt(payload, '')).rejects.toBeInstanceOf(MeshkitError); + }); + + it('two encrypts of the same plaintext both decrypt correctly (independent random state)', async () => { + const [p1, p2] = await Promise.all([fastEncrypt(PLAINTEXT), fastEncrypt(PLAINTEXT)]); + const [r1, r2] = await Promise.all([decrypt(p1, PASSWORD), decrypt(p2, PASSWORD)]); + expect(r1).toEqual(PLAINTEXT); + expect(r2).toEqual(PLAINTEXT); + }); + + it('decrypting p1 with password of p2 fails gracefully', async () => { + const p1 = await encrypt(PLAINTEXT, { password: 'password-one', iterations: 1 }); + await expect(decrypt(p1, 'password-two')).rejects.toBeInstanceOf(MeshkitError); + }); +}); From f4ef87c0af98ca1a2f3324a24cb51f02000dd65d Mon Sep 17 00:00:00 2001 From: Anurag Date: Tue, 28 Jul 2026 01:00:56 +0530 Subject: [PATCH 04/12] feat(core): add UploadOptions / RetrieveOptions types; extend MeshkitClient + Meshkit interfaces MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - UploadOptions.encrypt?: EncryptOptions — opt-in encryption on upload - RetrieveOptions.password?: string — opt-in decryption on retrieve - MeshkitClient.upload(data, options?) — backward-compatible signature - MeshkitClient.retrieve(cid, options?) — backward-compatible signature - Meshkit facade interface updated to match - Export UploadOptions / RetrieveOptions from core public index --- packages/core/src/index.ts | 2 ++ packages/core/src/types.ts | 74 +++++++++++++++++++++++++++++++++----- 2 files changed, 68 insertions(+), 8 deletions(-) diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index fe644fc..91800b7 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -3,6 +3,8 @@ export type { MeshkitConfig, MeshkitInitOptions, StoredObject, + UploadOptions, + RetrieveOptions, IpnsDuration, IpnsKey, IpnsKeyGenOptions, diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index 45e3308..3352274 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -5,6 +5,7 @@ import type { IpnsPublishResult, IpnsResolveOptions, } from './ipns/types.js'; +import type { EncryptOptions } from './crypto.js'; export type { IpnsDuration, @@ -15,6 +16,8 @@ export type { IpnsResolveOptions, } from './ipns/types.js'; +export type { EncryptOptions } from './crypto.js'; + export class MeshkitError extends Error { /** The individual errors collected from each node that was tried. */ readonly causes: Error[]; @@ -26,6 +29,46 @@ export class MeshkitError extends Error { } } +// --------------------------------------------------------------------------- +// Encryption option types +// --------------------------------------------------------------------------- + +/** + * Options controlling content encryption on upload. + * + * When provided, the raw bytes are encrypted with AES-256-GCM (key derived + * via PBKDF2-SHA256) before being sent to the IPFS/S3 backend. The CID is + * therefore computed from the *encrypted* bytes, not the plaintext. + * + * **Important:** the CID is the only handle to your encrypted file. + * It cannot be recomputed from the plaintext — store it alongside your + * application metadata. + */ +export interface UploadOptions { + /** + * If set, content is encrypted with AES-256-GCM before uploading. + * See {@link EncryptOptions} for details on the password and iteration count. + */ + encrypt?: EncryptOptions; +} + +/** + * Options controlling content decryption on retrieve. + * + * If the retrieved bytes are an EMSH encrypted payload and a `password` is + * provided, the payload is transparently decrypted before being returned. + * If no password is provided, the raw (possibly encrypted) bytes are returned. + */ +export interface RetrieveOptions { + /** + * Password to decrypt the retrieved bytes. + * Only used when the content is an EMSH encrypted payload + * (as produced by an upload with `encrypt` options). + * If the content is not encrypted this field is silently ignored. + */ + password?: string; +} + export interface MeshkitConfig { /** * Kubo RPC API base URL for a running IPFS node. @@ -49,11 +92,19 @@ export interface StoredObject { } export interface MeshkitClient { - /** Upload raw bytes to the connected IPFS node. Returns the CID string. */ - upload(data: Uint8Array): Promise; + /** + * Upload raw bytes to the connected IPFS node. Returns the CID string. + * If `options.encrypt` is provided the bytes are encrypted before upload; + * the CID identifies the encrypted blob, not the original plaintext. + */ + upload(data: Uint8Array, options?: UploadOptions): Promise; - /** Retrieve file contents from the connected IPFS node by CID. */ - retrieve(cid: string): Promise; + /** + * Retrieve file contents from the connected IPFS node by CID. + * If `options.password` is provided and the content is an EMSH encrypted + * payload it is automatically decrypted before being returned. + */ + retrieve(cid: string, options?: RetrieveOptions): Promise; /** Pin a CID on the connected IPFS node so it is not garbage-collected. */ pin(cid: string): Promise; @@ -117,11 +168,18 @@ export interface MeshkitInitOptions { } export interface Meshkit { - /** Upload raw bytes, trying each healthy node in priority order. */ - upload(data: Uint8Array): Promise; + /** + * Upload raw bytes, trying each healthy node in priority order. + * If `options.encrypt` is provided the bytes are encrypted before upload. + */ + upload(data: Uint8Array, options?: UploadOptions): Promise; - /** Retrieve file contents by CID, trying each healthy node in priority order. */ - retrieve(cid: string): Promise; + /** + * Retrieve file contents by CID, trying each healthy node in priority order. + * If `options.password` is provided and the content is encrypted it will be + * transparently decrypted before being returned. + */ + retrieve(cid: string, options?: RetrieveOptions): Promise; /** Pin a CID, trying each healthy node in priority order. */ pin(cid: string): Promise; From 72ef0870bbdb90e284b713b9ce46029932f09dd1 Mon Sep 17 00:00:00 2001 From: Anurag Date: Tue, 28 Jul 2026 01:01:36 +0530 Subject: [PATCH 05/12] feat(core): wire encryption into Kubo client (create-client.ts) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit upload(data, options?): - if options.encrypt is set, encrypt(data, opts) before ipfs.add() - CID is from Kubo over the encrypted blob retrieve(cid, options?): - reassemble streaming chunks as before - if options.password is set AND isEncryptedPayload(raw), decrypt transparently - no password → raw bytes returned unchanged (backward compatible) --- packages/core/src/create-client.ts | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/packages/core/src/create-client.ts b/packages/core/src/create-client.ts index 796f691..98797ab 100644 --- a/packages/core/src/create-client.ts +++ b/packages/core/src/create-client.ts @@ -1,4 +1,5 @@ import { create } from 'kubo-rpc-client'; +import { decrypt, encrypt, isEncryptedPayload } from './crypto.js'; import { extractCidFromPath, toIpfsPath, @@ -9,7 +10,7 @@ import type { IpnsPublishOptions, IpnsResolveOptions, } from './ipns/types.js'; -import type { MeshkitClient, MeshkitConfig, StoredObject } from './types.js'; +import type { MeshkitClient, MeshkitConfig, RetrieveOptions, StoredObject, UploadOptions } from './types.js'; import { MeshkitError } from './types.js'; function concatChunks(chunks: Uint8Array[], totalLength: number): Uint8Array { @@ -48,12 +49,17 @@ export function createMeshkitClient(config: MeshkitConfig): MeshkitClient { } return { - async upload(data: Uint8Array): Promise { - const { cid } = await ipfs.add(data, { pin: false }); + async upload(data: Uint8Array, options?: UploadOptions): Promise { + // Encrypt before sending to the node if requested. + // The CID is computed by Kubo from the encrypted bytes. + const payload = options?.encrypt + ? await encrypt(data, options.encrypt) + : data; + const { cid } = await ipfs.add(payload, { pin: false }); return cid.toString(); }, - async retrieve(cid: string): Promise { + async retrieve(cid: string, options?: RetrieveOptions): Promise { const chunks: Uint8Array[] = []; let totalLength = 0; @@ -62,7 +68,15 @@ export function createMeshkitClient(config: MeshkitConfig): MeshkitClient { totalLength += chunk.length; } - return concatChunks(chunks, totalLength); + const raw = concatChunks(chunks, totalLength); + + // Decrypt transparently if a password was supplied and the payload looks + // like an EMSH encrypted blob. If no password is given the raw bytes are + // returned as-is (allowing callers to inspect or forward the ciphertext). + if (options?.password && isEncryptedPayload(raw)) { + return decrypt(raw, options.password); + } + return raw; }, async pin(cid: string): Promise { From fa57fe90ae1c64bd23a84ecac4537b4b9b775929 Mon Sep 17 00:00:00 2001 From: Anurag Date: Tue, 28 Jul 2026 01:02:32 +0530 Subject: [PATCH 06/12] feat(core): wire encryption into S3 client (create-filone-client.ts) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit upload(data, options?): - if options.encrypt is set, encrypt before computing the CID - CID is sha256 of the encrypted blob (not the plaintext) - different passwords on same plaintext → different CIDs (by design) retrieve(cid, options?): - if options.password is set AND isEncryptedPayload(raw), decrypt - no password → raw bytes returned unchanged (backward compatible) Also fix pre-existing noUncheckedIndexedAccess TS errors in listAllObjects (match[1] defaulted to empty string; sizeStr defaulted to " 0\) --- packages/core/src/create-filone-client.ts | 33 ++++++++++++++++------- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/packages/core/src/create-filone-client.ts b/packages/core/src/create-filone-client.ts index 9c9358f..02ff0e1 100644 --- a/packages/core/src/create-filone-client.ts +++ b/packages/core/src/create-filone-client.ts @@ -2,8 +2,9 @@ import { AwsClient } from 'aws4fetch'; import { CID } from 'multiformats/cid'; import { sha256 } from 'multiformats/hashes/sha2'; import * as raw from 'multiformats/codecs/raw'; +import { decrypt, encrypt, isEncryptedPayload } from './crypto.js'; import { MeshkitError } from './types.js'; -import type { MeshkitClient, StoredObject } from './types.js'; +import type { MeshkitClient, RetrieveOptions, StoredObject, UploadOptions } from './types.js'; export interface S3StorageConfig { accessKeyId: string; @@ -110,9 +111,9 @@ export function createS3Client(config: S3StorageConfig): MeshkitClient { // Parse entries from S3 ListObjectsV2 XML response const contentsMatches = xml.matchAll(/([\s\S]*?)<\/Contents>/g); for (const match of contentsMatches) { - const block = match[1]; + const block = match[1] ?? ''; const key = block.match(/(.*?)<\/Key>/)?.[1]; - const sizeStr = block.match(/(.*?)<\/Size>/)?.[1]; + const sizeStr = block.match(/(.*?)<\/Size>/)?.[1] ?? '0'; const lastModStr = block.match(/(.*?)<\/LastModified>/)?.[1]; if (!key) continue; @@ -121,7 +122,7 @@ export function createS3Client(config: S3StorageConfig): MeshkitClient { results.push({ key, - size: sizeStr ? parseInt(sizeStr, 10) : 0, + size: parseInt(sizeStr, 10), lastModified: lastModStr ? new Date(lastModStr) : undefined, }); } @@ -135,12 +136,19 @@ export function createS3Client(config: S3StorageConfig): MeshkitClient { } return { - async upload(data: Uint8Array): Promise { - const cid = await computeCid(data); + async upload(data: Uint8Array, options?: UploadOptions): Promise { + // Encrypt before computing the CID if requested. + // The CID is derived from the encrypted bytes — different passwords on + // the same plaintext will produce different CIDs (by design). + const payload = options?.encrypt + ? await encrypt(data, options.encrypt) + : data; + + const cid = await computeCid(payload); const res = await aws.fetch(objectUrl(cid), { method: 'PUT', headers: { 'Content-Type': 'application/octet-stream' }, - body: data.buffer as BodyInit, + body: payload.buffer as BodyInit, }); if (!res.ok) { @@ -153,7 +161,7 @@ export function createS3Client(config: S3StorageConfig): MeshkitClient { return cid; }, - async retrieve(cid: string): Promise { + async retrieve(cid: string, options?: RetrieveOptions): Promise { const res = await aws.fetch(objectUrl(cid), { method: 'GET' }); if (!res.ok) { @@ -163,7 +171,14 @@ export function createS3Client(config: S3StorageConfig): MeshkitClient { ); } - return new Uint8Array(await res.arrayBuffer()); + const raw = new Uint8Array(await res.arrayBuffer()); + + // Decrypt transparently if a password was supplied and the payload looks + // like an EMSH encrypted blob. + if (options?.password && isEncryptedPayload(raw)) { + return decrypt(raw, options.password); + } + return raw; }, async pin(_cid: string): Promise {}, From 63c5d560c0c888f0d2eeea8dca0ce7bd403fbfa8 Mon Sep 17 00:00:00 2001 From: Anurag Date: Tue, 28 Jul 2026 01:03:09 +0530 Subject: [PATCH 07/12] feat(core): propagate UploadOptions/RetrieveOptions through Meshkit facade MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit upload(data, options?) and retrieve(cid, options?) pass options straight through to withFailover — no crypto logic in the facade layer. Backward compatible: all existing call sites work without changes. --- packages/core/src/meshkit.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/packages/core/src/meshkit.ts b/packages/core/src/meshkit.ts index ab61972..41e3ff0 100644 --- a/packages/core/src/meshkit.ts +++ b/packages/core/src/meshkit.ts @@ -11,7 +11,9 @@ import type { Meshkit as MeshkitFacade, MeshkitClient, MeshkitInitOptions, + RetrieveOptions, StoredObject, + UploadOptions, } from './types.js'; export class Meshkit implements MeshkitFacade { @@ -50,12 +52,12 @@ export class Meshkit implements MeshkitFacade { return new Meshkit(healthy.clients, healthy.urls); } - upload(data: Uint8Array): Promise { - return withFailover(this.clients, (client) => client.upload(data)); + upload(data: Uint8Array, options?: UploadOptions): Promise { + return withFailover(this.clients, (client) => client.upload(data, options)); } - retrieve(cid: string): Promise { - return withFailover(this.clients, (client) => client.retrieve(cid)); + retrieve(cid: string, options?: RetrieveOptions): Promise { + return withFailover(this.clients, (client) => client.retrieve(cid, options)); } pin(cid: string): Promise { From 29d9edc0263a2227ab52c522c735f55eda1bf07d Mon Sep 17 00:00:00 2001 From: Anurag Date: Tue, 28 Jul 2026 01:11:59 +0530 Subject: [PATCH 08/12] feat(mcp): add password/pbkdf2Iterations params to MCP storage tools schemas/storage.ts: - Add uploadShape (raw ZodRawShapeCompat) for MCP registration - uploadSchema wraps uploadShape with .refine() for type inference - Add password? and pbkdf2Iterations? to uploadShape - Add password? to retrieveSchema - Fix pre-existing ZodEffects issue: registerStorageTools now uses uploadShape (not uploadSchema) for server.tool() registration tools/storage.ts: - handleUpload: build UploadOptions from password + pbkdf2Iterations - handleRetrieve: build RetrieveOptions from password - Add encrypted: bool to upload result for caller awareness meshkit/src/index.ts: - Re-export UploadOptions, RetrieveOptions, EncryptOptions, encrypt, decrypt, isEncryptedPayload, DEFAULT_ITERATIONS packages/core/package.json, packages/node/package.json: - Add types + exports fields so tsc --build can resolve workspace packages --- packages/core/package.json | 8 +++++ packages/mcp/src/schemas/storage.ts | 55 +++++++++++++++++++++++------ packages/mcp/src/tools/storage.ts | 31 ++++++++++++---- packages/meshkit/src/index.ts | 8 ++++- packages/node/package.json | 8 +++++ 5 files changed, 93 insertions(+), 17 deletions(-) diff --git a/packages/core/package.json b/packages/core/package.json index 03138e7..5d0feab 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -4,6 +4,14 @@ "private": true, "description": "Core TypeScript SDK for IPFS Meshkit (bundled into root @ipfs-meshkit/meshkit).", "type": "module", + "types": "./dist/index.d.ts", + "exports": { + ".": { + "types": "./dist/index.d.ts", + "import": "./dist/index.js", + "require": "./dist/index.cjs" + } + }, "scripts": { "clean": "rm -rf dist" }, diff --git a/packages/mcp/src/schemas/storage.ts b/packages/mcp/src/schemas/storage.ts index 4da74bf..c1160bd 100644 --- a/packages/mcp/src/schemas/storage.ts +++ b/packages/mcp/src/schemas/storage.ts @@ -1,22 +1,57 @@ import { z } from 'zod'; +/** + * Raw Zod shape for the upload tool. + * Used for MCP tool registration (which expects ZodRawShapeCompat). + */ +export const uploadShape = { + content: z + .string() + .optional() + .describe('UTF-8 text content to upload'), + base64: z + .string() + .optional() + .describe('Base64-encoded binary content to upload'), + password: z + .string() + .optional() + .describe( + 'If provided, content is encrypted with AES-256-GCM (PBKDF2-SHA256 key) ' + + 'before upload. The CID identifies the encrypted blob. ' + + 'Only those who know this password can decrypt the file.', + ), + pbkdf2Iterations: z + .number() + .int() + .min(1) + .max(0xffffffff) + .optional() + .describe( + 'PBKDF2 iteration count for key derivation. Defaults to 200,000. ' + + 'Higher values increase brute-force resistance at the cost of speed.', + ), +}; + +/** + * Full schema including runtime refinement (either content or base64 required). + * Use for type inference (`UploadInput`) and manual validation in handlers. + */ export const uploadSchema = z - .object({ - content: z - .string() - .optional() - .describe('UTF-8 text content to upload'), - base64: z - .string() - .optional() - .describe('Base64-encoded binary content to upload'), - }) + .object(uploadShape) .refine((data) => data.content !== undefined || data.base64 !== undefined, { message: 'Either content or base64 is required', }); export const retrieveSchema = { cid: z.string().describe('IPFS CID to retrieve'), + password: z + .string() + .optional() + .describe( + 'If the stored content was uploaded with encryption, provide the same ' + + 'password to decrypt it. Omit to receive the raw (encrypted) bytes.', + ), }; export const pinSchema = { diff --git a/packages/mcp/src/tools/storage.ts b/packages/mcp/src/tools/storage.ts index 71dc8d9..8d51e19 100644 --- a/packages/mcp/src/tools/storage.ts +++ b/packages/mcp/src/tools/storage.ts @@ -9,7 +9,7 @@ import { import { pinSchema, retrieveSchema, - uploadSchema, + uploadShape, type PinInput, type RetrieveInput, type UploadInput, @@ -21,15 +21,34 @@ export async function handleUpload( input: UploadInput, ): Promise> { const bytes = decodeUploadInput(input as RawUploadInput); - const cid = await ctx.meshkit.upload(bytes); - return textResult({ cid }); + + const uploadOptions = input.password + ? { + encrypt: { + password: input.password, + ...(input.pbkdf2Iterations !== undefined + ? { iterations: input.pbkdf2Iterations } + : {}), + }, + } + : undefined; + + const cid = await ctx.meshkit.upload(bytes, uploadOptions); + return textResult({ + cid, + encrypted: uploadOptions !== undefined, + }); } export async function handleRetrieve( ctx: MeshkitContext, input: RetrieveInput, ): Promise> { - const bytes = await ctx.meshkit.retrieve(input.cid); + const retrieveOptions = input.password + ? { password: input.password } + : undefined; + + const bytes = await ctx.meshkit.retrieve(input.cid, retrieveOptions); const encoded = encodeRetrievedBytesSafe(bytes); return textResult({ cid: input.cid, ...encoded }); } @@ -55,8 +74,8 @@ export function registerStorageTools( ): void { server.tool( 'ipfs_upload', - 'Upload content to IPFS and return the CID', - uploadSchema, + 'Upload content to IPFS and return the CID. Optionally encrypt with AES-256-GCM.', + uploadShape, async (input) => runTool(ctx, handleUpload, input), ); diff --git a/packages/meshkit/src/index.ts b/packages/meshkit/src/index.ts index 46cf55f..dad3621 100644 --- a/packages/meshkit/src/index.ts +++ b/packages/meshkit/src/index.ts @@ -3,6 +3,8 @@ export type { MeshkitConfig, MeshkitInitOptions, StoredObject, + UploadOptions, + RetrieveOptions, IpnsDuration, IpnsKey, IpnsKeyGenOptions, @@ -21,8 +23,12 @@ export { extractCidFromPath, toIpfsPath, toIpnsPath, + encrypt, + decrypt, + isEncryptedPayload, + DEFAULT_ITERATIONS, } from '@ipfs-meshkit/core'; -export type { S3StorageConfig, FilOneConfig } from '@ipfs-meshkit/core'; +export type { EncryptOptions, S3StorageConfig, FilOneConfig } from '@ipfs-meshkit/core'; export type { IPFSNodeHandle, diff --git a/packages/node/package.json b/packages/node/package.json index 40327a1..298614a 100644 --- a/packages/node/package.json +++ b/packages/node/package.json @@ -4,6 +4,14 @@ "private": true, "description": "Local Kubo lifecycle for IPFS Meshkit (bundled into root @ipfs-meshkit/meshkit).", "type": "module", + "types": "./dist/index.d.ts", + "exports": { + ".": { + "types": "./dist/index.d.ts", + "import": "./dist/index.js", + "require": "./dist/index.cjs" + } + }, "scripts": { "clean": "rm -rf dist" }, From 6aa4fe10f7e8df64a65ac0f9b8161c097b2f9cf0 Mon Sep 17 00:00:00 2001 From: Anurag Date: Tue, 28 Jul 2026 01:14:51 +0530 Subject: [PATCH 09/12] test(core,mcp): update unit tests for encrypted upload/retrieve MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit create-client.test.ts (+7 tests): - upload with encrypt: EMSH blob sent to Kubo (not plaintext) - two encrypted uploads → different blobs (random salt) - retrieve without password returns raw encrypted bytes - retrieve with correct password decrypts transparently - retrieve with wrong password throws MeshkitError - retrieve with password on plaintext content is a no-op create-filone-client.test.ts (+6 tests): - encrypted upload sends EMSH blob to S3 - two encrypted uploads → different CIDs (random salt) - retrieve with correct password decrypts - retrieve without password returns raw bytes - retrieve with wrong password throws MeshkitError - retrieve with password on plaintext is a no-op mcp/tools/storage.test.ts (fix + 5 new tests): - Fix existing assertions for (bytes, undefined) calling convention - handleUpload with password passes encrypt options - handleUpload with custom iterations passes both fields - handleRetrieve with password passes decrypt options - handleUpload result includes encrypted: true/false flag --- packages/core/test/create-client.test.ts | 113 ++++++++++++++++++ .../core/test/create-filone-client.test.ts | 81 +++++++++++++ packages/mcp/test/tools/storage.test.ts | 47 +++++++- 3 files changed, 240 insertions(+), 1 deletion(-) diff --git a/packages/core/test/create-client.test.ts b/packages/core/test/create-client.test.ts index 2564f90..bcd92e0 100644 --- a/packages/core/test/create-client.test.ts +++ b/packages/core/test/create-client.test.ts @@ -1,4 +1,6 @@ import { beforeEach, describe, expect, it, vi } from 'vitest'; +import { isEncryptedPayload } from '../src/crypto.js'; +import { MeshkitError } from '../src/types.js'; const ipfs = { add: vi.fn(), @@ -22,6 +24,10 @@ vi.mock('kubo-rpc-client', () => ({ import { create } from 'kubo-rpc-client'; import { createMeshkitClient } from '../src/create-client.js'; +/** A fixed plaintext for upload/retrieve tests. */ +const PLAINTEXT = new TextEncoder().encode('hello meshkit'); +const PASSWORD = 'test-password-32chars-minimum-ok'; + describe('createMeshkitClient', () => { beforeEach(() => { vi.clearAllMocks(); @@ -39,6 +45,10 @@ describe('createMeshkitClient', () => { }); }); + // --------------------------------------------------------------------------- + // upload — unencrypted (backward compat) + // --------------------------------------------------------------------------- + it('upload returns the CID string from Kubo', async () => { ipfs.add.mockResolvedValue({ cid: { toString: () => 'QmUpload' } }); @@ -47,6 +57,48 @@ describe('createMeshkitClient', () => { expect(ipfs.add).toHaveBeenCalledWith(new Uint8Array([1, 2]), { pin: false }); }); + // --------------------------------------------------------------------------- + // upload — encrypted + // --------------------------------------------------------------------------- + + it('upload with encrypt option passes encrypted bytes to Kubo (not plaintext)', async () => { + ipfs.add.mockResolvedValue({ cid: { toString: () => 'QmEncrypted' } }); + + const client = createMeshkitClient({ apiUrl: 'http://127.0.0.1:5001' }); + const cid = await client.upload(PLAINTEXT, { + encrypt: { password: PASSWORD, iterations: 1 }, + }); + + expect(cid).toBe('QmEncrypted'); + + // The bytes actually sent to Kubo must NOT be the original plaintext + const sentBytes: Uint8Array = ipfs.add.mock.calls[0][0] as Uint8Array; + expect(sentBytes).not.toEqual(PLAINTEXT); + + // They must look like an EMSH encrypted payload + expect(isEncryptedPayload(sentBytes)).toBe(true); + }); + + it('two encrypted uploads of the same plaintext produce different blobs', async () => { + ipfs.add.mockResolvedValue({ cid: { toString: () => 'QmAny' } }); + const client = createMeshkitClient({ apiUrl: 'http://127.0.0.1:5001' }); + const opts = { encrypt: { password: PASSWORD, iterations: 1 } }; + + await client.upload(PLAINTEXT, opts); + await client.upload(PLAINTEXT, opts); + + const blob1: Uint8Array = ipfs.add.mock.calls[0][0] as Uint8Array; + const blob2: Uint8Array = ipfs.add.mock.calls[1][0] as Uint8Array; + + expect(Buffer.from(blob1).toString('hex')).not.toBe( + Buffer.from(blob2).toString('hex'), + ); + }); + + // --------------------------------------------------------------------------- + // retrieve — unencrypted (backward compat) + // --------------------------------------------------------------------------- + it('retrieve concatenates streamed chunks', async () => { async function* chunks() { yield new Uint8Array([1, 2]); @@ -58,6 +110,67 @@ describe('createMeshkitClient', () => { await expect(client.retrieve('QmX')).resolves.toEqual(new Uint8Array([1, 2, 3])); }); + it('retrieve without password returns raw encrypted bytes when content is encrypted', async () => { + // Simulate: upload encrypted, then retrieve without password + const { encrypt } = await import('../src/crypto.js'); + const encrypted = await encrypt(PLAINTEXT, { password: PASSWORD, iterations: 1 }); + + async function* chunks() { yield encrypted; } + ipfs.cat.mockReturnValue(chunks()); + + const client = createMeshkitClient({ apiUrl: 'http://127.0.0.1:5001' }); + const raw = await client.retrieve('QmEnc'); + // Should return the raw encrypted blob, not the plaintext + expect(raw).toEqual(encrypted); + expect(isEncryptedPayload(raw)).toBe(true); + }); + + // --------------------------------------------------------------------------- + // retrieve — decrypted + // --------------------------------------------------------------------------- + + it('retrieve with correct password decrypts transparently', async () => { + const { encrypt } = await import('../src/crypto.js'); + const encrypted = await encrypt(PLAINTEXT, { password: PASSWORD, iterations: 1 }); + + async function* chunks() { yield encrypted; } + ipfs.cat.mockReturnValue(chunks()); + + const client = createMeshkitClient({ apiUrl: 'http://127.0.0.1:5001' }); + const result = await client.retrieve('QmEnc', { password: PASSWORD }); + + expect(result).toEqual(PLAINTEXT); + }); + + it('retrieve with wrong password throws MeshkitError', async () => { + const { encrypt } = await import('../src/crypto.js'); + const encrypted = await encrypt(PLAINTEXT, { password: PASSWORD, iterations: 1 }); + + async function* chunks() { yield encrypted; } + ipfs.cat.mockReturnValue(chunks()); + + const client = createMeshkitClient({ apiUrl: 'http://127.0.0.1:5001' }); + await expect( + client.retrieve('QmEnc', { password: 'wrong-password' }), + ).rejects.toBeInstanceOf(MeshkitError); + }); + + it('retrieve with password on plaintext content returns plaintext unchanged', async () => { + // If the content is NOT encrypted, providing a password should be a no-op + async function* chunks() { yield PLAINTEXT; } + ipfs.cat.mockReturnValue(chunks()); + + const client = createMeshkitClient({ apiUrl: 'http://127.0.0.1:5001' }); + const result = await client.retrieve('QmPlain', { password: PASSWORD }); + + // isEncryptedPayload returns false → raw bytes returned as-is + expect(result).toEqual(PLAINTEXT); + }); + + // --------------------------------------------------------------------------- + // IPNS + key methods (unchanged) + // --------------------------------------------------------------------------- + it('publishName prefixes values with /ipfs/', async () => { ipfs.name.publish.mockResolvedValue({ name: 'k51Test', diff --git a/packages/core/test/create-filone-client.test.ts b/packages/core/test/create-filone-client.test.ts index c14316d..9dce8be 100644 --- a/packages/core/test/create-filone-client.test.ts +++ b/packages/core/test/create-filone-client.test.ts @@ -1,6 +1,7 @@ import { describe, it, expect, vi, beforeEach } from 'vitest'; import { createFilOneClient, createS3Client } from '../src/create-filone-client.js'; import { createMeshkitClient } from '../src/create-client.js'; +import { isEncryptedPayload } from '../src/crypto.js'; import { MeshkitError } from '../src/types.js'; const CONFIG = { @@ -77,6 +78,36 @@ describe('createS3Client / createFilOneClient', () => { const cid2 = await client.upload(new TextEncoder().encode('invoice-002')); expect(cid1).not.toBe(cid2); }); + + // ------------------------------------------------------------------------- + // Encrypted upload + // ------------------------------------------------------------------------- + + it('encrypted upload sends EMSH blob (not plaintext) to S3', async () => { + const fetchMock = vi.fn().mockResolvedValue(makeResponse(200)); + vi.stubGlobal('fetch', fetchMock); + + const data = new TextEncoder().encode('secret invoice'); + const client = createFilOneClient(CONFIG); + await client.upload(data, { encrypt: { password: 'pass', iterations: 1 } }); + + // The body sent to S3 must be an encrypted EMSH payload + const req = fetchMock.mock.calls[0][0] as Request; + const body = new Uint8Array(await req.arrayBuffer()); + expect(isEncryptedPayload(body)).toBe(true); + expect(body).not.toEqual(data); + }); + + it('two encrypted uploads of the same plaintext produce different CIDs', async () => { + vi.stubGlobal('fetch', vi.fn().mockResolvedValue(makeResponse(200))); + const client = createFilOneClient(CONFIG); + const data = new TextEncoder().encode('same secret'); + const opts = { encrypt: { password: 'pass', iterations: 1 } }; + const cid1 = await client.upload(data, opts); + const cid2 = await client.upload(data, opts); + // Different random salts → different ciphertexts → different CIDs + expect(cid1).not.toBe(cid2); + }); }); // --------------------------------------------------------------------------- @@ -108,6 +139,56 @@ describe('createS3Client / createFilOneClient', () => { const client = createFilOneClient(CONFIG); await expect(client.retrieve('bafkreimissing')).rejects.toBeInstanceOf(MeshkitError); }); + + // ------------------------------------------------------------------------- + // Encrypted retrieve + // ------------------------------------------------------------------------- + + it('retrieve with correct password decrypts encrypted S3 content', async () => { + const { encrypt } = await import('../src/crypto.js'); + const plaintext = new TextEncoder().encode('secret s3 payload'); + const encrypted = await encrypt(plaintext, { password: 'pass', iterations: 1 }); + + vi.stubGlobal('fetch', vi.fn().mockResolvedValue(new Response(encrypted.buffer))); + const client = createFilOneClient(CONFIG); + const result = await client.retrieve('bafkreiencrypted', { password: 'pass' }); + expect(result).toEqual(plaintext); + }); + + it('retrieve without password returns raw encrypted bytes', async () => { + const { encrypt } = await import('../src/crypto.js'); + const plaintext = new TextEncoder().encode('secret s3 payload'); + const encrypted = await encrypt(plaintext, { password: 'pass', iterations: 1 }); + + vi.stubGlobal('fetch', vi.fn().mockResolvedValue(new Response(encrypted.buffer))); + const client = createFilOneClient(CONFIG); + const raw = await client.retrieve('bafkreiencrypted'); + expect(raw).toEqual(encrypted); + expect(isEncryptedPayload(raw)).toBe(true); + }); + + it('retrieve with wrong password throws MeshkitError', async () => { + const { encrypt } = await import('../src/crypto.js'); + const encrypted = await encrypt( + new TextEncoder().encode('secret'), + { password: 'correct', iterations: 1 }, + ); + + vi.stubGlobal('fetch', vi.fn().mockResolvedValue(new Response(encrypted.buffer))); + const client = createFilOneClient(CONFIG); + await expect( + client.retrieve('bafkreiencrypted', { password: 'wrong' }), + ).rejects.toBeInstanceOf(MeshkitError); + }); + + it('retrieve with password on unencrypted content returns content unchanged', async () => { + const original = new TextEncoder().encode('plain unencrypted content'); + vi.stubGlobal('fetch', vi.fn().mockResolvedValue(new Response(original.buffer))); + const client = createFilOneClient(CONFIG); + const result = await client.retrieve('bafkreiplain', { password: 'pass' }); + // isEncryptedPayload returns false → content returned as-is + expect(result).toEqual(original); + }); }); // --------------------------------------------------------------------------- diff --git a/packages/mcp/test/tools/storage.test.ts b/packages/mcp/test/tools/storage.test.ts index df2c8ba..efe66fd 100644 --- a/packages/mcp/test/tools/storage.test.ts +++ b/packages/mcp/test/tools/storage.test.ts @@ -32,8 +32,10 @@ describe('storage tool handlers', () => { const ctx = createMockContext(); const result = await handleUpload(ctx, { content: 'hello' }); + // No password → called with (bytes, undefined) expect(ctx.meshkit.upload).toHaveBeenCalledWith( new TextEncoder().encode('hello'), + undefined, ); expect(result.content[0]?.text).toContain('QmUpload'); }); @@ -42,11 +44,53 @@ describe('storage tool handlers', () => { const ctx = createMockContext(); const result = await handleRetrieve(ctx, { cid: 'QmTest' }); - expect(ctx.meshkit.retrieve).toHaveBeenCalledWith('QmTest'); + // No password → called with (cid, undefined) + expect(ctx.meshkit.retrieve).toHaveBeenCalledWith('QmTest', undefined); expect(result.content[0]?.text).toContain('"encoding": "text"'); expect(result.content[0]?.text).toContain('hello'); }); + it('handleUpload with password passes encrypt options', async () => { + const ctx = createMockContext(); + await handleUpload(ctx, { content: 'secret', password: 'mypass' }); + + expect(ctx.meshkit.upload).toHaveBeenCalledWith( + new TextEncoder().encode('secret'), + { encrypt: { password: 'mypass' } }, + ); + }); + + it('handleUpload with password and custom iterations passes both', async () => { + const ctx = createMockContext(); + await handleUpload(ctx, { content: 'secret', password: 'mypass', pbkdf2Iterations: 50_000 }); + + expect(ctx.meshkit.upload).toHaveBeenCalledWith( + new TextEncoder().encode('secret'), + { encrypt: { password: 'mypass', iterations: 50_000 } }, + ); + }); + + it('handleRetrieve with password passes decrypt options', async () => { + const ctx = createMockContext(); + await handleRetrieve(ctx, { cid: 'QmEnc', password: 'mypass' }); + + expect(ctx.meshkit.retrieve).toHaveBeenCalledWith('QmEnc', { password: 'mypass' }); + }); + + it('handleUpload result includes encrypted: true when password is given', async () => { + const ctx = createMockContext(); + const result = await handleUpload(ctx, { content: 'secret', password: 'mypass' }); + + expect(result.content[0]?.text).toContain('"encrypted": true'); + }); + + it('handleUpload result includes encrypted: false when no password is given', async () => { + const ctx = createMockContext(); + const result = await handleUpload(ctx, { content: 'plain' }); + + expect(result.content[0]?.text).toContain('"encrypted": false'); + }); + it('handlePin pins a CID', async () => { const ctx = createMockContext(); const result = await handlePin(ctx, { cid: 'QmPin' }); @@ -82,6 +126,7 @@ describe('storage tool handlers', () => { expect(ctx.meshkit.upload).toHaveBeenCalledWith( new TextEncoder().encode('hello'), + undefined, ); }); From 365339e1927376ddb1ac1e33e89fadf9ce17005c Mon Sep 17 00:00:00 2001 From: Anurag Date: Tue, 28 Jul 2026 01:15:58 +0530 Subject: [PATCH 10/12] test(integration): 7-phase encrypted upload/retrieve end-to-end test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Phase 1: start node, upload text/JSON/binary/empty with encryption Phase 2: retrieve without password → EMSH blob returned (not plaintext) Phase 3: retrieve with correct password → original content restored Phase 4: wrong password → MeshkitError thrown Phase 5: same plaintext × 2 → different CIDs (random salt per call) Phase 6: unencrypted upload/retrieve regression guard Phase 7: graceful shutdown Uses iterations: 1_000 to keep test runtime fast while still exercising the full PBKDF2 code path. Skipped when SKIP_INTEGRATION=1 or Kubo is absent. --- tests/integration/encryption.test.ts | 204 +++++++++++++++++++++++++++ 1 file changed, 204 insertions(+) create mode 100644 tests/integration/encryption.test.ts diff --git a/tests/integration/encryption.test.ts b/tests/integration/encryption.test.ts new file mode 100644 index 0000000..9954fd2 --- /dev/null +++ b/tests/integration/encryption.test.ts @@ -0,0 +1,204 @@ +/** + * Integration tests for encrypted upload/retrieve. + * + * These tests require a running Kubo daemon (or are skipped via SKIP_INTEGRATION=1). + * They exercise the full stack: encrypt → upload to IPFS → retrieve → decrypt. + */ +import { dirname, join } from 'node:path'; +import { fileURLToPath } from 'node:url'; +import { afterAll, beforeAll, describe, expect, it } from 'vitest'; +import { + init, + isEncryptedPayload, + MeshkitError, + resolveRepoPath, + type IPFSNodeHandle, +} from '@ipfs-meshkit/meshkit'; +import { hasKubo, removeDir, stopManagedNode } from './helpers.js'; + +const testDir = dirname(fileURLToPath(import.meta.url)); +const TEST_REPO = join(testDir, '.ipfs-encryption-test'); +const TEST_PORT = 15_005; +const TEST_GATEWAY_PORT = 18_005; +const TEST_HOST = '127.0.0.1'; + +const localNodeOptions = { + repo: TEST_REPO, + host: TEST_HOST, + port: TEST_PORT, + gatewayPort: TEST_GATEWAY_PORT, +} as const; + +const PASSWORD = 'correct-horse-battery-staple-integration'; + +describe.skipIf(!hasKubo())('encrypted storage integration', () => { + let localNode: IPFSNodeHandle | undefined; + + beforeAll(async () => { + await removeDir(resolveRepoPath(TEST_REPO)); + }); + + afterAll(async () => { + await stopManagedNode(localNode); + await removeDir(resolveRepoPath(TEST_REPO)); + }); + + describe.sequential('encrypted upload / retrieve lifecycle', () => { + // ------------------------------------------------------------------------- + // Phase 1 — encrypt and upload several content types + // ------------------------------------------------------------------------- + + let textCid = ''; + let jsonCid = ''; + let binaryCid = ''; + let emptyCid = ''; + + const originalText = 'MSC1234567 — confidential manifest data'; + const originalJson = JSON.stringify({ vessel: 'Ever Given', port: 'Suez', msc: 'MSC9876543' }); + // Simulate PDF magic bytes + some binary payload + const originalBinary = new Uint8Array([ + 0x25, 0x50, 0x44, 0x46, 0x2d, 0x31, 0x2e, 0x34, // %PDF-1.4 + ...Array.from({ length: 64 }, (_, i) => i), + ]); + const originalEmpty = new Uint8Array(0); + + it('phase 1: start node and upload encrypted content of multiple types', async () => { + const { meshkit, localNode: node } = await init({ + localNode: localNodeOptions, + }); + localNode = node; + + const enc = { encrypt: { password: PASSWORD, iterations: 1_000 } }; + + textCid = await meshkit.upload(new TextEncoder().encode(originalText), enc); + jsonCid = await meshkit.upload(new TextEncoder().encode(originalJson), enc); + binaryCid = await meshkit.upload(originalBinary, enc); + emptyCid = await meshkit.upload(originalEmpty, enc); + + expect(textCid).toBeTruthy(); + expect(jsonCid).toBeTruthy(); + expect(binaryCid).toBeTruthy(); + expect(emptyCid).toBeTruthy(); + + // All four CIDs must be distinct (different random salts per upload) + const cids = [textCid, jsonCid, binaryCid, emptyCid]; + expect(new Set(cids).size).toBe(4); + }); + + // ------------------------------------------------------------------------- + // Phase 2 — retrieve raw bytes (no password) must be encrypted + // ------------------------------------------------------------------------- + + it('phase 2: retrieve without password returns encrypted blob (EMSH magic)', async () => { + const { meshkit } = await init({ + localNode: false, + nodes: [`http://${TEST_HOST}:${TEST_PORT}`], + }); + + const rawText = await meshkit.retrieve(textCid); + const rawJson = await meshkit.retrieve(jsonCid); + const rawBinary = await meshkit.retrieve(binaryCid); + const rawEmpty = await meshkit.retrieve(emptyCid); + + expect(isEncryptedPayload(rawText)).toBe(true); + expect(isEncryptedPayload(rawJson)).toBe(true); + expect(isEncryptedPayload(rawBinary)).toBe(true); + expect(isEncryptedPayload(rawEmpty)).toBe(true); + + // Must NOT match the original plaintext + expect(rawText).not.toEqual(new TextEncoder().encode(originalText)); + expect(rawJson).not.toEqual(new TextEncoder().encode(originalJson)); + }); + + // ------------------------------------------------------------------------- + // Phase 3 — retrieve with correct password decrypts transparently + // ------------------------------------------------------------------------- + + it('phase 3: retrieve with correct password decrypts to original content', async () => { + const { meshkit } = await init({ + localNode: false, + nodes: [`http://${TEST_HOST}:${TEST_PORT}`], + }); + + const opts = { password: PASSWORD }; + + const text = await meshkit.retrieve(textCid, opts); + const json = await meshkit.retrieve(jsonCid, opts); + const binary = await meshkit.retrieve(binaryCid, opts); + const empty = await meshkit.retrieve(emptyCid, opts); + + expect(new TextDecoder().decode(text)).toBe(originalText); + expect(new TextDecoder().decode(json)).toBe(originalJson); + expect(binary).toEqual(originalBinary); + expect(empty).toEqual(originalEmpty); + }); + + // ------------------------------------------------------------------------- + // Phase 4 — wrong password throws + // ------------------------------------------------------------------------- + + it('phase 4: retrieve with wrong password throws MeshkitError', async () => { + const { meshkit } = await init({ + localNode: false, + nodes: [`http://${TEST_HOST}:${TEST_PORT}`], + }); + + await expect( + meshkit.retrieve(textCid, { password: 'wrong-password-definitely' }), + ).rejects.toBeInstanceOf(MeshkitError); + }); + + // ------------------------------------------------------------------------- + // Phase 5 — same plaintext + same password → different CIDs each upload + // ------------------------------------------------------------------------- + + it('phase 5: uploading the same plaintext twice yields different CIDs', async () => { + const { meshkit } = await init({ + localNode: false, + nodes: [`http://${TEST_HOST}:${TEST_PORT}`], + }); + + const data = new TextEncoder().encode('repeated confidential data'); + const enc = { encrypt: { password: PASSWORD, iterations: 1_000 } }; + + const cid1 = await meshkit.upload(data, enc); + const cid2 = await meshkit.upload(data, enc); + + // Random salt makes the CID different every time + expect(cid1).not.toBe(cid2); + + // But both decrypt correctly + const r1 = await meshkit.retrieve(cid1, { password: PASSWORD }); + const r2 = await meshkit.retrieve(cid2, { password: PASSWORD }); + expect(r1).toEqual(data); + expect(r2).toEqual(data); + }); + + // ------------------------------------------------------------------------- + // Phase 6 — unencrypted content is unaffected (regression guard) + // ------------------------------------------------------------------------- + + it('phase 6: upload without encryption and retrieve without password works', async () => { + const { meshkit } = await init({ + localNode: false, + nodes: [`http://${TEST_HOST}:${TEST_PORT}`], + }); + + const plaintext = new TextEncoder().encode('completely public data'); + const cid = await meshkit.upload(plaintext); + + const retrieved = await meshkit.retrieve(cid); + expect(retrieved).toEqual(plaintext); + expect(isEncryptedPayload(retrieved)).toBe(false); + }); + + // ------------------------------------------------------------------------- + // Phase 7 — graceful shutdown + // ------------------------------------------------------------------------- + + it('phase 7: stop the local node', async () => { + await stopManagedNode(localNode); + localNode = undefined; + }); + }); +}); From 1c420f112b8e837db9ca2dc29dab5316bd45804f Mon Sep 17 00:00:00 2001 From: Anurag Date: Tue, 28 Jul 2026 15:49:25 +0530 Subject: [PATCH 11/12] feat(crypto): encrypted upload/retrieve + security hardening v1.2.1 - AES-256-GCM / PBKDF2-SHA256 encryption on both Kubo and S3 backends - EMSH wire format: magic + version + iterations + salt + nonce + ciphertext+tag - encrypt() / decrypt() / isEncryptedPayload() exported from package root - DEFAULT_ITERATIONS = 200_000 (OWASP 2023 minimum) - iterations floor guard on encrypt() (< 1_000 throws) - iterations ceiling guard on decrypt() (> 10_000_000 throws, DoS prevention) - MCP tools: ipfs_upload and ipfs_retrieve accept password + pbkdf2Iterations - Fix integration test: pass localNode as object so custom port is forwarded - Resolve 7 CVEs (brace-expansion, fast-uri, postcss, shell-quote, hono, esbuild) - Upgrade vitest/coverage-v8 3.x -> 4.x, zod 3.x -> 4.x, @types/node 25 -> 26 - Fix @ipfs-meshkit/meshkit pin in mcp from stale registry 1.0.2 to ^1.2.0 - Update README, SECURITY.md, CHANGELOG with full encryption process docs --- CHANGELOG.md | 74 + README.md | 104 +- SECURITY.md | 17 +- package-lock.json | 3098 ++++++++--------- package.json | 13 +- packages/capacitor/package.json | 2 +- packages/core/src/crypto.ts | 29 +- packages/core/test/create-client.test.ts | 10 +- .../core/test/create-filone-client.test.ts | 10 +- packages/core/test/crypto.test.ts | 22 +- packages/mcp/README.md | 31 +- packages/mcp/package.json | 10 +- packages/meshkit/package.json | 2 +- packages/react-native/package.json | 5 +- 14 files changed, 1665 insertions(+), 1762 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d996af0..531c779 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,79 @@ # Changelog +## 1.2.1 — 2026-07-28 + +### Added + +- **Client-side encrypted storage** — both the Kubo and S3 backends now support transparent AES-256-GCM encryption. Data is encrypted locally before it leaves the device; the IPFS network and storage provider only ever see the ciphertext. + + **How it works — upload path:** + 1. Caller passes `{ encrypt: { password, iterations? } }` to `upload()`. + 2. A fresh 128-bit random salt and a fresh 96-bit random nonce are generated using `globalThis.crypto.getRandomValues` (CSPRNG — works on Node.js ≥ 20, browsers, and React Native). + 3. A 256-bit AES key is derived from the password + salt using **PBKDF2-SHA256** with the configured iteration count (default: 200,000 — the OWASP 2023 minimum). Because salt and iteration count are random/configurable per call, the same password + plaintext always produces a different key. + 4. The plaintext is encrypted with **AES-256-GCM**. GCM appends a 128-bit authentication tag that covers both the ciphertext and the associated header fields, so any bit-level tampering is detected at decryption time. + 5. The output is assembled as an **EMSH** (Encrypted MeSHkit) blob with a self-describing 37-byte header: + + ``` + ┌────────┬─────────┬────────────┬──────────┬──────────┬──────────────────────┐ + │ MAGIC │ VERSION │ ITERATIONS │ SALT │ NONCE │ CIPHERTEXT + TAG │ + │ 4 B │ 1 B │ 4 B uint32 │ 16 B │ 12 B │ n + 16 B │ + │ "EMSH" │ 0x01 │ big-endian │ random │ random │ AES-256-GCM output │ + └────────┴─────────┴────────────┴──────────┴──────────┴──────────────────────┘ + ``` + + 6. The EMSH blob is uploaded to IPFS (or the S3 bucket). Because salt and nonce are randomised per call, uploading the same plaintext twice yields two different CIDs — content is not linkable across uploads. + + **How it works — retrieve path:** + 1. Caller passes `{ password }` to `retrieve()`. + 2. The raw bytes are fetched from IPFS / S3 by CID. + 3. `isEncryptedPayload()` checks the 4-byte EMSH magic prefix and the version byte. If they match, decryption proceeds; otherwise the raw bytes are returned as-is (plain-content round-trips are unaffected). + 4. The iteration count, salt, and nonce are read directly from the EMSH header — no out-of-band metadata is needed. + 5. The AES-256 key is re-derived from the password + header salt + header iteration count via PBKDF2-SHA256. + 6. AES-256-GCM decryption verifies the GCM authentication tag. Any wrong password or any ciphertext / header corruption causes decryption to throw a generic `MeshkitError` (`"Decryption failed: wrong password or corrupted data"`) — wrong-password and tampered-payload errors are intentionally indistinguishable to prevent oracle attacks. + 7. The original plaintext bytes are returned to the caller. + +- **New exports:** + - `encrypt(data, { password, iterations? }): Promise` — standalone encrypt; returns an EMSH payload + - `decrypt(data, password): Promise` — standalone decrypt; throws `MeshkitError` on wrong password or tampering + - `isEncryptedPayload(data): boolean` — structural check (magic + version bytes only; never touches the password) + - `DEFAULT_ITERATIONS` — `200_000`; re-exported constant for callers who want to reference the default explicitly + - `EncryptOptions` — TypeScript type: `{ password: string; iterations?: number }` + +- **Encryption on `upload()` / `retrieve()`** — both `MeshkitClient` implementations (`createMeshkitClient` for Kubo and `createS3Client` / `createFilOneClient` for S3) now accept: + - `upload(data, { encrypt: { password, iterations? } })` — encrypts before upload + - `retrieve(cid, { password })` — decrypts after fetch; omitting `password` returns the raw EMSH bytes + +- **MCP server encryption tools** (`@ipfs-meshkit/mcp`) — `ipfs_upload` now accepts optional `password` and `pbkdf2Iterations` parameters; `ipfs_retrieve` now accepts optional `password`. AI agents can store and retrieve encrypted content without the plaintext ever reaching the IPFS network. + +- **Crypto dependencies** — `@noble/ciphers@2.2.0` (AES-256-GCM) and `@noble/hashes@2.2.0` (PBKDF2-SHA256 + SHA-256) added as runtime dependencies. Both are from the `@noble` suite and covered by the published [Cure53 security audit](https://cure53.de/pentest-report_noble-crypto.pdf). + +### Security + +- Resolved **7 CVEs** across transitive dependencies (0 remaining): + - `brace-expansion` ≤5.0.7 — DoS via unbounded expansion (HIGH, CVSS 7.5) — fixed by upgrading `vitest` + `@vitest/coverage-v8` to v4 + - `brace-expansion` ≥2.0.0 <2.1.2 — DoS via exponential expansion (HIGH, CVSS 5.3) — bumped to 2.1.2 + - `fast-uri` 3.0.0–3.1.3 — host confusion via backslash (HIGH, CVSS 7.5) — bumped to 3.1.4 + - `postcss` ≤8.5.17 — path traversal in source map loading (HIGH, CVSS 7.5) — bumped to 8.5.23 + - `shell-quote` ≤1.8.4 — quadratic-complexity DoS (HIGH, CVSS 7.5) — bumped to 1.10.0 + - `@hono/node-server` <2.0.5 — path traversal via encoded backslash (MODERATE, CVSS 5.9) — bumped to 2.0.12 + - `esbuild` 0.27.3–0.28.0 — arbitrary file read via dev server (LOW, CVSS 2.5) — resolved via `overrides` +- Added **iteration count guards** to `crypto.ts`: + - `encrypt()` now rejects `iterations < 1_000` — catches accidental typos that would produce dangerously weak key derivation + - `decrypt()` now rejects payloads whose header declares `iterations > 10_000_000` — prevents a crafted EMSH blob from hanging a server with a multi-hour PBKDF2 run + +### Changed + +- `@vitest/coverage-v8` and `vitest` upgraded from v3 to v4.1.10 +- `zod` upgraded from 3.x to 4.4.3 in `@ipfs-meshkit/mcp` (`@modelcontextprotocol/sdk` supports `^3.25 || ^4.0`) +- `@types/node` upgraded from `^25` to `^26.1.2` across all packages +- `multiformats` floor bumped to `^14.0.5` +- `@modelcontextprotocol/sdk` floor bumped to `^1.30.0` +- `@ipfs-meshkit/meshkit` dependency in `@ipfs-meshkit/mcp` changed from pinned registry version `1.0.2` to workspace `^1.2.0` + +### Fixed + +- Integration test phase 1 passed `localNode: true, ...localNodeOptions` to `init()` which caused the daemon to start on the default port 5001 instead of the test port 15005; changed to `localNode: localNodeOptions` (object form) so the port is correctly forwarded to `startIPFSNode()` + ## 1.2.0 — 2026-07-22 ### Added diff --git a/README.md b/README.md index 786cab3..fb0ddc0 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# IPFS Meshkit0 +# IPFS Meshkit **@ipfs-meshkit/meshkit** is a TypeScript SDK for decentralized storage with two backends and one unified interface: @@ -7,6 +7,8 @@ Both backends implement the same `MeshkitClient` interface (`upload`, `retrieve`, `pin`, `list`, `listPins`, …) so you can swap backends without changing application logic. +**Built-in client-side encryption** — any upload can be transparently encrypted with AES-256-GCM before it leaves the device. The IPFS network and the storage provider only ever see the ciphertext. + [![test](https://github.com/IPFS-Meshkit/meshkit0/actions/workflows/test.yml/badge.svg)](https://github.com/IPFS-Meshkit/meshkit0/actions/workflows/test.yml) [![npm version](https://img.shields.io/npm/v/@ipfs-meshkit/meshkit.svg)](https://www.npmjs.com/package/@ipfs-meshkit/meshkit) [![license](https://img.shields.io/npm/l/@ipfs-meshkit/meshkit.svg)](https://github.com/IPFS-Meshkit/meshkit0/blob/main/LICENSE) @@ -98,6 +100,86 @@ const objects = await client.list(); --- +## Quick start — Encrypted storage + +Encryption works on **both backends** with a single option. The content is encrypted client-side before upload and decrypted client-side after retrieval — the IPFS network and storage provider never see the plaintext. + +```typescript +import { init, isEncryptedPayload } from '@ipfs-meshkit/meshkit'; + +const { meshkit } = await init({ localNode: true }); +const PASSWORD = 'correct-horse-battery-staple'; + +// Encrypt on upload +const data = new TextEncoder().encode('confidential manifest data'); +const cid = await meshkit.upload(data, { + encrypt: { password: PASSWORD }, +}); + +// Retrieve + decrypt in one call +const plaintext = await meshkit.retrieve(cid, { password: PASSWORD }); +console.log(new TextDecoder().decode(plaintext)); // confidential manifest data + +// Retrieve without password — you get the raw encrypted blob +const raw = await meshkit.retrieve(cid); +console.log(isEncryptedPayload(raw)); // true + +// Wrong password throws MeshkitError +await meshkit.retrieve(cid, { password: 'wrong' }); // throws +``` + +The S3 path works identically: + +```typescript +import { createFilOneClient } from '@ipfs-meshkit/meshkit'; + +const client = createFilOneClient({ /* credentials */ }); + +const cid = await client.upload(data, { + encrypt: { password: PASSWORD }, +}); +const plaintext = await client.retrieve(cid, { password: PASSWORD }); +``` + +### Encryption details + +| Property | Value | +|---|---| +| Cipher | AES-256-GCM (authenticated encryption — detects tampering) | +| Key derivation | PBKDF2-SHA256 | +| Default iterations | 200,000 (OWASP 2023 minimum) | +| Salt | 128-bit random, generated fresh per `upload()` call | +| Nonce | 96-bit random, generated fresh per `upload()` call | +| Wire format | `EMSH` magic + version + iteration count + salt + nonce + ciphertext + 128-bit GCM tag | + +Because salt and nonce are random per call, uploading the same plaintext twice produces two different CIDs — safe for content that should not be linkable across uploads. + +**Iteration count guards:** +- `iterations` must be ≥ 1,000 on `encrypt()` — values below this are almost certainly a typo and produce dangerously weak key derivation. +- `decrypt()` rejects any payload whose header declares more than 10,000,000 iterations — this blocks crafted payloads designed to hang a server by triggering a multi-hour KDF run. + +```typescript +// Custom iteration count (higher = more brute-force resistant, slower) +const cid = await meshkit.upload(data, { + encrypt: { password: PASSWORD, iterations: 600_000 }, +}); +``` + +### Standalone encrypt / decrypt + +If you need to encrypt bytes outside of an upload/retrieve flow: + +```typescript +import { encrypt, decrypt, isEncryptedPayload, DEFAULT_ITERATIONS } from '@ipfs-meshkit/meshkit'; + +const payload = await encrypt(data, { password: PASSWORD }); +console.log(isEncryptedPayload(payload)); // true + +const recovered = await decrypt(payload, PASSWORD); +``` + +--- + ## Usage ### Local Kubo daemon (recommended for Node.js servers) @@ -345,6 +427,26 @@ const { init } = require('@ipfs-meshkit/meshkit'); | `toIpfsPath(cid)` / `toIpnsPath(name)` | Normalize to `/ipfs/` or `/ipns/` path | | `MeshkitError` / `MeshkitNodeError` | Error classes | +### Encryption + +| Export | Purpose | +|---|---| +| `encrypt(data, { password, iterations? })` | Encrypt bytes with AES-256-GCM / PBKDF2-SHA256; returns EMSH payload | +| `decrypt(data, password)` | Decrypt an EMSH payload; throws `MeshkitError` on wrong password or tampering | +| `isEncryptedPayload(data)` | Returns `true` if `data` starts with the EMSH magic bytes | +| `DEFAULT_ITERATIONS` | `200_000` — OWASP 2023 minimum for PBKDF2-SHA256 | +| `EncryptOptions` | Type: `{ password: string; iterations?: number }` (iterations ≥ 1,000) | + +Both `upload()` and `retrieve()` accept inline encryption options so you do not need to call `encrypt` / `decrypt` directly in most cases: + +```typescript +// Upload option +meshkit.upload(bytes, { encrypt: { password, iterations? } }) + +// Retrieve option +meshkit.retrieve(cid, { password }) +``` + TypeScript types are included — no `@types/` package needed: ```typescript diff --git a/SECURITY.md b/SECURITY.md index b39b0a0..9c3c1d4 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -4,7 +4,22 @@ | Version | Supported | | ------- | --------- | -| 1.0.x | Yes | +| 1.2.x | Yes | +| 1.1.x | No | +| 1.0.x | No | + +## Encryption security properties + +All encrypted payloads use the **EMSH** (Encrypted MeSHkit) wire format: + +| Property | Detail | +|---|---| +| Cipher | AES-256-GCM — authenticated encryption, detects any bit-level tampering | +| Key derivation | PBKDF2-SHA256, default 200,000 iterations (OWASP 2023 minimum) | +| Salt | 128-bit random, unique per `encrypt()` call | +| Nonce | 96-bit random, unique per `encrypt()` call | +| Iteration bounds | Minimum 1,000 on encrypt; maximum 10,000,000 on decrypt (DoS guard) | +| Dependencies | `@noble/ciphers` and `@noble/hashes` — [Cure53-audited](https://cure53.de/pentest-report_noble-crypto.pdf) | ## Reporting a vulnerability diff --git a/package-lock.json b/package-lock.json index d8e589e..9c9971a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@ipfs-meshkit/meshkit", - "version": "1.2.0", + "version": "1.2.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@ipfs-meshkit/meshkit", - "version": "1.2.0", + "version": "1.2.1", "license": "MIT", "workspaces": [ "packages/*", @@ -17,14 +17,14 @@ "@noble/hashes": "^2.2.0", "aws4fetch": "^1.0.20", "kubo-rpc-client": "^7.1.0", - "multiformats": "^14.0.0" + "multiformats": "^14.0.5" }, "devDependencies": { - "@types/node": "^25.9.3", - "@vitest/coverage-v8": "^3.2.4", + "@types/node": "^26.1.2", + "@vitest/coverage-v8": "^4.1.10", "tsup": "^8.5.0", "typescript": "^6.0.3", - "vitest": "^3.2.4" + "vitest": "^4.1.10" }, "engines": { "node": ">=20" @@ -37,26 +37,12 @@ "@ipfs-meshkit/meshkit": "*" } }, - "node_modules/@ampproject/remapping": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", - "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/@babel/code-frame": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz", "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/helper-validator-identifier": "^7.29.7", "js-tokens": "^4.0.0", @@ -70,8 +56,8 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.7.tgz", "integrity": "sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==", + "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=6.9.0" } @@ -80,8 +66,8 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.7.tgz", "integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/code-frame": "^7.29.7", "@babel/generator": "^7.29.7", @@ -111,8 +97,8 @@ "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, "license": "ISC", - "peer": true, "bin": { "semver": "bin/semver.js" } @@ -121,8 +107,8 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.7.tgz", "integrity": "sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/parser": "^7.29.7", "@babel/types": "^7.29.7", @@ -138,8 +124,8 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.29.7.tgz", "integrity": "sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/compat-data": "^7.29.7", "@babel/helper-validator-option": "^7.29.7", @@ -155,8 +141,8 @@ "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, "license": "ISC", - "peer": true, "bin": { "semver": "bin/semver.js" } @@ -165,8 +151,8 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.29.7.tgz", "integrity": "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==", + "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=6.9.0" } @@ -175,8 +161,8 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.29.7.tgz", "integrity": "sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/traverse": "^7.29.7", "@babel/types": "^7.29.7" @@ -189,8 +175,8 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.29.7.tgz", "integrity": "sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/helper-module-imports": "^7.29.7", "@babel/helper-validator-identifier": "^7.29.7", @@ -207,6 +193,7 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==", + "dev": true, "license": "MIT", "engines": { "node": ">=6.9.0" @@ -216,6 +203,7 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", + "dev": true, "license": "MIT", "engines": { "node": ">=6.9.0" @@ -225,8 +213,8 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.29.7.tgz", "integrity": "sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==", + "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=6.9.0" } @@ -235,8 +223,8 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.7.tgz", "integrity": "sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/template": "^7.29.7", "@babel/types": "^7.29.7" @@ -249,6 +237,7 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.7.tgz", "integrity": "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==", + "dev": true, "license": "MIT", "dependencies": { "@babel/types": "^7.29.7" @@ -264,8 +253,8 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.7.tgz", "integrity": "sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==", + "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=6.9.0" } @@ -274,8 +263,8 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.29.7.tgz", "integrity": "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/code-frame": "^7.29.7", "@babel/parser": "^7.29.7", @@ -289,8 +278,8 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.7.tgz", "integrity": "sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/code-frame": "^7.29.7", "@babel/generator": "^7.29.7", @@ -308,6 +297,7 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.7.tgz", "integrity": "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==", + "dev": true, "license": "MIT", "dependencies": { "@babel/helper-string-parser": "^7.29.7", @@ -327,16 +317,6 @@ "node": ">=18" } }, - "node_modules/@capacitor/core": { - "version": "8.4.0", - "resolved": "https://registry.npmjs.org/@capacitor/core/-/core-8.4.0.tgz", - "integrity": "sha512-LrS1xPIrqLtJABBIPDGXxxKmI9OyesrzWw8DiHbxhSC9JoiLUleUAJlX1a0LWIVLRbuY4Szgf9huFeRqYH2SAQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "tslib": "^2.1.0" - } - }, "node_modules/@chainsafe/is-ip": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/@chainsafe/is-ip/-/is-ip-2.1.0.tgz", @@ -356,6 +336,40 @@ "node": ">=6" } }, + "node_modules/@emnapi/core": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.1.tgz", + "integrity": "sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.2.2", + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.1.tgz", + "integrity": "sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/wasi-threads": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.2.tgz", + "integrity": "sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, "node_modules/@esbuild/aix-ppc64": { "version": "0.28.1", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.1.tgz", @@ -799,12 +813,12 @@ } }, "node_modules/@hono/node-server": { - "version": "1.19.14", - "resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-1.19.14.tgz", - "integrity": "sha512-GwtvgtXxnWsucXvbQXkRgqksiH2Qed37H9xHZocE5sA3N8O8O8/8FA3uclQXxXVzc9XBZuEOMK7+r02FmSpHtw==", + "version": "2.0.12", + "resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-2.0.12.tgz", + "integrity": "sha512-eWpQYr67tqJLeaSUl0Q+TquuYfUdTibpOJlUMV2FfUP7+KqCC5TufnwnlXL6mobZBJbGAYRd7ZvEBDCbLInjhg==", "license": "MIT", "engines": { - "node": ">=18.14.1" + "node": ">=20" }, "peerDependencies": { "hono": "^4" @@ -871,135 +885,22 @@ "npm": ">=7.0.0" } }, - "node_modules/@isaacs/cliui": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", - "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", - "dev": true, - "license": "ISC", - "dependencies": { - "string-width": "^5.1.2", - "string-width-cjs": "npm:string-width@^4.2.0", - "strip-ansi": "^7.0.1", - "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", - "wrap-ansi": "^8.1.0", - "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@isaacs/cliui/node_modules/ansi-regex": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", - "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/@isaacs/cliui/node_modules/ansi-styles": { - "version": "6.2.3", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", - "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@isaacs/cliui/node_modules/emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "dev": true, - "license": "MIT" - }, - "node_modules/@isaacs/cliui/node_modules/string-width": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", - "dev": true, - "license": "MIT", - "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@isaacs/cliui/node_modules/strip-ansi": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", - "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^6.2.2" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", - "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^6.1.0", - "string-width": "^5.0.1", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, "node_modules/@isaacs/ttlcache": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/@isaacs/ttlcache/-/ttlcache-1.4.1.tgz", "integrity": "sha512-RQgQ4uQ+pLbqXfOmieB91ejmLwvSgv9nLx6sT6sD83s7umBypgg+OIBOBbEUiJXrfpnp9j0mRhYYdzp9uqq3lA==", + "dev": true, "license": "ISC", - "peer": true, "engines": { "node": ">=12" } }, - "node_modules/@istanbuljs/schema": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.6.tgz", - "integrity": "sha512-+Sg6GCR/wy1oSmQDFq4LQDAhm3ETKnorxN+y5nbLULOR3P0c14f2Wurzj3/xqPXtasLFfHd5iRFQ7AJt4KH2cw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/@jest/schemas": { "version": "29.6.3", "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@sinclair/typebox": "^0.27.8" }, @@ -1011,8 +912,8 @@ "version": "29.6.3", "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/schemas": "^29.6.3", "@types/istanbul-lib-coverage": "^2.0.0", @@ -1029,6 +930,7 @@ "version": "0.3.13", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, "license": "MIT", "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", @@ -1039,8 +941,8 @@ "version": "2.3.5", "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" @@ -1050,6 +952,7 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, "license": "MIT", "engines": { "node": ">=6.0.0" @@ -1059,8 +962,8 @@ "version": "0.3.11", "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.11.tgz", "integrity": "sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25" @@ -1070,12 +973,14 @@ "version": "1.5.5", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, "license": "MIT" }, "node_modules/@jridgewell/trace-mapping": { "version": "0.3.31", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, "license": "MIT", "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", @@ -1143,12 +1048,12 @@ } }, "node_modules/@modelcontextprotocol/sdk": { - "version": "1.29.0", - "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.29.0.tgz", - "integrity": "sha512-zo37mZA9hJWpULgkRpowewez1y6ML5GsXJPY8FI0tBBCd77HEvza4jDqRKOXgHNn867PVGCyTdzqpz0izu5ZjQ==", + "version": "1.30.0", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.30.0.tgz", + "integrity": "sha512-xKd8OIzlqNzcqcNumGAa6g+PW2kjD5vrpcKOnfldAUPP3j7lnqMPwlTXQm8gF+UwH72z0lqaRbjr9hqGz0eITA==", "license": "MIT", "dependencies": { - "@hono/node-server": "^1.19.9", + "@hono/node-server": "^1.19.9 || ^2.0.5", "ajv": "^8.17.1", "ajv-formats": "^3.0.1", "content-type": "^1.0.5", @@ -1232,6 +1137,25 @@ "@multiformats/multiaddr": "^13.0.0" } }, + "node_modules/@napi-rs/wasm-runtime": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.6.tgz", + "integrity": "sha512-ZLv/JdUfkvOy9eCnnBaGfiO+XimbjebAeO+MRQqD/B+FR1tnRN0tpKSJHRbE8sFfS6aqsXZ67TQjfwfsxULVbg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@tybys/wasm-util": "^0.10.3" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + }, + "peerDependencies": { + "@emnapi/core": "^1.7.1", + "@emnapi/runtime": "^1.7.1" + } + }, "node_modules/@noble/ciphers": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/@noble/ciphers/-/ciphers-2.2.0.tgz", @@ -1306,180 +1230,280 @@ "node": ">= 8" } }, - "node_modules/@pkgjs/parseargs": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", - "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "node_modules/@oxc-project/types": { + "version": "0.139.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.139.0.tgz", + "integrity": "sha512-r9gHphtCs+1M7J0pw6Sn/hh/Wpa/iQrOOkrNAlVLF/gHq+/CJmHIWKKUUhdWjcD6CIa8idarspCsASiXCXvFUw==", "dev": true, "license": "MIT", - "optional": true, - "engines": { - "node": ">=14" + "funding": { + "url": "https://github.com/sponsors/Boshen" } }, - "node_modules/@react-native/assets-registry": { - "version": "0.86.0", - "resolved": "https://registry.npmjs.org/@react-native/assets-registry/-/assets-registry-0.86.0.tgz", - "integrity": "sha512-nIaXbm2jX1OTYp0qbviJ3O6KZivoE8z3BnhUQ2LsqfZSWRoOK/n1qsiAr6oALiNKWnXY3j2KPwtYORnZzp8xew==", + "node_modules/@rolldown/binding-android-arm64": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.1.5.tgz", + "integrity": "sha512-lZg8fqIv2v7FF237bwMgzGZEJvGL79/s5knJ/i6FmsGF4XXlzccZ4jb+TrFIxtSSxFtIpdsgrPZeMk1I9AFcyQ==", + "cpu": [ + "arm64" + ], + "dev": true, "license": "MIT", - "peer": true, + "optional": true, + "os": [ + "android" + ], "engines": { - "node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@react-native/codegen": { - "version": "0.86.0", - "resolved": "https://registry.npmjs.org/@react-native/codegen/-/codegen-0.86.0.tgz", - "integrity": "sha512-uTs9DBo3+/lUqinsGZK0FKJRBVClrwMXoZToaDxE1Q2SL2e55vs2GwyZfIKzPl5uJnbu4PfFMIp0/mLXLWUMuA==", + "node_modules/@rolldown/binding-darwin-arm64": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.1.5.tgz", + "integrity": "sha512-51Bnx9pNiMRKSUNtBfySkNJ9vMU9Hh3I1ozDd6gyPPYzaXCfnptUcEZxXGYFn+ul2dtcMUiqGR1Yai2K10uoTw==", + "cpu": [ + "arm64" + ], + "dev": true, "license": "MIT", - "peer": true, - "dependencies": { - "@babel/core": "^7.25.2", - "@babel/parser": "^7.29.0", - "hermes-parser": "0.36.0", - "invariant": "^2.2.4", - "nullthrows": "^1.1.1", - "tinyglobby": "^0.2.15", - "yargs": "^17.6.2" - }, + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0" - }, - "peerDependencies": { - "@babel/core": "*" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@react-native/community-cli-plugin": { - "version": "0.86.0", - "resolved": "https://registry.npmjs.org/@react-native/community-cli-plugin/-/community-cli-plugin-0.86.0.tgz", - "integrity": "sha512-Jv8p1ebEPfTzs8gmrjsdT2XMXFfeAg45Pman+XPLFGaSeGAZkutRFRyX9Cs9aGTSOyIA9YPJ6vDNb1ayTf1FKQ==", + "node_modules/@rolldown/binding-darwin-x64": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.1.5.tgz", + "integrity": "sha512-Tm+gbfC0aHu1tBA/JvKQh32S0K6YgCHkiAF4/W6xX0K0RmNuc94VeK419dJoE65R5aRxmo+noZQSWrAMF6yb6g==", + "cpu": [ + "x64" + ], + "dev": true, "license": "MIT", - "peer": true, - "dependencies": { - "@react-native/dev-middleware": "0.86.0", - "debug": "^4.4.0", - "invariant": "^2.2.4", - "metro": "^0.84.3", - "metro-config": "^0.84.3", - "metro-core": "^0.84.3", - "semver": "^7.1.3" - }, + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0" - }, - "peerDependencies": { - "@react-native-community/cli": "*", - "@react-native/metro-config": "0.86.0" - }, - "peerDependenciesMeta": { - "@react-native-community/cli": { - "optional": true - }, - "@react-native/metro-config": { - "optional": true - } + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@react-native/debugger-frontend": { - "version": "0.86.0", - "resolved": "https://registry.npmjs.org/@react-native/debugger-frontend/-/debugger-frontend-0.86.0.tgz", - "integrity": "sha512-7Mb3nDfyJeys+ELF75Ageu7VKERlnIMoO+aNPoXqTXvz+b41L6l2CqMyLpDHxkBSlenij6gEepPNgaIyWHbJZw==", - "license": "BSD-3-Clause", - "peer": true, + "node_modules/@rolldown/binding-freebsd-x64": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.1.5.tgz", + "integrity": "sha512-JMzDKCCXq93YccG5gz3hvOs1oXRKAf0XYpfOS88e+wZrC8Iugj6j68867vrYZkvpDDpKn/KoKORThmchMpF6TA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], "engines": { - "node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@react-native/debugger-shell": { - "version": "0.86.0", - "resolved": "https://registry.npmjs.org/@react-native/debugger-shell/-/debugger-shell-0.86.0.tgz", - "integrity": "sha512-Y0zEkZzLz8ou6o/VLml1A31X/rMgc6DRjwxwzPMa94qRTMY070WeBCNTITQo4kKTBAUgbxh07oXPQqp0Tpja8w==", - "license": "MIT", - "peer": true, - "dependencies": { - "cross-spawn": "^7.0.6", - "debug": "^4.4.0", - "fb-dotslash": "0.5.8" - }, + "node_modules/@rolldown/binding-linux-arm-gnueabihf": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.1.5.tgz", + "integrity": "sha512-uML21j2K5TfPGutKxub+M+nLjZIrWjXQ5Grx4lCe/nimTj9B4L63zHpjXLl4y0L3mcm2htEQIb06oCG/szerNw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@react-native/dev-middleware": { - "version": "0.86.0", - "resolved": "https://registry.npmjs.org/@react-native/dev-middleware/-/dev-middleware-0.86.0.tgz", - "integrity": "sha512-20pTO6yTybmvXvro520H6C7jydIQnLKOl5qFtVEcHSdFrY63r3OGei+Rx9bILgSRmH6jgnfEcijcMx7pwWuQtw==", + "node_modules/@rolldown/binding-linux-arm64-gnu": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.1.5.tgz", + "integrity": "sha512-navSiuTMogvnQoZoM/v+l3ZWo50/NTwSHSzheABx/RCnmUPaKwq9qSo4Br2OYRs21+Fz8uFqITZM3H4opOB0/Q==", + "cpu": [ + "arm64" + ], + "dev": true, "license": "MIT", - "peer": true, - "dependencies": { - "@isaacs/ttlcache": "^1.4.1", - "@react-native/debugger-frontend": "0.86.0", - "@react-native/debugger-shell": "0.86.0", - "chrome-launcher": "^0.15.2", - "chromium-edge-launcher": "^0.3.0", - "connect": "^3.6.5", - "debug": "^4.4.0", - "invariant": "^2.2.4", - "nullthrows": "^1.1.1", - "open": "^7.0.3", - "serve-static": "^1.16.2", - "ws": "^7.5.10" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@react-native/gradle-plugin": { - "version": "0.86.0", - "resolved": "https://registry.npmjs.org/@react-native/gradle-plugin/-/gradle-plugin-0.86.0.tgz", - "integrity": "sha512-a1RcfaEDqWExCGfCwadIxt4l8FvKYgFqeMf2uzeKyAOnb+vTGNIeCvifFL2MqvgaeYxlER437HbMIajGcuJ1pQ==", + "node_modules/@rolldown/binding-linux-arm64-musl": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.1.5.tgz", + "integrity": "sha512-lAryqH7IteztmCXQXk0etKj4wBQ7Gx5S6LjKhsgp9zb8I5bsuvU/2llH1hDQcjsFeqIsovMVN339/8pUDDBXxA==", + "cpu": [ + "arm64" + ], + "dev": true, "license": "MIT", - "peer": true, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@react-native/js-polyfills": { - "version": "0.86.0", - "resolved": "https://registry.npmjs.org/@react-native/js-polyfills/-/js-polyfills-0.86.0.tgz", - "integrity": "sha512-zYy/Cjd1VTnZ2iCNaG9bDF9C3l2ntESiPRscjIlI5FKugu6aeTwsDSv1aI8Bc4Kp3vEdoVg+UQhLAhE4svREaQ==", + "node_modules/@rolldown/binding-linux-ppc64-gnu": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.1.5.tgz", + "integrity": "sha512-fsK/sNBnxzBlL4O1JNrZakVQxPspqpED5dLtNsZS9oOKmtSpdNIzxH2kkol5HYTWJN47sE20ztMJPxfZ89qGOg==", + "cpu": [ + "ppc64" + ], + "dev": true, "license": "MIT", - "peer": true, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0" + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-s390x-gnu": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.1.5.tgz", + "integrity": "sha512-gLYb4BIadlfTOYT5gO503n8zQjXflgzpD0FcyKh0Mzx3rqCZKnHoJWV9xe1KXUJ5lx2JfcSHr/mhzS0PC/McAA==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@react-native/normalize-colors": { - "version": "0.86.0", - "resolved": "https://registry.npmjs.org/@react-native/normalize-colors/-/normalize-colors-0.86.0.tgz", - "integrity": "sha512-kG0wfCGghUKlfxkJyyHCDVutWVYWK7/DG58ojA/4v9EfulgF+osuSQmlbNb3rcKX58qutm7JcldSeVLgGFha9g==", + "node_modules/@rolldown/binding-linux-x64-gnu": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.1.5.tgz", + "integrity": "sha512-FjcpEKUyJygHgs1o50VYNvkt5+7Le/VEdYt0AkRpkL33MnyQfwr8l5mXwMmfmTbyMPr5vJLC+8/Gd9gXnwU1QQ==", + "cpu": [ + "x64" + ], + "dev": true, "license": "MIT", - "peer": true + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@react-native/virtualized-lists": { - "version": "0.86.0", - "resolved": "https://registry.npmjs.org/@react-native/virtualized-lists/-/virtualized-lists-0.86.0.tgz", - "integrity": "sha512-4/ZLXdf/OSpPDVO0AsQ1SJdRIzt5t9BNQ46QwGgxvX7/cirYR5k8KXctNGGgW8lQo2gZChEfY2zFCZg9nM/jiw==", + "node_modules/@rolldown/binding-linux-x64-musl": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.1.5.tgz", + "integrity": "sha512-Me+PfPI2TMeOQk0gYWfLQZtTktrmzbr8cDboqX83XKc7UrgAi55gF+2dUkWdxd19n55Essp2yeca+O9N5rBxHg==", + "cpu": [ + "x64" + ], + "dev": true, "license": "MIT", - "peer": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-openharmony-arm64": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.1.5.tgz", + "integrity": "sha512-yc5WrLzXks6zCQfn9Oxr8pORKyl/pF+QjHmW/Qx3qu0oyrrNC+y2JLTU1E2rcWYAmzlnqngWXHQjy51VzW70Vw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-wasm32-wasi": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.1.5.tgz", + "integrity": "sha512-VbQGPX2b4r48TAMIM2cjgluIM1HYutm4pcTEJsle7iEP7sB1dFqtPLBVbdLAZCxy1txCcPxf4QFf4v8uvltPqA==", + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, "dependencies": { - "invariant": "^2.2.4", - "nullthrows": "^1.1.1" + "@emnapi/core": "1.11.1", + "@emnapi/runtime": "1.11.1", + "@napi-rs/wasm-runtime": "^1.1.6" }, "engines": { - "node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0" - }, - "peerDependencies": { - "@types/react": "^19.2.0", - "react": "*", - "react-native": "0.86.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-win32-arm64-msvc": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.1.5.tgz", + "integrity": "sha512-gHv82k63z4qpV5+Q1y/12KrK0ltWBukVDI8nZcbT7Tt/ZlOIVwppazneq0F93oDxTo3IgAMEDIoQh3E2n6mVsw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-win32-x64-msvc": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.1.5.tgz", + "integrity": "sha512-tTZuDBPw85tEN5PQi1pnEBzDy0Z49HtScLAbD5t6hyeU92A95pRWaSMw1GZZi/RwgSgUIl0xrSlXIT/9QzvYSA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" } }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz", + "integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==", + "dev": true, + "license": "MIT" + }, "node_modules/@rollup/rollup-android-arm-eabi": { "version": "4.62.2", "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.62.2.tgz", @@ -1834,8 +1858,26 @@ "version": "0.27.10", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.10.tgz", "integrity": "sha512-MTBk/3jGLNB2tVxv6uLlFh1iu64iYOQ2PbdOSK3NW8JZsmlaOh2q6sdtKowBhfw8QFLmYNzTW4/oK4uATIi6ZA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@standard-schema/spec": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", + "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tybys/wasm-util": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.3.tgz", + "integrity": "sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==", + "dev": true, "license": "MIT", - "peer": true + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } }, "node_modules/@types/chai": { "version": "5.2.3", @@ -1866,15 +1908,15 @@ "version": "2.0.6", "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", - "license": "MIT", - "peer": true + "dev": true, + "license": "MIT" }, "node_modules/@types/istanbul-lib-report": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@types/istanbul-lib-coverage": "*" } @@ -1883,27 +1925,28 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@types/istanbul-lib-report": "*" } }, "node_modules/@types/node": { - "version": "25.9.3", - "resolved": "https://registry.npmjs.org/@types/node/-/node-25.9.3.tgz", - "integrity": "sha512-603BddQMv3pUcr4U2dhujk83N2tTDVr/34wII2B6bJy6g+8WD6yUb11jszNs0gdi4PesVWl7ABt8nYMVpnLUcg==", + "version": "26.1.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-26.1.2.tgz", + "integrity": "sha512-Vu4a5UFA9rIIFJ7rB/Vaafh9lrCQszopTCx6KjFboXTGQbPNasehVR5TEiithSDGyd1DEiUByggTZsg8jukeIg==", + "dev": true, "license": "MIT", "dependencies": { - "undici-types": ">=7.24.0 <7.24.7" + "undici-types": "~8.3.0" } }, "node_modules/@types/yargs": { "version": "17.0.35", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.35.tgz", "integrity": "sha512-qUHkeCyQFxMXg79wQfTtfndEC+N9ZZg76HJftDJp+qH2tV7Gj4OJi7l+PiWwJ+pWtW8GwSmqsDj/oymhrTWXjg==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@types/yargs-parser": "*" } @@ -1912,36 +1955,33 @@ "version": "21.0.3", "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", - "license": "MIT", - "peer": true + "dev": true, + "license": "MIT" }, "node_modules/@vitest/coverage-v8": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-3.2.6.tgz", - "integrity": "sha512-LsAdmUapA0qSN306d8+zOyawM0hFm2m2Hg9IwVNIKBm+qJV8cijiq2c+gxKZcB1HCfIWAy+0qEZDCUQA58A1cw==", + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-4.1.10.tgz", + "integrity": "sha512-IM49HmthevbgAO4anp1hwtoT9wYe59w0LR00gr+eagHE+ZJ5lK4sLPeO0ubgoJcwLk6dehU3R24N+FbEEKDc8g==", "dev": true, "license": "MIT", "dependencies": { - "@ampproject/remapping": "^2.3.0", "@bcoe/v8-coverage": "^1.0.2", - "ast-v8-to-istanbul": "^0.3.3", - "debug": "^4.4.1", + "@vitest/utils": "4.1.10", + "ast-v8-to-istanbul": "^1.0.0", "istanbul-lib-coverage": "^3.2.2", "istanbul-lib-report": "^3.0.1", - "istanbul-lib-source-maps": "^5.0.6", - "istanbul-reports": "^3.1.7", - "magic-string": "^0.30.17", - "magicast": "^0.3.5", - "std-env": "^3.9.0", - "test-exclude": "^7.0.1", - "tinyrainbow": "^2.0.0" + "istanbul-reports": "^3.2.0", + "magicast": "^0.5.2", + "obug": "^2.1.1", + "std-env": "^4.0.0-rc.1", + "tinyrainbow": "^3.1.0" }, "funding": { "url": "https://opencollective.com/vitest" }, "peerDependencies": { - "@vitest/browser": "3.2.6", - "vitest": "3.2.6" + "@vitest/browser": "4.1.10", + "vitest": "4.1.10" }, "peerDependenciesMeta": { "@vitest/browser": { @@ -1950,39 +1990,40 @@ } }, "node_modules/@vitest/expect": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.2.6.tgz", - "integrity": "sha512-1+7q9BtaKzEmO+fmNT3kYvoNn5Y71XWAx2Q5HRim4tTVRQVRv4uJFAQ5FbK0OPUeNP/WmVCpxYxoJdvuHVjzBQ==", + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.10.tgz", + "integrity": "sha512-YsCn+qAk1GWjQOWFEsEcL2gNQ0zmVmQu3T03qP6UyjhtmdtwtbuI+DASn/7iQB3HGTXkdBwGddzxPlmiql5vlA==", "dev": true, "license": "MIT", "dependencies": { + "@standard-schema/spec": "^1.1.0", "@types/chai": "^5.2.2", - "@vitest/spy": "3.2.6", - "@vitest/utils": "3.2.6", - "chai": "^5.2.0", - "tinyrainbow": "^2.0.0" + "@vitest/spy": "4.1.10", + "@vitest/utils": "4.1.10", + "chai": "^6.2.2", + "tinyrainbow": "^3.1.0" }, "funding": { "url": "https://opencollective.com/vitest" } }, "node_modules/@vitest/mocker": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.2.6.tgz", - "integrity": "sha512-EZOrpDbkKotFAP7wPAQV1UIyoGOk4oX7ynWhBhLB7v+meMHbQhU16oPpIYGTTe4oFlhpryGpgpcZP/sin3hYuw==", + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.10.tgz", + "integrity": "sha512-v0xaezt+DKEmKfaxg133ldzADrwLGd7Ze1MfQQTYfvs8OqZIwbxyxaYURivwV7sWy5fqn3rH5uOrSp07bp44Ow==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/spy": "3.2.6", + "@vitest/spy": "4.1.10", "estree-walker": "^3.0.3", - "magic-string": "^0.30.17" + "magic-string": "^0.30.21" }, "funding": { "url": "https://opencollective.com/vitest" }, "peerDependencies": { "msw": "^2.4.9", - "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" }, "peerDependenciesMeta": { "msw": { @@ -1994,42 +2035,42 @@ } }, "node_modules/@vitest/pretty-format": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.2.6.tgz", - "integrity": "sha512-lb7XXXzmm2h2ASzFnRvQpDo6onT1NmMJA3tkGTWiBFtRJ9lxGY3d3mm/Apt36gej2bkkOVLL/yTOtufDaFa/jA==", + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.10.tgz", + "integrity": "sha512-W1HsjSH4MXQ9YfmmhLAoIYf1HRfekQCGngeIgcei6MP5QQGWUe0gkopdZQaVCFO+JDJMrAJGwa5pRpNpvy4P8Q==", "dev": true, "license": "MIT", "dependencies": { - "tinyrainbow": "^2.0.0" + "tinyrainbow": "^3.1.0" }, "funding": { "url": "https://opencollective.com/vitest" } }, "node_modules/@vitest/runner": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.2.6.tgz", - "integrity": "sha512-HYcoSj1w5tcgUnzoF0HcyaAQjpA1gj9ftUJ7iSJSuipc02jW9gKkigwZbjFldAfYHA1fa8UZVRftdMY5msWM9Q==", + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.10.tgz", + "integrity": "sha512-IKI6kpIH+LmpROplyLwBBaCfMgOZOMsygVa6BARD6ahA04VRuJSa6OaVG7kRvSEMD870Vd91rSSw0eegtWyLGg==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/utils": "3.2.6", - "pathe": "^2.0.3", - "strip-literal": "^3.0.0" + "@vitest/utils": "4.1.10", + "pathe": "^2.0.3" }, "funding": { "url": "https://opencollective.com/vitest" } }, "node_modules/@vitest/snapshot": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.2.6.tgz", - "integrity": "sha512-H+ZjNTWGpObenh0YnlBctAPnJSI20P81PL8BPzWpx54YXLLTm8hEsWawtcYLMrwvpK48hGxLLbCS+1KRXhsKhw==", + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.10.tgz", + "integrity": "sha512-xRkfOT1qpTAi/Ti4Y1LtfRc3kEuqxGw59eN2jN9pRWMtS/XDevekhcFSqvQqjUNGksfjMJu3Y+oJ+4Ypn2OaJw==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "3.2.6", - "magic-string": "^0.30.17", + "@vitest/pretty-format": "4.1.10", + "@vitest/utils": "4.1.10", + "magic-string": "^0.30.21", "pathe": "^2.0.3" }, "funding": { @@ -2037,28 +2078,25 @@ } }, "node_modules/@vitest/spy": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.2.6.tgz", - "integrity": "sha512-oq6BbH68WzcWmwtBrU9nqLeaXTR4XwJF7FSLkKEZo4i6eoXcrxjcwSuTvWBIRUTC6VC72nXYunzqgZA+IKdtxg==", + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.10.tgz", + "integrity": "sha512-PLf/Ugvoq5wO/b4rwYCR1h2PSIdXz7wnkQFMiUpLdtM7l6pqVFcQIBEHyT1+l+cj7mNwAfZHzqXqDyjvOuwbDw==", "dev": true, "license": "MIT", - "dependencies": { - "tinyspy": "^4.0.3" - }, "funding": { "url": "https://opencollective.com/vitest" } }, "node_modules/@vitest/utils": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.2.6.tgz", - "integrity": "sha512-lI23nIs4bnT3T8NIoh+vFaz5s2/DdP0Jgt2jxwgWljvwn82cLJtyi/If+fjFyoLMGIOz0U/fKvWE0d4jsNQEfg==", + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.10.tgz", + "integrity": "sha512-fy9am/HWxbaGt/Sawrp90vt6Y6jQwf1RX77cz3uwoJwJVMli/e1IEwRPnMNJ7vKfPTwo0diXifkpPvwH9v7nGA==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "3.2.6", - "loupe": "^3.1.4", - "tinyrainbow": "^2.0.0" + "@vitest/pretty-format": "4.1.10", + "convert-source-map": "^2.0.0", + "tinyrainbow": "^3.1.0" }, "funding": { "url": "https://opencollective.com/vitest" @@ -2068,8 +2106,8 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "event-target-shim": "^5.0.0" }, @@ -2094,6 +2132,7 @@ "version": "8.17.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.17.0.tgz", "integrity": "sha512-xRQbDb9BnwDafYNn6Vwl839DYVjqXYb1XVGtWAZ1kcDc6iwAL4hg3B1dZlRiuENFeO2H53gFG3in621AdERVAg==", + "dev": true, "license": "MIT", "bin": { "acorn": "bin/acorn" @@ -2106,8 +2145,8 @@ "version": "7.1.4", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">= 14" } @@ -2149,13 +2188,14 @@ "version": "1.4.10", "resolved": "https://registry.npmjs.org/anser/-/anser-1.4.10.tgz", "integrity": "sha512-hCv9AqTQ8ycjpSd3upOJd7vFwW1JaoYQ7tpham03GJ1ca8/65rqn0RpaWpItOAd6ylW9wAw6luXYPJIyPFVOww==", - "license": "MIT", - "peer": true + "dev": true, + "license": "MIT" }, "node_modules/ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -2165,6 +2205,7 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, "license": "MIT", "dependencies": { "color-convert": "^2.0.1" @@ -2197,8 +2238,8 @@ "version": "2.0.6", "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", - "license": "MIT", - "peer": true + "dev": true, + "license": "MIT" }, "node_modules/assertion-error": { "version": "2.0.1", @@ -2211,9 +2252,9 @@ } }, "node_modules/ast-v8-to-istanbul": { - "version": "0.3.12", - "resolved": "https://registry.npmjs.org/ast-v8-to-istanbul/-/ast-v8-to-istanbul-0.3.12.tgz", - "integrity": "sha512-BRRC8VRZY2R4Z4lFIL35MwNXmwVqBityvOIwETtsCSwvjl0IdgFsy9NhdaA6j74nUdtJJlIypeRhpDam19Wq3g==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/ast-v8-to-istanbul/-/ast-v8-to-istanbul-1.0.5.tgz", + "integrity": "sha512-UPAgKJFSEGMWSDr3LX4tqnAb4f7KGT8O40Tyx8wbYmmZ/yn58lNCm8h3svs3eXgiGd5AXxz8NDOvXWvicq+rJA==", "dev": true, "license": "MIT", "dependencies": { @@ -2239,22 +2280,12 @@ "version": "0.36.0", "resolved": "https://registry.npmjs.org/babel-plugin-syntax-hermes-parser/-/babel-plugin-syntax-hermes-parser-0.36.0.tgz", "integrity": "sha512-LhD0xdoedDw7ansQgXbB2DADLZIK/LRXuWNBPuVzMc5S2WK5GyT89tCM+cQzxFGO0mGyLK6D5TrVOJJzAoDy8Q==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "hermes-parser": "0.36.0" } }, - "node_modules/balanced-match": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", - "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", - "dev": true, - "license": "MIT", - "engines": { - "node": "18 || 20 || >=22" - } - }, "node_modules/base64-js": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", @@ -2279,8 +2310,8 @@ "version": "2.10.37", "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.37.tgz", "integrity": "sha512-girxaJ7WZssDOFhzCGZTDKoTa1gk6A1TbflaYTpykLJ4UU9Fz9kx1aREM8JCuoVHbL8X8T/mJg7w2oYSq72Oig==", + "dev": true, "license": "Apache-2.0", - "peer": true, "bin": { "baseline-browser-mapping": "dist/cli.cjs" }, @@ -2362,19 +2393,6 @@ "node": ">= 0.8" } }, - "node_modules/brace-expansion": { - "version": "5.0.7", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.7.tgz", - "integrity": "sha512-7oFy703dxfY3/NLxC1fh2SUCQ0H9rmAY+5EpDVfXjUTTs+HEwR2nYaqLv+GWcTsumwxPfiz6CzCNkwXwBUwqCA==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^4.0.2" - }, - "engines": { - "node": "18 || 20 || >=22" - } - }, "node_modules/braces": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", @@ -2397,6 +2415,7 @@ "version": "4.28.2", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.2.tgz", "integrity": "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==", + "dev": true, "funding": [ { "type": "opencollective", @@ -2412,7 +2431,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "baseline-browser-mapping": "^2.10.12", "caniuse-lite": "^1.0.30001782", @@ -2431,8 +2449,8 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", + "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { "node-int64": "^0.4.0" } @@ -2465,8 +2483,8 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "license": "MIT", - "peer": true + "dev": true, + "license": "MIT" }, "node_modules/bundle-require": { "version": "5.1.0", @@ -2536,8 +2554,8 @@ "version": "6.3.0", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=10" }, @@ -2549,6 +2567,7 @@ "version": "1.0.30001799", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001799.tgz", "integrity": "sha512-hG1bReV+OUU+MOqK4t/ZWI0tZOyz3rqS9XuhOUz1cIcbwBKjOyJEJuw9ER5JuNyqxNk8u/JUVbGibBOL1yrjFw==", + "dev": true, "funding": [ { "type": "opencollective", @@ -2563,8 +2582,7 @@ "url": "https://github.com/sponsors/ai" } ], - "license": "CC-BY-4.0", - "peer": true + "license": "CC-BY-4.0" }, "node_modules/cborg": { "version": "5.1.1", @@ -2576,18 +2594,11 @@ } }, "node_modules/chai": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/chai/-/chai-5.3.3.tgz", - "integrity": "sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==", + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz", + "integrity": "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==", "dev": true, "license": "MIT", - "dependencies": { - "assertion-error": "^2.0.1", - "check-error": "^2.1.1", - "deep-eql": "^5.0.1", - "loupe": "^3.1.0", - "pathval": "^2.0.0" - }, "engines": { "node": ">=18" } @@ -2596,8 +2607,8 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -2613,8 +2624,8 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "has-flag": "^4.0.0" }, @@ -2622,16 +2633,6 @@ "node": ">=8" } }, - "node_modules/check-error": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.3.tgz", - "integrity": "sha512-PAJdDJusoxnwm1VwW07VWwUN1sl7smmC3OKggvndJFadxxDRyFJBX/ggnu/KE4kQAB7a3Dp8f/YXC1FlUprWmA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 16" - } - }, "node_modules/chokidar": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", @@ -2652,8 +2653,8 @@ "version": "0.15.2", "resolved": "https://registry.npmjs.org/chrome-launcher/-/chrome-launcher-0.15.2.tgz", "integrity": "sha512-zdLEwNo3aUVzIhKhTtXfxhdvZhUghrnmkvcAq2NoDd+LeOHKf03H5jwZ8T/STsAlzyALkBVK552iaG1fGf1xVQ==", + "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { "@types/node": "*", "escape-string-regexp": "^4.0.0", @@ -2671,8 +2672,8 @@ "version": "0.3.0", "resolved": "https://registry.npmjs.org/chromium-edge-launcher/-/chromium-edge-launcher-0.3.0.tgz", "integrity": "sha512-p03azHlGjtyRvFEee3cyvtsRYdniSkwjkzmM/KmVnqT5d7QkkwpJBhis/zCLMYdQMVJ5tt140TBNqqrZPaWeFA==", + "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { "@types/node": "*", "escape-string-regexp": "^4.0.0", @@ -2685,15 +2686,15 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", - "license": "MIT", - "peer": true + "dev": true, + "license": "MIT" }, "node_modules/cliui": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, "license": "ISC", - "peer": true, "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.1", @@ -2707,6 +2708,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, "license": "MIT", "dependencies": { "color-name": "~1.1.4" @@ -2719,14 +2721,15 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, "license": "MIT" }, "node_modules/commander": { "version": "12.1.0", "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", + "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=18" } @@ -2742,8 +2745,8 @@ "version": "3.7.0", "resolved": "https://registry.npmjs.org/connect/-/connect-3.7.0.tgz", "integrity": "sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "debug": "2.6.9", "finalhandler": "1.1.2", @@ -2758,8 +2761,8 @@ "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "ms": "2.0.0" } @@ -2768,8 +2771,8 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "license": "MIT", - "peer": true + "dev": true, + "license": "MIT" }, "node_modules/consola": { "version": "3.4.2", @@ -2807,8 +2810,8 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "license": "MIT", - "peer": true + "dev": true, + "license": "MIT" }, "node_modules/cookie": { "version": "0.7.2", @@ -2908,16 +2911,6 @@ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "license": "MIT" }, - "node_modules/deep-eql": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz", - "integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, "node_modules/depd": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", @@ -2931,13 +2924,23 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">= 0.8", "npm": "1.2.8000 || >= 1.4.16" } }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, "node_modules/dunder-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", @@ -2952,13 +2955,6 @@ "node": ">= 0.4" } }, - "node_modules/eastasianwidth": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", - "dev": true, - "license": "MIT" - }, "node_modules/ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -2981,21 +2977,22 @@ "version": "1.5.372", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.372.tgz", "integrity": "sha512-M3yhbAlilnwqC8D21t28UCDGHyitShTmmLRU/H+b74P6Ski16Nb9HONYEaVpMj/pwC7BEo5B95FpjODLCWbtfA==", - "license": "ISC", - "peer": true + "dev": true, + "license": "ISC" }, "node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, "license": "MIT" }, "node_modules/encodeurl": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">= 0.8" } @@ -3019,8 +3016,8 @@ "version": "2.1.4", "resolved": "https://registry.npmjs.org/error-stack-parser/-/error-stack-parser-2.1.4.tgz", "integrity": "sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "stackframe": "^1.3.4" } @@ -3044,9 +3041,9 @@ } }, "node_modules/es-module-lexer": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", - "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.3.1.tgz", + "integrity": "sha512-shc1dbU90Yl/xq1QrC7QRtfcwURZuVRfPhZbDoldJ1cn1gzDvBaBWlv0eFolj5+0znnPJz5TXLxsN77X/12KTA==", "dev": true, "license": "MIT" }, @@ -3108,8 +3105,8 @@ "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=6" } @@ -3124,8 +3121,8 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=10" }, @@ -3156,8 +3153,8 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=6" } @@ -3203,8 +3200,8 @@ "version": "3.1.3", "resolved": "https://registry.npmjs.org/exponential-backoff/-/exponential-backoff-3.1.3.tgz", "integrity": "sha512-ZgEeZXj30q+I0EN+CbSSpIyPaJ5HVQD18Z1m+u1FXbAeT94mr1zw50q4q6jiiC447Nl/YTcIYSAftiGqetwXCA==", - "license": "Apache-2.0", - "peer": true + "dev": true, + "license": "Apache-2.0" }, "node_modules/express": { "version": "5.2.1", @@ -3421,9 +3418,9 @@ "license": "Apache-2.0" }, "node_modules/fast-uri": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.3.tgz", - "integrity": "sha512-i70LwGWUduXqzicKXWshooq+sWL1K3WUU5rKZNG/0i3a1OSoX3HqhH5WbWwTmqWfor4urUakGPiRQcleRZTwOg==", + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.4.tgz", + "integrity": "sha512-8JnbkQ4juDyvYs4mgFGQqg4yCYtFDtUtmp2QIQq11ZZe5CFQ5wcqm1rqDgAh/QdMySuBnPzMUiJUNZG5N/AiQw==", "funding": [ { "type": "github", @@ -3449,8 +3446,8 @@ "version": "0.5.8", "resolved": "https://registry.npmjs.org/fb-dotslash/-/fb-dotslash-0.5.8.tgz", "integrity": "sha512-XHYLKk9J4BupDxi9bSEhkfss0m+Vr9ChTrjhf9l2iw3jB5C7BnY4GVPoMcqbrTutsKJso6yj2nAB6BI/F2oZaA==", + "dev": true, "license": "(MIT OR Apache-2.0)", - "peer": true, "bin": { "dotslash": "bin/dotslash" }, @@ -3462,8 +3459,8 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", + "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { "bser": "2.1.1" } @@ -3484,8 +3481,8 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "debug": "2.6.9", "encodeurl": "~1.0.2", @@ -3503,8 +3500,8 @@ "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "ms": "2.0.0" } @@ -3513,8 +3510,8 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "license": "MIT", - "peer": true + "dev": true, + "license": "MIT" }, "node_modules/fix-dts-default-cjs-exports": { "version": "1.0.1", @@ -3532,25 +3529,8 @@ "version": "0.0.6", "resolved": "https://registry.npmjs.org/flow-enums-runtime/-/flow-enums-runtime-0.0.6.tgz", "integrity": "sha512-3PYnM29RFXwvAN6Pc/scUfkI7RwhQ/xqyLUyPNlXUp9S40zI8nup9tUSrTLSVnWGBN38FNiGWbwZOB6uR4OGdw==", - "license": "MIT", - "peer": true - }, - "node_modules/foreground-child": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", - "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", "dev": true, - "license": "ISC", - "dependencies": { - "cross-spawn": "^7.0.6", - "signal-exit": "^4.0.1" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } + "license": "MIT" }, "node_modules/forwarded": { "version": "0.2.0", @@ -3565,8 +3545,8 @@ "version": "0.5.2", "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">= 0.6" } @@ -3599,8 +3579,8 @@ "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=6.9.0" } @@ -3609,8 +3589,8 @@ "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, "license": "ISC", - "peer": true, "engines": { "node": "6.* || 8.* || >= 10.*" } @@ -3658,28 +3638,6 @@ "node": ">= 0.4" } }, - "node_modules/glob": { - "version": "10.5.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", - "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", - "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", - "dev": true, - "license": "ISC", - "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" - }, - "bin": { - "glob": "dist/esm/bin.mjs" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/glob-parent": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", @@ -3692,39 +3650,6 @@ "node": ">= 6" } }, - "node_modules/glob/node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true, - "license": "MIT" - }, - "node_modules/glob/node_modules/brace-expansion": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.1.tgz", - "integrity": "sha512-WR1cURNjuvBLMZBMbqM0UoE+WAfdUcEV1ccD8PVBVOI+Z3ND4+SZbN8RsfT2bMuG1qwz5RFvPukSZm5fF2D5eA==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/glob/node_modules/minimatch": { - "version": "9.0.9", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", - "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.2" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/gopd": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", @@ -3741,13 +3666,14 @@ "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "license": "ISC", - "peer": true + "dev": true, + "license": "ISC" }, "node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -3783,26 +3709,19 @@ "node": ">= 0.4" } }, - "node_modules/hermes-compiler": { - "version": "250829098.0.14", - "resolved": "https://registry.npmjs.org/hermes-compiler/-/hermes-compiler-250829098.0.14.tgz", - "integrity": "sha512-5meXwsZxgiqFaJjNzwjzI9IyUkuGGBisu+z9BvQWmGVpjH6nz11hgqkyxe4dl8UAdyIV4lTbz91+Dlnjz0VxqA==", - "license": "MIT", - "peer": true - }, "node_modules/hermes-estree": { "version": "0.36.0", "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.36.0.tgz", "integrity": "sha512-A1+8zn5oss2CFP7pKsOaxorQG6FNIz1WU1VDqruLPPZl3LVgeE2C5xfFg8Ow6/Ow4mSslLLtYP1J3n38eKyW9w==", - "license": "MIT", - "peer": true + "dev": true, + "license": "MIT" }, "node_modules/hermes-parser": { "version": "0.36.0", "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.36.0.tgz", "integrity": "sha512-GdpwMmH5x6IpC1cijvcvYnlPB60Mh6kTSF/NFdYV/j56gYdi+0RIakYs+eqOV+bbO0SW7mgVVGSsTJxyPQfo3w==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "hermes-estree": "0.36.0" } @@ -3856,8 +3775,8 @@ "version": "7.0.6", "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "agent-base": "^7.1.2", "debug": "4" @@ -3902,8 +3821,8 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/image-size/-/image-size-1.2.1.tgz", "integrity": "sha512-rH+46sQJ2dlwfjfhCyNx5thzrv+dtmBIhPHk0zgRUukHzZ/kRueTJXoYYsclBaKcSMBWuGbOFXtioLpzTb5euw==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "queue": "6.0.2" }, @@ -3955,8 +3874,8 @@ "version": "2.2.4", "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "loose-envify": "^1.0.0" } @@ -4013,8 +3932,8 @@ "version": "2.2.1", "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "dev": true, "license": "MIT", - "peer": true, "bin": { "is-docker": "cli.js" }, @@ -4044,6 +3963,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -4089,8 +4009,8 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "is-docker": "^2.0.0" }, @@ -4151,21 +4071,6 @@ "node": ">=8" } }, - "node_modules/istanbul-lib-source-maps": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-5.0.6.tgz", - "integrity": "sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.23", - "debug": "^4.1.1", - "istanbul-lib-coverage": "^3.0.0" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/istanbul-reports": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.2.0.tgz", @@ -4242,28 +4147,12 @@ "readable-stream": "^3.6.0" } }, - "node_modules/jackspeak": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", - "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "@isaacs/cliui": "^8.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - }, - "optionalDependencies": { - "@pkgjs/parseargs": "^0.11.0" - } - }, "node_modules/jest-get-type": { "version": "29.6.3", "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", + "dev": true, "license": "MIT", - "peer": true, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } @@ -4272,8 +4161,8 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/types": "^29.6.3", "@types/node": "*", @@ -4290,6 +4179,7 @@ "version": "3.9.0", "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", + "dev": true, "funding": [ { "type": "github", @@ -4297,7 +4187,6 @@ } ], "license": "MIT", - "peer": true, "engines": { "node": ">=8" } @@ -4306,8 +4195,8 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/types": "^29.6.3", "camelcase": "^6.2.0", @@ -4324,8 +4213,8 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@types/node": "*", "jest-util": "^29.7.0", @@ -4340,8 +4229,8 @@ "version": "8.1.1", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "has-flag": "^4.0.0" }, @@ -4375,22 +4264,22 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "license": "MIT", - "peer": true + "dev": true, + "license": "MIT" }, "node_modules/jsc-safe-url": { "version": "0.2.4", "resolved": "https://registry.npmjs.org/jsc-safe-url/-/jsc-safe-url-0.2.4.tgz", "integrity": "sha512-0wM3YBWtYePOjfyXQH5MWQ8H7sdk5EXSwZvmSLKk2RboVQ2Bu239jycHDz5J/8Blf3K0Qnoy2b6xD+z10MFB+Q==", - "license": "0BSD", - "peer": true + "dev": true, + "license": "0BSD" }, "node_modules/jsesc": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, "license": "MIT", - "peer": true, "bin": { "jsesc": "bin/jsesc" }, @@ -4414,8 +4303,8 @@ "version": "2.2.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, "license": "MIT", - "peer": true, "bin": { "json5": "lib/cli.js" }, @@ -4466,8 +4355,8 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=6" } @@ -4476,8 +4365,8 @@ "version": "1.4.2", "resolved": "https://registry.npmjs.org/lighthouse-logger/-/lighthouse-logger-1.4.2.tgz", "integrity": "sha512-gPWxznF6TKmUHrOQjlVo2UbaL2EJ71mb2CCeRs/2qBpi4L/g4LUVc9+3lKQ6DTUZwJswfM7ainGrLO1+fOqa2g==", + "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { "debug": "^2.6.9", "marky": "^1.2.2" @@ -4487,8 +4376,8 @@ "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "ms": "2.0.0" } @@ -4497,72 +4386,326 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "license": "MIT", - "peer": true + "dev": true, + "license": "MIT" }, - "node_modules/lilconfig": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", - "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", + "node_modules/lightningcss": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.33.0.tgz", + "integrity": "sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==", "dev": true, - "license": "MIT", + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, "engines": { - "node": ">=14" + "node": ">= 12.0.0" }, "funding": { - "url": "https://github.com/sponsors/antonk52" - } - }, - "node_modules/lines-and-columns": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-android-arm64": "1.33.0", + "lightningcss-darwin-arm64": "1.33.0", + "lightningcss-darwin-x64": "1.33.0", + "lightningcss-freebsd-x64": "1.33.0", + "lightningcss-linux-arm-gnueabihf": "1.33.0", + "lightningcss-linux-arm64-gnu": "1.33.0", + "lightningcss-linux-arm64-musl": "1.33.0", + "lightningcss-linux-x64-gnu": "1.33.0", + "lightningcss-linux-x64-musl": "1.33.0", + "lightningcss-win32-arm64-msvc": "1.33.0", + "lightningcss-win32-x64-msvc": "1.33.0" + } + }, + "node_modules/lightningcss-android-arm64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.33.0.tgz", + "integrity": "sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==", + "cpu": [ + "arm64" + ], "dev": true, - "license": "MIT" + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } }, - "node_modules/load-tsconfig": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/load-tsconfig/-/load-tsconfig-0.2.5.tgz", - "integrity": "sha512-IXO6OCs9yg8tMKzfPZ1YmheJbZCiEsnBdcB03l0OcfK9prKnJb96siuHCr5Fl37/yo9DnKU+TLpxzTUspw9shg==", + "node_modules/lightningcss-darwin-arm64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.33.0.tgz", + "integrity": "sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg==", + "cpu": [ + "arm64" + ], "dev": true, - "license": "MIT", + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "node_modules/lodash.throttle": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz", - "integrity": "sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ==", - "license": "MIT", - "peer": true - }, - "node_modules/loose-envify": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", - "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "license": "MIT", - "peer": true, - "dependencies": { - "js-tokens": "^3.0.0 || ^4.0.0" + "node_modules/lightningcss-darwin-x64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.33.0.tgz", + "integrity": "sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" }, - "bin": { - "loose-envify": "cli.js" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "node_modules/loupe": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.2.1.tgz", - "integrity": "sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==", + "node_modules/lightningcss-freebsd-x64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.33.0.tgz", + "integrity": "sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg==", + "cpu": [ + "x64" + ], "dev": true, - "license": "MIT" - }, + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.33.0.tgz", + "integrity": "sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.33.0.tgz", + "integrity": "sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.33.0.tgz", + "integrity": "sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.33.0.tgz", + "integrity": "sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.33.0.tgz", + "integrity": "sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.33.0.tgz", + "integrity": "sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.33.0.tgz", + "integrity": "sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lilconfig": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", + "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true, + "license": "MIT" + }, + "node_modules/load-tsconfig": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/load-tsconfig/-/load-tsconfig-0.2.5.tgz", + "integrity": "sha512-IXO6OCs9yg8tMKzfPZ1YmheJbZCiEsnBdcB03l0OcfK9prKnJb96siuHCr5Fl37/yo9DnKU+TLpxzTUspw9shg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } + }, + "node_modules/lodash.throttle": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz", + "integrity": "sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, "node_modules/lru-cache": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, "license": "ISC", - "peer": true, "dependencies": { "yallist": "^3.0.2" } @@ -4578,15 +4721,15 @@ } }, "node_modules/magicast": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/magicast/-/magicast-0.3.5.tgz", - "integrity": "sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ==", + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/magicast/-/magicast-0.5.3.tgz", + "integrity": "sha512-pVKE4UdSQ7DvHzivsCIFx2BJn1mHG6KsyrFcaxFx6tONdneEuThrDx0Cj3AMg58KyN4pzYT+LHOotxDQDjNvkw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/parser": "^7.25.4", - "@babel/types": "^7.25.4", - "source-map-js": "^1.2.0" + "@babel/parser": "^7.29.3", + "@babel/types": "^7.29.0", + "source-map-js": "^1.2.1" } }, "node_modules/main-event": { @@ -4615,8 +4758,8 @@ "version": "1.0.12", "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", + "dev": true, "license": "BSD-3-Clause", - "peer": true, "dependencies": { "tmpl": "1.0.5" } @@ -4625,8 +4768,8 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/marky/-/marky-1.3.0.tgz", "integrity": "sha512-ocnPZQLNpvbedwTy9kNrQEsknEfgvcLMvOtz3sFeWApDq1MXH1TqkCIx58xlpESsfwQOnuBO9beyQuNGzVvuhQ==", - "license": "Apache-2.0", - "peer": true + "dev": true, + "license": "Apache-2.0" }, "node_modules/math-intrinsics": { "version": "1.1.0", @@ -4650,8 +4793,8 @@ "version": "5.2.1", "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.2.1.tgz", "integrity": "sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==", - "license": "MIT", - "peer": true + "dev": true, + "license": "MIT" }, "node_modules/merge-descriptors": { "version": "2.0.0", @@ -4681,8 +4824,8 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "license": "MIT", - "peer": true + "dev": true, + "license": "MIT" }, "node_modules/merge2": { "version": "1.4.1", @@ -4701,8 +4844,8 @@ "version": "0.84.4", "resolved": "https://registry.npmjs.org/metro/-/metro-0.84.4.tgz", "integrity": "sha512-8ETTubqfD6ornDy2zYDvRcKnVDOXdFJsjetYDBsY4oAsb6NJkiwFR+FaMESyGppFmQUyBQA4H4sFGxzcQSGtFA==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/code-frame": "^7.29.0", "@babel/core": "^7.25.2", @@ -4755,8 +4898,8 @@ "version": "0.84.4", "resolved": "https://registry.npmjs.org/metro-babel-transformer/-/metro-babel-transformer-0.84.4.tgz", "integrity": "sha512-rvCfz8snl9h20VcvpOHxZuHP1SlAkv4HXbzw7nyyVwu6Eqo5PRerbakQ9XmUCOsRy70spJ37O+G1TK8oMzo48g==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/core": "^7.25.2", "flow-enums-runtime": "^0.0.6", @@ -4772,15 +4915,15 @@ "version": "0.35.0", "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.35.0.tgz", "integrity": "sha512-xVx5Opwy8Oo1I5yGpVRhCvWL/iV3M+ylksSKVNlxxD90cpDpR/AR1jLYqK8HWihm065a6UI3HeyAmYzwS8NOOg==", - "license": "MIT", - "peer": true + "dev": true, + "license": "MIT" }, "node_modules/metro-babel-transformer/node_modules/hermes-parser": { "version": "0.35.0", "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.35.0.tgz", "integrity": "sha512-9JLjeHxBx8T4CAsydZR49PNZUaix+WpQJwu9p2010lu+7Kwl6D/7wYFFJxoz+aXkaaClp9Zfg6W6/zVlSJORaA==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "hermes-estree": "0.35.0" } @@ -4789,8 +4932,8 @@ "version": "0.84.4", "resolved": "https://registry.npmjs.org/metro-cache/-/metro-cache-0.84.4.tgz", "integrity": "sha512-gpcFQdSLUwUCk71saKoE64jLFbx2nwTfVCcPSULMNT8QYq0p1eZZE29Jvd0HtT/UlhC3ZOutLxJME5xqD2JUZg==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "exponential-backoff": "^3.1.1", "flow-enums-runtime": "^0.0.6", @@ -4805,8 +4948,8 @@ "version": "0.84.4", "resolved": "https://registry.npmjs.org/metro-cache-key/-/metro-cache-key-0.84.4.tgz", "integrity": "sha512-wVO79aGrkYImpnaVS4+d5RrRBRPX31QtvKB3wKGBuiNSznduZTQHzsrJZRroFJSwnygrzdsGUtDQPuqqFjFdvw==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "flow-enums-runtime": "^0.0.6" }, @@ -4818,8 +4961,8 @@ "version": "0.84.4", "resolved": "https://registry.npmjs.org/metro-config/-/metro-config-0.84.4.tgz", "integrity": "sha512-PMotGDjXcXLWo2TMRH+VR99phFNgYTwqh4OoieIKK3yTJa1Jmkl+fZJxDO0jfBvNF+WESHciHvpNuBtXaF3B0Q==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "connect": "^3.6.5", "flow-enums-runtime": "^0.0.6", @@ -4838,8 +4981,8 @@ "version": "0.84.4", "resolved": "https://registry.npmjs.org/metro-core/-/metro-core-0.84.4.tgz", "integrity": "sha512-HONpWC5LGXZn3ffkd4Hu6AIrfE7j4Z0g0wMo/goV24WOB3lhuFZ40KgvaDiSw8iyQHloMYay5N/wPX+z8oN/PQ==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "flow-enums-runtime": "^0.0.6", "lodash.throttle": "^4.1.1", @@ -4853,8 +4996,8 @@ "version": "0.84.4", "resolved": "https://registry.npmjs.org/metro-file-map/-/metro-file-map-0.84.4.tgz", "integrity": "sha512-KSVDi/u60hKPx++NLu3MTIvyjzNoJnFAF8PQFxaj1jiSka/wjw+Ua6sNuJ0TDHQv+7AAoFQxeMgaRAe8Yic5wQ==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "debug": "^4.4.0", "fb-watchman": "^2.0.0", @@ -4874,8 +5017,8 @@ "version": "0.84.4", "resolved": "https://registry.npmjs.org/metro-minify-terser/-/metro-minify-terser-0.84.4.tgz", "integrity": "sha512-5qpbaVOMC7CPitIpuewzVeGw7E+C3ykbv2mqTjQLl85Z3annSVGlSCTcsZjqXZzjupfK4Ztj3dDc4kc44NZwtQ==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "flow-enums-runtime": "^0.0.6", "terser": "^5.15.0" @@ -4888,8 +5031,8 @@ "version": "0.84.4", "resolved": "https://registry.npmjs.org/metro-resolver/-/metro-resolver-0.84.4.tgz", "integrity": "sha512-1qLgbxQ5ZGhhutuPot1Yp348ofDsATL2WkrHF65TobqTT9K3P9qJXw38bomk7ncp5B7OYMfWwtyBZo1lCV792A==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "flow-enums-runtime": "^0.0.6" }, @@ -4901,8 +5044,8 @@ "version": "0.84.4", "resolved": "https://registry.npmjs.org/metro-runtime/-/metro-runtime-0.84.4.tgz", "integrity": "sha512-Jibypds4g7AhzdRKY+kDoj51s5EXMwgyp5ddtlreDAsWefMdOx+agWqgm0H2XSZ/ueanHHVM89fnf5OJnlxa8Q==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/runtime": "^7.25.0", "flow-enums-runtime": "^0.0.6" @@ -4915,8 +5058,8 @@ "version": "0.84.4", "resolved": "https://registry.npmjs.org/metro-source-map/-/metro-source-map-0.84.4.tgz", "integrity": "sha512-jbWkPxIesVuo1IWkvezmMJld6iu8nD62GsrZiV6jP37AOdbo4OBq1FJ+qkOg8sV05wAHB//jAbziuW0SlJfW4g==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/traverse": "^7.29.0", "@babel/types": "^7.29.0", @@ -4936,8 +5079,8 @@ "version": "0.84.4", "resolved": "https://registry.npmjs.org/metro-symbolicate/-/metro-symbolicate-0.84.4.tgz", "integrity": "sha512-OnfpacxUqGPZQ27t8qK9mFa7uqHIlVWeqRqkCbvMvreEBiamEeOn8krKtcwgP5M4cYDPwuSmCTopHMVthqG4zA==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "flow-enums-runtime": "^0.0.6", "invariant": "^2.2.4", @@ -4957,8 +5100,8 @@ "version": "0.84.4", "resolved": "https://registry.npmjs.org/metro-transform-plugins/-/metro-transform-plugins-0.84.4.tgz", "integrity": "sha512-kehr6HbAecqD0/a3xLXobELdPaAmRAl8bel0qagPF4vhZtux93nS8S4eq2kgKt6J2GnQpVjSoW1PXdst04mwow==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/core": "^7.25.2", "@babel/generator": "^7.29.1", @@ -4975,8 +5118,8 @@ "version": "0.84.4", "resolved": "https://registry.npmjs.org/metro-transform-worker/-/metro-transform-worker-0.84.4.tgz", "integrity": "sha512-W1IYMvvXTu4MxYr7d9h7CeG2vpIr3bmLLIavkPY4O1ilzDrvS8z/NEe6y+pC44Ff7raMXQgYSfdqDUwN/i39gg==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/core": "^7.25.2", "@babel/generator": "^7.29.1", @@ -5000,15 +5143,15 @@ "version": "0.35.0", "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.35.0.tgz", "integrity": "sha512-xVx5Opwy8Oo1I5yGpVRhCvWL/iV3M+ylksSKVNlxxD90cpDpR/AR1jLYqK8HWihm065a6UI3HeyAmYzwS8NOOg==", - "license": "MIT", - "peer": true + "dev": true, + "license": "MIT" }, "node_modules/metro/node_modules/hermes-parser": { "version": "0.35.0", "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.35.0.tgz", "integrity": "sha512-9JLjeHxBx8T4CAsydZR49PNZUaix+WpQJwu9p2010lu+7Kwl6D/7wYFFJxoz+aXkaaClp9Zfg6W6/zVlSJORaA==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "hermes-estree": "0.35.0" } @@ -5030,8 +5173,8 @@ "version": "1.6.0", "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true, "license": "MIT", - "peer": true, "bin": { "mime": "cli.js" }, @@ -5064,38 +5207,12 @@ "url": "https://opencollective.com/express" } }, - "node_modules/minimatch": { - "version": "10.2.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", - "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "brace-expansion": "^5.0.5" - }, - "engines": { - "node": "18 || 20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/minipass": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", - "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", - "dev": true, - "license": "BlueOak-1.0.0", - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, "node_modules/mkdirp": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true, "license": "MIT", - "peer": true, "bin": { "mkdirp": "bin/cmd.js" }, @@ -5126,9 +5243,9 @@ } }, "node_modules/multiformats": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/multiformats/-/multiformats-14.0.0.tgz", - "integrity": "sha512-iWK1RrAS58p2NDfeZFuSUSv3ZPewTIhsGbh/5NgeGGJwJmRljLxGtjRR3nkn+loG3zl+IrfR/W1590QnrSK+Gg==", + "version": "14.0.5", + "resolved": "https://registry.npmjs.org/multiformats/-/multiformats-14.0.5.tgz", + "integrity": "sha512-vbIm83F2yZ1pWJGS0yl0ysracIvv56LtbrIyiIQHoLdYDJOMoLfVFsXhh9DUH4SFdkdkFhucyWniihsNzVEjkQ==", "license": "Apache-2.0 OR MIT" }, "node_modules/mz": { @@ -5174,15 +5291,15 @@ "version": "0.4.0", "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", - "license": "MIT", - "peer": true + "dev": true, + "license": "MIT" }, "node_modules/node-releases": { "version": "2.0.47", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.47.tgz", "integrity": "sha512-Uzmd6LXpouKo8EUK68IjH4+E01w/hXyV3R3g/geCJo+rXLNfh1xucB+LOzYEOQPSiUK3h/xZf0cQGcSsmyL2Og==", + "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=18" } @@ -5191,15 +5308,15 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/nullthrows/-/nullthrows-1.1.1.tgz", "integrity": "sha512-2vPPEi+Z7WqML2jZYddDIfy5Dqb0r2fze2zTxNNknZaFpVHU3mFB3R+DWeJWGVx0ecvttSGlJTI+WG+8Z4cDWw==", - "license": "MIT", - "peer": true + "dev": true, + "license": "MIT" }, "node_modules/ob1": { "version": "0.84.4", "resolved": "https://registry.npmjs.org/ob1/-/ob1-0.84.4.tgz", "integrity": "sha512-eJXMpz4aQHXF/YBB9ddqZDIS+ooO91hObo9FoW/xBkr54/zCwYYCDqT/O54vNo8kOkWs5Ou/y28NgdrV0edQNA==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "flow-enums-runtime": "^0.0.6" }, @@ -5228,12 +5345,26 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/obug": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.4.tgz", + "integrity": "sha512-4a+OsYv9UktOJKE+l1A4OufDgdRF9PifWj+tJnHURo/P+WOxpG4GzUFL9qCalmWauao6ogiG+QvnCovwPoyAWA==", + "dev": true, + "funding": [ + "https://github.com/sponsors/sxzz", + "https://opencollective.com/debug" + ], + "license": "MIT", + "engines": { + "node": ">=12.20.0" + } + }, "node_modules/on-finished": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", "integrity": "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "ee-first": "1.1.1" }, @@ -5254,8 +5385,8 @@ "version": "7.4.2", "resolved": "https://registry.npmjs.org/open/-/open-7.4.2.tgz", "integrity": "sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "is-docker": "^2.0.0", "is-wsl": "^2.1.1" @@ -5314,13 +5445,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/package-json-from-dist": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", - "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", - "dev": true, - "license": "BlueOak-1.0.0" - }, "node_modules/parse-duration": { "version": "2.1.6", "resolved": "https://registry.npmjs.org/parse-duration/-/parse-duration-2.1.6.tgz", @@ -5345,30 +5469,6 @@ "node": ">=8" } }, - "node_modules/path-scurry": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", - "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "lru-cache": "^10.2.0", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" - }, - "engines": { - "node": ">=16 || 14 >=14.18" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/path-scurry/node_modules/lru-cache": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", - "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", - "dev": true, - "license": "ISC" - }, "node_modules/path-to-regexp": { "version": "8.4.2", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.4.2.tgz", @@ -5386,20 +5486,11 @@ "dev": true, "license": "MIT" }, - "node_modules/pathval": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.1.tgz", - "integrity": "sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 14.16" - } - }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, "license": "ISC" }, "node_modules/picomatch": { @@ -5446,9 +5537,9 @@ } }, "node_modules/postcss": { - "version": "8.5.16", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.16.tgz", - "integrity": "sha512-vuwillviilfKZsg0VGj5R/YwwcHx4SLsIOI/7K6mQkWx+l5cUHTjj5g0AasTBcyXsbfTgrwsUNmVUb5xVwyPwg==", + "version": "8.5.23", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.23.tgz", + "integrity": "sha512-g50586zr4bZmwFiTlflMu8E0bDTb5I5gertgwAKmsdUlTQIhZtunzUlD1WSzwcVWPoAVpsrA6vlfCD7oXvRwgg==", "dev": true, "funding": [ { @@ -5466,7 +5557,7 @@ ], "license": "MIT", "dependencies": { - "nanoid": "^3.3.12", + "nanoid": "^3.3.16", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" }, @@ -5518,9 +5609,9 @@ } }, "node_modules/postcss/node_modules/nanoid": { - "version": "3.3.15", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.15.tgz", - "integrity": "sha512-y7Wygv/7mEOvxTuEQDB8StXdMRBWf1kR/tlhAzBRUFkB2jfcLOAxO/SHmOO2zgz1pVgK29/kyupn059/bCHdjA==", + "version": "3.3.16", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.16.tgz", + "integrity": "sha512-bzlKTyNJ7+LdGIIwy8ijFpIqEQIvafahV7eYykJ8Cvh42EdJeODoJ6gUJXpQJvej1BddH8OqTXZNE/KfbWAu8Q==", "dev": true, "funding": [ { @@ -5540,8 +5631,8 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/schemas": "^29.6.3", "ansi-styles": "^5.0.0", @@ -5555,8 +5646,8 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=10" }, @@ -5574,8 +5665,8 @@ "version": "8.3.0", "resolved": "https://registry.npmjs.org/promise/-/promise-8.3.0.tgz", "integrity": "sha512-rZPNPKTOYVNEEKFaq1HqTgOwZD+4/YHS5ukLzQCypkj+OkYx7iv0mA91lJlpPPZ8vMau3IIGj5Qlwrx+8iiSmg==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "asap": "~2.0.6" } @@ -5629,16 +5720,6 @@ "node": ">= 0.10" } }, - "node_modules/punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, "node_modules/qs": { "version": "6.15.3", "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.3.tgz", @@ -5659,8 +5740,8 @@ "version": "6.0.2", "resolved": "https://registry.npmjs.org/queue/-/queue-6.0.2.tgz", "integrity": "sha512-iHZWu+q3IdFZFX36ro/lKBkSvfkztY5Y7HMiPlOUjhupPcG2JMfst2KKEpu5XndviX/3UhFbRngUPNKtgvtZiA==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "inherits": "~2.0.3" } @@ -5729,6 +5810,7 @@ "version": "19.2.7", "resolved": "https://registry.npmjs.org/react/-/react-19.2.7.tgz", "integrity": "sha512-HNe9WslTbXmFK8o8cmwgAeJFSBvt1bPdHCVKtaaV+WlAN36mpT4hcRpwbf3fY56ar2oIXzsBpOAiIRHAdY0OlQ==", + "dev": true, "license": "MIT", "peer": true, "engines": { @@ -5739,8 +5821,8 @@ "version": "6.1.5", "resolved": "https://registry.npmjs.org/react-devtools-core/-/react-devtools-core-6.1.5.tgz", "integrity": "sha512-ePrwPfxAnB+7hgnEr8vpKxL9cmnp7F322t8oqcPshbIQQhDKgFDW4tjhF2wjVbdXF9O/nyuy3sQWd9JGpiLPvA==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "shell-quote": "^1.6.1", "ws": "^7" @@ -5750,68 +5832,8 @@ "version": "18.3.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", - "license": "MIT", - "peer": true - }, - "node_modules/react-native": { - "version": "0.86.0", - "resolved": "https://registry.npmjs.org/react-native/-/react-native-0.86.0.tgz", - "integrity": "sha512-17ALh/dd6AO4pgOVmOO5Axll5PbErEo3XFyLokyzW6usyi+OShIEPwUW26wLPlhVifgSOIfECCH0WN+0IqtJ1w==", - "license": "MIT", - "peer": true, - "dependencies": { - "@react-native/assets-registry": "0.86.0", - "@react-native/codegen": "0.86.0", - "@react-native/community-cli-plugin": "0.86.0", - "@react-native/gradle-plugin": "0.86.0", - "@react-native/js-polyfills": "0.86.0", - "@react-native/normalize-colors": "0.86.0", - "@react-native/virtualized-lists": "0.86.0", - "abort-controller": "^3.0.0", - "anser": "^1.4.9", - "ansi-regex": "^5.0.0", - "babel-plugin-syntax-hermes-parser": "0.36.0", - "base64-js": "^1.5.1", - "commander": "^12.0.0", - "flow-enums-runtime": "^0.0.6", - "hermes-compiler": "250829098.0.14", - "invariant": "^2.2.4", - "memoize-one": "^5.0.0", - "metro-runtime": "^0.84.3", - "metro-source-map": "^0.84.3", - "nullthrows": "^1.1.1", - "pretty-format": "^29.7.0", - "promise": "^8.3.0", - "react-devtools-core": "^6.1.5", - "react-refresh": "^0.14.0", - "regenerator-runtime": "^0.13.2", - "scheduler": "0.27.0", - "semver": "^7.1.3", - "stacktrace-parser": "^0.1.10", - "tinyglobby": "^0.2.15", - "whatwg-fetch": "^3.0.0", - "ws": "^7.5.10", - "yargs": "^17.6.2" - }, - "bin": { - "react-native": "cli.js" - }, - "engines": { - "node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0" - }, - "peerDependencies": { - "@react-native/jest-preset": "0.86.0", - "@types/react": "^19.1.1", - "react": "^19.2.3" - }, - "peerDependenciesMeta": { - "@react-native/jest-preset": { - "optional": true - }, - "@types/react": { - "optional": true - } - } + "dev": true, + "license": "MIT" }, "node_modules/react-native-fetch-api": { "version": "3.0.0", @@ -5823,38 +5845,12 @@ "p-defer": "^3.0.0" } }, - "node_modules/react-native-get-random-values": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/react-native-get-random-values/-/react-native-get-random-values-1.11.0.tgz", - "integrity": "sha512-4BTbDbRmS7iPdhYLRcz3PGFIpFJBwNZg9g42iwa2P6FOv9vZj/xJc678RZXnLNZzd0qd7Q3CCF6Yd+CU2eoXKQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-base64-decode": "^1.0.0" - }, - "peerDependencies": { - "react-native": ">=0.56" - } - }, - "node_modules/react-native-url-polyfill": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/react-native-url-polyfill/-/react-native-url-polyfill-2.0.0.tgz", - "integrity": "sha512-My330Do7/DvKnEvwQc0WdcBnFPploYKp9CYlefDXzIdEaA+PAhDYllkvGeEroEzvc4Kzzj2O4yVdz8v6fjRvhA==", - "dev": true, - "license": "MIT", - "dependencies": { - "whatwg-url-without-unicode": "8.0.0-3" - }, - "peerDependencies": { - "react-native": "*" - } - }, "node_modules/react-refresh": { "version": "0.14.2", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.2.tgz", "integrity": "sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==", + "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=0.10.0" } @@ -5891,15 +5887,15 @@ "version": "0.13.11", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==", - "license": "MIT", - "peer": true + "dev": true, + "license": "MIT" }, "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=0.10.0" } @@ -5933,6 +5929,40 @@ "node": ">=0.10.0" } }, + "node_modules/rolldown": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.1.5.tgz", + "integrity": "sha512-t9z29cJjXf/vxQ8dyhCSpt6H6aSwHTk8cT5I3iy6SMXuFpk5mB6PL6XfC8PCwrPTx93udwKUm9HRteAlTGBLiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@oxc-project/types": "=0.139.0", + "@rolldown/pluginutils": "^1.0.0" + }, + "bin": { + "rolldown": "bin/cli.mjs" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "optionalDependencies": { + "@rolldown/binding-android-arm64": "1.1.5", + "@rolldown/binding-darwin-arm64": "1.1.5", + "@rolldown/binding-darwin-x64": "1.1.5", + "@rolldown/binding-freebsd-x64": "1.1.5", + "@rolldown/binding-linux-arm-gnueabihf": "1.1.5", + "@rolldown/binding-linux-arm64-gnu": "1.1.5", + "@rolldown/binding-linux-arm64-musl": "1.1.5", + "@rolldown/binding-linux-ppc64-gnu": "1.1.5", + "@rolldown/binding-linux-s390x-gnu": "1.1.5", + "@rolldown/binding-linux-x64-gnu": "1.1.5", + "@rolldown/binding-linux-x64-musl": "1.1.5", + "@rolldown/binding-openharmony-arm64": "1.1.5", + "@rolldown/binding-wasm32-wasi": "1.1.5", + "@rolldown/binding-win32-arm64-msvc": "1.1.5", + "@rolldown/binding-win32-x64-msvc": "1.1.5" + } + }, "node_modules/rollup": { "version": "4.62.2", "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.62.2.tgz", @@ -6047,13 +6077,14 @@ "version": "0.27.0", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", - "license": "MIT", - "peer": true + "dev": true, + "license": "MIT" }, "node_modules/semver": { "version": "7.8.4", "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.4.tgz", "integrity": "sha512-rUCObTnP32Q08R2uuIrt7r9PlEonuTmtuXYcW6s5kjdlj3xbnwe+21yXptAUYcMAABLkYYTtnmzb3w3EDZfueA==", + "dev": true, "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -6066,8 +6097,8 @@ "version": "0.19.2", "resolved": "https://registry.npmjs.org/send/-/send-0.19.2.tgz", "integrity": "sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "debug": "2.6.9", "depd": "2.0.0", @@ -6091,8 +6122,8 @@ "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "ms": "2.0.0" } @@ -6101,15 +6132,15 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "license": "MIT", - "peer": true + "dev": true, + "license": "MIT" }, "node_modules/send/node_modules/encodeurl": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">= 0.8" } @@ -6118,15 +6149,15 @@ "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT", - "peer": true + "dev": true, + "license": "MIT" }, "node_modules/send/node_modules/on-finished": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "ee-first": "1.1.1" }, @@ -6138,8 +6169,8 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">= 0.8" } @@ -6148,8 +6179,8 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-2.1.0.tgz", "integrity": "sha512-ghgmKt5o4Tly5yEG/UJp8qTd0AN7Xalw4XBtDEKP655B699qMEtra1WlXeE6WIvdEG481JvRxULKsInq/iNysw==", + "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=0.10.0" } @@ -6158,8 +6189,8 @@ "version": "1.16.3", "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.3.tgz", "integrity": "sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "encodeurl": "~2.0.0", "escape-html": "~1.0.3", @@ -6174,8 +6205,8 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">= 0.8" } @@ -6208,11 +6239,11 @@ } }, "node_modules/shell-quote": { - "version": "1.8.4", - "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.4.tgz", - "integrity": "sha512-VsC6n6vz1ihYYyZZwX7YZSF5l5x36ca17OC+a69h94YqB7X6XLwf+5MOgynYir2SLFUbl8gIYvBo8K8RoNQ6bQ==", + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.10.0.tgz", + "integrity": "sha512-w1aiOKwKuRgtwAReIIj89puqg+I7GvX4IbLrvmhXbzQsj1+Zwi4VO3+fa6ZF91TWSjIxoEkKnMeHcLEODK5ZXA==", + "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">= 0.4" }, @@ -6299,25 +6330,12 @@ "dev": true, "license": "ISC" }, - "node_modules/signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/source-map": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", + "dev": true, "license": "BSD-3-Clause", - "peer": true, "engines": { "node": ">=0.10.0" } @@ -6336,8 +6354,8 @@ "version": "0.5.21", "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "buffer-from": "^1.0.0", "source-map": "^0.6.0" @@ -6347,8 +6365,8 @@ "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, "license": "BSD-3-Clause", - "peer": true, "engines": { "node": ">=0.10.0" } @@ -6364,15 +6382,15 @@ "version": "1.3.4", "resolved": "https://registry.npmjs.org/stackframe/-/stackframe-1.3.4.tgz", "integrity": "sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==", - "license": "MIT", - "peer": true + "dev": true, + "license": "MIT" }, "node_modules/stacktrace-parser": { "version": "0.1.11", "resolved": "https://registry.npmjs.org/stacktrace-parser/-/stacktrace-parser-0.1.11.tgz", "integrity": "sha512-WjlahMgHmCJpqzU8bIBy4qtsZdU9lRlcZE3Lvyej6t4tuOuv1vk57OW3MBrj6hXBFx/nNoC9MPMTcr5YA7NQbg==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "type-fest": "^0.7.1" }, @@ -6384,16 +6402,16 @@ "version": "1.5.0", "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", + "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">= 0.6" } }, "node_modules/std-env": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.10.0.tgz", - "integrity": "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-4.2.0.tgz", + "integrity": "sha512-oCUKSupKTHX53EyjDtuZQ64pjLJ6yYCtpmEw0goYxtjG9KpbRe8KAsl2tBUGU9DyMcJ0RwJ8GqJAFzMXcXW1Rw==", "dev": true, "license": "MIT" }, @@ -6416,21 +6434,6 @@ } }, "node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/string-width-cjs": { - "name": "string-width", "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", @@ -6446,19 +6449,6 @@ } }, "node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi-cjs": { - "name": "strip-ansi", "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", @@ -6471,26 +6461,6 @@ "node": ">=8" } }, - "node_modules/strip-literal": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-3.1.0.tgz", - "integrity": "sha512-8r3mkIM/2+PpjHoOtiAW8Rg3jJLHaV7xPwG+YRGrv6FP0wwk/toTpATxWYOW0BKdWwl82VT2tFYi5DlROa0Mxg==", - "dev": true, - "license": "MIT", - "dependencies": { - "js-tokens": "^9.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/antfu" - } - }, - "node_modules/strip-literal/node_modules/js-tokens": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-9.0.1.tgz", - "integrity": "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==", - "dev": true, - "license": "MIT" - }, "node_modules/sucrase": { "version": "3.35.1", "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.1.tgz", @@ -6540,8 +6510,8 @@ "version": "5.48.0", "resolved": "https://registry.npmjs.org/terser/-/terser-5.48.0.tgz", "integrity": "sha512-J/9An6vs9Us6wKRriSFXBWdRZapREHqFzdNUKk0pmu804EMR6dr6winwo7e5JDxN4xahxQsuysyYFwlwj4XN/Q==", + "dev": true, "license": "BSD-2-Clause", - "peer": true, "dependencies": { "@jridgewell/source-map": "^0.3.3", "acorn": "^8.15.0", @@ -6559,23 +6529,8 @@ "version": "2.20.3", "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "license": "MIT", - "peer": true - }, - "node_modules/test-exclude": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-7.0.2.tgz", - "integrity": "sha512-u9E6A+ZDYdp7a4WnarkXPZOx8Ilz46+kby6p1yZ8zsGTz9gYa6FIS7lj2oezzNKmtdyyJNNmmXDppga5GB7kSw==", "dev": true, - "license": "ISC", - "dependencies": { - "@istanbuljs/schema": "^0.1.2", - "glob": "^10.4.1", - "minimatch": "^10.2.2" - }, - "engines": { - "node": ">=18" - } + "license": "MIT" }, "node_modules/thenify": { "version": "3.3.1", @@ -6604,8 +6559,8 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/throat/-/throat-5.0.0.tgz", "integrity": "sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA==", - "license": "MIT", - "peer": true + "dev": true, + "license": "MIT" }, "node_modules/tinybench": { "version": "2.9.0", @@ -6625,6 +6580,7 @@ "version": "0.2.17", "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", + "dev": true, "license": "MIT", "dependencies": { "fdir": "^6.5.0", @@ -6641,6 +6597,7 @@ "version": "6.5.0", "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, "license": "MIT", "engines": { "node": ">=12.0.0" @@ -6658,6 +6615,7 @@ "version": "4.0.4", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "dev": true, "license": "MIT", "engines": { "node": ">=12" @@ -6666,30 +6624,10 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/tinypool": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.1.1.tgz", - "integrity": "sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.0.0 || >=20.0.0" - } - }, "node_modules/tinyrainbow": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-2.0.0.tgz", - "integrity": "sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/tinyspy": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-4.0.4.tgz", - "integrity": "sha512-azl+t0z7pw/z958Gy9svOTuzqIk6xq+NSheJzn5MMWtWTFywIacg2wUlzKFGtt3cthx0r2SxMK0yzJOR0IES7Q==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.1.0.tgz", + "integrity": "sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==", "dev": true, "license": "MIT", "engines": { @@ -6700,8 +6638,8 @@ "version": "1.0.5", "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", - "license": "BSD-3-Clause", - "peer": true + "dev": true, + "license": "BSD-3-Clause" }, "node_modules/to-regex-range": { "version": "5.0.1", @@ -6801,506 +6739,22 @@ } } }, - "node_modules/tsup/node_modules/@esbuild/aix-ppc64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.7.tgz", - "integrity": "sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg==", - "cpu": [ - "ppc64" - ], + "node_modules/tsup/node_modules/source-map": { + "version": "0.7.6", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.6.tgz", + "integrity": "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==", "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "aix" - ], + "license": "BSD-3-Clause", "engines": { - "node": ">=18" - } - }, - "node_modules/tsup/node_modules/@esbuild/android-arm": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.7.tgz", - "integrity": "sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsup/node_modules/@esbuild/android-arm64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.7.tgz", - "integrity": "sha512-62dPZHpIXzvChfvfLJow3q5dDtiNMkwiRzPylSCfriLvZeq0a1bWChrGx/BbUbPwOrsWKMn8idSllklzBy+dgQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsup/node_modules/@esbuild/android-x64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.7.tgz", - "integrity": "sha512-x5VpMODneVDb70PYV2VQOmIUUiBtY3D3mPBG8NxVk5CogneYhkR7MmM3yR/uMdITLrC1ml/NV1rj4bMJuy9MCg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsup/node_modules/@esbuild/darwin-arm64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.7.tgz", - "integrity": "sha512-5lckdqeuBPlKUwvoCXIgI2D9/ABmPq3Rdp7IfL70393YgaASt7tbju3Ac+ePVi3KDH6N2RqePfHnXkaDtY9fkw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsup/node_modules/@esbuild/darwin-x64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.7.tgz", - "integrity": "sha512-rYnXrKcXuT7Z+WL5K980jVFdvVKhCHhUwid+dDYQpH+qu+TefcomiMAJpIiC2EM3Rjtq0sO3StMV/+3w3MyyqQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsup/node_modules/@esbuild/freebsd-arm64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.7.tgz", - "integrity": "sha512-B48PqeCsEgOtzME2GbNM2roU29AMTuOIN91dsMO30t+Ydis3z/3Ngoj5hhnsOSSwNzS+6JppqWsuhTp6E82l2w==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsup/node_modules/@esbuild/freebsd-x64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.7.tgz", - "integrity": "sha512-jOBDK5XEjA4m5IJK3bpAQF9/Lelu/Z9ZcdhTRLf4cajlB+8VEhFFRjWgfy3M1O4rO2GQ/b2dLwCUGpiF/eATNQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsup/node_modules/@esbuild/linux-arm": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.7.tgz", - "integrity": "sha512-RkT/YXYBTSULo3+af8Ib0ykH8u2MBh57o7q/DAs3lTJlyVQkgQvlrPTnjIzzRPQyavxtPtfg0EopvDyIt0j1rA==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsup/node_modules/@esbuild/linux-arm64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.7.tgz", - "integrity": "sha512-RZPHBoxXuNnPQO9rvjh5jdkRmVizktkT7TCDkDmQ0W2SwHInKCAV95GRuvdSvA7w4VMwfCjUiPwDi0ZO6Nfe9A==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsup/node_modules/@esbuild/linux-ia32": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.7.tgz", - "integrity": "sha512-GA48aKNkyQDbd3KtkplYWT102C5sn/EZTY4XROkxONgruHPU72l+gW+FfF8tf2cFjeHaRbWpOYa/uRBz/Xq1Pg==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsup/node_modules/@esbuild/linux-loong64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.7.tgz", - "integrity": "sha512-a4POruNM2oWsD4WKvBSEKGIiWQF8fZOAsycHOt6JBpZ+JN2n2JH9WAv56SOyu9X5IqAjqSIPTaJkqN8F7XOQ5Q==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsup/node_modules/@esbuild/linux-mips64el": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.7.tgz", - "integrity": "sha512-KabT5I6StirGfIz0FMgl1I+R1H73Gp0ofL9A3nG3i/cYFJzKHhouBV5VWK1CSgKvVaG4q1RNpCTR2LuTVB3fIw==", - "cpu": [ - "mips64el" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsup/node_modules/@esbuild/linux-ppc64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.7.tgz", - "integrity": "sha512-gRsL4x6wsGHGRqhtI+ifpN/vpOFTQtnbsupUF5R5YTAg+y/lKelYR1hXbnBdzDjGbMYjVJLJTd2OFmMewAgwlQ==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsup/node_modules/@esbuild/linux-riscv64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.7.tgz", - "integrity": "sha512-hL25LbxO1QOngGzu2U5xeXtxXcW+/GvMN3ejANqXkxZ/opySAZMrc+9LY/WyjAan41unrR3YrmtTsUpwT66InQ==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsup/node_modules/@esbuild/linux-s390x": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.7.tgz", - "integrity": "sha512-2k8go8Ycu1Kb46vEelhu1vqEP+UeRVj2zY1pSuPdgvbd5ykAw82Lrro28vXUrRmzEsUV0NzCf54yARIK8r0fdw==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsup/node_modules/@esbuild/linux-x64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.7.tgz", - "integrity": "sha512-hzznmADPt+OmsYzw1EE33ccA+HPdIqiCRq7cQeL1Jlq2gb1+OyWBkMCrYGBJ+sxVzve2ZJEVeePbLM2iEIZSxA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsup/node_modules/@esbuild/netbsd-arm64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.7.tgz", - "integrity": "sha512-b6pqtrQdigZBwZxAn1UpazEisvwaIDvdbMbmrly7cDTMFnw/+3lVxxCTGOrkPVnsYIosJJXAsILG9XcQS+Yu6w==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsup/node_modules/@esbuild/netbsd-x64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.7.tgz", - "integrity": "sha512-OfatkLojr6U+WN5EDYuoQhtM+1xco+/6FSzJJnuWiUw5eVcicbyK3dq5EeV/QHT1uy6GoDhGbFpprUiHUYggrw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsup/node_modules/@esbuild/openbsd-arm64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.7.tgz", - "integrity": "sha512-AFuojMQTxAz75Fo8idVcqoQWEHIXFRbOc1TrVcFSgCZtQfSdc1RXgB3tjOn/krRHENUB4j00bfGjyl2mJrU37A==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsup/node_modules/@esbuild/openbsd-x64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.7.tgz", - "integrity": "sha512-+A1NJmfM8WNDv5CLVQYJ5PshuRm/4cI6WMZRg1by1GwPIQPCTs1GLEUHwiiQGT5zDdyLiRM/l1G0Pv54gvtKIg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsup/node_modules/@esbuild/openharmony-arm64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.7.tgz", - "integrity": "sha512-+KrvYb/C8zA9CU/g0sR6w2RBw7IGc5J2BPnc3dYc5VJxHCSF1yNMxTV5LQ7GuKteQXZtspjFbiuW5/dOj7H4Yw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openharmony" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsup/node_modules/@esbuild/sunos-x64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.7.tgz", - "integrity": "sha512-ikktIhFBzQNt/QDyOL580ti9+5mL/YZeUPKU2ivGtGjdTYoqz6jObj6nOMfhASpS4GU4Q/Clh1QtxWAvcYKamA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsup/node_modules/@esbuild/win32-arm64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.7.tgz", - "integrity": "sha512-7yRhbHvPqSpRUV7Q20VuDwbjW5kIMwTHpptuUzV+AA46kiPze5Z7qgt6CLCK3pWFrHeNfDd1VKgyP4O+ng17CA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsup/node_modules/@esbuild/win32-ia32": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.7.tgz", - "integrity": "sha512-SmwKXe6VHIyZYbBLJrhOoCJRB/Z1tckzmgTLfFYOfpMAx63BJEaL9ExI8x7v0oAO3Zh6D/Oi1gVxEYr5oUCFhw==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsup/node_modules/@esbuild/win32-x64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.7.tgz", - "integrity": "sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsup/node_modules/esbuild": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.7.tgz", - "integrity": "sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=18" - }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.27.7", - "@esbuild/android-arm": "0.27.7", - "@esbuild/android-arm64": "0.27.7", - "@esbuild/android-x64": "0.27.7", - "@esbuild/darwin-arm64": "0.27.7", - "@esbuild/darwin-x64": "0.27.7", - "@esbuild/freebsd-arm64": "0.27.7", - "@esbuild/freebsd-x64": "0.27.7", - "@esbuild/linux-arm": "0.27.7", - "@esbuild/linux-arm64": "0.27.7", - "@esbuild/linux-ia32": "0.27.7", - "@esbuild/linux-loong64": "0.27.7", - "@esbuild/linux-mips64el": "0.27.7", - "@esbuild/linux-ppc64": "0.27.7", - "@esbuild/linux-riscv64": "0.27.7", - "@esbuild/linux-s390x": "0.27.7", - "@esbuild/linux-x64": "0.27.7", - "@esbuild/netbsd-arm64": "0.27.7", - "@esbuild/netbsd-x64": "0.27.7", - "@esbuild/openbsd-arm64": "0.27.7", - "@esbuild/openbsd-x64": "0.27.7", - "@esbuild/openharmony-arm64": "0.27.7", - "@esbuild/sunos-x64": "0.27.7", - "@esbuild/win32-arm64": "0.27.7", - "@esbuild/win32-ia32": "0.27.7", - "@esbuild/win32-x64": "0.27.7" - } - }, - "node_modules/tsup/node_modules/source-map": { - "version": "0.7.6", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.6.tgz", - "integrity": "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">= 12" + "node": ">= 12" } }, "node_modules/type-fest": { "version": "0.7.1", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.7.1.tgz", "integrity": "sha512-Ne2YiiGN8bmrmJJEuTWTLJR32nh/JdL1+PSicowtNb0WFpn59GK8/lfD61bVtzguz7b3PBt74nxpv/Pw5po5Rg==", + "dev": true, "license": "(MIT OR CC0-1.0)", - "peer": true, "engines": { "node": ">=8" } @@ -7410,9 +6864,10 @@ } }, "node_modules/undici-types": { - "version": "7.24.6", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.24.6.tgz", - "integrity": "sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==", + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-8.3.0.tgz", + "integrity": "sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ==", + "dev": true, "license": "MIT" }, "node_modules/unpipe": { @@ -7428,6 +6883,7 @@ "version": "1.2.3", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", + "dev": true, "funding": [ { "type": "opencollective", @@ -7443,7 +6899,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" @@ -7471,8 +6926,8 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">= 0.4.0" } @@ -7487,18 +6942,17 @@ } }, "node_modules/vite": { - "version": "7.3.6", - "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.6.tgz", - "integrity": "sha512-4XP60spRGjSZFf1qYH+dJIkK2znL3zQfl9KkOV9MkkRR/3Dls0dxaBsQPTloEc5BLXWPL9vsOxopxyKoMmDueg==", + "version": "8.1.5", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.1.5.tgz", + "integrity": "sha512-7ULLwsCdYx/nRyrpiEwvqb5TFHrMVZyBt+rg/OAXT7rgj/z+DtTDyKFeLAdDkubDVDKD8jOsndmy7m55XcfUsw==", "dev": true, "license": "MIT", "dependencies": { - "esbuild": "^0.27.0 || ^0.28.0", - "fdir": "^6.5.0", - "picomatch": "^4.0.3", - "postcss": "^8.5.6", - "rollup": "^4.43.0", - "tinyglobby": "^0.2.15" + "lightningcss": "^1.32.0", + "picomatch": "^4.0.5", + "postcss": "^8.5.17", + "rolldown": "~1.1.5", + "tinyglobby": "^0.2.17" }, "bin": { "vite": "bin/vite.js" @@ -7514,9 +6968,10 @@ }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", + "@vitejs/devtools": "^0.3.0", + "esbuild": "^0.27.0 || ^0.28.0", "jiti": ">=1.21.0", "less": "^4.0.0", - "lightningcss": "^1.21.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", @@ -7529,13 +6984,16 @@ "@types/node": { "optional": true }, - "jiti": { + "@vitejs/devtools": { "optional": true }, - "less": { + "esbuild": { + "optional": true + }, + "jiti": { "optional": true }, - "lightningcss": { + "less": { "optional": true }, "sass": { @@ -7561,47 +7019,6 @@ } } }, - "node_modules/vite-node": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.2.4.tgz", - "integrity": "sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg==", - "dev": true, - "license": "MIT", - "dependencies": { - "cac": "^6.7.14", - "debug": "^4.4.1", - "es-module-lexer": "^1.7.0", - "pathe": "^2.0.3", - "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" - }, - "bin": { - "vite-node": "vite-node.mjs" - }, - "engines": { - "node": "^18.0.0 || ^20.0.0 || >=22.0.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/vite/node_modules/fdir": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", - "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12.0.0" - }, - "peerDependencies": { - "picomatch": "^3 || ^4" - }, - "peerDependenciesMeta": { - "picomatch": { - "optional": true - } - } - }, "node_modules/vite/node_modules/picomatch": { "version": "4.0.5", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz", @@ -7616,65 +7033,79 @@ } }, "node_modules/vitest": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-3.2.6.tgz", - "integrity": "sha512-xejya+bT/j/+R/AGa1XOfRxLmNUlLtlwjRsFUILF+xHfzElmGcmFydy2gqqIrd62ptIEfwVMofd19uNWD9L7Nw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/chai": "^5.2.2", - "@vitest/expect": "3.2.6", - "@vitest/mocker": "3.2.6", - "@vitest/pretty-format": "^3.2.6", - "@vitest/runner": "3.2.6", - "@vitest/snapshot": "3.2.6", - "@vitest/spy": "3.2.6", - "@vitest/utils": "3.2.6", - "chai": "^5.2.0", - "debug": "^4.4.1", - "expect-type": "^1.2.1", - "magic-string": "^0.30.17", + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.10.tgz", + "integrity": "sha512-R9jUTe5S4Qb0HCd4TNqpC7oGcrMssMRGXLW80ubjWsW9VH5GF8y1Y0SFLY9AbqSk6nt0PnOx4H4WNJYZ13GUPw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/expect": "4.1.10", + "@vitest/mocker": "4.1.10", + "@vitest/pretty-format": "4.1.10", + "@vitest/runner": "4.1.10", + "@vitest/snapshot": "4.1.10", + "@vitest/spy": "4.1.10", + "@vitest/utils": "4.1.10", + "es-module-lexer": "^2.0.0", + "expect-type": "^1.3.0", + "magic-string": "^0.30.21", + "obug": "^2.1.1", "pathe": "^2.0.3", - "picomatch": "^4.0.2", - "std-env": "^3.9.0", + "picomatch": "^4.0.3", + "std-env": "^4.0.0-rc.1", "tinybench": "^2.9.0", - "tinyexec": "^0.3.2", - "tinyglobby": "^0.2.14", - "tinypool": "^1.1.1", - "tinyrainbow": "^2.0.0", - "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0", - "vite-node": "3.2.4", + "tinyexec": "^1.0.2", + "tinyglobby": "^0.2.15", + "tinyrainbow": "^3.1.0", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0", "why-is-node-running": "^2.3.0" }, "bin": { "vitest": "vitest.mjs" }, "engines": { - "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + "node": "^20.0.0 || ^22.0.0 || >=24.0.0" }, "funding": { "url": "https://opencollective.com/vitest" }, "peerDependencies": { "@edge-runtime/vm": "*", - "@types/debug": "^4.1.12", - "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", - "@vitest/browser": "3.2.6", - "@vitest/ui": "3.2.6", + "@opentelemetry/api": "^1.9.0", + "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", + "@vitest/browser-playwright": "4.1.10", + "@vitest/browser-preview": "4.1.10", + "@vitest/browser-webdriverio": "4.1.10", + "@vitest/coverage-istanbul": "4.1.10", + "@vitest/coverage-v8": "4.1.10", + "@vitest/ui": "4.1.10", "happy-dom": "*", - "jsdom": "*" + "jsdom": "*", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" }, "peerDependenciesMeta": { "@edge-runtime/vm": { "optional": true }, - "@types/debug": { + "@opentelemetry/api": { "optional": true }, "@types/node": { "optional": true }, - "@vitest/browser": { + "@vitest/browser-playwright": { + "optional": true + }, + "@vitest/browser-preview": { + "optional": true + }, + "@vitest/browser-webdriverio": { + "optional": true + }, + "@vitest/coverage-istanbul": { + "optional": true + }, + "@vitest/coverage-v8": { "optional": true }, "@vitest/ui": { @@ -7685,6 +7116,9 @@ }, "jsdom": { "optional": true + }, + "vite": { + "optional": false } } }, @@ -7701,19 +7135,29 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/vitest/node_modules/tinyexec": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.2.4.tgz", + "integrity": "sha512-SHf/r48b7vOrjve9PxJo3MN5v5yuyjHvdUcrQffT3WXMUfnGmHDVbC4k3sHJaJTgZCwpUplIaAo5ANtMyp3YHg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/vlq": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/vlq/-/vlq-1.0.1.tgz", "integrity": "sha512-gQpnTgkubC6hQgdIcRdYGDSDc+SaujOdyesZQMv6JlfQee/9Mp0Qhnys6WxDWvQnL5WZdT7o2Ul187aSt0Rq+w==", - "license": "MIT", - "peer": true + "dev": true, + "license": "MIT" }, "node_modules/walker": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", + "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { "makeerror": "1.0.12" } @@ -7728,78 +7172,28 @@ "supports-color": "^10.0.0" } }, - "node_modules/web-streams-polyfill": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-4.3.0.tgz", - "integrity": "sha512-/Gnggvj9oSrEvJbDyyPtAnxBt5fGQM2iWOKQNu7ie1OxDgK40iZpyV3TKaRiEzVj1oA1UxKnEy9XPXh6PW3eVw==", - "dev": true, - "license": "MIT", - "workspaces": [ - "test/benchmark-test", - "test/rollup-test", - "test/webpack-test" - ], - "engines": { - "node": ">= 8" - } - }, - "node_modules/webidl-conversions": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-5.0.0.tgz", - "integrity": "sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=8" - } - }, - "node_modules/whatwg-fetch": { - "version": "3.6.20", - "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.20.tgz", - "integrity": "sha512-EqhiFU6daOA8kpjOWTL0olhVOF3i7OrFzSYiGsEMB8GcXS+RrzauAERX65xMeNWVqxA6HXH2m69Z9LaKKdisfg==", - "license": "MIT", - "peer": true - }, - "node_modules/whatwg-url-without-unicode": { - "version": "8.0.0-3", - "resolved": "https://registry.npmjs.org/whatwg-url-without-unicode/-/whatwg-url-without-unicode-8.0.0-3.tgz", - "integrity": "sha512-HoKuzZrUlgpz35YO27XgD28uh/WJH4B0+3ttFqRo//lmq+9T/mIOJ6kqmINI9HpUpz1imRC/nR/lxKpJiv0uig==", - "dev": true, - "license": "MIT", - "dependencies": { - "buffer": "^5.4.3", - "punycode": "^2.1.1", - "webidl-conversions": "^5.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/whatwg-url-without-unicode/node_modules/buffer": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], + "node_modules/web-streams-polyfill": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-4.3.0.tgz", + "integrity": "sha512-/Gnggvj9oSrEvJbDyyPtAnxBt5fGQM2iWOKQNu7ie1OxDgK40iZpyV3TKaRiEzVj1oA1UxKnEy9XPXh6PW3eVw==", + "dev": true, "license": "MIT", - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.1.13" + "workspaces": [ + "test/benchmark-test", + "test/rollup-test", + "test/webpack-test" + ], + "engines": { + "node": ">= 8" } }, + "node_modules/whatwg-fetch": { + "version": "3.6.20", + "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.20.tgz", + "integrity": "sha512-EqhiFU6daOA8kpjOWTL0olhVOF3i7OrFzSYiGsEMB8GcXS+RrzauAERX65xMeNWVqxA6HXH2m69Z9LaKKdisfg==", + "dev": true, + "license": "MIT" + }, "node_modules/wherearewe": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/wherearewe/-/wherearewe-2.0.1.tgz", @@ -7846,25 +7240,6 @@ } }, "node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "license": "MIT", - "peer": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrap-ansi-cjs": { - "name": "wrap-ansi", "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", @@ -7892,8 +7267,8 @@ "version": "7.5.11", "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.11.tgz", "integrity": "sha512-zS54Oen9bITtp7kp2XM3AydrCIq1D+HwJOuH+c+e4LfpL/lotP5osijd+UoMnxwAam1GN8R4KtLAyIrIcBNpiA==", + "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=8.3.0" }, @@ -7914,8 +7289,8 @@ "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, "license": "ISC", - "peer": true, "engines": { "node": ">=10" } @@ -7924,15 +7299,15 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "license": "ISC", - "peer": true + "dev": true, + "license": "ISC" }, "node_modules/yaml": { "version": "2.9.0", "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.9.0.tgz", "integrity": "sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==", + "dev": true, "license": "ISC", - "peer": true, "bin": { "yaml": "bin.mjs" }, @@ -7947,8 +7322,8 @@ "version": "17.7.2", "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "cliui": "^8.0.1", "escalade": "^3.1.1", @@ -7966,8 +7341,8 @@ "version": "21.1.1", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, "license": "ISC", - "peer": true, "engines": { "node": ">=12" } @@ -7998,12 +7373,22 @@ "@ipfs-meshkit/core": "*" }, "devDependencies": { - "@capacitor/core": "^8.0.0" + "@capacitor/core": "^8.4.2" }, "peerDependencies": { "@capacitor/core": ">=6.0.0" } }, + "packages/capacitor/node_modules/@capacitor/core": { + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/@capacitor/core/-/core-8.4.2.tgz", + "integrity": "sha512-fQPRb3JXRaU2pnufDUvqOjhsrYxDQeFRZyaj/sHydIUxD2NxOGHKMpmKUdI+U4OOmKuGZyvRpi7TSJU+7Bjbmg==", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.1.0" + } + }, "packages/core": { "name": "@ipfs-meshkit/core", "version": "1.0.0", @@ -8014,18 +7399,18 @@ }, "packages/mcp": { "name": "@ipfs-meshkit/mcp", - "version": "1.0.0", + "version": "1.0.1", "license": "MIT", "dependencies": { - "@ipfs-meshkit/meshkit": "1.0.2", - "@modelcontextprotocol/sdk": "^1.29.0", - "zod": "^3.25.76" + "@ipfs-meshkit/meshkit": "^1.2.0", + "@modelcontextprotocol/sdk": "^1.30.0", + "zod": "^4.4.3" }, "bin": { "meshkit-mcp": "dist/main.js" }, "devDependencies": { - "@types/node": "^25.9.3", + "@types/node": "^26.1.2", "tsup": "^8.5.0", "typescript": "^6.0.3" }, @@ -8034,26 +7419,37 @@ } }, "packages/mcp/node_modules/@ipfs-meshkit/meshkit": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@ipfs-meshkit/meshkit/-/meshkit-1.0.2.tgz", - "integrity": "sha512-vGZnn4aUWS6TDIIetgOCYNBhBAyNS7KqftL3ODZphoUVTzM53hShBuIVLz/aEpRYHl2gpd/KQ78u1/jOyjpfBA==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@ipfs-meshkit/meshkit/-/meshkit-1.2.0.tgz", + "integrity": "sha512-jCuBdQwOpMlmlMjTldu61sJS02D6M0OPKEclqmYlnePOToxApFx7kedYbn+9NrwAWIhDmHGyn5irlKqRMbyLIQ==", "license": "MIT", "workspaces": [ "packages/*", "examples/*" ], "dependencies": { - "kubo-rpc-client": "^7.1.0" + "aws4fetch": "^1.0.20", + "kubo-rpc-client": "^7.1.0", + "multiformats": "^14.0.0" }, "engines": { "node": ">=20" } }, + "packages/mcp/node_modules/zod": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.4.3.tgz", + "integrity": "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, "packages/meshkit": { "name": "@ipfs-meshkit/meshkit-workspace", "version": "1.0.0", "devDependencies": { - "@types/node": "^25.9.3" + "@types/node": "^26.1.2" } }, "packages/node": { @@ -8073,9 +7469,10 @@ }, "devDependencies": { "fast-text-encoding": "^1.0.6", + "react-native": "^0.86.2", "react-native-fetch-api": "^3.0.0", - "react-native-get-random-values": "^1.11.0", - "react-native-url-polyfill": "^2.0.0", + "react-native-get-random-values": "^2.0.0", + "react-native-url-polyfill": "^4.0.0", "web-streams-polyfill": "^4.2.0" }, "peerDependencies": { @@ -8087,6 +7484,259 @@ "web-streams-polyfill": ">=4.0.0" } }, + "packages/react-native/node_modules/@react-native/assets-registry": { + "version": "0.86.2", + "resolved": "https://registry.npmjs.org/@react-native/assets-registry/-/assets-registry-0.86.2.tgz", + "integrity": "sha512-vcX/mBjWAVnWofu7KecotquI2unZ/tITwA7OGdq/mdY/zmGXIEvYhfEYyOQij/LRqi9WAL+iizInTBWnxDhK/Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0" + } + }, + "packages/react-native/node_modules/@react-native/codegen": { + "version": "0.86.2", + "resolved": "https://registry.npmjs.org/@react-native/codegen/-/codegen-0.86.2.tgz", + "integrity": "sha512-xKkudsahUJ1n//55g4fXk5BStVqqmZlz8HQveL45ZxcfDnwvhuYe2GymksQANFsSN+slvrarjrfq8kIxJzbceA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.25.2", + "@babel/parser": "^7.29.0", + "hermes-parser": "0.36.0", + "invariant": "^2.2.4", + "nullthrows": "^1.1.1", + "tinyglobby": "^0.2.15", + "yargs": "^17.6.2" + }, + "engines": { + "node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0" + }, + "peerDependencies": { + "@babel/core": "*" + } + }, + "packages/react-native/node_modules/@react-native/community-cli-plugin": { + "version": "0.86.2", + "resolved": "https://registry.npmjs.org/@react-native/community-cli-plugin/-/community-cli-plugin-0.86.2.tgz", + "integrity": "sha512-YHXNKoM6Y/HjREySZ5arET2xgiHgg67r1MdwJB//MPJAJ0Xc5g0u6UHxY9VzsHO3Y07dre6s0BinYwjt1SEWvQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@react-native/dev-middleware": "0.86.2", + "debug": "^4.4.0", + "invariant": "^2.2.4", + "metro": "^0.84.3", + "metro-config": "^0.84.3", + "metro-core": "^0.84.3", + "semver": "^7.1.3" + }, + "engines": { + "node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0" + }, + "peerDependencies": { + "@react-native-community/cli": "*", + "@react-native/metro-config": "0.86.2" + }, + "peerDependenciesMeta": { + "@react-native-community/cli": { + "optional": true + }, + "@react-native/metro-config": { + "optional": true + } + } + }, + "packages/react-native/node_modules/@react-native/debugger-frontend": { + "version": "0.86.2", + "resolved": "https://registry.npmjs.org/@react-native/debugger-frontend/-/debugger-frontend-0.86.2.tgz", + "integrity": "sha512-KGS1aV5F6cIqpnoIUhLBXyVzy1oAj8jBFGau6vX4Vy0HXRJN7p+68RU7x6NuyraHvQcR14ccMGT5TkFuNjQ4gA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0" + } + }, + "packages/react-native/node_modules/@react-native/debugger-shell": { + "version": "0.86.2", + "resolved": "https://registry.npmjs.org/@react-native/debugger-shell/-/debugger-shell-0.86.2.tgz", + "integrity": "sha512-/TaVJ2+gGajZPJGrFaObUQmHmlaxAlfmOPZicl6pNKDUjzSgFMpcLkdTOExvb+USYTVdGX1XwxXyvjQdUO2bvg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.6", + "debug": "^4.4.0", + "fb-dotslash": "0.5.8" + }, + "engines": { + "node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0" + } + }, + "packages/react-native/node_modules/@react-native/dev-middleware": { + "version": "0.86.2", + "resolved": "https://registry.npmjs.org/@react-native/dev-middleware/-/dev-middleware-0.86.2.tgz", + "integrity": "sha512-B7L0vKvg+IcEElT7Vpqh1xj5yJAqWUegjbP+bQRaorJMAYnv11GkliTnZV2AdTDfZQJWgOEx8i8LGkHkUg7bnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@isaacs/ttlcache": "^1.4.1", + "@react-native/debugger-frontend": "0.86.2", + "@react-native/debugger-shell": "0.86.2", + "chrome-launcher": "^0.15.2", + "chromium-edge-launcher": "^0.3.0", + "connect": "^3.6.5", + "debug": "^4.4.0", + "invariant": "^2.2.4", + "nullthrows": "^1.1.1", + "open": "^7.0.3", + "serve-static": "^1.16.2", + "ws": "^7.5.10" + }, + "engines": { + "node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0" + } + }, + "packages/react-native/node_modules/@react-native/gradle-plugin": { + "version": "0.86.2", + "resolved": "https://registry.npmjs.org/@react-native/gradle-plugin/-/gradle-plugin-0.86.2.tgz", + "integrity": "sha512-2F6x14NcHMpVmfTTFKfMkpV5dZedZrLiv6PE+c3vgnesV2bjleUBydr4U+NI8VkI7OwW71L0A5qQ76I9LCrfoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0" + } + }, + "packages/react-native/node_modules/@react-native/js-polyfills": { + "version": "0.86.2", + "resolved": "https://registry.npmjs.org/@react-native/js-polyfills/-/js-polyfills-0.86.2.tgz", + "integrity": "sha512-bIwNcGBaQ74shB5z1mRkxOpjikimuwsnOCEkZSzL67Z1FTyK1ObpENfyd2QvcvVW9Cjl+tHuw9ynpBnb2jPoJQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0" + } + }, + "packages/react-native/node_modules/@react-native/normalize-colors": { + "version": "0.86.2", + "resolved": "https://registry.npmjs.org/@react-native/normalize-colors/-/normalize-colors-0.86.2.tgz", + "integrity": "sha512-EzPFc9Y6lzYOWeso2almwXI7f8+qReHxWvT+algsOczb2UhWXIWXDoSvkdwoSfiwwmGt/ijJgKJoeHlzPkLwRg==", + "dev": true, + "license": "MIT" + }, + "packages/react-native/node_modules/@react-native/virtualized-lists": { + "version": "0.86.2", + "resolved": "https://registry.npmjs.org/@react-native/virtualized-lists/-/virtualized-lists-0.86.2.tgz", + "integrity": "sha512-uO0J72gh3EvE+1/GHRk18QRyBDTRHRB0AraAfojsRjbT7VMuJwKrZYaKGshavoaEud6aw00ZB9/8mTMIKjjcAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "invariant": "^2.2.4", + "nullthrows": "^1.1.1" + }, + "engines": { + "node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0" + }, + "peerDependencies": { + "@types/react": "^19.2.0", + "react": "*", + "react-native": "0.86.2" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "packages/react-native/node_modules/hermes-compiler": { + "version": "250829098.0.16", + "resolved": "https://registry.npmjs.org/hermes-compiler/-/hermes-compiler-250829098.0.16.tgz", + "integrity": "sha512-xsgzk+mUyvt9t1nUbF8USBlYxajTUtPJhVZ86q85s/SEoMKCF+52YZcudb0ENSnV3T3lV9mgB3s6R7+pH90zgw==", + "dev": true, + "license": "MIT" + }, + "packages/react-native/node_modules/react-native": { + "version": "0.86.2", + "resolved": "https://registry.npmjs.org/react-native/-/react-native-0.86.2.tgz", + "integrity": "sha512-zbJXGZpwfZGA79Z9ob6Atvfx4nAQL8yJBa35s58E4Oo+khPykfQP2sTeumkKbjwajFYfVayg8pj7Il9nIfTk7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@react-native/assets-registry": "0.86.2", + "@react-native/codegen": "0.86.2", + "@react-native/community-cli-plugin": "0.86.2", + "@react-native/gradle-plugin": "0.86.2", + "@react-native/js-polyfills": "0.86.2", + "@react-native/normalize-colors": "0.86.2", + "@react-native/virtualized-lists": "0.86.2", + "abort-controller": "^3.0.0", + "anser": "^1.4.9", + "ansi-regex": "^5.0.0", + "babel-plugin-syntax-hermes-parser": "0.36.0", + "base64-js": "^1.5.1", + "commander": "^12.0.0", + "flow-enums-runtime": "^0.0.6", + "hermes-compiler": "250829098.0.16", + "invariant": "^2.2.4", + "memoize-one": "^5.0.0", + "metro-runtime": "^0.84.3", + "metro-source-map": "^0.84.3", + "nullthrows": "^1.1.1", + "pretty-format": "^29.7.0", + "promise": "^8.3.0", + "react-devtools-core": "^6.1.5", + "react-refresh": "^0.14.0", + "regenerator-runtime": "^0.13.2", + "scheduler": "0.27.0", + "semver": "^7.1.3", + "stacktrace-parser": "^0.1.10", + "tinyglobby": "^0.2.15", + "whatwg-fetch": "^3.0.0", + "ws": "^7.5.10", + "yargs": "^17.6.2" + }, + "bin": { + "react-native": "cli.js" + }, + "engines": { + "node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0" + }, + "peerDependencies": { + "@react-native/jest-preset": "0.86.2", + "@types/react": "^19.1.1", + "react": "^19.2.3" + }, + "peerDependenciesMeta": { + "@react-native/jest-preset": { + "optional": true + }, + "@types/react": { + "optional": true + } + } + }, + "packages/react-native/node_modules/react-native-get-random-values": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/react-native-get-random-values/-/react-native-get-random-values-2.0.0.tgz", + "integrity": "sha512-wx7/aPqsUIiWsG35D+MsUJd8ij96e3JKddklSdrdZUrheTx89gPtz3Q2yl9knBArj5u26Cl23T88ai+Q0vypdQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-base64-decode": "^1.0.0" + }, + "peerDependencies": { + "react-native": ">=0.81" + } + }, + "packages/react-native/node_modules/react-native-url-polyfill": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/react-native-url-polyfill/-/react-native-url-polyfill-4.0.0.tgz", + "integrity": "sha512-eqYM3wBAA0eL1sPYbBAoNfbES3+NkgcxUdelQ7QzmoVtqKB5qGG0U13MPTRUroAWK+y2EoJFS3MZUK0fwTf0pA==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "react-native": "*" + } + }, "tests/integration": { "name": "ipfs-meshkit/integration-tests", "version": "1.0.0", diff --git a/package.json b/package.json index 4b44b3e..aa6ea1a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@ipfs-meshkit/meshkit", - "version": "1.2.0", + "version": "1.2.1", "description": "Node.js SDK for Kubo/IPFS — upload, pin, IPNS, local daemon, and multi-node failover.", "type": "module", "main": "./dist/index.cjs", @@ -93,13 +93,16 @@ "@noble/hashes": "^2.2.0", "aws4fetch": "^1.0.20", "kubo-rpc-client": "^7.1.0", - "multiformats": "^14.0.0" + "multiformats": "^14.0.5" }, "devDependencies": { - "@types/node": "^25.9.3", - "@vitest/coverage-v8": "^3.2.4", + "@types/node": "^26.1.2", + "@vitest/coverage-v8": "^4.1.10", "tsup": "^8.5.0", "typescript": "^6.0.3", - "vitest": "^3.2.4" + "vitest": "^4.1.10" + }, + "overrides": { + "esbuild": ">=0.28.1" } } diff --git a/packages/capacitor/package.json b/packages/capacitor/package.json index 2700f1e..18156d4 100644 --- a/packages/capacitor/package.json +++ b/packages/capacitor/package.json @@ -25,7 +25,7 @@ "@capacitor/core": ">=6.0.0" }, "devDependencies": { - "@capacitor/core": "^8.0.0" + "@capacitor/core": "^8.4.2" }, "capacitor": { "ios": { diff --git a/packages/core/src/crypto.ts b/packages/core/src/crypto.ts index 782cd07..ce35ac6 100644 --- a/packages/core/src/crypto.ts +++ b/packages/core/src/crypto.ts @@ -60,6 +60,20 @@ const MIN_PAYLOAD_LEN = HEADER_LEN + GCM_TAG_LEN; // 53 /** Default PBKDF2 iteration count. 200k is the OWASP 2023 minimum for PBKDF2-SHA256. */ export const DEFAULT_ITERATIONS = 200_000; +/** + * Minimum iteration count accepted by encrypt(). + * Values below this are almost certainly a typo (e.g. `1` instead of `1_000`) + * and would produce dangerously weak key derivation. + */ +const MIN_ENCRYPT_ITERATIONS = 1_000; + +/** + * Maximum iteration count accepted by decrypt() when reading from the payload header. + * A crafted EMSH blob with iterations = 0xFFFFFFFF (~4.3 billion) would hang a server + * for hours; this ceiling rejects such payloads before the KDF is ever invoked. + */ +const MAX_DECRYPT_ITERATIONS = 10_000_000; + // --------------------------------------------------------------------------- // Public interface // --------------------------------------------------------------------------- @@ -74,7 +88,7 @@ export interface EncryptOptions { /** * PBKDF2 iteration count. Defaults to 200,000. * Higher values increase brute-force resistance at the cost of encrypt/decrypt time. - * Must be a positive integer ≤ 4,294,967,295 (uint32 max). + * Must be an integer ≥ 1,000 and ≤ 4,294,967,295 (uint32 max). */ iterations?: number; } @@ -124,11 +138,11 @@ async function deriveKey( function validateIterations(iterations: number): void { if ( !Number.isInteger(iterations) || - iterations < 1 || + iterations < MIN_ENCRYPT_ITERATIONS || iterations > 0xffffffff ) { throw new MeshkitError( - `iterations must be a positive integer ≤ 4,294,967,295, got: ${iterations}`, + `iterations must be an integer ≥ ${MIN_ENCRYPT_ITERATIONS} and ≤ 4,294,967,295, got: ${iterations}`, ); } } @@ -228,6 +242,15 @@ export async function decrypt( const view = new DataView(data.buffer, data.byteOffset, data.byteLength); const iterations = view.getUint32(OFF_ITERATIONS, false /* big-endian */); + // Reject payloads whose header declares an absurdly high iteration count. + // A crafted EMSH blob with iterations = 0xFFFFFFFF would hang the process + // for hours; this check fires before the KDF is ever invoked. + if (iterations > MAX_DECRYPT_ITERATIONS) { + throw new MeshkitError( + `Encrypted payload has an unsafe iteration count (${iterations}); max allowed for decryption is ${MAX_DECRYPT_ITERATIONS}`, + ); + } + // slice() creates owned copies — safe to hand to noble even if data is a view. const salt = data.slice(OFF_SALT, OFF_NONCE); const nonce = data.slice(OFF_NONCE, HEADER_LEN); diff --git a/packages/core/test/create-client.test.ts b/packages/core/test/create-client.test.ts index bcd92e0..c847bdf 100644 --- a/packages/core/test/create-client.test.ts +++ b/packages/core/test/create-client.test.ts @@ -66,7 +66,7 @@ describe('createMeshkitClient', () => { const client = createMeshkitClient({ apiUrl: 'http://127.0.0.1:5001' }); const cid = await client.upload(PLAINTEXT, { - encrypt: { password: PASSWORD, iterations: 1 }, + encrypt: { password: PASSWORD, iterations: 1_000 }, }); expect(cid).toBe('QmEncrypted'); @@ -82,7 +82,7 @@ describe('createMeshkitClient', () => { it('two encrypted uploads of the same plaintext produce different blobs', async () => { ipfs.add.mockResolvedValue({ cid: { toString: () => 'QmAny' } }); const client = createMeshkitClient({ apiUrl: 'http://127.0.0.1:5001' }); - const opts = { encrypt: { password: PASSWORD, iterations: 1 } }; + const opts = { encrypt: { password: PASSWORD, iterations: 1_000 } }; await client.upload(PLAINTEXT, opts); await client.upload(PLAINTEXT, opts); @@ -113,7 +113,7 @@ describe('createMeshkitClient', () => { it('retrieve without password returns raw encrypted bytes when content is encrypted', async () => { // Simulate: upload encrypted, then retrieve without password const { encrypt } = await import('../src/crypto.js'); - const encrypted = await encrypt(PLAINTEXT, { password: PASSWORD, iterations: 1 }); + const encrypted = await encrypt(PLAINTEXT, { password: PASSWORD, iterations: 1_000 }); async function* chunks() { yield encrypted; } ipfs.cat.mockReturnValue(chunks()); @@ -131,7 +131,7 @@ describe('createMeshkitClient', () => { it('retrieve with correct password decrypts transparently', async () => { const { encrypt } = await import('../src/crypto.js'); - const encrypted = await encrypt(PLAINTEXT, { password: PASSWORD, iterations: 1 }); + const encrypted = await encrypt(PLAINTEXT, { password: PASSWORD, iterations: 1_000 }); async function* chunks() { yield encrypted; } ipfs.cat.mockReturnValue(chunks()); @@ -144,7 +144,7 @@ describe('createMeshkitClient', () => { it('retrieve with wrong password throws MeshkitError', async () => { const { encrypt } = await import('../src/crypto.js'); - const encrypted = await encrypt(PLAINTEXT, { password: PASSWORD, iterations: 1 }); + const encrypted = await encrypt(PLAINTEXT, { password: PASSWORD, iterations: 1_000 }); async function* chunks() { yield encrypted; } ipfs.cat.mockReturnValue(chunks()); diff --git a/packages/core/test/create-filone-client.test.ts b/packages/core/test/create-filone-client.test.ts index 9dce8be..82c7857 100644 --- a/packages/core/test/create-filone-client.test.ts +++ b/packages/core/test/create-filone-client.test.ts @@ -89,7 +89,7 @@ describe('createS3Client / createFilOneClient', () => { const data = new TextEncoder().encode('secret invoice'); const client = createFilOneClient(CONFIG); - await client.upload(data, { encrypt: { password: 'pass', iterations: 1 } }); + await client.upload(data, { encrypt: { password: 'pass', iterations: 1_000 } }); // The body sent to S3 must be an encrypted EMSH payload const req = fetchMock.mock.calls[0][0] as Request; @@ -102,7 +102,7 @@ describe('createS3Client / createFilOneClient', () => { vi.stubGlobal('fetch', vi.fn().mockResolvedValue(makeResponse(200))); const client = createFilOneClient(CONFIG); const data = new TextEncoder().encode('same secret'); - const opts = { encrypt: { password: 'pass', iterations: 1 } }; + const opts = { encrypt: { password: 'pass', iterations: 1_000 } }; const cid1 = await client.upload(data, opts); const cid2 = await client.upload(data, opts); // Different random salts → different ciphertexts → different CIDs @@ -147,7 +147,7 @@ describe('createS3Client / createFilOneClient', () => { it('retrieve with correct password decrypts encrypted S3 content', async () => { const { encrypt } = await import('../src/crypto.js'); const plaintext = new TextEncoder().encode('secret s3 payload'); - const encrypted = await encrypt(plaintext, { password: 'pass', iterations: 1 }); + const encrypted = await encrypt(plaintext, { password: 'pass', iterations: 1_000 }); vi.stubGlobal('fetch', vi.fn().mockResolvedValue(new Response(encrypted.buffer))); const client = createFilOneClient(CONFIG); @@ -158,7 +158,7 @@ describe('createS3Client / createFilOneClient', () => { it('retrieve without password returns raw encrypted bytes', async () => { const { encrypt } = await import('../src/crypto.js'); const plaintext = new TextEncoder().encode('secret s3 payload'); - const encrypted = await encrypt(plaintext, { password: 'pass', iterations: 1 }); + const encrypted = await encrypt(plaintext, { password: 'pass', iterations: 1_000 }); vi.stubGlobal('fetch', vi.fn().mockResolvedValue(new Response(encrypted.buffer))); const client = createFilOneClient(CONFIG); @@ -171,7 +171,7 @@ describe('createS3Client / createFilOneClient', () => { const { encrypt } = await import('../src/crypto.js'); const encrypted = await encrypt( new TextEncoder().encode('secret'), - { password: 'correct', iterations: 1 }, + { password: 'correct', iterations: 1_000 }, ); vi.stubGlobal('fetch', vi.fn().mockResolvedValue(new Response(encrypted.buffer))); diff --git a/packages/core/test/crypto.test.ts b/packages/core/test/crypto.test.ts index 3d10ad8..40fc77b 100644 --- a/packages/core/test/crypto.test.ts +++ b/packages/core/test/crypto.test.ts @@ -15,9 +15,9 @@ const PASSWORD = 'correct-horse-battery-staple'; const PLAINTEXT = new TextEncoder().encode('Hello, IPFS-Meshkit encryption!'); const EMPTY = new Uint8Array(0); -/** Encrypt with default options and low iterations so tests run fast. */ +/** Encrypt with low-but-valid iterations so tests run fast without hitting the floor. */ function fastEncrypt(data: Uint8Array, password = PASSWORD) { - return encrypt(data, { password, iterations: 1 }); + return encrypt(data, { password, iterations: 1_000 }); } // --------------------------------------------------------------------------- @@ -134,6 +134,12 @@ describe('encrypt', () => { await expect(encrypt(PLAINTEXT, { password: '' })).rejects.toBeInstanceOf(MeshkitError); }); + it('throws MeshkitError for iterations below minimum (< 1,000)', async () => { + await expect( + encrypt(PLAINTEXT, { password: PASSWORD, iterations: 1 }), + ).rejects.toBeInstanceOf(MeshkitError); + }); + it('throws MeshkitError for iterations = 0', async () => { await expect( encrypt(PLAINTEXT, { password: PASSWORD, iterations: 0 }), @@ -273,7 +279,17 @@ describe('decrypt', () => { }); it('decrypting p1 with password of p2 fails gracefully', async () => { - const p1 = await encrypt(PLAINTEXT, { password: 'password-one', iterations: 1 }); + const p1 = await encrypt(PLAINTEXT, { password: 'password-one', iterations: 1_000 }); await expect(decrypt(p1, 'password-two')).rejects.toBeInstanceOf(MeshkitError); }); + + it('throws MeshkitError when header iteration count exceeds the safe maximum (DoS guard)', async () => { + // Craft a valid-looking EMSH payload but with iterations = 0xFFFFFFFF in the header. + // decrypt() must reject it before ever invoking the KDF. + const payload = await fastEncrypt(PLAINTEXT); + const crafted = new Uint8Array(payload); + const view = new DataView(crafted.buffer); + view.setUint32(5, 0xffffffff, false /* big-endian, iterations field at offset 5 */); + await expect(decrypt(crafted, PASSWORD)).rejects.toBeInstanceOf(MeshkitError); + }); }); diff --git a/packages/mcp/README.md b/packages/mcp/README.md index b652dc2..2e55818 100644 --- a/packages/mcp/README.md +++ b/packages/mcp/README.md @@ -5,11 +5,9 @@ MCP (Model Context Protocol) server for [IPFS Meshkit](https://github.com/IPFS-M ## Prerequisites - **Node.js 20+** -- **`@ipfs-meshkit/meshkit` ≥ 1.0.2** (installed automatically as a dependency) +- **`@ipfs-meshkit/meshkit` ≥ 1.2.1** (installed automatically as a dependency) - A running [Kubo](https://docs.ipfs.tech/install/) node with RPC API reachable (default: `http://127.0.0.1:5001`), **or** set `MESHKIT_LOCAL_NODE=true` to start/attach to Kubo automatically (requires `ipfs` on `PATH`) -> **Publish order:** release `@ipfs-meshkit/meshkit@1.0.2` before `@ipfs-meshkit/mcp@1.0.0`. MCP uses `meshkit.listPins()`, added in meshkit 1.0.2. - ## Quick start ```bash @@ -92,8 +90,8 @@ Add to `claude_desktop_config.json`: | Tool | Description | |------|-------------| -| `ipfs_upload` | Upload text or base64 content; returns CID | -| `ipfs_retrieve` | Retrieve content by CID | +| `ipfs_upload` | Upload text or base64 content; returns CID. Supports optional `password` and `pbkdf2Iterations` for client-side AES-256-GCM encryption before upload | +| `ipfs_retrieve` | Retrieve content by CID. Pass `password` to decrypt if the content was uploaded encrypted | | `ipfs_pin` | Pin a CID on the node | | `ipfs_list_pins` | List all pinned CIDs on the primary node | | `ipfs_publish_name` | Publish an IPNS record | @@ -109,18 +107,39 @@ Add to `claude_desktop_config.json`: { "content": "Hello, IPFS!" } ``` +**Upload text with encryption:** + +```json +{ + "content": "confidential manifest data", + "password": "correct-horse-battery-staple", + "pbkdf2Iterations": 200000 +} +``` + +The content is encrypted with AES-256-GCM (PBKDF2-SHA256 key derivation) before it leaves the local machine. The IPFS network and storage provider only ever see the ciphertext. `pbkdf2Iterations` defaults to 200,000 (OWASP 2023 minimum) and must be ≥ 1,000. + **Upload binary:** ```json { "base64": "aGVsbG8=" } ``` -**Retrieve:** +**Retrieve (no encryption):** ```json { "cid": "Qm..." } ``` +**Retrieve and decrypt:** + +```json +{ + "cid": "Qm...", + "password": "correct-horse-battery-staple" +} +``` + **Publish to IPNS:** ```json diff --git a/packages/mcp/package.json b/packages/mcp/package.json index f4c49eb..ae17615 100644 --- a/packages/mcp/package.json +++ b/packages/mcp/package.json @@ -1,6 +1,6 @@ { "name": "@ipfs-meshkit/mcp", - "version": "1.0.0", + "version": "1.0.1", "description": "MCP server for IPFS Meshkit — upload, pin, retrieve, and IPNS tools for AI agents.", "type": "module", "bin": { @@ -42,12 +42,12 @@ "access": "public" }, "dependencies": { - "@ipfs-meshkit/meshkit": "1.0.2", - "@modelcontextprotocol/sdk": "^1.29.0", - "zod": "^3.25.76" + "@ipfs-meshkit/meshkit": "^1.2.0", + "@modelcontextprotocol/sdk": "^1.30.0", + "zod": "^4.4.3" }, "devDependencies": { - "@types/node": "^25.9.3", + "@types/node": "^26.1.2", "tsup": "^8.5.0", "typescript": "^6.0.3" } diff --git a/packages/meshkit/package.json b/packages/meshkit/package.json index 943cd8b..80a7189 100644 --- a/packages/meshkit/package.json +++ b/packages/meshkit/package.json @@ -8,6 +8,6 @@ "clean": "rm -rf dist" }, "devDependencies": { - "@types/node": "^25.9.3" + "@types/node": "^26.1.2" } } diff --git a/packages/react-native/package.json b/packages/react-native/package.json index 41d1bfa..39cac5b 100644 --- a/packages/react-native/package.json +++ b/packages/react-native/package.json @@ -35,9 +35,10 @@ }, "devDependencies": { "fast-text-encoding": "^1.0.6", + "react-native": "^0.86.2", "react-native-fetch-api": "^3.0.0", - "react-native-get-random-values": "^1.11.0", - "react-native-url-polyfill": "^2.0.0", + "react-native-get-random-values": "^2.0.0", + "react-native-url-polyfill": "^4.0.0", "web-streams-polyfill": "^4.2.0" } } From e730c3e1cc8b399e552c7a452c4cd452d8028251 Mon Sep 17 00:00:00 2001 From: Anurag Date: Tue, 28 Jul 2026 16:08:07 +0530 Subject: [PATCH 12/12] docs: replace encryption table with flow diagrams and wire format offset table --- README.md | 70 +++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 58 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index fb0ddc0..42e0311 100644 --- a/README.md +++ b/README.md @@ -141,22 +141,68 @@ const cid = await client.upload(data, { const plaintext = await client.retrieve(cid, { password: PASSWORD }); ``` -### Encryption details +### How encryption works + +**Upload path** + +``` +password + plaintext + │ + ├── CSPRNG ──────────────────────► 128-bit salt (fresh every call) + │ 96-bit nonce (fresh every call) + │ + ├── PBKDF2-SHA256(password, salt, iterations) + │ └──────────────────────► 256-bit AES key + │ + └── AES-256-GCM(key, nonce, plaintext) + └──────────────────────► ciphertext + 128-bit auth tag + │ + packed as EMSH blob + │ + upload to IPFS / S3 ──► CID +``` + +**Retrieve path** + +``` +CID ──► fetch raw bytes from IPFS / S3 + │ + ├── isEncryptedPayload()? ──► no ──► return as-is + │ + └── yes: read iterations, salt, nonce from EMSH header + │ + ├── PBKDF2-SHA256(password, salt, iterations) + │ └──────────────────────► 256-bit AES key + │ + └── AES-256-GCM decrypt + verify auth tag + │ + ├── tag valid ──► plaintext + └── tag invalid ──► MeshkitError (wrong password or tampered) +``` + +**EMSH wire format** — every encrypted payload starts with a 37-byte self-describing header: + +| Offset | Size | Field | Value | +|--------|------|-------|-------| +| 0 | 4 B | Magic | `EMSH` (`0x45 0x4D 0x53 0x48`) — identifies encrypted content | +| 4 | 1 B | Version | `0x01` | +| 5 | 4 B | Iterations | PBKDF2 iteration count, uint32 big-endian | +| 9 | 16 B | Salt | Random, unique per `encrypt()` call | +| 25 | 12 B | Nonce | Random, unique per `encrypt()` call | +| 37 | n + 16 B | Ciphertext + Tag | AES-256-GCM output; last 16 bytes are the authentication tag | + +Because salt and nonce are random per call, uploading the same plaintext twice produces two different CIDs — content is not linkable across uploads. + +**Algorithm properties** | Property | Value | |---|---| -| Cipher | AES-256-GCM (authenticated encryption — detects tampering) | +| Cipher | AES-256-GCM — authenticated; any bit-flip in the ciphertext or header is detected | | Key derivation | PBKDF2-SHA256 | -| Default iterations | 200,000 (OWASP 2023 minimum) | -| Salt | 128-bit random, generated fresh per `upload()` call | -| Nonce | 96-bit random, generated fresh per `upload()` call | -| Wire format | `EMSH` magic + version + iteration count + salt + nonce + ciphertext + 128-bit GCM tag | - -Because salt and nonce are random per call, uploading the same plaintext twice produces two different CIDs — safe for content that should not be linkable across uploads. - -**Iteration count guards:** -- `iterations` must be ≥ 1,000 on `encrypt()` — values below this are almost certainly a typo and produce dangerously weak key derivation. -- `decrypt()` rejects any payload whose header declares more than 10,000,000 iterations — this blocks crafted payloads designed to hang a server by triggering a multi-hour KDF run. +| Default iterations | 200,000 (OWASP 2023 minimum for PBKDF2-SHA256) | +| Iteration floor | 1,000 — `encrypt()` rejects lower values (typo guard) | +| Iteration ceiling | 10,000,000 — `decrypt()` rejects higher values in the header (DoS guard) | +| Dependencies | `@noble/ciphers` + `@noble/hashes` — [Cure53-audited](https://cure53.de/pentest-report_noble-crypto.pdf) | ```typescript // Custom iteration count (higher = more brute-force resistant, slower)