diff --git a/examples/stellar-svelte-receive/README.md b/examples/stellar-svelte-receive/README.md new file mode 100644 index 0000000..27ed2dc --- /dev/null +++ b/examples/stellar-svelte-receive/README.md @@ -0,0 +1,12 @@ +# Stellar Svelte receive + +A minimal Svelte 5 app that uses `@wraith-protocol/sdk-svelte` to derive a Stellar +stealth meta-address and scan recent on-chain announcements. + +```bash +pnpm --filter @wraith-protocol/sdk-svelte build +pnpm --filter @wraith-protocol/example-stellar-svelte-receive dev +``` + +Set `VITE_STELLAR_SECRET_KEY` to prefill the 64-byte hexadecimal secret, or paste +one in the app. Scanning uses the Stellar deployment configured by the core SDK. diff --git a/examples/stellar-svelte-receive/index.html b/examples/stellar-svelte-receive/index.html new file mode 100644 index 0000000..49a19a9 --- /dev/null +++ b/examples/stellar-svelte-receive/index.html @@ -0,0 +1,13 @@ + + + + + + + Wraith Stellar Receive + + +
+ + + diff --git a/examples/stellar-svelte-receive/package.json b/examples/stellar-svelte-receive/package.json new file mode 100644 index 0000000..ba06103 --- /dev/null +++ b/examples/stellar-svelte-receive/package.json @@ -0,0 +1,21 @@ +{ + "name": "@wraith-protocol/example-stellar-svelte-receive", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "vite build", + "preview": "vite preview" + }, + "dependencies": { + "@stellar/stellar-sdk": "^13.1.0", + "@wraith-protocol/sdk-svelte": "workspace:*", + "svelte": "^5.0.0" + }, + "devDependencies": { + "@sveltejs/vite-plugin-svelte": "^5.0.0", + "typescript": "^5.7.0", + "vite": "^6.0.0" + } +} diff --git a/examples/stellar-svelte-receive/src/App.svelte b/examples/stellar-svelte-receive/src/App.svelte new file mode 100644 index 0000000..fa13150 --- /dev/null +++ b/examples/stellar-svelte-receive/src/App.svelte @@ -0,0 +1,293 @@ + + + + Wraith Stellar Receive + + +
+

Wraith Protocol · Svelte

+

Receive Stellar stealth payments

+

+ Derive a shareable stealth meta-address, then scan Stellar for payments announced to it. +

+ +
+

1. Derive your receive address

+ + + + + {#if validationError || $keyError} + + {/if} + + {#if $keys && $metaAddress} +
+ Stealth meta-address + {$metaAddress} +
+ View public keys +
+
Spending
+
{hex($keys.spendingPubKey)}
+
Viewing
+
{hex($keys.viewingPubKey)}
+
+
+
+ {/if} +
+ +
+
+
+

2. Scan announcements

+

Read recent announcements from the configured Stellar deployment.

+
+ +
+ + {#if $scanError} + + {:else if $announcements.length} + + {:else} +

No announcements loaded yet.

+ {/if} +
+
+ + diff --git a/examples/stellar-svelte-receive/src/main.ts b/examples/stellar-svelte-receive/src/main.ts new file mode 100644 index 0000000..f562e21 --- /dev/null +++ b/examples/stellar-svelte-receive/src/main.ts @@ -0,0 +1,6 @@ +import { mount } from 'svelte'; +import App from './App.svelte'; + +mount(App, { + target: document.getElementById('app')!, +}); diff --git a/examples/stellar-svelte-receive/src/vite-env.d.ts b/examples/stellar-svelte-receive/src/vite-env.d.ts new file mode 100644 index 0000000..11f02fe --- /dev/null +++ b/examples/stellar-svelte-receive/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/examples/stellar-svelte-receive/svelte.config.js b/examples/stellar-svelte-receive/svelte.config.js new file mode 100644 index 0000000..4c6b24b --- /dev/null +++ b/examples/stellar-svelte-receive/svelte.config.js @@ -0,0 +1,5 @@ +import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'; + +export default { + preprocess: vitePreprocess(), +}; diff --git a/examples/stellar-svelte-receive/tsconfig.json b/examples/stellar-svelte-receive/tsconfig.json new file mode 100644 index 0000000..34d4cd7 --- /dev/null +++ b/examples/stellar-svelte-receive/tsconfig.json @@ -0,0 +1,13 @@ +{ + "compilerOptions": { + "target": "ES2022", + "lib": ["ES2022", "DOM", "DOM.Iterable"], + "module": "ESNext", + "moduleResolution": "bundler", + "strict": true, + "noEmit": true, + "skipLibCheck": true, + "isolatedModules": true + }, + "include": ["src/**/*.ts", "src/**/*.svelte"] +} diff --git a/examples/stellar-svelte-receive/vite.config.ts b/examples/stellar-svelte-receive/vite.config.ts new file mode 100644 index 0000000..0cf4613 --- /dev/null +++ b/examples/stellar-svelte-receive/vite.config.ts @@ -0,0 +1,6 @@ +import { defineConfig } from 'vite'; +import { svelte } from '@sveltejs/vite-plugin-svelte'; + +export default defineConfig({ + plugins: [svelte()], +}); diff --git a/packages/sdk-svelte/package.json b/packages/sdk-svelte/package.json index 8fa4365..f713fbf 100644 --- a/packages/sdk-svelte/package.json +++ b/packages/sdk-svelte/package.json @@ -25,9 +25,13 @@ "@wraith-protocol/sdk": "workspace:*" }, "peerDependencies": { + "@stellar/stellar-sdk": "^13.1.0", "svelte": "^5.0.0" }, "devDependencies": { + "@stellar/stellar-sdk": "^13.1.0", + "@testing-library/svelte": "^5.2.0", + "jsdom": "^25.0.0", "svelte": "^5.0.0", "tsup": "^8.4.0", "typescript": "^5.7.0", diff --git a/packages/sdk-svelte/src/index.ts b/packages/sdk-svelte/src/index.ts index 0541b81..f981928 100644 --- a/packages/sdk-svelte/src/index.ts +++ b/packages/sdk-svelte/src/index.ts @@ -1,5 +1,9 @@ export { useWraith } from './primitives/useWraith.js'; export { useStellarStealthKeys } from './primitives/useStellarStealthKeys.js'; +export { useStellarAnnouncementScan } from './primitives/useStellarAnnouncementScan.js'; +export { useStellarSendStealthPayment } from './primitives/useStellarSendStealthPayment.js'; +export { useStellarBalance } from './primitives/useStellarBalance.js'; +export { useStellarName } from './primitives/useStellarName.js'; export { useEvmStealthKeys } from './primitives/useEvmStealthKeys.js'; export { useSolanaStealthKeys } from './primitives/useSolanaStealthKeys.js'; export { useStealthMetaAddress } from './primitives/useStealthMetaAddress.js'; diff --git a/packages/sdk-svelte/src/primitives/useStellarAnnouncementScan.ts b/packages/sdk-svelte/src/primitives/useStellarAnnouncementScan.ts new file mode 100644 index 0000000..629c7bb --- /dev/null +++ b/packages/sdk-svelte/src/primitives/useStellarAnnouncementScan.ts @@ -0,0 +1,40 @@ +import { readonly, writable } from 'svelte/store'; +import { + fetchAnnouncementsStream, + type Announcement, + type FetchAnnouncementsOptions, +} from '@wraith-protocol/sdk/chains/stellar'; + +/** Store primitive for scanning Stellar stealth announcements. */ +export function useStellarAnnouncementScan() { + const _announcements = writable([]); + const _scanning = writable(false); + const _error = writable(null); + + async function scan(options: FetchAnnouncementsOptions): Promise { + _scanning.set(true); + _error.set(null); + + try { + const announcements: Announcement[] = []; + for await (const announcement of fetchAnnouncementsStream('stellar', options)) { + announcements.push(announcement); + } + _announcements.set(announcements); + return announcements; + } catch (cause) { + const error = cause instanceof Error ? cause : new Error(String(cause)); + _error.set(error); + throw cause; + } finally { + _scanning.set(false); + } + } + + return { + announcements: readonly(_announcements), + scanning: readonly(_scanning), + error: readonly(_error), + scan, + }; +} diff --git a/packages/sdk-svelte/src/primitives/useStellarBalance.ts b/packages/sdk-svelte/src/primitives/useStellarBalance.ts new file mode 100644 index 0000000..f89eff3 --- /dev/null +++ b/packages/sdk-svelte/src/primitives/useStellarBalance.ts @@ -0,0 +1,30 @@ +import { Horizon } from '@stellar/stellar-sdk'; +import { readonly, writable } from 'svelte/store'; + +/** Store primitive for loading an account's native Stellar balance. */ +export function useStellarBalance(publicKey: string, rpcUrl = 'https://horizon.stellar.org') { + const _balance = writable(null); + const _loading = writable(false); + + async function load() { + if (!publicKey) return; + + _loading.set(true); + try { + const account = await new Horizon.Server(rpcUrl).loadAccount(publicKey); + const nativeBalance = account.balances.find((entry) => entry.asset_type === 'native'); + _balance.set(nativeBalance ? nativeBalance.balance : '0'); + } catch { + _balance.set('0'); + } finally { + _loading.set(false); + } + } + + void load(); + + return { + balance: readonly(_balance), + loading: readonly(_loading), + }; +} diff --git a/packages/sdk-svelte/src/primitives/useStellarName.ts b/packages/sdk-svelte/src/primitives/useStellarName.ts new file mode 100644 index 0000000..b2321d2 --- /dev/null +++ b/packages/sdk-svelte/src/primitives/useStellarName.ts @@ -0,0 +1,34 @@ +import { getDeployment } from '@wraith-protocol/sdk/chains/stellar'; +import { readonly, writable } from 'svelte/store'; + +/** Store primitive for resolving a Wraith name on Stellar. */ +export function useStellarName(name: string | undefined, chain = 'testnet') { + const _address = writable(null); + const _loading = writable(false); + const _error = writable(null); + + async function resolve() { + if (!name) return; + + _loading.set(true); + _error.set(null); + try { + getDeployment(chain); + // Name resolution remains a placeholder until the core SDK exposes it. + await new Promise((resolveDelay) => setTimeout(resolveDelay, 500)); + _address.set(null); + } catch (cause) { + _error.set(cause instanceof Error ? cause : new Error(String(cause))); + } finally { + _loading.set(false); + } + } + + void resolve(); + + return { + address: readonly(_address), + loading: readonly(_loading), + error: readonly(_error), + }; +} diff --git a/packages/sdk-svelte/src/primitives/useStellarSendStealthPayment.ts b/packages/sdk-svelte/src/primitives/useStellarSendStealthPayment.ts new file mode 100644 index 0000000..dc5cdab --- /dev/null +++ b/packages/sdk-svelte/src/primitives/useStellarSendStealthPayment.ts @@ -0,0 +1,32 @@ +import { readonly, writable } from 'svelte/store'; +import { + buildStealthPayment, + type BuildStealthPaymentOptions, +} from '@wraith-protocol/sdk/chains/stellar'; + +/** Store primitive for building Stellar stealth payment transactions. */ +export function useStellarSendStealthPayment() { + const _building = writable(false); + const _error = writable(null); + + async function build(options: BuildStealthPaymentOptions) { + _building.set(true); + _error.set(null); + + try { + return await buildStealthPayment(options); + } catch (cause) { + const error = cause instanceof Error ? cause : new Error(String(cause)); + _error.set(error); + throw cause; + } finally { + _building.set(false); + } + } + + return { + building: readonly(_building), + error: readonly(_error), + build, + }; +} diff --git a/packages/sdk-svelte/src/primitives/useStellarStealthKeys.ts b/packages/sdk-svelte/src/primitives/useStellarStealthKeys.ts index c8340db..366bcb1 100644 --- a/packages/sdk-svelte/src/primitives/useStellarStealthKeys.ts +++ b/packages/sdk-svelte/src/primitives/useStellarStealthKeys.ts @@ -7,7 +7,7 @@ import { deriveStealthPrivateScalar, encodeStealthMetaAddress, decodeStealthMetaAddress, - fetchAnnouncements, + fetchAnnouncementsStream, } from '@wraith-protocol/sdk/chains/stellar'; import type { StealthKeys, @@ -41,6 +41,9 @@ export function useStellarStealthKeys() { } } + // React parity: `useStellarStealthKeys` exposes this operation as `generate`. + const generate = deriveKeys; + function generateAddress( spendingPubKey: Uint8Array, viewingPubKey: Uint8Array, @@ -126,7 +129,13 @@ export function useStellarStealthKeys() { _loading.set(true); _error.set(null); try { - const list = await fetchAnnouncements(chain, sorobanUrl); + const list: Announcement[] = []; + for await (const announcement of fetchAnnouncementsStream( + chain ?? 'stellar', + sorobanUrl, + )) { + list.push(announcement); + } _announcements.set(list); return list; } catch (e) { @@ -165,6 +174,7 @@ export function useStellarStealthKeys() { metaAddress: readonly(_metaAddress), loading: readonly(_loading), error: readonly(_error), + generate, deriveKeys, generateAddress, checkAddress, diff --git a/packages/sdk-svelte/test/StoreHarness.svelte b/packages/sdk-svelte/test/StoreHarness.svelte new file mode 100644 index 0000000..ae41cc1 --- /dev/null +++ b/packages/sdk-svelte/test/StoreHarness.svelte @@ -0,0 +1,20 @@ + + +{#if run} + +{/if} +{serialize($value)} diff --git a/packages/sdk-svelte/test/stellarParity.test.ts b/packages/sdk-svelte/test/stellarParity.test.ts new file mode 100644 index 0000000..9e7ba7d --- /dev/null +++ b/packages/sdk-svelte/test/stellarParity.test.ts @@ -0,0 +1,116 @@ +import { fireEvent, render, screen, waitFor } from '@testing-library/svelte'; +import { get } from 'svelte/store'; +import { beforeEach, describe, expect, it, vi } from 'vitest'; +import StoreHarness from './StoreHarness.svelte'; +import { + useStellarAnnouncementScan, + useStellarBalance, + useStellarName, + useStellarSendStealthPayment, + useStellarStealthKeys, +} from '../src/index.js'; +import { buildStealthPayment, fetchAnnouncementsStream } from '@wraith-protocol/sdk/chains/stellar'; + +vi.mock('@wraith-protocol/sdk/chains/stellar', () => ({ + META_ADDRESS_PREFIX: 'st:xlm:', + deriveStealthKeys: vi.fn(() => ({ marker: 'generated' })), + generateStealthAddress: vi.fn(), + checkStealthAddress: vi.fn(), + scanAnnouncements: vi.fn(), + deriveStealthPrivateScalar: vi.fn(), + encodeStealthMetaAddress: vi.fn(), + decodeStealthMetaAddress: vi.fn(), + fetchAnnouncements: vi.fn(), + fetchAnnouncementsStream: vi.fn(), + buildStealthPayment: vi.fn(), + getDeployment: vi.fn(() => ({ + horizonUrl: 'https://horizon.test', + sorobanUrl: 'https://soroban.test', + contracts: { announcer: 'announcer', names: 'names' }, + })), +})); + +vi.mock('@stellar/stellar-sdk', () => ({ + Horizon: { + Server: vi.fn().mockImplementation(() => ({ + loadAccount: vi.fn().mockResolvedValue({ + balances: [{ asset_type: 'native', balance: '10.5' }], + }), + })), + }, +})); + +describe('Stellar store parity', () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + + it('matches the React key primitive and notifies the rendered subscriber', async () => { + const state = useStellarStealthKeys(); + expect(state.keys).toBeDefined(); + expect(state.generate).toBeTypeOf('function'); + + render(StoreHarness, { + props: { + value: state.keys, + run: () => state.generate(new Uint8Array([1, 2, 3])), + }, + }); + + expect(screen.getByTestId('value').textContent).toContain('null'); + await fireEvent.click(screen.getByRole('button', { name: 'Run' })); + expect(screen.getByTestId('value').textContent).toContain('generated'); + }); + + it('scans announcements and updates scanning and announcement stores', async () => { + vi.mocked(fetchAnnouncementsStream).mockImplementation(async function* () { + await Promise.resolve(); + yield { stealthAddress: 'GSTEALTH' } as never; + }); + const state = useStellarAnnouncementScan(); + + render(StoreHarness, { + props: { + value: state.announcements, + run: () => state.scan({ fromLedger: 10 }), + }, + }); + + await fireEvent.click(screen.getByRole('button', { name: 'Run' })); + await waitFor(() => expect(screen.getByTestId('value').textContent).toContain('GSTEALTH')); + expect(get(state.scanning)).toBe(false); + expect(get(state.error)).toBeNull(); + }); + + it('exposes building state while a payment is being built', async () => { + let finish!: (value: unknown) => void; + vi.mocked(buildStealthPayment).mockReturnValue( + new Promise((resolve) => { + finish = resolve; + }) as never, + ); + const state = useStellarSendStealthPayment(); + + const pending = state.build({} as never); + expect(get(state.building)).toBe(true); + finish({ transaction: 'built' }); + await expect(pending).resolves.toEqual({ transaction: 'built' }); + expect(get(state.building)).toBe(false); + }); + + it('loads the native Stellar balance reactively', async () => { + const state = useStellarBalance('GACCOUNT', 'https://horizon.test'); + render(StoreHarness, { props: { value: state.balance } }); + + await waitFor(() => expect(screen.getByTestId('value').textContent).toContain('10.5')); + expect(get(state.loading)).toBe(false); + }); + + it('matches the React name primitive shape', () => { + const state = useStellarName(undefined); + + expect(get(state.address)).toBeNull(); + expect(get(state.loading)).toBe(false); + expect(get(state.error)).toBeNull(); + }); +}); diff --git a/packages/sdk-svelte/test/useStellarStealthKeys.test.ts b/packages/sdk-svelte/test/useStellarStealthKeys.test.ts index a275211..81408e1 100644 --- a/packages/sdk-svelte/test/useStellarStealthKeys.test.ts +++ b/packages/sdk-svelte/test/useStellarStealthKeys.test.ts @@ -28,7 +28,7 @@ vi.mock('@wraith-protocol/sdk/chains/stellar', () => ({ spendingPubKey: new Uint8Array(32), viewingPubKey: new Uint8Array(32), }), - fetchAnnouncements: vi.fn().mockResolvedValue([]), + fetchAnnouncementsStream: vi.fn(), })); describe('useStellarStealthKeys', () => { diff --git a/packages/sdk-svelte/tsup.config.ts b/packages/sdk-svelte/tsup.config.ts index 4cbb94d..ab927b6 100644 --- a/packages/sdk-svelte/tsup.config.ts +++ b/packages/sdk-svelte/tsup.config.ts @@ -5,7 +5,7 @@ export default defineConfig({ index: 'src/index.ts', }, format: ['esm'], - dts: false, + dts: true, splitting: true, clean: true, treeshake: true, diff --git a/packages/sdk-svelte/vitest.config.ts b/packages/sdk-svelte/vitest.config.ts index 51deb5f..abf7db3 100644 --- a/packages/sdk-svelte/vitest.config.ts +++ b/packages/sdk-svelte/vitest.config.ts @@ -6,9 +6,10 @@ export default defineConfig({ plugins: [svelte()], test: { globals: true, - environment: 'node', + environment: 'jsdom', }, resolve: { + conditions: ['browser'], alias: { '@wraith-protocol/sdk': path.resolve(__dirname, '../../src'), }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 50fb26f..731f9d6 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -215,6 +215,28 @@ importers: specifier: ^5.7.0 version: 5.9.3 + examples/stellar-svelte-receive: + dependencies: + '@stellar/stellar-sdk': + specifier: ^13.1.0 + version: 13.3.0 + '@wraith-protocol/sdk-svelte': + specifier: workspace:* + version: link:../../packages/sdk-svelte + svelte: + specifier: ^5.0.0 + version: 5.56.4 + devDependencies: + '@sveltejs/vite-plugin-svelte': + specifier: ^5.0.0 + version: 5.1.1(svelte@5.56.4)(vite@6.4.3(@types/node@25.6.0)(jiti@2.6.1)(terser@5.48.0)(tsx@4.23.0)(yaml@2.9.0)) + typescript: + specifier: ^5.7.0 + version: 5.9.3 + vite: + specifier: ^6.0.0 + version: 6.4.3(@types/node@25.6.0)(jiti@2.6.1)(terser@5.48.0)(tsx@4.23.0)(yaml@2.9.0) + examples/svelte-stellar-app: dependencies: '@wraith-protocol/sdk': @@ -308,9 +330,18 @@ importers: specifier: workspace:* version: link:../.. devDependencies: + '@stellar/stellar-sdk': + specifier: ^13.1.0 + version: 13.3.0 '@sveltejs/vite-plugin-svelte': specifier: ^5.0.0 version: 5.1.1(svelte@5.56.4)(vite@7.3.2(@types/node@25.6.0)(jiti@2.6.1)(terser@5.48.0)(tsx@4.23.0)(yaml@2.9.0)) + '@testing-library/svelte': + specifier: ^5.2.0 + version: 5.4.2(svelte@5.56.4)(vite@7.3.2(@types/node@25.6.0)(jiti@2.6.1)(terser@5.48.0)(tsx@4.23.0)(yaml@2.9.0))(vitest@3.2.4(@types/node@25.6.0)(jiti@2.6.1)(jsdom@25.0.1(bufferutil@4.1.0)(utf-8-validate@6.0.6))(terser@5.48.0)(tsx@4.23.0)(yaml@2.9.0)) + jsdom: + specifier: ^25.0.0 + version: 25.0.1(bufferutil@4.1.0)(utf-8-validate@6.0.6) svelte: specifier: ^5.0.0 version: 5.56.4 @@ -1830,7 +1861,7 @@ packages: '@expo/bunyan@4.0.1': resolution: {integrity: sha512-+Lla7nYSiHZirgK+U/uYzsLv/X+HaJienbD5AKX1UQZHYfWaP+9uuQluRB4GrEVWF0GZ7vEVp/jzaOT9k/SQlg==} - engines: {node: '>=0.10.0'} + engines: {'0': node >=0.10.0} '@expo/cli@0.18.31': resolution: {integrity: sha512-v9llw9fT3Uv+TCM6Xllo54t672CuYtinEQZ2LPJ2EJsCwuTc4Cd2gXQaouuIVD21VoeGQnr5JtJuWbF97sBKzQ==} @@ -2146,79 +2177,66 @@ packages: resolution: {integrity: sha512-L+34Qqil+v5uC0zEubW7uByo78WOCIrBvci69E7sFASRl0X7b/MB6Cqd1lky/CtcSVTydWa2WZwFuWexjS5o6g==} cpu: [arm] os: [linux] - libc: [glibc] '@rollup/rollup-linux-arm-musleabihf@4.60.1': resolution: {integrity: sha512-n83O8rt4v34hgFzlkb1ycniJh7IR5RCIqt6mz1VRJD6pmhRi0CXdmfnLu9dIUS6buzh60IvACM842Ffb3xd6Gg==} cpu: [arm] os: [linux] - libc: [musl] '@rollup/rollup-linux-arm64-gnu@4.60.1': resolution: {integrity: sha512-Nql7sTeAzhTAja3QXeAI48+/+GjBJ+QmAH13snn0AJSNL50JsDqotyudHyMbO2RbJkskbMbFJfIJKWA6R1LCJQ==} cpu: [arm64] os: [linux] - libc: [glibc] '@rollup/rollup-linux-arm64-musl@4.60.1': resolution: {integrity: sha512-+pUymDhd0ys9GcKZPPWlFiZ67sTWV5UU6zOJat02M1+PiuSGDziyRuI/pPue3hoUwm2uGfxdL+trT6Z9rxnlMA==} cpu: [arm64] os: [linux] - libc: [musl] '@rollup/rollup-linux-loong64-gnu@4.60.1': resolution: {integrity: sha512-VSvgvQeIcsEvY4bKDHEDWcpW4Yw7BtlKG1GUT4FzBUlEKQK0rWHYBqQt6Fm2taXS+1bXvJT6kICu5ZwqKCnvlQ==} cpu: [loong64] os: [linux] - libc: [glibc] '@rollup/rollup-linux-loong64-musl@4.60.1': resolution: {integrity: sha512-4LqhUomJqwe641gsPp6xLfhqWMbQV04KtPp7/dIp0nzPxAkNY1AbwL5W0MQpcalLYk07vaW9Kp1PBhdpZYYcEw==} cpu: [loong64] os: [linux] - libc: [musl] '@rollup/rollup-linux-ppc64-gnu@4.60.1': resolution: {integrity: sha512-tLQQ9aPvkBxOc/EUT6j3pyeMD6Hb8QF2BTBnCQWP/uu1lhc9AIrIjKnLYMEroIz/JvtGYgI9dF3AxHZNaEH0rw==} cpu: [ppc64] os: [linux] - libc: [glibc] '@rollup/rollup-linux-ppc64-musl@4.60.1': resolution: {integrity: sha512-RMxFhJwc9fSXP6PqmAz4cbv3kAyvD1etJFjTx4ONqFP9DkTkXsAMU4v3Vyc5BgzC+anz7nS/9tp4obsKfqkDHg==} cpu: [ppc64] os: [linux] - libc: [musl] '@rollup/rollup-linux-riscv64-gnu@4.60.1': resolution: {integrity: sha512-QKgFl+Yc1eEk6MmOBfRHYF6lTxiiiV3/z/BRrbSiW2I7AFTXoBFvdMEyglohPj//2mZS4hDOqeB0H1ACh3sBbg==} cpu: [riscv64] os: [linux] - libc: [glibc] '@rollup/rollup-linux-riscv64-musl@4.60.1': resolution: {integrity: sha512-RAjXjP/8c6ZtzatZcA1RaQr6O1TRhzC+adn8YZDnChliZHviqIjmvFwHcxi4JKPSDAt6Uhf/7vqcBzQJy0PDJg==} cpu: [riscv64] os: [linux] - libc: [musl] '@rollup/rollup-linux-s390x-gnu@4.60.1': resolution: {integrity: sha512-wcuocpaOlaL1COBYiA89O6yfjlp3RwKDeTIA0hM7OpmhR1Bjo9j31G1uQVpDlTvwxGn2nQs65fBFL5UFd76FcQ==} cpu: [s390x] os: [linux] - libc: [glibc] '@rollup/rollup-linux-x64-gnu@4.60.1': resolution: {integrity: sha512-77PpsFQUCOiZR9+LQEFg9GClyfkNXj1MP6wRnzYs0EeWbPcHs02AXu4xuUbM1zhwn3wqaizle3AEYg5aeoohhg==} cpu: [x64] os: [linux] - libc: [glibc] '@rollup/rollup-linux-x64-musl@4.60.1': resolution: {integrity: sha512-5cIATbk5vynAjqqmyBjlciMJl1+R/CwX9oLk/EyiFXDWd95KpHdrOJT//rnUl4cUcskrd0jCCw3wpZnhIHdD9w==} cpu: [x64] os: [linux] - libc: [musl] '@rollup/rollup-openbsd-x64@4.60.1': resolution: {integrity: sha512-cl0w09WsCi17mcmWqqglez9Gk8isgeWvoUZ3WiJFYSR3zjBQc2J5/ihSjpl+VLjPqjQ/1hJRcqBfLjssREQILw==} @@ -2380,6 +2398,25 @@ packages: react: ^18.0.0 react-dom: ^18.0.0 + '@testing-library/svelte-core@1.1.3': + resolution: {integrity: sha512-KkMAvXeWorxN2Yn0kdC1lfoAItxpoj4uOWzxK5leDrNxonLvS5nwBFvztrroyTszQ0Wf/EU6iLT8JhY5qcn22g==} + engines: {node: '>=16'} + peerDependencies: + svelte: ^3 || ^4 || ^5 || ^5.0.0-next.0 + + '@testing-library/svelte@5.4.2': + resolution: {integrity: sha512-4o31E4HGo5BU5KwPkulNRocEden+7Tt9JYm9uhln5ajF7DULeyFA46BBWVfKJ8Ms9B3JmOFPTIiVamH7n3KpuQ==} + engines: {node: '>= 10'} + peerDependencies: + svelte: ^3 || ^4 || ^5 || ^5.0.0-next.0 + vite: '*' + vitest: '*' + peerDependenciesMeta: + vite: + optional: true + vitest: + optional: true + '@types/aria-query@5.0.4': resolution: {integrity: sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==} @@ -4536,28 +4573,24 @@ packages: engines: {node: '>= 12.0.0'} cpu: [arm64] os: [linux] - libc: [glibc] lightningcss-linux-arm64-musl@1.19.0: resolution: {integrity: sha512-vSCKO7SDnZaFN9zEloKSZM5/kC5gbzUjoJQ43BvUpyTFUX7ACs/mDfl2Eq6fdz2+uWhUh7vf92c4EaaP4udEtA==} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [linux] - libc: [musl] lightningcss-linux-x64-gnu@1.19.0: resolution: {integrity: sha512-0AFQKvVzXf9byrXUq9z0anMGLdZJS+XSDqidyijI5njIwj6MdbvX2UZK/c4FfNmeRa2N/8ngTffoIuOUit5eIQ==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [linux] - libc: [glibc] lightningcss-linux-x64-musl@1.19.0: resolution: {integrity: sha512-SJoM8CLPt6ECCgSuWe+g0qo8dqQYVcPiW2s19dxkmSI5+Uu1GIRzyKA0b7QqmEXolA+oSJhQqCmJpzjY4CuZAg==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [linux] - libc: [musl] lightningcss-win32-x64-msvc@1.19.0: resolution: {integrity: sha512-C+VuUTeSUOAaBZZOPT7Etn/agx/MatzJzGRkeV+zEABmPuntv1zihncsi+AyGmjkkzq3wVedEy7h0/4S84mUtg==} @@ -9135,6 +9168,19 @@ snapshots: transitivePeerDependencies: - '@types/react' + '@testing-library/svelte-core@1.1.3(svelte@5.56.4)': + dependencies: + svelte: 5.56.4 + + '@testing-library/svelte@5.4.2(svelte@5.56.4)(vite@7.3.2(@types/node@25.6.0)(jiti@2.6.1)(terser@5.48.0)(tsx@4.23.0)(yaml@2.9.0))(vitest@3.2.4(@types/node@25.6.0)(jiti@2.6.1)(jsdom@25.0.1(bufferutil@4.1.0)(utf-8-validate@6.0.6))(terser@5.48.0)(tsx@4.23.0)(yaml@2.9.0))': + dependencies: + '@testing-library/dom': 9.3.4 + '@testing-library/svelte-core': 1.1.3(svelte@5.56.4) + svelte: 5.56.4 + optionalDependencies: + vite: 7.3.2(@types/node@25.6.0)(jiti@2.6.1)(terser@5.48.0)(tsx@4.23.0)(yaml@2.9.0) + vitest: 3.2.4(@types/node@25.6.0)(jiti@2.6.1)(jsdom@25.0.1(bufferutil@4.1.0)(utf-8-validate@6.0.6))(terser@5.48.0)(tsx@4.23.0)(yaml@2.9.0) + '@types/aria-query@5.0.4': {} '@types/babel__core@7.20.5':