From 33442f0c9295e0bd968654c1f777d7b69e31e135 Mon Sep 17 00:00:00 2001 From: digitalbase Date: Sat, 11 Oct 2025 09:43:22 +0200 Subject: [PATCH 001/139] Remove docs cookbook --- docs/cookbook/README.md | 113 ------ docs/cookbook/TEMPLATE.md | 156 ------- docs/cookbook/index.md | 384 ------------------ docs/cookbook/svelte5/basic-authentication.md | 375 ----------------- .../svelte5/multi-session-management.md | 384 ------------------ 5 files changed, 1412 deletions(-) delete mode 100644 docs/cookbook/README.md delete mode 100644 docs/cookbook/TEMPLATE.md delete mode 100644 docs/cookbook/index.md delete mode 100644 docs/cookbook/svelte5/basic-authentication.md delete mode 100644 docs/cookbook/svelte5/multi-session-management.md diff --git a/docs/cookbook/README.md b/docs/cookbook/README.md deleted file mode 100644 index 4481912b9..000000000 --- a/docs/cookbook/README.md +++ /dev/null @@ -1,113 +0,0 @@ -# NDK Cookbook - -Welcome to the NDK Cookbook! This is a collection of self-contained recipes for building with the Nostr Development Kit (NDK). - -## What is a Cookbook Recipe? - -A cookbook recipe is a focused, practical guide that shows you how to accomplish a specific task with NDK. Unlike tutorials that teach concepts step-by-step, recipes are: - -- **Self-contained**: Everything you need is in one place -- **Practical**: Focuses on a specific, real-world use case -- **Copy-pasteable**: Includes complete working code examples -- **Focused**: Covers one topic deeply rather than many topics shallowly - -## Recipe Structure - -Each recipe follows a consistent structure: - -1. **Problem Statement**: What you'll build and why -2. **Prerequisites**: Required packages and knowledge -3. **Quick Start**: Minimal working example -4. **Step-by-Step**: Detailed implementation -5. **Complete Example**: Full, production-ready code -6. **Common Patterns**: Alternative approaches -7. **Troubleshooting**: Solutions to common issues -8. **Best Practices**: Tips from experience - -## Browse Recipes - -Visit [/ndk/cookbook/](/ndk/cookbook/) to browse all recipes. - -### By Category - -- ๐Ÿ” **Authentication** - Login flows, signers, sessions -- ๐Ÿ“ **Events** - Creating, publishing, and handling events -- ๐ŸŒ **Relays** - Connection management, hints, outbox model -- ๐Ÿ’ฐ **Payments** - Zaps, NWC, Cashu, Lightning -- ๐Ÿงช **Testing** - Mocks, fixtures, test patterns -- ๐Ÿ“ฑ **Mobile** - React Native specific recipes - -### By Package - -- **ndk-core** - Core functionality, events, relays -- **ndk-svelte5** - Svelte 5 reactive patterns -- **ndk-mobile** - React Native mobile apps -- **ndk-wallet** - Payment and wallet integration - -### By Difficulty - -- โญ **Beginner** - No prior NDK knowledge required -- โญโญ **Intermediate** - Assumes basic NDK understanding -- โญโญโญ **Advanced** - Complex concepts, multiple systems - -## Contributing a Recipe - -We welcome contributions! Here's how to add a new recipe: - -1. **Copy the template**: Use `/docs/cookbook/TEMPLATE.md` as a starting point -2. **Choose a category**: Place your recipe in the appropriate subdirectory -3. **Fill in metadata**: Complete all frontmatter fields -4. **Write the recipe**: Follow the structure in the template -5. **Add to index**: Update `/docs/cookbook/index.md` with your recipe card -6. **Submit PR**: Open a pull request with your recipe - -### Recipe Guidelines - -**Good recipes:** -- Solve a specific, real-world problem -- Include complete, working code examples -- Explain why, not just how -- Handle errors and edge cases -- Follow NDK best practices - -**Avoid:** -- Overly broad topics (split into multiple recipes) -- Incomplete or broken examples -- Copying API documentation -- Explaining concepts without practical examples - -### File Naming - -Use kebab-case for file names: -- โœ… `implementing-outbox-model.md` -- โœ… `custom-event-validation.md` -- โŒ `Implementing_Outbox_Model.md` -- โŒ `customEventValidation.md` - -### Directory Structure - -``` -cookbook/ -โ”œโ”€โ”€ index.md # Main cookbook index -โ”œโ”€โ”€ TEMPLATE.md # Recipe template -โ”œโ”€โ”€ README.md # This file -โ”œโ”€โ”€ core/ # ndk-core recipes -โ”‚ โ”œโ”€โ”€ events/ -โ”‚ โ”œโ”€โ”€ relays/ -โ”‚ โ””โ”€โ”€ signers/ -โ”œโ”€โ”€ svelte5/ # ndk-svelte5 recipes -โ”‚ โ”œโ”€โ”€ authentication/ -โ”‚ โ””โ”€โ”€ state-management/ -โ”œโ”€โ”€ mobile/ # ndk-mobile recipes -โ””โ”€โ”€ wallet/ # ndk-wallet recipes -``` - -## Getting Help - -- **Questions**: Ask in [GitHub Discussions](https://github.com/nostr-dev-kit/ndk/discussions) -- **Issues**: Report bugs in [GitHub Issues](https://github.com/nostr-dev-kit/ndk/issues) -- **Chat**: Join the Nostr developer community - -## License - -All recipes are released under the MIT License unless otherwise specified. diff --git a/docs/cookbook/TEMPLATE.md b/docs/cookbook/TEMPLATE.md deleted file mode 100644 index 07d461ee4..000000000 --- a/docs/cookbook/TEMPLATE.md +++ /dev/null @@ -1,156 +0,0 @@ ---- -title: Your Recipe Title Here -description: A brief 1-2 sentence description of what this recipe teaches -tags: ['ndk-core', 'tag1', 'tag2', 'tag3'] -difficulty: beginner|intermediate|advanced -timeToRead: 10 -package: ndk-core|ndk-svelte5|ndk-mobile|ndk-wallet -author: Your Name -dateAdded: 2024-03-04 ---- - -# Your Recipe Title Here - -A compelling introduction paragraph that explains what the reader will learn and why it's useful. Be specific about the problem this recipe solves. - -## What You'll Build - -- Bullet point of feature 1 -- Bullet point of feature 2 -- Bullet point of feature 3 -- Expected outcome - -## Prerequisites - -```bash -npm install @nostr-dev-kit/ndk [other packages] -``` - -Optional: List any knowledge prerequisites: -- Understanding of X -- Familiarity with Y - -## Quick Start - -A minimal working example that demonstrates the core concept: - -```typescript -import NDK from "@nostr-dev-kit/ndk" - -// Minimal example here -const ndk = new NDK() -``` - -## Step-by-Step Implementation - -### Step 1: Setup - -Explain the first step with code: - -```typescript -// Code example -``` - -### Step 2: Core Logic - -Explain the main implementation: - -```typescript -// Code example -``` - -### Step 3: Error Handling - -Show how to handle errors properly: - -```typescript -// Code example -``` - -## Complete Working Example - -Provide a full, copy-pasteable example: - -```typescript -// Complete example that ties everything together -``` - -## Common Patterns - -### Pattern 1: Use Case - -```typescript -// Example -``` - -### Pattern 2: Another Use Case - -```typescript -// Example -``` - -## Troubleshooting - -### Issue: Common Problem - -**Solution:** How to fix it - -### Issue: Another Common Problem - -**Solution:** How to fix it - -## Best Practices - -1. **Practice 1**: Explanation -2. **Practice 2**: Explanation -3. **Practice 3**: Explanation - -## Key Points - -- Important takeaway 1 -- Important takeaway 2 -- Important takeaway 3 - -## See Also - -- [Related Recipe](/ndk/cookbook/category/recipe-name) - Brief description -- [API Reference](/ndk/api/) - Full API documentation -- [NIP-XX Spec](https://github.com/nostr-protocol/nips/blob/master/XX.md) - If relevant - ---- - -## Metadata Guidelines - -### Title -- Clear and specific -- Action-oriented (e.g., "Building a...", "Implementing...", "Creating...") -- Max 60 characters - -### Description -- 1-2 sentences -- Focuses on what the reader learns -- Max 160 characters - -### Tags -- Use lowercase -- Separate words with hyphens -- Include: package name, NIPs, main concepts -- 3-6 tags recommended - -### Difficulty -- **beginner**: No prior NDK knowledge required -- **intermediate**: Assumes basic NDK understanding -- **advanced**: Complex concepts, multiple systems - -### Time to Read -- Realistic estimate in minutes -- Include time to understand and implement -- Round to nearest 5 minutes - -### Package -- The main NDK package used -- Options: ndk-core, ndk-svelte5, ndk-mobile, ndk-wallet, ndk-react - -### Author -- Your name or handle -- Can be "NDK Team" for official recipes diff --git a/docs/cookbook/index.md b/docs/cookbook/index.md deleted file mode 100644 index 0f4ff8732..000000000 --- a/docs/cookbook/index.md +++ /dev/null @@ -1,384 +0,0 @@ ---- -layout: page -title: NDK Cookbook ---- - - - -
-

NDK Cookbook

-

Discover self-contained recipes for building with NDK

- - - - -
- -## Recent Recipes - -
-

- Multi-Session Management with Account Switcher -

- -

- Learn how to manage multiple Nostr accounts simultaneously in a Svelte 5 application. Includes session switching, profile management, and a complete UI for handling multiple logged-in users. -

- -
- #ndk-svelte5 - #authentication - #multi-session - #session-management -
- -
- ๐Ÿ“ฆ ndk-svelte5 - โญโญโญ Advanced - โฑ 20 min -
- ๐Ÿ‘ 47 - ๐Ÿ’ฌ 12 - ๐Ÿ“– 1.2k -
-
-
- -
-

- Complete Authentication Flow with NIP-07, nsec, and NIP-46 -

- -

- Implement a complete authentication system supporting browser extensions (NIP-07), private keys (nsec), and remote signers (NIP-46) with bunker:// and nostrconnect:// flows. Includes QR code generation and error handling. -

- -
- #ndk-svelte5 - #authentication - #nip-07 - #nip-46 - #sessions -
- -
- ๐Ÿ“ฆ ndk-svelte5 - โญโญ Intermediate - โฑ 15 min -
- ๐Ÿ‘ 234 - ๐Ÿ’ฌ 23 - ๐Ÿ“– 3.4k -
-
-
- -
-

- Testing with Mock Relays -

- -

- Create and use mock relays for testing NDK applications without connecting to real Nostr relays. Perfect for unit tests, integration tests, and development environments. -

- -
- #ndk-core - #testing - #mock - #relays -
- -
- ๐Ÿ“ฆ ndk-core - โญ Beginner - โฑ 8 min -
- ๐Ÿ‘ 189 - ๐Ÿ’ฌ 15 - ๐Ÿ“– 2.8k -
-
-
- -
-

- Connect to Nostr Wallet Connect (NWC) -

- -

- Set up a connection to an NWC wallet and configure it for zapping. Learn how to handle wallet events, request permissions, and send Lightning payments through Nostr. -

- -
- #ndk-wallet - #nwc - #zaps - #payments - #lightning -
- -
- ๐Ÿ“ฆ ndk-wallet - โญโญ Intermediate - โฑ 12 min -
- ๐Ÿ‘ 156 - ๐Ÿ’ฌ 8 - ๐Ÿ“– 1.9k -
-
-
- -
-

- Using Cashu Wallet for E-Cash -

- -

- Create and use a Cashu wallet for managing e-cash tokens. Includes minting, melting, sending, and receiving Cashu tokens with complete error handling and balance management. -

- -
- #ndk-wallet - #cashu - #e-cash - #nutzaps - #nip-60 -
- -
- ๐Ÿ“ฆ ndk-wallet - โญโญโญ Advanced - โฑ 25 min -
- ๐Ÿ‘ 98 - ๐Ÿ’ฌ 19 - ๐Ÿ“– 876 -
-
-
- -
-

- Publishing Your First Nostr Event -

- -

- Learn the fundamentals of creating and publishing Nostr events with NDK. Covers event structure, signing, and publishing to relays with proper error handling. -

- -
- #ndk-core - #events - #publishing - #beginner -
- -
- ๐Ÿ“ฆ ndk-core - โญ Beginner - โฑ 5 min -
- ๐Ÿ‘ 445 - ๐Ÿ’ฌ 34 - ๐Ÿ“– 8.2k -
-
-
- ---- - -## Browse by Category - -
-
-

๐Ÿ” Authentication

-

8 recipes

-
- -
-

๐Ÿ“ Events

-

12 recipes

-
- -
-

๐ŸŒ Relays

-

6 recipes

-
- -
-

๐Ÿ’ฐ Payments

-

10 recipes

-
- -
-

๐Ÿงช Testing

-

8 recipes

-
- -
-

๐Ÿ“ฑ Mobile

-

7 recipes

-
-
- -## Popular Tags - -
- #ndk-core - #ndk-svelte5 - #authentication - #events - #relays - #subscriptions - #signers - #zaps - #cashu - #nip-46 - #nip-07 - #outbox - #cache - #testing - #mobile - #profiles - #reactions - #dms - #media - #blossom -
diff --git a/docs/cookbook/svelte5/basic-authentication.md b/docs/cookbook/svelte5/basic-authentication.md deleted file mode 100644 index 0a497ac4b..000000000 --- a/docs/cookbook/svelte5/basic-authentication.md +++ /dev/null @@ -1,375 +0,0 @@ ---- -title: Complete Authentication Flow with NIP-07, nsec, and NIP-46 -description: Implement a complete authentication system supporting browser extensions, private keys, and remote signers -tags: ['ndk-svelte5', 'authentication', 'nip-07', 'nip-46', 'sessions'] -difficulty: intermediate -timeToRead: 15 -package: ndk-svelte5 -author: NDK Team -dateAdded: 2024-03-04 ---- - -# Complete Authentication Flow with NIP-07, nsec, and NIP-46 - -This cookbook demonstrates how to implement a complete authentication system in a Svelte 5 application using NDK's session management. It supports multiple login methods including NIP-07 (browser extensions), private keys (nsec), and NIP-46 (remote signers). - -## What You'll Build - -- Browser extension login (NIP-07) with nos2x/Alby support -- Private key (nsec) login with validation -- Remote signer via bunker:// URI -- Remote signer via nostrconnect:// with QR code generation -- Session management and persistence -- Error handling and loading states - -## Prerequisites - -```bash -npm install @nostr-dev-kit/ndk @nostr-dev-kit/ndk-svelte5 qrcode -``` - -## Basic Setup - -First, initialize NDK and the session stores: - -```typescript -import NDK from "@nostr-dev-kit/ndk" -import { sessions, initStores } from "@nostr-dev-kit/ndk-svelte5" - -// Initialize NDK with your relay configuration -const ndk = new NDK({ - explicitRelayUrls: [ - 'wss://relay.damus.io', - 'wss://nos.lol', - 'wss://relay.nostr.band' - ] -}) - -// Initialize stores and connect -let initialized = $state(false) -initStores(ndk).then(() => { - ndk.connect() - initialized = true -}) -``` - -## NIP-07: Browser Extension Login - -Login using a Nostr browser extension like nos2x or Alby: - -```typescript -import { NDKNip07Signer } from "@nostr-dev-kit/ndk" - -async function loginWithNip07() { - try { - const signer = new NDKNip07Signer() - await sessions.login(signer) - console.log('Logged in with browser extension') - } catch (error) { - console.error('Failed to connect to extension:', error) - } -} -``` - -## Private Key (nsec) Login - -Login using an nsec private key: - -```typescript -import { NDKPrivateKeySigner } from "@nostr-dev-kit/ndk" - -async function loginWithNsec(nsecKey: string) { - try { - const signer = new NDKPrivateKeySigner(nsecKey) - await sessions.login(signer) - console.log('Logged in with private key') - } catch (error) { - console.error('Invalid nsec:', error) - } -} -``` - -## NIP-46: Remote Signer (bunker://) - -Login using a bunker:// URI from a remote signer: - -```typescript -import { NDKNip46Signer } from "@nostr-dev-kit/ndk" - -async function loginWithBunker(bunkerUri: string) { - try { - const signer = new NDKNip46Signer(ndk, bunkerUri) - await signer.blockUntilReady() - await sessions.login(signer) - console.log('Connected via bunker://') - } catch (error) { - console.error('Failed to connect via bunker://', error) - } -} -``` - -## NIP-46: Generate nostrconnect:// URI - -Generate a nostrconnect:// URI and QR code for remote signers to scan: - -```typescript -import { NDKNip46Signer } from "@nostr-dev-kit/ndk" -import QRCode from "qrcode" - -async function generateNostrConnect() { - try { - // Use a relay from your NDK pool - const relay = ndk.pool.relays.values().next().value?.url || 'wss://relay.damus.io' - - const signer = NDKNip46Signer.nostrconnect(ndk, relay) - - // Get the generated URI - const uri = signer.nostrConnectUri - if (!uri) throw new Error('Failed to generate nostrconnect URI') - - // Generate QR code - const qrCodeDataUrl = await QRCode.toDataURL(uri, { - width: 300, - margin: 2, - }) - - // Display the QR code to the user - console.log('Scan this QR code:', qrCodeDataUrl) - console.log('Or use this URI:', uri) - - // Wait for the remote signer to connect - await signer.blockUntilReady() - await sessions.login(signer) - - console.log('Connected via nostrconnect://') - } catch (error) { - console.error('Failed to generate nostrconnect URI:', error) - } -} -``` - -## Complete Login Modal Component - -Here's a complete Svelte 5 component with all login methods: - -```svelte - - -
- {#if !initialized} -

Initializing...

- {:else if !sessions.current} - - {:else} -
-

Logged in as: {sessions.current.pubkey}

-

Name: {sessions.profile?.name || '(no name)'}

- -
- {/if} -
- -{#if showLoginModal} - -{/if} -``` - -## Key Points - -- Always initialize stores with `initStores(ndk)` before using session management -- The `sessions.current` store contains the active session or `undefined` if not logged in -- The `sessions.profile` store contains the current user's profile metadata -- All session data persists across page reloads -- NIP-46 requires `blockUntilReady()` before logging in - -## See Also - -- [Multi-Session Management](/ndk/cookbook/svelte5/multi-session-management) - Handle multiple accounts -- [Session API Reference](/ndk/api/) - Full API documentation -- [NIP-46 Spec](https://github.com/nostr-protocol/nips/blob/master/46.md) - Remote signer protocol diff --git a/docs/cookbook/svelte5/multi-session-management.md b/docs/cookbook/svelte5/multi-session-management.md deleted file mode 100644 index d86694ba4..000000000 --- a/docs/cookbook/svelte5/multi-session-management.md +++ /dev/null @@ -1,384 +0,0 @@ ---- -title: Multi-Session Management with Account Switcher -description: Manage multiple Nostr accounts simultaneously with session switching and profile management -tags: ['ndk-svelte5', 'authentication', 'multi-session', 'session-management'] -difficulty: advanced -timeToRead: 20 -package: ndk-svelte5 -author: NDK Team -dateAdded: 2024-03-04 ---- - -# Multi-Session Management with Account Switcher - -This cookbook demonstrates how to manage multiple Nostr accounts simultaneously in a Svelte 5 application. Users can log in with multiple accounts, switch between them instantly, and view all active sessions with a beautiful account switcher UI. - -## What You'll Build - -- Multiple account management -- Session switching with instant updates -- Account switcher dropdown component -- Session list view -- Profile data for each session -- Logout individual or all sessions - -## Understanding Multi-Session - -NDK's session management system allows you to: -- Have multiple accounts logged in simultaneously -- Switch between accounts instantly -- Maintain separate profile data, follows, and mutes for each account -- Persist all sessions across page reloads - -## Adding Multiple Sessions - -### Add a New Session - -Use `sessions.add()` to add an additional session without logging out of the current one: - -```typescript -import { NDKPrivateKeySigner } from "@nostr-dev-kit/ndk" -import { sessions } from "@nostr-dev-kit/ndk-svelte5" - -async function addAnotherAccount(nsecKey: string) { - try { - const signer = new NDKPrivateKeySigner(nsecKey) - await sessions.add(signer) - console.log('Added new session') - } catch (error) { - console.error('Failed to add session:', error) - } -} -``` - -### Generate and Add a New Account - -```typescript -import { NDKPrivateKeySigner } from "@nostr-dev-kit/ndk" -import { sessions } from "@nostr-dev-kit/ndk-svelte5" - -async function generateAndAddAccount() { - const signer = NDKPrivateKeySigner.generate() - await sessions.add(signer) - console.log('Generated and added new account') -} -``` - -## Switching Between Sessions - -Use `sessions.switch()` to switch to a different logged-in account: - -```typescript -function switchSession(pubkey: string) { - sessions.switch(pubkey) - console.log('Switched to session:', pubkey) -} -``` - -## Accessing All Sessions - -The `sessions.all` store contains an array of all logged-in sessions: - -```typescript -// Get all sessions -console.log('Total sessions:', sessions.all.length) - -// Iterate through all sessions -sessions.all.forEach(session => { - console.log('Pubkey:', session.pubkey) - console.log('Follows:', session.follows.size) - console.log('Mutes:', session.mutes.size) -}) - -// Check if a specific pubkey is logged in -const isActive = sessions.all.some(s => s.pubkey === sessions.current?.pubkey) -``` - -## Session Data Structure - -Each session in `sessions.all` contains: - -```typescript -interface Session { - pubkey: string // The user's public key - follows: Set // Set of followed pubkeys - mutes: Set // Set of muted pubkeys -} -``` - -## Logging Out - -### Logout Current Session - -```typescript -function logout() { - sessions.logout() -} -``` - -### Logout All Sessions - -```typescript -function logoutAll() { - sessions.logoutAll() -} -``` - -## Complete Session Switcher Component - -Here's a beautiful dropdown session switcher: - -```svelte - - - - -{#if initialized && sessions.current} -
- - - {#if showSessionMenu} -
-
- Logged in Accounts -
- - {#each sessions.all as session} -
switchSession(session.pubkey)} - > -
- - {formatPubkey(session.pubkey)} - - {#if sessions.current?.pubkey === session.pubkey} - ACTIVE - {/if} -
-
- Follows: {session.follows.size} ยท Mutes: {session.mutes.size} -
-
- {/each} - - -
- {/if} -
-{/if} -``` - -## Session List Component - -A simple list showing all sessions: - -```svelte - - -
-

All Sessions ({sessions.all.length})

- - {#if sessions.all.length === 0} -

No sessions available

- {:else} - {#each sessions.all as session} -
-
- {session.pubkey.slice(0, 16)}... - {#if sessions.current?.pubkey === session.pubkey} - ACTIVE - {/if} -
-
- Follows: {session.follows.size} | Mutes: {session.mutes.size} -
- {#if sessions.current?.pubkey !== session.pubkey} - - {/if} -
- {/each} - {/if} -
-``` - -## Best Practices - -1. **Check for current session**: Always use `sessions.current` to check if a user is logged in -2. **Handle session switching**: Subscriptions update automatically when switching sessions -3. **Session persistence**: All sessions persist in localStorage across page reloads -4. **Memory management**: Each session loads its own profile, follows, and mutes independently -5. **UI feedback**: Always show which session is currently active - -## Common Patterns - -### Quick Session Switch in Header - -```svelte -
- {#if sessions.all.length > 1} - - {/if} -
-``` - -### Reactive Session Updates - -```svelte - -``` - -## See Also - -- [Complete Authentication Flow](/ndk/cookbook/svelte5/basic-authentication) - Login methods -- [Session API Reference](/ndk/api/) - Full API documentation From 4424765267a3576ccb307486a210d99ad86dfc6c Mon Sep 17 00:00:00 2001 From: digitalbase Date: Sat, 11 Oct 2025 10:49:08 +0200 Subject: [PATCH 002/139] Revert Doc removal --- ndk-core/docs/ai-guardrails.md | 506 ++++++++++++++++++ ndk-core/docs/api-examples.md | 49 ++ ndk-core/docs/event-class-registration.md | 173 ++++++ .../docs/examples/ai-guardrails-example.ts | 167 ++++++ ndk-core/docs/getting-started/introduction.md | 35 ++ ndk-core/docs/getting-started/signers.md | 150 ++++++ ndk-core/docs/getting-started/usage.md | 258 +++++++++ ndk-core/docs/index.md | 18 + ndk-core/docs/internals/subscriptions.md | 292 ++++++++++ ndk-core/docs/migration/2.12-to-2.13.md | 59 ++ ndk-core/docs/tutorial/auth.md | 29 + ndk-core/docs/tutorial/filter-validation.md | 218 ++++++++ ndk-core/docs/tutorial/local-first.md | 63 +++ ndk-core/docs/tutorial/mute-filtering.md | 130 +++++ ndk-core/docs/tutorial/nip19.md | 186 +++++++ ndk-core/docs/tutorial/publishing.md | 24 + ndk-core/docs/tutorial/signer-persistence.md | 154 ++++++ ndk-core/docs/tutorial/speed.md | 48 ++ .../docs/tutorial/subscription-management.md | 105 ++++ ndk-core/docs/tutorial/zaps/index.md | 16 + 20 files changed, 2680 insertions(+) create mode 100644 ndk-core/docs/ai-guardrails.md create mode 100644 ndk-core/docs/api-examples.md create mode 100644 ndk-core/docs/event-class-registration.md create mode 100644 ndk-core/docs/examples/ai-guardrails-example.ts create mode 100644 ndk-core/docs/getting-started/introduction.md create mode 100644 ndk-core/docs/getting-started/signers.md create mode 100644 ndk-core/docs/getting-started/usage.md create mode 100644 ndk-core/docs/index.md create mode 100644 ndk-core/docs/internals/subscriptions.md create mode 100644 ndk-core/docs/migration/2.12-to-2.13.md create mode 100644 ndk-core/docs/tutorial/auth.md create mode 100644 ndk-core/docs/tutorial/filter-validation.md create mode 100644 ndk-core/docs/tutorial/local-first.md create mode 100644 ndk-core/docs/tutorial/mute-filtering.md create mode 100644 ndk-core/docs/tutorial/nip19.md create mode 100644 ndk-core/docs/tutorial/publishing.md create mode 100644 ndk-core/docs/tutorial/signer-persistence.md create mode 100644 ndk-core/docs/tutorial/speed.md create mode 100644 ndk-core/docs/tutorial/subscription-management.md create mode 100644 ndk-core/docs/tutorial/zaps/index.md diff --git a/ndk-core/docs/ai-guardrails.md b/ndk-core/docs/ai-guardrails.md new file mode 100644 index 000000000..aa0eca813 --- /dev/null +++ b/ndk-core/docs/ai-guardrails.md @@ -0,0 +1,506 @@ +# AI Guardrails + +AI Guardrails is a runtime validation system designed to catch common mistakes when using NDK, especially those made by LLMs (Large Language Models) generating code. + +## Overview + +AI Guardrails provides: + +- **Educational error messages** - Clear explanations of what went wrong and how to fix it +- **Zero performance impact** - Disabled by default, opt-in only +- **Granular control** - Enable all checks or selectively disable specific ones +- **LLM-friendly** - Designed to help AI-generated code self-correct +- **Always visible** - Both errors and warnings throw exceptions AND log to console.error (so AIs see them even if throws get swallowed) + +## Quick Start + +```typescript +import NDK from "@nostr-dev-kit/ndk"; + +// Enable all guardrails (recommended for development) +const ndk = new NDK({ aiGuardrails: true }); + +// Or enable with exceptions +const ndk = new NDK({ + aiGuardrails: { + skip: new Set(['filter-large-limit', 'fetch-events-usage']) + } +}); + +// Or programmatically control +ndk.aiGuardrails.skip('event-param-replaceable-no-dtag'); +ndk.aiGuardrails.enable('filter-bech32-in-array'); +``` + +## Available Guardrails + +### Filter-Related Checks + +#### `filter-bech32-in-array` +**Level:** Error + +Catches bech32-encoded values in filter arrays. Filters expect hex values, not bech32. + +```typescript +// โŒ WRONG +ndk.subscribe({ + authors: ['npub1...'] // bech32 npub +}); + +// โœ… CORRECT +import { nip19 } from 'nostr-tools'; +const { data } = nip19.decode('npub1...'); +ndk.subscribe({ + authors: [data] // hex pubkey +}); + +// Or use filterFromId for complete bech32 entities +import { filterFromId } from '@nostr-dev-kit/ndk'; +const filter = filterFromId('nevent1...'); +ndk.subscribe(filter); +``` + +#### `filter-only-limit` +**Level:** Error + +Catches filters with only a `limit` parameter and no filtering criteria. + +```typescript +// โŒ WRONG - will fetch random events +ndk.subscribe({ limit: 10 }); + +// โœ… CORRECT +ndk.subscribe({ + kinds: [1], + limit: 10 +}); +``` + +#### `filter-large-limit` +**Level:** Warning + +Warns about very large limit values that can cause performance issues. + +```typescript +// โš ๏ธ WARNING +ndk.subscribe({ + kinds: [1], + limit: 10000 // Too large! +}); + +// โœ… BETTER +ndk.subscribe({ + kinds: [1], + limit: 100 // More reasonable +}); +``` + +#### `filter-empty` +**Level:** Error + +Catches completely empty filters. + +```typescript +// โŒ WRONG +ndk.subscribe({}); + +// โœ… CORRECT +ndk.subscribe({ kinds: [1] }); +``` + +#### `filter-since-after-until` +**Level:** Error + +Catches filters where `since` is after `until`, which would match zero events. + +```typescript +// โŒ WRONG +ndk.subscribe({ + since: 1000000, + until: 500000 +}); + +// โœ… CORRECT +ndk.subscribe({ + since: 500000, + until: 1000000 +}); +``` + +#### `filter-invalid-a-tag` +**Level:** Error + +Catches malformed `#a` tag values. Must be `kind:pubkey:d-tag` format. + +```typescript +// โŒ WRONG +ndk.subscribe({ + '#a': ['nevent1...'] // bech32 instead of address +}); + +// โœ… CORRECT +ndk.subscribe({ + '#a': ['30023:fa984bd7...:my-article'] +}); +``` + +### fetchEvents Anti-Pattern + +#### `fetch-events-usage` +**Level:** Warning + +Warns about using `fetchEvents()` which is a blocking operation. + +```typescript +// โš ๏ธ SUBOPTIMAL - blocks until EOSE +const events = await ndk.fetchEvents({ kinds: [1] }); + +// โœ… BETTER - reactive, non-blocking +ndk.subscribe({ kinds: [1] }, { + onEvent: (event) => { + console.log('Got event:', event); + } +}); + +// Or for single events +const event = await ndk.fetchEvent({ kinds: [1] }); +``` + +**When to disable:** If you truly need to block until all events arrive (rare). + +```typescript +ndk.aiGuardrails.skip('fetch-events-usage'); +const events = await ndk.fetchEvents(filter); +``` + +### Event Construction Checks + +#### `event-missing-kind` +**Level:** Error + +Catches attempts to sign events without a `kind`. + +```typescript +// โŒ WRONG +const event = new NDKEvent(ndk); +event.content = "Hello"; +await event.sign(); // Error! + +// โœ… CORRECT +const event = new NDKEvent(ndk); +event.kind = 1; // Set kind first +event.content = "Hello"; +await event.sign(); +``` + +#### `event-content-is-object` +**Level:** Error + +Catches attempts to set event content to an object instead of a string. + +```typescript +// โŒ WRONG +const event = new NDKEvent(ndk); +event.kind = 30023; +event.content = { title: "My Article" }; // Object! +await event.sign(); // Error! + +// โœ… CORRECT +const event = new NDKEvent(ndk); +event.kind = 30023; +event.content = JSON.stringify({ title: "My Article" }); +await event.sign(); +``` + +#### `event-param-replaceable-no-dtag` +**Level:** Warning + +Warns about parameterized replaceable events (kinds 30000-39999) without a d-tag. + +```typescript +// โš ๏ธ WARNING - will use empty string as d-tag +const event = new NDKEvent(ndk); +event.kind = 30023; +event.content = "My article"; +await event.sign(); // Warning! + +// โœ… BETTER +const event = new NDKEvent(ndk); +event.kind = 30023; +event.dTag = "my-unique-article-id"; +event.content = "My article"; +await event.sign(); +``` + +#### `event-created-at-milliseconds` +**Level:** Error + +Catches using milliseconds instead of seconds for `created_at`. + +```typescript +// โŒ WRONG +const event = new NDKEvent(ndk); +event.kind = 1; +event.content = "Hello"; +event.created_at = Date.now(); // Milliseconds! +await event.sign(); // Error! + +// โœ… CORRECT +const event = new NDKEvent(ndk); +event.kind = 1; +event.content = "Hello"; +event.created_at = Math.floor(Date.now() / 1000); // Seconds +await event.sign(); +``` + +### Tag Validation Checks + +#### `tag-invalid-p-tag` +**Level:** Error + +Catches invalid p-tags (must be 64-character hex pubkeys). + +```typescript +// โŒ WRONG +const event = new NDKEvent(ndk); +event.kind = 1; +event.tags.push(['p', 'npub1...']); // bech32! +await event.sign(); // Error! + +// โœ… CORRECT +const event = new NDKEvent(ndk); +event.kind = 1; +event.tags.push(['p', ndkUser.pubkey]); // hex pubkey +await event.sign(); +``` + +#### `tag-invalid-e-tag` +**Level:** Error + +Catches invalid e-tags (must be 64-character hex event IDs). + +```typescript +// โŒ WRONG +const event = new NDKEvent(ndk); +event.kind = 1; +event.tags.push(['e', 'note1...']); // bech32! +await event.sign(); // Error! + +// โœ… CORRECT +const event = new NDKEvent(ndk); +event.kind = 1; +event.tags.push(['e', referencedEvent.id]); // hex event ID +await event.sign(); +``` + +#### `event-manual-reply-markers` +**Level:** Warning + +Warns about manually adding e-tags with reply/root markers instead of using `.reply()`. + +```typescript +// โš ๏ธ SUBOPTIMAL +const reply = new NDKEvent(ndk); +reply.kind = 1; +reply.content = "Great post!"; +reply.tags.push(['e', parentEvent.id, '', 'reply']); // Manual marker +await reply.sign(); // Warning! + +// โœ… BETTER - Use reply() method +const reply = new NDKEvent(ndk); +reply.kind = 1; +reply.content = "Great post!"; +await reply.reply(parentEvent); // Handles threading automatically +``` + +## Programmatic Control + +### Temporarily Disable a Check + +```typescript +// Disable for one-time use +ndk.aiGuardrails.skip('fetch-events-usage'); +const events = await ndk.fetchEvents(filter); + +// Re-enable it +ndk.aiGuardrails.enable('fetch-events-usage'); +``` + +### Check What's Skipped + +```typescript +const skipped = ndk.aiGuardrails.getSkipped(); +console.log('Skipped checks:', skipped); +``` + +### Runtime Enable/Disable + +```typescript +// Start with guardrails disabled +const ndk = new NDK(); + +// Enable later +ndk.aiGuardrails.setMode(true); + +// Or enable with specific skips +ndk.aiGuardrails.setMode({ + skip: new Set(['filter-large-limit']) +}); + +// Disable entirely +ndk.aiGuardrails.setMode(false); +``` + +## Error Messages + +When a guardrail is triggered, it will: +1. **Log to console.error** (visible even if the exception is caught) +2. **Throw an Error** (stops execution) + +Both "ERROR" and "WARNING" level checks throw exceptions - warnings are not just console warnings. + +### Fatal vs Non-Fatal Errors + +Some errors are **fatal** - they represent fundamental mistakes that cannot be bypassed: +- Missing event kind +- Content as object instead of string +- Timestamps in milliseconds instead of seconds +- Invalid p-tag/e-tag formats +- Bech32 in filter arrays +- Empty filters or invalid time ranges + +**Fatal errors do NOT show the "To disable this check" message.** + +Example fatal error (no disable option): + +``` +๐Ÿค– AI_GUARDRAILS ERROR: Cannot sign event without 'kind'. Set event.kind before signing. + +๐Ÿ’ก Example: event.kind = 1; // for text note +``` + +Example non-fatal error (can be disabled): + +``` +๐Ÿค– AI_GUARDRAILS ERROR: Filter[0] contains only 'limit' without any filtering criteria. + +๐Ÿ’ก Add filtering criteria like 'kinds', 'authors', or '#e' tags. + +๐Ÿ”‡ To disable this check: + ndk.aiGuardrails.skip('filter-only-limit') + or set: ndk.aiGuardrails = { skip: new Set(['filter-only-limit']) } +``` + +## Best Practices + +### For Development + +Enable all guardrails during development: + +```typescript +const ndk = new NDK({ + aiGuardrails: true, + // ... other options +}); +``` + +### For Production + +Keep guardrails disabled in production for zero performance impact: + +```typescript +const ndk = new NDK({ + aiGuardrails: process.env.NODE_ENV === 'development', + // ... other options +}); +``` + +### For AI-Generated Code + +If you're using AI to generate code, enable guardrails and let the AI learn from the errors: + +```typescript +// In your prompt/system message: +"When NDK throws an AI_GUARDRAILS error, read the error message carefully. +It explains what's wrong and how to fix it. Update your code accordingly." +``` + +The AI can also programmatically skip checks it knows are safe: + +```typescript +// LLM can disable specific checks when it knows what it's doing +ndk.aiGuardrails.skip('filter-large-limit'); // I know I need 5000 events +ndk.subscribe({ kinds: [1], limit: 5000 }); +``` + +## Complete Check ID Reference + +Import these for type-safe check IDs: + +```typescript +import { GuardrailCheckId } from '@nostr-dev-kit/ndk'; + +// All available check IDs: +GuardrailCheckId.FILTER_BECH32_IN_ARRAY +GuardrailCheckId.FILTER_ONLY_LIMIT +GuardrailCheckId.FILTER_LARGE_LIMIT +GuardrailCheckId.FILTER_EMPTY +GuardrailCheckId.FILTER_SINCE_AFTER_UNTIL +GuardrailCheckId.FILTER_INVALID_A_TAG +GuardrailCheckId.FETCH_EVENTS_USAGE +GuardrailCheckId.EVENT_MISSING_KIND +GuardrailCheckId.EVENT_PARAM_REPLACEABLE_NO_DTAG +GuardrailCheckId.EVENT_CREATED_AT_MILLISECONDS +GuardrailCheckId.EVENT_NO_NDK_INSTANCE +GuardrailCheckId.EVENT_CONTENT_IS_OBJECT +GuardrailCheckId.EVENT_MODIFIED_AFTER_SIGNING +GuardrailCheckId.EVENT_MANUAL_REPLY_MARKERS +GuardrailCheckId.TAG_E_FOR_PARAM_REPLACEABLE +GuardrailCheckId.TAG_BECH32_VALUE +GuardrailCheckId.TAG_DUPLICATE +GuardrailCheckId.TAG_INVALID_P_TAG +GuardrailCheckId.TAG_INVALID_E_TAG +GuardrailCheckId.SUBSCRIBE_NOT_STARTED +GuardrailCheckId.SUBSCRIBE_CLOSE_ON_EOSE_NO_HANDLER +GuardrailCheckId.SUBSCRIBE_PASSED_EVENT_NOT_FILTER +GuardrailCheckId.SUBSCRIBE_AWAITED +GuardrailCheckId.RELAY_INVALID_URL +GuardrailCheckId.RELAY_HTTP_INSTEAD_OF_WS +GuardrailCheckId.RELAY_NO_ERROR_HANDLERS +GuardrailCheckId.VALIDATION_PUBKEY_IS_NPUB +GuardrailCheckId.VALIDATION_PUBKEY_WRONG_LENGTH +GuardrailCheckId.VALIDATION_EVENT_ID_IS_BECH32 +GuardrailCheckId.VALIDATION_EVENT_ID_WRONG_LENGTH +``` + +## Philosophy + +AI Guardrails is designed with these principles: + +1. **Educational, not punitive** - Error messages teach, don't just reject +2. **Opt-in, not opt-out** - Zero impact when disabled (default) +3. **Flexible** - Granular control over what's checked +4. **LLM-friendly** - Help AI code self-correct and learn patterns + +## Contributing + +To add a new guardrail: + +1. Add the check ID to `GuardrailCheckId` in `ai-guardrails.ts` +2. Implement the check where appropriate (filter validation, event signing, etc.) +3. Use `ndk.aiGuardrails.error()` or `ndk.aiGuardrails.warn()` +4. Add clear, actionable error messages with hints +5. Document it in this file +6. Add tests + +Example: + +```typescript +if (ndk?.aiGuardrails.isEnabled()) { + ndk.aiGuardrails.error( + GuardrailCheckId.YOUR_NEW_CHECK, + "Clear explanation of what's wrong", + "Helpful hint on how to fix it" + ); +} +``` diff --git a/ndk-core/docs/api-examples.md b/ndk-core/docs/api-examples.md new file mode 100644 index 000000000..6bd8bb5c1 --- /dev/null +++ b/ndk-core/docs/api-examples.md @@ -0,0 +1,49 @@ +--- +outline: deep +--- + +# Runtime API Examples + +This page demonstrates usage of some of the runtime APIs provided by VitePress. + +The main `useData()` API can be used to access site, theme, and page data for the current page. It works in both `.md` and `.vue` files: + +```md + + +## Results + +### Theme Data +
{{ theme }}
+ +### Page Data +
{{ page }}
+ +### Page Frontmatter +
{{ frontmatter }}
+``` + + + +## Results + +### Theme Data +
{{ theme }}
+ +### Page Data +
{{ page }}
+ +### Page Frontmatter +
{{ frontmatter }}
+ +## More + +Check out the documentation for the [full list of runtime APIs](https://vitepress.dev/reference/runtime-api#usedata). diff --git a/ndk-core/docs/event-class-registration.md b/ndk-core/docs/event-class-registration.md new file mode 100644 index 000000000..367cce8fe --- /dev/null +++ b/ndk-core/docs/event-class-registration.md @@ -0,0 +1,173 @@ +# Custom Event Class Registration + +NDK provides a registration system that allows you to register custom event classes to work with the `wrapEvent()` function. This enables you to create your own event types that integrate seamlessly with NDK's event wrapping system. + +## Overview + +The `wrapEvent()` function automatically wraps raw `NDKEvent` objects into more specific event types based on their `kind` property. By default, NDK includes many built-in event classes (like `NDKArticle`, `NDKImage`, etc.), but you can also register your own custom event classes. + +## Registering Custom Event Classes + +### Requirements + +Your custom event class must meet these requirements: + +1. **Extend NDKEvent**: Your class should extend the `NDKEvent` base class +2. **Static `kinds` property**: An array of event kind numbers this class handles +3. **Static `from` method**: A factory method that creates an instance from an `NDKEvent` + +### Example + +```typescript +import { NDKEvent, registerEventClass } from "@nostr-dev-kit/ndk"; + +class MyCustomEvent extends NDKEvent { + static kinds = [12345]; // Custom event kind number + + static from(event: NDKEvent) { + return new MyCustomEvent(event.ndk, event); + } + + constructor(ndk: NDK | undefined, rawEvent?: NostrEvent | NDKEvent) { + super(ndk, rawEvent); + this.kind ??= 12345; + } + + // Add your custom methods and properties + get customProperty(): string | undefined { + return this.tagValue("custom"); + } + + set customProperty(value: string | undefined) { + this.removeTag("custom"); + if (value) this.tags.push(["custom", value]); + } +} + +// Register the class +registerEventClass(MyCustomEvent); +``` + +### Multiple Kinds + +Your custom event class can handle multiple event kinds: + +```typescript +class MyMultiKindEvent extends NDKEvent { + static kinds = [12345, 12346, 12347]; + + static from(event: NDKEvent) { + return new MyMultiKindEvent(event.ndk, event); + } + + // Implementation... +} + +registerEventClass(MyMultiKindEvent); +``` + +## API Reference + +### `registerEventClass(eventClass)` + +Registers a custom event class to be used with `wrapEvent()`. + +**Parameters:** +- `eventClass`: An object that implements the `NDKEventClass` interface + +**Example:** +```typescript +registerEventClass(MyCustomEvent); +``` + +### `unregisterEventClass(eventClass)` + +Removes a previously registered event class. + +**Parameters:** +- `eventClass`: The event class to unregister + +**Example:** +```typescript +unregisterEventClass(MyCustomEvent); +``` + +### `getRegisteredEventClasses()` + +Returns a Set of all currently registered custom event classes. + +**Returns:** +- `Set`: Set of registered event classes + +**Example:** +```typescript +const registeredClasses = getRegisteredEventClasses(); +console.log(`${registeredClasses.size} custom classes registered`); +``` + +## How It Works + +When you call `wrapEvent()` on an `NDKEvent`, the function: + +1. Creates a mapping of event kinds to their corresponding classes +2. Includes both built-in NDK classes and your registered custom classes +3. Looks up the event's `kind` in this mapping +4. If found, calls the class's `from()` method to create a wrapped instance +5. If not found, returns the original `NDKEvent` + +```typescript +// This will now return a MyCustomEvent instance for kind 12345 +const wrappedEvent = wrapEvent(rawEvent); // rawEvent.kind === 12345 +``` + +## Best Practices + +1. **Choose unique kind numbers**: Make sure your custom event kinds don't conflict with existing Nostr event kinds +2. **Follow NIP specifications**: If you're implementing a NIP, follow its specifications for event structure +3. **Extend existing functionality**: Build upon NDK's existing event capabilities rather than replacing them +4. **Handle errors gracefully**: Your `from()` method should handle invalid or malformed events +5. **Test thoroughly**: Test your custom event classes with various input scenarios + +## Example: Custom Chat Message Event + +```typescript +import { NDKEvent, registerEventClass, NDKKind } from "@nostr-dev-kit/ndk"; + +class NDKChatMessage extends NDKEvent { + static kinds = [42]; // Using kind 42 for chat messages + + static from(event: NDKEvent) { + return new NDKChatMessage(event.ndk, event); + } + + constructor(ndk: NDK | undefined, rawEvent?: NostrEvent | NDKEvent) { + super(ndk, rawEvent); + this.kind ??= 42; + } + + get roomId(): string | undefined { + return this.tagValue("room"); + } + + set roomId(roomId: string | undefined) { + this.removeTag("room"); + if (roomId) this.tags.push(["room", roomId]); + } + + get replyTo(): string | undefined { + return this.tagValue("reply"); + } + + set replyTo(eventId: string | undefined) { + this.removeTag("reply"); + if (eventId) this.tags.push(["reply", eventId]); + } +} + +// Register the custom chat message class +registerEventClass(NDKChatMessage); + +// Now wrapEvent will automatically create NDKChatMessage instances for kind 42 events +``` + +This registration system provides a flexible way to extend NDK with your own event types while maintaining compatibility with the existing event wrapping infrastructure. \ No newline at end of file diff --git a/ndk-core/docs/examples/ai-guardrails-example.ts b/ndk-core/docs/examples/ai-guardrails-example.ts new file mode 100644 index 000000000..77d161a19 --- /dev/null +++ b/ndk-core/docs/examples/ai-guardrails-example.ts @@ -0,0 +1,167 @@ +/** + * AI Guardrails Example + * + * This example demonstrates how AI Guardrails help catch common mistakes + * when using NDK, especially useful for LLM-generated code. + */ + +import NDK, { GuardrailCheckId, NDKEvent } from "@nostr-dev-kit/ndk"; +import { nip19 } from "nostr-tools"; + +// Example 1: Enable all guardrails (recommended for development) +const ndk = new NDK({ + explicitRelayUrls: ["wss://relay.damus.io"], + aiGuardrails: true, // Enable all checks +}); + +await ndk.connect(); + +// Example 2: Filter with bech32 - will throw error +try { + const npub = "npub1l2vyh47mk2p0qlsku7hg0vn29faehy9hy34ygaclpn66ukqp3afqutajft"; + + // โŒ This will throw an AI Guardrails error + ndk.subscribe({ + authors: [npub], // Wrong! Should be hex + }); +} catch (error) { + console.log("Caught error:", error.message); + // Error message will explain how to fix it +} + +// โœ… Correct way - decode bech32 first +const npub = "npub1l2vyh47mk2p0qlsku7hg0vn29faehy9hy34ygaclpn66ukqp3afqutajft"; +const { data: pubkey } = nip19.decode(npub); +ndk.subscribe({ + authors: [pubkey as string], +}); + +// Example 3: Empty filter - will throw error +try { + // โŒ This will throw + ndk.subscribe({}); +} catch (error) { + console.log("Caught empty filter error"); +} + +// Example 4: Filter with only limit - will throw error +try { + // โŒ This will throw + ndk.subscribe({ limit: 10 }); +} catch (error) { + console.log("Caught limit-only filter error"); +} + +// Example 5: Event signing without kind - will throw error +try { + const event = new NDKEvent(ndk); + event.content = "Hello world"; + // โŒ Missing kind! + await event.sign(); +} catch (error) { + console.log("Caught missing kind error"); +} + +// โœ… Correct way +const event = new NDKEvent(ndk); +event.kind = 1; +event.content = "Hello world"; +await event.sign(); + +// Example 6: Created_at in milliseconds - will throw error +try { + const event = new NDKEvent(ndk); + event.kind = 1; + event.content = "Hello"; + event.created_at = Date.now(); // โŒ Milliseconds! + await event.sign(); +} catch (error) { + console.log("Caught milliseconds error"); +} + +// โœ… Correct way +const event2 = new NDKEvent(ndk); +event2.kind = 1; +event2.content = "Hello"; +event2.created_at = Math.floor(Date.now() / 1000); // Seconds +await event2.sign(); + +// Example 7: fetchEvents warning (doesn't throw, just warns) +console.log("\nThis will show a warning:"); +const events = await ndk.fetchEvents({ kinds: [1], limit: 5 }); +console.log("Got events:", events.size); + +// Example 8: Selectively disable checks +const ndk2 = new NDK({ + explicitRelayUrls: ["wss://relay.damus.io"], + aiGuardrails: { + skip: new Set([ + GuardrailCheckId.FETCH_EVENTS_USAGE, // Disable fetchEvents warning + GuardrailCheckId.FILTER_LARGE_LIMIT, // Disable large limit warning + ]), + }, +}); + +await ndk2.connect(); + +// โœ… This won't warn because we skipped the check +const manyEvents = await ndk2.fetchEvents({ kinds: [1], limit: 5 }); + +// Example 9: Programmatic control +ndk.aiGuardrails.skip(GuardrailCheckId.FETCH_EVENTS_USAGE); +// Now fetchEvents won't warn +const more = await ndk.fetchEvents({ kinds: [1], limit: 5 }); + +// Re-enable it +ndk.aiGuardrails.enable(GuardrailCheckId.FETCH_EVENTS_USAGE); + +// Example 10: Check what's currently skipped +const skipped = ndk.aiGuardrails.getSkipped(); +console.log("Currently skipped checks:", skipped); + +// Example 11: Disable all guardrails at runtime +ndk.aiGuardrails.setMode(false); +// Now no checks will run + +// Example 12: Production setup - disable by default +const productionNdk = new NDK({ + explicitRelayUrls: ["wss://relay.damus.io"], + aiGuardrails: process.env.NODE_ENV === "development", // Only in dev +}); + +// Example 13: LLM-friendly error handling +/** + * When an LLM generates code that triggers a guardrail, + * it should read the error message and self-correct: + */ +async function llmGeneratedCode() { + try { + const event = new NDKEvent(ndk); + // LLM forgot to set kind + event.content = "Generated content"; + await event.sign(); + } catch (error) { + console.log("\nLLM would see this error:"); + console.log(error.message); + + /** + * The error message includes: + * 1. What went wrong + * 2. How to fix it + * 3. How to disable the check if needed + * + * LLM can then: + * - Fix the code (add event.kind = 1) + * - Or skip the check if it knows better + */ + + // LLM self-corrects: + const correctedEvent = new NDKEvent(ndk); + correctedEvent.kind = 1; // Fixed! + correctedEvent.content = "Generated content"; + await correctedEvent.sign(); + console.log("\nโœ… LLM successfully self-corrected!"); + } +} + +await llmGeneratedCode(); diff --git a/ndk-core/docs/getting-started/introduction.md b/ndk-core/docs/getting-started/introduction.md new file mode 100644 index 000000000..50c63b8f5 --- /dev/null +++ b/ndk-core/docs/getting-started/introduction.md @@ -0,0 +1,35 @@ +# Getting started + +## Installation + +```sh +npm add @nostr-dev-kit/ndk +``` + +## Debugging + +NDK uses the `debug` package to assist in understanding what's happening behind the hood. If you are building a package +that runs on the server define the `DEBUG` envionment variable like + +```sh +export DEBUG='ndk:*' +``` + +or in the browser enable it by writing in the DevTools console + +```sh +localStorage.debug = 'ndk:*' +``` + +## Network Debugging + +You can construct NDK passing a netDebug callback to receive network traffic events, particularly useful for debugging applications not running in a browser. + +```ts +const netDebug = (msg: string, relay: NDKRelay, direction?: "send" | "recv") = { + const hostname = new URL(relay.url).hostname; + netDebug(hostname, msg, direction); +} + +ndk = new NDK({ netDebug }); +``` diff --git a/ndk-core/docs/getting-started/signers.md b/ndk-core/docs/getting-started/signers.md new file mode 100644 index 000000000..f20840a0b --- /dev/null +++ b/ndk-core/docs/getting-started/signers.md @@ -0,0 +1,150 @@ +# Signers + +NDK uses signers _optionally_ passed in to sign events. Note that it is possible to use NDK without signing events (e.g. [to get someone's profile](https://github.com/nostr-dev-kit/ndk-cli/blob/master/src/commands/profile.ts)). + +Signing adapters can be passed in when NDK is instantiated or later during runtime. + +### Using a NIP-07 browser extension (e.g. Alby, nos2x) + +Instatiate NDK with a NIP-07 signer + +```ts +// Import the package, NIP-07 signer and NDK event +import NDK, { NDKEvent, NDKNip07Signer } from "@nostr-dev-kit/ndk"; + +const nip07signer = new NDKNip07Signer(); +const ndk = new NDK({ signer: nip07signer }); +``` + +NDK can now ask for permission, via their NIP-07 extension, to... + +**Read the user's public key** + +```ts +nip07signer.user().then(async (user) => { + if (!!user.npub) { + console.log("Permission granted to read their public key:", user.npub); + } +}); +``` + +**Sign & publish events** + +```ts +const ndkEvent = new NDKEvent(ndk); +ndkEvent.kind = 1; +ndkEvent.content = "Hello, world!"; +ndkEvent.publish(); // This will trigger the extension to ask the user to confirm signing. +``` + +### Using a Remote Signer (NIP-46) + +#### bunker:// +* Create a `NDKNip46Signer`, optionally providing the local signer if you are restoring a connection that was already generated in your app: + +```ts +const signerConnectionString = 'bunker://....'; // ask the user for the bunker connection string +const localNsec = `nsec1....`; // Restore this from whatever storage your app is using, if you have it +const signer = NDKNip46Signer.bunker(ndk, signerConnectionString, localNsec); +const user = await signer.blockUntilReady(); + +console.log("Welcome", user.npub); + +// if you didn't have a localNsec you should store it for future sessions of your app +save(signer.localSigner.nsec) +``` + +#### nostrconnect:// +The `nostrconnect://` flow is the reverse of the `bunker://` flow; the app generates a connection string and is sent to the signer out of band (such as scanning a QR code). + +```ts +const relay = 'wss://relay.primal.net'; // choose a relay to be used to communicate with the signer +const localNsec = `nsec1....`; // Restore this from whatever storage your app is using, if you have it + +// instantiate the signer +const signer = NDKNip46Signer.nostrconnect(ndk, relay, localNsec, { + name: "", + icon: "", + perms: [ 'sign_event:1,sign_event:30023' ] // permissions to request (e.g. sign event kind:1 and kind:30023) +}); + +// Open the signer or show the signer.nostrConnectUri URI as a QR code +open(signer.nostrConnectUri); + +// Wait for the user to connect +const user = await signer.blockUntilReady(); + +console.log("Welcome", user.npub); + +// if you didn't have a localNsec you should store it for future sessions of your app +save(signer.localSigner.nsec) +``` +### Using a Private Key Signer + +NDK provides `NDKPrivateKeySigner` for managing in-memory private keys. This is useful for development, testing, or applications that manage keys locally. + +#### Basic Usage + +```ts +import { NDKPrivateKeySigner } from "@nostr-dev-kit/ndk"; + +// Generate a new private key +const signer = NDKPrivateKeySigner.generate(); +console.log("nsec:", signer.nsec); +console.log("npub:", signer.npub); + +// Or load from an existing key +const signer = new NDKPrivateKeySigner("nsec1..."); +``` + +#### Password-Protected Keys (NIP-49) + +NDK supports NIP-49 encrypted private keys (ncryptsec format) for secure storage: + +```ts +import { NDKPrivateKeySigner } from "@nostr-dev-kit/ndk"; + +// Encrypt a private key with a password +const signer = NDKPrivateKeySigner.generate(); +const password = "user-chosen-password"; +const ncryptsec = signer.encryptToNcryptsec(password); + +// Store ncryptsec securely (e.g., in localStorage) +localStorage.setItem("encrypted_key", ncryptsec); + +// Later, restore the signer from encrypted key +const storedKey = localStorage.getItem("encrypted_key"); +const restoredSigner = NDKPrivateKeySigner.fromNcryptsec(storedKey, password); +console.log("Restored pubkey:", restoredSigner.pubkey); +``` + +**Security Parameters:** + +The `encryptToNcryptsec` method accepts optional parameters for security tuning: + +```ts +// Higher security (slower, more resistant to brute force) +const ncryptsec = signer.encryptToNcryptsec(password, 20); // log_n = 20 (~2 seconds, 1GB memory) + +// Default security (faster) +const ncryptsec = signer.encryptToNcryptsec(password, 16); // log_n = 16 (~100ms, 64MB memory) + +// With key security byte (0x00, 0x01, or 0x02) +const ncryptsec = signer.encryptToNcryptsec(password, 16, 0x02); +``` + +**Direct NIP-49 utilities:** + +NDK also re-exports NIP-49 utilities for advanced use cases: + +```ts +import { nip49 } from "@nostr-dev-kit/ndk"; +import { hexToBytes, bytesToHex } from "@noble/hashes/utils"; + +// Encrypt raw bytes +const privateKeyBytes = hexToBytes("14c226dbdd865d5e..."); +const ncryptsec = nip49.encrypt(privateKeyBytes, password); + +// Decrypt to raw bytes +const decryptedBytes = nip49.decrypt(ncryptsec, password); +``` diff --git a/ndk-core/docs/getting-started/usage.md b/ndk-core/docs/getting-started/usage.md new file mode 100644 index 000000000..ae7204c38 --- /dev/null +++ b/ndk-core/docs/getting-started/usage.md @@ -0,0 +1,258 @@ +# Usage + +## Instantiate an NDK instance + +You can pass an object with several options to a newly created instance of NDK. + +- `explicitRelayUrls` โ€“ an array of relay URLs. +- `signer` - an instance of a [signer](#signers). +- `cacheAdapter` - an instance of a [Cache Adapter](#caching) +- `debug` - Debug instance to use for logging. Defaults to `debug("ndk")`. + +```ts +// Import the package +import NDK from "@nostr-dev-kit/ndk"; + +// Create a new NDK instance with explicit relays +const ndk = new NDK({ + explicitRelayUrls: ["wss://a.relay", "wss://another.relay"], +}); +``` + +If the signer implements the `getRelays()` method, NDK will use the relays returned by that method as the explicit relays. + +```ts +// Import the package +import NDK, { NDKNip07Signer } from "@nostr-dev-kit/ndk"; + +// Create a new NDK instance with just a signer (provided the signer implements the getRelays() method) +const nip07signer = new NDKNip07Signer(); +const ndk = new NDK({ signer: nip07signer }); +``` + +Note: In normal client use, it's best practice to instantiate NDK as a singleton class. [See more below](#architecture-decisions--suggestions). + +## Connecting + +After you've instatiated NDK, you need to tell it to connect before you'll be able to interact with any relays. + +```ts +// Import the package +import NDK from "@nostr-dev-kit/ndk"; + +// Create a new NDK instance with explicit relays +const ndk = new NDK({ + explicitRelayUrls: ["wss://a.relay", "wss://another.relay"], +}); +// Now connect to specified relays +await ndk.connect(); +``` + +## Creating Users + +NDK provides flexible ways to fetch user objects, including support for NIP-19 encoded identifiers and NIP-05 addresses: + +```typescript +// From hex pubkey +const user1 = await ndk.fetchUser("3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d"); + +// From npub (NIP-19 encoded) +const user2 = await ndk.fetchUser("npub1n0sturny6w9zn2wwexju3m6asu7zh7jnv2jt2kx6tlmfhs7thq0qnflahe"); + +// From nprofile (includes relay hints) +const user3 = await ndk.fetchUser("nprofile1qqsrhuxx8l9ex335q7he0f09aej04zpazpl0ne2cgukyawd24mayt8gpp4mhxue69uhhytnc9e3k7mgpz4mhxue69uhkg6nzv9ejuumpv34kytnrdaksjlyr9p"); + +// From NIP-05 identifier +const user4 = await ndk.fetchUser("pablo@test.com"); +const user5 = await ndk.fetchUser("test.com"); // Uses _@test.com + +// The method automatically detects the format +const user6 = await ndk.fetchUser("deadbeef..."); // Assumes hex pubkey +``` + +Note: `fetchUser` is async and returns a Promise. For NIP-05 lookups, it may return `undefined` if the address cannot be resolved. + +## Working with NIP-19 Identifiers + +NDK re-exports NIP-19 utilities for encoding and decoding Nostr identifiers: + +```typescript +import { nip19 } from '@nostr-dev-kit/ndk'; + +// Encode a pubkey as npub +const npub = nip19.npubEncode("3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d"); + +// Decode any NIP-19 identifier +const decoded = nip19.decode("npub1..."); +console.log(decoded.type); // "npub" +console.log(decoded.data); // hex pubkey + +// Encode events +const nevent = nip19.neventEncode({ + id: eventId, + relays: ["wss://relay.example.com"], + author: authorPubkey +}); +``` + +See the [NIP-19 tutorial](/tutorial/nip19.html) for comprehensive examples and use cases. + +## Usage with React Hooks (`ndk-hooks`) + +When using the `ndk-hooks` package in a React application, the initialization process involves creating the NDK instance and then using the `useNDKInit` hook to make it available to the rest of your application via Zustand stores. + +This hook ensures that both the core NDK store and dependent stores (like the user profiles store) are properly initialized with the NDK instance. + +It's recommended to create and connect your NDK instance outside of your React components, potentially in a dedicated setup file or at the root of your application. Then, use the `useNDKInit` hook within your main App component or a context provider to initialize the stores once the component mounts. + +```tsx +import React, { useEffect } from 'react'; // Removed useState +import NDK from '@nostr-dev-kit/ndk'; +import { useNDKInit } from '@nostr-dev-kit/ndk-hooks'; // Assuming package name + +// 1. Configure your NDK instance (e.g., in src/ndk.ts or similar) +const ndk = new NDK({ + explicitRelayUrls: ['wss://relay.damus.io', 'wss://relay.primal.net'], + // Add signer or cache adapter if needed +}); + +// 2. Connect the instance immediately +ndk.connect() + .then(() => console.log('NDK connected')) + .catch((e) => console.error('NDK connection error:', e)); + +// Example: App component or Context Provider that initializes NDK stores +function App() { + const initializeNDK = useNDKInit(); // Hook returns the function directly + + useEffect(() => { + // 3. Initialize stores once the component mounts + initializeNDK(ndk); + }, [initializeNDK]); // Dependency ensures this runs if initializeNDK changes, though unlikely + + // Your application components can now use other ndk-hooks + // No need to wait for connection state here, as hooks handle NDK readiness internally + return ( +
+ {/* ... Your app content using useProfile, useSubscribe, etc. ... */} +
+ ); +} + +export default App; +``` + +**Key Points:** + +* Create and configure your `NDK` instance globally or outside components. +* Call `ndk.connect()` immediately after creation. Connection happens in the background. +* In your main App or Provider component, get the `initializeNDK` function from `useNDKInit`. +* Use `useEffect` with an empty dependency array (or `[initializeNDK]`) to call `initializeNDK(ndk)` once on mount. +* This sets up the necessary Zustand stores. Other `ndk-hooks` will access the initialized `ndk` instance from the store and handle its readiness internally. + +--- + + +## Architecture decisions & suggestions + +- Users of NDK should instantiate a single NDK instance. +- That instance tracks state with all relays connected, explicit and otherwise. +- All relays are tracked in a single pool that handles connection errors/reconnection logic. +- RelaySets are assembled ad-hoc as needed depending on the queries set, although some RelaySets might be long-lasting, like the `explicitRelayUrls` specified by the user. +- RelaySets are always a subset of the pool of all available relays. + + +## Subscribing to Events + +Once connected, you can subscribe to events using `ndk.subscribe()`. You provide filters to specify the events you're interested in. + +### Preferred Method: Direct Event Handlers + +The **recommended** way to handle events is to provide handler functions directly when calling `ndk.subscribe()`. This is done using the third argument (`autoStart`), which accepts an object containing `onEvent`, `onEvents`, and/or `onEose` callbacks. + +**Why is this preferred?** Subscriptions can start receiving events (especially from a fast cache) almost immediately after `ndk.subscribe()` is called. By providing handlers directly, you ensure they are attached *before* any events are emitted, preventing potential race conditions where you might miss the first few events if you attached handlers later using `.on()`. + +```typescript +// Example with default relay calculation +ndk.subscribe( + { kinds: [1], authors: [pubkey] }, // Filters + { closeOnEose: true }, // Options (no explicit relays specified) + { // Direct handlers via autoStart parameter (now the 3rd argument) + onEvent: (event: NDKEvent, relay?: NDKRelay) => { + // Called for events received from relays after the initial cache load (if onEvents is used) + console.log("Received event from relay (id):", event.id); + }, + onEvents: (events: NDKEvent[]) => { // Parameter renamed to 'events' + console.log(`Received ${events.length} events from cache initially.`); + }, + onEose: (subscription: NDKSubscription) => { + console.log("Subscription reached EOSE:", subscription.internalId); + } + } +); + +// Example specifying explicit relays using relayUrls option +ndk.subscribe( + { kinds: [0], authors: [pubkey] }, // Filters + { // Options object now includes relayUrls + closeOnEose: true, + relayUrls: ["wss://explicit1.relay", "wss://explicit2.relay"] + }, + { // Direct handlers + onEvent: (event: NDKEvent) => { /* ... */ } + } +); + +// Example specifying explicit relays using relaySet option +const explicitRelaySet = NDKRelaySet.fromRelayUrls(["wss://explicit.relay"], ndk); +ndk.subscribe( + { kinds: [7], authors: [pubkey] }, // Filters + { // Options object now includes relaySet + closeOnEose: true, + relaySet: explicitRelaySet + }, + { // Direct handlers + onEvent: (event: NDKEvent) => { /* ... */ } + } +); +``` + +### Efficient Cache Handling with `onEvents` + +Using the `onEvents` handler provides an efficient way to process events loaded from the cache. When you provide `onEvents`: + +1. If NDK finds matching events in its cache *synchronously* when the subscription starts, `onEvents` is called **once** with an array of all those cached events. +2. The `onEvent` handler is **skipped** for this initial batch of cached events. +3. `onEvent` will still be called for any subsequent events received from relays or later asynchronous cache updates. + +This is ideal for scenarios like populating initial UI state, as it allows you to process the cached data in a single batch, preventing potentially numerous individual updates that would occur if `onEvent` were called for each cached item. + +If you *don't* provide `onEvents`, the standard `onEvent` handler will be triggered for every event, whether it comes from the cache or a relay. + +### Alternative Method: Attaching Handlers with `.on()` + +You can also attach event listeners *after* creating the subscription using the `.on()` method. While functional, be mindful of the potential race condition mentioned above, especially if you rely on immediate cache results. + +```typescript +// Subscribe using default relay calculation +const subscription = ndk.subscribe( + { kinds: [1], authors: [pubkey] }, + { closeOnEose: true } // Options +); + +// Subscribe using explicit relays via options +const subscriptionWithRelays = ndk.subscribe( + { kinds: [0], authors: [pubkey] }, + { relayUrls: ["wss://explicit.relay"] } // Options with explicit relays +); + +// Attach handlers later +subscription.on("event", (event) => { + console.log("Received event:", event.id); +}); +subscription.on("eose", () => { + console.log("Initial events loaded"); +}); + +// Remember to stop the subscription when it's no longer needed +// setTimeout(() => subscription.stop(), 5000); diff --git a/ndk-core/docs/index.md b/ndk-core/docs/index.md new file mode 100644 index 000000000..52fac990a --- /dev/null +++ b/ndk-core/docs/index.md @@ -0,0 +1,18 @@ +--- +# https://vitepress.dev/reference/default-theme-home-page +layout: home + +hero: + name: "NDK Documentation" + tagline: "Nostr Development Kit Docs" + actions: + - theme: brand + text: Getting Started + link: /getting-started/introduction.html + - theme: secondary + text: References + link: https://github.com/nostr-dev-kit/ndk/blob/master/REFERENCES.md + +--- + +NDK is a nostr development kit that makes the experience of building Nostr-related applications, whether they are relays, clients, or anything in between, better, more reliable and overall nicer to work with than existing solutions. \ No newline at end of file diff --git a/ndk-core/docs/internals/subscriptions.md b/ndk-core/docs/internals/subscriptions.md new file mode 100644 index 000000000..9c92ccca9 --- /dev/null +++ b/ndk-core/docs/internals/subscriptions.md @@ -0,0 +1,292 @@ +# Subscriptions Lifecycle + +When an application creates a subscription a lot of things happen under the hood. + +Say we want to see `kind:1` events from pubkeys `123`, `456`, and `678`. + +```ts +const subscription = ndk.subscribe({ kinds: [1], authors: ["123", "456", "678"] }); +``` + +Since the application level didn't explicitly provide a relay-set, which is the most common use case, NDK will calculate a relay set based on the outbox model plus a variety of some other factors. + +## Relay Selection for Authors + +When a subscription includes an `authors` filter, NDK uses the outbox model to determine which relays to query for each author. By default, NDK will query **2 relays** for each author, but this can be customized using the `relayGoalPerAuthor` option: + +```ts +// Query 3 relays for each author instead of the default 2 +const subscription = ndk.subscribe( + { kinds: [1], authors: ["123", "456", "678"] }, + { relayGoalPerAuthor: 3 } +); + +// Use all available relays for each author +const subscription = ndk.subscribe( + { kinds: [1], authors: ["123", "456", "678"] }, + { relayGoalPerAuthor: Infinity } +); +``` + +Higher values improve redundancy and reduce the chance of missing events, but increase bandwidth usage and the number of relay connections. Lower values reduce resource usage but may miss events if a relay is down or doesn't have the event. Setting `relayGoalPerAuthor: Infinity` will query all available relays for each author. + +So the first thing we'll do before talking to relays is, decide to _which_ relays we should talk to. + +The `calculateRelaySetsFromFilters` function will take care of this and provide us with a map of relay URLs and filters for each relay. + +This means that the query, as specified by the client might be broken into distinct queries specialized for the different relays. + +For example, if we have 3 relays, and the query is for `kind:1` events from pubkeys `a` and `b`, the `calculateRelaySetsFromFilters` function might return something like this: + +```ts +{ + "wss://relay1": { kinds: [1], authors: [ "a" ] }, + "wss://relay2": { kinds: [1], authors: [ "b" ] }, +} +``` + +```mermaid +flowchart TD + Client -->|"kinds: [1], authors: [a, b]"| Subscription1 + Subscription1 -->|"kinds: [1], authors: [a]"| wss://relay1 + Subscription1 -->|"kinds: [1], authors: [b]"| wss://relay2 +``` + +## Subscription bundling + +Once the subscription has been split into the filters each relay should receive, the filters are sent to the individual `NDKRelay`'s `NDKRelaySubscriptionManager` instances. + +`NDKRelaySubscriptionManager` is responsible for keeping track of the active and scheduled subscriptions that are pending to be executed within an individual relay. + +This is an important aspect to consider: + +> `NDKSubscription` have a different lifecycle than `NDKRelaySubscription`. For example, a subscription that is set to close after EOSE might still be active within the `NDKSubscription` lifecycle, but it might have been already been closed within the `NDKRelaySubscription` lifecycle, since NDK attempts to keep the minimum amount of open subscriptions at any given time. + +## NDKRelaySubscription + +Most NDK subscriptions (by default) are set to be executed with a grouping delay. Will cover what this looks like in practice later, but for now, let's understand than when the `NDKRelaySubscriptionManager` receives an order, it might not execute it right away. + +The different filters that can be grouped together (thus executed as a single `REQ` within a relay) are grouped within the same `NDKRelaySubscription` instance and the execution scheduler is computed respecting what each individual `NDKSubscription` has requested. + +(For example, if a subscription with a `groupingDelay` of `at-least` 500 millisecond has been grouped with another subscription with a `groupingDelay` of `at-least` 1000 milliseconds, the `NDKRelaySubscriptionManager` will wait 1000 ms before sending the `REQ` to this particular relay). + +### Execution + +Once the filter is executed at the relay level, the `REQ` is submitted into that relay's `NDKRelayConnectivity` instance, which will take care of monitoring for responses for this particular REQ and communicate them back into the `NDKRelaySubscription` instance. + +Each `EVENT` that comes back as a response to our `REQ` within this `NDKRelaySubscription` instance is sent to the top-level `NDKSubscriptionManager`. This manager tracks ALL active subscriptions and when events come in dispatches the event to all `NDKSubscription`s interested in this event. + +# Example + +If an application requests `kind:1` of pubkeys `123`, `456`, and `789`. It creates an `NDKSubscription`: + +```ts +ndk.subscribe( + { kinds: [1], authors: ["123", "456", "789"] }, + { groupableDelay: 500, groupableDelayType: "at-least" } +); +// results in NDKSubscription1 with filters { kinds: [1], authors: [ "123", "456", "789" ] } +``` + +Some other part of the application requests a kind:7 from pubkey `123` at the same time. + +```ts +ndk.subscribe( + { kinds: [7], authors: ["123"] }, + { groupableDelay: 500, groupableDelayType: "at-most" } +); +// results in NDKSubscription2 with filters { kinds: [7], authors: [ "123" ] } +``` + +```mermaid +flowchart TD + subgraph Subscriptions Lifecycle + A[Application] -->|"kinds: [1], authors: [123, 456, 678], groupingDelay: at-least 500ms"| B[NDKSubscription1] + + A2[Application] -->|"kinds: [7], authors: [123], groupingDelay: at-most 1000ms"| B2[NDKSubscription2] + end +``` + +Both subscriptions have their relayset calculated by NDK and, the resulting filters are sent into the `NDKRelaySubscriptionManager`, which will decide what, and how filters can be grouped. + +```mermaid +flowchart TD + subgraph Subscriptions Lifecycle + A[Application] -->|"kinds: [1], authors: [123, 456, 678], groupingDelay: at-least 500ms"| B[NDKSubscription1] + B --> C{Calculate Relay Sets} + + A2[Application] -->|"kinds: [7], authors: [123], groupingDelay: at-most 1000ms"| B2[NDKSubscription2] + B2 --> C2{Calculate Relay Sets} + end + + subgraph Subscription Bundling + C -->|"kinds: [1], authors: [123]"| E1[wss://relay1 NDKRelaySubscriptionManager] + C -->|"kinds: [1], authors: [456]"| E2[wss://relay2 NDKRelaySubscriptionManager] + C -->|"kinds: [1], authors: [678]"| E3[wss://relay3 NDKRelaySubscriptionManager] + + C2 -->|"kinds: [7], authors: [123]"| E1 + end +``` + +The `NDKRelaySubscriptionManager` will create `NDKRelaySubscription` instances, or add filters to them if `NDKRelaySubscription` with the same filter fingerprint exists. + +```mermaid +flowchart TD + subgraph Subscriptions Lifecycle + A[Application] -->|"kinds: [1], authors: [123, 456, 678], groupingDelay: at-least 500ms"| B[NDKSubscription1] + B --> C{Calculate Relay Sets} + + A2[Application] -->|"kinds: [7], authors: [123], groupingDelay: at-most 1000ms"| B2[NDKSubscription2] + B2 --> C2{Calculate Relay Sets} + end + + subgraph Subscription Bundling + C -->|"kinds: [1], authors: [123]"| E1[wss://relay1 NDKRelaySubscriptionManager] + C -->|"kinds: [1], authors: [456]"| E2[wss://relay2 NDKRelaySubscriptionManager] + C -->|"kinds: [1], authors: [678]"| E3[wss://relay3 NDKRelaySubscriptionManager] + + C2 -->|"kinds: [7], authors: [123]"| E1 + + E1 -->|"Grouping Delay: at-most 1000ms"| F1[NDKRelaySubscription] + E2 -->|"Grouping Delay: at-least 500ms"| F2[NDKRelaySubscription] + E3 -->|"Grouping Delay: at-least 500ms"| F3[NDKRelaySubscription] + end +``` + +Each individual `NDKRelaySubscription` computes the execution schedule of the filters it has received and sends them to the `NDKRelayConnectivity` instance, which in turns sends the `REQ` to the relay. + +```mermaid +flowchart TD + subgraph Subscriptions Lifecycle + A[Application] -->|"kinds: [1], authors: [123, 456, 678], groupingDelay: at-least 500ms"| B[NDKSubscription1] + B --> C{Calculate Relay Sets} + + A2[Application] -->|"kinds: [7], authors: [123], groupingDelay: at-most 1000ms"| B2[NDKSubscription2] + B2 --> C2{Calculate Relay Sets} + end + + subgraph Subscription Bundling + C -->|"kinds: [1], authors: [123]"| E1[wss://relay1 NDKRelaySubscriptionManager] + C -->|"kinds: [1], authors: [456]"| E2[wss://relay2 NDKRelaySubscriptionManager] + C -->|"kinds: [1], authors: [678]"| E3[wss://relay3 NDKRelaySubscriptionManager] + + C2 -->|"kinds: [7], authors: [123]"| E1 + + E1 -->|"Grouping Delay: at-most 1000ms"| F1[NDKRelaySubscription] + E2 -->|"Grouping Delay: at-least 500ms"| F2[NDKRelaySubscription] + E3 -->|"Grouping Delay: at-least 500ms"| F3[NDKRelaySubscription] + + F1 -->|"REQ: kinds: [1, 7], authors: [123]"| G1[NDKRelayConnectivity] + F2 -->|"REQ: kinds: [1], authors: [456]"| G2[NDKRelayConnectivity] + F3 -->|"REQ: kinds: [1], authors: [678]"| G3[NDKRelayConnectivity] + end + + subgraph Execution + G1 -->|"Send REQ to wss://relay1 after 1000ms"| R1[Relay1] + G2 -->|"Send REQ to wss://relay2 after 500ms"| R2[Relay2] + G3 -->|"Send REQ to wss://relay3 after 500ms"| R3[Relay3] + end +``` + +As the events come from the relays, `NDKRelayConnectivity` will send them back to the `NDKRelaySubscription` instance, which will compare the event with the filters of the `NDKSubscription` instances that have been grouped together and send the received event back to the correct `NDKSubscription` instance. + +```mermaid +flowchart TD + subgraph Subscriptions Lifecycle + A[Application] -->|"kinds: [1], authors: [123, 456, 678], groupingDelay: at-least 500ms"| B[NDKSubscription1] + B --> C{Calculate Relay Sets} + + A2[Application] -->|"kinds: [7], authors: [123], groupingDelay: at-most 1000ms"| B2[NDKSubscription2] + B2 --> C2{Calculate Relay Sets} + end + + subgraph Subscription Bundling + C -->|"kinds: [1], authors: [123]"| E1[wss://relay1 NDKRelaySubscriptionManager] + C -->|"kinds: [1], authors: [456]"| E2[wss://relay2 NDKRelaySubscriptionManager] + C -->|"kinds: [1], authors: [678]"| E3[wss://relay3 NDKRelaySubscriptionManager] + + C2 -->|"kinds: [7], authors: [123]"| E1 + + E1 -->|"Grouping Delay: at-most 1000ms"| F1[NDKRelaySubscription] + E2 -->|"Grouping Delay: at-least 500ms"| F2[NDKRelaySubscription] + E3 -->|"Grouping Delay: at-least 500ms"| F3[NDKRelaySubscription] + + F1 -->|"REQ: kinds: [1, 7], authors: [123]"| G1[NDKRelayConnectivity] + F2 -->|"REQ: kinds: [1], authors: [456]"| G2[NDKRelayConnectivity] + F3 -->|"REQ: kinds: [1], authors: [678]"| G3[NDKRelayConnectivity] + end + + subgraph Execution + G1 -->|"Send REQ to wss://relay1 after 1000ms"| R1[Relay1] + G2 -->|"Send REQ to wss://relay2 after 500ms"| R2[Relay2] + G3 -->|"Send REQ to wss://relay3 after 500ms"| R3[Relay3] + + R1 -->|"EVENT: kinds: [1]"| H1[NDKRelaySubscription] + R1 -->|"EVENT: kinds: [7]"| H2[NDKRelaySubscription] + R2 -->|"EVENT"| H3[NDKRelaySubscription] + R3 -->|"EVENT"| H4[NDKRelaySubscription] + + H1 -->|"Matched Filters: kinds: [1]"| I1[NDKSubscription1] + H2 -->|"Matched Filters: kinds: [7]"| I2[NDKSubscription2] + H3 -->|"Matched Filters: kinds: [1]"| I1 + H4 -->|"Matched Filters: kinds: [1]"| I1 + end +``` + +## Handling Subscription Events + +When creating a subscription using `ndk.subscribe`, you can provide handlers for different stages of the subscription lifecycle directly within the options or the `autoStart` parameter. + +```typescript +interface NDKSubscriptionEventHandlers { + /** + * Called for each individual event received after the initial cache load (if applicable) + * or for all events if onEvents is not provided. + */ + onEvent?: (event: NDKEvent, relay?: NDKRelay) => void; + + /** + * Called *once* with all events found synchronously in the cache when the subscription starts. + * If this handler is provided, `onEvent` will *not* be called for these initial cached events. + * This is useful for bulk processing or batching UI updates. + */ + onEvents?: (events: NDKEvent[]) => void; + + /** + * Called when the subscription receives an EOSE (End of Stored Events) marker + * from all connected relays for this subscription request. + */ + onEose?: (sub: NDKSubscription) => void; +} + +// Example passing handlers directly (preferred method) +ndk.subscribe( + filters, + { // Options can include explicit relays now + closeOnEose: true, + // relayUrls: ["wss://explicit.relay"] // Optionally specify relays here + }, + { // Pass handlers via the autoStart parameter (now the 3rd argument) + onEvent: (event) => { + console.log("Received event:", event.id); + }, + onEvents: (events) => { // Renamed parameter + console.log(`Received ${events.length} events from cache initially.`); + // Process the batch of cached events here + }, + onEose: (subscription) => { + console.log("Subscription reached EOSE:", subscription.internalId); + } + } +); +``` + +### Bulk Cache Event Handling (`onEvents`) + +A key feature is the behavior when using the `onEvents` handler. If NDK has a cache adapter configured and finds events matching the subscription filter synchronously in the cache upon starting the subscription: + +1. The `onEvents` handler will be called exactly once with an array containing all these cached `NDKEvent` objects. +2. The regular `onEvent` handler will *not* be called for this initial batch of cached events. +3. After this initial batch, `onEvent` will be called for any subsequent events received from relays or asynchronous cache updates. + +This allows applications to efficiently process the initial state from the cache in one go, which can be particularly beneficial for UI frameworks to avoid multiple re-renders that might occur if `onEvent` were called for each cached item individually. If `onEvents` is *not* provided, `onEvent` will be called for every event, including those from the cache. diff --git a/ndk-core/docs/migration/2.12-to-2.13.md b/ndk-core/docs/migration/2.12-to-2.13.md new file mode 100644 index 000000000..3b1c8b427 --- /dev/null +++ b/ndk-core/docs/migration/2.12-to-2.13.md @@ -0,0 +1,59 @@ +# Migration Guide: NDK v2.12.x to v2.13.0 + +This guide outlines the breaking changes introduced in NDK version 2.13.0 and how to update your code. + +## Breaking Changes + +### `ndk.subscribe()` Signature Change + +The way explicit relays are specified for a subscription has changed. + +**Before (v2.12.x and earlier):** + +The `ndk.subscribe()` method accepted an optional `NDKRelaySet` as its third parameter: + +```typescript +// Old signature +ndk.subscribe( + filters: NDKFilter | NDKFilter[], + opts?: NDKSubscriptionOptions, + relaySet?: NDKRelaySet, // Explicit relay set as 3rd argument + autoStart?: boolean | NDKSubscriptionEventHandlers +): NDKSubscription; + +// Example usage +const explicitRelaySet = NDKRelaySet.fromRelayUrls(["wss://explicit.relay"], ndk); +ndk.subscribe(filters, { closeOnEose: true }, explicitRelaySet); +``` + +**After (v2.13.0):** + +The third parameter (`relaySet`) has been **removed**. Instead, you now specify explicit relays directly within the `NDKSubscriptionOptions` (the second parameter) using either: + +1. `relaySet`: Pass an existing `NDKRelaySet` instance. +2. `relayUrls`: Pass an array of relay URLs. NDK will create an `NDKRelaySet` from these URLs internally. + +If both `relaySet` and `relayUrls` are provided in the options, `relaySet` takes precedence. + +```typescript +// New signature +ndk.subscribe( + filters: NDKFilter | NDKFilter[], + opts?: NDKSubscriptionOptions, // Includes optional relaySet or relayUrls + autoStart?: boolean | NDKSubscriptionEventHandlers +): NDKSubscription; + +// Example usage with relaySet option +const explicitRelaySet = NDKRelaySet.fromRelayUrls(["wss://explicit.relay"], ndk); +ndk.subscribe(filters, { closeOnEose: true, relaySet: explicitRelaySet }); + +// Example usage with relayUrls option +ndk.subscribe(filters, { closeOnEose: true, relayUrls: ["wss://explicit.relay"] }); +``` + +**Migration Steps:** + +1. Identify all calls to `ndk.subscribe()` where you were passing an `NDKRelaySet` as the third argument. +2. Move the `NDKRelaySet` instance into the `opts` object (the second argument) under the `relaySet` key. +3. Alternatively, if you were creating the `NDKRelaySet` just to pass it in, you can simplify by passing the array of URLs directly using the `relayUrls` key in the `opts` object. +4. Ensure the `autoStart` argument (if used) is now the third argument. \ No newline at end of file diff --git a/ndk-core/docs/tutorial/auth.md b/ndk-core/docs/tutorial/auth.md new file mode 100644 index 000000000..664c6cdd8 --- /dev/null +++ b/ndk-core/docs/tutorial/auth.md @@ -0,0 +1,29 @@ +# Relay Authentication + +NIP-42 defines that relays can request authentication from clients. + +NDK makes working with NIP-42 very simple. NDK uses an `NDKAuthPolicy` callback to provide a way to handle authentication requests. + +* Relays can have specific `NDKAuthPolicy` functions. +* NDK can be configured with a default `relayAuthDefaultPolicy` function. +* NDK provides some generic policies: + * `NDKAuthPolicies.signIn`: Authenticate to the relay (using the `ndk.signer` signer). + * `NDKAuthPolicies.disconnect`: Immediately disconnect from the relay if asked to authenticate. + +```ts +import { NDK, NDKRelayAuthPolicies } from "@nostr-dev-kit/ndk"; + +const ndk = new NDK(); +ndk.addExplicitRelay("wss://relay.f7z.io", NDKRelayAuthPolicies.signIn({ndk})); +``` + +Clients should typically allow their users to choose where to authenticate. This can be accomplished by returning the decision the user made from the `NDKAuthPolicy` function. + +```ts +import { NDK, NDKRelayAuthPolicies } from "@nostr-dev-kit/ndk"; + +const ndk = new NDK(); +ndk.relayAuthDefaultPolicy = (relay: NDKRelay) => { + return confirm(`Authenticate to ${relay.url}?`); +}; +``` diff --git a/ndk-core/docs/tutorial/filter-validation.md b/ndk-core/docs/tutorial/filter-validation.md new file mode 100644 index 000000000..7750795de --- /dev/null +++ b/ndk-core/docs/tutorial/filter-validation.md @@ -0,0 +1,218 @@ +# Filter Validation + +NDK provides automatic validation and fixing of subscription filters to prevent runtime errors in cache adapters and relay communications. + +## The Problem + +When creating filters programmatically, it's easy to accidentally include `undefined` values in arrays, especially when using `.map()` operations or conditional logic. These undefined values can cause: + +- Runtime errors in cache adapters (especially SQLite WASM: "Wrong API use: tried to bind a value of an unknown type") +- Invalid requests sent to relays +- Unexpected filtering behavior + +## Filter Validation Modes + +NDK supports three validation modes configurable at the instance level: + +### 1. Validate Mode (Default) + +Throws an error when invalid filters are detected, helping you catch bugs early in development. + +```typescript +const ndk = new NDK({ + filterValidationMode: "validate" // This is the default +}); + +// This will throw an error +const filter = { + authors: ["valid_pubkey", undefined], // โŒ Contains undefined + kinds: [1, 30023] +}; + +ndk.subscribe(filter); // Throws: "Invalid filter(s) detected..." +``` + +### 2. Fix Mode + +Automatically removes invalid values from filters, useful for production environments where you want to be lenient with data. + +```typescript +const ndk = new NDK({ + filterValidationMode: "fix" +}); + +// Filter with undefined values +const filter = { + authors: ["pubkey1", undefined, "pubkey2"], + kinds: [1, undefined, 30023], + "#t": ["bitcoin", undefined, "nostr"] +}; + +// NDK will automatically clean the filter before sending to relays: +// { +// authors: ["pubkey1", "pubkey2"], +// kinds: [1, 30023], +// "#t": ["bitcoin", "nostr"] +// } +``` + +### 3. Ignore Mode + +Skip validation entirely (legacy behavior). + +```typescript +const ndk = new NDK({ + filterValidationMode: "ignore" +}); + +// Filters are passed as-is, may cause runtime errors +``` + +## What is Validated? + +The filter validation checks for: + +### 1. Undefined Values +- Removes or reports `undefined` in any filter array + +### 2. Type Validation +- **authors**: Must be strings and valid 64-character hex pubkeys +- **ids**: Must be strings and valid 64-character hex event IDs +- **kinds**: Must be numbers, integers between 0-65535 +- **Tag filters** (#e, #p, etc.): Must be strings + - #e and #p tags: Must be valid 64-character hex strings + +### 3. Invalid Data +- Non-string values in string arrays +- Non-numeric values in kinds array +- Invalid hex strings where required +- Out-of-range kind numbers + +## Common Scenarios + +### Filtering Deleted Users + +```typescript +// Problem: Some users might be deleted/undefined +const userIds = ["user1", "deleted_user", "user3"]; +const filter = { + authors: userIds.map(id => getUserPubkey(id)), // May include undefined + kinds: [1] +}; + +// Solution 1: Use fix mode +const ndk = new NDK({ filterValidationMode: "fix" }); +ndk.subscribe(filter); // Undefined values automatically removed + +// Solution 2: Pre-filter your data +const validAuthors = userIds + .map(id => getUserPubkey(id)) + .filter(pubkey => pubkey !== undefined); +const filter = { + authors: validAuthors, + kinds: [1] +}; +``` + +### Conditional Kind Inclusion + +```typescript +// Problem: Conditional logic might produce undefined +const filter = { + kinds: [ + 1, // Text note + includeArticles ? 30023 : undefined, // May be undefined + includeVideos ? 30024 : undefined + ] +}; + +// Solution: Filter out undefined values +const kinds = [ + 1, + includeArticles && 30023, + includeVideos && 30024 +].filter(Boolean); + +const filter = { kinds }; +``` + +## Migration Guide + +If you're upgrading NDK and encountering validation errors: + +### Option 1: Fix Your Filters (Recommended) + +```typescript +// Before +const filter = { + authors: possiblyUndefinedArray, + kinds: [1] +}; + +// After +const filter = { + authors: possiblyUndefinedArray?.filter(a => a !== undefined), + kinds: [1] +}; +``` + +### Option 2: Use Fix Mode Temporarily + +```typescript +const ndk = new NDK({ + filterValidationMode: "fix" // Auto-fix while you update your code +}); +``` + +### Option 3: Use Ignore Mode (Not Recommended) + +```typescript +const ndk = new NDK({ + filterValidationMode: "ignore" // Maintain old behavior +}); +``` + +## Programmatic Validation + +You can also validate or fix filters manually using the exported utilities: + +```typescript +import { processFilters, NDKFilterValidationMode } from "@nostr-dev-kit/ndk"; + +// Validate filters (throws on invalid) +try { + const validatedFilters = processFilters( + filters, + NDKFilterValidationMode.VALIDATE + ); +} catch (error) { + console.error("Invalid filters:", error.message); +} + +// Fix filters (returns cleaned filters) +const cleanedFilters = processFilters( + filters, + NDKFilterValidationMode.FIX +); +``` + +## Best Practices + +1. **Use validate mode during development** to catch filter issues early +2. **Consider fix mode for production** if you're dealing with dynamic/user-generated filters +3. **Always filter out undefined values** when using `.map()` operations +4. **Validate hex strings** before adding them to filters +5. **Use TypeScript** to catch type issues at compile time + +## Error Messages + +In validate mode, you'll get detailed error messages showing exactly what's wrong: + +``` +Invalid filter(s) detected: +Filter[0].authors[1] is undefined +Filter[0].kinds[2] is not a number (got string) +Filter[0].#p[0] is not a valid 64-char hex string: "invalid" +``` + +This helps you quickly identify and fix the source of invalid data. \ No newline at end of file diff --git a/ndk-core/docs/tutorial/local-first.md b/ndk-core/docs/tutorial/local-first.md new file mode 100644 index 000000000..aa9efa935 --- /dev/null +++ b/ndk-core/docs/tutorial/local-first.md @@ -0,0 +1,63 @@ +# Local first + +NDK allows for local-first software. + +This mode of operation depends on a few key things: +* A cache adapter must be present +* Events `event:publish-failed` should be handled by the application + +A few considerations: +* Failed events are automatically retried by NDK +* Handling of failed events is up to the application; handling here exclusively refers to notifying the user and updating the UI accordingly + +## Blocked publishing +The default behavior when publishing an event will make `event.publish()` block +until the event has been published or a failure has ocurred. +```ts +const event = new NDKEvent(ndk, { kind: 1, content: 'Blocking event' }); +const publishedToRelays = await event.publish(); +console.log(publishedToRelays); // relays where the event has published to +``` + +## Optimistic publishing +If you want to publish an event without waiting for it to be published, you can use the `event.publish()` method. +```ts +const event = new NDKEvent(ndk, { kind: 1, content: 'Optimistic event' }); +event.publish(); +``` + +When using a cache adapter that supports unpublished event tracking (like `NDKCacheAdapterDexie`), the event will be first +written to the cache and then published to relays. When a minimal amount of relays have successfully received the event, the event will be removed from the cache. + +With this technique you can fire and forget event publshing. + +## Handling persistent failures +When an event fails to publish, you can handle the failure by listening to the `event.failed` event. + +You should handle this event to notify the user that the event has failed to publish and update the UI accordingly. + +```ts +// application-wide +function handlePublishingFailures(event: NDKEvent, error: NDKPublishError) { + console.log(`Event ${event.id} failed to publish`, { publishedToRelays: error.publishedToRelays }); +} + +ndk.on("event:publish-failed", handlePublishingFailures); +const event = new NDKEvent(ndk, { kind: 1, content: 'Failing event' }); +event.publish(); +``` + +## Querying cached failed events +Cache adapters with support for failed publish tracking can be queried via the `getUnpublishedEvents` interface. + +```ts +const failedEvents = ndk.cachedAdapter.getUnpublishedEvents() + +console.log(failedEvents.length + " events have not published before; trying now"); +failedEvents.forEach((event) => event.publish()); +``` + +When an event successfully publishes, the event will emit `published`. + +## Handling retries +When booting up your application, NDK will automatically reattempt to publish any events that have failed to publish in the past. diff --git a/ndk-core/docs/tutorial/mute-filtering.md b/ndk-core/docs/tutorial/mute-filtering.md new file mode 100644 index 000000000..28f38df67 --- /dev/null +++ b/ndk-core/docs/tutorial/mute-filtering.md @@ -0,0 +1,130 @@ +# Mute Filtering + +NDK provides automatic mute filtering for subscriptions, allowing applications to automatically hide content from muted users or muted events. + +## How It Works + +When you set an active user, NDK automatically: +1. Fetches the user's mute list (kind 10000, per [NIP-51](https://nips.nostr.com/51)) +2. Populates `ndk.mutedIds` with muted pubkeys and event IDs +3. Applies mute filtering to all subscriptions by default + +## Default Behavior + +By default, all subscriptions automatically filter out muted events: + +```typescript +// Muted events are automatically filtered out +const sub = ndk.subscribe({ kinds: [1], limit: 50 }); +sub.on('event', (event) => { + // Will never receive events from muted authors or muted event IDs + console.log(event.content); +}); +``` + +## Including Muted Events + +Sometimes you need to display muted content (e.g., in moderation interfaces or settings pages): + +```typescript +// Opt-in to include muted events +const sub = ndk.subscribe( + { kinds: [1], limit: 50 }, + { includeMuted: true } +); + +sub.on('event', (event) => { + if (event.muted()) { + // This is a muted event - render with special styling + renderMutedEvent(event); + } else { + renderNormalEvent(event); + } +}); +``` + +## Custom Mute Logic + +You can customize the mute filter to implement your own mute logic: + +```typescript +// Add keyword-based muting +ndk.muteFilter = (event: NDKEvent) => { + // Check if author is muted + if (ndk.mutedIds.has(event.pubkey)) return true; + + // Check if event ID is muted + if (event.id && ndk.mutedIds.has(event.id)) return true; + + // Custom: mute based on content keywords + const blockedWords = ['spam', 'scam']; + if (blockedWords.some(word => event.content.includes(word))) { + return true; + } + + return false; +}; +``` + +## Checking Mute Status + +You can check if an individual event is muted: + +```typescript +const muteReason = event.muted(); +if (muteReason) { + console.log(`Event is muted: ${muteReason}`); + // muteReason will be "author" if the author is muted + // or "event" if the specific event is muted +} +``` + +## Managing Mute Lists + +NDK automatically manages mute lists when you have an active user: + +```typescript +// Set active user - triggers automatic mute list fetch +ndk.activeUser = await ndk.signer.user(); + +// The mute list is automatically fetched and populated in ndk.mutedIds +// You can disable this by setting autoFetchUserMutelist: false in NDK constructor +const ndk = new NDK({ + explicitRelayUrls: ["wss://relay.damus.io"], + autoFetchUserMutelist: false // Disable automatic mute list fetching +}); +``` + +## Best Practices + +1. **Use the default behavior**: Most applications should rely on automatic mute filtering +2. **Only use `includeMuted: true` when necessary**: Reserve this for moderation interfaces or user settings +3. **Customize the mute filter sparingly**: The default implementation covers most use cases +4. **Provide UI feedback**: When showing muted content, make it visually distinct (grayed out, collapsed, etc.) + +## Example: Moderation Interface + +Here's a complete example of a moderation interface that shows muted content: + +```typescript +const sub = ndk.subscribe( + { kinds: [1], authors: [userPubkey] }, + { includeMuted: true } +); + +sub.on('event', (event) => { + const muteStatus = event.muted(); + + if (muteStatus) { + // Render muted event with warning + renderEvent({ + event, + style: 'muted', + warning: `This event is hidden because ${muteStatus === 'author' ? 'the author' : 'this specific event'} is muted`, + showUnmuteButton: true + }); + } else { + renderEvent({ event, style: 'normal' }); + } +}); +``` diff --git a/ndk-core/docs/tutorial/nip19.md b/ndk-core/docs/tutorial/nip19.md new file mode 100644 index 000000000..79fdbb2db --- /dev/null +++ b/ndk-core/docs/tutorial/nip19.md @@ -0,0 +1,186 @@ +# Working with NIP-19 Identifiers + +NDK provides comprehensive support for NIP-19 identifiers (npub, nprofile, nevent, etc.), both for encoding/decoding data and for working with NDK entities. + +## Direct NIP-19 Utilities + +NDK re-exports all NIP-19 utilities from nostr-tools for lightweight data conversion without needing to instantiate NDK objects: + +```typescript +import { nip19 } from '@nostr-dev-kit/ndk'; + +// Encoding +const npub = nip19.npubEncode(pubkey); +const nsec = nip19.nsecEncode(privateKey); +const note = nip19.noteEncode(eventId); + +// Encoding with metadata +const nprofile = nip19.nprofileEncode({ + pubkey: "hexPubkey", + relays: ["wss://relay1.example.com", "wss://relay2.example.com"] +}); + +const nevent = nip19.neventEncode({ + id: eventId, + relays: ["wss://relay.example.com"], + author: authorPubkey +}); + +const naddr = nip19.naddrEncode({ + kind: 30023, + pubkey: authorPubkey, + identifier: "article-slug", + relays: ["wss://relay.example.com"] +}); + +// Decoding +const decoded = nip19.decode("npub1..."); +console.log(decoded.type); // "npub" +console.log(decoded.data); // hex pubkey + +// Type-specific decoding +if (decoded.type === 'nprofile') { + console.log(decoded.data.pubkey); + console.log(decoded.data.relays); +} +``` + +## Creating NDK Users from NIP-19 + +The `ndk.fetchUser()` method accepts NIP-19 encoded strings directly, automatically detecting and decoding the format: + +```typescript +import NDK from '@nostr-dev-kit/ndk'; + +const ndk = new NDK({ /* ... */ }); + +// From npub +const user1 = await ndk.fetchUser("npub1n0sturny6w9zn2wwexju3m6asu7zh7jnv2jt2kx6tlmfhs7thq0qnflahe"); + +// From nprofile (includes relay hints) +const user2 = await ndk.fetchUser("nprofile1qqsrhuxx8l9ex335q7he0f09aej04zpazpl0ne2cgukyawd24mayt8gpp4mhxue69uhhytnc9e3k7mgpz4mhxue69uhkg6nzv9ejuumpv34kytnrdaksjlyr9p"); + +// From hex pubkey +const user3 = await ndk.fetchUser("3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d"); + +// From NIP-05 identifier +const user4 = await ndk.fetchUser("pablo@test.com"); +const user5 = await ndk.fetchUser("test.com"); // Uses _@test.com + +// Note: fetchUser is async and returns a Promise +// For NIP-05 lookups, it may return undefined if the address cannot be resolved +``` + +## Encoding NDK Events + +NDK events have a built-in `encode()` method that automatically determines the appropriate NIP-19 format: + +```typescript +const event = new NDKEvent(ndk); +event.kind = 1; +event.content = "Hello Nostr!"; +await event.sign(); + +// Automatically chooses the right format: +// - naddr for parameterized replaceable events +// - nevent for events with relay information +// - note for simple note references +const encoded = event.encode(); + +// Control relay hints +const encodedWith5Relays = event.encode(5); // Include up to 5 relay hints +``` + +## Working with Private Keys + +The `NDKPrivateKeySigner` can be instantiated with an nsec: + +```typescript +import { NDKPrivateKeySigner } from '@nostr-dev-kit/ndk'; + +// From nsec +const signer = new NDKPrivateKeySigner("nsec1..."); + +// From hex private key +const signer2 = new NDKPrivateKeySigner("hexPrivateKey"); +``` + +## Common Use Cases + +### Converting between formats + +```typescript +import { nip19 } from '@nostr-dev-kit/ndk'; + +// Convert hex pubkey to npub +function hexToNpub(hexPubkey: string): string { + return nip19.npubEncode(hexPubkey); +} + +// Extract pubkey from any NIP-19 identifier +function extractPubkey(nip19String: string): string | undefined { + const decoded = nip19.decode(nip19String); + + switch (decoded.type) { + case 'npub': + return decoded.data; + case 'nprofile': + return decoded.data.pubkey; + case 'naddr': + return decoded.data.pubkey; + case 'nevent': + return decoded.data.author; + default: + return undefined; + } +} +``` + +### Sharing content with relay hints + +```typescript +// Create shareable event reference with relay hints +const event = await ndk.fetchEvent({ id: eventId }); +const shareableLink = event.encode(3); // Include up to 3 relay hints + +// Create user profile reference with relays +const user = ndk.getUser({ pubkey: userPubkey }); +const nprofile = nip19.nprofileEncode({ + pubkey: user.pubkey, + relays: ["wss://relay.damus.io", "wss://nos.lol"] +}); +``` + +### Validating NIP-19 strings + +```typescript +import { nip19 } from '@nostr-dev-kit/ndk'; + +function isValidNip19(str: string): boolean { + try { + nip19.decode(str); + return true; + } catch { + return false; + } +} + +function isNpub(str: string): boolean { + try { + const decoded = nip19.decode(str); + return decoded.type === 'npub'; + } catch { + return false; + } +} +``` + +## Best Practices + +1. **Use NIP-19 for user-facing displays**: Always show npub/nprofile to users instead of hex pubkeys +2. **Include relay hints for better discovery**: When sharing events or profiles, include 2-3 relay hints +3. **Handle decoding errors**: Always wrap `nip19.decode()` in try-catch blocks +4. **Use the right tool**: + - Use `nip19` utilities for pure data conversion + - Use `ndk.getUser()` when you need an NDK User object + - Use `event.encode()` for encoding existing NDK events \ No newline at end of file diff --git a/ndk-core/docs/tutorial/publishing.md b/ndk-core/docs/tutorial/publishing.md new file mode 100644 index 000000000..bab86a570 --- /dev/null +++ b/ndk-core/docs/tutorial/publishing.md @@ -0,0 +1,24 @@ +# Publishing Events + +## Optimistic publish lifecycle + +Read more about the [local-first](./local-first.md) mode of operation. + +## Publishing Replaceable Events + +Some events in Nostr allow for replacement. + +Kinds `0`, `3`, range `10000-19999`. + +Range `30000-39999` is parameterized replaceable events, which means that multiple events of the same kind under the same pubkey can exist and are differentiated via their `d` tag. + +Since replaceable events depend on having a newer `created_at`, NDK provides a convenience method to reset `id`, `sig`, and `created_at` to allow for easy replacement: `event.publishReplaceable()` + +```ts +const existingEvent = await ndk.fetchEvent({ kinds: [0], authors: []}); // fetch the event to replace +existingEvent.tags.push( + [ "p", "fa984bd7dbb282f07e16e7ae87b26a2a7b9b90b7246a44771f0cf5ae58018f52" ] // follow a new user +); +existingEvent.publish(); // this will NOT work +existingEvent.publishReplaceable(); // this WILL work +``` diff --git a/ndk-core/docs/tutorial/signer-persistence.md b/ndk-core/docs/tutorial/signer-persistence.md new file mode 100644 index 000000000..d428729ce --- /dev/null +++ b/ndk-core/docs/tutorial/signer-persistence.md @@ -0,0 +1,154 @@ +# Signer Persistence + +NDK provides mechanisms to serialize and deserialize signer instances, allowing applications to store authentication credentials (e.g., in `localStorage` or a secure store) and restore them later. This enables users to maintain their session state across app restarts without requiring re-authentication. + +This is achieved through the `toPayload()` method available on signer instances and the central `ndkSignerFromPayload()` function. + +## Serialization: `signer.toPayload()` +Every NDK signer (`NDKPrivateKeySigner`, `NDKNip07Signer`, `NDKNip46Signer`, etc.) implements a `toPayload()` method. This method returns a JSON string containing the minimal information needed to reconstruct a functionally equivalent signer instance later. +Every NDK signer (`NDKPrivateKeySigner`, `NDKNip07Signer`, `NDKNip46Signer`, etc.) implements a `toPayload()` method. This method returns a string containing the information needed to reconstruct a functionally equivalent signer instance later. + +**Example Usage:** + +```typescript +import { NDKPrivateKeySigner } from "@nostr-dev-kit/ndk"; + +// Create or obtain a signer instance +const signer = new NDKPrivateKeySigner("your-nsec-or-hex-private-key"); + +// Serialize the signer +const payloadString = signer.toPayload(); + +// Store the payload string (e.g., in localStorage) +localStorage.setItem("currentUserSigner", payloadString); + +console.log("Signer stored:", payloadString); +``` + +## Deserialization: `ndkSignerFromPayload()` + +The `ndkSignerFromPayload()` function handles reconstructing signers from their serialized form: + +```typescript +async function ndkSignerFromPayload( + payloadString: string, + ndk?: NDK +): Promise +``` + +* `payloadString`: The JSON string from a signer's `toPayload()` method +* `ndk`: Optional NDK instance (required for some signers like NIP-46) +* Returns a Promise resolving to the restored signer +* Throws an error if deserialization fails for any reason + +**Example Usage:** + +```typescript +import NDK, { ndkSignerFromPayload } from "@nostr-dev-kit/ndk"; + +const ndk = new NDK(); + +// Retrieve the stored payload string +const storedPayload = localStorage.getItem("currentUserSigner"); + +if (storedPayload) { + try { + // Deserialize the signer + // Note: Pass 'ndk' if the signer might be NIP-46 or another type requiring it + const restoredSigner = await ndkSignerFromPayload(storedPayload, ndk); + + // Successfully restored - use the signer + ndk.signer = restoredSigner; + console.log("Signer restored successfully!"); + const user = await restoredSigner.user(); + console.log("Restored user pubkey:", user.pubkey); + } catch (error) { + console.error("Error during signer deserialization:", error); + } +} else { + console.log("No stored signer found."); + // Initiate login/key entry flow +} +``` +## Error Handling + +The `ndkSignerFromPayload()` function will throw errors in the following cases: + +1. **Invalid JSON**: If the payload string cannot be parsed as valid JSON + ```typescript + throw new Error(`Failed to parse signer payload: ${errorMessage}`); + ``` + +2. **Missing or Invalid Type**: If the parsed JSON doesn't contain a valid `type` field + ```typescript + throw new Error("Invalid signer payload format: missing or invalid type field"); + ``` + +3. **Unknown Signer Type**: If the `type` value doesn't match any registered signer + ```typescript + throw new Error(`Unknown signer type: ${parsed.type}`); + ``` + +4. **Deserialization Failure**: If the signer's `fromPayload` implementation throws an error + ```typescript + throw new Error(`Failed to deserialize signer type ${parsed.type}: ${errorMessage}`); + ``` + +Always wrap calls to `ndkSignerFromPayload()` in a try/catch block to handle potential errors: + +```typescript +try { + const signer = await ndkSignerFromPayload(storedPayload, ndk); + // Success - use the signer + ndk.signer = signer; +} catch (error) { + // Handle specific error cases as needed + console.error("Failed to restore signer:", error.message); + // Fallback to login flow or other error handling +} +``` +When using `ndkSignerFromPayload()`, always check for a `undefined` result, which indicates that deserialization failed. The function handles errors internally, but returns `undefined` rather than throwing exceptions. + +## Usage Recommendations + +1. **Security Considerations**: + * For `NDKPrivateKeySigner`, the serialized payload contains a private key in hex format - store it in a secure location + * Consider using device-specific secure storage rather than localStorage for sensitive payloads + +2. **Type-Specific Considerations**: + * `NDKNip07Signer`: Use when you need browser extension integration; requires minimal storage + * `NDKPrivateKeySigner`: Best for self-contained applications that manage their own keys + * `NDKNip46Signer`: Ideal for remote signing scenarios such as NIP-46 Nostr Connect + +3. **NDK Instance**: + * Always provide the `ndk` parameter when deserializing signers that need it (like `NDKNip46Signer`) + * For client-side apps, try to deserialize the signer early in the application lifecycle +## How it Works Internally + +The serialization system uses a registry pattern to handle different signer types: + +1. **Registration**: Each signer class is registered in a central `signerRegistry` map with its type string as the key. + ```typescript + // From signers/deserialization.ts + export const signerRegistry: Map> = new Map(); + signerRegistry.set("private-key", NDKPrivateKeySigner); + signerRegistry.set("nip07", NDKNip07Signer); + signerRegistry.set("nip46", NDKNip46Signer); + ``` + +2. **Serialization**: Each signer's `toPayload()` method creates a JSON string with: + * A `type` field that identifies which signer class to use for reconstruction + * A `payload` field containing class-specific data needed for reconstruction + +3. **Deserialization Flow**: + * `ndkSignerFromPayload()` parses the JSON string + * It uses the `type` field to look up the correct signer class in the registry + * It calls the static `fromPayload(payloadString, ndk)` method on that class + * Each signer's `fromPayload` method handles its own type-specific reconstruction + +4. **Recursive Deserialization**: For composite signers like `NDKNip46Signer`, the `fromPayload` method recursively calls `ndkSignerFromPayload()` on nested signer payloads. + +5. **Extensibility**: Custom signer types can participate in this system by: + * Implementing a `toPayload()` method that returns a JSON string with the proper format + * Implementing a static `fromPayload(payloadString, ndk)` method + * Registering the class in the `signerRegistry` with a unique type identifier \ No newline at end of file diff --git a/ndk-core/docs/tutorial/speed.md b/ndk-core/docs/tutorial/speed.md new file mode 100644 index 000000000..80cde51b6 --- /dev/null +++ b/ndk-core/docs/tutorial/speed.md @@ -0,0 +1,48 @@ +# Built for speed + +NDK makes multiple optimizations possible to create a performant client. + +## Signature Verifications +Signature validation is typically the most computationally expensive operation in a nostr client. Thus, NDK attempts to reduce the number of signature verifications that need to be done as much as possible. + +### Service Worker signature validation +In order to create performant clients, it's very useful to offload this computation to a service worker, to avoid blocking the main thread. + +```ts +// Using with vite +const sigWorker = import.meta.env.DEV ? + new Worker(new URL('@nostr-dev-kit/ndk/workers/sig-verification?worker', import.meta.url), { type: 'module' }) : new NDKSigVerificationWorker(); + +const ndk = new NDK(); +ndk.signatureVerificationWorker = worker +``` + +Since signature verification will thus be done asynchronously, it's important to listen for invalid signatures and handle them appropriately; you should +always warn your users when they are receiving invalid signatures from a relay and/or immediately disconnect from an evil relay. + +```ts +ndk.on("event:invalid-sig", (event) => { + const { relay } = event; + console.error("Invalid signature coming from relay", relay.url); +}); +``` + +### Signature verification sampling +Another parameter we can tweak is how many signatures we verify. By default, NDK will verify every signature, but you can change this by setting a per-relay verification rate. + +```ts +ndk.initialValidationRatio = 0.5; // Only verify 50% of the signatures for each relay +ndk.lowestValidationRatio = 0.01; // Never verify less than 1% of the signatures for each relay +``` + +NDK will then begin verifying signatures from each relay and, as signatures as verified, it will reduce the verification rate for that relay. + +### Custom validation ratio function +If you need further control on how the verification rate is adjusted, you can provide a validation ratio function. This function will be called periodically and the returning value will be used to adjust the verification rate. + +```ts +ndk.validationRatioFn = (relay: NDKRelay, validatedEvents: number, nonValidatedEvents: number): number => { + // write your own custom function here + return validatedEvents / (validatedEvents + nonValidatedEvents); +} +``` \ No newline at end of file diff --git a/ndk-core/docs/tutorial/subscription-management.md b/ndk-core/docs/tutorial/subscription-management.md new file mode 100644 index 000000000..8ade11a89 --- /dev/null +++ b/ndk-core/docs/tutorial/subscription-management.md @@ -0,0 +1,105 @@ +# Subscription Management + +NDK attempts to intelligently group subscriptions to avoid excessively hitting relays with too many subscriptions when similar requests are going to be created with similar requests. + +Take the example of an application rendering a list of events along with the authors' name. + +This would typically be accomplished by creating a subscription for the desired events, say, kind:1s with a `#nostr` tag. + +```ts +const sub = ndk.subscribe({ kinds: [1], "#t": ["nostr"] }); +sub.on("event", (event: NDKEvent) => { + const author = event.author; + const profile = await author.fetchProfile(); + + console.log(`${profile.name}: ${event.content}`); +}); +``` + +Now, this seemingly simple approach would have created a kind:0 subscription (`fetchProfile()`) for each note. + +Not great. Most relays will start to reject subscriptions when you have around 10 or 20 active requests. + +In this case, NDK will automatically realize that you are requesting `kind:0` events for a lot of different pubkeys and group them into a single subscription. + +Without grouping: + +```ts +[ "REQ", "", '{ "kinds": [0], pubkeys: [ "pubkey1" ] }'], +[ "REQ", "", '{ "kinds": [0], pubkeys: [ "pubkey2" ] }'], +[ "REQ", "", '{ "kinds": [0], pubkeys: [ "pubkey3" ] }'], +[ "REQ", "", '{ "kinds": [0], pubkeys: [ "pubkey4" ] }'], +[ "REQ", "", '{ "kinds": [0], pubkeys: [ "pubkey5" ] }'], +``` + +With grouping: + +```ts +[ "REQ", "", '{ "kinds": [0], pubkeys: [ "pubkey1", "pubkey2", "pubkey3", "pubkey4", "pubkey5" ] }'], +``` + +Application code doesn't need to concern itself with checking if the event they are receiving is the one they asked for; NDK will only call the event handler with the correct event so that the grouping is transparent to the application. + +## Disabling Grouping + +Sometimes you have a specific need or are certain that you won't be requesting multiple requests of the same type, so we can safely disable grouping and enjoy a small performance boost (since we don't need to wait for grouping to happen). + +```ts +const sub = ndk.subscribe({ kinds: [1], "#t": ["nostr"] }, { groupable: false }); +``` + +## Mute Filtering + +By default, NDK automatically filters out events from muted users and muted event IDs. When you set an active user, their mute list (kind 10000) is automatically fetched and applied to all subscriptions. See the [Mute Filtering](./mute-filtering.md) guide for more details on customizing mute behavior and including muted content when needed. + +## Filter Validation + +NDK automatically validates subscription filters to prevent runtime errors. By default, filters containing `undefined` values or invalid data will throw an error, helping you catch bugs early. See the [Filter Validation](./filter-validation.md) guide for more details on validation modes and best practices. + +This will make the REQ for `kind:1` events to hit the relays immediately and skip the `100ms` (default) grouping window. + +If you want to change the grouping delay you can do so by setting the `groupingDelay` option + +```ts +const sub = ndk.subscribe({ kinds: [1], "#t": ["nostr"] }, { groupingDelay: 500 }); +``` + +You can also establish how the delay should be interpreted: + +```ts +const sub = ndk.subscribe({ kinds: [1], "#t": ["nostr"] }, { groupingDelayType: "at-least" }); +// * "at-least" means "wait at least this long before sending the subscription" +// * "at-most" means "wait at most this long before sending the subscription" +``` + +When using `at-least` the subscription timer will be reset every time a new subscription is added to the group. + +For example, if you create two subscriptions, one at `t=0` and the other one 50ms later (`t=50ms`), with a `groupableDelay` of `200ms`, `at-least` would send the subscription at `t=250ms` and `at-most` would send it at `t=200ms`. + +### Deferred subscription + +Another useful interface to creating subscriptions is to pass event handlers within the subscription itself. In this way, the subscription will first connect the event handlers and then auto-start the subscription. + +```ts +const sub = ndk.subscribe( + { kinds: [1] }, // filters + { groupable: false }, /// subscription options + relaySet, // optional relaySet + { + onEvent: (event: NDKEvent) => console.log("an event was received", event.id), + onEose: () => console.log("the subscription EOSED"), + } +); +``` + +## Advanced uses + +### `cacheUnconstrainFilter` + +`NDKSubscribe` supports passing a number of filters that will be removed when querying the cache; this allows you to pass certain filters when going to relays and drop them when going to the cache. For example, a typical use is querying for events `since` a certain timestamp. But you might want to load, in the same subscription everything that the cache also has, regardless of that timestamp. + +```ts +const events = ndk.subscribe([{ kinds: [1], limit: 10 }, { cacheUnconstrainFilter: ["limit"] }]); +``` + +This query will hit relays and only load 10 events from each relay that it hits, but the cache will be unconstrained from the `limit` filter and everything that matches the `kinds:[1]` filter will be loaded by the subscription. diff --git a/ndk-core/docs/tutorial/zaps/index.md b/ndk-core/docs/tutorial/zaps/index.md new file mode 100644 index 000000000..f9ab1e62a --- /dev/null +++ b/ndk-core/docs/tutorial/zaps/index.md @@ -0,0 +1,16 @@ +# Zaps + +NDK comes with an interface to make zapping as simple as possible. + +```ts +const user = await ndk.fetchUser("pablo@f7z.io"); +const lnPay = ({ pr: string }) => { + console.log("please pay to complete the zap", pr); +}; +const zapper = new NDKZapper(user, 1000, { lnPay }); +zapper.zap(); +``` + +## NDK-Wallet + +Refer to the Wallet section of the tutorial to learn more about zapping. NDK-wallet provides many conveniences to integrate with zaps. From d771a40f55dd8884ecd0265574dddcaabe0398e6 Mon Sep 17 00:00:00 2001 From: digitalbase Date: Sat, 11 Oct 2025 10:50:13 +0200 Subject: [PATCH 003/139] Move docs from ndk-core to core --- .gitignore | 2 + {ndk-core => core}/docs/ai-guardrails.md | 0 {docs => core/docs}/api-examples.md | 0 .../docs/event-class-registration.md | 0 .../docs/examples/ai-guardrails-example.ts | 0 .../docs/getting-started/introduction.md | 0 .../docs/getting-started/signers.md | 0 .../docs/getting-started/usage.md | 0 {ndk-core => core}/docs/index.md | 0 .../docs/internals/subscriptions.md | 0 .../docs/migration/2.12-to-2.13.md | 0 {ndk-core => core}/docs/tutorial/auth.md | 0 .../docs/tutorial/filter-validation.md | 0 .../docs/tutorial/local-first.md | 0 .../docs/tutorial/mute-filtering.md | 0 {ndk-core => core}/docs/tutorial/nip19.md | 0 .../docs/tutorial/publishing.md | 2 +- .../docs/tutorial/signer-persistence.md | 0 {ndk-core => core}/docs/tutorial/speed.md | 0 .../docs/tutorial/subscription-management.md | 4 +- .../docs/tutorial/zaps/index.md | 0 docs/index.md | 12 +- docs/wot/negentropy.md | 198 ------------------ ndk-core/docs/api-examples.md | 49 ----- 24 files changed, 13 insertions(+), 254 deletions(-) rename {ndk-core => core}/docs/ai-guardrails.md (100%) rename {docs => core/docs}/api-examples.md (100%) rename {ndk-core => core}/docs/event-class-registration.md (100%) rename {ndk-core => core}/docs/examples/ai-guardrails-example.ts (100%) rename {ndk-core => core}/docs/getting-started/introduction.md (100%) rename {ndk-core => core}/docs/getting-started/signers.md (100%) rename {ndk-core => core}/docs/getting-started/usage.md (100%) rename {ndk-core => core}/docs/index.md (100%) rename {ndk-core => core}/docs/internals/subscriptions.md (100%) rename {ndk-core => core}/docs/migration/2.12-to-2.13.md (100%) rename {ndk-core => core}/docs/tutorial/auth.md (100%) rename {ndk-core => core}/docs/tutorial/filter-validation.md (100%) rename {ndk-core => core}/docs/tutorial/local-first.md (100%) rename {ndk-core => core}/docs/tutorial/mute-filtering.md (100%) rename {ndk-core => core}/docs/tutorial/nip19.md (100%) rename {ndk-core => core}/docs/tutorial/publishing.md (92%) rename {ndk-core => core}/docs/tutorial/signer-persistence.md (100%) rename {ndk-core => core}/docs/tutorial/speed.md (100%) rename {ndk-core => core}/docs/tutorial/subscription-management.md (94%) rename {ndk-core => core}/docs/tutorial/zaps/index.md (100%) delete mode 100644 docs/wot/negentropy.md delete mode 100644 ndk-core/docs/api-examples.md diff --git a/.gitignore b/.gitignore index daec6dbe1..2d3ec1cf3 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,7 @@ **/node_modules temp +.idea +bun.lockb **/build **/dist **/lib diff --git a/ndk-core/docs/ai-guardrails.md b/core/docs/ai-guardrails.md similarity index 100% rename from ndk-core/docs/ai-guardrails.md rename to core/docs/ai-guardrails.md diff --git a/docs/api-examples.md b/core/docs/api-examples.md similarity index 100% rename from docs/api-examples.md rename to core/docs/api-examples.md diff --git a/ndk-core/docs/event-class-registration.md b/core/docs/event-class-registration.md similarity index 100% rename from ndk-core/docs/event-class-registration.md rename to core/docs/event-class-registration.md diff --git a/ndk-core/docs/examples/ai-guardrails-example.ts b/core/docs/examples/ai-guardrails-example.ts similarity index 100% rename from ndk-core/docs/examples/ai-guardrails-example.ts rename to core/docs/examples/ai-guardrails-example.ts diff --git a/ndk-core/docs/getting-started/introduction.md b/core/docs/getting-started/introduction.md similarity index 100% rename from ndk-core/docs/getting-started/introduction.md rename to core/docs/getting-started/introduction.md diff --git a/ndk-core/docs/getting-started/signers.md b/core/docs/getting-started/signers.md similarity index 100% rename from ndk-core/docs/getting-started/signers.md rename to core/docs/getting-started/signers.md diff --git a/ndk-core/docs/getting-started/usage.md b/core/docs/getting-started/usage.md similarity index 100% rename from ndk-core/docs/getting-started/usage.md rename to core/docs/getting-started/usage.md diff --git a/ndk-core/docs/index.md b/core/docs/index.md similarity index 100% rename from ndk-core/docs/index.md rename to core/docs/index.md diff --git a/ndk-core/docs/internals/subscriptions.md b/core/docs/internals/subscriptions.md similarity index 100% rename from ndk-core/docs/internals/subscriptions.md rename to core/docs/internals/subscriptions.md diff --git a/ndk-core/docs/migration/2.12-to-2.13.md b/core/docs/migration/2.12-to-2.13.md similarity index 100% rename from ndk-core/docs/migration/2.12-to-2.13.md rename to core/docs/migration/2.12-to-2.13.md diff --git a/ndk-core/docs/tutorial/auth.md b/core/docs/tutorial/auth.md similarity index 100% rename from ndk-core/docs/tutorial/auth.md rename to core/docs/tutorial/auth.md diff --git a/ndk-core/docs/tutorial/filter-validation.md b/core/docs/tutorial/filter-validation.md similarity index 100% rename from ndk-core/docs/tutorial/filter-validation.md rename to core/docs/tutorial/filter-validation.md diff --git a/ndk-core/docs/tutorial/local-first.md b/core/docs/tutorial/local-first.md similarity index 100% rename from ndk-core/docs/tutorial/local-first.md rename to core/docs/tutorial/local-first.md diff --git a/ndk-core/docs/tutorial/mute-filtering.md b/core/docs/tutorial/mute-filtering.md similarity index 100% rename from ndk-core/docs/tutorial/mute-filtering.md rename to core/docs/tutorial/mute-filtering.md diff --git a/ndk-core/docs/tutorial/nip19.md b/core/docs/tutorial/nip19.md similarity index 100% rename from ndk-core/docs/tutorial/nip19.md rename to core/docs/tutorial/nip19.md diff --git a/ndk-core/docs/tutorial/publishing.md b/core/docs/tutorial/publishing.md similarity index 92% rename from ndk-core/docs/tutorial/publishing.md rename to core/docs/tutorial/publishing.md index bab86a570..73d16c044 100644 --- a/ndk-core/docs/tutorial/publishing.md +++ b/core/docs/tutorial/publishing.md @@ -2,7 +2,7 @@ ## Optimistic publish lifecycle -Read more about the [local-first](./local-first.md) mode of operation. +Read more about the [local-first](local-first.md) mode of operation. ## Publishing Replaceable Events diff --git a/ndk-core/docs/tutorial/signer-persistence.md b/core/docs/tutorial/signer-persistence.md similarity index 100% rename from ndk-core/docs/tutorial/signer-persistence.md rename to core/docs/tutorial/signer-persistence.md diff --git a/ndk-core/docs/tutorial/speed.md b/core/docs/tutorial/speed.md similarity index 100% rename from ndk-core/docs/tutorial/speed.md rename to core/docs/tutorial/speed.md diff --git a/ndk-core/docs/tutorial/subscription-management.md b/core/docs/tutorial/subscription-management.md similarity index 94% rename from ndk-core/docs/tutorial/subscription-management.md rename to core/docs/tutorial/subscription-management.md index 8ade11a89..6bd5749f1 100644 --- a/ndk-core/docs/tutorial/subscription-management.md +++ b/core/docs/tutorial/subscription-management.md @@ -50,11 +50,11 @@ const sub = ndk.subscribe({ kinds: [1], "#t": ["nostr"] }, { groupable: false }) ## Mute Filtering -By default, NDK automatically filters out events from muted users and muted event IDs. When you set an active user, their mute list (kind 10000) is automatically fetched and applied to all subscriptions. See the [Mute Filtering](./mute-filtering.md) guide for more details on customizing mute behavior and including muted content when needed. +By default, NDK automatically filters out events from muted users and muted event IDs. When you set an active user, their mute list (kind 10000) is automatically fetched and applied to all subscriptions. See the [Mute Filtering](mute-filtering.md) guide for more details on customizing mute behavior and including muted content when needed. ## Filter Validation -NDK automatically validates subscription filters to prevent runtime errors. By default, filters containing `undefined` values or invalid data will throw an error, helping you catch bugs early. See the [Filter Validation](./filter-validation.md) guide for more details on validation modes and best practices. +NDK automatically validates subscription filters to prevent runtime errors. By default, filters containing `undefined` values or invalid data will throw an error, helping you catch bugs early. See the [Filter Validation](filter-validation.md) guide for more details on validation modes and best practices. This will make the REQ for `kind:1` events to hit the relays immediately and skip the `100ms` (default) grouping window. diff --git a/ndk-core/docs/tutorial/zaps/index.md b/core/docs/tutorial/zaps/index.md similarity index 100% rename from ndk-core/docs/tutorial/zaps/index.md rename to core/docs/tutorial/zaps/index.md diff --git a/docs/index.md b/docs/index.md index 52fac990a..6caa8fb8a 100644 --- a/docs/index.md +++ b/docs/index.md @@ -7,12 +7,16 @@ hero: tagline: "Nostr Development Kit Docs" actions: - theme: brand - text: Getting Started + text: Documentation link: /getting-started/introduction.html - theme: secondary - text: References - link: https://github.com/nostr-dev-kit/ndk/blob/master/REFERENCES.md + text: Github Repository + link: https://github.com/nostr-dev-kit/ndk/ --- -NDK is a nostr development kit that makes the experience of building Nostr-related applications, whether they are relays, clients, or anything in between, better, more reliable and overall nicer to work with than existing solutions. \ No newline at end of file +NDK (**Nostr Development Kit**) is a comprehensive type-safe Typescript toolkit for building Nostr applications. + +The library is a monorepo containing different packages and tools you need to create modern, performant, and feature-rich +Nostr clients and applications. The package contains relay interaction code, reactive UI bindings, wallet abstractions, +caching libraries, Web of Trust functionality, and more. \ No newline at end of file diff --git a/docs/wot/negentropy.md b/docs/wot/negentropy.md deleted file mode 100644 index edc2af9e2..000000000 --- a/docs/wot/negentropy.md +++ /dev/null @@ -1,198 +0,0 @@ -# Negentropy Integration in ndk-wot - -## Overview - -The ndk-wot package now supports efficient batch syncing of contact lists using NIP-77 Negentropy protocol. This significantly reduces bandwidth when building large Web of Trust graphs. - -## How It Works - -### Smart Fetch Strategy - -The implementation uses different fetch strategies based on batch size: - -1. **Small Batches (< threshold)**: Uses regular subscription-based fetch - - Fetching fewer than `negentropyMinAuthors` contact lists (default: 5) - - Subscription is faster for small batches - - No overhead from negentropy protocol - -2. **Large Batches (>= threshold)**: Attempts to use negentropy - - Fetching `negentropyMinAuthors` or more contact lists - - Negentropy provides significant bandwidth savings - - Automatically detects negentropy-compatible relays - - Gracefully falls back to subscription if negentropy unavailable - -### Key Features - -- **Automatic Relay Detection**: Checks which connected relays support NIP-77 -- **Graceful Fallback**: Falls back to subscription-based fetching if: - - No negentropy-compatible relays found - - Negentropy sync fails - - Cache adapter not provided -- **Efficient Reconciliation**: Recognizes events already in cache vs. events to fetch -- **Debug Logging**: Detailed logs available via `DEBUG=ndk-wot` environment variable - -## Usage - -```typescript -import NDK from "@nostr-dev-kit/ndk"; -import { NDKWoT } from "@nostr-dev-kit/wot"; - -// Create NDK instance with cache adapter (required for negentropy) -const ndk = new NDK({ - explicitRelayUrls: ["wss://relay.damus.io"], - cacheAdapter: yourCacheAdapter, // Required for negentropy -}); - -await ndk.connect(); - -// Create WoT instance -const wot = new NDKWoT(ndk, rootUserPubkey); - -// Load with negentropy enabled (default) -await wot.load({ - depth: 2, - maxFollows: 100, - useNegentropy: true, // Enable negentropy (default) - negentropyMinAuthors: 5, // Use negentropy when fetching 5+ authors (default) - relayUrls: ["wss://relay.damus.io"], // Optional: specific relays -}); - -// Use lower threshold for more aggressive negentropy usage -await wot.load({ - depth: 2, - negentropyMinAuthors: 2, // Use negentropy even for small batches -}); - -// Disable negentropy (use subscription-based fetch for all batches) -await wot.load({ - depth: 2, - useNegentropy: false, -}); -``` - -## Configuration Options - -### WoTBuildOptions - -- `depth: number` - Maximum depth to traverse (hops from root user) -- `maxFollows?: number` - Maximum follows to process per user (default: 1000) -- `timeout?: number` - Timeout for building the graph in ms -- `useNegentropy?: boolean` - Enable negentropy for batch fetches (default: true) -- `negentropyMinAuthors?: number` - Minimum batch size to use negentropy (default: 5) -- `relayUrls?: string[]` - Specific relays to use (optional) - -## Performance - -### Test Results - -From `test-negentropy-with-cache.ts`: - -- **Depth 2 WoT build**: ~30 seconds -- **Total nodes**: 26 (1 root + 5 depth 1 + 20 depth 2) -- **Negentropy sync**: 7 events fetched (4 needed, 3 already had) -- **Cache growth**: 13 events stored - -### Bandwidth Savings - -Negentropy provides significant bandwidth savings by: -- Only fetching event IDs we don't have -- Skipping events already in cache -- Efficient set reconciliation protocol - -## Debug Logging - -Enable debug logs to see negentropy in action: - -```bash -DEBUG=ndk-wot,ndk-sync npx tsx your-script.ts -``` - -Example output: -``` -ndk-wot Building WOT graph for with depth 2 (negentropy: true, minAuthors: 5) -ndk-wot Processing 1 users at depth 0 -ndk-wot Fetching 1 contact lists using subscription -ndk-wot Subscription fetch completed: 2 events -ndk-wot Processing 30 users at depth 1 -ndk-wot Attempting negentropy sync for 30 contact lists -ndk-wot Found 2 negentropy-compatible relays out of 2 -ndk-wot Negentropy sync completed: 7 events, 4 needed, 3 we have -ndk-wot WOT graph built with 76 nodes in 30290ms -``` - -## Requirements - -- **Cache Adapter**: Required for negentropy to work -- **Compatible Relays**: Relays must support NIP-77 (e.g., strfry-based relays) -- **NDK Sync**: Requires `@nostr-dev-kit/sync` package - -## Relay Compatibility - -Known compatible relays: -- wss://relay.damus.io (strfry) -- wss://nos.lol (strfry) -- wss://relay.snort.social (strfry) - -The implementation automatically detects compatible relays using NIP-11 relay information documents. - -## Implementation Details - -### fetchContactLists() - -Internal method that decides between negentropy and subscription: - -```typescript -private async fetchContactLists(options: { - authors: string[]; - useNegentropy: boolean; - relayUrls?: string[]; -}): Promise> -``` - -Logic: -1. If `useNegentropy === false` or `authors.length < negentropyMinAuthors`: Use subscription -2. Check for negentropy-compatible relays -3. Attempt negentropy sync with cache -4. On error: Fall back to subscription - -The threshold check happens in `load()`: -```typescript -useNegentropy: useNegentropy && usersAtDepth.length >= negentropyMinAuthors -``` - -### fetchViaSubscription() - -Reliable subscription-based fetching: - -```typescript -private async fetchViaSubscription(authors: string[]): Promise> -``` - -- Uses NDK subscriptions with `closeOnEose: true` -- 30-second timeout -- Returns Set of events - -## Testing - -Run the test suite: - -```bash -# Basic WoT functionality (no negentropy) -npx tsx test-simple-wot.ts - -# Test size-based threshold behavior -npx tsx test-threshold.ts - -# With negentropy (no cache - will fall back) -npx tsx test-with-negentropy.ts - -# With negentropy and cache (full functionality) -npx tsx test-negentropy-with-cache.ts -``` - -## Future Improvements - -- [ ] Parallel negentropy syncs to multiple relays -- [ ] Better retry logic for failed syncs -- [ ] Metrics/statistics on bandwidth saved -- [ ] Progressive loading with partial results diff --git a/ndk-core/docs/api-examples.md b/ndk-core/docs/api-examples.md deleted file mode 100644 index 6bd8bb5c1..000000000 --- a/ndk-core/docs/api-examples.md +++ /dev/null @@ -1,49 +0,0 @@ ---- -outline: deep ---- - -# Runtime API Examples - -This page demonstrates usage of some of the runtime APIs provided by VitePress. - -The main `useData()` API can be used to access site, theme, and page data for the current page. It works in both `.md` and `.vue` files: - -```md - - -## Results - -### Theme Data -
{{ theme }}
- -### Page Data -
{{ page }}
- -### Page Frontmatter -
{{ frontmatter }}
-``` - - - -## Results - -### Theme Data -
{{ theme }}
- -### Page Data -
{{ page }}
- -### Page Frontmatter -
{{ frontmatter }}
- -## More - -Check out the documentation for the [full list of runtime APIs](https://vitepress.dev/reference/runtime-api#usedata). From 6c4d7b55c8a286fd88d7bcbceaaf1dceb9eadf57 Mon Sep 17 00:00:00 2001 From: digitalbase Date: Sat, 11 Oct 2025 10:50:41 +0200 Subject: [PATCH 004/139] Remove dupe commands --- prepare-docs.sh | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/prepare-docs.sh b/prepare-docs.sh index 67b438281..bf588d639 100755 --- a/prepare-docs.sh +++ b/prepare-docs.sh @@ -25,8 +25,7 @@ rm -rf \ "$DOCS_DIR/api-examples.md" \ "$DOCS_DIR/index.md" \ "$DOCS_DIR/snippets" \ - "$DOCS_DIR/snippets" \ - "$DOCS_DIR/cache" \ +Re "$DOCS_DIR/cache" \ "$DOCS_DIR/mobile" \ "$DOCS_DIR/wallet" \ "$DOCS_DIR/wot" \ @@ -42,7 +41,6 @@ mkdir -p \ "$DOCS_DIR/mobile" \ "$DOCS_DIR/snippets/mobile" \ "$DOCS_DIR/wallet" \ - "$DOCS_DIR/wallet" \ "$DOCS_DIR/snippets/wallet" \ "$DOCS_DIR/wot" \ "$DOCS_DIR/wrappers" \ From 15e102508ddac62df79936d32e15b7dcfba4770e Mon Sep 17 00:00:00 2001 From: digitalbase Date: Sat, 11 Oct 2025 12:56:32 +0200 Subject: [PATCH 005/139] Add gitignore for vitepress cache --- .vitepress/.gitignore | 1 + package.json | 6 ++---- 2 files changed, 3 insertions(+), 4 deletions(-) create mode 100644 .vitepress/.gitignore diff --git a/.vitepress/.gitignore b/.vitepress/.gitignore new file mode 100644 index 000000000..5e4659675 --- /dev/null +++ b/.vitepress/.gitignore @@ -0,0 +1 @@ +cache \ No newline at end of file diff --git a/package.json b/package.json index 305f55f70..87a316c11 100755 --- a/package.json +++ b/package.json @@ -14,7 +14,7 @@ "lint": "biome check .", "clean": "turbo clean && rm -rf node_modules", "format": "biome format --write .", - "docs:dev": "bash ./prepare-docs.sh && npx vitepress dev docs", + "docs:dev": "npx vitepress dev", "docs:build": "bash ./prepare-docs.sh && vitepress build docs", "changeset": "changeset", "cs": "changeset", @@ -46,9 +46,7 @@ "svelte/examples/event-graph", "svelte/examples/feed-viewer", "svelte/examples/nutsack", - "svelte/examples/sessions-demo", - "projects/Voces", - "projects/solidjs" + "svelte/examples/sessions-demo" ], "engines": { "node": ">=16.0" From 9b5498fa0bd68b8eccf3c9410f07e17a36becb0e Mon Sep 17 00:00:00 2001 From: digitalbase Date: Sat, 11 Oct 2025 12:57:03 +0200 Subject: [PATCH 006/139] Move vitepress to root folder to be able to access rewritten docs --- {docs/.vitepress => .vitepress}/config.mts | 21 ++++++++++++++++--- .../.vitepress => .vitepress}/theme/index.ts | 0 .../.vitepress => .vitepress}/theme/style.css | 0 3 files changed, 18 insertions(+), 3 deletions(-) rename {docs/.vitepress => .vitepress}/config.mts (86%) rename {docs/.vitepress => .vitepress}/theme/index.ts (100%) rename {docs/.vitepress => .vitepress}/theme/style.css (100%) diff --git a/docs/.vitepress/config.mts b/.vitepress/config.mts similarity index 86% rename from docs/.vitepress/config.mts rename to .vitepress/config.mts index 342e6f1ec..cf18f9f36 100644 --- a/docs/.vitepress/config.mts +++ b/.vitepress/config.mts @@ -12,6 +12,11 @@ export default defineConfig({ dark: 'github-dark' } }, + rewrites: { + 'docs/:slug.md': ':slug.md', + 'core/docs/:slug*': 'core/:slug*', + 'core/docs/:subdir/:slug*': 'core/:subdir/:slug*' + }, themeConfig: { // https://vitepress.dev/reference/default-theme-config nav: [ @@ -26,13 +31,14 @@ export default defineConfig({ { text: "Getting Started", items: [ - { text: "Introduction", link: "/getting-started/introduction" }, - { text: "Usage", link: "/getting-started/usage" }, - { text: "Signers", link: "/getting-started/signers" }, + { text: "Introduction", link: "/core/getting-started/introduction" }, + { text: "Usage", link: "/core/getting-started/usage" }, + { text: "Signers", link: "/core/getting-started/signers" }, ], }, { text: "Tutorial", + collapsed: true, items: [ { text: "Local-first", link: "/tutorial/local-first" }, { text: "Publishing", link: "/tutorial/publishing" }, @@ -48,6 +54,7 @@ export default defineConfig({ }, { text: "Cache Adapters", + collapsed: true, items: [ { text: "In-memory LRU", link: "/cache/memory" }, { text: "In-memory + dexie", link: "/cache/dexie" }, @@ -64,6 +71,7 @@ export default defineConfig({ }, { text: "Wallet", + collapsed: true, items: [ { text: "Introduction", link: "/wallet/index" }, { text: "Nutsack (NIP-60)", link: "/wallet/nutsack" }, @@ -72,12 +80,14 @@ export default defineConfig({ }, { text: "Sync & Negentropy", + collapsed: true, items: [ { text: "Introduction", link: "/sync/index" }, ], }, { text: "Web of Trust", + collapsed: true, items: [ { text: "Introduction", link: "/wot/index" }, { text: "Negentropy Integration", link: "/wot/negentropy" }, @@ -85,6 +95,7 @@ export default defineConfig({ }, { text: "Wrappers", + collapsed: true, items: [ { text: "NDK Svelte", link: "/wrappers/svelte" }, { @@ -99,6 +110,7 @@ export default defineConfig({ }, { text: "Sessions", + collapsed: true, items: [ { text: "Introduction", link: "/sessions/index" }, { text: "Quick Start", link: "/sessions/quick-start" }, @@ -108,6 +120,7 @@ export default defineConfig({ }, { text: "Mobile", + collapsed: true, items: [ { text: "Introduction", link: "/mobile/index" }, { text: "Session", link: "/mobile/session" }, @@ -116,12 +129,14 @@ export default defineConfig({ }, { text: "Blossom (Media)", + collapsed: true, items: [ { text: "Introduction", link: "/blossom/getting-started" }, ] }, { text: "Internals", + collapsed: true, items: [{ text: "Subscription Lifecycle", link: "/internals/subscriptions" }], }, ], diff --git a/docs/.vitepress/theme/index.ts b/.vitepress/theme/index.ts similarity index 100% rename from docs/.vitepress/theme/index.ts rename to .vitepress/theme/index.ts diff --git a/docs/.vitepress/theme/style.css b/.vitepress/theme/style.css similarity index 100% rename from docs/.vitepress/theme/style.css rename to .vitepress/theme/style.css From 056575a9158ff472a28258d56b4ffd95a4d053f8 Mon Sep 17 00:00:00 2001 From: digitalbase Date: Sat, 11 Oct 2025 12:57:17 +0200 Subject: [PATCH 007/139] Delete/remove package from docs folder --- docs/package.json | 17 ----------------- 1 file changed, 17 deletions(-) delete mode 100644 docs/package.json diff --git a/docs/package.json b/docs/package.json deleted file mode 100644 index 703f6b53a..000000000 --- a/docs/package.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "name": "docs", - "version": "1.0.0", - "main": "index.js", - "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" - }, - "author": "", - "license": "ISC", - "description": "", - "dependencies": { - "vitepress": "^1.6.3" - }, - "devDependencies": { - "vitepress-plugin-mermaid": "^2.0.17" - } -} From 9887505df0532314c3766cbfeb69780c5b632d10 Mon Sep 17 00:00:00 2001 From: digitalbase Date: Sat, 11 Oct 2025 12:57:39 +0200 Subject: [PATCH 008/139] Update index to correct paths --- docs/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/index.md b/docs/index.md index 6caa8fb8a..7f3f1094c 100644 --- a/docs/index.md +++ b/docs/index.md @@ -8,7 +8,7 @@ hero: actions: - theme: brand text: Documentation - link: /getting-started/introduction.html + link: /core/getting-started/introduction - theme: secondary text: Github Repository link: https://github.com/nostr-dev-kit/ndk/ From 025d1ebd971a9033001a04d35657f39c0b84008c Mon Sep 17 00:00:00 2001 From: digitalbase Date: Sat, 11 Oct 2025 13:09:27 +0200 Subject: [PATCH 009/139] Empty two broken markdown files --- mobile/CHANGELOG.md | 778 -------------------------------------------- sync/README.md | 459 -------------------------- 2 files changed, 1237 deletions(-) diff --git a/mobile/CHANGELOG.md b/mobile/CHANGELOG.md index 83167ea4f..e69de29bb 100644 --- a/mobile/CHANGELOG.md +++ b/mobile/CHANGELOG.md @@ -1,778 +0,0 @@ -# @nostr-dev-kit/ndk-mobile - -## 0.8.50 - -### Patch Changes - -- Updated dependencies [8315d5e] -- Updated dependencies [d9d5662] -- Updated dependencies [6fb3a7f] -- Updated dependencies [028367b] - - @nostr-dev-kit/ndk@2.18.0 - -## 0.8.49 - -### Patch Changes - -- Updated dependencies - - @nostr-dev-kit/ndk@2.17.1 - -## 0.8.48 - -### Patch Changes - -- Updated dependencies [344c313] -- Updated dependencies [3407126] -- Updated dependencies [344c313] - - @nostr-dev-kit/ndk@2.17.0 - - @nostr-dev-kit/wallet@0.8.2 - - @nostr-dev-kit/react@1.3.8 - -## 0.8.47 - -### Patch Changes - -- Updated dependencies - - @nostr-dev-kit/wallet@0.8.1 - - @nostr-dev-kit/react@1.3.7 - -## 0.8.46 - -### Patch Changes - -- Updated dependencies - - @nostr-dev-kit/ndk@2.16.1 - -## 0.8.45 - -### Patch Changes - -- Updated dependencies [e596023] -- Updated dependencies [e596023] - - @nostr-dev-kit/ndk@2.16.0 - - @nostr-dev-kit/wallet@0.8.0 - - @nostr-dev-kit/ndk-hooks@1.3.6 - -## 0.8.44 - -### Patch Changes - -- Updated dependencies [a912a2c] - - @nostr-dev-kit/ndk@2.15.3 - - @nostr-dev-kit/ndk-wallet@0.7.2 - - @nostr-dev-kit/ndk-hooks@1.3.5 - -## 0.8.43 - -### Patch Changes - -- Updated dependencies [b7e7f92] - - @nostr-dev-kit/ndk@2.17.1 - -## 0.8.42 - -### Patch Changes - -- Updated dependencies [73c6a2f] -- Updated dependencies [fad1f3d] - - @nostr-dev-kit/ndk@2.17.0 - -## 0.8.40 - -### Patch Changes - -- Updated dependencies -- Updated dependencies -- Updated dependencies -- Updated dependencies - - @nostr-dev-kit/ndk-wallet@0.7.0 - - @nostr-dev-kit/ndk@2.15.0 - - @nostr-dev-kit/ndk-hooks@1.3.3 - -## 0.8.39 - -### Patch Changes - -- Updated dependencies - - @nostr-dev-kit/ndk-hooks@1.3.2 - -## 0.8.38 - -### Patch Changes - -- Updated dependencies - - @nostr-dev-kit/ndk@2.14.38 - -## 0.8.37 - -### Patch Changes - -- Updated dependencies [2886111] -- Updated dependencies [96341c3] - - @nostr-dev-kit/ndk@2.14.37 - - @nostr-dev-kit/ndk-wallet@0.6.3 - - @nostr-dev-kit/ndk-hooks@1.3.1 - -## 0.8.36 - -### Patch Changes - -- Updated dependencies [8bd22bd] -- Updated dependencies - - @nostr-dev-kit/ndk@2.14.36 - - @nostr-dev-kit/ndk-hooks@1.3.0 - -## 0.8.35 - -### Patch Changes - -- Updated dependencies - - @nostr-dev-kit/ndk-hooks@1.2.5 - -## 0.8.34 - -### Patch Changes - -- Updated dependencies - - @nostr-dev-kit/ndk@2.14.35 - -## 0.8.33 - -### Patch Changes - -- Updated dependencies - - @nostr-dev-kit/ndk-hooks@1.2.4 - -## 0.8.32 - -### Patch Changes - -- Updated dependencies [d89dbc6] -- Updated dependencies [fff020a] - - @nostr-dev-kit/ndk@2.14.34 - -## 0.8.31 - -### Patch Changes - -- Updated dependencies - - @nostr-dev-kit/ndk@2.14.33 - -## 0.8.30 - -### Patch Changes - -- Updated dependencies [9cb8407] - - @nostr-dev-kit/ndk@2.14.32 - -## 0.8.29 - -### Patch Changes - -- Updated dependencies - - @nostr-dev-kit/ndk@2.14.31 - -## 0.8.28 - -### Patch Changes - -- Updated dependencies - - @nostr-dev-kit/ndk@2.14.30 - -## 0.8.27 - -### Patch Changes - -- Updated dependencies - - @nostr-dev-kit/ndk-hooks@1.2.3 - -## 0.8.26 - -### Patch Changes - -- Updated dependencies - - @nostr-dev-kit/ndk@2.14.29 - -## 0.8.25 - -### Patch Changes - -- Updated dependencies - - @nostr-dev-kit/ndk@2.14.28 - -## 0.8.24 - -### Patch Changes - -- Updated dependencies - - @nostr-dev-kit/ndk@2.14.27 - -## 0.8.23 - -### Patch Changes - -- Updated dependencies - - @nostr-dev-kit/ndk@2.14.26 - -## 0.8.22 - -### Patch Changes - -- Updated dependencies - - @nostr-dev-kit/ndk@2.14.25 - -## 0.8.21 - -### Patch Changes - -- Updated dependencies - - @nostr-dev-kit/ndk-wallet@0.6.2 - - @nostr-dev-kit/ndk-hooks@1.2.2 - -## 0.8.20 - -### Patch Changes - -- Updated dependencies - - @nostr-dev-kit/ndk-wallet@0.6.1 - - @nostr-dev-kit/ndk-hooks@1.2.1 - -## 0.8.19 - -### Patch Changes - -- Updated dependencies - - @nostr-dev-kit/ndk@2.14.24 - -## 0.8.18 - -### Patch Changes - -- Updated dependencies - - @nostr-dev-kit/ndk-hooks@1.2.0 - -## 0.8.17 - -### Patch Changes - -- Updated dependencies [7476407] -- Updated dependencies [7476407] - - @nostr-dev-kit/ndk@2.14.23 - - @nostr-dev-kit/ndk-hooks@1.1.45 - -## 0.8.16 - -### Patch Changes - -- Updated dependencies - - @nostr-dev-kit/ndk@2.14.22 - -## 0.8.15 - -### Patch Changes - -- Updated dependencies - - @nostr-dev-kit/ndk@2.14.21 - -## 0.8.14 - -### Patch Changes - -- Updated dependencies - - @nostr-dev-kit/ndk@2.14.20 - -## 0.8.13 - -### Patch Changes - -- Updated dependencies - - @nostr-dev-kit/ndk@2.14.19 - -## 0.8.12 - -### Patch Changes - -- Updated dependencies - - @nostr-dev-kit/ndk-hooks@1.1.44 - -## 0.8.11 - -### Patch Changes - -- Updated dependencies - - @nostr-dev-kit/ndk-hooks@1.1.43 - -## 0.8.10 - -### Patch Changes - -- Updated dependencies - - @nostr-dev-kit/ndk@2.14.18 - -## 0.8.9 - -### Patch Changes - -- Updated dependencies - - @nostr-dev-kit/ndk@2.14.17 - -## 0.8.8 - -### Patch Changes - -- Updated dependencies - - @nostr-dev-kit/ndk@2.14.16 - -## 0.8.7 - -### Patch Changes - -- Updated dependencies - - @nostr-dev-kit/ndk-hooks@1.1.42 - -## 0.8.6 - -### Patch Changes - -- Updated dependencies - - @nostr-dev-kit/ndk@2.14.15 - -## 0.8.5 - -### Patch Changes - -- Updated dependencies - - @nostr-dev-kit/ndk@2.14.14 - -## 0.8.4 - -### Patch Changes - -- Updated dependencies - - @nostr-dev-kit/ndk@2.14.13 - -## 0.8.3 - -### Patch Changes - -- Updated dependencies - - @nostr-dev-kit/ndk-hooks@1.1.41 - -## 0.8.2 - -### Patch Changes - -- Updated dependencies - - @nostr-dev-kit/ndk-hooks@1.1.40 - -## 0.8.1 - -### Patch Changes - -- Updated dependencies - - @nostr-dev-kit/ndk-hooks@1.1.39 - -## 0.8.0 - -### Minor Changes - -- bump - -### Patch Changes - -- Updated dependencies - - @nostr-dev-kit/ndk-wallet@0.6.0 - - @nostr-dev-kit/ndk-hooks@1.1.38 - -## 0.7.15 - -### Patch Changes - -- Updated dependencies - - @nostr-dev-kit/ndk-wallet@0.5.14 - - @nostr-dev-kit/ndk-hooks@1.1.37 - -## 0.7.14 - -### Patch Changes - -- Updated dependencies - - @nostr-dev-kit/ndk-hooks@1.1.36 - -## 0.7.13 - -### Patch Changes - -- Updated dependencies - - @nostr-dev-kit/ndk@2.14.12 - -## 0.7.12 - -### Patch Changes - -- update expo-nip55 - -## 0.7.11 - -### Patch Changes - -- Updated dependencies - - @nostr-dev-kit/ndk@2.14.11 - -## 0.7.10 - -### Patch Changes - -- Updated dependencies - - @nostr-dev-kit/ndk@2.14.10 - -## 0.7.9 - -### Patch Changes - -- Updated dependencies - - @nostr-dev-kit/ndk@2.14.8 - -## 0.7.8 - -### Patch Changes - -- Updated dependencies - - @nostr-dev-kit/ndk@2.14.7 - - @nostr-dev-kit/ndk-wallet@0.5.11 - - @nostr-dev-kit/ndk-hooks@1.1.26 - -## 0.7.7 - -### Patch Changes - -- Updated dependencies - - @nostr-dev-kit/ndk@2.14.6 - - @nostr-dev-kit/ndk-wallet@0.5.10 - - @nostr-dev-kit/ndk-hooks@1.1.25 - -## 0.7.6 - -### Patch Changes - -- Updated dependencies - - @nostr-dev-kit/ndk@2.14.5 - - @nostr-dev-kit/ndk-wallet@0.5.9 - - @nostr-dev-kit/ndk-hooks@1.1.24 - -## 0.7.5 - -### Patch Changes - -- Updated dependencies -- Updated dependencies - - @nostr-dev-kit/ndk@2.14.4 - - @nostr-dev-kit/ndk-wallet@0.5.8 - - @nostr-dev-kit/ndk-hooks@1.1.23 - -## 0.7.4 - -### Patch Changes - -- Updated dependencies - - @nostr-dev-kit/ndk@2.14.3 - - @nostr-dev-kit/ndk-wallet@0.5.7 - - @nostr-dev-kit/ndk-hooks@1.1.22 - -## 0.7.3 - -### Patch Changes - -- Updated dependencies - - @nostr-dev-kit/ndk-hooks@1.1.21 - -## 0.7.2 - -### Patch Changes - -- Updated dependencies - - @nostr-dev-kit/ndk-hooks@1.1.20 - -## 0.7.1 - -### Patch Changes - -- Updated dependencies - - @nostr-dev-kit/ndk-hooks@1.1.19 - -## 0.6.19 - -### Patch Changes - -- Updated dependencies - - @nostr-dev-kit/ndk-hooks@1.1.17 - -## 0.6.18 - -### Patch Changes - -- Updated dependencies - - @nostr-dev-kit/ndk-hooks@1.1.16 - -## 0.6.17 - -### Patch Changes - -- Updated dependencies - - @nostr-dev-kit/ndk-hooks@1.1.15 - -## 0.6.16 - -### Patch Changes - -- Updated dependencies - - @nostr-dev-kit/ndk-hooks@1.1.14 - -## 0.6.15 - -### Patch Changes - -- Updated dependencies - - @nostr-dev-kit/ndk-hooks@1.1.13 - -## 0.6.14 - -### Patch Changes - -- Updated dependencies - - @nostr-dev-kit/ndk-hooks@1.1.12 - -## 0.6.13 - -### Patch Changes - -- Updated dependencies - - @nostr-dev-kit/ndk-wallet@0.5.6 - - @nostr-dev-kit/ndk-hooks@1.1.11 - -## 0.6.12 - -### Patch Changes - -- Updated dependencies - - @nostr-dev-kit/ndk-hooks@1.1.10 - -## 0.6.11 - -### Patch Changes - -- redo imports -- Updated dependencies - - @nostr-dev-kit/ndk-hooks@1.1.9 - -## 0.6.10 - -### Patch Changes - -- Updated dependencies - - @nostr-dev-kit/ndk@2.14.2 - - @nostr-dev-kit/ndk-hooks@1.1.7 - - @nostr-dev-kit/ndk-wallet@0.5.5 - -## 0.6.9 - -### Patch Changes - -- Updated dependencies - - @nostr-dev-kit/ndk-hooks@1.1.6 - -## 0.6.8 - -### Patch Changes - -- update docs -- Updated dependencies - - @nostr-dev-kit/ndk-hooks@1.1.5 - -## 0.6.7 - -### Patch Changes - -- fiddle with exports -- Updated dependencies - - @nostr-dev-kit/ndk-hooks@1.1.3 - -## 0.6.6 - -### Patch Changes - -- Updated dependencies - - @nostr-dev-kit/ndk-hooks@1.1.3 - -## 0.6.5 - -### Patch Changes - -- export NDK - -## 0.6.4 - -### Patch Changes - -- Updated dependencies - - @nostr-dev-kit/ndk-hooks@1.1.1 - -## 0.6.3 - -### Patch Changes - -- Updated dependencies - - @nostr-dev-kit/ndk-hooks@1.1.0 - -## 0.6.2 - -### Patch Changes - -- Updated dependencies - - @nostr-dev-kit/ndk-wallet@0.5.4 - - @nostr-dev-kit/ndk-hooks@1.0.2 - -## 0.6.1 - -### Patch Changes - -- Updated dependencies - - @nostr-dev-kit/ndk@2.14.1 - - @nostr-dev-kit/ndk-hooks@1.0.1 - - @nostr-dev-kit/ndk-wallet@0.5.3 - -## 0.6.0 - -### Minor Changes - -- 5ab19ef: feat: Refactor session management and add persistence - - **ndk-core:** Added signer serialization (`toPayload`, `fromPayload`) and deserialization (`ndkSignerFromPayload`, `signerRegistry`) framework. - - **ndk-hooks:** (Breaking Change) Refactored session state into `useNDKSessions` store with new management functions (`addSigner`, `startSession`, `switchToUser`, etc.), removing old session logic. - - **ndk-mobile:** Added persistent session storage using `expo-secure-store` (`session-storage.ts`, `useSessionMonitor`, `bootNDK`). Updated `NDKNip55Signer` for serialization and registration. - -### Patch Changes - -- c83166a: bump -- 6e16e06: Enhance SQLite adapter to support decrypted events storage and retrieval. -- import changes -- df73b9b: add component -- Updated dependencies [c83166a] -- Updated dependencies [5ab19ef] -- Updated dependencies [6e16e06] -- Updated dependencies -- Updated dependencies [5ab19ef] - - @nostr-dev-kit/ndk-wallet@0.5.2 - - @nostr-dev-kit/ndk-hooks@1.0.0 - - @nostr-dev-kit/ndk@2.14.0 - -## 2.3.1-rc1.0 - -### Patch Changes - -- add component - -## 0.4.4 - -### Patch Changes - -- Updated dependencies -- Updated dependencies -- Updated dependencies - - @nostr-dev-kit/ndk-wallet@0.5.0 - -## 0.4.3 - -### Patch Changes - -- Updated dependencies - - @nostr-dev-kit/ndk@2.12.2 - - @nostr-dev-kit/ndk-wallet@0.4.3 - -## 0.4.2 - -### Patch Changes - -- sqlite search profile support -- e667a60: store parsed profile in sqlite adapter -- Updated dependencies [3ea9695] -- Updated dependencies [cca3357] -- Updated dependencies [1235f69] - - @nostr-dev-kit/ndk@2.12.1 - - @nostr-dev-kit/ndk-wallet@0.4.2 - -## 0.4.1 - -### Patch Changes - -- d87d886: Leverage synchronous cache adapter to load events in one go in useSubscribe hook -- Updated dependencies [f255a07] -- Updated dependencies [f255a07] -- Updated dependencies [2171140] -- Updated dependencies [72c8492] -- Updated dependencies [72c8492] - - @nostr-dev-kit/ndk@2.12.0 - - @nostr-dev-kit/ndk-wallet@0.4.1 - -## 0.4.0 - -### Minor Changes - -- changes to the initialization hook to allow for more fine grained database initialization logic - -### Patch Changes - -- Updated dependencies -- Updated dependencies -- Updated dependencies -- Updated dependencies - - @nostr-dev-kit/ndk@2.11.2 - - @nostr-dev-kit/ndk-wallet@0.4.0 - -## 0.3.0 - -### Minor Changes - -- NIP-55 support (thanks to nostr:npub1ehhfg09mr8z34wz85ek46a6rww4f7c7jsujxhdvmpqnl5hnrwsqq2szjqv !) - -### Patch Changes - -- Updated dependencies - - @nostr-dev-kit/ndk@2.11.1 - - @nostr-dev-kit/ndk-wallet@0.3.17 - -## 0.2.2 - -### Patch Changes - -- f2b307d: add useUserProfile hook -- 6b3ea8b: add LRU cache for profiles -- 1301db9: add sync profile fetching from cache -- Updated dependencies [35987be] -- Updated dependencies [689305c] -- Updated dependencies [35987be] -- Updated dependencies [35987be] -- Updated dependencies [689305c] -- Updated dependencies -- Updated dependencies [4ed75a6] - - @nostr-dev-kit/ndk@2.11.0 - - @nostr-dev-kit/ndk-wallet@0.3.16 - -## 0.2.1 - -### Patch Changes - -- Updated dependencies - - @nostr-dev-kit/ndk@2.10.7 - - @nostr-dev-kit/ndk-wallet@0.3.15 - -## 0.2.0 - -### Minor Changes - -- add the very handy useNDKSessionEventKind - -## 0.1.5 - -### Patch Changes - -- add default export diff --git a/sync/README.md b/sync/README.md index 60a829034..e69de29bb 100644 --- a/sync/README.md +++ b/sync/README.md @@ -1,459 +0,0 @@ -# @nostr-dev-kit/sync - -NIP-77 Negentropy sync protocol implementation for NDK. - -Efficient event synchronization using set reconciliation to minimize bandwidth usage when syncing events between clients and relays. - -## Features - -- **Bandwidth Efficient**: Uses Negentropy protocol to identify differences without transferring full event data -- **Automatic Fallback**: Falls back to standard `fetchEvents` for relays without NIP-77 support -- **Capability Tracking**: Caches which relays support Negentropy to optimize future syncs -- **Cache Integration**: Automatically populates NDK cache with synced events -- **Sequential Multi-Relay**: Syncs with multiple relays for optimal efficiency -- **Clean API**: Type-safe class-based interface - -## Installation - -```bash -npm install @nostr-dev-kit/sync -# or -bun add @nostr-dev-kit/sync -``` - -## Requirements - -- `@nostr-dev-kit/ndk` (workspace dependency) -- An NDK cache adapter must be configured - -## Usage - -### Recommended: NDKSync Class - -The `NDKSync` class provides a clean, stateful API with automatic relay capability tracking: - -```typescript -import NDK from '@nostr-dev-kit/ndk'; -import { NDKSync } from '@nostr-dev-kit/sync'; - -const ndk = new NDK({ - explicitRelayUrls: ['wss://relay.damus.io'], - cacheAdapter: myCacheAdapter // Required! -}); - -await ndk.connect(); - -// Create sync instance (caches relay capabilities) -const sync = new NDKSync(ndk); - -// Sync recent notes from a user -const result = await sync.sync({ - kinds: [1], - authors: [pubkey], - since: Math.floor(Date.now() / 1000) - 86400 // Last 24h -}); - -console.log(`Synced ${result.events.length} events`); -console.log(`Needed ${result.need.size} events from relays`); -console.log(`Have ${result.have.size} events relays don't`); -``` - -### Sync + Subscribe (Recommended) - -The `syncAndSubscribe` method combines efficient syncing with live subscriptions, ensuring you don't miss any events during the sync process: - -```typescript -import { NDKSync } from '@nostr-dev-kit/sync'; - -const sync = new NDKSync(ndk); - -const sub = await sync.syncAndSubscribe( - { kinds: [1], authors: [pubkey] }, - { - onEvent: (event) => { - console.log('Event:', event.content); - }, - onRelaySynced: (relay, count) => { - console.log(`โœ“ Synced ${count} events from ${relay.url}`); - }, - onSyncComplete: () => { - console.log('โœ“ All relays synced!'); - } - } -); - -// Subscription is already receiving events -// Background sync continues for historical events -``` - -**How it works:** -1. Immediately starts a subscription with `limit: 0` to catch new events -2. Returns the subscription right away (non-blocking) -3. Background: Syncs historical events from each relay - - Checks capability cache to determine if relay supports Negentropy - - Uses Negentropy where available (efficient) - - Falls back to `fetchEvents` for non-Negentropy relays -4. All synced events automatically flow to the subscription - -**Perfect for:** -- Wallet syncing (kind 7375, 7376, 5) -- Feed loading -- DM synchronization -- Any scenario where you need complete event coverage - -### Static Methods - -If you don't need persistent capability tracking, use static methods: - -```typescript -import { NDKSync } from '@nostr-dev-kit/sync'; - -// One-off sync -const result = await NDKSync.sync(ndk, { kinds: [1], limit: 100 }); - -// One-off sync and subscribe -const sub = await NDKSync.syncAndSubscribe(ndk, { kinds: [1] }); -``` - -### Checking Relay Capabilities - -The `NDKSync` class automatically tracks which relays support Negentropy: - -```typescript -const sync = new NDKSync(ndk); - -// Check if a relay supports Negentropy -const relay = ndk.pool.relays.get("wss://relay.example.com"); -const supported = await sync.checkRelaySupport(relay); - -// Get all relays that support Negentropy -const negentropyRelays = await sync.getNegentropyRelays(); - -// Get cached capability info -const capability = sync.getRelayCapability("wss://relay.example.com"); -console.log(capability?.supportsNegentropy); -console.log(capability?.lastChecked); - -// Clear cache for a specific relay (e.g., after relay update) -sync.clearCapabilityCache("wss://relay.example.com"); - -// Clear all capability cache -sync.clearCapabilityCache(); -``` - -### Sync Options - -```typescript -// Sync with specific relays -const result = await sync.sync(filters, { - relayUrls: ['wss://relay.nostr.band', 'wss://nos.lol'] -}); - -// Sync without auto-fetch -const result = await sync.sync(filters, { - autoFetch: false -}); - -// Manually fetch if needed -if (result.need.size > 100) { - console.log('Too many to fetch now, schedule for later'); -} else { - await ndk.fetchEvents({ ids: Array.from(result.need) }); -} -``` - -### Background Cache Warming - -```typescript -// Good for background sync to populate cache -await sync.sync(filters, { - autoFetch: true // Fetch and cache events -}); - -// Later, subscriptions will be instant from cache -const sub = ndk.subscribe(filters); -``` - -## Utility Functions - -For checking relay support without creating an `NDKSync` instance: - -```typescript -import { supportsNegentropy, getRelayCapabilities, filterNegentropyRelays } from '@nostr-dev-kit/sync'; - -// Check if a relay supports NIP-77 -const supported = await supportsNegentropy("wss://relay.example.com"); - -// Get detailed relay capabilities -const caps = await getRelayCapabilities("wss://relay.damus.io"); -console.log(`Negentropy: ${caps.supportsNegentropy}`); -console.log(`Software: ${caps.software} ${caps.version}`); -console.log(`Supported NIPs: ${caps.supportedNips.join(", ")}`); - -// Filter relays to only those with NIP-77 support -const allRelays = ["wss://relay1.com", "wss://relay2.com", "wss://relay3.com"]; -const syncRelays = await filterNegentropyRelays(allRelays); -``` - -## API Reference - -### `NDKSync` Class - -#### Constructor - -```typescript -new NDKSync(ndk: NDK) -``` - -Creates a new sync instance with relay capability tracking. - -#### Methods - -##### `sync(filters, options?)` - -Performs NIP-77 sync with relays. - -**Parameters:** -- `filters`: NDKFilter | NDKFilter[] - Filters to sync -- `options?`: NDKSyncOptions - Sync options - -**Returns:** Promise - -##### `syncAndSubscribe(filters, options?)` - -Combines sync with live subscription for complete event coverage. - -**Parameters:** -- `filters`: NDKFilter | NDKFilter[] - Filters to sync and subscribe -- `options?`: SyncAndSubscribeOptions - Subscription options with sync callbacks - -**Returns:** Promise - -##### `checkRelaySupport(relay)` - -Check if a relay supports Negentropy (uses cache when available). - -**Parameters:** -- `relay`: NDKRelay - Relay to check - -**Returns:** Promise - -##### `getNegentropyRelays(relays?)` - -Get all relays that support Negentropy. - -**Parameters:** -- `relays?`: NDKRelay[] - Optional specific relays to check (defaults to all NDK relays) - -**Returns:** Promise - -##### `getRelayCapability(relayUrl)` - -Get cached capability info for a relay. - -**Parameters:** -- `relayUrl`: string - Relay URL - -**Returns:** RelayCapability | undefined - -##### `clearCapabilityCache(relayUrl?)` - -Clear capability cache. - -**Parameters:** -- `relayUrl?`: string - Optional specific relay URL (clears all if omitted) - -#### Static Methods - -##### `NDKSync.sync(ndk, filters, options?)` - -Static convenience method for one-off syncs. - -##### `NDKSync.syncAndSubscribe(ndk, filters, options?)` - -Static convenience method for one-off sync+subscribe. - -### Types - -#### `NDKSyncOptions` - -```typescript -interface NDKSyncOptions { - // Relay selection - relaySet?: NDKRelaySet; // Explicit relay set - relayUrls?: string[]; // Or explicit relay URLs - - // Behavior - autoFetch?: boolean; // Auto-fetch events (default: true) - frameSizeLimit?: number; // Message size limit (default: 50000) -} -``` - -#### `NDKSyncResult` - -```typescript -interface NDKSyncResult { - events: NDKEvent[]; // Fetched events (if autoFetch: true) - need: Set; // Event IDs we needed - have: Set; // Event IDs we have -} -``` - -#### `SyncAndSubscribeOptions` - -```typescript -interface SyncAndSubscribeOptions extends NDKSubscriptionOptions { - onRelaySynced?: (relay: NDKRelay, eventCount: number) => void; - onSyncComplete?: () => void; - relaySet?: NDKRelaySet; - relayUrls?: string[]; -} -``` - -#### `RelayCapability` - -```typescript -interface RelayCapability { - supportsNegentropy: boolean; - lastChecked: number; - lastError?: string; -} -``` - -## How It Works - -1. **Cache Query**: Queries NDK cache for events matching filters -2. **Storage Build**: Builds Negentropy storage from cached events -3. **Capability Check**: Checks if relay supports NIP-77 (cached for 1 hour) -4. **Sync Session**: For Negentropy relays, exchanges compact messages to identify differences -5. **Fallback**: For non-Negentropy relays, uses standard `fetchEvents` -6. **Event Fetch**: Automatically fetches missing events (if autoFetch: true) -7. **Cache Update**: Saves fetched events to cache for future use - -### Sequential Multi-Relay Sync - -When syncing with multiple relays: - -```typescript -const result = await sync.sync(filters, { - relayUrls: ['wss://relay1.com', 'wss://relay2.com'] -}); -``` - -1. Sync with relay1, fetch events, cache them -2. Sync with relay2 (now includes relay1's events in storage) -3. Fetch any new events from relay2, cache them -4. Return merged results - -This approach is bandwidth-efficient: later relays see events from earlier relays and won't re-request them. - -## Error Handling - -```typescript -try { - const result = await sync.sync(filters); -} catch (error) { - if (error.message.includes('cache adapter')) { - console.error('Sync requires a cache adapter'); - } else { - console.error('Sync failed:', error); - } -} -``` - -Note: Relays without NIP-77 support automatically fall back to `fetchEvents` - no error is thrown. - -## Advanced Usage - -### Manual Negentropy - -For advanced use cases, you can use the Negentropy classes directly: - -```typescript -import { Negentropy, NegentropyStorage } from '@nostr-dev-kit/sync'; - -// Build storage from events -const storage = NegentropyStorage.fromEvents(events); - -// Create negentropy instance -const neg = new Negentropy(storage, 50000); - -// Generate initial message -const initialMsg = await neg.initiate(); - -// Process responses -const { nextMessage, have, need } = await neg.reconcile(response); -``` - - -## Protocol Details - -This package implements [NIP-77](https://nips.nostr.com/77) - Negentropy Protocol for set reconciliation. - -**Key Features:** -- Uses range-based set reconciliation -- XOR-based fingerprinting for efficient comparison -- Variable-length encoding for compact messages -- Frame size limiting to prevent oversized messages -- Automatic fallback to standard REQ/EVENT for non-supporting relays - -## Performance - -Negentropy is extremely bandwidth-efficient when relays support it: - -- **Small differences**: ~1-2 KB of messages to sync 1000s of events -- **Large differences**: Scales logarithmically with set size -- **No differences**: Single round-trip with ~100 bytes - -Compared to traditional REQ/EVENT syncing, Negentropy can reduce bandwidth by 10-100x when sets are mostly synchronized. - -## Development - -```bash -# Install dependencies -bun install - -# Build -bun run build - -# Watch mode -bun run dev - -# E2E Test (requires NIP-77 compatible relay) -bun run e2e - -# E2E test for syncAndSubscribe -bun run e2e:sync-subscribe - -# Lint -bun run lint -``` - -### E2E Examples - -**Test syncAndSubscribe pattern:** -```bash -# Using your own pubkey -bun run e2e:sync-subscribe npub1... - -# Or with hex pubkey -bun run e2e:sync-subscribe 3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d -``` - -This will: -- Connect to multiple relays -- Start a live subscription immediately (non-blocking) -- Sync historical events in the background -- Show progress for each relay (Negentropy vs fallback) -- Display live events as they arrive in real-time -- Keep running to demonstrate live subscription - -**Note on Testing**: Most Nostr relays don't support NIP-77 yet, so the basic E2E test will timeout. The syncAndSubscribe E2E test works with any relay (falls back to fetchEvents). See [TESTING.md](./TESTING.md) for details on testing approaches and relay compatibility. - -## License - -MIT - -## Credits - -Based on the [Negentropy protocol](https://github.com/hoytech/negentropy) by Doug Hoyte, implementing the range-based set reconciliation algorithm by Aljoscha Meyer. From ca7b32a748f454bcffb3a6978dffc6a14b2856d5 Mon Sep 17 00:00:00 2001 From: digitalbase Date: Sat, 11 Oct 2025 13:09:41 +0200 Subject: [PATCH 010/139] Correct docs:build command --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 87a316c11..6967afabc 100755 --- a/package.json +++ b/package.json @@ -15,7 +15,7 @@ "clean": "turbo clean && rm -rf node_modules", "format": "biome format --write .", "docs:dev": "npx vitepress dev", - "docs:build": "bash ./prepare-docs.sh && vitepress build docs", + "docs:build": "vitepress build", "changeset": "changeset", "cs": "changeset", "cs:ver": "changeset version", From eadc6dc7c004b4eccd40e2959a0afb6caf8b1608 Mon Sep 17 00:00:00 2001 From: digitalbase Date: Sat, 11 Oct 2025 13:16:08 +0200 Subject: [PATCH 011/139] Also commit lockb --- .gitignore | 1 - bun.lockb | Bin 0 -> 771588 bytes package.json | 7 +------ 3 files changed, 1 insertion(+), 7 deletions(-) create mode 100755 bun.lockb diff --git a/.gitignore b/.gitignore index 2d3ec1cf3..618531f68 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,6 @@ **/node_modules temp .idea -bun.lockb **/build **/dist **/lib diff --git a/bun.lockb b/bun.lockb new file mode 100755 index 0000000000000000000000000000000000000000..78710d3650ad56fac9732f598950b2df2fc28b7c GIT binary patch literal 771588 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 >vZiV9+Xpx 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

Z_e{r=(=Ls$NecY&tI4*ztoTT@=dJ zj9R$(>b9lFwoOelclkU2Z?{es+;FRqvlY~B2D&l^XdgbsyekqhdO*qLP89m}GebR% zk3qQUS_0RSu8|(+>pP}~7DfGv2v{+KA*FX& zB$eTQp0mc0>>l10Gl>d*nru#XdOu=rPw2ZVW(i30mYOll-+256xSc?^_|J<(*uzxH z*0lNFn)P^f0{bcD1M)$e#*{TKdp@kl>%e`p>jRVbiSK^O{$M!2|C&nX?x+d>{E9Vv zpb=K<0^oK5U8g`H^RvWQk#=!U&1$1Sd0ydsuf|VD%<(Ytj){d|3=EwInzF8ZD+3&` z+iGdW?&w!l_e;$S`v{3G(B1Bjiveyo(9L~(-`spNueetuuF{Mtb*oa%c1x+OHlmuJ zBHHnJTRFv~0dE-JNsF^C%yjNRhDCy>tK{4jyVrY`G>$-j6LJ=Wj&BdpMc|3ywau$t z!ll!j>>RnzntGneDcJZxd1Zlw&XhJYy{o+YmeuBs^<+G8+Ctib-tv1<-$=z5LizM9 zf?hiZhXA)1=)URyt~}d3BvvuPvorSXPhd6UQvQY=%vZFhhaATN86=jO{6?7XpHge_ zKDyrG8Bc%F?QIag!heeTBA7rzaReOSKA^irh3%*Ebr>@(F0WLdg0_U~_NEus|Gt+- z@h9rWQB*(odQWl5!ryTOrMfHDO+UU5M1^9_Q~dO@V|2^~LL!hoFLWIGfv#)bl@1;* zZ(I&)n(J$V6BwQ~PR4YP;ZH4_Lz3h!qM0k(MVlUi!UA0}k=}xKLIP}K$b~xytH|QF z2)?5BtHAjT(o2H|Xey?@B=&HfTHmh@;RZ)NwK=cl=xM&`dwS!IF|7B=vO z0=o0zjr);=-;jvD@TK`j*7PA4?R`a{VFW|adIzB>ID?X7o*J4DEJ?!z2NyOe^lK>; zN?-_-+Y|C)1y`aNv)Z{-(#I1#?IrZh9f{lYi(aX|+P{*F=B@a)!gvHH5)2trs5=C7 zJ1sYFNf@b|>ePO0E4`DF+I%gOJC$@UTf5!l0K(FyBURnzIkQhImc_?*zFyUkk2 z*b}>R1rUa*ojy(oljvAjgAM5TLiSY9012l(g)Tk%@!2UJtAy2?8nL8<23+TVLQ!xAIlYJy*oVu2 zNoTqK_Khu~YBfS6b7c4JM0QEQ)!I1s{T{kQLJpPr_uMz&9>e2liz8V!`{=7z+2so z`>4sAu3cwD!`%GWO+}MVszh|9ezxXE0CybdrnG6dVauPsaY~1oATSnaxw<&;>2*Xy z`uztVeSInO)7uLDq2Cx97?sNg`wS)c>CCok^ePx8sIN+1b9_ECjRv^CfG*?Gdrwtc z{RLfoU&A@~S03$#SaDqD?o$=Dq?#~H=N3QeQ@skkh7|3c6avakqDTEv8O*r9=f2w? z+*=cU7>5J66F?Vr@4`JtjqE0n>;XBXDRo6L&d~U<$?*g6#9L~~H+)8Zxy_`#k9CUg zP7$l;h5ZdF=>LEo@M@$_D^3`F_*1+A?j+E)YZ4q8=jaHpMz*n3!M}d?MltYswAGM4 z?pon%)dCS3wQvvu+``Z3_fJogvGoKhsLa(@ja{27Y^ZIfB$~(U0Cx)LIx^-^`h0c| zXBL_b*Dj*{zQyn8YS{5poH2&w)FQZU?gtUNmRpGuV~)j@;hT{sOI6b1_WXy3^8{t? z%k$Gd$a@H&_ro;MO)l=fh5M9m>eR8?^eDeC*|m`OVW17x0++9z2JIONg%()#{x_wU9(ZrLJA2yv;yMkCa30EOUSgb< zO}aiWrGJxhF|al{8>-o^=+6;-j?|($+9VEe=Yj5BuD)ZS$+6g(+X8)mTsIrDFN$_8+(9UmH{HchDvNOx!*K+y$VEaE6kP zy)5^HYxD4-AEt2yry{jK{o`r`91AfsMd86U?e&*+iRRrTJ?#6%1(W7C87&O_!A#*& z-sCrwQuLmj02k70hXx3qOle%gtZG{!9{Ds&HH5ERMVQkMv8vfJ7eUVEpv~Y-!g*?6 zOK@B71BOQHm_tqG!2ZV%DID`ryT{rCFE;7H5cIl1_G-`oO`Zsp@wW*zBH#bubeayl z)PK6PxZI>}V0n0uWU5=SDjVC-cYko$L_}Mv^{Fh7+he7ftmqh=fnA=z$%kbH_Rk=_ z2xx#x6I$w3-%cM^N4lyeB2Jr z5wA)pGoAE4;A6>5`NZU3DEPZYc0wIH5blZAjXyi!I@$K+3xEqbb3p@S`tE6e<*U&b z`M(%jDq37G+>oLOuvbHdmq~OiZ?aX`7(4C|qi;af zXO$V^t7n~z>7LgFIxS@%@sZmzwt8zy>Pwiz`KLQX1<{VmSKy}<=F8tZJAk#8m)Q&zBur3~cr zpZrNQ$!cS-I!sv145A2lq;J(+eBF!3UWpv>fg$MlZbDIT1|hR|I$&DvG*T~-P%je^ z&11cG8Ep;W@RDMg72Ey5WQ=T7d^L_2Hs4jXi?nKDMEd^DRde=aEtlPIQBSNS+#ld> z0bQoCPb+V0Kb1u+R;sis8vaZQQ7R$)+f8cDA8I%^o`tC7IVGF7zHz5GE`o6<%J_1u z!Yl!&^wUaE$Dz;R??>SCwheR-HAQC#LD=DP*EkB3X^-lB$C^nov2R4EiXJM~;rhbn zy8qiX3O9vjP z7f6oD@5H}`Erz})V=?2r5=m`p8x2t^D`wIo&mKCTXcPO$P4C=OHm_Kc3x6aWe!5L) zXHsKuQVyi)uWy}b2Ci?rKv$+e#`Tk~$d6T9UCbBUaeSsyFGLSlmhq|JW4R&|g1?n6 zob9mG=!333mPkG3g{}#G5-9Xn=lZqurC89n4txRi?g8B&N=k=@%57?fw0%V?Bq{J3 zT(327ZTrox`j=cuK&Z);*IrDYJEuPg$QL~Ptt|eQJ1zUE5^b>FzZ$My{S1X4;O+z6 zhFckCN|JLQ>|s?|D-ygoZki{J)LPY;p3dJ&oxMlFNZDj~i>d1(+Bq$c=o%uHic+kv z1;(sadd`ikHfqqh0qz0N^?J{<$EkRaKG?j1+&H{G{TYMr^kZOj-c2P=9@T}=!3K)- zrl5!zWMuY|k-iuejBIVLA37@(?*Org& zGZCw0<_RJE<)#s}!}0;M!m~2jAViOMN*OpL_wQ@U2XGHso-UKSCCcPHj?F=kvkLTi zgX}G#0a|Kcn5hk1@hUnk3HQ#ueq-Dj-o$d6(s0vZ2g*jIHZi!niIZyPWwW|XNg{V& z36==)<2AjNj>XY0<`l5Lhpd%Q_ZW(TGw3JA@8J3~cfxmKDvKDGU$boj^B4ywEX@R_ z+lQT1V;)?eKT9qPd0V_KO-yYu_RMwNmfZ60a$D7&0`64OxEeSPCqUOZT@gX2`(QB( z(YS{hML?|ENdEPYr6VC7c$4z=uc}zy?U%zPHX1$^m|OGSwmYWH&hHZ>UZF`pYi#s8 z5sYF2)O!ka)pK%xU@u42MJfi6+L{`m^O+dcsw)SWTay)?w=*5|ppaQ1F~0cAm+<7z z%gTgoN)s6 zlM?Hy;~<%%CVgkwJS}QA`RQBAUyVchL+%o9PpfV2}Qvf9M#_MR6zB!CU* zI9vhUjM<@wbO+X@ot{s&gC#E)Tyt9WOBbho7)Ke}()Fg&j>!me&R#H-jV!%I!k&q) z0D0t$Eg5*AvBjTXSPHyX0=U;ew~J(!-u_y*d(a$3yX`sV)>->$`0HAY;sdm}zC*Tt zH+-3edget+d)vi?fsEL#zovt%FOfg%D_e=1bz(eUhn!WQ^+NVk&;V6(vI!_o9JS*5 zsIoDRiofIbj}cRDa*Ti-Iri2_&%FnEF0YSA9Q=JM*UpRw_v7!@PKPN(;k190 zx$7j#|HeqrgDq+ECCHS_49Kt+C=-K)^r)fk9nhVO6syF=L1PK-{42GGYgmm>cuizC zPqgXyBMYB5PdYGs#A#VP?!IcC`#m-xts70?w|b!k+{wFXlF}aWRZ>-e3)xFR1C%m% zNS5(|0iNI1Zf(G&D(dubmxk0+VUI+C`)zknB*) z$`lu*icK8HK=@HYbhp^RR{I3T?g3VgLhmw7Swu*Tw zkBU{IiL&Wh-d~QpO3ez9*ziyf;64G})pvdJ*yM3brj}$xe?E&9cFj_sWZ~2eR7JqYAJ<+J;jx6h-{r4`u?hr* zDt^G#1-P(4*Z2+6A6O>i%i_lFL?#5Os$l-zQnLuTB0hu6oR8(>4T#~OnCQzZ*e;!w zp4oBIkCi5(L&?ToBvEo_V|;&;ivTVh(3Q#QYJn~I-IEahssV<8+r62!>kDe0Pi=^9 zpEU~dp=NzR=NLrO0kK~-x*DVi7@H(!8m zVj9}3@%n5}Bz26?pi;OBDKet*OKu*_33i&e#!tdJEy7}gQn}nZ^0WKLR?(b%BV2VC z#Za&W6kECUKd1G}fc(ONuCje>#|)44#SqrBzxDR>5v!BPb-V4GIbo4->NFykv<9ri`5-%J+Z04>~nlm-!g|+l(l()xIcR-XTB?eps z(A~PoKVM}C9&};LEF)Dc)u|QVrlTru7FG6(jof~vcAfyI)UmlF#yqZ5e>Py4Wv^V8 z^+nE!XvG1OoENnr6I_o%1YP5FF8c9A)OwQ7HCxQJA66lIHq`>Vd%c$*kAnMP2y`g|EM9Yqj#@hrh>{r%`wco?Oou0jygr$f>ils5kB84@( zd@sUmb@yw4fsl$%Y-d~tjxjS5;VlGvuSSB)Fq}n9Cg8pV-2#g_$|i9IW&UJn8INAp zT=&6a(n3CSv@atwY1#2*xP(I}TRulNI8d0+mkY}tQi#}DuYy?QjNd&#LP$!hB?2xg z=sI-0*76_~Dg6Ys_u2L_#y<*6T+*mIP(~H)X^ZhVUm-b8oz9*gCej{a- z#1pRuq1hknF&nRidqx2l4Rl}m+6BJ(5Un&2?18Q&vaJ!++xT;b7(v-P;bi8R?qvLj zeqTWQ5o4k84LJglLGRZ+NHjA+F1Y6nm-+;yq|Sf#SpLf|I_Mtze$W1ZOY2e{QX)1% zI~NegMbEBjj*(%qw@kP{pt&#Pj@i0}Wga{AG4sj5aV&TqgSfHOOk<>;tWAB1nF4(N z#Q@z*cw1Qf=x13WbZL( z6}+Y*{aoo88!N-Y5q!=Nw=6p2kavo^+v1E#d3F>B_B&XhtKOLE?DyvV^t>V~oO_pV zK~u+&TZvwwI?}}CM1V!L&TuKDBg*+v|APmc)`|DH#;($~<{EOU-L$GCsxVb5j)S!;mK){T}2wR2V;DRFHoEa)@UW?;~`QRzpZj3EL3lV7@i8 zlTZ3JOpPBm0^}OPjWOWjfNm0thw&it@U%!n8Fmx?(rTXw$zCyLcVun)yE`c|nvSGx z=dUk~Y585GE(Q#CJ&+rd@)Ri2y02ZEI4StJt2q>LVU znd{G)PJ5YmuU;85NQ};-xUPlUSVD)9%33W^j&7IGr&Rzh9_WUr za4RAEbGn2|sTJxFV%(G`$ax9sqj>LXR}$^b1`D-7Zi~JXhOo2wMMoR0xW?c2DvA?f zT@2FtE=h*>>pOhF#Rpwz9wX zzBj#>5}`WlHL>=))h%Gf#dxRC2@^zl~JF^Vy-d zX2rCYkC|Vq+8n&5T_Q>G6p)cRCi!gteLrV7kMpoDt1yPo$EodG+Yf!FmP`a3z$FCT z9vCY15CmNPsW!Z-@25t!3$9l!OL6AQ3{J&Z1Gtm*`EM?QxQ`8J(0OHo4WZ|2`r@`> z$CqO+4qS?LfAtB2;}8+()~%8$I8(FIKonhT=&RXlVatvg-StDqWqV>2!q6W?) zc2D4xRZY?15u+~>GSYOEV|6(`pxfkGhuc%u0`U@q?iF#dx}-wl%pHtR1pKzZ%&VJW zZ=@p%&66T1T7h^A7z8Psm0~_BSIS8VC54C|J;m?!qO!hqz`Xe$eD=_{C&%0tOn zAvHRR4~4`=?*Ht`|M$I-f^LKBK|sHvbg0|4fX*3Oz-v(KYK+kBGytsIoQur`e|Ow%OBwUb|0q9cQ}KD zX@Ron+8Jqlg-AtH`i2E?$w4>#+_h%Z_{NAZ9qBO-k#4`P!z8f~3uiA!7K(`FeP7qx z+4B+2R)4EkSMUm+ZgR9Aj)(eGR47gvFqVVL7&A5jmjZNKl3HqUFUL-g33Tfn8CPOW zVd?tM?`hM_hiIpAZkJcMo6Qe*CEgP9eVq<-G~X_CThxOb#A`rDpcC6LkhDhxTuRWj zN5gHi>!q@xr-*g3zNJlgiPF#$-EG~4^f*$nR`B30k8?bs zp1|dP=EL*P367Cjz@-A+`UE@9P{;;CGcJ1EdC%rLLR0#ISGndW`Qb?(r$W(A2|Z=k zrucL-=*f0

jeI{(4T5XNXttf0`sT!FQp9&rz>IH&_0O&u}TD^rSCe3R-t?Y8sj_ z8?Hjo#D#Quf>|C{ec}CzosK>hnH&W=gh<$Kcw(#mil!;X(_s^KZJqWi*xsl?cP~!y z6`X7$rkik-#&TpjW@0osRag6)0OW_>>y(yD1ef`7Gm0zMHzOHn4*ZD07q1&h&b-tS zwANMilPC)p{DC~sfUemO`O$gcbN9(bF4k0akp$n1#NR9n6cfA8m)((B|h z`0Agk;6j@!fq3abH+XwNqou8dXn6_;H$M<(@3UcfQN8Zw{ihle2ubvZPY=B=@8w{k ztv-EolbXSJ&QuB_CBit(PCc2b#<4mpZUS5e&^=(LF0meX;#C=xlol%AcL^s`{lrMOblNk6W7B%*y!k7(GB9A;ZaP8UXO*~ z&n_<8{oR1e1iIc(+e>-8wYZyS*F1ETZe25$maPw}2d^~C#Gp@1&}Wy#kiRxIp;>uc z$YPu^uIMb+U!_u5_XlsYCO>;AN&K_d^k09*47!5jvlmLlW}JnGH*A~~l%AqZ`f>!y z()kb6ia&2D1pm}nEWAd6^)#K4a1CYr*mW_s45%Sq&3WKW@90%q!I9ukt?w ze?LYt)Fp!XW&MBL0TEM5W?Sn|!{!DyB5-vVj!ejUcRhLE*m~i2%xa^DwgTmy_`J>J z!li;U=6W$Pdy#eH9Hye-_xBzpEf+ok@v?!gYfD8JDSJFz>=U0q>;3#g4fi^Q0WJsVnujG?k+~OjOuKG>juk5J#@F_JPfM>tXvLlSiVUVGiMK|&6gyY>wwR`t zzypbmZTRaIs*)M%GUu3#9m0uu5a7N6T^|-wPFq!nx!AW;KI8!{F5R1lr2gX~y^^}P z)tt7RS>=6@FNNgHggzj~&&58KgbDgfC}G+}^V+C8PQ85Ku>xF9(9O<=#NBw(gDG{) zYwa$}^zWec`_ZE3GD*>eI@B|&?C#B3$^>I_qSpJW{%ri!bOk@5*^d&M-}WRnvJkbO z$!-Cc3v?MYakMw6yMMu?aZ3L9In}@BMO>$p*xenTuuQl2M*pbX>HX&##)^?hUYu(*i5K%OklLSheAhpbMpl~iQi37_3b?$W`=;ORHG4}A{{g*yF+1lFgJPI&Te9BPsOaTMFgDJX7_ph@G{# zLPtHr`5kqZPZHegbyZ2IhW%i>aKPmQT_S}Y^%57gDFW8=@snQRtfhm+n zZG{Ln%hHvxH`s`Q7GrRh-YWO>HO7xFB$c1k@*+qcYUnvG!F4Wv&@Bkdi@CLj-QZJ_ zmdqAWT=*g647GcpAk2`u5zk0NJxJIiX}-p5BsVY*)2Q%hwL<{Ub$Mje-%*zyP$BcU z39iEkfbMlxxwD9C&huJ?m+CgVZ3MEA>UN6{WP8eOhlc99Y*hBQ>q%SmH&Ghx`?m94 z<#_P;u$1v$pUKqh6_Z#bx_N*+2!by9mFi6GhUTt475-gek5qky{|#?)mb_B_d*0-L zZMcng5BkdYW4c{K>78LOb~3*3#)eRDhD)kjCT-H5vhk?^*uk;Yjy z;wH3*Zz_e_@YUXC3EHIA=Y_qgQ0zGks@{NkQht~DVOC1VCFHgU`32Op+!?&FY=#S0 zH7?0(2I-um7u8`652@PX@jxC#K=)T#3wy9bJ-SAGK|-66oC>?r^);u>ca~<@C+jo5 zbJ>Oxl?q?hPHmRT!cdce2#VAXWuv3v_e*at3Kq2z<~0CU6m-Q$EqbLx5_Ufr8)hr- zRysHP3orZ*8ta$JjDAaRe&3bGt**daz7FT-(f+GHSb8TrY^>iaGgWjyZBKw*W)n987YzDO0V?$G8qX?3F~$>ESeS?^ttA-DJnvcNUoH^a|pl zHmMV;(O+e!hps-k{;EIj()${4#XmHFZ7RDDj$5 z4~SP1bfqH$$2g2;CN1;Nw8Xp=Ea=cpbt7j>gm?6cU>M(~s_$jShQvtEH%5j}3hI^`Z9(@@=}&`s97eS&0NB+?cxj zrLoDD`-b#y?Z?Qx6N??~wrOF>HL?+&)F8UYH+SWaQB{B|4Z3qvzA&Y^^@}BJBg{J( zLYyB&>9Vy5XLZsdJp{Bn5|uHhAL%*nma+WH;omBRy^x_<^co}mSvHa~DI$>j1*HLS zWkA>Ttp1t1EdoHv?Ta1Q&}NdT%Z!P+ z5fsqsEc&Y2vaf9eDesGJTD^iypkL39rxQm*KiCd;K1;U^+6MkH{=0wWz<9-NS#-S7 zIwcI>T_rc!h2XUd_6KKpAJqYL4rc zI_fJ0?*}E&-P>$M=CSZBj_sn@qhB18nXNSE_O?^_Z8A|CG3Bem=Mi$N&Tqt4Km#jD zM*G0IeVcnXh0*(!nI}4g{~9V0T%S}1-3lDv0%G-=$r#jGPEMJ?cBN6Hq|S?74Xybf zbUUi1cd)ft(=g8RzaX}LG}3M9Zj|Ly(lxX`!&Qd$MPtl*fX`wFsg^HiJldLP-k9#Lqg%<)m)qO5yPBhJ^s5FXAhlCdO-4$TxCzy=KD=-33Ji* zU*Nc@3cBQkZ)KE4B%-!SRmj?gD36!rkqx0`2vT6t#&l{(NkXG5x7MwG2kD_qzVNE9 z9td>(L6$5*H2TyFQw>Ar)dLNDZ)%_$C^6PgG)KDAJzehOCgwUE;3{V?QnHCxiid2~ z0GpmXilNx=d``M`7}30mH*H86%?8zv8BW*NMc425)z<@D2UiE(x(~}b)cEeQWg!Cd?MEZzqhiSY@>CD5|Ho+7$__M zbJq30_ND>4H+Z3R7uD~|6_=w?k9FfW@lfm|yF`bPrSh3-Zt6tK9w9&G>;+_nEN~WJ z$jg^i_peDKW8PJ~9@T!^Oe68y8sBZNMW z#=!1wJ$H#mSHt?&hM)VveqGm1{`YJJT? zWoA}E_~eT>EwU>2ulu(S##_*RN7~_#EsMgDU67M=s3nuL%S^|{weGqs+KKgPmqoWR zxI2l#wkuoFvX3vs*$cX6Fjxmg=n{$9#>O~K$Fz<9Ul#&i_|H`vbbZGt*Ia+7{4T*u zi_K^88j(2)(MAvzM&s%K>eR2;@XK|#h4AfO0@TvIOlitUN~CCGgGesHb&OJAebnvV zjKKfm{d0lP0bNM-nHN~!F5~(qzZ8-wn-s0cj3;0@#o;iK4DD}J7F7D--5IakMjy6K zup%ecruD#k^+$a*WBVo}r^kWfNb>Gq_iw#HUC=cU=JsuEkZ-=T6@z><-ltG%mi(~N zY+>lRmQ!~%UYD?XA|zlAAtfe_v^e#^%-3)aYZjY%xd27PUp~$8OlbJOcwhaw>VfWe z>U#A_&Bn^#rvaAtGuvsu(+0l~HLk!dN2tH(+DkdKm@*BN(8`c7XMVYaw~3wl_4jxX zN4vuf@;V1fSnAu+|Hb>~0-+DOr&w3@>;q~=>mTnln>XHxriAk2NO+U5Tfng|sWw_B zTf51NQ}oE13~^Ci*Jt0lVLlbiru}Gj?0GW*mxcA>?>Gu`%cRPFls z{3x?f<9Tyf#2#+EOTKQuv|VXRKMnmC2|w0J>8rIL)RuhogJ|0j0Jir4>=>qrxj{Q3X2E-)G(yF{Aokf8BrUF&Tp{ zmZHb0yY|aAt&3Us52#2m$$t*+L|>|;bU#dKD~`e?Me!7GP)1wGtVTV-zR9S>ubT>> zyKh!T3Ml#%adALh{;&JDPNWIwax@oBvgep1y(sM4MQ@Uke_hn|(yu3a{AMl(L;rn9 z!7v;ezxm3?9v!$NpakQ zzw3Q}T{F-f2rdb2`T&`tbA2?2%+>oND@1wdID!9HCr>4Am|=lbiKt8d|wM<{F{frbqvixm$N=_&?GOzi8|8?V~gK%8nR;gLrLCY z#<5_z>XL`;7CxD3?)a~gU+vYKjACR_*B%b{X%?t3s_JW1zf3ysQT}!R*2}a2-96oi zx9h1igaqB|hPOhIe7YVVtKMcWpI2;ufn( zOH?h^Mf~gjtsi9xy1B$R1XF6*{-tPWNEFKtx&&Ci(@Fny56kIH=2(43!c`oG zYYoo%Z3DQDX=MIYD;`R99`hR%tlKe^I{BZtI{$V5)`7DEU4~`^Z)dYv0|=YYB0SEm z%a-un7C}*Ju~geLB%`PHr@gP2S<)L}8>CSF` zRajQ@M+x?Z{ilLYrc6kQmW~^8WAv5RVivecF@F)okdvzGaW(BkP<@C6 zx43lpKK+#CfARj)`PhOk+p7))OQniC6TAM4DnIL6oQ6ReZR(kHjmK+fiJD#^aRp!I zU9t^aCEdZb`_CT6Q9G=`FPmKRRfL<91E?7P-oJmc@W-?RT}DcR)6L~DI)~qCt#3=o z%uHt}d0TkYiq~XerBt|^0=~@KU!y}~1;-V98JD`_6L}DOs^^9*+TqTbsxB%a_5C+q zsDE91&~<}g#nsZBW4tcd401^H zm8q-zVQ)H5RqwVyBFLw@cjsUCZ=Fa7(EanfC!bN}=h??%(&u9YJ@zhy2l? zNn(7kiEIV!tS4sc=z@Dna)N`TJ43hZa><0F5%sk(bX?wn12IGJ=U2jcO^ck)nNpQP z6qs%OFAZ(}b^o?wC(zwJzU_g4?a4V8CBw)%Te;ouoxe@6Ipb(UJP3ubD%j)SlzFe{ zD3;T!HCS9Jvb>(7@}=8>nY4uW!ma`1qqx++?%#398FV3aqTo574=R`WXNdaVe-ssX zQ>$Srcu6>Df?)Cc@A&q&z5V;+mVf@8e?Bm*!B3f)zq&+m zG{pjnzrnQ zLxU$B;1gBXQ-X8;qD(q!webc?Ndv{bnzcgJ3qAMG+1(D;cRTE2g%Dnw?vkIhH2)W` z|DWpz(8aymYH=*Dz7%d5iVk+ppOv?SN0yObK2&>Ss2>r_D(m;#tu6H|{sYZP?ET(F zCj{S5$tAUbsq<{gyNKoQrxgFXf7^uz=zgD+ct7&{PeJu{G!D$y7}$Sa4WHMj>5L?^ z$$*{_G0y&NCQKd+3uAo>2}}Ad6Ldc)w-~bR z1vndihH6so+fx?~?Ag@gAy{xbp^0#`RCRFQfd(k4Z4hwNcfd2PzCS(PLJjLl(GaR ze{FSnnF~&Bi1Z9Nc#jUsNS=tj?`E}B6)8lC7@<6ePILTly~G0}>tk$GSwr@3e*ez1 zd_Z?COwZcxPO;7n(v~hD@OOZ0p+zs|mf+@Ptnj2zWiKRx?ioWsj~O|$wUJ)ZR=jF7 z;jZt_QoI$Z>|)t2ni$pp+<)@m3%V~!bm1ZLh|cL%-pl2e3uZW(kWA4o(h_7cL{yw7 zk~=YDbed7%4YZSgz3A4uBvXk&%8gw)B)`Qoi~j&&RP*M4?my3IexMsXR^`&pH(Vr= z950!2yD#;n(>3XG%>&bma+Wl@FIrsjfJVGdyo_sT7DvFmWk-u9U~8VXi^;ZCV^o&K zdcp5s_wRk?54sm)nwoZC>k?|bd99A44pl7T0YA2S)Eg$mZ+yG$fthiUWwT7cNjwY40Ag2f-#&80Ut&s z+d>iCzX$@|D13yxd}fGee#~;D;h6z~oQT>Q^{i631CM1F7Hvhyt z3M}50v{6Gyo14(SAMXFV(xGy`JgHQZaC<|?m-XQ*x_{{#fekD9`M|cJTu;cC}`9h6^h0u^~?k za3er>tDJ0LmTYUx$6hN=N8gngedi*)sUi3#!e8UqJ) zJgLm^^cB)66%pzWZ`DWNf5hB*7@LmeY~=sNXYT5`>$pks0^Y@-wMr5mVYv=8gL zS%}=MS`micn`wv(u;IQRIlezNK&1?>&u#{W*%O7K18x-H{`u?w@6UhhT+x6FgWbjF ziPai||1-Po+K$ucNKng}x6wYZRi67pm|L9$_7T=R2h#2=fnBv|1yeme501}NmGc|9 zsL9a5pER3bmLRT0{(KdN?(+tw89 zuRAj|(4P2m54q-A!G?@yPHBPT-MA*KiPN9|?SJvcg6^lbLG~sLD=U{1eY|GEO`;L3 zUqg|Z;1O;*<0>#CBRAyjPy6I?fCoP_nQm|;cJN~Tho@HR z2R*ANCCpA4CjA<2?P=D?-zwM=M5n%TLKmpgn==921kly`y21Z}>VXeiz_awj$?W9; zN}N>ynXWGybu^0urrYprq}b2g96J@A5~Vvl1d?_uVHs(|6uS^s*~JyCk#I2HM9{qq z%uVSI3!f1xP)KGiCABd-sHz(lhrbZ67HOq5qn^2c)W6cGZlgTEkZU!G^k)`eKwnLa zo(uGAi&h?Q!leV^{Rp~wqvsw`&g0YA_G=S<$hbJ2sLNC$%oz=0p3U4-hA44!`Nt`# zhq^&=7>^>B-}V!$G<}}}+$7MQU9*CbH$qzyinsa+nN|VAPTLRN z+k^!HOB`^kc|+BpR##r(&Z*FI=Oh+{^R7<;lSdcLj{?D1v;_bog_Md3M6bpNb&ndBc0joE$Vlj@nV^7xf(V^qRUD!;F!m^|y zAG+K(vQAya!6F*K{RFy}n7K6!HKtI*ah`H!)Y+W$PPGC8>kGG?Rj%oo6$7CpFA7(q zxsPy&qA$Nwls$%Iln+0cGnL`1E^0H$#K?p1x1@k>ZWvYhBD&S2YBe^bM&mE`oLQ|# zDG9d9z@zot0BWaLz2k(5G@^4^%DxRIQR6R*l&AxmvRitxuv6oUHnU^dK)k7-%c|Ut z219!?+UIQdgVq=Yha3HF__g2W&Gr1SqKI?~BAHsM9&F2vg=pzXfm)RDo|nrQp8oGC z&ZvEhb{uoG zEg7dQ=>~ht7%nxZDmGtM_ZuIDcP)Nfv^|DM(-=H1T0@AbnVPgCC?t<2`2cPP=x*mQ zC%KlwK>T^{fJMTX*R4eL%^c==!&%dkQurq$^nu^v2r9>`=quaFF!0pon(20$a3`u> z{=2x$s5`=FcwqgV&!GD{%S()*z1uxHVb#dryzl16 zz&4?pWt7z=8U`~@*j;rOJ$?b|?**+q2|ulXcr!uQI=MNeH|JYUapGo_UsuSLY$r`c zEgua*fuhC6ffXSN^7|;F!|UxvcEMIog35H$?}>_JrK0SAZpfc;6S%(fxS*N|TM&!NT;&?07gp!W|?22SNp8d%(Ux*%hw-LEbi@pVy3FGb>Xl~6uh#68jYtKE zTTLH6Hq#Nw9_S5~vvJ}C;Br!kwyp<=8bcHWZnT6Q`tRwKGzwxa{OShskPEs>OqXAM zQV_G5rgs^yY8mrKc`KA;qwrS5j{#L?d~7B6pR9);KkNz@%K16{T}S*o5BLJQmoi6*_irS^ct|9{UP@qw)>? z0@Bl1!FOCp-n60QuyTL`*qkTkyV0o+e8p_3vMyJqI|OPq7H zbuuT^MOXN{c@Rbc)njn@!ZW{;s7xO*Gi?OsP-4QwJM`VVE*Uz4@@ZQDw+M8D?)P@z zV%a>8Aqp62DY?ieEwkqZKzK%Rt??PF)A_ac>}&)%oQ)h_y zcc&c4Uq3P7T=_UPxjR)sx!Uc$4>NB4yZ`q0ekcLmXnKLJ8ZpFF-+M`2inT3>Kzl{wG^ zlq0;`%aiMha=$!QsV}og^$wB^C?L-j|B39o8U129XNRID`RW_L`5=Y%Cp$&Jtpi;s zjns2P6@V`$WcAu(s{Z$}3H_|Vw~eRX?Y(H#FegU>r>HjxLqis?&~?A&XX`j4FBAFeJY z3EHFRtafy=-U9J9fv%>J^?8ft8^l(D$LM0R-VN-66`Q_X8^m`!uvZ(kEI8H4;_cL| z@DJC?tj95CQPC`4>GbjHu!=i!1dWpNvBCLfGw3pll@&765hO`IU+drbxwb3&u9NMz zoVv)66>{tZhN(nRx6aY(VJ~`Z$EHe(FeasA<9oq$Xd+!2g&9@t56l7awt#M&#k&#ls4~{E-r-1495bRO+D`+ zF)Y_S^Y1Z$+X}jSFnj)zLbZ9V{u-m?eJG@c=GvVkwilz&!I=zCYcMI3gM%q*Sm$%+q!onXb{c=ZX4*D>ub8pFcZPCU!(u|LK31*o}HJ605sIs5_lE9ffxO01l`&vGCY zj$ei0OUgo-=MgP;ukV~FTjAjj)pNfzj7HqhEOS4w;ym7s=oMNDaQPI!34xi_o?i|Z z@D&SiyFhnxT$1B#KQjm>v?ljQQ*C+x_kH9K9bb7zXMC1QJs*gIyQy!pqfi#GL_eRR zNj`Lt*@Q6JX|KBW7@XGKTzf77Za3(PuPT#1gkL|pH)0Q?!Z9?6HIE+^AIBw)K9|2y z7a0uMU;O^WlWiq*5b=5XU7?DNjfJ|#cA!i}v4&NXQdBb7ulInigr+K)fi$5$+(-AK zyydO00+=_4tuAEUOd`)lFYM`xyhHF2L#TW3!cZ3r@TSBrGgrGf@MAvdEeO5Hzi&2> z1LExk-BYoPwNnD%Sk znc1&Cg`DViQcfM{-&0{|j#AOCu4kz+wy=82G^6C71ucN9_Oa(*pmz)Iu^$P)BAO-I~b-?n@7Y zt^uzV=Fqum9Ppo=h!5+_^cRCiS*i#Xmv%z%TN2%pj4Kl8bM62!{^kv&u{Z^x5gc}*Z%3_JsB^`}M!9K#hNdFkU1{Eme9OC2dWs9uY%&z_ zX0jI)*0Ob!B;fr!1-kl`D7^;#a#OUzsy?kI+f+<65Xq% z5mCxN8&9p8@s%3EeCrM9H+WI1&Bdtucms}a)1XT^#ctkzjJVFJZgOTS!0wIQ>y0$5 zZSU_BAqI;aq!r(j6jQj@^_qSsoUe`N#{ERHKdeaM3q<;~_3Si0pQj9vhZ)c<-l>z* zf>f}8-9-Q8qZNmY8GB1-$L5rT{u#yPC$f&~{IZ*zWrNilV^k7JqVaf+f@O%)B~Py! zx#Mh0!@0)4=QaM$FJ?iPF#m_SFaH1{`(UT{nJkIn0>k10+kth}^YyRWZbq+ppB@Z? z{9cJ|YS{s|M$xIyOq2`BUoX60XFyHC>fnih^NTsqeZ9s^N+B3)4p|!BeWIX17Mj^4 zH#CiG+Q0S@qoHF5;`&Y@3d-JrrvF>d%QQ2rLBa1-FJa(ly>{?$4ITBh|DF^38}B^m zt_No+pJK8av<0sTr!}0=N2ix4p2m`Wcfb-eJCd3z=(wX!*{T<|_JPPIbZsJzu1vDl zg&`1vq9P+h2npd40`j{6y4=>UL_No@&sOv62+zTvJ~vWeNiBBIxoB zt;Q67d#4pkI5jo9iLP%UqHsUCWhz|8mq%IoJtY<*13mKV)qD2f$vC{l4W4d<$8J5n zGiN)y?$watns@)~bN{zay9Bz%D&cX~1gZ5O2Vch2q#nTG&h!lb7#=jJwd5|^<(!{z z5!`btG>q}X*6(}NC%KPN-{sYR{bCcAab%-Es`>`(AC^J4t2|x;(Ue*azNjL&;FS2cOr;)y7d_+FaMz4KQpF=2eY@4*+)!bWQ!R1;|!QMQjigEX7vNWIRe+ zkMDC`BJ(Xj9_E%+d2-=T3MXr}QsT}@Zx%VAhng9For#1$DV=N)uWOJ}-v!)t&@J6o zHc+2YHo0Z+Q&sWg*FwVE9wErWqdBNk&6t3htJcFD<>Hg4eNX8!OzQIclMI_0&R5?f z?!DgyGv=%!h}eL;0lMSWKIP=XN;)gCRALTR?`TxUjoOVGl~7cOv{yqNQJp+FS9SH1 zpt4-^=2}?q{0~BAzvE;!uD}X2gd3P8Vf;OZ_qQEyg02KQl;F@~C?EHRxm z*3wDb(|aEEY1&S9p>%3PSc#1{7HPaFj{5^hn+Y#dMt&=LZ(YCI7?489rJ@Dm-2z?L zlg?G7Piqy`ZyKe@ckm0iUPaqPsXYcTKV~>3`0%{%{W7?*dCsPpCE_QQEjJpR8_81^ z;B>kmzzg%j0D?gnaJNCXmTBs3Y@PB*%V7-$&TUSk?U<`+#TVS^d+NGxYBY8~`5GOP z6Qn3VE-WlbW&5xutJLM!;V1SALo6fl2X!bQ0PYUxk~mKw5TC~k@Y@}sFny!2%-$dH zN%u8{IeKgJvMD1^Hu_z5irx42?746G7ZWAZk$Xk(>%l+p@)wRF+R%oQivf2RbSWk= zjgvp$d`Kn6;(m7LspQ@Wl-^>dYW%bn9L&i;9^v zI%ll+pQ7*i$)25E%9z;)c@C#MV30JoUR><4&i2B7b7DIXAErf4_f+q|ozO^!cl}l= zqAou08dVMnT#q^c-A(QXb_WEwBM8!A*aWt@FMEw~&>@LO85{JU)r9@u{HguXt~%ZO zBl8m!0TW!uD+R3{McA?R{!7hdC;jlo~zl0q&&DMGkDwWX_w zj3*&fB$q8NGXK8$VoQE&D*|0Rgfo7km8^;s=W>)l=2M5o^dpx}pcVYyjzD*XHs>f< zq+oB|f>~X8<(1f^ZW!?!g}&X?xq;8K?N_uP)9Q0ZBQ{!jH`PUCbv|OE8NHbk-c?-R zE8d$GSj>3^#Cr_7+H!4@C}NdCe7Q&i+Shpy6%E;)FM@XOJT>}6B3_2BMtN98-~@#- zx{Odf8;yl${h)VLZ)xvY$b+kBFs zWCP;;L-F<-`xRU%x~ez#vm*0)7mA&*E8iooQ8sdCd27E`$SMmy$pY>v=n}n2A^fwq zw!i**NuuhR5;b)4f%yE$u&e}D!S-cG*>vUj$rkOsNGnQrhHKD15| zX~7yci!0!sfvy{`RhRT?^2?DR5>;1xI;l6t=vcUU&q68Fb~p3#H}^Tyc8DTWI{JQU z+e{T*6ypuyY3!Nb3n-JIp>(mX!})H~`7s2y$?^UaZaasrX5LQH z6<2qMdqmE3qEo=MgYcoqYXvYEE3(zeI7ohpw>?-&! zp-N__$<&M*n%=qoA{b&-$cXH%NaW?H;u7V8+!tXFseO>kLN_j6RP;T?0 zZQoh~?j`8vJgTurl9Yc0M#2=DkGl-Sii#iXr?5eu*kJ3cc|yLoZ8UjNkbX<;KTF~=SCp~_5WP;U z-iTYmpDg7=N`Fd1b)vW{{`DC3S{d&f6>Bd;W-3RDOPEJ}~J5eoc#9E&bLAn2X<@tD) zvIua0fbP@11PvnGE)C;qSSMw(iUV{x0$dyfUefxEnA4Fur-gpJ)YWxt zmjU_g>3VRoeyQcdpC9JqJDddE8_?Z|hWs5nrrUTOoo}j~zr2&jY1x!~Y-SdE+es%& zt6R_f^*Zq_K~=(QzRu&9f_3)h{V&$1XglVOI7m@>1!lnK-&@cvP=!G#S-@gyAmw@g z5ER%L=#Fc!8>lJz{KaQ$S97@A2*&3=*xQwfseUVwJ#;YlXG`{SCehcn`SLx9*>1GHJwoZ zsUfvD=?x+Nz!qAbG9Q-$&b#kGR}}f|N~*gv?lW|p=Q7XaTnWYjFYWGJYf!xhgC;^% z=r5J8ANYUZs>(t2&Yl~Dr3hf@yJ3p%GRlaEznAW=_zdLt9(3`z9pjs>x=n8D%KGAH zY@+s4(k>t3R39l7+H?q_JfaW6F1V0!Qt_m3NiX}&XrJQ;w=+8W4bc&lPfCs*cvb;aj?-8$`N8q`RfNyFp6n4gu*7Ns(@l?nb)1ySpVNUibFD z$1~sKd!4KS2cPj>bNy;wGnS4);_{`y0$2zC1G+0#M?Aa61NkZZ76J8s^>@K*q?97R zT`0BpLea1TP=h2kvR@&Y?8Ppeir09rk5wFzCT94OBv?z;VDK52tY-pwA3>MYv5AYG ze-M?Cxpa;Hhh{2n=%<)$jDb$)4yRQ;6#L88{Hg-Vc8|Z~pNMTAl6+nUqeav51Z11+ zN7xw(P;G($_X%+S{`>#=^}l}0GvGphB_GTUsx+Bd=Cz(=sagsV^AzfMjkk@jhAVZD z+P%{$9Vx=qkzYi9Dk-m7f7pe z%E_P3>nfJ1CP?K2ulrhZN}XeC=5^pJ%4UN5&tCYDP{Yw(qakb{CGSxz(%jxwJVB?Q z{Qb25%liuQ|NH3B!8{ByF}cFO5D{PNR_ghVRJrcw*b|;FE%+fn9d@%w0Eb(rCT?xLWs^Xv!^??z#n-tP_E3~lJ!F8~|#elq!pxdp+XXbRi z3Fo>uptH!wvFoLI{fea6yUiv$hh~?UjbUV+#UJ}f`n2n$Oxy4%pV`xM)?`AiH+8}K z73nS9z$)NEf$p_TgBG#ONsJ++ehufHPgGHKAK@O-!Z@VX9`99I)I?ol!yYZWDt4p> z3hrx@Ly=;&TTY{6p^+H5AW4-asb;{123@C_(l~m24=P?r6E?7E_4bCS9JYAVQZ4Fr_SRv2SU2_D)pPyeB48q7z~$+|tIvCtASfg|^%VYuyG`r*IT) zr`EEdeW~3}Kem4K>Ic@C#&OXj&<=2*+l($rB!~CSg%lY{ZG%|qpv*FC3OF{=wegWBc)*1RUB;KzKUU9ji|(Cv zb4Xv8N${pn78?``fAMvb(u7y%#;Z@Xd&FwTb$%jLs5JUOy$eaI>H89gK)6!M!|FqX zwFkHepu3ax%a#rO6(^D4I%cumX%NPj_ZW02;u^bYIap3ggWWBy=|Z2g0y32e4-?_| zGr}L&>lnu>6^4S&il7+k#8(t(=| z3-yfqr~BXSZiff62>~Cg02di_eI_*p9@M;%dPY-dq%MYH(!M*PuDh{#L7Or@rEJIH zcNz!b94Z>}!<-&Igm<6fo+SzGC2O3+J#JOr)4Gd+<5?)6n`1L-Oj0yL5I21H+k9g!`4(3C2UgCc$jC(xiTpX@M+l=$gm)$j*eM;8i#6Ei2&&^xe9%?K8 zVdLu(#y85bfB~4s966moEb=#WRWAM1m=NALOIAQ$bkMbQoqAJMy{lX5dRi&BllPD{ z68zv=q)zD8B+$?5ZZEVOL|td@S00yYpz~&lKmrDiX^z^?l7I7?A93~6d9x<{C$NzA^& zii~TDjy(Ca$H@Zrk1;{_LCFcGYLj9aJK^Zgtst*UEs?P>%mC$B+5mH3r!&El$l%P) z!q_dRXG>|pA&@!8wNC)fA$~b&^8AUA5mObs4`G2Wrr!sHE7vc+UgMC71xX5r5duDn z*Nm_hFQ`~)mC2^3&6{jZ#x8F?($csN7}}49_~{Heee8S-S^iM1$+gC@0_}hex`i4& zV;c515}~~ZIq|U$@|Wz<;S|D!2SF0|L52L?(?{S3(drF?cfwDP2bBYa;UdQsYfYo$v+8+|Q%asPNC(|xTDRyd{`g3@Y^$g4g~wHa^H zWQg|?ErY8w8Kl;<=5)W}n+!zD`4$Se1fXm3sZc%}Yx5}DS0ew5z{u;g2ky;`IpgEz zZODg_PW2@1qfnKQUdVnMl3mG$7F|^u@Dop?XMB?R5W9JhcZ+0Gk? z8o7BGw!Wa?GUbX+{*>>+rRtEmzKjOLZc-C_{PwOrh>a_;kir%MQhpEk#xoqs88j_h zZ?0Pb_YLS`T!fvAxT4?$d)JtuvnE0?S@KTdlX9juID`(gGY=F_d&9g<7a^G+8bao9 zQ~143I#5p}hlz!Uqt*24qc_%Dz$F4*4LCj61E|r{>#W-jVPenSgH95w4BAxLKfM}9 z6uFl!r0QYZ2tg@Bysn0X^RFoZI*(w*(2{OK0&U)4VL@U;^vT7|2*5t~DPW2NW!z%1omM2@d1 zIPOmjx-I1jdw1Ww7reJ21t@jDH3VbVuG0AYvF2&*SSMhRhHVQ6(4SkB{!DCuP$IZ7C-GEBk{;{v1o+> z?Mnu_!r7I<5|Lz#`Iw6?EfS92g(x(3Y0=tJds)zLOi?rlyvo-$t68R%^v|xAy?M32 zC~TR@j;f!sH{B24;r!J6w~qB+9h@9=u@&cC2|q>)H;LGG1x!ua{`$N(gXD1NTDNH* zqkd1{VY`@sOH#Ksg`=+0HFth={d;F8vX-TFE3gxjIo50R4#-OZx|;p%+{mdts%tN_ zbO`XcA;F9frZKNIBo^+O10G2*kp^bPs?G4Sf3&jVnk`+P3h?Co3bF~=?_^q+sAfxh z2FC*^L6`Eh=ro1mE{B!nDM`1;7t`@gYN))BhJ8#an?xrbN82%4ZNq+Q0sHpc71dZM z{JXEZ`G|gAjGo+I-fNp*DuC-Gs6h7`%5=LF3U}RhZ0nM{kxDA0e^&-uDcnS-0&B_Y zhOK~@Aqlr&H7kbw+uF{a(-D^KI@&e7!N__1{Fy(y>AeEb4%DFQe@Iyi@&3;Jq?YPRq2^7EHy^MP4W`MJ(_-i#Tx@Dpo-&%oKFI&B1?(*c<{<{ zDo(I1j6k}hG}#m3MR$gf9>_}zx`7k!=I~R+uc&K^C=w#rB;d%PYz@ z-z3z$=8X0(_%+qsM4FFLmJgLTcKDNmh)_Lp2FZSC6qRe(I$EW?cH@7V_#pPh(iEHz zLl3%UU+-1Xj{`G1Le=dfYt-zNEgeta6F~pA`FS?RD8aBsE_>#=ZBN)3u|V0mFC@-Q zuU<3OOVFRfqAIpH)fWlQ%VYrECBYJ{<)u$lwnq;eb&qF7yNYisw2Ki*ir2WSvgAJH zFYs*WASmfvimqbJnuHV)mVJ8Kr8{6NOm@=<4@Kw!?{AEtORvZ?Hl)1bNd@1_==Z>aSO4G153SJNbg!X^tfXMnWZxV?G}(ZKh{ z1iHwXtI8i1=s3lLU=>wIqqTxQVy(JcLoXz>VBjVxnEd}rn; zBA(9WnvAUjP0}^2W%AA)F#;-?@qMK2qPgnA$;bW#Xjh!q$X~Ob&}!~r$Q8B0^Y}gJ zF0av83D!jIFuy3>{@ptMb;2({W@q0#9-^@&xE~zN%M+77%U#=fbYq`{9|lPzVG>fZ z%;}mu5_UE#4}*E(!TUT5=*sAc=@~l=iL&lcy%RU*OH0JW(r{)VSNl_s+YgUcvsE_I z23-Yrh_W~IXRfrbwcV}3=EFovXOUj>oeyTX8#K@kte{I0dZtpp_*w6+*rL@h8kbww z$b_7w!Mjp%QD?DS(B$-f&p6M68@;WhYi?mU>qC1ZM9}9POQ_sapVJTTVzj;jE*t1t zta-9K2kyi8$rT!?ZqmbDDF?}JZu|7ywA$Pl-r8~9(M1_7786NgpU{*LC^7rmR_d! zcS9B)pY|vsVaAi6kGAh!OTq}%AaKo7DEB#}fV>=_+YQevdC(oy*f<<{(6ALY#3=v1 zi;^T!lP!_AyTVR}vqqUqfyp@|+9+^dT4j>E+9EDQGi#_aH_nRlcBdW>tfz8Vv3=Bh4-$}<3v~HJU=gA|NvkpLNznE8tRb;KXKo#G%A6A9Re$>B(tE!>D&27S zwP>tf(UQ5-`4ZChtMk@|?+2mt1!ot|aNAM9r$bDhqb7O*X+C5rcWc1L4$G2gdyKaMw{Ev$S+xxoErmvjrf)TE)VE3px{R% zd{kZrAmZr^g_Ni=?MK|A+X{GuW8Ykf!nZH(` z&upa1-|kX|QZ`ay-DACewU<_Njw*Nqo^O1h8z{YNX{e{l`Mx!*V33G^^k8rVGcmGG zC1kn937tHaX*ED-JhXP;#B(Qg_PmjkQkjf}bG=IM)t$N}&Bc}ZzkM+NJ@4>??u;V> zX3}_$W-oER;K}Y@$CV$32=osDG+gf=J48Net{SadY$TWn^}}n{*w=ziq#B6xo~NEi z``fRUlw~aaY=QO_0NsHWC(V;D7d3tiACk;3h)$t!R%mZ2wA5F4=VqP6Y*;4Pp|stm zyflAOxxGPkf{h}tIr)Gkm2tr^$7NkgdU^`Df}pE?_d{gIcSNq}_InE+WS-byME{Bd zR|@P~FIQHTp=+FsV`%nx$ic7juTT_I*S388x0AE$+P2^C^TS(G2 zjp6`01js7}x{p=E8dRl|np=>Icp(qTA??^&Ueij`8)atP58gG%-=D45^TdP8Rrt86 zu?0x?-JiyI2NyA648*53!@MT-z;$0AK=&8Mie@j;0w4TT|HrEvD~P5U59=C+*+{lT zjWfrlqHH1r&bg)~q<{f(CsB>bLL1SgkT5H*r(Bd2cbaVP0%RbsIOwiA6&Sf=$3E@q zYIL7oI|x|ROO7@eG~q4)aBn#o%`ksou?@{A!E z^H}d^xF`tlE%@bxO4;k$Zj!er zUQvIun2#haGfjRy6SbRi%nM!c1Y0s>VEvI>w-y?kp$BkfLHAL-uwfV2y_k*TIAZSg;KZOfsqgU;=*TOkWK z>5C7B`nyTMT{G!O?H&Q(%7bpC!}IN~97$YBQ}ChwQ9Po?-Nts2ox5SQU3SBKmZDyN z_WnuA2rKE&X^Ndjapu7p|4z!3Sy+|RVjLE`hQe&XRRCS)kFQf>bIo>--zVz`6}|d0 zp0xMAJW5EV=mo#ej@HQRXbqxGfdh|7Yw81sbO61Gjj@Ytcq#g zze?@om1I;!Xo@R42J$L_u2?`X%v}zXke>7Ry^Q{q;(*Hn!b6+Qc@g2D9HpQpDm6oR+W}}ZTPUmHQ+NHUMNOFmaV82iqbfb_6*q5pwTx|(SKdo+;X_s7X zDi)9=7q%@j39U+*I)))I#x$W5ed>n8!8Wbtk6chN(oN;dnrKGnOZ+BLh6?0W0bPjU zylhm##BIKJ4S5f6g)(?zv888m1BS=HBWZT-Z_GQdyUN|3wM*tbH=^cMhQH>fy{%_M z`+3P+`;1fIKj;X!s-PQ^MS~m^o3diwJ?3O>65wrNQpI{;_$TH;1+OYXc{PtEi3(wvS2e*|znmecIUJLkqrUtrFvPAd~KWL8M!`~(t^I;4SNS$P4 z1f{`4F8C|WN@``y3x4y~L}F>Ap=DIuyl$AyTnX28w5h%|T|Jzuoqi7yc|>l`EBt z#?lO`Y~Cd;&<+})d*rQ3ol`ThCGORqNZ9@+euT&i=lg|Eh^nIdEQj=VFwuz}!;!fA zPz{mP+Bse*5S5J9Un4Icy2ui?GeTl0F)6HdNd@TL_Cx)D8mYpXq|`O;g93D7x6w zIy8TutmUtRj-I?M_E^Q)9t%3n1mp?AE;d8~PQfQaG@mrb;7RHwRo$9E5_XM}9+dlw zy-*c&An$)S$NT&De_qhxdTPYU82f(ujFIciSZq^QR7|eef>koQ$LwvI6oZqy6lLRS z(tp@pe>33P`ngv0%*f!FAZipqrnD zSx2T-BHC`n$a+S7>N0Mr8TOuaIN;EJbH<0)P1Ob6B(~`0bf0$Y+Y-6kYRk&@@50Z) zkB=GG=V@)+dq04@CZHRbD>j(g+$+k9QNK=3!Zx;UvJMgUt@}p;1V_}R#UWuLsuEF5 zKK7mM)TT#`p|DFE^wgzv1XeM7gZtB-^qmyonu4xn^Go3#6aPJ@66P8zcZ!PgVCpen ztYA%=-x*z1CQb{PPv{qyz%(Zm{NkRNb5U!oBBG;e;{52L?3;e&-jg-JH3MC>JcOzk z1D`KL%w%erLz55-rFLm?GbK4Y-}BOaRI`Hd_PkZKKiUOi=h_(;;48R(kDb(gkrA{m z+Bx?5L#S7U_R!R7IaHKjUE3H0OObtFzNv2STQUdtCRM0Nd(OOpo3)Bf`D zTVDEWwXZPLKweAGeR%fFge^2&7+u9++Q~Sf#4#yJ3}&_IYCY5Z*tzGYv=k)Y^nu>u zGtPBY&M?UZrMrF_TR!5(MhQeEzq0zyBH&tquKi(U)}HFm-m2g_UNfIa=en+8Q~7X8 zVP7WHGBT9_j5yWnf;6QCNQwSgWt9sEQ(lO>!tFZA6pSsT0|Enx-2b}&_L;T@UDtSh zOBda@8K@%>jq1)bE2=-{`EM=~pRXtAw(WUrQQA01Lsc){m!d=+bw&RQ+N-UWnoRqZ zC#)qOc|LBFUJJN3psU4Tarjo>E=q&g2N9`-yLIa$o#g2nvl4?CvPon4uC;J#^$QED zFg$_{t94@lvzd&exIG(fCgH?rrmpy+)xY&`|MGqU-5<^^>6t2}hoTI3s3uDZf1X66 z3DAytAm-QJW(^mgM{6`?d%}{ue%<$!p{vC@y{;qTHQH20+TL+G(n17_IDAPi>1t9R2AkR3<;xrQk*LIqdIiCE5Cn24PjcIj+E454*j zLSwN!ES~R1nsi)|C6ayqjbHq&QbN3XWed8T0`!!9q(dLJT*^$ZJQHTJO#)2e|i5?|Fi>LTPz8k z6gZVHtTL7gvR75j6ubBD(zB|kf3TW5#S%lzY5Gb~CPkPkbaY;?)J%1p$TfO4BxcTg z25ip?nInw+TR-$~pHq9#b)JQh$YwM~Bq#~LqcTOafaI=jJQ$suS%>H0XfU??gkI*D zREST~8Mp7Px>KT|t;U8NG|8P!9p5IV;GMequWtWupMM9?^-O9oqGuAmzH`K7`HDIX ze+eyEN=VXP>goBOe?0qrr3{R9jR%!Hb!{Q);V1Z}-N+c}8B1RB%olP;!vx`|-v744 ztN-pp?+ChoM(U?c7*-9T>)wW1Ylcnyv=Q|n^&WK0!|1y_O53|kc&)WcTzK*-t_Q#1 zU^E_`_wTx_%y+t_tH0}eSgPRE{_FbwcOQBu(A9Ljbf-pVAMoXH7>_IS{X#i`(!3#OIRVZ9Cl=}mNgG4mu?Z0_8|Mq2d2Hl0A zV>^Ry?`mQolxcrh&v%T~GYznym~41@TVl2^X0gK1l=eF=$k>v8%eVY~QT{3%GyF&g z?b*m^^*uR}pRLosc7XrS@#_M*buh7fe_|?ECX#XNtSS?TzP+pG?1nyE$w*`Ejq8$5GxEHBs*CYpNf-N|)I& z|HhsEf`45%(EUQwEXyJo#uCQ?_?7$9J|ZtDYF>M@I1^A|+l4gYPDLdN@epsmo6P*A9Q*JwP|) zMEB2*!VxxvvgIeD)YmnXL;_l4_~RDev*~$5SZFRjsA6Pi`ctWgp>IcX{@|?G5lt?W zTPI1@F(<~|im2KDzr6oB?>s?QF!-kqViQ@nzJf42jWgFob-{4wK5^@!euf9m7$&Je z^>5D>!txqPr-~cK>`JSyMVE`kV(tNHUqm2XtdNdc?GyjK7VmX&)-Bevk`I%!zWu z&s&}N0zGXzM59>KKJ3)`cg~k-I4LJ&#?PIUxc6RLxHZ%#;lJ%UpP2tWpZ(i+{0rzR zg=H5V6a^$9S12*Wj^0M1blz|=M_Nrqu9-5_sI$3 zy1t{8gP+QHSJ=|9_@IWD#MaE0B7DM*yR9&M?#oS4+kf4E^>shc#n*vy=^Ys3YmoVK z>yj5uT}j*;_jl>V*P=5va=MTSDB=VoPR1 z|MLEG{XtiMR#${fPwxzYJB8a1vIg@-n6uEsPYabOV{r$bn$9=>In=-B$a9=o5{r>= z^D*lrp>u2i-GHPZhyPB=KJYV;_bce;>TOH(5M-4!pAV`gYh2j`*y{@_xFh~*xGlny z4<+k6rhX6%_4s{)oGr9GC`Gj+^aCcuEKjBkc{4%z`&K;);0A#16FSR5Q3ZdB+dA`A zD_X8aq}5CnnGg{bnktfVgX0>10R4(?f%~7u3UX!bDM8tpC8GgS(F7uh=w5WCXuOmE zY(V+H=e|JDW%*6;@)Bkr^DPFeA8B@nb^Y7#A3>wrmvvp=I-l}Fmu%ySAN=iUXOITk zeHd?UO#-yCtna^tj4yEt{Z3B!(FC|bpgXeEqT3~gd?-yzoMgDuq54O?iIH+h^iwuA zgCpvgO&STfqH5f?W89zmyT#(_D*KzWw#Ls47gSoIOo;`tmL- zSAT#1{Qql*5YR2KrbfXFw{&fCF0`puUU4M|_`aRnz&mrX&@c4qwKiXHUKg^rLp0{u zFGtxnfj3eHc3j;i`dBpgStAh2Z-0RO?oiN0a8(US%KzMD(#V6>j#I@S>#bh+=@hr3 z>anh3C)KNYbD@+tQP7-=>HC`y?#ZyZbAMaSeE!Y=!C|7@Y^dV|Aa5Ax#XlX8^(Kvoy?7`Pn1w+lN_?j7%5U( z2>DLr1GwR!dns5eVn`@(xsZdRn9mf6kmH5*wx-~@^6JkTM^5fDTHD*B9&?Pl`;V(} z`-)6-fo6`G!i&et#7W&!{*L*#*nk@Wx~Pj28OzNv)qB+YvL!X`>4f|AuivRAX2CN0 z3D~%PzBxvptk;>(zUWXUV0}Kb9ZDOeK6v1~Uw|}-)t5s&)dk#0&}BXhZs`fUeUUXN zy0R9@Y*D&<8;>29Cu5;q`L`?>Y=W|>m6UFB@)v9UauB+WpUkA%s zyuk6dD9}}z?ZIJxtkLw9M>3_>U0=!wYa_$Whqe`SYN;%#$G=k>;ZhQ*PLJzaRS;6w zF+nyTkw-UnetkVEH1L}y*A%>-MT73)8-2>4@Hst#>oyg%Z#k2Q<3_jX2897io3`d_J~+5!ud*Rr&3_e7LqZ#stsQxRlBU z_OoI^*M_M(v0lm|Wy}WZd0Lv$_#vgTb=5)NElJ_Zt(>CD7gYMl)l})V-ZHFD*XShMb zfX+C~@drB@H%MbI1IhQ~6e7MTmoR0!F_9~~$0UgCa=OPWb1N1zW1~l&H68OPA5;MM z8|dyC)2M5mm|2q0CtX2GxI6nDG$`$y1ak^g+L<0~QcVBm+f2h-Kn;A$&Ug9#BOF;_ z|LKE`=}u(_{aB8_w8RGBCV(yx&dc(d@5{MlANdbX9 zr#*nDf=JDQ;_FM8wrICEs!FV|fTH2}J^a~O`y|2U&Q&)38g;l*1n$x<_pLuT-#!U& z|9*`B=hy%4L&<;(9l~xZVQ2zbgo!etF-hz;pwm4MpM~K5VFG1>)G3r7D|R~)6-sru z0iFlT-ZC|JZ*%H`TKsaHteIbwGEKP`tWTzZu3S#&=t=qR-Z3w6Rr4xxkv;cR#BNi9 z9u|YdEb>Nt($z;d%)P3OYvP;Z{d3mfrkq_@WV@X!>K~(3Q?1GweZQ8&|lI7z?Ow(Xyv_dZ6BFUfwfOtdtBLVg5YGl8HX} zs$)2CDmZ4t+y%0l`fOW&FcIlUJS-iy0|CgJ2D$~WE|)Aj(ySsSG*-DGgM0l+ z@eSHEE%E4m(ZY<)x4LkFS1+EB)`w2pjM|GwNXcBOA!-IOQ{jtqY^u3ab7{j(tpr;t zbw9hk`XcZEHyw24&vAMgsgX}io||RuKf*_^3Si$hWwdPMApfoMV$kE4rbZkeEWdbe z5Jb~vWNN^dxXPt%KM;!Gi9;!k_ru=g?QO0E>OOO4aDnUA|$LBl*_Y=ShDQ zmY#wB1K?(XuKc1d+mAUYK39Tr2CX*(mg+g;BTO1-9EJ!)wwk*Op{QeiO&%~XP$Zlg z-%rqj^u6x6waT#cs>)=N24fO__yTS==x&)OdOd6=_jS>5H5l!)$h7#p$e4{3SMb03 z#!y$#ZZ}M()4HEKAR#flOOIn@vE=kV{DO*qYy=_#6-_HugROft`aIvA~GtUAeGt}1>ThUiHacKrV zuNQzWT8qL4x5gUXO?&F_3Dutoy12SBxt2(GN6Dw?JMyaP#@12+ANPao^KdldH+dTw zq4!LMX_Ole)CcWhF4bR+fbXpkbW32a2Hg*!gEOZEIw9vro4S3jbVUk&Y@Je;JqDHd zQ)m_8^^&PFHEY~PXtsF}^aYwA<&ek7QP@8QrcmW1*8*-4=*}mKUh#*vky(HCeX{$L z^hEzMP4gigwKCXRBoH}iqr!z~v#qSI{j;%ax8(^!{{~MstK|D8($&=q3~!<9A@I6h z47y4JV#p_H%|?Rzr(}N8_xz+bOYUcF2W0j}7yFi@0!kfWRD{Dz{vTeIg-S?J!qL$d z$wD(V4wOF;Lhz@fPlV_y>6%e%pZprE#ihB?*N6Y=mvILiYJ{3W{~ zqM~8LmJB>>ca-S@vn3;uB2S$IF|{V;FG%tig5gVmTMD{r$Pq9HQzaU+X2MUenyXU6H!5aRS0?Kjkj!zlFC=s6)(}DW-?$IaRqAbaCjkvgv;nh0FO45g_PNo^bG!aC8AYglFEFO{_9)|hpd9U;Q@Dfts`>_ zO<3f2d(Dsh&)08u0k;fv>1CzFXn#IzSDfLNrn#qOv{pfzE}2Bxe(%R6_g~O!arr2D z9-J6>OVBHVI3sX^V@s-rrPuu&trn2TdO(2#&Yvs?-Cy&^or-#kH!^tFR+fnq2~?S)JxX&HFs{oa!ZJ+{Tc+1-zw0JmO>HpHNPkg<1#}_D zw$OB(8*Z7l46o{IOr~`PSnu3FSG`C3Zel1pv)*%|cC`$>auQ#WtElMMxIN#uH#v~p zm!nbf+Vt*#{}KbLf;phRMFNZ6S$ z^5PW^-Y$ksp`n`Z;Fb$Z5%rcR{^kKW9FVsbbO%ua9!>=Blh3SoPaW;yZBJTtCAog- zSe}7UL6v;dq*hiv-2W5;_o+@KSCyLh`AE08h0R%e|^^73zqwZ zbu_Xwvhqd|$XgG(L>@`egp!;>X-LgT8flslGHAHsJ%){u^jOV%RCNQfF)#ccDo0@l zbvHz-H==Y&-<&TsSE!c!xbcS>uC9#0`+NiFvh#~jE!GD((7WxYrlAr@xj!HwBaD14 zLxvswNxpiD7??&UEqE3od@_mxRi8~1M4=KJ^NOgr{s(ti&DhUg8z65Z=-SQFLF}gz zJ~ukdh$(KIOm|bu>PgwpTEU8X=hol)w=Abh{V4Zhe@q~mVT==?S^*|?GJ7GF^* z_Y+d);n>zO=^6R^obdg)$Hps}h8VzY2Hk*#b%f1IQZX`gQCGZ6`FH_l+?#jK5;|<{ zMiscPg}D(&nxIYu50`gQnzj}gg?XIbC=;ZS#-~&?@cuYX5Xu4E7SIjm-g*|y#|jVg z8h4TpN>S@EaamO6@_@+l-nVCXg2{)hDK9Y4VDsZPu=yeEkcefvWJP#nskBMl>Xsoc z-OdfTt)L5cG;k(M9_x zdfZUm=S)D31@ZDO@kcp-&B(TQLwjKoJ*pkd@uZ>m*vWtD>i_Gvw1cidP+sy@oD4+-kK@QE`^BC!!>)lzM#Na9wPjRWPfXnkkvh zcN^u5;-PMg$TMFD7h_UxqgWAXAa5t=_GQ%+qO3svE`E?Uf~tPlw2gBJ6oas3xJmKM zaqFk%gx9$|vT3!CC(rr(svJ+|d;rGgE8S1VSL2JpdD-Dm;C1I0=-%Y9N&B|du!R}p zu4t;(yGRyd$n7TSron8R^Wu31AG3LK$7fr_@6j*ctPHYx(^d~$7ql~wJh1;Z^4LAm z1M6H}p!+)EcKRE`z~N!$lt2KIPJ{=-mRUPS$}ls%V)-4q%n$?m;!JHvS0H&^op|)Y zlu3XsE3t*NanAt*NuU$gyI(*%bb~Gjz7!hQZ`-SDaRDr@e#Uj0$@ReQKqr{W`J~id z{GNV;a(~Zxk7Mo|o6U1NQrPX%2CM`>`p5SX>^8%g?J%W)+XK2HC<0m^Ujn$uUI@QREHWJ!$11b{4T`3(?<8RdtU2tTdiSt!82Zz2{S{^lf9%Qk0VE-_SL|qIMsLn z++NWAoIW3>@qy=vvB6k8sk=YzQ&Vt@jzf%el~ltu*LgqQbP@U(o)7k%zWYYOWDVl; z=k6W8RZL`0PftSI5@$RH!0iKFnuKrRCt=)4IchjJG?B=x4(o{V#@}(7epLI@^-2%I zd{KN;9Vq3gl<7-aUM1Nj5Zm02KC%(Zx9Ih8QDxSY32^&C*OXN`F6~d4Tr|c0(cnmP zIRed&#Ol7aOl0Q!sC%6649mVy@s{JOb;NR(xWcL(y?$h!w6&XoL0Y< zrL~Azs-g0#ZP2kqHjsA^bp3uAt{>?J(OPe8fBS_w(%GP(ER5IcW~!8N%?z)GC&0$@ zP$$@{9+O-I4qT<_R{BiLHCpI;zr+nb4|RG(!{?a! zdU_p1cE;ZiteHUG5zx&$m*qKhrmS86VxAyBQ496mvrt{CEatgm>U1!Jt#W>4 z4uot1QV@NtJ;pQDi%a&lT3dOPS7&-KIPNq7x==UOn}ffMPKQnlrJ+s52{SmKJS6?? zRSbVFiX1B{QiN>FYrPk-iK68$E&P>2oH{ZuY)sal%N2&afZUUt242@ELHCVby$gwU z$`PV!MzJ!46IJ89tdz}Y2LCj3%ZrZGeT=h#GuoMl+FiLKOz)f}rl`JlDF#9jQl@{e z6u-UpKmyPXQ=sd_sZH!E^z)s6a3Rlzm}hdd(M#IYL?$Y}1kMyC{)v@aZum_n7CSQ9 ztrjJ&TcHKRa_7yK@KN;2*cLVJp|m;RPJ^yk;*6lu1j^q|PNCF^3Z}!E(svcdt__I% zuSd7xdo}_{oo}zV^?o^`a}Zi`ZQoYd=ewUKuhs^&F$IJ&yu$DW+!@f#S))Q?$<@R= zOUbGZFSl+d(VO=799HQims}B^}adKWfY3uIl9Fc6CLcH4SDA6#8Si(xvraZ&ZQ2i=a!Yq7|k9uZE!{-)_*W`IEUI)ZP9A)(V0JvBH~+ znDfaP8!Lk-)r(u!h7Wrqmx!Jog(E`)m-LQW$!|vaH{aI)?h@!`r&Cw5xt+W%n!LhN z4x)T2&x@#3tJ7H27a3127?RKqPWfr{LXQ_8BBqzYH&K6s5Q14HGi7~Sdz+`XGd>Q^ zBV7hvCvirZrWirP9F@=G&`cJJ$1<3I?%f(;3oqDAv*2HwFl=5XdRCG9R;OES97_n@ z8y{8Knab}5#OixdC!;)i19?|KH~WB?R!}xRQs(Cd&eYP$R?=hF+pg?-3oUV3qgg>m z5{fev6>YHz_*qTdX53Tn3b=+#JOt16W5ZvV>$JsR-vjO{=w7z&#(WiA+B%5vK&d0a zxxnL$i0iY|zYv7@`?~=p?O6&_uPL4vF>|3Ww7w>eWBkxT@LEv4sZl4}=2qhV`MMWy z*Fe{cC}BL-T9}*xFVI$T?bu+<-;6kDTK}GGxNSys|qnRGsZ6Hn(*E-oz*XG6fDjz zkfF-g(eIU?BqtPx*O9$#=j?3s+nCT+;uzav?GW1FY|^j)Q@-7K(=@l9rl_&Cnfx7qFfhMr#J_! zKWu_-3*6tS0_ppZh-Z?~J3`mfa9ma7s%#m?FH;N4la*7=u!lYG-3!?nKgyN%q|@X> zFzKGsZxkFQ67710A1n!E0PU~^y4Ku2^|F5oP_J$?V2768ar0IehVt~5M@r*{j{3;H zw^o;{)~5@Y zM*3z-ldn02Gfz>*ic_Xg&gGQ-qkg+z4b`kLGIJIk$F}|LzKw2-)76C6ot^R&mxKZC z4(MJAxlX+8`&Do^9=}P4?U*%bvjE3#n(o-he>#RDQF3dr# zc3Tlaf$3hPnHNe&p(_1uj57?b)g*87_3Y#H8x~x^-2>e%RwJjFmyrEQVj;Gj-8S0O z2$hulJUBv;?x;98ZN*>SBiy30g1yGNG(pDDizg)3=<}gU-G9f9Z_K~MO{#wd+cC&aMd)2ALPG!w{!TnCt$_Xw)~(x!2g&!@tUEVE6Kr_j*17&EK>m&3Et9 z0DCh&gF>()r{nB@=41Y^zj*+5pH~E}v=gm9S_s)xpEicI{;FlWFlB*ZxyP^G-U0F+ zf$q!waggEt&L|JghTT%B+6%gn#ajv7nH8(_J_@vtr3FH+<-Og^+HA;PXQIuwXP-S# zYVG4D)yASMv<#AW=hXoB7<8%7#gUag>kJ3TCbNtoMewQKTa|v^0qWxqfZzbcBfB6pRzv&XIFnR#{^QWL2 zAx49Qz!yw)1Ch2#vblj$Y`%9=(e*t9v&fY9Imd3jtn!|R2@@{Qc@4UD>SGv;`33Ge&&oQ>slLYdc^-wN^f%ZKIU9-BD#W7wI?N+Fn-6+cY#uz~e z?60LnoLuf!_qy!f)St$q8y-u3cD;iT3=$l=i4q&+nO0?M3A&GtdM6#F0FLusfUdze zcH04YXJhE~Oq#a|^^yv3twKI#-?1`R8_eOh&@xbRVu0gk>V!cytuW<*{0Q#abcH70y^=BbcsTGw6Ph@~ME*{kI&1oVe zjniBV6w{80$2rE!$WtL2`d)7sS@JcLP7F72+-^V@TfNE5%IdBum(w!)*J#OQR%5x! zwiFf2LqLcvQZnC}MOUmO@6`2_sMTb2)Ki%Kcv#!vInrG3H$U=_?bHfVz`X_CPgSzZ zq?9!-;p=ibIFVr_+}-Hfu$xYheVw8M1TF4fySupTB{tXurdve$#-!{n}1Y_=<<%5M{ z)l=Uq)nkO#(eC>O7Fj-zSZqqR9|Nf5T0q`=&~@hEH&^FidtTmd5`o)U>-^U{xkmP= zi%4UUc=PZP2?}Yw5la8PFSF+A8i{l=?OFvEKO95x5^TVI0A2AbFN)bp zva3A?{_O-sB^8WG5vRvw$AUUi27<`EpWj?WCE`MRI=+5hLignzg1O0}-N*ZMP4koQts*rKi5_)F=yO688pCW=>nL}tF@td0Ho-Q2+d$<7jP7UGUVtvx7N=ZOia zWquW`|2~7RX%y{`qluQfHv5G8`PH%_Bo^(7M+&w$!l*H(l}6auxLO3d!x&v+$4x}a z=6)4i5-MlfX@4OBmfj3vf8j>~Anyz4mdPL8iF}L?PHp@>h{Xg=F-qQXdhytiPG)=k z7jIdoqibF85(a$F_4Jlo!!BbMqVuFMF&fE&&qgbIPa;ubMf2&+p2KYMtS$ey z-ekaqfd2Q#|M`KL?Hc!ha;GdIO>I983@oJ0gY4O8x*VX6jGgHPt;wfzs_PnmQv>MlFyR7IbL>{W91@ zMR9O?#ouUROWabxr`Wd@!OFm)ij|nX|7MC1f{=Hkq=5Z+7%TShuGM*z%bs_;e45)p z@)XOYk?|gI;XqgJhpxSYk5uyJ~c>Kk_?atsj=u3MuU+N39O0gC|@D{mN>X^`b!r$cq5Fu4QhEJ7Vfl zhi}lO3O@$Z#G|%VV+WYnskW$v#i8$>d5_6_z#ogNnA;a{*BWfpWyX3}VbHYNcV$<2 z+aN&>t~U`ucLuEv_Jfi9qie|upO=@9_t{%iNqTxYSZ03JsFh8}Y;JA-@If@xPwb~c zQ~6O+f*<5ybTwWt?vcDs;x3tG6@a`*p!-BGcvmlYn8rg-+yxgSR)QvoaV%>E8z}bn z_Ad552+yd~88=XBAEva*INj68%ZoKrBsGCr?a7&y_d`K`<~P871G+cYd9o!8&(|{Q z5x)IoW|+mFdeAYBYwc*W{Yi^MYxr(|l#7Nz$O=VM@$p=p8;gIyE=?gHC@xA2OwEe! z^a1@}hw5-{2DmNQxcT$sq+cTHhQzDV)AZ3wxm^`27O) z)ShP||Ebsq{9q3UISt#6UN|AfI5d&Mq+hC9k2nQ?uB%Vy0QW8EPGP9ukbLhjVJE@S z!Om0GH*aw-^49r%?HC|9x=|xPGO*@+e~v?0UMZbYq@f{tDQF`7=i3D;1msEMh8!;z zc>bb-Zg#OM)*y0EBFW;OMI08-zsG-dDTQ0~dt#D11w>aAN^?KuTiNZahpFMT>JM6f zNbSF2K}24}@8CCbw3wr9?*;Orfo>tp$1M!cmtGTXinwdUpYqYHtF;O!ywpTc4dY*c#jQ7nkvxy>n^}X0)SK_8>$w6v*NbTAefBC&q2PWhEWWx#| zF9ztAVKS5RT0xIKV(x^SM+n_JzAx!HihDaIdr6kj+aF>@Q~7rOVLDkof#Ct>@s2fr zY%J@oGcvbXuE(cn+xBVjx`PS2JvZcVGa=lat#a&ErLPK$JluPdxTsz&3;sHcbt2N5 zQ*0_lvq_nH8?yI;g1=8mNkZgd`+9Xwkwe4IRJiiSfxK9t`+BPJj+>N%@7;Iz&D^4v z566X$f*i=m_;b#V*0z&vP7Gt2u=xgY9CI_aJ8eVXyo!8QwFV=ma)^0pirghu!Ry02 z(4`NX9pUYEf3tE`$Ok7CX)69@-L=Z_$RAtU9PUiVR~o`@Rxdwj@2%yT#cfRVyI&PA z5<(JtsCwMjmbt~v8*hNT*r1zw$KsI7E>0eyG|7VqP0tV8oKHOR}QdMZ(K|zXOqW*MK!AOPIh+S}A@-Dr0l5pZXz8zB`ATKWH&cgqgzBpQPNGZh~oBovtZQ&PLh|785C2frkv43~)hfuyG zOzYlWAYK(CF1;nW3VSlOD%GimXVPw5*y^T*8F2AHSF!#sO_+KrR18ML%%qDo?k7AX z12=OyIoYO9l-kop+1(p+(iEFRTSqG>j^D%%7sZi3%d!mTIBKG34svY&vz-5b=Oy@{ zdka~;Z+e0V&A;-ViV$}Ft?`L3&$J3hO&(T{eWqqZSTz*RMioz~cIZ3tOZdV|UQPJV zk(Zxws=hd`&n0U1yaFx(=nlRHOoq@$n6iovBKoC9px5BdtgIno%Iru}J|9pehNPt> zD>B>`Xps>h#w!xLFsEhP`x~gShG=!ln$&1e+5j#g=(2G!(6}`7HwV<1O7&a3(ZvZa zS{J8&j!*Msjw;v+KF$21{k?d+b$iq4HFV+oepySvI89ELop=IcK5X4Q@P0uKx@LY~yh}fHA7UBg`>Nv|C*kPssTu9` z{pyKqZg7M4Z*^vu77ehJcsm;+{8x|8H>ddOH_`qKH}ZoY^SND<#ZO=yC_q>Hx|8VQ zmty!}M9=>9ARUZLc)XD*!P!Yt`X3LvJuiE_&q$uZMU8>6qo))ay&{zyNLd zTUKiwQvCzP#hr1ogjP0-+dF@{pr<#RkWEdxMFWBJ>?3`%3vznifJ+U!SY(6sXs-cY zVqv6xVQ~}Pi^0Oj>_~=vv6Ifn%kTR)JzJ~nYQE^SBCt@{%A|ychWCh+)!s`hByWyK zAl1@?^GO=eb&JU6q|!r0#G|$#l3cfpBF%%VEaY>LPn+F?a)KN3RD4bF4&!W4B;XD` z6pkWwY8jtgXq+PntIC5QhgfVS0rJvZlpmBkm8AQ!t>Ii~Y230hOq38#N#)QT zRBNmJBfm0EFz3(4#1r&7FR*UJ0J>ZoFtXwaY|Szs_Xg=%9_`~#*X9`4@wYi=WW+ZO zlw}ni0@`$lt`V~@LbeT?8I$oh^Ww=_`1w)(q%x9OY=ZqIjG&uQ-llyhBofvg!)>n+ zD6x?W`z$6)PkoD);Xv_oIf2hPP(r9sJd~5|S{%8B<0Uvqxi5>;dxgNMxJOt2yDqqo z!vwlGgShuU2NFqhrx#_FJI6h8!!1^_8*ZVdZ{8c$2xgihF$`b2$9x&NJ-aTqmlI_= z)(~tD+|`2GSa)FjbjY|095-gr?epAx+rGkqQ3ubhPDws*?yaImyxY~+Ed3VZRBiZ9 zasc8qOy+e>fZaY%ezRy-{Xyh`DuS?!1R^Jaa7F7YJ>ar{E-7W2%b-nXh?E~x!S1jX zfrC=lmECCAg!Z*e5S|m?*O!o7%*Z#n`8!;;%SO-w5GzE)Wb92`I!_y`7&mPt=YY!! zy28k$=*95r`KuDMO1)-z+$1IweWCOru16^e9E>4+Td|Rlw!Ybj4l0^P&%B=@thOvUjzX>%RQGH}( zw&OXgkn@UPZae(0_C$MEpyXOl5Ra6VCUl7^u(m=a=kcK5vWb+RnnoToPq$j9H3iu zYhL7NupdXJc=fr|S|ajwx)=8=mJj)}6O%E=Y4z#f$ewEJJWOQ)r z@C)vK#u?p~Z=bXRE+^=Ew&3WU4~`f#Ok(rMP=y7!8a0hPRxpt_NkK?5!yI-6m9iD{ zk@7BU5airB-52+o8#tw1q*&z!#K%hTV~FJdE*I!-!Fy{aMvMINRLKp%()jpOjGjB^Be8edMk3126tE|jz{NN6DDY$ zKT<}PlO0n$H8<`hLI2J0T3LWNmEjr02J5uEpqpL19P}}k)Jwhj_-_~{)sR%vkEVN) z9kv!6;a+A_lg>HyI~I~N4d{wCp)>TD)0bcNk1};i-VB0B2^>e-O({TLKF}?R`Pz>A zrDpjX(OpE1@8FM~4;?SAbGdkqWxo>6UYL*PL`5bIwavU%!%kA+M_|us4(0{5o=TDi_~kLb(rrwjzd+ z_=mqZkv+$;UjcF&nF)o@ztiF!H^zekSVt59-MqkgN`xOpsXmLtuvTtF*{~g-=usK} z)h?pD>}8sRAoQ<(Y_YvB&kg09aQW%NgRdAKBwWDv%BVAxjZkxiga_mm1YHO5>+1NC zM*21)=~&3~l3$@Sl5rh!$H>vq>K_f&m)mHeD9%NoC`&WLYy&$+7KN!e>V5j$Husvf z(LT{J$%3C_A<$ihhkEg^CZZRH^WTa2OX-gAT{e`~w0v020Xw_jVJ{<;3Lf{eS*v5S zJv@bE@(62o4POch4w8XQx9FnPNdc@+3WIL-3)y<}uZ1e7ExU904z|bL{lbc*=P7mr z6j)@-V`Fz|jdF3DZt1HZ=LzlE^)iM}7+TQ}+_6%sD_EAIhiI_CIEa9*i4U5qHkU?4 zX2Ufv`FM>g`j4uT72_zcpVgn3RhQ|FJ+%}iZ&(Yw5{5#Y8@ASVa#GUMP`PLR&9|J`tWu`R=JJ!17w1 zM3l_bs_D}bHWd1Vj#i?^^Gb4MFOXLZbUS6`SC%7ky^Y_zw@i@v`jNyJHe}CTHN8h= zj9z5wsv!Hl*cc_9a}h;4*m$*pP9 z2%?C`l)MQKKgB%QpdY)NKk-swwZysmj=or~-FY>W7$T;sMPF=c9XdN-=uH&|x_)M0 zdT#=_5}>=XP3g3XH|+-3oTmI)q@EbdBLk%|d9!bbCbif74*MXSWL>Th4{nT2j@6k{ zzCBNaQNQ}2cNn5BCRJ%xSpr;VNrLW{n^Y1h?SQ2ibJuYq36=-0=+RD81)8gk-a`bh z(L9=4hWP2IPPL_vbG2RugLhIT2`i%|pG|V^gAyi_-w`&DR|<5Gc5UFx8teoEKRI`Z zt~+Ga2JdIb;5xZE1wYs0e0%Jm@qe)fG0Y$_gZ-Hs-zs_gW*ihY_I z;7Wt;C$e3MgDyntxB=1>s73=nt)Y5&&olTE_@=7*$7c#3^om>@vw2vyr#{C{Cj#2Y z-{iJ@7p^YR1$yM1YRQIRABYU-?nkJc8YlGQ;BwTwwId{*Y<8+HQI!cnG0ngcun3+q zoh>pBCiT2Qfk(~}Y>;J;irpd`iLj!m+tCc2PbThdz*PoaUK8<){YK4cv0I|qBe^U;*TkIa zqr0^LWdzpdCN@!j+CT@_&?#5@;~<1-S#Mm}^z^sZJfHFPy(GVKx(q1D1Fj0_Qnz@m zVHraSaB{G+^lG$5`o6%E(es)#>%%~d|Cr_NxfWPS?@8X(x^mrP6`6z){J;?8jM=swe(Z7#D6LyP)`N&OVIJP5hN7n~{= zIfU`GQQDW1ZpdlST%dDv>NW4d>NAVZ=0|s0($8Y4^00K(TaLphECF0K(EU|nQtYbH z(ZGh(e^U$Nr_WZ`UXds!fxO>qdHk4kR^=RpU`YR<(L#xN+ zEKH$?YIILUB2OV)!ke~ZnleqgM`6I#0NwrrRU6X5^+H3;2F(!d)&jfxptpZ=(zDo!eWys#>8`w{Pa?rvAzQLjSCUvDrLO1^cTtL07gW9bysXNO3kY ztk3*KknxW!n=c#w8~pJMnld>y4Epk_)ji5kJ$~O>U-a+;Na*}s8Elc0%nDg)+{%BO z`QW@-3v{W>#w^O1+IS+g$IDf?=^G0cYzC4|ft_p)k zY$%yVi5j{WFOgAGg=t_^{QkfFF#q|kTW!z{=~CmAi%}4u(48kVjLw}d6#gM1QS}>| zqG-;Y=1u!P+oi}U1x#Bru2q~g+D}p(W+}fymdhPSb2bh!YD+rsITRhx{X~FpE}o-- zfc>*5kojbJs*}$z)C=2{pa`0=>1Zqfc@Ejqq~XGw)GWoKZ0)9t=z#bxdWE{ z+J@(|xd%o!0!yqy5fV+I{_iLLlHW2bP!41KxdGi98-iDNUg}p30pC|jE!2I~vP`65 zNoK+Ou|DWB=9$#LOMv~}F-By8dFAL-f;^)BJ$|Rd)J1ewVAZxqA&cq9h?fZ7+WgC+ zpJ+s*LTJ#Tk91+u{z7$4T6Ht{xiA1-#lWLWEree82QzBY?wczsg$j;Z_Wep)E~VjMediPCmg_Kd438sD`Q@T# z9KA>FCHL{d2^biPWFM|>Qa*VxNi0gP656g$BRf!VC6j^~L1?UI4kUum&<$ATCo<7l z1jg48boE_ly6Cs6D=e*gr6gWeeU`Ohej!KhD0iWo+iObT)Oy!KE{HO9nmzE#60mLv znXHiB&TFDbj&LH2qiEcU{Qz7e(DiF1z`AUdb&~!`_*uojMk1@95}vWC&~;oddiAk8 z&wr2J18;OzLQleL=oXfM5h9?-KIZSpxAKO@m|bYPkWs)j23=ggn%cLhFbvnid<+EK z#ioWR7@J}&EqjtYL8H?Ic)apV*{bEOvXMOl^BxAL{AGM-iw zqSN<_>9;ZOG>(d>k}$CTYYMuX^`Bv{-P3;lQb_I4N>>(~e=$$b35%R=g7_RbOi=EU zTEOutj{Hv2(7F4Iq`XW_4srxO$;oRBtLd3H1wdcw6rCL=*9D%Tw6hUD$ZIi+ z^|$^RwOUio?28N>>mlG;fUZ-eRqy9=+aMSETolUDwg z$_K+!NPh13b!kEUD_?G_y)5CWrO|htL{}zD(P{wK5_DMxMWp^ni1S1%Zi%1sAeUG= zO6!YCqOQQa7BkHe%JQzS^t`>eFbE5ju>108Hf{Dr`3ZXd&R>D&ns8ri^c?K>v;y7l zd0pZ*36-77=4d9uG0ncXKHh$kskjs0x*ig&=E^h#B8^(lN=4~Ae+u_hh)PTk!Krs& z(|tssSNB!0^%FA!@>+wgDvs&y;GxrA=e6Gv!j^Gt)Z=M1-l70kkf}0{UCJ!skHCf_ z$rF2gg)~lx!PcFLmmO4`7X!o+2P4{0ReoJ5>9{RtWEF0#tJKj7MeZq@9B zPxjBVh?5HpD4Nk>`T7y9o1I$`epE7>OJn1B*VZ=(Y-`1)akgrbhDX+#jh8ABwK*%0J=#yeqEm; zda8lL7%(G#VZ{8vg_Kx47$lrW%Q9$536v%I5>mu-rnT-D>Bxv z4rnSdRJ`t`R9vi!+G5y*iG<9SKk^+8?^s9ZIYTPpN?=_u+U$_;#Rm4af??1Pn!4E5 zUFhM!d6Wz2+Ua8ke3#q8nM6Dgvh1S1Qn$)itz4!E6y2tgO|Mjdsp*>8Q;+#tMkTSJ zQBrt`S6AAq-Y2iC?uK4=ripeL4CHkMT?hO%tze(dB>XK8SVh4jK^k0`L$NsSk!dCx z4Gr0@$Ut>01r)EzVOROPNwPmBU()CHhD6DA9-4JU%7mO6bU9x@+AGxK zpaZTu=E2~3;zAvWu&hZ-o(JBC zK7(#_eg{WZ&H!y$7kWz}yVleuqPg7kv;eIpV>h(-{IYp49-B!TOuU@QfhmPt+E@8M zn!@ZQt^z;Cy!?Bt+ux1SNR9OjM}p?p$$q&eHa+Z1u`bWw5J z&n!k1*7D+R92DQ*mS4ARS-$f&sL{lyRTTXU=j_5w3b@{&dvLY#KtvXb^nHJQiR?LR z4jTSaEwSDdJ~z8*sG>-QQzK`z5Nfcegcm2XD1{9-s+)Ni&H3O&D<^67Fu7PF0&smm zw4}@T@2*)1>J(qF=MmiIz+Z=bf?RdXmUL% ztQiC>l(QJcfl_}aXf4S@$b;9T4f9f{_esT_%qXjf@p};VqHrN}>#!j4VFVgaZiY67 z71MdYF_oYi*9cr^eFa_NE)%H!Gc)Ey5>AA9ZD~YE=CcsYqXw+VI9aUOu%hEzPqLhd>%ajbag}> zw1SJsVcX!Ikvy}*NMs1*NR?_1oVgWj*v?ufqYqzj-!gihIi$C$Fq@#_{~jM%#;{3+ z^>wDqxH*e9$p!KTf-c$=p-Wg~tMr!@kCcekk+insP(EVew~4U{av~P(TrYg)ZVBVv zZvFb4ZlisH&yM;MUNZ-UiPV?gP`Tp!d;rf%q`BZFt`uhb7fBg`4K#fve(Oc&?yQxGh+qN04F+8VDlz2r zA5Fwcs-mVQ#w3ODI&6#uun5pgW#WHtsF{wKu|T*6kK?fYa3B^zT|pNHMmW@p~Y zmBr$8Uyg(Qav`8=Ln%qPtn-LZH@6x%YR`gXl#F{ycWu*HV<~S4HC)lbzZ!dcJW4Io z8A&T^oR$fxofd`Mvn!P0CqCoBgD4N4zoDSplPtvOkvl?D*7qP0t1xpodMU4UL~=3~ z`2Ob0N~@5=`#>Hh8^Y)n%}&(Y>{;t{zO+^Hqm(0PtmMh11&3U4z7qzzULrjoZmb{* z!tHQ&|9y?HR8>)ImlQA8j4n74)qefM+F(#LRa|+LTQaRiqK5y@|w9?vPEuDy&SW zS%q zd)LljF0Ixbk!aacjdz0|(?)9yIeJ921S>;{M0qzFEc9vr@6xdN4diLfUHuRkE(Q7p zCcym;x`IB$aM(;!Xkx;s&{?8F+FbZ2KkwYgX0?kN|Gh$fBUExzAUK78}HSR9w#5HDO)aK!$W?Zu)xGnc$LB;*!5wasf$l)}3V}UB7>sS}La)O*v z9`U5oo-0V3dr8+Q6!flc1aRX)_bC1;QYd+klzw)MAmEzSu7>4Amiy~*vumiGMMQ7= z)?rAatnk*>UmveVvN{f_mj%O`9~)6rq$On#Ju-^|)qtA-y4xzbzA}8?eV2)J`t^JU zjB0f4BobO2Cw1O^5<>U$hZYuHD4mKN8-4QIu1EsBayQ+TT$#~k=IU^o1Nu=;V7)I9 zbT2DrF`yCk0(|NRXN8$brl&uO!|6LfN|fMq>ZH<;^X$F44xqkKLX6bHN8{!nRh~#@ zMZe2trIS$DP#0U1`#)Rf|5l1) zglDFWHJ?{)-z-?z^=8~%m$H-dN?g;&*xp+)>*N7$GU#R-`l{g?>I|X9A=8{&z~I9d z=CInWsJ^`^T(ZSY!-pNp#z?(4y_|}_xk_5Ydpnv$K9zAiU)QuU8wiov!kbcRnCHMC|>uH6Uujs2jZ8^2eY zoVnoGNFfso&f^l`rh=}NjIz*=+KTKh5!BnL1&fJ!R4Bd2O%Efo%%!t$p3rH`9y=_lPZW~tLkZh7tjHw|=m4c-kW`0O|MB?#y!66F{uaW>0%CB$*I8-%xMc=!+HmvbxkbkN&eNO(G8j!>7xWSg<3u*^7r@bZz- zyG}buy#z|Hu$+5|A0@PS5!g&`%{~mp+lPOj%geO& z#8R$p{^8mV1*UMGjwt!oL7i*>=kHo&JK+8R-N1aTFV8$VY0|aQA-h@JWvZKme_W^b zg^gY?eK0%gPp?kAyL(InneeaxA_bokhu#P(KDI|`bJZkmV@3Jz*W@Zo5%P-Zel_GWy zOlCQV4Zi_53v`1}N&4`bTI8rTTrPiY<%AcIzE(p2QWn6aEq|l^6~iel{3tNe?@ch% zwkzpxCZ?dW7)4~m+72R#ENBTT_cE!-}H+OR{20qC2Xy6@qSA+;w8 z8Q5=atiP&tZa8^*Ux5R|R)2`((8gaC1O6l)0ci*ex+l zqt^1br4;j5sy`O*XsF095f7^D;Cwl33UnpVM@4@Nex>>R+QZ4P#Izp8bVXaA4*&79 zDF&^s7vScCZiY||uF3ZN`)}#~`BO?cj8ra*pHU3(Q_}zK8d|_-KYb11c#cf!!avs2 zzK8Xk@gcYvY>1b03&x@P)Vof!4(`Y1fiA{WRrYCqAKA0^sZ&HssA8(>7;dX}-Q#lI z;hU#K$yInd>o!iGOnVpYK0e6EHpYvcxpKLYkK~D#y6uBHB;fokA9RT(zVu;!Qi=S@ zqKyq%o&0B{{$Cd|6dMUiS9cIA|2aeD$<(s; zi`X40wC2P7io@KX|8v{D$2(r?S3b{l9bP4(P~~m&3c}G+k!0Z!iDQdcEQZ1t9gMaA z>w7?oK=-m|+XAXiddqA;lR)W@#js%IBfgb;Q!h_xl;am2(uqFvvY2W+zisK;uKPCl?9-*6ZqGxon$aUuP_LYNvLn@>jrh14P8sE31!+p5dQx!8Wwg{Epi`J;G5qV;PBcBp>utJFE-N;JR44+!^v* z#Z2!XS&Di}afio#e^)<(*WWVGl?=SLRUpdXyz+fUo$LA9!=;*-?_7j?jU%Y0>Dhd# zTZ2KD-OI05XiZ2}T`OFuwwUv0V_>@7_5v*p8Nm?@7 z(kKPLgV?^33U$D7RhkXBm7rVr#m45DN?`Y@BmgbzX-1K7(eztF^A851JfuCsJeKOx z+8>)!{hP6RfzHjgRXzx8O8v&QJ`yr2c;v%XQ0$mJV_sa%U|hi&XxmREeh>|ve}Yzjht$R42$ zM$g9=u4%cZN)@MO2izLa4H7yRJgY3bXI)iHUhF&{W@pq5**z*p9f5|Ut4}iG7EF55 zKIc=!;!0Q`Q_0d}XJSpyx(#1L#SZt5u$JqK2i#iF{e@7(L8i07zAzaBsa&|~8c`5} zOq%cRmt3I@8NmTVMm_yLuyA;qSdb($XdqrZ*~|`6YBB>A>Tm0lf#k}|IpEfT?mkb{ zhSJ&Q2qtD^Ep)%|v!$Ida!v$oqc=?-i^@;cjO7Zk_O93s8jjmk;!yjUQjaTpJIxe@ zL)Ah~%JUf_DZs4r|h2fC6CS-Rp3D)nn^(0P68{2X8D zODmkkV~0~L#w)sPm4?IABsDrg7Sc+$Z*D*RQkXSO3M_ML2u8oxY#`t^g6;}VL1U@D z%fRH~{Pwj(;$2NO!NL^geL-bxZ2cHJCOCg+16`CplYcM9qJMyjzr*tGzq&{$P9Kh2Gf^y@ z>xeS^dLR*m^1zs|ygR?eWHBG_$Tc1xElP|eP57sSTSxAV5IaARw;goddV)WB?)@5( zvGUxi33BkpKvWm2wXV2YYLM%HjgsqBi(L#&>*qz*sl$zhW5DhDzudld5x$V$KJF(` zsB-I{sNYvchkLG@Lv^2zeaUK_MWa(~Fz~B6qJ{HdaGvO*Xu_%$EmV70cXzs4Q}x^k z1?vRepsPm2_SLMc)q+2wThn`hX*S%R$~rL=FJiH0o)dx>va3<_c7Q9M-}mJGMd#Ig zi58QSlUd|k@alRHwjgqIzyXlA2XtFzzcpTjPW)lbsHukkX?mq@$$N<6>4{(NS%Ig- zHe_I(NbnLCtl63wwD=f8pgTV)fuf4h{W?5SaxHz7D~AiXy`Wp^JwGJ(8!^DU2c>P~ zT>FoD5yU6l4lV4vb4N8+M%AISPv;}G_Jm~eRo|A2E1_hG1};C;bQK(GD-w!wM}}4a zZXf7UkQ`OL;LcJ#Q$yBSX2IlHKg9=D?2J(#-*ba|zV*`j`_)V@)*bdq1wXkOxo zZpU)Q*h*u3DL^Ss5~_bU)@Fk}d5gV;8dST_ewbA-uqM4bLHS8scWPup1Uw(cK{sz( z>8Za|iUJ1n`%@N-Y|yH-ZlXDrz1v)jv>N7U!w*Q6pxOjHGhS?rRBlQnX9uzJa=NqQ z0R0QiRDzSl3b4LD0lL&$t5=dmF>OQXIS^WHPj4JL-zb)-!#-}uE#?}cLt_1=l3J#u z;-u2K>-nW)Of>^PDEOM>HIUn-<)`7+s)-1U?hssteKH0Y+APk2b* zN(!4^Ksn9C`}`Tu9{Td3NJT&Sjj5d0eI42Y15K{E_l-{gi&amHa~Y2HRgK#-iXb1d z7?ND+o(Aa7fbJh^er;rJ*N&1~;TKeyVLH}WG^&^wy6nUSzec_QKH=3AR*9wf1F`1#(!! zuj>5PY;}P1`mgCBtEBZumF&>DV_NwC$z23o7~*xUXcghT9`;Qr7O=(0#RemI2T43_-F zhl6gz?3}`9{$PW(s3czfw&q@Dg}Un#@}$ohTFu3C@IAnZ4!z^j-bW_s_3_E<>(a z{lpuMX(XbNe(yjyjz`y!?0#sC;zrNDIY>74&m*c=x%{usLO7|W9t)DUl>5kp3ch%X z{ZmCa|DN|K*!XYW|8dtq7mIOE1vaNmm#INpBo1=@;zhbkBZ0Ya!ToaOnd7vt*nx@h zCI9cLIjNki?W=(PwF05s{7l6gv}y9Vj3SuuBEVe--6axgf2nSPk*4*zj>*>z%ri|m zElai^wmW8JA86Cl6J2uW=E4d{WtHwT>(f4h)vL?!NhKpvdkfRiRa>go0}H<@)k0LA8Dw@ zO{lTp1D?>VHja#zoz%DCG?MkIk-my?9JeJ$45fmgd#fqL(A~=aIUisk{j`F<}py`%p)L5c-98^r^rDP0K9^l;~G?Mej3qAHX zQ<17uwP~c=rA>U6{*dIkY|+T6EV)sg{uEe8JCFB2>o*7ll7H?F=sJ5f+t+B9Q@JA; zWUMC#q}_*cf994P+a(qzGAc0REPYqft9>6=6r9M*Qk=%~Jt9s=%!56GcKZ0NJQ3l% zVd|gvZ)l;={{8*~x}?>%@7ff!TO-h`UrzgW=-D1QQbU%mokw$9DbjfNP}}-=`TyOf zv&tL{e$AabW@Tqf(pP?3OM#@M#1MIW+5E5j72-ecF6cf__Tp0=-z6qLPoKkH7;h0t<)^}rVd{0L{?+JeWotQ#exhkmy@YU{k`VI{2;m(0KU zo}ebsw}kp_M8N~Ub0#ke@#A9N;H{0UlrdYmBgP*||FVQf#sA~}XOIs-w^?G^B}$5e zu^-;I^Z5n56>vJhn$G`!NV}`3EStW6_;h!7NQZzlNOvnKAuWw`gLF$t zNlSNkceivm(%n+;_1xEU|JOX*@3F%=-T3%ja~?7Cn>pvuru*@4oeKfN_aFZVbUhHD z8H$E#xZWZzs@F(}{w00rT-5pVf~_gbxOMwES;xKQVhc58TK6PXQw_&c@VD>p^MNO~ zY9;T-02Mhpq<`yj|G7Ot9D^=0q~*5dJR&)^LFk}&LibnP>+*EtoC0)8@hJ;yj;|`b z=6myZIX{hi_~{qR1P9-Xag(7Ld!q>A6*w%xY_0!$o+13_d^iE!9)HXr<~sEj)Ly5L zd7J(uve`BTs;Xpl>SIq3cg&J43BF}$0nCwl8)UjBJ<2~;ELg#qF4B3 z|Mz?QpZmip=u*V-Ql}yZ;W6Fee`RI*@Rss9?(aKkmwgK{f>3t|D`Y#)-C^wRLe&w4 z_SQnbdF*poA=n4gkg-p{ayi?$b;SSI_5J7X8R*6%I5-#;s2%+Skye`oia;QIDSXi zFFh^t?eHqG4+eHsl3 zrq;mm<`v-n^WXjdKK|Q(xCUGpulH73zZC6wRtXp7ugax#T0#k7ck8i_?Ik|&W9%?U zH=^KRNn)z`d|MB)=ABX5tq~?v+^HIWJmofb-xw{x`6tzX-5b!oFmR>mFI^kOYG)^d zwyr?<74e<<;irZvd{&?BjPttQYQt2w*mt&obE$Fz+8-toHJl~oXqer`jfdH^3!8K@ zfO`wN8p)!ND6B?RR~qzR$K2x8J)5k}m`*ouqIL-9w8H)6gE_yv;O1@b@@(p!=o|yVhEAj%eUe6)%jzS*t1~^qI?DD0hOH)L30; zw1JcDwV+dWCX7{$s+)+9i&Ak1k>z8cX7D`4r1Yl04mf{t54uvR5T@E1b^>UE5ZsHI zAsgv$k2?^PkdKk^$7K{~PkNbGS}1wEV=VaCT-%`2uz#W&me!zVsDub0=4yP9JWT`I z;Q@3XIE4oMb(QS)aU|z$U3=OtqPo2zKQ-8p56o+E^vuzvz!`XDn?DkxAFyHAY;AEj z#`vptqNC;_j#>Roe-FO_xPL)cZJsqe7@B;dudg7EQJnXgH?DiF2v%QWWv@Tww(@d% zcdB^+Ap`lRp0uQJ5j&-b&lR%RIS2CTk1=r=*2xTTJmBita z(T`=2DQ$at@crQr$?PR1MQrNwuakI(S4m^n4&XYm7toFMl3FUqLH2B|o{<`al||UI z$Fq7sP5NQKC}R$T4_W(*j<%hjh=KdNyjaUyIHL06Q^NeMRMd$l`Jzm{e1T51@Rw@SJGJpwo7 z5E;7~L$-9>_0PC84<-li_4R`(Qe|-~B8fd3>R&M|7uaCE1oD3#M*n-jR2BLUy!#y_ z_qz|)g%kVSJ6eVfU$?rSBF17NpR#v2EE@QDz00@VbC~TPKid*$H;hLRx|?%XRQwfD3vW15&gYb@Fg!&e zrt)3(42dZuEIFKJFW7@J5rT)2cvz~>wovaJ$a8hEasfkf}QuuWofVD_liv4%cc%tub{SBzGJE1`|{88aIjvE{A&cpJa zqU8+db^YM^SbO)7ls}`7R1yN2!GIe4F(MIg;Xv1lBD~%9Ads?(UkX!(N??iN++kf9 zD{Kl2BDT+3{PeCl!i}O*%k5jjN0@6xKP05^U7xH4$x`b{-8AB`^HwRqg$LaMO1ojA z{ih%FK3Q3w<~0lR_}PM`i2>Hy9rKkE;!Jp|y}TOgDyPFKGS1^J1e zWe7fQ=6j0GEju@AYNHggXJ<0at_cNHATJ{5P6+bLk2vMQUzF+SU3(>k@~=;{&%*T!`lU4`S2hgU`k5Y8yx#=P%SOy3D>|b& zdSGmWsp09L9bY^5PwpUS%b9OUTd)Ba1$6D3Lz+D?v>g<&v$NExnBQ$akY7GyrRCf5 z$BBD>@0|Q@rp9}vjb|BhNZvGlp!>dG{oFWbQ*?%#a!+I6R_@>a@4wd@D(G%pt8TeD zV}wQ9tmZ&cg_C+uz*h@-`r~o7?9hL_;KnlX=3Yqrlq5#ly8G)c>3Tp0ssrjyAr(!O zb2*8;H_H#miw3$vG`ve4Rqum{mEY<#M~(<2Q^lX&uS@Rj(uf2tIga-XRC6z=KbWO` z5>bLnO>0T_+$e6bvDjbFT>305phL6`xago8v#v|ivNuFI!yIOV;?=Nd_*>;NiEBA#M_7p``4b3(ZAox;}YK@i&RiZ&z%txJPNf04@gTreZ-ucF0%T z1Y~Y`_WCqEc(c#c32(SHO?HU=>1OUM8yXXv+H9qs(b;w)U^OHo9us=oB=H7WPZHbg zfwcO}7;rH`m-E`;@nMBxARvK_xw#bS4c%HfZDr72AMGNl>{rGwKN#7hUJ8kQ&MJK( z%rZPa1Wj#c@FEfgUOMQ_ zaVMQUFKa286rHQ>T_v?R_Pn0ZaDbd(KF~QkXJ^-XX`ShOI}PN;23=-$M?qy6jBD?a zXpTEOPRvzy-3|r_l3?xVo!wwkBP7k`=fKJ%>dKVEQ`M0E))KXY8;^I*nMhui#N(6@ zPc?vx1G>t8$=J|-QT$?4^pjBG$xF`)J!<)TzjpZf2dmf?oYlI?HZfHBP7K#?Uu&Wix_S0dIsHgUtIImz;)1Rn$zD3TTtrIzQKkDLHq=o}gk^R8ks=8X zWH!~Cw~`;P!n!eBMpF>2+5roEe*gjKHZNtQX%%1gT`;(HiI&16s0Y;u*vtwR{U+La+_7`5bvLV z-g%LWiPHFKB?gKSGf(QHfX)$+ml$-V!zLirI#or!p?mufDW1~2KI#TfIxhn)AsL1z%$c!9(*GF&Z#kOnxL=_4(k+AyfngSt9#g|%wY zKweVN9dFUo^GNEuraaY?B!6>v$$odU&`Lf$khOs-<93Bt0w z_=f%9g^fB6o+Ys#QAzBWBjA#OZWI>2`SImk{}UA>iDgTo+&9%Ii!O}`h+%y8D~~_D zQ(h%jiS!gyD-1)%TSbI4-AxByH77KFqDFtN|`L=#p4hW$rAfQbZh%sCi}~ z`n$Seg;YXBUA>z>e18BJ<*t>4arNBmp7+Z1-0P-YUR&95Fd0S?DGDPD(?Fm-2=1pq z0lM09a|OSDY~-!en{x0r#Wk83%}NIOAoH9xJ=>4Fzs&tbJFLbp{wy9t9BR)TlzCvh z{5LDNWYI1l>~}r~ydwpWmlAY+*;6m_6;L-`>k>+@U)oE=zHuOu{w!7a)VrN#d{>wt zJDtDEjz|orGIJiH&)?0AFX*SEJ*F-=-}sy6ilegwaNmG#zzkPVLF$Nilq{2-ZP*Ed zVX%1#kvje4(Lh&!AW8G0`?t5YO_5&YURVuoeSlFr2AA{(Q=P8@F*0hbDN z{{~OlsKz7{d>$nBSbQuI%M}rebI|e-fZO4-URxy|rR_qm=yWK0sSHB*Em3%ds^o;8 z8{9WrL)zAx$CMSI1YBy+EikF8M#_%%8s(rCn=bDO&`b5O6z71-v^k@g}oui`| z2?BhZIqsM0F>N9GTbaMflLAjR_KQq>sXK28k9-m^tDuD_(go2`+w3urDS*7Rpi3K- zPNxKwOn*A=g(&;>KzTRc_2T;5_3jVnjjp72k1{nR%a~4jZ?_f#KHo~?b?5Jf+99FN zFyc@H;c6lkbp#nmzXk7S^q?yq{^XDEHBGPdDs%JR&IL|@Oy6~j z4E?jyF%`{f*jX;u`cENF0V$*cO$JmIdg35&*i=Y`F7I0^K~XYpq{tZ{F9Ya0_trJ* zx7C{k+{M2P*a~}KZMZH5a+Qr#pHDaNHFQu&ys!O`zubv_>)c<)@3>lDo1ppiMAI!z-0p66`0R$`fqh#)tbX8N{4sf&tn(cxtB&sk;bol1YBm&)uy`el_{ar z6?O}VaIhDJ_8Fv!AuKF9hlZ_gzcbp>VVX6FT(;PBe!X*U>&g0w6UpTwS8zoI?NCr! z-#@mO0=REM_jvtq?Q`;domfH5RSVHjg@1%KCU=lc*N1x4u(G?!O?%20(zSL+jifYT zo2DtfZh^HQ94q5Agsz;HmU7HISb)m{x)Wd8b!H#eFmP04+Ih~jtSm$m51#ILGv8B| zRF!%&Nbq;&HJSz$Vfua{!lCL!*hXQ%>&N3%%c6`MZ+a$cj`vhYRdXf^SaQzjq1qT8QH)#z(Zl36e~GOuIh&^7FHC+x1MqpElc$0to|3i%FDf`>;oh zQ^4f_T~=Ex1yau`FFg6OnW#kxUd7df8Hl{>Pp0CbbK9^!Ja=Wp(H@XXq(|)TXGwkN zi3d!-QDHq)8kC#~e$1zz(*iCh=mt$YcWEzvZEJJG%qC*jaeGCyEMz(EA`nNh?&uQ5oYYSHi~9H)zgI z@vOY!jY}DWw*NzVdx-wW%EFY0p2lX3HFm>`a9X}SMm<-Llb+_*Q!3!{g6`pD=OwzA z)(MBJXodPV_Dyjwztj0`4(Ce8XBrp(sK%n1?UtiJahOW|QZkYr4kzBmVm0wP5g#W8d=j`aorN!fi`qLE;U*HOa?~s9SKJY zDmg##f)b|L5tJJboW7O)ouk^UMUezte$b^_IA{O%UY>QZbN_Bk3pZRbOzED%iUInI zOeSlt{rJ*KA+ z3&){M)km+^{f}tI$t}4vT%u_Tn3%|2m$!S({y~-x2qZY_ zMW~jo{V&&5JI`Hi!kI3q{n68*Qa;VdM{wWZHY@eD>EatLn#0NE88N`Rkr3$Wrda3i z_?=#A1Ydr>W}ix|+nqCmSim$*qxsj!(bc*290?F30?2ov(mJ71{up-kCdSpAH$odNCO}N_Yv@iD4A`$01>Ke0dfNl! zlSh`gyD+mV3>SM^vEZznzkeB|f1Q%pCQgg&aC&jVqCAcwzN>aH^J{x;Z0JavfC)g| zElq_Y?Evp*VxT)M$4j*_Tei2^aWFr1;PBmLOU5o5^{krZ#r!2B{aMl^yfFv9i)&v; zzl-9{7V3#Z@#L18ND)jSj+Cqi9t=2c5eMBviqGRgFEO0&t$dQG2{%`T!%avOUT9GT z-O`GcnxEnS%yzhjpH=gQl|$y8*Vyg#gU8 z3-4^e0TsVfv4eJgv{=j;12%8XUt=Oh{zqTlcj~d>I#K^ zQ~7n@4$yz(eG#0IpmLYvDPm7c)+dl*8!Fu^^0H;HW7&L0pc8Sy>W~dv{k5n=m{O;G zSqzea`Uep#A~4vY;E>$8`?hiop4MzmVmcn|xBLyW+Mf)abf`=_6Y3 zjQx@(Z!vXTcWgg90;qFdM_T6XgsxZ1$PXx6p!y4Iej{M1cFhUkwozK zvGSl>U{6v_habh{DO*DQ*^?^}-C3LfyC*8BBMX*3D|Uxdl3AFY(StHVce6kA^e*_7 zuZJ)TId?qn0Oo_uTa_BHo~i)4U-?9E$opUeNSb`O{HpKyhNX8N@^C!z5Ew;%cG6&T zaQsnn&73^r8Fm(!PGpu1T_&S1Pd_r>6y4LYz43{_1=>Lobm{#c-J#b>VZ^R>cbl@= zjxlF?kiO5)P5ugX=d$G<}aJbRrlCH?3Cfq zUQSRMq;^I5CTlwpBEFU)&7WS=@ky>`-uN8{4qsc&oQaD22N=~H4afT!2Blzcz*Poa z;+otYmcSPwEW)SnNZR;HO&cPCI4d`0pRL}mQ@w}%(}s(S>7&-T_+!^3)tb8`V?otL zD2Vb~!UFSbC00A>TfkKTU8(VAeDT*g>GZf{2V=_TMv~Le=p8P?N@|#I#aqp4M>2Wo z!GEy{T{kDcw%qOvLO~b8eEW-H5@Dhg;-)oVMGv^Dpeqrt+sepKfjHqZGV$8fY}3og z?S6wp@o6{iD^7~}o0FEzKX22| zmBbWgu(ifqO8)ZBJ=r0&L~}85y6CM~%vJ}V7aE{js9Mbww;g7x`v8Lwa$+1b_so)} ziZ54s{Lq>c4{w-`vUXYg&nJ^C$itw#zF);l-J)#K!}ahkJc)K zMbP&F$IBV)|7wD+M2QCeuv9YZ`|k#S{c~Af^m+{m)t#T#lHMpjJjyQI&jI>c9Sr(p4!B@yUStI{p0H0B}BNfi7C?u=Omj4ueH+TCUjsI^rmSHjNK0 zVbi1~{WQ`nMJ4MWj*^@8kOv7@Asl~n%WecQim5#z;)8Nn0q8w{L$H6Q4Z1zEC`HzY zy^c!l0gf{1g#vnZZ6U0}jWcZ#n+4^USU6kr_2MuXbrVafPF6J$!hty9-(>^s+>;Ky zsjkG80&Rf2I{(K#HKnsJ$LWZS)R9emN|z%FoZWT}aa^YAcwj~*pnknAUXIY?8ChtS zmL)3OY$ub^lY4r1L*oz+;pDd{*)#>XpFuaeWNz$lzxvb9e)#tH3hvcpX&o5!0a6{C z___;ae@CfscelJ>kCdR?Fy9ZLq4a2cRnyhb3O0edHGF$1-9HG{HU8OB{Qn&=u&n6b zR^mh+8-~+`X{H~Y>a7!u$Pf%a+%9SVk};ZL%0OZvJ-THkgYbF109D zLBgjl)Kw5@2Sd=6cV^@8%K2lZCL=wutxlM-el5d@YbcE~*M#4(@o^_qAbsk-@-HGM zE+Qu0FQ?mwsCPp|+Z4kKNUhoAdwpZ@fNKQ0d`$%udX%3RxZn!S$jpgRB>FCnXP{dHr|)yRx*#wVSl z@OBw_3cx<8Yd10_@0nssm3giU@DA7g&Ek{IM@wJm(T5uqlTNCyV1i4ore)hbg)?|} zk&|lG0Ob7wy5v~8hL&~j*fFfr_j;rmtGo6UX1R~5^Ge?bo2e)L#F(m+ms7z``RCo@ z^S0Bbh8KO(r?ffqG6yZS0x31(v0z=<1avWVN9K1iG%cByRr53cQZaew2cyRg`Uix# z1;J#R2e;e1J5X}szq<@d_m}9!MEAo`)^w`1DI8ec>EPv2ch>;&nu2b33KkBELrwmz zl{LJX`T;6E^zpip1~2#Z{kJClQ}b7efy5uuvT(!tDEALV#(Nay9 z@klCbutDiV(RcFkOm8cO!G{rSRegGAk1DM@wK-r})tffL1-KTVt1d`LRYWK`vw0B_ z*5efMHLRm$+tR!99acir)U8+GPQJy3hdQUIHd@s;v{orZ2w&_CIAZHMUtP)Xf`q!ZFIX8TLJNMT z9H8;VzM5E?xMV>wK(3tC0rvy30^KvSDGxV=9>c|VT+(h7g7geU7{OQC)XWjK8VfEmQ(&tlW#V1DQ$x9w+oi!-MUc4ujSsf3HR$f^U_v6l6E8FEIgFl7 zTV|d7d^YM*5Le6%ZIR&%`<8$H(p#=S$q8v_GbuY#mn%mvkB?PON|0>R=)+H~l)qqq z{VV9gGDzBOjL=Z zq~*_8RXgS6TW$WsnE#)W&VTDLHlX{#RIO@Gk_>{UC8SC4YB_Rk0EORP$cgw5hm&08 zal}lyy8Te`3U<0>@Fc0RX(KJ&%WkwXt(vcc_Nqaz1ztPwz1e~;vc0w1@0+HXK>cV6 z!f*E8PuN^CVxf5_5K-YYWzNY^)!uZ?zE84*Q z1Pj^kF0Bb{CiZk?iHB>#VYD22v0ieN4Th=TIL;KGWB(ooi@EHP@sibHBDo#`c>%&9L%!hVM6LhaP zlJsx|U$6;TVs_svja60vt`q1=R=dCO1=&rD*>Sn&%*Ul+4Lx~sXz8M#xvboOx=&|4 zq7_cEUr)IDL`7R)Xnl@WV~UuX#8|&7 zD-O3GsfF+b)BOQcgE%V2FCqQ3OWa#r_AgiQr6oA;ITf{kK6mT|*9Ccit`^*6$i<1!*7tRsJ(lM z0uBD2^mcrQ7AAj$xK&Gi|8%~~v{U#ahZ-3Rt^mmE3A)jABQI&_MOmK)i?%OSpF*4X z&17rumVZh|Y0Iobbg1`i-P?*C+4nweK$}k6p7|HW6UhOtqQ{X>OLLAQifUgxy8vuRio=Ro`VJ8egoZM)bxAVMn>jEPFn{2J-RpOz8|ot)GzsM;&a}k%<%B1d@?b3 zE2qD?zGwen6zf4bHtxI8n!3dtzsZkYExpRP$mEGiX2=Q#I0?R@ogA?~B_OX4=n~NN z&RJQL7zZ-b2zcIl8d@Ku(jE=Grpxk-l-~*z5vCZI&MV{<9DG5D%!KVxY6}}`CR9a@ zMXu#x=MBA8;{jY>&<%1`Q%poqJ95S*e~o~7^9ZwOn!wOW@cJ7WZM-1U)}x^hgaqS~y_XvM{HBGEzaI>R3hjn?MOP{96!Kj@N};%Tw2 z1?SGLV>?g1ZKxOD$@S)Es|;mR6|=~Wv^>XG&{d8t)$SBO$>8fZ%Nv#n>td9Nf{u}s z|C1aR*$M7f5dga6<`H$P2pc2r?x87p_%dkx-ugUwx~Ok)ln~|XVLLxn=sME!|1uq1 znrWryL@#B&WXzQ~*Qa+Q?xyFGvycbJ-GQK+tHGEoz<*vh8@U_yj{5$gnaL0O7Mcpx z230gK>D$}ZEamMl?m2z8DC+_y5b0F7YdH(fCTu97+X#)ZCE?8G z^yF�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 z GC)pf=CS-wl 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 XHL~<5A2v;2;gM~mam>B@PyhY`fw`el literal 0 HcmV?d00001 diff --git a/package.json b/package.json index 6967afabc..5402f17f3 100755 --- a/package.json +++ b/package.json @@ -41,12 +41,7 @@ "wallet", "wot", "blossom", - "cache-sqlite-wasm", - "svelte/examples/component-browser", - "svelte/examples/event-graph", - "svelte/examples/feed-viewer", - "svelte/examples/nutsack", - "svelte/examples/sessions-demo" + "cache-sqlite-wasm" ], "engines": { "node": ">=16.0" From 4b84677adeeb4531cb83eb60d582dc08b20cbe0c Mon Sep 17 00:00:00 2001 From: digitalbase Date: Sat, 11 Oct 2025 13:17:40 +0200 Subject: [PATCH 012/139] Update lockfile --- bun.lockb | Bin 771588 -> 765300 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/bun.lockb b/bun.lockb index 78710d3650ad56fac9732f598950b2df2fc28b7c..3dee859a1a8f14403b94e2587a4f2c6bb1e0282d 100755 GIT binary patch delta 72619 zcmeFa37nPV`^W#BX^wMFnpRXY+Nh>X8I_r|BkfeuBJE8z)wE5^4C+i}DG52aTO>r9 zG>BG-vZN5Q7P2pekhKv1_vbv!VZ9HPEHd<6HRWRm49Q|66>eGIrF&@rq|=%Yau7e0^d^FLw0vJ8=B?oS_9?)ay?GWjuG_*z_@j zMtJ4$RWV(RpN&?&8<`HC?`I^@l8N(bb6hP}1$}gjjn5vEJ$i!Yt-ZkKCA#?WFULFRfhAJFpmUFc`}wu>$>|ba}X`RI$Rl!UgXD9gPtfp9A6!> zCt6*jT2H?qYrFZp0_T;Ea^Oh}1^j{oYKmg>7oru>H1vt+9CRggceFCx5v}me(5k_D zXeBge^62!D*#&te@YPj*AYRqThiGN=d9+5xGIV2|cnd~6A+#bMW%?4dB4~}Sf}To^ zD5AXB+?xF}5<-AyeklNV6y8bHm^8hB4< z`=K60D-Vm&;<*DyW~b8$ufx}QQ_;%P!eQ3T_{vl5;eHu%2M)^_JTPxU6{Z2j(>=!@ z_>XX$a5Xd#2h$zA&qfBk8U$E9${%2bx&D9|5!-)C#mIA`{bV-B%AMLE&?(mX)QZtl z#`y&+&v;eG*um*a`eM#hmO7$Uoh{MgZ;bal&I?Bu}@E}Rk!&Fzg_F1 zmCoQXqeo{`##<-*@!o(|es<)=&Z<=_TCcz#C`A-p_+d^{>-R%z0PV2j4_xY>Uhy)& zf;_ak>6>VE$8R~V^RK+zZ>hw99F;vOdo&fig?Ln~*;D-Ei%E~}SH3WFs$WlOXpQU* zvHi8GM!$&qr6_||v@=ie_1x?IT3%J?N7sCse|ET%U)tR43FF6*-1pP{?A?M^YmA_x z2E%jk)hgAm@H^JU=BJ{Sz4NXNc-1{GTDTWOmx$NVs*h*U%1Dc={oz7%14j)UlAVFC zdP+sB2bV)D+~-&MJ@~gXU}D0pO?;Y>Yh2^k=kb+5a!8ozJgYlzIG;Z)+Kv95KhM)NtU@~%az7@e>8>-B2mozSZF zI%t)Dc?sVieuMR%ob;U0L&tb04L9@^j!+%Uf8l3okrRTM4k zjG?ktc-db&%p(Ggq&qC4uc={0{0>?*{+#I|v?5Ae)aRL>k5+;s(Q3JFXmydcXrNbLpMP8LDxc`j;@D}plj;>4WzjQ$iE zRtDRk)d5>RVm;UR9(;AEUihWdaVBi^JHR^}&-6k16}L!fL9mY=n3Bsqff(sAFlQdqczmFptbz`Y*WBXM{h>!ys2AgcXgcC9`i4w z&JK)0*MYY+{lZqiiK?1^fbi-#tI_IMH<|wY8Go!##MhV)KIb350j&%*+wM2no@f2B z|1Ex0RrsudUYw{&p#@q)XVS|7uMxV@D}Gmh9j!4q=~X`i7onAbWv>Oin&>HL4T%nL zHPQI&VI%UGlGE`OZ%wrN!r2yYnU@1muOZ;=m;9lU`-Y#>z?=S1cmQ6X<5!?nwY||w zpaEJ{7&QIXj)0ej|B&g~Xw~3g)6LMTu@lg$iTyA7L+-g3qrP#Eff;BG_5o;>sJZDX zXsvV-v)I8Gd_VA^U)4MDRTCfLYoWFdzZ!ZzT1{~oT799f=~~pV!c~Z3XgZ92;+J$N zTAgI>gMMclIDVK0hc^vhv)XvHhTRafn(lnGsfJx0HEb87)nvDz)w#!x zrb&ozLU!(0tqG&eh(HHk-sk6Z<>!8czg^Qk%5lx#i3>=P`59(GgaxCZc2GN^|Ugd~cwx;8& zz9x?uJyH>l#n))>XZixPveU|R4bv4&xB0=3J&3RR`+<0+KSitF60^*1-;Wl)f55NU zA=!f_swsAGKozu!zRnv|FdMFLaQG-Bg>|mqEqVEug8p>`O3jf}{LiqHg`0V&1T4^Sh)QP3EEqMzQE8WDB zII(6;tmG1FS6j(N3&#w~h}Y86>3#zymU@ZB-QQapCKh*zx=E~{5*`2Vt(+25LwbWG zdf-EA&U~i&Lo<7otq>mcT58YZhfJ>b(7cg1hFq1b5+_u-x%7-A8szZnDF2~8d2POP z?+y=?4J60LUE07^%L$Z?ef!mEu^aM|-GJ4BNVs9ZJC~X&9oxSsGje*PfOj@t`Plxo znUV2$=iqs<{n5<8YL_-5&@#9Wc!5hF5oj80);QpG_RU#G%-4>XHP1X8U@T@wKkNp~ z_P&{%dD!fH#JuH*`OXouPS)YDV49xJfU!3KZj|ohT+|o4Qu@Wfs-2USOnUM<30^V7;VYjkWAT!X*6^#wFjBJMXfF{RQ z7Gwtdu_bkWtax(MzzUZ$F3>XR`{r)>xIl}@8SGKDiU-ShH8XITOB>I*Yk(~h=Z3>A z16~(AH@D(3r^nuXI1&PA{pVJ;NIpGwdtNv)AJPF*A~tSfX5<6B)_B3#$`3Omr?m1T z3&mEh%L?NtuQcD6nUP!Zvf=^9U78sw*E(@*+>*>lN4#c-kKKhg2rnt#C5~ax>%2fL zVku`YS_Z1!+Z`SYeTEvPAnnoJ6_p2mc#PCEsRrBE% z3f~V=3N9nb+Uz}uS~241YHEjgy-`~)VV%bTKl7D3`cWMd4;Jo*tCAg#IWiN{PQl&E zR)MUr9X}>EZc=8Xd?&wV1M%E;!)xSW!%R(c#4ze%+HJ@bv|6Z)I|%t zmj1C9G9y#)&c&k}OvwtHb4+aI!punN^Zk4#$0KQHUNGKYrsG+2OwNp~!_$Nkud1XU z&v(lUY3`mU6c(|UzPe9Z=4wFbqmqZT9Z@4|4qB~L}vmxqkl=_j(NaL=4 z>r+3+F&@5Lb?aMPVornVUfG=E{b~@n=*sPUc|9j z`h=z>Zn`KC8#$qQ@I^?sSng~(K@b1DB-KH%5U)opcS6(1Ygihdbgv1S;rc!ON{sjD z`0yPMQJ%=~6PZa{d%91rWNJw1Wz=Pyn3NU9QzEo%N;_N#QCG0s{1gcTlvy%!WoD#opTk3) zzR?a(eT{<9yKcZ!%LHQknRQ;o^M_>y$4Xs7ch)hNkxb_C;{7eQa&l&HDPE`esQDVp zZz=V>bNePd#diyy((+?}8?Rs7Q|!(9`6)AvEy@gB?uus9h7SWZ%;}Fbdx`%3fa7#7 zVvX_q3{ZRF%kbh|CQ%EKjS$Uf@s^8xXC75Rw$caq@zP%>WJX5f>0AcOw9H5`o?mEH z)?Pfd5h3SihHDP=<2>AP!#R-nw2+tt!b>3Wsr)c`8KNO}xI2gI4f^d_*$}0AI800g?B#3;^RO3 zI$k`L!xytM!~9(M4Syk?xYi}qwD?agr`Zr0Em6^P#!R`BQER=E9L?#--UKF7S!Hy7syyxws$asX4k*^3Xp zzN7s)s)QRCW3s?g6LNjsOQqoXy;gJ6DP#Pub7ZIwf~dH3H2U!Z^QeBR=_R~IeqJYL z29Fs_ys`A#nnqe-`CR8r!1Kqa8t_p(H77AJwSHwG{p(ozIKShN77a8EPbpW5?cbFd zSnP5Za1nkN*ebSfLDN8cw-*q&!KE!^EPXWI&p5NqBzkgQq6gljS>vHd7@)Wq1amS2 zH@mc3iE9tAm1f3BoeBQgw9Xo$ zQar0AYVkole~@sRyM0C=k_PiGIa9jpNKqbdaoq!}AAgp-WxPomBVRKuzGD`a!VnrK~ty z?wx_AkzAP^@RuSR@S6A@OOb|`(LeE$;wN2;cY#)0!SApxisiDB=y18ekSP~i`AueI z9-g}9@$t#~T|CW2JN9O7#4lk z;*=rD^{#{M$=$6)_L&| z_hM;fp6td|W-ZeFYP(3rFSRT2)Tv6imGzRc0$00FSFkQR&7~DHE~mO2RMJy3-ST24 zu#;!`1q#M8KFkW^_`R35h+K!K?s!aWKTU}TUfl7 z8I0kbr^Q2X7nXIpv>W^joG2!g@-=}L zNx$CcK1B!H#)v6CFV4VHzoKclzOKeoR;-(@hy-Hp)3uyX@22<(Br^<4ZRy{+L>A&U z#4YE><z&*UIjAY&H_j;O)EKS8zPh+rC2an+?;c)yyxgSrNr}NCu3{-b%57WWU zcR8rw`di|2&%-p`&qvIbE^#Rg&cht&(w9&#So8{AHM`7GcAC0s^3F0ObQ+}u^n=hkE)o$2s-Bk&Z9zZktA zPa(PHY|0AZbip}114NqNdUP+y$2**X;6{1zd~!Qb^ca1#+aiD3k} zsAdu7rd^qlQ*ZNYpSIZ%$O_?Uay`6Q3aoW$n;Ee`-sVPZX2dpK?B|b4r$GwvniJfw zk3)Ff@VIH?*uvWr%V1`o@FBc-#PPd-R_wtxcW__e*vW1dmc@B~ocr}RmRv%?T=8<6q!(sqf`{Bnb*8&4gyWCY&;(dNQCdZeQ`FQF^TnLLZgKO~Gx_w*yP=|X=r2GAT|KS$>^~~UWysTL9 zl}*F%U}@$#(hF+3v}eg$zX$y8S4vlu$b3AF{BXR-zknB?cXS<3`t<=<_c`v=&RA)a z^x=DysSveL>G&Wn`Jg|f8@v50=%PJb+IG@e^^kuSoszw%h_}k-5B1n!BfN{XCJGe0 zwC4$3ceUTm{nd0no-!ZsXO~BCm0U?J=);}Y_+9PDN_GjPEyo+j=No3ac;{m`LwDJA z*ZOgVxOXLYQ(fANq_+vGD;xue&91VpNU-xde<+lRcm8YeR5h&EmuE&c;c5P3-OZNN zw-)kPRX{LfJ=YN}UBmsc;tR0EvN95b#4q43V#+NcB)U9$)e!enY3k*w2exfTzKm z!6WWstIqCznO+2tzla$aMSZtIFr!KshZ zW#e0CTd~wgDZ26mr*95;SH%ZMm&XI%B>%cU^a-F#f0GFt>(cn)p-@7}MNWPu;Ef>DbOY{lX5tNr#~nQOS?w4q(vh;_`H$3kE)hDTFf;N3p5`5H zI`KMg_pf>WE)LT}f;D=a~{F5)M z_yaEY+i$a6=K3=o|AwQ#$&W=yS-M%>7 zmlkCPuft4?!I2O)@uSVc7~TxVv6WkF!LZ~h@}@djpSe{FO-yP>n1$K z?&tp(^SBCsnHlW+BbU4Q2KO8+h4L4i&*J&%Xi*dT$@l1nd6|K8T-uNH&AC9o18Z0C ziNitS`y%GC)Ot88{Ih>a4aYbXz6{S_P22r?8C$aK&ASwKb*&1Y3pga@U9#?`r;9 zZ5^&M6W??Qe}(6-X%g=jof1fj_b~qrmJxVLHr`&rrSf9A#Z7}>W3`T#A>1%2Dc)G_ z^qfG}+Tnqu&o7Q1<9S|>kf%>Dc-iP6dIUOv9*HiAo`^mHJsYjpzfnj3-4Xtu_$on` zkntq+-Bti;nJYr1AI4Z^0oGUmX`QeRt(sVGTv~jC`O-T6i1|nB67a{2ODE$$VZOBd zr=td*Mk}JN=1VKUb7&>_yy+M1_|aO&UotMOgkLrPXs!5PGoH|GWGI52Xa(41`VG@> zqVT7Bmu`7XC#QpI>vevDfR@-m{vx{$&2|X=Uq>9seV( zcxXjt72o*NI>~KMS-3L(oayZr z?)S99J#XP&h+4oG47`X|hF&)R6+14i4DK{vS|xeI{NK}xXSW@fR>E(aFD>6y8kkf{ zA4>H0L3GAf7D8J7Hx}|+$f69^12aI{vP4}nV$ zx8uL3H4G-&acRXj$$V*blFQ8hKRWvN4*UtOgf1tXGFWKgrPW)nM(g-Y<40@N$TfC6 zI@}3b)4eTI14n;P0SSppVc&()T{K0Me?u{pL%nH-C%PDxm~R z0<~pWOM~BN@f5him$l=6M)RRuKGSU{9IaIY$HP^!igsLDer5BI)(W2rSIJMdu9Y?Y7bY!oh{t?Xhq!J!b>Zji%j=0 zE-l`(BtVYmX8PID)u{C;VKyaJ+0%%aa?y1bvWx5Yq z8M?&6OUv(T{?S?)7yy?ZY{#Yfpt3g!t@9=yOZu9juCM^o>SVLbmsSMVqIJR?w5oCm zS|z;)t>Qff3#M-ryG}+pJBeV&WkoQqcK_uG%?*At%O_I@v}{zYrL)b?a?}~v+*v* zyP588yr=oSP51G6)a!?#2nN}KA*P3#9&UPs=}~A^Z5~=RQDFQs^QW0V!}OJCg})Z9 z0$p$VM)Wc2|MM)sVmojrT2*rwnm_LW{WC58pdDX@R>s$%)oC}PHI;8ctE!(y>vgmi z-)cOrHU4Cq$}4oJ&yh}MaX%|FwQOY3;1=`7>Y3V)Wg%gwcWkx2QyN$H1FzFCg%i zYn_bR?dGH0ykrg^O6Kr8u6+>xuDe6(J$F#*eb@CE)CaCe>O&U@p+0iGr9O5mr9N?y zFlvt*D7DwEm-^I|i=aMpIZ~gy%~JbZ<&vl`+&HN(-8QMOT=ip7`(1(5*ZP)4&%ie> ztrVH7Q;N*ZDMjYKbGrm~3N%jv{NQG#0A{8D_6ZzxO-ci@N&^;^2K?mq3hWW+Tn2E+ z%`XF(R|aqhko0Q_*P(2357(|Nq_`|W0`6d0f*cU&TMkgd6_o=lD+fp^4+y&6Qn^GsR%gU?Go52(7Y0$qMKC-FtZY1pFm~Tq%t6@GGI|< z0N>jb*dx&SL_n&We^+-9do^0)0;c)O1BB0hXNvNJ#~p z>UyUFdZhw33eD2)luAn*~zdB&IKtq>S15l?1U``D{W4B9Sr$F9`x=0!zoCe5E1GIPR1=b0q)&+EQIduWU>jJh5bas{N0V>r4 zOsNOBz-<%QDv*8}psOo54Um5tV7I`9E-f8UCmk>+9dMD`C9qSVd3``nH>*BiW_`du zf!?l313*@T0VvJ@40Hzt z4hZx;12EVXodH;O1|X#&AlvnB2=bC; z1TfjnY66(q1h7w_z%@AwkaZSd(OH1Y++Kk_0-c)zrnvb{0rQ#y4hckE`(}W4%>c#C z0Mpz-fdc}4n*(OJqUL~Q%>gMb09U%+Edae*B$slJv|yUP+C^Fd!Yv7r+maBo+bOB^_0W9hQ*x>dG>=EeP z6|m9G?+TdL6>v!4G1tBupj|gWaW}wbcTnJfK;H`iPq?BB0n07~q;v;7<$8Aq^y&`S zDDbq4Tm%ST1jxMzu+6O(SSOI$1MsZN=>Ztt1F&6SyQ|z2P^l+iN>9KGZkxbXf%IO0 z9j>4kAio!2x4_FTtv8@fZ@`@1fLGlvft>=)F9y8sW?c-Jc`;z0z%JLM4jY9q0giP!qX5H40k#XI zxXQVJO1Xe3xqvcmo4{6q^wEHFu3$7Ee>7mXKn0gJ22f`VV9pr8@otyEPJ!lQ0Ttb> zv4ELl0s90hyC&lRS>phU#sR9hy#jj#I*$jWy7}V)^Tq=X2~>6M^8oGg0L6KL>h7Sx z0fD{~05x6F1i-QhfRu@VQ(f;8wF~+$Rt2`5+HXHAkD28SSOG=8BmX3Ap;Db z4A?G^?keX4D&+&FB9-Qvtpgv~j%)0lf+V8wJ|B$TUEB8X$KXpuJl!uudR#I-sM= znGP5}9k5-Xv#UGd6X@-lTn)&&8nEbUKp(ePV2?oOnSj1-{!GBUnSetA{ayQ6 zfOfM0#j^kd-9do^0)4Lm40c7=0G3??NSO`DcD-ijYBg07kl;Ie_7F0NVv}UFGWlm97I!xehSKZ4=lkkbXU2oGZ8=)=K?0XS#tq1=K}T#6u2fg0bJncFL{N1$^IFvZP}0p`U3 zhXkUo{Y`*&Hvx)o0!(uU1r7-Gy%{jW72OP2b~7O57QmIR_bq^4w*WQ@TR>jYAl0Pb};O8~={0JaMhyUKR~D%}N`au?u!w@qNHK>FQ)m9F4! zK>po;-2xA}w55PLO96A10#>_S0y_no-ve0dX59mrc@JQpzPHP~d<--+KX1xT1Rj%kBlFtN=XadanTV zS^?N7@U)8*1H#3C++x5sw_aeKK=bDJAmDX3>p{TG2Lby8cDW`G0kR$f zEP4p=rrRs9N1*d6z-~8x6=2>fz#)NmT>I64cB=u!s{!x1g8~Nx`mO=bCe5%7bXwGl9LBVeDv zLD%F_K-QyxMUMi0a(e~#2y}i7aLCPn3^4C8z#%}=uR+&glNKPGAjO*q5^x7M5#)eC z-_3v$u4pr0*=9h>4F#Jit zc7YUE`6)o9rvOu)0+eyv1hxvKZvm8Z1zQ05TL8NSD!8<#0d<}R%y}AcyxS$PQ=s`) zKt(reD`4hUz&?S>uE{n));7SRZGb9nufQIG&d&f+-TY?&^PT}55~%9hKMQF0ETH&V zKy`Oe;DA8i=KwWb(Q|-h&jC`l15S0lw*z`@2W%9m?IOnhQ9#VE|BgjzX+)GB4EmkfCg@xz*d3u9e@m1umh041F&15p-X!SQ0FDUoR;FY$=|5sQ`;N~n0PG6R5wro|58>3$4@=a{}ph3 zR)$|fiGRR$s+)FMypsO^uA+(BQD2)pJZJE5^nreUx5E5SvuNReA(1~wW8{Cf#H!V? zIYTCl&(8L`xc8#;x&Nt3=`z^=iGOA65jS@l8TwKPI?xIwAi?rJOm|A6JU% z6t_bG{ze^szSZDSIra6N1qG}&-v0sm3^d<=F9iLkShVoJkjNjT(f>bMy26aLIadVR zmT3OmG`|^ClZod5{1H1^{boEf<`+mM{xdB8?T$Ya@Xtp04~C2C>p+G7Xs-Xk zME)QRYyW>=g#TFe$G^PzKdgRFU*7#6RzF`J*F&=YXVtIz|6f+WzP$avtbQ;0zpZ}i z|9@Kjr2jvy{&H&n|6T9r%d`L6>i6pYVf80dHT+Om{EqZYxBo_#G{2{!iR*{1go#HJ zUbo7BM44*$Kg|3^%zrqM!oOb-E@HY>_%D|DZ!;79?^Si7nmFNqdzgXhG%}n&a zSJi);u%#QnCHVcS`LEPW{P(o}ch&zo?XN|#$Lr73f=0=@EqV zh5uZ_mTpvS_^(yrzf^e1yo_g`Vrf92$V!@1oG|6L_HoSC(A?g-u+a4+5w zJZ^2)lHlcG+o}JXE&4y+=09~sFc$rzZDUpYpV?miy>M#AKd}e;*JkOF;r1u0t58+_ z?+nLY$!{WDJEd#zoWR=f1Hls>*PH7ZYx(!p^=?zCKsEgvcEngiwX%yX=s1ay`FEr>~;D z36n05#PmCLyxxN8*K_n!Wmg(|$L&xQ?^{RTKu_R-)U}0k=U{qRE&xrSqwFrY2untVWu`PTIOtqGVI2DFh#KP6Zo@e1o z!u0jyddOl6cf5r=4R*V+6JTnsbflMl{+eZ~R|%+QtB>?1pjdqvh4vaC{p{q1FlFX+ zWT1sR6Q)|oKn5AhG^U?q?haGNvWzvvK8MCu4L8w`FX?1{*xWl$J%HC)Fy)Yk>UwRB zHM4MM!rB>YZs9Uv9gMYvDT7%^M`LH(xjbOb>tyU4J2$G0q4N#4v4BlsU5uS)j9*sw zx*2P0jK?*37aD74Out{&3#QVwhpFT(k=_=rJ4}UZg^Wzr&M~hZc5-X%v1DIkxTmqR zvFoTvym}cs2RqGJZ;S9;SOb{GdLIkd20NeB=-1vQ#?HeYr7Gvu_ZZSwZrUQe&m4dC zw}9>Nc>6g18URzc_K3zN!_XUKtOK^jrq~de3fd8=f#@~d!ga!~9Vfgq3RBdbRep6m zy+#^5ANv}rK-1GGm4DYIt1aA6?5ET#RsU-Y4#Pf<2CI%<3sc+ab28P8J!0WW&DeX!Cc_pR zd*4_-OznOO`U9BSy#To!cB<PRbpT$6EZmjYn~eQp>?&CFE`z@syc&47F^#zZ{!C=4v4F8zuzQRp8M_8n zQ77;!VQe<`T&k!(I@#E@*f;qs>IDtX0XhqKjIrxr2Vo7+A(&>$>yckzdZk*p8?Xb( zapawBY%Z>b?CEIDemdnwWEi$yHPA_F{}_$}o`F8a0^Vdy*Xr8FZpQ9}txI)Xv-zTex}HS=f!y^^MKPt{t_24bVDy0iuZX%CLY7u}{N36McrUTd@_n zSVLoruv6#&ndnBwZo@8PtT9Y!EJi9Aiz=tOM&FL;8m`ybcJdwA9gLl0>`qu`W9M3g zOJKT$>vf)my9--aJ}pGr8oL`ijNJs?&ObNmEyXEiu!9A>2d0kJ6y4F-GHmtcW@t5o zs=o;N7Sx#!YLkZ$b;8!@kudevRmRRm zk2bd2SR3>>V{5Qe6$!5iFcoAiQUi7#dZMv)*flNOBxBL_Kz17AE1<~+ABHVfEWGlK zZNTQCV0s3TS77WBY##R&XO|k=2)hE&>oQ}HVyhOldb%7|5&al4Jiz$tfDyBRo3P_; zj=ss*W^A=pC*)?B3h+3hY59D#vv5yfe`Tf9J%RH7B%*{bK<2}g@KeY)s$O0XX#KB% zTaeEUuC$Y%hJ6C-f_@ODxVIuNS-4deZW~N9oR(gzjXi^{8BT1Cv1hS0!HKOk_8jSZ zCjc)*uLCNP?TAX(1O2cCd>;E6STFPjV=q{^i_v=SjtcT3GO{cyVDv^~JFq{%*6UGY zFJX5goYId!q(>*ejC2O}MQ^fzuV5=P{n49^y^39(9y|d3xUtu;6>cC}5BE`guNxbL ze$v=Z>_cVv$LpyO=_}waL?;hHZ?S-HV7FlCj6gqa>`m;}#`J(7PVwGCRQ;pS&sezK z*s6Z9XN|p$t?Czh&e%IDzkZZTuk9A$yVw~gPmkdPaaaj?<1+OT(q7#B=!MP z)xy1E>_b=$W3O3!AHhx$Q~keg0YApB5MivN6Hg)f1iKbYFFlb+iR?k@7<jYVZFYxaNl8H0~?Fp zZ|r;Q>nxlez@)*v2@c{=WsPS`Q+>Sc$*4fIq_COyM7|AB_Eky$zO! zK49!;?7LtS(FbAtd54g@?Obh*C^Nqx>gf6CfSB6|yA(5MEItvHVNq4FUga#{F~Bd4l{XfGX@Z$b zxC+L?FikLG$HA1D2<(14_XG=95~krfgX0yA9ShU&ghjnd21@}oJ{7RCu@u-!*lgH| z#!ADAU~^zqjFo|H#nbbB~W949@m_KfWoeYbw|H@my7_gcJtYGXWSaoB^ z!S2Jq1*rkk#pHNcQ%dZxn^?FLU|PD(hn;0it70wP#F`qb1go#>{{mn$psKJk@I+QG zw_&$1b|Orxm&LGFFifusteUYl7U4-SEmRclJY%UaEmXwX+PNpg?k0^pV9`z%uqto? zP_NF$s=;oB-3ilkzi_?k{3{Dvf_;Ip8Zfo-{je^^YQogUVqJ}$0#g%zPDhjEL^6=_cm;` zv1k_XnNkp5iQj#00(;ijdJA|KY`d|CjWvb6YHWiszEkGCVeAoO&0%jE+i0u>?CnzV z>mREUgDrs{T0lK+O*PjFw%6EZ3)dR>6vi2UPoPjTNz9|5Uvxf2N$A`36OXuTxZzrC3PxmJUjV(SR-RQj9mcJqk(kof5})E z*iFU~&y?#5(=WDCe$RWw0(JwA2kP}IOx1rOEYH~M7Op#N0_-!6>mhWw-bMVIXyJAl z>j9f&>`%Z`U|NXi!FLKc09M=B zuNH0~td23gQ-SLZ;$NDv08BMF7^V_y!J>Cg6mAIr?qi|yz3M;N0%ij*QEI&Oc8UTH z&ON}=IrkU_p>~b*m zKX0T##Z=x-9)(T&cv^I5QHt_%`KQjO*Kx*1!_@h-5IG*E5|6>&2GgRWqJ`7$x>j0x zRWdeC_kUUvhd5Z-ptjs~^PvUCi56iVwq{2yCQh<&6Rzoc2Q=;S{N(DR+raBMWaCOH2wvQ zwK6sxmSn88u^F&rV`m$?0u~J#JO_yBT?ssfdqBO;vw&B@>e7*Pt!N8V8()o`Zmhk9 zn+Y2N)3u_5v02!Or?ho6b`4C=E9CkSjX&}c-{Tup-V~r-ozWWa*TO>NN*9Z+cJdtT zP9)M2txMLY@o3PFg>YF*M&haCA<*(E(@n$>&NwO<=;|c*)U~#5ljWuwIVv)0^Wu* zn}E77%vH5cVJ&fI9&rq8M_nvQkbp*xyF`YKaH)|Xf&CQ zdUpZ0<7mDgYXR@Z-faQL8Cwdw&e(XgN^%eE24fQ~+%lN%nERq9p;a?Q*t!!I%eQdL zu^Z~KpMnJj@5R=gu-K)>R$y!55YuBj6=5-UALjY~XmwhYcOU;o8JlYCe%SrY^Fz>4 zV-H}jg6UP5fGdG(EZ{T?_#jN}r1^fjv4^lV&*z|L7+Zy{8&*xRR~TE3y%WY1>s@JV z4YnpJ&4^bSi>}4dET!4+YJ=;rr!d25rkiPOJ+@Asf}UmUVQihN$?6(o8?ZHGHMz_- z_6WAFC7MUBHMSA^j1+(U;mrZ6S|0@#VrwqA-U2>`ttnV@f!Z47ZQ`GLxQ6arw9eg( zt9>_x0QdTZD?z>h;8Ga9-643yU5rxs=5aa-e&Mw*fN;L?qXxlVJ|m! zyRq%CD`0xvVeEOBo-wJ{oyJ~(b%1I3Eiv{Yb`@AuL+vhuJFr(^YpC6A>?Q2`j4d_x zGE56f4Xb;My@IV7P9tcUu~)Hmh13WtGWHsFOJmFR*k4ua>p=B%4HsnqOqH1;L7YDurBjD4l+|4$^M zoNh6=ANxZ~SQiUi?`!^j1XHHA!j!>pu$37i@t(18-(v42oZ@)a*mu~U!gTC8JNJ9+ z&tTsLj^jDp7I0HDvdrt}!ER`$&}-2{w3(-4NC_l}M39omu}CQ-1u27E$Ei0UbCH`6 zy(ytL92Ou8kz0{P$YSJn_PS-pCToYWF&~_o#}g!Wr*I9UXI+0EOC3g zh8m<*q&6xeCnDD~7^f?8MNUSlBGr)UNDbsvMA!1# zNF5{%sf*M@PD9d>`bY!hbR+{g18Iopl3kX|v7YKUA6bAbEX4a(ZE2(o@-cP%39<+I z%w_ZlRj95_aBX^P`+o&;AEKStm5AP}dT8yA-l4Stetv6KpHRb~o=13z+uSGAG^)KG z?d@nU=SAct44o4IbW-{E*R~QHi(`AtOpsKg49AbQE8iz$B`!y-DK&esw|=#sq%ZPB9c`nihISM5S&<9!H))o;5u(-Q9SEz^lfA0}T8j2Yv`TD=u7hYXcZ>UOV5ni&+N50vNki%)^^nt$<>XD9 zi1VrBHOMOLl}J8vBmN%bO6=)KM`Q=Zeja(+-90GOFsjGV_9c}bq}CnLa!)I{A86lq z5v^f?^B5FL}~%|m@UQM$kDIwX{trjPFFlXW_* zPrr>N`fsM@zVVIiZu?vSFcW z(Ny|id0++PMC2r-5t51gLP_*SbdZ4?LQ)XD39UDsD1 z^IxOHdJFea0N>xm7!Wz3>uEyg=l5HrK-13_3rf}h~E8tl%JUnT~N9bD<<6> z--{HLSo?5J=$gPWt64g&Np}6lhHAOOk)dmo<|Mh2qe9c&4WmLQ1-V1#diE2+l+kTyqw#eB?JGi#}w9R)C5=J6ONkpGF z)-Ie1a-lMfOoVCE>?}mJG6JnVuP!CIl4+ysR-ihfHnX&Or2tK6M%}h;M07K=9chi6 zjdVm5>0)Y4{ZngJtyi@+{q4F`Yf|;!#)$f`&bbKDHqyW{9hnR;1|vg}VaNz%B%(c~ zvB-GjJfsDZj$|Urp!S!vbySnmmc{Sk=1d6HOjEa&=)DuYnWN9k=<_jpJL-G%P9)A; zFI`F(H3T%sv6s6dswmzBSe{LVnGK(0b`%dF$I372MJhrkCSoscewwo|ly(h1R) ziL%-QTU#Ybh_*ts^RWWC2Vut}8sFkjg{l#LD;)^b2}wn?b+Ljl_aVCJcSvG9Xbg-- zjvfQ5>?w#^U7G;w5T)}lnb+q!b|DXzaDxg$Cqz|Yy8mxv1uIQqM{(X*M0vWzwC?j0 z6{dT6-MZ_zZr7EL#)10#1K2ak&s0RmmH$;}aaD>$m1!L+8qb*S+YcbRSJ$oiZbTW; z6WA44qC}M0L|0biDwJAQch$OJ4DH)5bhh-dSR$R>u*8HxHSltic~@Rz~Ubf#{M2#H@8iYpOGICZHVZj z#s`oe+{%UAsp=kBK@K6mA}4Ta2_y+Q9*H19BpC@I2S`$zJjY;%k>ilENExIgQW`lH zNkK}PEnNXAkCa2y#ud?ImygC^R7Orjl#!Frtw^LDqR-#;g58Yh8w}T>PexZmGLbWp zbVOxtj5I<{M;ajYk$OmNq$YA3=PDDYAj;^eNUdZR*L5+{kUEGWI}K@woPlH@XCYaL zdSe073{hli`ia;RkWmO<^znuxgOGv90HirmhOqUJ7T6`wnMi-^3g|Ke_@{fQet^En zB}gfJy^W>7eef?v+9SHp(pz4=vGwgEy(Lx->4fc}Rjp?tXCM)zC%lKdV^OF=SABCz zU&qp%Rtq@|Nk=Y%>3c`r(fX3pN!a=VRAY2kw7wv90irKYbw)ZOt&o;3cw4Auj-ow| zpsmqI7c-IjM2^qqxJs!~{7Sp(3tt)d+66inNyS$7^wp*lk>e44qD1FN+PJy5g(?Kv zxw~%*wMWK2y!Oa3iRY{4! zzqQp;zb%$dRRs^@yrGEt{}5y_qCMzrbPh5C=}NdU=m?UFj7IX1ab}OzSHm=@#v|%F zlaWbCKB6wxkqG-y4_D&9iEcwf&%mCB6e3fRa|kyDeL3>0y|M|XbHyc5~580pxx}=ii4eMwTNh5QV!()79M=cOgp=ourH?P!X~WSwY(?g4NhcWECQR zo%w6gi3rzVD;{M~ncaY>7F9#4!R^Rae+ZtaBZd6|Nz_X}j&D?-{u%fa z@*tu}enb+9s#0~LGVt3ls(2ks$gkL{u?VG6T-Vbp6raAal4xm#O~zLj9YU>@xCEyu z0o8?q*f*fnpA|qS>8mYaq$F}IQW`0X-1z^r_uX+-9Zmn7E66!w0V#61fIuwQb_Fg) zqbL}SMzLUdOf+gpY$&2&iLn7D_Fl&lTa3M;(Q8NS1`VRIMC@Hr6Ewya_5IE%aIb=S zp7-~A-@jfypZUz$-PzgM-PxJhJ$o)FV|C)MjJyDP0FNXGS)O<<2k@Z8<&Nc&eemcD z(E7&~iKE1^Am^q`C}WwlW^3Y^KXiOMzbf+hDyR`q1;8TvqdxbO>$14jk(*8v*z`g*pJHj|66?QXiPV zX{ZYbM%u$3k!=JLyqNh#7VD*40VBW_h67lL-T+SX%trzmqRvNv1^~{}fXn`cypI8w z?djGE#}AVm%g;)~%$o)HU&P6>aJ)8nL|ABTw9?UG9(|r77D$_>j>zi(_!Phkmq)Q1 zI@T9-?E#-5&1&2U&=dI@4{heGRk{Oz;l*mqD$DBI8P8t=x&!V4Mggt@#sS!TUje=h zxCFQWm;#s#7z6kYkO1fn=mqEjV13p8SZ7&x>z{2`~Y` zmjZYeqd>=l$?l>Ro>}Nf;IT-v@NDt8zx=MB`Dq>HPVVOS@rTiCt*6{O?rl7PM}9nj z2hUmnW{faQ?lpMJmW0chM>xuU1Dpe_1+eK}1N#}RC85E zTD~3a0-ph#2Jn>p%B%Gx9<1l(1k%3%jsuPXG66pWcw~+O zjsQLZ90nW$$nyw%5TH%v0X**q>;v$$W&n6<_X5%ZdjPuu&3XQJ0C>K)1GWLy0l25@ z0W3ZDWCNaA4>)ZB-U`?P_z92(*a+AR*aXN?X_yDoYgn7oooYU(lXoGZF=j>4A{nic zhB;q*w#GbV8bi+46w#VS6T)iVHNlkmXJW0lnv^`>TE+Karq4qCw|ytqiA0BdP{#bZ zli2_kqCiN@KNE7#PoaPbxkuWFyxj|K>>_~kwIygRXaVDbMl`-0^RaI6Qr9|kH5r*F zk->_Q12}J0s0qR4T;U?HwM86?G#|;Z*=FnB8NgPN4G~*BwxVoN8M8HItI3v9%fEu> z}r2at=3?2tV0><-{wxB;_JF2MYOP$YyY z1I$jfG%!1zQoxD8PQWDqd3fgE@(}^t@jkr%&3G6Ca71Po3{!Dndw}Kxba?&)VSXRkuO2r#efw6Z9^V{di>K# ze3>v9I7ofwG;2Q}Rq)Dq^Tw|r`sV^ZPvNuU^XMfX6tA(PzveocDl#!I!3Cg61+y;z zzv1~Dpc9@u0Irj?*|EGk&&Y99WhJiz;2V$g8+QsKY)J%=@2p(1NtL_-6osFBs?bqG`HCg&-}|n>^c*G*?sl_*4!r{ z&A(Q(8@M-cFF+iCy(#|?#8hDRs6FtkdDZTC?ndrg5cl$IT3dI`{cs+8?dbrf(cFoOBya)yu(ye= zrPZQ5rnP?NI^CguQ4r+q2EN50^IFn8N;saiwK582Ymokd?H;QX&nnL(n;rgR#$2KB zXsz&IZ8Vr3N1gv9Sc_$~InKz((NtDqaB0^?B>R98;S`)3#xqaKG3ruLQ8S z+~zqnlq zyM1`b0Qgaj?T#M^n9hjOy}`11|ztJKnXZxd!kv=+*%n&Km3Ekp`@%H4i{tmsxTD1(9~W>( zwGL{>d=kpIf;KO_wl#yGEfuYdI}(Y$F+Drq`7~>%?-q{99R9fG-0)Y4UAU;-vH(hM?8V!z*Z}}hctWOyTG{sR_I5- z-0U0R*8uI+a;@TI+U@wUPc7te@A;i0|Jd9MJagI)@2!1k+HS}8E}hVXBa*Yh&H=bH zJ>Ts(6z+fE9>;JU7_Hyq82q8eXf~Kk1_OSd!+G3j71ZJe_+2*VakRB@IWqBgzn#S^ z-7z|nrj8OzP{Tfwvk*Y3ZyfbN^A!}-RpL}?r08$?pxQKcnpOHXDish7% zh5{LkLT9rn&=d$i+xM&9+(fmrTLVsblzwQW4Ito#0$hX4Etf(dL=GiG_k7d1f9?Py zy<#o_l$9!@pt4FT5H5?$( z%W{fD!KQFi5RSl5(+h1jNj%%`d8czeMylzlNMujbM~EJ%aWxryj5HkiKo>{IT+=ue zK$$YK-1HP{yQA~vO`<+DQ0N@0ba%^fY$?F}QZeJVI}Wy+SuhZpD8bE#6vMHKt5!Ps zPBl^=ROOKhR;u0wfj0_jfJmfm%FqqY zE1wzZca}kJDTpb0#P|I&Hom`|Y0oxOgJ9!HlmvyDLNFWpU$ALG6C1j&?EK>wFIO2U zqNdGkeE+%I)VJ_D@_X!rGByzOlL=u<{26)5V9g8srfbD$}lBKt^g z815;^3YD|WJb5FbwXR^cseGd!qBEQh6qL+Di91Se*K+yz@W6h$0J?~(tTllqv&oE* z6LR2wOG>G&AO708e2a(xatV@Ww3>oiekiB9AjJ&^o5D3;K9h zl!TaqnBOIpU&-MY@s&a!trXXm)-D|buRWASjXh5Zf*#UG_Y3XG?ow+tR z1!lpbqlq;UCa0tOSNs@YsTnK=HKUYKqFd1#$qfaYf>@=84D3H3HleHCk#n0r_b3}M z6Fp-E4y0;8JQG2+pgMujoG%B)#do!H{vhI`uN@y-jMN1Ljy!8&rXb4OAiAp5Jl3u1 zD?K0TS?5LEn6}-Gv<}p~atm80azzXo=ICv4u)tEZF@?N=@~3AuQzZnNAR*S}A+sRs z-`Ioe&!@L~l@LI~QB^`$Ls5!|BPLoj2!58GDWapjWUiz9`g&c;{7y6rE$Pj7==N8n zA0^Hg23xj_62k@Ei#qghv{)7Y##%o{tb>1c-)@X(4xL(8`9}@y+8>MQc7H?@9c!bk z+VfGuvr%bO{m>heN26p!C>9SUd^PPJBR0o>nRXg0Yf25hq^EjgMXyj(gz^s$o)7*u z?|Q|BdYKz0$81*zCC=dXS>~uGFMe;k7bVaR6MEB(CXE$Sg(NC7PBa_-tS86|DslNm zyFr!pJwX=;IxMxZ)M*@AO`mt)*bfZCsCC_wuk#uF8=xK0_ zQvPj4&(JQ*YgT_S3l!=?Od{9uVh@bK(D9JJ9?cprR;!oOKoL;;@5#oXWm$$h1D`5_ z@`&Vuf(N8S<&3b@sclcnO$Ml|B)0lLDcK+ zenim|K==%VJQ|fUD{N1{a%b4vgrP+fe9=hxcdSc$4I22(>yAE(6k(7rfsUgMVKCjE zAZ7{O>F##XOzQ(gFS4H`jx^*pQD*v=8@v0>nf&CkDn3Rmo|a6)&?jN&d4*?`t@ETy z^z=*+VCZXsvkUz`35r{Uu0YcbO_h#E^f=LV*qB}wn0FwSn%S;0MNG!p3jslC@Q%0N zf9CS~T?rrvLpfBv0VRR}|KoN7({%wIkMWl5+pi9NaVHFPtR@gLfzm;s!|a})ECvf- zP^l?UgF@znEi*(?=Y%>;5nJd^G^5p1P|JlbP7#~)Qm8%^)8NoT>2b{UnB<1iwijqk zt-YFJD@pn=OG~9+-7oDQw`tSrU=XMiTCA0#UGXJ%4&2b>#Lu#z^18X$iq?a#-WHg>`PJ;wbV<=?07%fCm>U6P3=;y5!efW9hm@?-a8edeoVR+(E z!Y1NG*nOczrhC(Zl2r1UA@(rL20>X6l#ZNz&oH#jry#(B;}Kg)i)Nrj3uVt0Jt%92 z81DTGh}o@`v0r$tOR#r}%uOb~(V7BgiqX1q5cbSOJu+v52j&r=D*5zV%tjf7cHq^FH@L~mg-9iOAjf%jZ7Mfbcd zEu0JGkO#^vv?qsoSPeaKL6IXe(;WXQ_o4z!GD~@=lBo>{1bGDF zC4IiQNKgmSvtY<3(~JdTs%?IArC4v7y-@TOpb?g>i^T5)K^{FlotrO~quxtIAC-$` z@e(lsWb(X3Yx6_>mx|%}iHxOU3^Q{3QS2oM9VvMk1~CtolIQ$oo6h>*E{(s0j$mQ( z(u=3f%g~W>lpG~_@Ju_BupAiy6v}9cC2l#IHzaja7QuoI&;Fz@a@cgUi@+KiK}%LZ z!zR;CMvEwF6%;LZ1uAW#`e#HB2k<=HjVi1ZQ;?Is5=1iUVVI8(;W-8wJYm3Y*ZVIf z$wJA?N9LjzD$|2s;baG_5~n9~REasqUs_MahMCGd7<1;v4MItf;t+SCgc%xFuXyrg zQ(AnWP%i?}6DI5kN_ZltwT}C(%Z8mnTmrWai+LF(t|&>pliTI{YR)s@|51e_mPaUY zLCN@t3g(No?o_$Uu>*&vwcineOG2^WLhOpgDeCO|X3xMG#h-^5Y4>W3KL|qM)zJY1 z2sl_wI^U#w|3v4F0h9*<47bx7&;I5mqt}RM@xwCQl z=ke#9y#pu%%|WajppxLgZWA+kHg5J>(NoxE(XSPY2|`thO~VX@trJ58Gxc7FDiM_C zirrCTT7;CLbuVQ~9-g0kYoq1$Hq5bVJUi2=bx^S06cH|!D;8)snEcnH`dF$1WSE0` zEUEJ;WBEQ6+LuK=mH_&;iuy7gKA1ZS+kXsnJbg#2{FL@pwHLmiL)gW1mwVBk^|0@c zK*6TxY5Zr`yWjb1w+``Dcx~>BBjqtCZ*tmz7Q=cA{Il;VhtJ;%Z)@Ikzkn`=>VqIO z0R%im<>!3qd&9VXY5~DSl$7VHO%K#cJJ?GHA%bgTe-&uAh9;q^_s<~k2SN39K0YOK zJoC6}Fcb#6;@-4xgZPs=kXSy`HbSIT6!VGXPJ1_s_TF3K1$ig*Y{0{IcgJrVRnTm9 zJYCs{Ri#XVHt_W+lgGaqZNSV39q0{GhF*P?^?$Qiw&m;bbCyFwcFk}QDfB80aZogM z+azXrXCx?V%i;E{u!K45OM(uohv|+bP^-=2JfQ;RZkFlW{3Q0!Whc=e-jb*8d=j1c zNxZ}9y<0@ju=7a@pQH89AKNo_{!fS-nGe+VP=BSQ`P@C>*Go)XuM=2n(STice=5Bd zKJ!9wt z(Zb&r)RX(57pl=$+Yn4HqO@%oshyMwq&xHtZQKJXvcH+V9g5zWGMY(l)FoYXP*ski zw~JodbINux#PC9myvI09H|EsI$&qzX9^8$Tb`q@RFsz5}&mq)$hZt<&Ul(FyG`wux z;H{SUQ=nJZL^)ct1Dcit0^XFrS`eOGy-m$(I-x2XKKzwK32%$%EU)_D>8y)x#;yW8AV_q1cfJq?SL{!@c1>Xhbh*6Q0yMmDMyzpLM|g6Zy{;35Nt4m1zYq!lZKbR?zf{MSg_tgfK~Kr4+N-B zLFuS%HBE+S)Zs95XzH;Z=hily%nPPgFz+(dA?QF+Pf;h1GX48<}EV>UcJJ8%Zv9U*kTS%yjEu<+pI6R1kA4HGq5ginpyS^=jv#^q$9u)m~ z-d`bxHQNPH?w^o3`VhL}Loqe6flc9OSuyl9@ec$qR^wnh3990$>nN0B4)bzQe4jE; zswoMk`-h<>MXQ#LPN~|WJ3o>79l=Qbqi)cyBM@1g_&<)o58Jo{d5zIYNMNn+?@?NX zlk4J10{;(!l&q;$d*9BgfE@)4%u$TqyWG{)pvv$TYEB1#hK*I@gzA})Lmebvip|7q z*{pl5a!-o7h?%*TDQ*>H8_EGH?1KkgKZd|^CJDz;wKN%y%X^Xqobo3NQelPdLV5QX zWcSNVWpQtaTXXuD>mCm%4!a)Mg^M#O;TQNec~0Gm%Z^!|QT5rj&;7D6$h)ZTH8iRi zc*C5z${zCBvfWRI1UGEStd#&j>50~fB@}f6wKh>lpntoTNEex0kF$GL{H3Rl7$0vs zS(6nXo)gA!Ah~2?AXIOojFdfgP|fLZHU|Gwaz6>>v=QY!jjIE$2uZI`zO7O=!{kG$ zr@&j?-zHJkDfDj+-93eN3k2CpF$m;!2NzNNukfvB7SY}dz;_nWVx+P7g!7`CAD=SV z6|&LbfEOjoZotrTv0^QY-3@R4qut$aFbo_S;smBU`JYDXDO3kY+uu{dY0*pfU@?t9 zEp|ssd8Z++!b;hoG)HRE8HoEe<($DVPx?Wz(-DV6V;k}CmI4>Ml**hHQw@JCRRoBs zZ2Dl!ypDXng~<+s5p<+AXCZ)MON1$O8L6?)(=M7KW|Prvv0?*e%O z=@mE8k!n5%N~NIRfHqPyAcGHlloM+G^xXIS)RcwWoP}S(rz~>1Q>+=W>WLiDUYSUa zcM8eE+_+KA^N6G5>1L{bFN=B2w&7k#z5garHXJ~KJcUTjSq?lU|E|!97ocCV=X7(F zPuf*w7bekf7Z5nhtI&)#p@{AdK{!>nS+>4dwtSz51DIA$?FU_0<~I&Aq*{~Gly;dX z0|YK0C{;1NZo}-F-9W&*I7CH?ch(jbHM|4^s!GXMpf3mx;_`GdpofygjlE@Zg+4KExroVa&{AaQC9Nw*8ctc+ENW~-lOHA zyxo8^KqI-E=piT#_M4SpyQ0G@@3TusW$6UiAyZ%tvz;&bvyHDwb$|>FK*#2{%$Q+P zi>cKQx#b7tvMKnUZdbyi zoCiLAZmsRc8o*8tR?>xp+u&QC47Z_(I%)~@9=?ky76fGv6`8(oW z-Q``hu(T8ml|`WLPOb0aq)MJ|mVUxr>;muXqQLtwS8CVfNYUMaU8_78cTte#hWb0yuI6Ue5zvBv1r2WdZ2`l7(%u#n|-CcbCR>r6gezmcaklUNdA9= zVI}ha9Y`@bwDnIVctg-?Z?-$Mj{=KJo`Ouu8Q1pFp5LLz;(o!dIyc**bGrU`0&npV z$8$^_LU{_i!a`i@a5qE^eRcoLQ}uDG+^TU@cM58zvL)b=T$_gx96?u~!Q!!#&nHJ9 z&VInw3jbqD-gaoiqsux%uOET`f6T^alnq%&1pe1_Dd)^PEhl^W@)-`s4#omu1xsYO z|Fd$)v+g&)ll!I*SzIu_HjHfLP$o|mP1$egGBYWqGR{oyWRl+#2<|{FjaY)}piHIo zzu3gT<8QYfQv_6oRc(eU?Z=Xw@(H|ZUHbhAmU__@70zLQ;%6q=yCiPxa(b`e*Ae}I z(d7frqN`?(qKSV&$IfEk#&-9Rx9jT#cguKcV-Rj9Gk;&}>fSZ~xKG{0cA}2hiyMaG zdt%-YhQ>}A_2rrGRmQw#=8|BO zb94Eo^?QBtZ2>`DO8XFI)WA})Uhs1BVZ;odofP;#uBJ4)mJmGgV#zfHT)rch>S~pZ)J1z# z!cEqGcF)oiLZ=>w|IW~B!8{Tu6{>1ic!{pE0Qy~8;T zxFtEv2()WQyJ2h%gFwLXaN~MM59*F?iUI)+n6U2chEpC}ER&G)CaLxjlRbX z;h*FUE&9B!aHGolEXPRhw9ygAt2Pq7hub-rXns#JY-X)Q$3Anf77ScX@(~g8b-yj} zKYC7~H7QlxcGiAYKrn-1Kw#UF5+rGvhN8JE@>z;YlCPi!@Kmn^ltPJwDXD}MqQ=3V zhYOc{wb&`4-X5UlxPRC|+bpB<_Bkm0EJz%dEk!wlGY*+&V zKG8hc{`9rWkEYHRZzmO8d0KbPC|ErfOq&=+12*$ zjtbi+6NK}2?2uYACvw??HTsV6GE?5U+9}(`NawOJ?1@`}C=^6s&uCpC&pZidArentDGA`B3g52TyjZP{QvS14lG^ zQMK!6SGlAXx4(zhfm3J^FY-We3RU_2VV%R{BA**7;ne_UO+J}Sqn9j%a%4&-SE=V) z7VJNM+h=x@dT87A&2ExG8}y=wPuZ~L@6-|~++AvJD2q$!JV`IF-sv@{>y?AtT^2gr zPMsZf)a8wxp6&IsFO+Gci<$V$i zAP*&j0joWM;ux(a3!|^d!CSh^uOa!B=>N+rP{RehB;`=2_#OXa%}bO_;{y;SCh1Fw zKGI#-nU1~~plbpR^2KlYG_9u%|9_l%=Vk^tnB@w<{`uR6T%qswjoy`J;-`d?hOt{pWj9 zssp7dl_UoY5Z;Hl(W**PAMc_!9C#fRTp8jkUh(aXk)dd>(}|8(#*#05*TBw4-Zkh2 zd8^<~fkyZs)j7($Ewi>OrSFYW2=0+(8>Jqh-y&SEfKfkN%F+GS+?7_&rSOQKx3bEv zwm3f`>K|JLwk_`j?E+Q~Z&RUfiB*+IeqvSR$|1bF!&@Gb9#xe>iU{GER0-idsY5l0 zD+ljp<=DZU&Q+6qiwN7frX03whZAlTSsgA;j^BH5(0-&kMDnDU)iHl^gzipNYk*$^ zH9mhD)5&v0j(80#Ks`(tL|@gwTD0-#NO7311#$TSo>it!?SYUVd1M4(H^G+k-SF0` zp2pfXOtfw9-IE2|K=o}|t>deFS*GdLdP1A(h=x-A^N zXUGqVlEc!6%6norNBLcha;)dZF9CvjP~}?i9sg5j8SgoWeDCSSIvh^?-|5Bw+UbS& zpdwE%u+4l}SJKX}wYY6;1Tr*7X9H=ppe9IcDBZ)#eB2PxiTbwxuSV$5e|#%&#>eQta!g2_(tM z7ejAAQ0R?7h2VWR0-HCH3cnF(mSw<=z>!U)d0Lz=-w0$!$^FEwK)tYqdIRZj`)~9o z1$64Iio*5-LN8j{6oZ;VXPYAUS8jNy7u+bRndHScRrbbUJ0@R+b1N>tx0J6`HP>!r zsP|md+hbLz6JIG)Gt~=bN{S9d!-FY|=y5cZUA30VH5aNIgW`8?FfGQ#Ypcsr>XJ~_ zj4O@!msDK4Tc+N{P~KX;=YE(XsqXSf@@|R4cCCklsbxz{?W%Ysz@F0f`-43!^n_m> zL6>kp(Rv7?X#t3Fb58E9JRp4ct5j=D;Bp+$^LEqb+Lz1$m$v*`NuHsFUigwJ{^?>I zzDnWSX0*5!HZ$)GUkJuMF$SCIBKmfIqH-nH`%dvH%iEs7v4Yk&MRc7iplY3L8+dnW z?6qlDHO%T%&8v`GLX(v9O}i&QO9Ca&d)giY!cT~irUG4F)DH?AN6~&ct zerX7^G!MT6!>1|(Uaozr{q{ zg3w+5CzJf@=F2la?cKY6U5(i*B|9Q?^1;Oq&k_Rdk9hULr=DQQpSC&RtGQNvME)jG zZ6lML3b5SlBn=dV)|QT4q!`BQzL07N_I$~m0(XcW5;Nz+fIvzfh4)kHhotyimT$PL z&DzEFP3qP)u0OsMxpDJE6F-jtOQ$cH9X^WvO464w3TGPY`oXeBjs@p z&l9G`c4q4*6pU-XRiamEGmdg<@lT55Z*T|8=me>CG2Qc4bUsn?^>>OjOr{Id}ae zC!Rh_l4=GYO;c(gkBQkodYaqaUy-T;YPKdmGHG39_SR&>c0To7oh|I delta 81486 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 Date: Sat, 11 Oct 2025 13:19:26 +0200 Subject: [PATCH 013/139] Switch to bun 1.3.0 --- bun.lock | 541 +-------------------------------------------------- bun.lockb | Bin 765300 -> 0 bytes package.json | 118 +++++------ 3 files changed, 62 insertions(+), 597 deletions(-) delete mode 100755 bun.lockb diff --git a/bun.lock b/bun.lock index 6b1f23d44..89f927616 100644 --- a/bun.lock +++ b/bun.lock @@ -199,65 +199,6 @@ "react-native-reanimated": ">=3.16.0", }, }, - "projects/Voces": { - "name": "agora-app", - "version": "0.1.1", - "dependencies": { - "@cashu/cashu-ts": "^2.7.2", - "@nostr-dev-kit/cache-sqlite-wasm": "workspace:*", - "@nostr-dev-kit/ndk": "workspace:*", - "@nostr-dev-kit/sessions": "workspace:*", - "@nostr-dev-kit/svelte": "workspace:*", - "@nostr-dev-kit/wallet": "workspace:*", - "blossom-client-sdk": "^4.1.0", - "clsx": "^2.1.1", - "date-fns": "^4.1.0", - "lucide-svelte": "^0.545.0", - "marked": "^16.3.0", - "nostr-tools": "^2.17.0", - "qrcode": "^1.5.3", - "shakespeare": "^0.0.4", - "svelte-i18n": "^4.0.1", - "tailwind-merge": "^3.3.1", - }, - "devDependencies": { - "@sveltejs/adapter-auto": "^6.1.1", - "@sveltejs/kit": "^2.45.0", - "@sveltejs/vite-plugin-svelte": "^4.0.0", - "@tailwindcss/postcss": "^4.1.13", - "@tailwindcss/typography": "^0.5.18", - "@types/node": "^22.0.0", - "@types/qrcode": "^1.5.5", - "autoprefixer": "^10.4.21", - "eslint": "^9.36.0", - "postcss": "^8.5.6", - "svelte": "^5.39.9", - "svelte-check": "^4.0.0", - "tailwindcss": "^4.1.13", - "typescript": "~5.8.3", - "vite": "^7.1.9", - }, - }, - "projects/solidjs": { - "name": "solidjs", - "version": "0.0.1", - "dependencies": { - "@nostr-dev-kit/ndk": "workspace:*", - "@nostr-dev-kit/sessions": "workspace:*", - "@nostr-dev-kit/wallet": "workspace:*", - "@nostr-dev-kit/wot": "workspace:*", - "solid-js": "^1.9.9", - }, - "devDependencies": { - "@solidjs/testing-library": "^0.8.10", - "@types/node": "^24.6.0", - "@vitest/ui": "^3.2.4", - "typescript": "~5.9.3", - "vite": "^5.4.11", - "vite-plugin-solid": "^2.10.2", - "vitest": "^3.2.4", - }, - }, "react": { "name": "@nostr-dev-kit/react", "version": "1.3.8", @@ -348,84 +289,6 @@ "svelte": "^5.0.0", }, }, - "svelte/examples/component-browser": { - "name": "ndk-svelte5-component-browser", - "version": "0.1.3", - "dependencies": { - "@nostr-dev-kit/ndk": "^2.17.2", - "@nostr-dev-kit/svelte": "^2.0.2", - }, - "devDependencies": { - "@sveltejs/vite-plugin-svelte": "6.2.1", - "esbuild": "^0.25.10", - "svelte": "^5.39.9", - "typescript": "^5.7.3", - "vite": "^7.1.9", - }, - }, - "svelte/examples/event-graph": { - "name": "event-graph-example", - "version": "0.1.4", - "dependencies": { - "@nostr-dev-kit/cache-sqlite-wasm": "^0.7.0", - "@nostr-dev-kit/ndk": "^2.17.2", - "@nostr-dev-kit/svelte": "^2.0.2", - "@nostr-dev-kit/sync": "^0.3.0", - }, - "devDependencies": { - "@sveltejs/vite-plugin-svelte": "^6.2.1", - "svelte": "^5.39.9", - "typescript": "^5.9.3", - "vite": "^7.1.9", - }, - }, - "svelte/examples/feed-viewer": { - "name": "feed-viewer-example", - "version": "0.0.1", - "dependencies": { - "@nostr-dev-kit/ndk": "workspace:*", - "@nostr-dev-kit/svelte": "workspace:*", - }, - "devDependencies": { - "@sveltejs/vite-plugin-svelte": "^6.2.1", - "svelte": "^5.39.9", - "typescript": "^5.0.0", - "vite": "^7.1.9", - }, - }, - "svelte/examples/nutsack": { - "name": "ndk-svelte5-nutsack-wallet", - "version": "0.1.7", - "dependencies": { - "@nostr-dev-kit/cache-sqlite-wasm": "workspace:*", - "@nostr-dev-kit/ndk": "workspace:*", - "@nostr-dev-kit/svelte": "workspace:*", - "@nostr-dev-kit/wallet": "workspace:*", - "qrcode": "^1.5.4", - }, - "devDependencies": { - "@sveltejs/vite-plugin-svelte": "^6.2.1", - "svelte": "^5.39.9", - "vite": "^7.1.9", - }, - }, - "svelte/examples/sessions-demo": { - "name": "ndk-svelte5-sessions-demo", - "version": "0.1.14", - "dependencies": { - "@nostr-dev-kit/ndk": "^2.17.2", - "@nostr-dev-kit/sessions": "^0.3.0", - "@nostr-dev-kit/svelte": "^2.0.2", - "qrcode": "^1.5.4", - }, - "devDependencies": { - "@sveltejs/vite-plugin-svelte": "^6.2.1", - "@types/qrcode": "^1.5.5", - "svelte": "^5.39.9", - "typescript": "^5.9.3", - "vite": "^7.1.9", - }, - }, "sync": { "name": "@nostr-dev-kit/sync", "version": "0.3.1", @@ -524,8 +387,6 @@ "@algolia/requester-node-http": ["@algolia/requester-node-http@5.40.0", "", { "dependencies": { "@algolia/client-common": "5.40.0" } }, "sha512-5qCRoySnzpbQVg2IPLGFCm4LF75pToxI5tdjOYgUMNL/um91aJ4dH3SVdBEuFlVsalxl8mh3bWPgkUmv6NpJiQ=="], - "@alloc/quick-lru": ["@alloc/quick-lru@5.2.0", "", {}, "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw=="], - "@ampproject/remapping": ["@ampproject/remapping@2.3.0", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw=="], "@antfu/install-pkg": ["@antfu/install-pkg@1.1.0", "", { "dependencies": { "package-manager-detector": "^1.3.0", "tinyexec": "^1.0.1" } }, "sha512-MGQsmw10ZyI+EJo45CdSER4zEb+p31LpDAFp2Z3gkSd1yqVZGi0Ebx++YTEMonJy4oChEMLsxZ64j8FH6sSqtQ=="], @@ -980,16 +841,6 @@ "@expo/xcpretty": ["@expo/xcpretty@4.3.2", "", { "dependencies": { "@babel/code-frame": "7.10.4", "chalk": "^4.1.0", "find-up": "^5.0.0", "js-yaml": "^4.1.0" }, "bin": { "excpretty": "build/cli.js" } }, "sha512-ReZxZ8pdnoI3tP/dNnJdnmAk7uLT4FjsKDGW7YeDdvdOMz2XCQSmSCM9IWlrXuWtMF9zeSB6WJtEhCQ41gQOfw=="], - "@formatjs/ecma402-abstract": ["@formatjs/ecma402-abstract@2.3.6", "", { "dependencies": { "@formatjs/fast-memoize": "2.2.7", "@formatjs/intl-localematcher": "0.6.2", "decimal.js": "^10.4.3", "tslib": "^2.8.0" } }, "sha512-HJnTFeRM2kVFVr5gr5kH1XP6K0JcJtE7Lzvtr3FS/so5f1kpsqqqxy5JF+FRaO6H2qmcMfAUIox7AJteieRtVw=="], - - "@formatjs/fast-memoize": ["@formatjs/fast-memoize@2.2.7", "", { "dependencies": { "tslib": "^2.8.0" } }, "sha512-Yabmi9nSvyOMrlSeGGWDiH7rf3a7sIwplbvo/dlz9WCIjzIQAfy1RMf4S0X3yG724n5Ghu2GmEl5NJIV6O9sZQ=="], - - "@formatjs/icu-messageformat-parser": ["@formatjs/icu-messageformat-parser@2.11.4", "", { "dependencies": { "@formatjs/ecma402-abstract": "2.3.6", "@formatjs/icu-skeleton-parser": "1.8.16", "tslib": "^2.8.0" } }, "sha512-7kR78cRrPNB4fjGFZg3Rmj5aah8rQj9KPzuLsmcSn4ipLXQvC04keycTI1F7kJYDwIXtT2+7IDEto842CfZBtw=="], - - "@formatjs/icu-skeleton-parser": ["@formatjs/icu-skeleton-parser@1.8.16", "", { "dependencies": { "@formatjs/ecma402-abstract": "2.3.6", "tslib": "^2.8.0" } }, "sha512-H13E9Xl+PxBd8D5/6TVUluSpxGNvFSlN/b3coUp0e0JpuWXXnQDiavIpY3NnvSp4xhEMoXyyBvVfdFX8jglOHQ=="], - - "@formatjs/intl-localematcher": ["@formatjs/intl-localematcher@0.6.2", "", { "dependencies": { "tslib": "^2.8.0" } }, "sha512-XOMO2Hupl0wdd172Y06h6kLpBz6Dv+J4okPLl4LPtzbr8f66WbIoy4ev98EBuZ6ZK4h5ydTN6XneT4QVpD7cdA=="], - "@gerrit0/mini-shiki": ["@gerrit0/mini-shiki@3.13.0", "", { "dependencies": { "@shikijs/engine-oniguruma": "^3.13.0", "@shikijs/langs": "^3.13.0", "@shikijs/themes": "^3.13.0", "@shikijs/types": "^3.13.0", "@shikijs/vscode-textmate": "^10.0.2" } }, "sha512-mCrNvZNYNrwKer5PWLF6cOc0OEe2eKzgy976x+IT2tynwJYl+7UpHTSeXQJGijgTcoOf+f359L946unWlYRnsg=="], "@humanfs/core": ["@humanfs/core@0.19.1", "", {}, "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA=="], @@ -1194,8 +1045,6 @@ "@sinonjs/fake-timers": ["@sinonjs/fake-timers@10.3.0", "", { "dependencies": { "@sinonjs/commons": "^3.0.0" } }, "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA=="], - "@solidjs/testing-library": ["@solidjs/testing-library@0.8.10", "", { "dependencies": { "@testing-library/dom": "^10.4.0" }, "peerDependencies": { "@solidjs/router": ">=0.9.0", "solid-js": ">=1.0.0" }, "optionalPeers": ["@solidjs/router"] }, "sha512-qdeuIerwyq7oQTIrrKvV0aL9aFeuwTd86VYD3afdq5HYEwoox1OBTJy4y8A3TFZr8oAR0nujYgCzY/8wgHGfeQ=="], - "@standard-schema/spec": ["@standard-schema/spec@1.0.0", "", {}, "sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA=="], "@sveltejs/acorn-typescript": ["@sveltejs/acorn-typescript@1.0.6", "", { "peerDependencies": { "acorn": "^8.9.0" } }, "sha512-4awhxtMh4cx9blePWl10HRHj8Iivtqj+2QdDCSMDzxG+XKa9+VCNupQuCuvzEhYPzZSrX+0gC+0lHA/0fFKKQQ=="], @@ -1210,38 +1059,6 @@ "@sveltejs/vite-plugin-svelte-inspector": ["@sveltejs/vite-plugin-svelte-inspector@5.0.1", "", { "dependencies": { "debug": "^4.4.1" }, "peerDependencies": { "@sveltejs/vite-plugin-svelte": "^6.0.0-next.0", "svelte": "^5.0.0", "vite": "^6.3.0 || ^7.0.0" } }, "sha512-ubWshlMk4bc8mkwWbg6vNvCeT7lGQojE3ijDh3QTR6Zr/R+GXxsGbyH4PExEPpiFmqPhYiVSVmHBjUcVc1JIrA=="], - "@tailwindcss/node": ["@tailwindcss/node@4.1.14", "", { "dependencies": { "@jridgewell/remapping": "^2.3.4", "enhanced-resolve": "^5.18.3", "jiti": "^2.6.0", "lightningcss": "1.30.1", "magic-string": "^0.30.19", "source-map-js": "^1.2.1", "tailwindcss": "4.1.14" } }, "sha512-hpz+8vFk3Ic2xssIA3e01R6jkmsAhvkQdXlEbRTk6S10xDAtiQiM3FyvZVGsucefq764euO/b8WUW9ysLdThHw=="], - - "@tailwindcss/oxide": ["@tailwindcss/oxide@4.1.14", "", { "dependencies": { "detect-libc": "^2.0.4", "tar": "^7.5.1" }, "optionalDependencies": { "@tailwindcss/oxide-android-arm64": "4.1.14", "@tailwindcss/oxide-darwin-arm64": "4.1.14", "@tailwindcss/oxide-darwin-x64": "4.1.14", "@tailwindcss/oxide-freebsd-x64": "4.1.14", "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.14", "@tailwindcss/oxide-linux-arm64-gnu": "4.1.14", "@tailwindcss/oxide-linux-arm64-musl": "4.1.14", "@tailwindcss/oxide-linux-x64-gnu": "4.1.14", "@tailwindcss/oxide-linux-x64-musl": "4.1.14", "@tailwindcss/oxide-wasm32-wasi": "4.1.14", "@tailwindcss/oxide-win32-arm64-msvc": "4.1.14", "@tailwindcss/oxide-win32-x64-msvc": "4.1.14" } }, "sha512-23yx+VUbBwCg2x5XWdB8+1lkPajzLmALEfMb51zZUBYaYVPDQvBSD/WYDqiVyBIo2BZFa3yw1Rpy3G2Jp+K0dw=="], - - "@tailwindcss/oxide-android-arm64": ["@tailwindcss/oxide-android-arm64@4.1.14", "", { "os": "android", "cpu": "arm64" }, "sha512-a94ifZrGwMvbdeAxWoSuGcIl6/DOP5cdxagid7xJv6bwFp3oebp7y2ImYsnZBMTwjn5Ev5xESvS3FFYUGgPODQ=="], - - "@tailwindcss/oxide-darwin-arm64": ["@tailwindcss/oxide-darwin-arm64@4.1.14", "", { "os": "darwin", "cpu": "arm64" }, "sha512-HkFP/CqfSh09xCnrPJA7jud7hij5ahKyWomrC3oiO2U9i0UjP17o9pJbxUN0IJ471GTQQmzwhp0DEcpbp4MZTA=="], - - "@tailwindcss/oxide-darwin-x64": ["@tailwindcss/oxide-darwin-x64@4.1.14", "", { "os": "darwin", "cpu": "x64" }, "sha512-eVNaWmCgdLf5iv6Qd3s7JI5SEFBFRtfm6W0mphJYXgvnDEAZ5sZzqmI06bK6xo0IErDHdTA5/t7d4eTfWbWOFw=="], - - "@tailwindcss/oxide-freebsd-x64": ["@tailwindcss/oxide-freebsd-x64@4.1.14", "", { "os": "freebsd", "cpu": "x64" }, "sha512-QWLoRXNikEuqtNb0dhQN6wsSVVjX6dmUFzuuiL09ZeXju25dsei2uIPl71y2Ic6QbNBsB4scwBoFnlBfabHkEw=="], - - "@tailwindcss/oxide-linux-arm-gnueabihf": ["@tailwindcss/oxide-linux-arm-gnueabihf@4.1.14", "", { "os": "linux", "cpu": "arm" }, "sha512-VB4gjQni9+F0VCASU+L8zSIyjrLLsy03sjcR3bM0V2g4SNamo0FakZFKyUQ96ZVwGK4CaJsc9zd/obQy74o0Fw=="], - - "@tailwindcss/oxide-linux-arm64-gnu": ["@tailwindcss/oxide-linux-arm64-gnu@4.1.14", "", { "os": "linux", "cpu": "arm64" }, "sha512-qaEy0dIZ6d9vyLnmeg24yzA8XuEAD9WjpM5nIM1sUgQ/Zv7cVkharPDQcmm/t/TvXoKo/0knI3me3AGfdx6w1w=="], - - "@tailwindcss/oxide-linux-arm64-musl": ["@tailwindcss/oxide-linux-arm64-musl@4.1.14", "", { "os": "linux", "cpu": "arm64" }, "sha512-ISZjT44s59O8xKsPEIesiIydMG/sCXoMBCqsphDm/WcbnuWLxxb+GcvSIIA5NjUw6F8Tex7s5/LM2yDy8RqYBQ=="], - - "@tailwindcss/oxide-linux-x64-gnu": ["@tailwindcss/oxide-linux-x64-gnu@4.1.14", "", { "os": "linux", "cpu": "x64" }, "sha512-02c6JhLPJj10L2caH4U0zF8Hji4dOeahmuMl23stk0MU1wfd1OraE7rOloidSF8W5JTHkFdVo/O7uRUJJnUAJg=="], - - "@tailwindcss/oxide-linux-x64-musl": ["@tailwindcss/oxide-linux-x64-musl@4.1.14", "", { "os": "linux", "cpu": "x64" }, "sha512-TNGeLiN1XS66kQhxHG/7wMeQDOoL0S33x9BgmydbrWAb9Qw0KYdd8o1ifx4HOGDWhVmJ+Ul+JQ7lyknQFilO3Q=="], - - "@tailwindcss/oxide-wasm32-wasi": ["@tailwindcss/oxide-wasm32-wasi@4.1.14", "", { "dependencies": { "@emnapi/core": "^1.5.0", "@emnapi/runtime": "^1.5.0", "@emnapi/wasi-threads": "^1.1.0", "@napi-rs/wasm-runtime": "^1.0.5", "@tybys/wasm-util": "^0.10.1", "tslib": "^2.4.0" }, "cpu": "none" }, "sha512-uZYAsaW/jS/IYkd6EWPJKW/NlPNSkWkBlaeVBi/WsFQNP05/bzkebUL8FH1pdsqx4f2fH/bWFcUABOM9nfiJkQ=="], - - "@tailwindcss/oxide-win32-arm64-msvc": ["@tailwindcss/oxide-win32-arm64-msvc@4.1.14", "", { "os": "win32", "cpu": "arm64" }, "sha512-Az0RnnkcvRqsuoLH2Z4n3JfAef0wElgzHD5Aky/e+0tBUxUhIeIqFBTMNQvmMRSP15fWwmvjBxZ3Q8RhsDnxAA=="], - - "@tailwindcss/oxide-win32-x64-msvc": ["@tailwindcss/oxide-win32-x64-msvc@4.1.14", "", { "os": "win32", "cpu": "x64" }, "sha512-ttblVGHgf68kEE4om1n/n44I0yGPkCPbLsqzjvybhpwa6mKKtgFfAzy6btc3HRmuW7nHe0OOrSeNP9sQmmH9XA=="], - - "@tailwindcss/postcss": ["@tailwindcss/postcss@4.1.14", "", { "dependencies": { "@alloc/quick-lru": "^5.2.0", "@tailwindcss/node": "4.1.14", "@tailwindcss/oxide": "4.1.14", "postcss": "^8.4.41", "tailwindcss": "4.1.14" } }, "sha512-BdMjIxy7HUNThK87C7BC8I1rE8BVUsfNQSI5siQ4JK3iIa3w0XyVvVL9SXLWO//CtYTcp1v7zci0fYwJOjB+Zg=="], - - "@tailwindcss/typography": ["@tailwindcss/typography@0.5.19", "", { "dependencies": { "postcss-selector-parser": "6.0.10" }, "peerDependencies": { "tailwindcss": ">=3.0.0 || insiders || >=4.0.0-alpha.20 || >=4.0.0-beta.1" } }, "sha512-w31dd8HOx3k9vPtcQh5QHP9GwKcgbMp87j58qi6xgiBnFFtKEAgCWnDw4qUT8aHwkCp8bKvb/KGKWWHedP0AAg=="], - "@testing-library/dom": ["@testing-library/dom@9.3.4", "", { "dependencies": { "@babel/code-frame": "^7.10.4", "@babel/runtime": "^7.12.5", "@types/aria-query": "^5.0.1", "aria-query": "5.1.3", "chalk": "^4.1.0", "dom-accessibility-api": "^0.5.9", "lz-string": "^1.5.0", "pretty-format": "^27.0.2" } }, "sha512-FlS4ZWlp97iiNWig0Muq8p+3rVDjRiYE+YKGbAqXOu9nwJFFOdL00kFpz42M+4huzYi86vAK1sOOfyOG45muIQ=="], "@testing-library/react": ["@testing-library/react@14.3.1", "", { "dependencies": { "@babel/runtime": "^7.12.5", "@testing-library/dom": "^9.0.0", "@types/react-dom": "^18.0.0" }, "peerDependencies": { "react": "^18.0.0", "react-dom": "^18.0.0" } }, "sha512-H99XjUhWQw0lTgyMN05W3xQG1Nh4lq574D8keFf1dDoNTJgp66VbJozRaczoF+wsiaPJNt/TcnfpLGufGxSrZQ=="], @@ -1434,8 +1251,6 @@ "@types/prop-types": ["@types/prop-types@15.7.15", "", {}, "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw=="], - "@types/qrcode": ["@types/qrcode@1.5.5", "", { "dependencies": { "@types/node": "*" } }, "sha512-CdfBi/e3Qk+3Z/fXYShipBT13OJ2fDO2Q2w5CIP5anLTLIndQG9z6P1cnm+8zCWSpm5dnxMFd/uREtb0EXuQzg=="], - "@types/react": ["@types/react@18.3.26", "", { "dependencies": { "@types/prop-types": "*", "csstype": "^3.0.2" } }, "sha512-RFA/bURkcKzx/X9oumPG9Vp3D3JUgus/d0b67KB0t5S/raciymilkOa66olh78MUI92QLbEJevO7rvqU/kjwKA=="], "@types/react-dom": ["@types/react-dom@18.3.7", "", { "peerDependencies": { "@types/react": "^18.0.0" } }, "sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ=="], @@ -1554,8 +1369,6 @@ "aggregate-error": ["aggregate-error@3.1.0", "", { "dependencies": { "clean-stack": "^2.0.0", "indent-string": "^4.0.0" } }, "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA=="], - "agora-app": ["agora-app@workspace:projects/Voces"], - "ajv": ["ajv@6.12.6", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g=="], "algoliasearch": ["algoliasearch@5.40.0", "", { "dependencies": { "@algolia/abtesting": "1.6.0", "@algolia/client-abtesting": "5.40.0", "@algolia/client-analytics": "5.40.0", "@algolia/client-common": "5.40.0", "@algolia/client-insights": "5.40.0", "@algolia/client-personalization": "5.40.0", "@algolia/client-query-suggestions": "5.40.0", "@algolia/client-search": "5.40.0", "@algolia/ingestion": "1.40.0", "@algolia/monitoring": "1.40.0", "@algolia/recommend": "5.40.0", "@algolia/requester-browser-xhr": "5.40.0", "@algolia/requester-fetch": "5.40.0", "@algolia/requester-node-http": "5.40.0" } }, "sha512-a9aIL2E3Z7uYUPMCmjMFFd5MWhn+ccTubEvnMy7rOTZCB62dXBJtz0R5BZ/TPuX3R9ocBsgWuAbGWQ+Ph4Fmlg=="], @@ -1598,8 +1411,6 @@ "async-limiter": ["async-limiter@1.0.1", "", {}, "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ=="], - "autoprefixer": ["autoprefixer@10.4.21", "", { "dependencies": { "browserslist": "^4.24.4", "caniuse-lite": "^1.0.30001702", "fraction.js": "^4.3.7", "normalize-range": "^0.1.2", "picocolors": "^1.1.1", "postcss-value-parser": "^4.2.0" }, "peerDependencies": { "postcss": "^8.1.0" }, "bin": { "autoprefixer": "bin/autoprefixer" } }, "sha512-O+A6LWV5LDHSJD3LjHYoNi4VLsj/Whi7k6zG12xTYaU4cQ8oxQGckXNX8cRHK5yOZ/ppVHe0ZBXGzSV9jXdVbQ=="], - "available-typed-arrays": ["available-typed-arrays@1.0.7", "", { "dependencies": { "possible-typed-array-names": "^1.0.0" } }, "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ=="], "await-lock": ["await-lock@2.2.2", "", {}, "sha512-aDczADvlvTGajTDjcjpJMqRkOF6Qdz3YbPZm/PyW6tKPkx2hlYBzxMhEywM/tU72HrVZjgl5VCdRuMlA7pZ8Gw=="], @@ -1614,8 +1425,6 @@ "babel-plugin-jest-hoist": ["babel-plugin-jest-hoist@29.6.3", "", { "dependencies": { "@babel/template": "^7.3.3", "@babel/types": "^7.3.3", "@types/babel__core": "^7.1.14", "@types/babel__traverse": "^7.0.6" } }, "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg=="], - "babel-plugin-jsx-dom-expressions": ["babel-plugin-jsx-dom-expressions@0.40.1", "", { "dependencies": { "@babel/helper-module-imports": "7.18.6", "@babel/plugin-syntax-jsx": "^7.18.6", "@babel/types": "^7.20.7", "html-entities": "2.3.3", "parse5": "^7.1.2", "validate-html-nesting": "^1.2.1" }, "peerDependencies": { "@babel/core": "^7.20.12" } }, "sha512-b4iHuirqK7RgaMzB2Lsl7MqrlDgQtVRSSazyrmx7wB3T759ggGjod5Rkok5MfHjQXhR7tRPmdwoeGPqBnW2KfA=="], - "babel-plugin-polyfill-corejs2": ["babel-plugin-polyfill-corejs2@0.4.14", "", { "dependencies": { "@babel/compat-data": "^7.27.7", "@babel/helper-define-polyfill-provider": "^0.6.5", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, "sha512-Co2Y9wX854ts6U8gAAPXfn0GmAyctHuK8n0Yhfjd6t30g7yvKjspvvOo9yG+z52PZRgFErt7Ka2pYnXCjLKEpg=="], "babel-plugin-polyfill-corejs3": ["babel-plugin-polyfill-corejs3@0.13.0", "", { "dependencies": { "@babel/helper-define-polyfill-provider": "^0.6.5", "core-js-compat": "^3.43.0" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, "sha512-U+GNwMdSFgzVmfhNm8GJUX88AadB3uo9KpJqS3FaqNIPKgySuvMb+bHPsOmmuWyIcuqZj/pzt1RUIUZns4y2+A=="], @@ -1634,8 +1443,6 @@ "babel-preset-jest": ["babel-preset-jest@29.6.3", "", { "dependencies": { "babel-plugin-jest-hoist": "^29.6.3", "babel-preset-current-node-syntax": "^1.0.0" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA=="], - "babel-preset-solid": ["babel-preset-solid@1.9.9", "", { "dependencies": { "babel-plugin-jsx-dom-expressions": "^0.40.1" }, "peerDependencies": { "@babel/core": "^7.0.0", "solid-js": "^1.9.8" }, "optionalPeers": ["solid-js"] }, "sha512-pCnxWrciluXCeli/dj5PIEHgbNzim3evtTn12snjqqg8QZWJNMjH1AWIp4iG/tbVjqQ72aBEymMSagvmgxubXw=="], - "balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], "base64-js": ["base64-js@1.5.1", "", {}, "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="], @@ -1658,8 +1465,6 @@ "bl": ["bl@4.1.0", "", { "dependencies": { "buffer": "^5.5.0", "inherits": "^2.0.4", "readable-stream": "^3.4.0" } }, "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w=="], - "blossom-client-sdk": ["blossom-client-sdk@4.1.0", "", { "dependencies": { "@cashu/cashu-ts": "^2.4.3", "@noble/hashes": "^1.8.0" } }, "sha512-IEjX3/e6EYnEonlog8qbd1/7qYIatOKEAQMWGkPCPjTO/b9fsrSnoELwOam52a5U3M83XLvYFhf6qE9MmlmJuQ=="], - "bplist-creator": ["bplist-creator@0.1.0", "", { "dependencies": { "stream-buffers": "2.2.x" } }, "sha512-sXaHZicyEEmY86WyueLTQesbeoH/mquvarJaQNbjuOQO+7gbFcDEWqKmcWA4cOTLzFlfgvkiVxolk1k5bBIpmg=="], "bplist-parser": ["bplist-parser@0.3.2", "", { "dependencies": { "big-integer": "1.6.x" } }, "sha512-apC2+fspHGI3mMKj+dGevkGo/tCqVB8jMb6i+OX+E29p0Iposz07fABkRIfVUPNd5A5VbuOz1bZbnmkKLYF+wQ=="], @@ -1730,8 +1535,6 @@ "clean-stack": ["clean-stack@2.2.0", "", {}, "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A=="], - "cli-color": ["cli-color@2.0.4", "", { "dependencies": { "d": "^1.0.1", "es5-ext": "^0.10.64", "es6-iterator": "^2.0.3", "memoizee": "^0.4.15", "timers-ext": "^0.1.7" } }, "sha512-zlnpg0jNcibNrO7GG9IeHH7maWFeCz+Ja1wx/7tZNU5ASSSSZ+/qZciM0/LHCYxSdqv5h2sdbQ/PXYdOuetXvA=="], - "cli-cursor": ["cli-cursor@2.1.0", "", { "dependencies": { "restore-cursor": "^2.0.0" } }, "sha512-8lgKz8LmCRYZZQDpRyT2m5rKJ08TnU4tR9FFFW2rxpxR1FzWi4PQ/NfyODchAatHaUgnSPVcx/R5w6NuTBzFiw=="], "cli-spinners": ["cli-spinners@2.9.2", "", {}, "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg=="], @@ -1880,8 +1683,6 @@ "data-view-byte-offset": ["data-view-byte-offset@1.0.1", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "is-data-view": "^1.0.1" } }, "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ=="], - "date-fns": ["date-fns@4.1.0", "", {}, "sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg=="], - "dayjs": ["dayjs@1.11.18", "", {}, "sha512-zFBQ7WFRvVRhKcWoUh+ZA1g2HVgUbsZm9sbddh8EC5iv93sui8DVVz1Npvz+r6meo9VKfa8NyLWBsQK1VvIKPA=="], "debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], @@ -1968,8 +1769,6 @@ "end-of-stream": ["end-of-stream@1.4.5", "", { "dependencies": { "once": "^1.4.0" } }, "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg=="], - "enhanced-resolve": ["enhanced-resolve@5.18.3", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.2.0" } }, "sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww=="], - "entities": ["entities@4.5.0", "", {}, "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="], "env-editor": ["env-editor@0.4.2", "", {}, "sha512-ObFo8v4rQJAE59M69QzwloxPZtd33TpYEIjtKD1rrFDcM1Gd7IkDxEBU+HriziN6HSHQnBJi8Dmy+JWkav5HKA=="], @@ -2000,8 +1799,6 @@ "es6-symbol": ["es6-symbol@3.1.4", "", { "dependencies": { "d": "^1.0.2", "ext": "^1.7.0" } }, "sha512-U9bFFjX8tFiATgtkJ1zg25+KviIXpgRvRHS8sau3GfhVzThRQrOeksPeT0BWW2MNZs1OEWJ1DPXOQMn0KKRkvg=="], - "es6-weak-map": ["es6-weak-map@2.0.3", "", { "dependencies": { "d": "1", "es5-ext": "^0.10.46", "es6-iterator": "^2.0.3", "es6-symbol": "^3.1.1" } }, "sha512-p5um32HOTO1kP+w7PRnB+5lQ43Z6muuMuIMffvDN8ZB4GcnjLBV6zGStpbASIMk4DCAvEaamhe2zhyCb/QXXsA=="], - "esbuild": ["esbuild@0.25.10", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.10", "@esbuild/android-arm": "0.25.10", "@esbuild/android-arm64": "0.25.10", "@esbuild/android-x64": "0.25.10", "@esbuild/darwin-arm64": "0.25.10", "@esbuild/darwin-x64": "0.25.10", "@esbuild/freebsd-arm64": "0.25.10", "@esbuild/freebsd-x64": "0.25.10", "@esbuild/linux-arm": "0.25.10", "@esbuild/linux-arm64": "0.25.10", "@esbuild/linux-ia32": "0.25.10", "@esbuild/linux-loong64": "0.25.10", "@esbuild/linux-mips64el": "0.25.10", "@esbuild/linux-ppc64": "0.25.10", "@esbuild/linux-riscv64": "0.25.10", "@esbuild/linux-s390x": "0.25.10", "@esbuild/linux-x64": "0.25.10", "@esbuild/netbsd-arm64": "0.25.10", "@esbuild/netbsd-x64": "0.25.10", "@esbuild/openbsd-arm64": "0.25.10", "@esbuild/openbsd-x64": "0.25.10", "@esbuild/openharmony-arm64": "0.25.10", "@esbuild/sunos-x64": "0.25.10", "@esbuild/win32-arm64": "0.25.10", "@esbuild/win32-ia32": "0.25.10", "@esbuild/win32-x64": "0.25.10" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-9RiGKvCwaqxO2owP61uQ4BgNborAQskMR6QusfWzQqv7AZOg5oGehdY2pRJMTKuwxd1IDBP4rSbI5lHzU7SMsQ=="], "esbuild-plugin-alias": ["esbuild-plugin-alias@0.2.1", "", {}, "sha512-jyfL/pwPqaFXyKnj8lP8iLk6Z0m099uXR45aSN8Av1XD4vhvQutxxPzgA2bTcAwQpa1zCXDcWOlhFgyP3GKqhQ=="], @@ -2042,7 +1839,7 @@ "estraverse": ["estraverse@5.3.0", "", {}, "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA=="], - "estree-walker": ["estree-walker@2.0.2", "", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="], + "estree-walker": ["estree-walker@3.0.3", "", { "dependencies": { "@types/estree": "^1.0.0" } }, "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g=="], "esutils": ["esutils@2.0.3", "", {}, "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g=="], @@ -2050,8 +1847,6 @@ "event-emitter": ["event-emitter@0.3.5", "", { "dependencies": { "d": "1", "es5-ext": "~0.10.14" } }, "sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA=="], - "event-graph-example": ["event-graph-example@workspace:svelte/examples/event-graph"], - "event-target-shim": ["event-target-shim@5.0.1", "", {}, "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ=="], "exec-async": ["exec-async@2.2.0", "", {}, "sha512-87OpwcEiMia/DeiKFzaQNBNFeN3XkkpYIh9FyOqq5mS2oKv3CBE67PXoEKcr6nodWdXNogTiQ0jE2NGuoffXPw=="], @@ -2112,8 +1907,6 @@ "fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="], - "feed-viewer-example": ["feed-viewer-example@workspace:svelte/examples/feed-viewer"], - "fflate": ["fflate@0.8.2", "", {}, "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A=="], "file-entry-cache": ["file-entry-cache@8.0.0", "", { "dependencies": { "flat-cache": "^4.0.0" } }, "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ=="], @@ -2146,8 +1939,6 @@ "foreground-child": ["foreground-child@3.3.1", "", { "dependencies": { "cross-spawn": "^7.0.6", "signal-exit": "^4.0.1" } }, "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw=="], - "fraction.js": ["fraction.js@4.3.7", "", {}, "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew=="], - "freeport-async": ["freeport-async@2.0.0", "", {}, "sha512-K7od3Uw45AJg00XUmy15+Hae2hOcgKcmN3/EF6Y7i01O0gaqiRx8sUSpsb9+BRNL8RPBrhzPsVfy8q9ADlJuWQ=="], "fresh": ["fresh@0.5.2", "", {}, "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q=="], @@ -2196,12 +1987,8 @@ "globalthis": ["globalthis@1.0.4", "", { "dependencies": { "define-properties": "^1.2.1", "gopd": "^1.0.1" } }, "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ=="], - "globalyzer": ["globalyzer@0.1.0", "", {}, "sha512-40oNTM9UfG6aBmuKxk/giHn5nQ8RVz/SS4Ir6zgzOv9/qC3kKZ9v4etGTcJbEl/NyVQH7FGU7d+X1egr57Md2Q=="], - "globby": ["globby@11.1.0", "", { "dependencies": { "array-union": "^2.1.0", "dir-glob": "^3.0.1", "fast-glob": "^3.2.9", "ignore": "^5.2.0", "merge2": "^1.4.1", "slash": "^3.0.0" } }, "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g=="], - "globrex": ["globrex@0.1.2", "", {}, "sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg=="], - "gopd": ["gopd@1.2.0", "", {}, "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="], "graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="], @@ -2242,8 +2029,6 @@ "html-encoding-sniffer": ["html-encoding-sniffer@4.0.0", "", { "dependencies": { "whatwg-encoding": "^3.1.1" } }, "sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ=="], - "html-entities": ["html-entities@2.3.3", "", {}, "sha512-DV5Ln36z34NNTDgnz0EWGBLZENelNAtkiFA4kyNOG2tDI6Mz1uSWiq1wAKdyjnJwyDiDO7Fa2SO1CTxPXL8VxA=="], - "html-escaper": ["html-escaper@2.0.2", "", {}, "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg=="], "html-void-elements": ["html-void-elements@3.0.0", "", {}, "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg=="], @@ -2282,8 +2067,6 @@ "internmap": ["internmap@2.0.3", "", {}, "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg=="], - "intl-messageformat": ["intl-messageformat@10.7.18", "", { "dependencies": { "@formatjs/ecma402-abstract": "2.3.6", "@formatjs/fast-memoize": "2.2.7", "@formatjs/icu-messageformat-parser": "2.11.4", "tslib": "^2.8.0" } }, "sha512-m3Ofv/X/tV8Y3tHXLohcuVuhWKo7BBq62cqY15etqmLxg2DZ34AGGgQDeR+SCta2+zICb1NX83af0GJmbQ1++g=="], - "invariant": ["invariant@2.2.4", "", { "dependencies": { "loose-envify": "^1.0.0" } }, "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA=="], "ioredis": ["ioredis@5.8.1", "", { "dependencies": { "@ioredis/commands": "1.4.0", "cluster-key-slot": "^1.1.0", "debug": "^4.3.4", "denque": "^2.1.0", "lodash.defaults": "^4.2.0", "lodash.isarguments": "^3.1.0", "redis-errors": "^1.2.0", "redis-parser": "^3.0.0", "standard-as-callback": "^2.1.0" } }, "sha512-Qho8TgIamqEPdgiMadJwzRMW3TudIg6vpg4YONokGDudy4eqRIJtDbVX72pfLBcWxvbn3qm/40TyGUObdW4tLQ=="], @@ -2350,8 +2133,6 @@ "is-potential-custom-element-name": ["is-potential-custom-element-name@1.0.1", "", {}, "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ=="], - "is-promise": ["is-promise@2.2.2", "", {}, "sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ=="], - "is-reference": ["is-reference@3.0.3", "", { "dependencies": { "@types/estree": "^1.0.6" } }, "sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw=="], "is-regex": ["is-regex@1.2.1", "", { "dependencies": { "call-bound": "^1.0.2", "gopd": "^1.2.0", "has-tostringtag": "^1.0.2", "hasown": "^2.0.2" } }, "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g=="], @@ -2552,10 +2333,6 @@ "lru-cache": ["lru-cache@11.2.2", "", {}, "sha512-F9ODfyqML2coTIsQpSkRHnLSZMtkU8Q+mSfcaIyKwy58u+8k5nvAYeiNhsyMARvzNcXJ9QfWVrcPsC9e9rAxtg=="], - "lru-queue": ["lru-queue@0.1.0", "", { "dependencies": { "es5-ext": "~0.10.2" } }, "sha512-BpdYkt9EvGl8OfWHDQPISVpcl5xZthb+XPsbELj5AQXxIC8IriDZIQYjBJPEm5rS420sjZ0TLEzRcq5KdBhYrQ=="], - - "lucide-svelte": ["lucide-svelte@0.545.0", "", { "peerDependencies": { "svelte": "^3 || ^4 || ^5.0.0-next.42" } }, "sha512-lVza6hIAf1abADTU1ThyNp+M3bQZtzTGQ9EQZ2xo3n9ftkd4QcWbDB2ekP2MqRLTJpeLXX5gaFEgFZtbWCnyqg=="], - "lunr": ["lunr@2.3.9", "", {}, "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow=="], "lz-string": ["lz-string@1.5.0", "", { "bin": { "lz-string": "bin/bin.js" } }, "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ=="], @@ -2592,14 +2369,10 @@ "memoize-one": ["memoize-one@5.2.1", "", {}, "sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q=="], - "memoizee": ["memoizee@0.4.17", "", { "dependencies": { "d": "^1.0.2", "es5-ext": "^0.10.64", "es6-weak-map": "^2.0.3", "event-emitter": "^0.3.5", "is-promise": "^2.2.2", "lru-queue": "^0.1.0", "next-tick": "^1.1.0", "timers-ext": "^0.1.7" } }, "sha512-DGqD7Hjpi/1or4F/aYAspXKNm5Yili0QDAFAY4QYvpqpgiY6+1jOfqpmByzjxbWd/T9mChbCArXAbDAsTm5oXA=="], - "memorystream": ["memorystream@0.3.1", "", {}, "sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw=="], "meow": ["meow@9.0.0", "", { "dependencies": { "@types/minimist": "^1.2.0", "camelcase-keys": "^6.2.2", "decamelize": "^1.2.0", "decamelize-keys": "^1.1.0", "hard-rejection": "^2.1.0", "minimist-options": "4.1.0", "normalize-package-data": "^3.0.0", "read-pkg-up": "^7.0.1", "redent": "^3.0.0", "trim-newlines": "^3.0.0", "type-fest": "^0.18.0", "yargs-parser": "^20.2.3" } }, "sha512-+obSblOQmRhcyBt62furQqRAQpNyWXo8BuQ5bN7dG8wmwQ+vwHKp/rCFD4CrTP8CsDQD1sjoZ94K417XEUk8IQ=="], - "merge-anything": ["merge-anything@5.1.7", "", { "dependencies": { "is-what": "^4.1.8" } }, "sha512-eRtbOb1N5iyH0tkQDAoQ4Ipsp/5qSR79Dzrz8hEPxRX10RWWR/iQXdoKmBSRCThY1Fh5EhISDtpSc93fpxUniQ=="], - "merge-stream": ["merge-stream@2.0.0", "", {}, "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w=="], "merge2": ["merge2@1.4.1", "", {}, "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg=="], @@ -2692,12 +2465,6 @@ "natural-compare": ["natural-compare@1.4.0", "", {}, "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw=="], - "ndk-svelte5-component-browser": ["ndk-svelte5-component-browser@workspace:svelte/examples/component-browser"], - - "ndk-svelte5-nutsack-wallet": ["ndk-svelte5-nutsack-wallet@workspace:svelte/examples/nutsack"], - - "ndk-svelte5-sessions-demo": ["ndk-svelte5-sessions-demo@workspace:svelte/examples/sessions-demo"], - "negotiator": ["negotiator@0.6.3", "", {}, "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg=="], "neo-async": ["neo-async@2.6.2", "", {}, "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw=="], @@ -2728,8 +2495,6 @@ "normalize-path": ["normalize-path@3.0.0", "", {}, "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA=="], - "normalize-range": ["normalize-range@0.1.2", "", {}, "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA=="], - "nostr-editor": ["nostr-editor@1.0.1", "", { "dependencies": { "js-base64": "^3.7.7", "light-bolt11-decoder": "^3.1.1" }, "peerDependencies": { "@tiptap/core": "^2.6.6", "@tiptap/extension-image": "^2.6.6", "@tiptap/extension-link": "^2.6.6", "@tiptap/pm": "^2.6.6", "linkifyjs": "^4.1.3", "nostr-tools": "~2.14.2", "prosemirror-markdown": "^1.13.0", "prosemirror-model": "^1.22.3", "prosemirror-state": "^1.4.3", "prosemirror-view": "^1.39.3", "tiptap-markdown": "^0.8.10" } }, "sha512-HXqXjxtIN0CcC7sLV5xYjEsQF0bFYLmNKxS75ya2yZGQ/z16U+uK6bb2Hd72QyqXlHXyWN0m24E5Gcws8/NhRQ=="], "nostr-tools": ["nostr-tools@2.17.0", "", { "dependencies": { "@noble/ciphers": "^0.5.1", "@noble/curves": "1.2.0", "@noble/hashes": "1.3.1", "@scure/base": "1.1.1", "@scure/bip32": "1.3.1", "@scure/bip39": "1.2.1", "nostr-wasm": "0.1.0" }, "peerDependencies": { "typescript": ">=5.0.0" }, "optionalPeers": ["typescript"] }, "sha512-lrvHM7cSaGhz7F0YuBvgHMoU2s8/KuThihDoOYk8w5gpVHTy0DeUCAgCN8uLGeuSl5MAWekJr9Dkfo5HClqO9w=="], @@ -2864,8 +2629,6 @@ "postcss-selector-parser": ["postcss-selector-parser@7.1.0", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA=="], - "postcss-value-parser": ["postcss-value-parser@4.2.0", "", {}, "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ=="], - "preact": ["preact@10.27.2", "", {}, "sha512-5SYSgFKSyhCbk6SrXyMpqjb5+MQBgfvEKE/OC+PujcY34sOpqtr+0AZQtPYx5IA6VxynQ7rUPCtKzyovpj9Bpg=="], "prebuild-install": ["prebuild-install@7.1.3", "", { "dependencies": { "detect-libc": "^2.0.0", "expand-template": "^2.0.3", "github-from-package": "0.0.0", "minimist": "^1.2.3", "mkdirp-classic": "^0.5.3", "napi-build-utils": "^2.0.0", "node-abi": "^3.3.0", "pump": "^3.0.0", "rc": "^1.2.7", "simple-get": "^4.0.0", "tar-fs": "^2.0.0", "tunnel-agent": "^0.6.0" }, "bin": { "prebuild-install": "bin.js" } }, "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug=="], @@ -3080,10 +2843,6 @@ "serialize-error": ["serialize-error@2.1.0", "", {}, "sha512-ghgmKt5o4Tly5yEG/UJp8qTd0AN7Xalw4XBtDEKP655B699qMEtra1WlXeE6WIvdEG481JvRxULKsInq/iNysw=="], - "seroval": ["seroval@1.3.2", "", {}, "sha512-RbcPH1n5cfwKrru7v7+zrZvjLurgHhGyso3HTyGtRivGWgYjbOmGuivCQaORNELjNONoK35nj28EoWul9sb1zQ=="], - - "seroval-plugins": ["seroval-plugins@1.3.3", "", { "peerDependencies": { "seroval": "^1.0" } }, "sha512-16OL3NnUBw8JG1jBLUoZJsLnQq0n5Ua6aHalhJK4fMQkz1lqR7Osz1sA30trBtd9VUDc2NgkuRCn8+/pBwqZ+w=="], - "serve-static": ["serve-static@1.16.2", "", { "dependencies": { "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "parseurl": "~1.3.3", "send": "0.19.0" } }, "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw=="], "set-blocking": ["set-blocking@2.0.0", "", {}, "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw=="], @@ -3098,8 +2857,6 @@ "setprototypeof": ["setprototypeof@1.2.0", "", {}, "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="], - "shakespeare": ["shakespeare@0.0.4", "", {}, "sha512-2W4qfEmCChz7cMHMMQvt9ggMzmYRQ0skGOe8qcGr57wmleVIjq8s2XmKue0YAapvQs1351qH7uCHNBijQ0BAMQ=="], - "shallow-clone": ["shallow-clone@3.0.1", "", { "dependencies": { "kind-of": "^6.0.2" } }, "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA=="], "shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="], @@ -3136,12 +2893,6 @@ "slugify": ["slugify@1.6.6", "", {}, "sha512-h+z7HKHYXj6wJU+AnS/+IH8Uh9fdcX1Lrhg1/VMdf9PwoBQXFcXiAdsy2tSK0P6gKwJLXp02r90ahUCqHk9rrw=="], - "solid-js": ["solid-js@1.9.9", "", { "dependencies": { "csstype": "^3.1.0", "seroval": "~1.3.0", "seroval-plugins": "~1.3.0" } }, "sha512-A0ZBPJQldAeGCTW0YRYJmt7RCeh5rbFfPZ2aOttgYnctHE7HgKeHCBB/PVc2P7eOfmNXqMFFFoYYdm3S4dcbkA=="], - - "solid-refresh": ["solid-refresh@0.6.3", "", { "dependencies": { "@babel/generator": "^7.23.6", "@babel/helper-module-imports": "^7.22.15", "@babel/types": "^7.23.6" }, "peerDependencies": { "solid-js": "^1.3" } }, "sha512-F3aPsX6hVw9ttm5LYlth8Q15x6MlI/J3Dn+o3EQyRTtTxidepSTwAYdozt01/YA+7ObcciagGEyXIopGZzQtbA=="], - - "solidjs": ["solidjs@workspace:projects/solidjs"], - "source-map": ["source-map@0.8.0-beta.0", "", { "dependencies": { "whatwg-url": "^7.0.0" } }, "sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA=="], "source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="], @@ -3236,20 +2987,12 @@ "svelte-eslint-parser": ["svelte-eslint-parser@1.3.3", "", { "dependencies": { "eslint-scope": "^8.2.0", "eslint-visitor-keys": "^4.0.0", "espree": "^10.0.0", "postcss": "^8.4.49", "postcss-scss": "^4.0.9", "postcss-selector-parser": "^7.0.0" }, "peerDependencies": { "svelte": "^3.37.0 || ^4.0.0 || ^5.0.0" }, "optionalPeers": ["svelte"] }, "sha512-oTrDR8Z7Wnguut7QH3YKh7JR19xv1seB/bz4dxU5J/86eJtZOU4eh0/jZq4dy6tAlz/KROxnkRQspv5ZEt7t+Q=="], - "svelte-i18n": ["svelte-i18n@4.0.1", "", { "dependencies": { "cli-color": "^2.0.3", "deepmerge": "^4.2.2", "esbuild": "^0.19.2", "estree-walker": "^2", "intl-messageformat": "^10.5.3", "sade": "^1.8.1", "tiny-glob": "^0.2.9" }, "peerDependencies": { "svelte": "^3 || ^4 || ^5" }, "bin": { "svelte-i18n": "dist/cli.js" } }, "sha512-jaykGlGT5PUaaq04JWbJREvivlCnALtT+m87Kbm0fxyYHynkQaxQMnIKHLm2WeIuBRoljzwgyvz0Z6/CMwfdmQ=="], - "svelte2tsx": ["svelte2tsx@0.7.45", "", { "dependencies": { "dedent-js": "^1.0.1", "scule": "^1.3.0" }, "peerDependencies": { "svelte": "^3.55 || ^4.0.0-next.0 || ^4.0 || ^5.0.0-next.0", "typescript": "^4.9.4 || ^5.0.0" } }, "sha512-cSci+mYGygYBHIZLHlm/jYlEc1acjAHqaQaDFHdEBpUueM9kSTnPpvPtSl5VkJOU1qSJ7h1K+6F/LIUYiqC8VA=="], "symbol-tree": ["symbol-tree@3.2.4", "", {}, "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw=="], "tabbable": ["tabbable@6.2.0", "", {}, "sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew=="], - "tailwind-merge": ["tailwind-merge@3.3.1", "", {}, "sha512-gBXpgUm/3rp1lMZZrM/w7D8GKqshif0zAymAhbCyIt8KMe+0v9DQ7cdYLR4FHH/cKpdTXb+A/tKKU3eolfsI+g=="], - - "tailwindcss": ["tailwindcss@4.1.14", "", {}, "sha512-b7pCxjGO98LnxVkKjaZSDeNuljC4ueKUddjENJOADtubtdo8llTaJy7HwBMeLNSSo2N5QIAgklslK1+Ir8r6CA=="], - - "tapable": ["tapable@2.3.0", "", {}, "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg=="], - "tar": ["tar@7.5.1", "", { "dependencies": { "@isaacs/fs-minipass": "^4.0.0", "chownr": "^3.0.0", "minipass": "^7.1.2", "minizlib": "^3.1.0", "yallist": "^5.0.0" } }, "sha512-nlGpxf+hv0v7GkWBK2V9spgactGOp0qvfWRxUMjqHyzrt3SgwE48DIv/FhqPHJYLHpgW1opq3nERbz5Anq7n1g=="], "tar-fs": ["tar-fs@2.1.4", "", { "dependencies": { "chownr": "^1.1.1", "mkdirp-classic": "^0.5.2", "pump": "^3.0.0", "tar-stream": "^2.1.4" } }, "sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ=="], @@ -3272,10 +3015,6 @@ "throat": ["throat@5.0.0", "", {}, "sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA=="], - "timers-ext": ["timers-ext@0.1.8", "", { "dependencies": { "es5-ext": "^0.10.64", "next-tick": "^1.1.0" } }, "sha512-wFH7+SEAcKfJpfLPkrgMPvvwnEtj8W4IurvEyrKsDleXnKLCDw71w8jltvfLa8Rm4qQxxT4jmDBYbJG/z7qoww=="], - - "tiny-glob": ["tiny-glob@0.2.9", "", { "dependencies": { "globalyzer": "0.1.0", "globrex": "^0.1.2" } }, "sha512-g/55ssRPUjShh+xkfx9UPDXqhckHEsHr4Vd9zX55oSdGZc/MD0m3sferOkwWtp98bv+kcVfEHtRJgBVJzelrzg=="], - "tinybench": ["tinybench@2.9.0", "", {}, "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg=="], "tinyexec": ["tinyexec@0.3.2", "", {}, "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA=="], @@ -3426,8 +3165,6 @@ "v8-compile-cache-lib": ["v8-compile-cache-lib@3.0.1", "", {}, "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg=="], - "validate-html-nesting": ["validate-html-nesting@1.2.3", "", {}, "sha512-kdkWdCl6eCeLlRShJKbjVOU2kFKxMF8Ghu50n+crEoyx+VKm3FxAxF9z4DCy6+bbTOqNW0+jcIYRnjoIRzigRw=="], - "validate-npm-package-license": ["validate-npm-package-license@3.0.4", "", { "dependencies": { "spdx-correct": "^3.0.0", "spdx-expression-parse": "^3.0.0" } }, "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew=="], "validate-npm-package-name": ["validate-npm-package-name@5.0.1", "", {}, "sha512-OljLrQ9SQdOUqTaQxqL5dEfZWrXExyyWsozYlAWFawPVNuD83igl7uJD2RTkNMbniIYgt8l81eCJGIdQF7avLQ=="], @@ -3442,8 +3179,6 @@ "vite-node": ["vite-node@1.6.1", "", { "dependencies": { "cac": "^6.7.14", "debug": "^4.3.4", "pathe": "^1.1.1", "picocolors": "^1.0.0", "vite": "^5.0.0" }, "bin": { "vite-node": "vite-node.mjs" } }, "sha512-YAXkfvGtuTzwWbDSACdJSg4A4DZiAqckWe90Zapc/sEX3XvHcw1NdurM/6od8J207tSDqNbSsgdCacBgvJKFuA=="], - "vite-plugin-solid": ["vite-plugin-solid@2.11.9", "", { "dependencies": { "@babel/core": "^7.23.3", "@types/babel__core": "^7.20.4", "babel-preset-solid": "^1.8.4", "merge-anything": "^5.1.7", "solid-refresh": "^0.6.3", "vitefu": "^1.0.4" }, "peerDependencies": { "@testing-library/jest-dom": "^5.16.6 || ^5.17.0 || ^6.*", "solid-js": "^1.7.2", "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" }, "optionalPeers": ["@testing-library/jest-dom"] }, "sha512-bTA6p+bspXZsuulSd2y6aTzegF8xGaJYcq1Uyh/mv+W4DQtzCgL9nN6n2fsTaxp/dMk+ZHHKgGndlNeooqHLKw=="], - "vitefu": ["vitefu@1.1.1", "", { "peerDependencies": { "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0-beta.0" }, "optionalPeers": ["vite"] }, "sha512-B/Fegf3i8zh0yFbpzZ21amWzHmuNlLlmJT6n7bu5e+pCHUKQIfXSYokrqOBGEMMe9UG2sostKQF9mml/vYaWJQ=="], "vitepress": ["vitepress@1.6.4", "", { "dependencies": { "@docsearch/css": "3.8.2", "@docsearch/js": "3.8.2", "@iconify-json/simple-icons": "^1.2.21", "@shikijs/core": "^2.1.0", "@shikijs/transformers": "^2.1.0", "@shikijs/types": "^2.1.0", "@types/markdown-it": "^14.1.2", "@vitejs/plugin-vue": "^5.2.1", "@vue/devtools-api": "^7.7.0", "@vue/shared": "^3.5.13", "@vueuse/core": "^12.4.0", "@vueuse/integrations": "^12.4.0", "focus-trap": "^7.6.4", "mark.js": "8.11.1", "minisearch": "^7.1.1", "shiki": "^2.1.0", "vite": "^5.4.14", "vue": "^3.5.13" }, "peerDependencies": { "markdown-it-mathjax3": "^4", "postcss": "^8" }, "optionalPeers": ["markdown-it-mathjax3", "postcss"], "bin": { "vitepress": "bin/vitepress.js" } }, "sha512-+2ym1/+0VVrbhNyRoFFesVvBvHAVMZMK0rw60E3X/5349M1GuVdKeazuksqopEdvkKwKGs21Q729jX81/bkBJg=="], @@ -3710,30 +3445,12 @@ "@sinonjs/commons/type-detect": ["type-detect@4.0.8", "", {}, "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g=="], - "@solidjs/testing-library/@testing-library/dom": ["@testing-library/dom@10.4.1", "", { "dependencies": { "@babel/code-frame": "^7.10.4", "@babel/runtime": "^7.12.5", "@types/aria-query": "^5.0.1", "aria-query": "5.3.0", "dom-accessibility-api": "^0.5.9", "lz-string": "^1.5.0", "picocolors": "1.1.1", "pretty-format": "^27.0.2" } }, "sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg=="], - "@sveltejs/kit/vite": ["vite@7.1.9", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.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", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-4nVGliEpxmhCL8DslSAUdxlB6+SMrhB0a1v5ijlh1xB1nEPuy1mxaHxysVucLHuWryAxLWg6a5ei+U4TLn/rFg=="], "@sveltejs/vite-plugin-svelte/vite": ["vite@7.1.9", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.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", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-4nVGliEpxmhCL8DslSAUdxlB6+SMrhB0a1v5ijlh1xB1nEPuy1mxaHxysVucLHuWryAxLWg6a5ei+U4TLn/rFg=="], "@sveltejs/vite-plugin-svelte-inspector/vite": ["vite@7.1.9", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.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", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-4nVGliEpxmhCL8DslSAUdxlB6+SMrhB0a1v5ijlh1xB1nEPuy1mxaHxysVucLHuWryAxLWg6a5ei+U4TLn/rFg=="], - "@tailwindcss/node/lightningcss": ["lightningcss@1.30.1", "", { "dependencies": { "detect-libc": "^2.0.3" }, "optionalDependencies": { "lightningcss-darwin-arm64": "1.30.1", "lightningcss-darwin-x64": "1.30.1", "lightningcss-freebsd-x64": "1.30.1", "lightningcss-linux-arm-gnueabihf": "1.30.1", "lightningcss-linux-arm64-gnu": "1.30.1", "lightningcss-linux-arm64-musl": "1.30.1", "lightningcss-linux-x64-gnu": "1.30.1", "lightningcss-linux-x64-musl": "1.30.1", "lightningcss-win32-arm64-msvc": "1.30.1", "lightningcss-win32-x64-msvc": "1.30.1" } }, "sha512-xi6IyHML+c9+Q3W0S4fCQJOym42pyurFiJUHEcEyHS0CeKzia4yZDEsLlqOFykxOdHpNy0NmvVO31vcSqAxJCg=="], - - "@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.5.0", "", { "dependencies": { "@emnapi/wasi-threads": "1.1.0", "tslib": "^2.4.0" }, "bundled": true }, "sha512-sbP8GzB1WDzacS8fgNPpHlp6C9VZe+SJP3F90W9rLemaQj2PzIuTEl1qDOYQf58YIpyjViI24y9aPWCjEzY2cg=="], - - "@tailwindcss/oxide-wasm32-wasi/@emnapi/runtime": ["@emnapi/runtime@1.5.0", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-97/BJ3iXHww3djw6hYIfErCZFee7qCtrneuLa20UXFCOTCfBM2cvQHjWJ2EG0s0MtdNwInarqCTz35i4wWXHsQ=="], - - "@tailwindcss/oxide-wasm32-wasi/@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.1.0", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ=="], - - "@tailwindcss/oxide-wasm32-wasi/@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.0.6", "", { "dependencies": { "@emnapi/core": "^1.5.0", "@emnapi/runtime": "^1.5.0", "@tybys/wasm-util": "^0.10.1" }, "bundled": true }, "sha512-DXj75ewm11LIWUk198QSKUTxjyRjsBwk09MuMk5DGK+GDUtyPhhEHOGP/Xwwj3DjQXXkivoBirmOnKrLfc0+9g=="], - - "@tailwindcss/oxide-wasm32-wasi/@tybys/wasm-util": ["@tybys/wasm-util@0.10.1", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg=="], - - "@tailwindcss/oxide-wasm32-wasi/tslib": ["tslib@2.8.1", "", { "bundled": true }, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], - - "@tailwindcss/typography/postcss-selector-parser": ["postcss-selector-parser@6.0.10", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w=="], - "@testing-library/dom/aria-query": ["aria-query@5.1.3", "", { "dependencies": { "deep-equal": "^2.0.5" } }, "sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ=="], "@testing-library/dom/pretty-format": ["pretty-format@27.5.1", "", { "dependencies": { "ansi-regex": "^5.0.1", "ansi-styles": "^5.0.0", "react-is": "^17.0.1" } }, "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ=="], @@ -3742,8 +3459,6 @@ "@types/graceful-fs/@types/node": ["@types/node@24.7.1", "", { "dependencies": { "undici-types": "~7.14.0" } }, "sha512-CmyhGZanP88uuC5GpWU9q+fI61j2SkhO3UGMUdfYRE6Bcy0ccyzn1Rqj9YAB/ZY4kOXmNf0ocah5GtphmLMP6Q=="], - "@types/qrcode/@types/node": ["@types/node@24.7.1", "", { "dependencies": { "undici-types": "~7.14.0" } }, "sha512-CmyhGZanP88uuC5GpWU9q+fI61j2SkhO3UGMUdfYRE6Bcy0ccyzn1Rqj9YAB/ZY4kOXmNf0ocah5GtphmLMP6Q=="], - "@types/sql.js/@types/node": ["@types/node@24.7.1", "", { "dependencies": { "undici-types": "~7.14.0" } }, "sha512-CmyhGZanP88uuC5GpWU9q+fI61j2SkhO3UGMUdfYRE6Bcy0ccyzn1Rqj9YAB/ZY4kOXmNf0ocah5GtphmLMP6Q=="], "@typescript-eslint/eslint-plugin/ignore": ["ignore@7.0.5", "", {}, "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg=="], @@ -3760,27 +3475,15 @@ "@vitest/mocker/@vitest/spy": ["@vitest/spy@3.2.4", "", { "dependencies": { "tinyspy": "^4.0.3" } }, "sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw=="], - "@vitest/mocker/estree-walker": ["estree-walker@3.0.3", "", { "dependencies": { "@types/estree": "^1.0.0" } }, "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g=="], - "@vitest/ui/@vitest/utils": ["@vitest/utils@3.2.4", "", { "dependencies": { "@vitest/pretty-format": "3.2.4", "loupe": "^3.1.4", "tinyrainbow": "^2.0.0" } }, "sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA=="], "@vitest/ui/pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="], "@vitest/ui/vitest": ["vitest@3.2.4", "", { "dependencies": { "@types/chai": "^5.2.2", "@vitest/expect": "3.2.4", "@vitest/mocker": "3.2.4", "@vitest/pretty-format": "^3.2.4", "@vitest/runner": "3.2.4", "@vitest/snapshot": "3.2.4", "@vitest/spy": "3.2.4", "@vitest/utils": "3.2.4", "chai": "^5.2.0", "debug": "^4.4.1", "expect-type": "^1.2.1", "magic-string": "^0.30.17", "pathe": "^2.0.3", "picomatch": "^4.0.2", "std-env": "^3.9.0", "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", "why-is-node-running": "^2.3.0" }, "peerDependencies": { "@edge-runtime/vm": "*", "@types/debug": "^4.1.12", "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "@vitest/browser": "3.2.4", "@vitest/ui": "3.2.4", "happy-dom": "*", "jsdom": "*" }, "optionalPeers": ["@edge-runtime/vm", "@types/debug", "@types/node", "@vitest/browser", "@vitest/ui", "happy-dom", "jsdom"], "bin": { "vitest": "vitest.mjs" } }, "sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A=="], - "@vitest/utils/estree-walker": ["estree-walker@3.0.3", "", { "dependencies": { "@types/estree": "^1.0.0" } }, "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g=="], - - "agora-app/@cashu/cashu-ts": ["@cashu/cashu-ts@2.7.2", "", { "dependencies": { "@noble/curves": "^1.9.5", "@noble/hashes": "^1.5.0", "@scure/bip32": "^1.5.0" } }, "sha512-AOwL6+10AA0syRf2mtRYgoTvAz31gE4MnElj9TLS9xG3k8Msxy27awBlCX5MsBpBgcZZoqWOReYEuUgnbN3IQQ=="], - - "agora-app/@sveltejs/adapter-auto": ["@sveltejs/adapter-auto@6.1.1", "", { "peerDependencies": { "@sveltejs/kit": "^2.0.0" } }, "sha512-cBNt4jgH4KuaNO5gRSB2CZKkGtz+OCZ8lPjRQGjhvVUD4akotnj2weUia6imLl2v07K3IgsQRyM36909miSwoQ=="], - - "agora-app/@sveltejs/vite-plugin-svelte": ["@sveltejs/vite-plugin-svelte@4.0.4", "", { "dependencies": { "@sveltejs/vite-plugin-svelte-inspector": "^3.0.0-next.0||^3.0.0", "debug": "^4.3.7", "deepmerge": "^4.3.1", "kleur": "^4.1.5", "magic-string": "^0.30.12", "vitefu": "^1.0.3" }, "peerDependencies": { "svelte": "^5.0.0-next.96 || ^5.0.0", "vite": "^5.0.0" } }, "sha512-0ba1RQ/PHen5FGpdSrW7Y3fAMQjrXantECALeOiOdBdzR5+5vPP6HVZRLmZaQL+W8m++o+haIAKq5qT+MiZ7VA=="], + "@vue/compiler-core/estree-walker": ["estree-walker@2.0.2", "", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="], - "agora-app/@types/node": ["@types/node@22.18.9", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-5yBtK0k/q8PjkMXbTfeIEP/XVYnz1R9qZJ3yUicdEW7ppdDJfe+MqXEhpqDL3mtn4Wvs1u0KLEG0RXzCgNpsSg=="], - - "agora-app/typescript": ["typescript@5.8.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ=="], - - "agora-app/vite": ["vite@7.1.9", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.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", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-4nVGliEpxmhCL8DslSAUdxlB6+SMrhB0a1v5ijlh1xB1nEPuy1mxaHxysVucLHuWryAxLWg6a5ei+U4TLn/rFg=="], + "@vue/compiler-sfc/estree-walker": ["estree-walker@2.0.2", "", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="], "ansi-escapes/type-fest": ["type-fest@0.21.3", "", {}, "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w=="], @@ -3788,16 +3491,12 @@ "babel-plugin-istanbul/test-exclude": ["test-exclude@6.0.0", "", { "dependencies": { "@istanbuljs/schema": "^0.1.2", "glob": "^7.1.4", "minimatch": "^3.0.4" } }, "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w=="], - "babel-plugin-jsx-dom-expressions/@babel/helper-module-imports": ["@babel/helper-module-imports@7.18.6", "", { "dependencies": { "@babel/types": "^7.18.6" } }, "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA=="], - "babel-plugin-polyfill-corejs2/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], "better-opn/open": ["open@8.4.2", "", { "dependencies": { "define-lazy-prop": "^2.0.0", "is-docker": "^2.1.1", "is-wsl": "^2.2.0" } }, "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ=="], "bl/buffer": ["buffer@5.7.1", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.1.13" } }, "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ=="], - "blossom-client-sdk/@cashu/cashu-ts": ["@cashu/cashu-ts@2.7.2", "", { "dependencies": { "@noble/curves": "^1.9.5", "@noble/hashes": "^1.5.0", "@scure/bip32": "^1.5.0" } }, "sha512-AOwL6+10AA0syRf2mtRYgoTvAz31gE4MnElj9TLS9xG3k8Msxy27awBlCX5MsBpBgcZZoqWOReYEuUgnbN3IQQ=="], - "caller-callsite/callsites": ["callsites@2.0.0", "", {}, "sha512-ksWePWBloaWPxJYQ8TL0JHvtci6G5QTKwQ95RcWAa/lzoAKuAOflGdAK92hpHXjkwb8zLxoLNUoNYZgVsaJzvQ=="], "camelcase-keys/camelcase": ["camelcase@5.3.1", "", {}, "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg=="], @@ -3842,8 +3541,6 @@ "eslint-plugin-svelte/postcss-load-config": ["postcss-load-config@3.1.4", "", { "dependencies": { "lilconfig": "^2.0.5", "yaml": "^1.10.2" }, "peerDependencies": { "postcss": ">=8.0.9", "ts-node": ">=9.0.0" }, "optionalPeers": ["postcss", "ts-node"] }, "sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg=="], - "event-graph-example/vite": ["vite@7.1.9", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.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", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-4nVGliEpxmhCL8DslSAUdxlB6+SMrhB0a1v5ijlh1xB1nEPuy1mxaHxysVucLHuWryAxLWg6a5ei+U4TLn/rFg=="], - "expo/babel-preset-expo": ["babel-preset-expo@13.2.4", "", { "dependencies": { "@babel/helper-module-imports": "^7.25.9", "@babel/plugin-proposal-decorators": "^7.12.9", "@babel/plugin-proposal-export-default-from": "^7.24.7", "@babel/plugin-syntax-export-default-from": "^7.24.7", "@babel/plugin-transform-export-namespace-from": "^7.25.9", "@babel/plugin-transform-flow-strip-types": "^7.25.2", "@babel/plugin-transform-modules-commonjs": "^7.24.8", "@babel/plugin-transform-object-rest-spread": "^7.24.7", "@babel/plugin-transform-parameters": "^7.24.7", "@babel/plugin-transform-private-methods": "^7.24.7", "@babel/plugin-transform-private-property-in-object": "^7.24.7", "@babel/plugin-transform-runtime": "^7.24.7", "@babel/preset-react": "^7.22.15", "@babel/preset-typescript": "^7.23.0", "@react-native/babel-preset": "0.79.6", "babel-plugin-react-native-web": "~0.19.13", "babel-plugin-syntax-hermes-parser": "^0.25.1", "babel-plugin-transform-flow-enums": "^0.0.2", "debug": "^4.3.4", "react-refresh": "^0.14.2", "resolve-from": "^5.0.0" }, "peerDependencies": { "babel-plugin-react-compiler": "^19.0.0-beta-e993439-20250405" }, "optionalPeers": ["babel-plugin-react-compiler"] }, "sha512-3IKORo3KR+4qtLdCkZNDj8KeA43oBn7RRQejFGWfiZgu/NeaRUSri8YwYjZqybm7hn3nmMv9OLahlvXBX23o5Q=="], "expo-modules-autolinking/commander": ["commander@7.2.0", "", {}, "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw=="], @@ -3854,8 +3551,6 @@ "fast-glob/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], - "feed-viewer-example/vite": ["vite@7.1.9", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.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", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-4nVGliEpxmhCL8DslSAUdxlB6+SMrhB0a1v5ijlh1xB1nEPuy1mxaHxysVucLHuWryAxLWg6a5ei+U4TLn/rFg=="], - "finalhandler/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="], "finalhandler/encodeurl": ["encodeurl@1.0.2", "", {}, "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w=="], @@ -3930,12 +3625,6 @@ "mlly/pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="], - "ndk-svelte5-component-browser/vite": ["vite@7.1.9", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.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", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-4nVGliEpxmhCL8DslSAUdxlB6+SMrhB0a1v5ijlh1xB1nEPuy1mxaHxysVucLHuWryAxLWg6a5ei+U4TLn/rFg=="], - - "ndk-svelte5-nutsack-wallet/vite": ["vite@7.1.9", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.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", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-4nVGliEpxmhCL8DslSAUdxlB6+SMrhB0a1v5ijlh1xB1nEPuy1mxaHxysVucLHuWryAxLWg6a5ei+U4TLn/rFg=="], - - "ndk-svelte5-sessions-demo/vite": ["vite@7.1.9", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.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", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-4nVGliEpxmhCL8DslSAUdxlB6+SMrhB0a1v5ijlh1xB1nEPuy1mxaHxysVucLHuWryAxLWg6a5ei+U4TLn/rFg=="], - "node-dir/minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="], "normalize-package-data/hosted-git-info": ["hosted-git-info@4.1.0", "", { "dependencies": { "lru-cache": "^6.0.0" } }, "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA=="], @@ -4030,12 +3719,6 @@ "simple-plist/bplist-parser": ["bplist-parser@0.3.1", "", { "dependencies": { "big-integer": "1.6.x" } }, "sha512-PyJxiNtA5T2PlLIeBot4lbp7rj4OadzjnMZD/G5zuBNt8ei/yCU7+wW0h2bag9vr8c+/WuRWmSxbqAl9hL1rBA=="], - "solidjs/@types/node": ["@types/node@24.7.1", "", { "dependencies": { "undici-types": "~7.14.0" } }, "sha512-CmyhGZanP88uuC5GpWU9q+fI61j2SkhO3UGMUdfYRE6Bcy0ccyzn1Rqj9YAB/ZY4kOXmNf0ocah5GtphmLMP6Q=="], - - "solidjs/vite": ["vite@5.4.20", "", { "dependencies": { "esbuild": "^0.21.3", "postcss": "^8.4.43", "rollup": "^4.20.0" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || >=20.0.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.4.0" }, "optionalPeers": ["@types/node", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser"], "bin": { "vite": "bin/vite.js" } }, "sha512-j3lYzGC3P+B5Yfy/pfKNgVEg4+UtcIJcVRt2cDjIOmhLourAqPqf8P7acgxeiSgUB7E3p2P8/3gNIgDLpwzs4g=="], - - "solidjs/vitest": ["vitest@3.2.4", "", { "dependencies": { "@types/chai": "^5.2.2", "@vitest/expect": "3.2.4", "@vitest/mocker": "3.2.4", "@vitest/pretty-format": "^3.2.4", "@vitest/runner": "3.2.4", "@vitest/snapshot": "3.2.4", "@vitest/spy": "3.2.4", "@vitest/utils": "3.2.4", "chai": "^5.2.0", "debug": "^4.4.1", "expect-type": "^1.2.1", "magic-string": "^0.30.17", "pathe": "^2.0.3", "picomatch": "^4.0.2", "std-env": "^3.9.0", "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", "why-is-node-running": "^2.3.0" }, "peerDependencies": { "@edge-runtime/vm": "*", "@types/debug": "^4.1.12", "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "@vitest/browser": "3.2.4", "@vitest/ui": "3.2.4", "happy-dom": "*", "jsdom": "*" }, "optionalPeers": ["@edge-runtime/vm", "@types/debug", "@types/node", "@vitest/browser", "@vitest/ui", "happy-dom", "jsdom"], "bin": { "vitest": "vitest.mjs" } }, "sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A=="], - "source-map/whatwg-url": ["whatwg-url@7.1.0", "", { "dependencies": { "lodash.sortby": "^4.7.0", "tr46": "^1.0.1", "webidl-conversions": "^4.0.2" } }, "sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg=="], "source-map-support/source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="], @@ -4046,8 +3729,6 @@ "sucrase/commander": ["commander@4.1.1", "", {}, "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA=="], - "svelte-i18n/esbuild": ["esbuild@0.19.12", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.19.12", "@esbuild/android-arm": "0.19.12", "@esbuild/android-arm64": "0.19.12", "@esbuild/android-x64": "0.19.12", "@esbuild/darwin-arm64": "0.19.12", "@esbuild/darwin-x64": "0.19.12", "@esbuild/freebsd-arm64": "0.19.12", "@esbuild/freebsd-x64": "0.19.12", "@esbuild/linux-arm": "0.19.12", "@esbuild/linux-arm64": "0.19.12", "@esbuild/linux-ia32": "0.19.12", "@esbuild/linux-loong64": "0.19.12", "@esbuild/linux-mips64el": "0.19.12", "@esbuild/linux-ppc64": "0.19.12", "@esbuild/linux-riscv64": "0.19.12", "@esbuild/linux-s390x": "0.19.12", "@esbuild/linux-x64": "0.19.12", "@esbuild/netbsd-x64": "0.19.12", "@esbuild/openbsd-x64": "0.19.12", "@esbuild/sunos-x64": "0.19.12", "@esbuild/win32-arm64": "0.19.12", "@esbuild/win32-ia32": "0.19.12", "@esbuild/win32-x64": "0.19.12" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg=="], - "tar/chownr": ["chownr@3.0.0", "", {}, "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g=="], "temp/rimraf": ["rimraf@2.6.3", "", { "dependencies": { "glob": "^7.1.3" }, "bin": { "rimraf": "./bin.js" } }, "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA=="], @@ -4276,38 +3957,12 @@ "@shikijs/core/@shikijs/engine-javascript/oniguruma-to-es": ["oniguruma-to-es@3.1.1", "", { "dependencies": { "emoji-regex-xs": "^1.0.0", "regex": "^6.0.1", "regex-recursion": "^6.0.2" } }, "sha512-bUH8SDvPkH3ho3dvwJwfonjlQ4R80vjyvrU8YpxuROddv55vAEJrTuCuCVUhhsHbtlD9tGGbaNApGQckXhS8iQ=="], - "@solidjs/testing-library/@testing-library/dom/aria-query": ["aria-query@5.3.0", "", { "dependencies": { "dequal": "^2.0.3" } }, "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A=="], - - "@solidjs/testing-library/@testing-library/dom/pretty-format": ["pretty-format@27.5.1", "", { "dependencies": { "ansi-regex": "^5.0.1", "ansi-styles": "^5.0.0", "react-is": "^17.0.1" } }, "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ=="], - - "@tailwindcss/node/lightningcss/lightningcss-darwin-arm64": ["lightningcss-darwin-arm64@1.30.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-c8JK7hyE65X1MHMN+Viq9n11RRC7hgin3HhYKhrMyaXflk5GVplZ60IxyoVtzILeKr+xAJwg6zK6sjTBJ0FKYQ=="], - - "@tailwindcss/node/lightningcss/lightningcss-darwin-x64": ["lightningcss-darwin-x64@1.30.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-k1EvjakfumAQoTfcXUcHQZhSpLlkAuEkdMBsI/ivWw9hL+7FtilQc0Cy3hrx0AAQrVtQAbMI7YjCgYgvn37PzA=="], - - "@tailwindcss/node/lightningcss/lightningcss-freebsd-x64": ["lightningcss-freebsd-x64@1.30.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-kmW6UGCGg2PcyUE59K5r0kWfKPAVy4SltVeut+umLCFoJ53RdCUWxcRDzO1eTaxf/7Q2H7LTquFHPL5R+Gjyig=="], - - "@tailwindcss/node/lightningcss/lightningcss-linux-arm-gnueabihf": ["lightningcss-linux-arm-gnueabihf@1.30.1", "", { "os": "linux", "cpu": "arm" }, "sha512-MjxUShl1v8pit+6D/zSPq9S9dQ2NPFSQwGvxBCYaBYLPlCWuPh9/t1MRS8iUaR8i+a6w7aps+B4N0S1TYP/R+Q=="], - - "@tailwindcss/node/lightningcss/lightningcss-linux-arm64-gnu": ["lightningcss-linux-arm64-gnu@1.30.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-gB72maP8rmrKsnKYy8XUuXi/4OctJiuQjcuqWNlJQ6jZiWqtPvqFziskH3hnajfvKB27ynbVCucKSm2rkQp4Bw=="], - - "@tailwindcss/node/lightningcss/lightningcss-linux-arm64-musl": ["lightningcss-linux-arm64-musl@1.30.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-jmUQVx4331m6LIX+0wUhBbmMX7TCfjF5FoOH6SD1CttzuYlGNVpA7QnrmLxrsub43ClTINfGSYyHe2HWeLl5CQ=="], - - "@tailwindcss/node/lightningcss/lightningcss-linux-x64-gnu": ["lightningcss-linux-x64-gnu@1.30.1", "", { "os": "linux", "cpu": "x64" }, "sha512-piWx3z4wN8J8z3+O5kO74+yr6ze/dKmPnI7vLqfSqI8bccaTGY5xiSGVIJBDd5K5BHlvVLpUB3S2YCfelyJ1bw=="], - - "@tailwindcss/node/lightningcss/lightningcss-linux-x64-musl": ["lightningcss-linux-x64-musl@1.30.1", "", { "os": "linux", "cpu": "x64" }, "sha512-rRomAK7eIkL+tHY0YPxbc5Dra2gXlI63HL+v1Pdi1a3sC+tJTcFrHX+E86sulgAXeI7rSzDYhPSeHHjqFhqfeQ=="], - - "@tailwindcss/node/lightningcss/lightningcss-win32-arm64-msvc": ["lightningcss-win32-arm64-msvc@1.30.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-mSL4rqPi4iXq5YVqzSsJgMVFENoa4nGTT/GjO2c0Yl9OuQfPsIfncvLrEW6RbbB24WtZ3xP/2CCmI3tNkNV4oA=="], - - "@tailwindcss/node/lightningcss/lightningcss-win32-x64-msvc": ["lightningcss-win32-x64-msvc@1.30.1", "", { "os": "win32", "cpu": "x64" }, "sha512-PVqXh48wh4T53F/1CCu8PIPCxLzWyCnn/9T5W1Jpmdy5h9Cwd+0YQS6/LwhHXSafuc61/xg9Lv5OrCby6a++jg=="], - "@testing-library/dom/pretty-format/react-is": ["react-is@17.0.2", "", {}, "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w=="], "@types/better-sqlite3/@types/node/undici-types": ["undici-types@7.14.0", "", {}, "sha512-QQiYxHuyZ9gQUIrmPo3IA+hUl4KYk8uSA7cHrcKd/l3p1OTpZcM0Tbp9x7FAtXdAYhlasd60ncPpgu6ihG6TOA=="], "@types/graceful-fs/@types/node/undici-types": ["undici-types@7.14.0", "", {}, "sha512-QQiYxHuyZ9gQUIrmPo3IA+hUl4KYk8uSA7cHrcKd/l3p1OTpZcM0Tbp9x7FAtXdAYhlasd60ncPpgu6ihG6TOA=="], - "@types/qrcode/@types/node/undici-types": ["undici-types@7.14.0", "", {}, "sha512-QQiYxHuyZ9gQUIrmPo3IA+hUl4KYk8uSA7cHrcKd/l3p1OTpZcM0Tbp9x7FAtXdAYhlasd60ncPpgu6ihG6TOA=="], - "@types/sql.js/@types/node/undici-types": ["undici-types@7.14.0", "", {}, "sha512-QQiYxHuyZ9gQUIrmPo3IA+hUl4KYk8uSA7cHrcKd/l3p1OTpZcM0Tbp9x7FAtXdAYhlasd60ncPpgu6ihG6TOA=="], "@vitejs/plugin-vue/vite/esbuild": ["esbuild@0.21.5", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.21.5", "@esbuild/android-arm": "0.21.5", "@esbuild/android-arm64": "0.21.5", "@esbuild/android-x64": "0.21.5", "@esbuild/darwin-arm64": "0.21.5", "@esbuild/darwin-x64": "0.21.5", "@esbuild/freebsd-arm64": "0.21.5", "@esbuild/freebsd-x64": "0.21.5", "@esbuild/linux-arm": "0.21.5", "@esbuild/linux-arm64": "0.21.5", "@esbuild/linux-ia32": "0.21.5", "@esbuild/linux-loong64": "0.21.5", "@esbuild/linux-mips64el": "0.21.5", "@esbuild/linux-ppc64": "0.21.5", "@esbuild/linux-riscv64": "0.21.5", "@esbuild/linux-s390x": "0.21.5", "@esbuild/linux-x64": "0.21.5", "@esbuild/netbsd-x64": "0.21.5", "@esbuild/openbsd-x64": "0.21.5", "@esbuild/sunos-x64": "0.21.5", "@esbuild/win32-arm64": "0.21.5", "@esbuild/win32-ia32": "0.21.5", "@esbuild/win32-x64": "0.21.5" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw=="], @@ -4376,18 +4031,10 @@ "@vitest/ui/vitest/vite-node": ["vite-node@3.2.4", "", { "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" } }, "sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg=="], - "agora-app/@cashu/cashu-ts/@scure/bip32": ["@scure/bip32@1.7.0", "", { "dependencies": { "@noble/curves": "~1.9.0", "@noble/hashes": "~1.8.0", "@scure/base": "~1.2.5" } }, "sha512-E4FFX/N3f4B80AKWp5dP6ow+flD1LQZo/w8UnLGYZO674jS6YnYeepycOOksv+vLPSpgN35wgKgy+ybfTb2SMw=="], - - "agora-app/@sveltejs/vite-plugin-svelte/@sveltejs/vite-plugin-svelte-inspector": ["@sveltejs/vite-plugin-svelte-inspector@3.0.1", "", { "dependencies": { "debug": "^4.3.7" }, "peerDependencies": { "@sveltejs/vite-plugin-svelte": "^4.0.0-next.0||^4.0.0", "svelte": "^5.0.0-next.96 || ^5.0.0", "vite": "^5.0.0" } }, "sha512-2CKypmj1sM4GE7HjllT7UKmo4Q6L5xFRd7VMGEWhYnZ+wc6AUVU01IBd7yUi6WnFndEwWoMNOd6e8UjoN0nbvQ=="], - - "agora-app/@sveltejs/vite-plugin-svelte/vite": ["vite@5.4.20", "", { "dependencies": { "esbuild": "^0.21.3", "postcss": "^8.4.43", "rollup": "^4.20.0" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || >=20.0.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.4.0" }, "optionalPeers": ["@types/node", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser"], "bin": { "vite": "bin/vite.js" } }, "sha512-j3lYzGC3P+B5Yfy/pfKNgVEg4+UtcIJcVRt2cDjIOmhLourAqPqf8P7acgxeiSgUB7E3p2P8/3gNIgDLpwzs4g=="], - "babel-plugin-istanbul/test-exclude/glob": ["glob@7.2.3", "", { "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } }, "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q=="], "babel-plugin-istanbul/test-exclude/minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="], - "blossom-client-sdk/@cashu/cashu-ts/@scure/bip32": ["@scure/bip32@1.7.0", "", { "dependencies": { "@noble/curves": "~1.9.0", "@noble/hashes": "~1.8.0", "@scure/base": "~1.2.5" } }, "sha512-E4FFX/N3f4B80AKWp5dP6ow+flD1LQZo/w8UnLGYZO674jS6YnYeepycOOksv+vLPSpgN35wgKgy+ybfTb2SMw=="], - "chalk/ansi-styles/color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="], "chrome-launcher/@types/node/undici-types": ["undici-types@7.14.0", "", {}, "sha512-QQiYxHuyZ9gQUIrmPo3IA+hUl4KYk8uSA7cHrcKd/l3p1OTpZcM0Tbp9x7FAtXdAYhlasd60ncPpgu6ihG6TOA=="], @@ -4564,80 +4211,10 @@ "serve-static/send/encodeurl": ["encodeurl@1.0.2", "", {}, "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w=="], - "solidjs/@types/node/undici-types": ["undici-types@7.14.0", "", {}, "sha512-QQiYxHuyZ9gQUIrmPo3IA+hUl4KYk8uSA7cHrcKd/l3p1OTpZcM0Tbp9x7FAtXdAYhlasd60ncPpgu6ihG6TOA=="], - - "solidjs/vite/esbuild": ["esbuild@0.21.5", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.21.5", "@esbuild/android-arm": "0.21.5", "@esbuild/android-arm64": "0.21.5", "@esbuild/android-x64": "0.21.5", "@esbuild/darwin-arm64": "0.21.5", "@esbuild/darwin-x64": "0.21.5", "@esbuild/freebsd-arm64": "0.21.5", "@esbuild/freebsd-x64": "0.21.5", "@esbuild/linux-arm": "0.21.5", "@esbuild/linux-arm64": "0.21.5", "@esbuild/linux-ia32": "0.21.5", "@esbuild/linux-loong64": "0.21.5", "@esbuild/linux-mips64el": "0.21.5", "@esbuild/linux-ppc64": "0.21.5", "@esbuild/linux-riscv64": "0.21.5", "@esbuild/linux-s390x": "0.21.5", "@esbuild/linux-x64": "0.21.5", "@esbuild/netbsd-x64": "0.21.5", "@esbuild/openbsd-x64": "0.21.5", "@esbuild/sunos-x64": "0.21.5", "@esbuild/win32-arm64": "0.21.5", "@esbuild/win32-ia32": "0.21.5", "@esbuild/win32-x64": "0.21.5" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw=="], - - "solidjs/vitest/@vitest/expect": ["@vitest/expect@3.2.4", "", { "dependencies": { "@types/chai": "^5.2.2", "@vitest/spy": "3.2.4", "@vitest/utils": "3.2.4", "chai": "^5.2.0", "tinyrainbow": "^2.0.0" } }, "sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig=="], - - "solidjs/vitest/@vitest/runner": ["@vitest/runner@3.2.4", "", { "dependencies": { "@vitest/utils": "3.2.4", "pathe": "^2.0.3", "strip-literal": "^3.0.0" } }, "sha512-oukfKT9Mk41LreEW09vt45f8wx7DordoWUZMYdY/cyAk7w5TWkTRCNZYF7sX7n2wB7jyGAl74OxgwhPgKaqDMQ=="], - - "solidjs/vitest/@vitest/snapshot": ["@vitest/snapshot@3.2.4", "", { "dependencies": { "@vitest/pretty-format": "3.2.4", "magic-string": "^0.30.17", "pathe": "^2.0.3" } }, "sha512-dEYtS7qQP2CjU27QBC5oUOxLE/v5eLkGqPE0ZKEIDGMs4vKWe7IjgLOeauHsR0D5YuuycGRO5oSRXnwnmA78fQ=="], - - "solidjs/vitest/@vitest/spy": ["@vitest/spy@3.2.4", "", { "dependencies": { "tinyspy": "^4.0.3" } }, "sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw=="], - - "solidjs/vitest/@vitest/utils": ["@vitest/utils@3.2.4", "", { "dependencies": { "@vitest/pretty-format": "3.2.4", "loupe": "^3.1.4", "tinyrainbow": "^2.0.0" } }, "sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA=="], - - "solidjs/vitest/chai": ["chai@5.3.3", "", { "dependencies": { "assertion-error": "^2.0.1", "check-error": "^2.1.1", "deep-eql": "^5.0.1", "loupe": "^3.1.0", "pathval": "^2.0.0" } }, "sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw=="], - - "solidjs/vitest/pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="], - - "solidjs/vitest/tinypool": ["tinypool@1.1.1", "", {}, "sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg=="], - - "solidjs/vitest/vite": ["vite@7.1.9", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.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", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-4nVGliEpxmhCL8DslSAUdxlB6+SMrhB0a1v5ijlh1xB1nEPuy1mxaHxysVucLHuWryAxLWg6a5ei+U4TLn/rFg=="], - - "solidjs/vitest/vite-node": ["vite-node@3.2.4", "", { "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" } }, "sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg=="], - "source-map/whatwg-url/tr46": ["tr46@1.0.1", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA=="], "source-map/whatwg-url/webidl-conversions": ["webidl-conversions@4.0.2", "", {}, "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg=="], - "svelte-i18n/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.19.12", "", { "os": "aix", "cpu": "ppc64" }, "sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA=="], - - "svelte-i18n/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.19.12", "", { "os": "android", "cpu": "arm" }, "sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w=="], - - "svelte-i18n/esbuild/@esbuild/android-arm64": ["@esbuild/android-arm64@0.19.12", "", { "os": "android", "cpu": "arm64" }, "sha512-P0UVNGIienjZv3f5zq0DP3Nt2IE/3plFzuaS96vihvD0Hd6H/q4WXUGpCxD/E8YrSXfNyRPbpTq+T8ZQioSuPA=="], - - "svelte-i18n/esbuild/@esbuild/android-x64": ["@esbuild/android-x64@0.19.12", "", { "os": "android", "cpu": "x64" }, "sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew=="], - - "svelte-i18n/esbuild/@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.19.12", "", { "os": "darwin", "cpu": "arm64" }, "sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g=="], - - "svelte-i18n/esbuild/@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.19.12", "", { "os": "darwin", "cpu": "x64" }, "sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A=="], - - "svelte-i18n/esbuild/@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.19.12", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-4aRvFIXmwAcDBw9AueDQ2YnGmz5L6obe5kmPT8Vd+/+x/JMVKCgdcRwH6APrbpNXsPz+K653Qg8HB/oXvXVukA=="], - - "svelte-i18n/esbuild/@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.19.12", "", { "os": "freebsd", "cpu": "x64" }, "sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg=="], - - "svelte-i18n/esbuild/@esbuild/linux-arm": ["@esbuild/linux-arm@0.19.12", "", { "os": "linux", "cpu": "arm" }, "sha512-J5jPms//KhSNv+LO1S1TX1UWp1ucM6N6XuL6ITdKWElCu8wXP72l9MM0zDTzzeikVyqFE6U8YAV9/tFyj0ti+w=="], - - "svelte-i18n/esbuild/@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.19.12", "", { "os": "linux", "cpu": "arm64" }, "sha512-EoTjyYyLuVPfdPLsGVVVC8a0p1BFFvtpQDB/YLEhaXyf/5bczaGeN15QkR+O4S5LeJ92Tqotve7i1jn35qwvdA=="], - - "svelte-i18n/esbuild/@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.19.12", "", { "os": "linux", "cpu": "ia32" }, "sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA=="], - - "svelte-i18n/esbuild/@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.19.12", "", { "os": "linux", "cpu": "none" }, "sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA=="], - - "svelte-i18n/esbuild/@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.19.12", "", { "os": "linux", "cpu": "none" }, "sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w=="], - - "svelte-i18n/esbuild/@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.19.12", "", { "os": "linux", "cpu": "ppc64" }, "sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg=="], - - "svelte-i18n/esbuild/@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.19.12", "", { "os": "linux", "cpu": "none" }, "sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg=="], - - "svelte-i18n/esbuild/@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.19.12", "", { "os": "linux", "cpu": "s390x" }, "sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg=="], - - "svelte-i18n/esbuild/@esbuild/linux-x64": ["@esbuild/linux-x64@0.19.12", "", { "os": "linux", "cpu": "x64" }, "sha512-B71g1QpxfwBvNrfyJdVDexenDIt1CiDN1TIXLbhOw0KhJzE78KIFGX6OJ9MrtC0oOqMWf+0xop4qEU8JrJTwCg=="], - - "svelte-i18n/esbuild/@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.19.12", "", { "os": "none", "cpu": "x64" }, "sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA=="], - - "svelte-i18n/esbuild/@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.19.12", "", { "os": "openbsd", "cpu": "x64" }, "sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw=="], - - "svelte-i18n/esbuild/@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.19.12", "", { "os": "sunos", "cpu": "x64" }, "sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA=="], - - "svelte-i18n/esbuild/@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.19.12", "", { "os": "win32", "cpu": "arm64" }, "sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A=="], - - "svelte-i18n/esbuild/@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.19.12", "", { "os": "win32", "cpu": "ia32" }, "sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ=="], - - "svelte-i18n/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.19.12", "", { "os": "win32", "cpu": "x64" }, "sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA=="], - "temp/rimraf/glob": ["glob@7.2.3", "", { "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } }, "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q=="], "tiptap-markdown/@types/markdown-it/@types/linkify-it": ["@types/linkify-it@3.0.5", "", {}, "sha512-yg6E+u0/+Zjva+buc3EIb+29XEg4wltq7cSmd4Uc2EE/1nUVmxyzpX6gUXD0V8jIrG0r7YeOGVIbYRkxeooCtw=="], @@ -4864,8 +4441,6 @@ "@react-native/codegen/glob/minimatch/brace-expansion": ["brace-expansion@1.1.12", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="], - "@solidjs/testing-library/@testing-library/dom/pretty-format/react-is": ["react-is@17.0.2", "", {}, "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w=="], - "@vitejs/plugin-vue/vite/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.21.5", "", { "os": "aix", "cpu": "ppc64" }, "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ=="], "@vitejs/plugin-vue/vite/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.21.5", "", { "os": "android", "cpu": "arm" }, "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg=="], @@ -4958,8 +4533,6 @@ "@vitest/ui/vitest/chai/pathval": ["pathval@2.0.1", "", {}, "sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ=="], - "agora-app/@sveltejs/vite-plugin-svelte/vite/esbuild": ["esbuild@0.21.5", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.21.5", "@esbuild/android-arm": "0.21.5", "@esbuild/android-arm64": "0.21.5", "@esbuild/android-x64": "0.21.5", "@esbuild/darwin-arm64": "0.21.5", "@esbuild/darwin-x64": "0.21.5", "@esbuild/freebsd-arm64": "0.21.5", "@esbuild/freebsd-x64": "0.21.5", "@esbuild/linux-arm": "0.21.5", "@esbuild/linux-arm64": "0.21.5", "@esbuild/linux-ia32": "0.21.5", "@esbuild/linux-loong64": "0.21.5", "@esbuild/linux-mips64el": "0.21.5", "@esbuild/linux-ppc64": "0.21.5", "@esbuild/linux-riscv64": "0.21.5", "@esbuild/linux-s390x": "0.21.5", "@esbuild/linux-x64": "0.21.5", "@esbuild/netbsd-x64": "0.21.5", "@esbuild/openbsd-x64": "0.21.5", "@esbuild/sunos-x64": "0.21.5", "@esbuild/win32-arm64": "0.21.5", "@esbuild/win32-ia32": "0.21.5", "@esbuild/win32-x64": "0.21.5" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw=="], - "babel-plugin-istanbul/test-exclude/minimatch/brace-expansion": ["brace-expansion@1.1.12", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="], "chalk/ansi-styles/color-convert/color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="], @@ -5028,68 +4601,6 @@ "serve-static/send/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="], - "solidjs/vite/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.21.5", "", { "os": "aix", "cpu": "ppc64" }, "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ=="], - - "solidjs/vite/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.21.5", "", { "os": "android", "cpu": "arm" }, "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg=="], - - "solidjs/vite/esbuild/@esbuild/android-arm64": ["@esbuild/android-arm64@0.21.5", "", { "os": "android", "cpu": "arm64" }, "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A=="], - - "solidjs/vite/esbuild/@esbuild/android-x64": ["@esbuild/android-x64@0.21.5", "", { "os": "android", "cpu": "x64" }, "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA=="], - - "solidjs/vite/esbuild/@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.21.5", "", { "os": "darwin", "cpu": "arm64" }, "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ=="], - - "solidjs/vite/esbuild/@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.21.5", "", { "os": "darwin", "cpu": "x64" }, "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw=="], - - "solidjs/vite/esbuild/@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.21.5", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g=="], - - "solidjs/vite/esbuild/@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.21.5", "", { "os": "freebsd", "cpu": "x64" }, "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ=="], - - "solidjs/vite/esbuild/@esbuild/linux-arm": ["@esbuild/linux-arm@0.21.5", "", { "os": "linux", "cpu": "arm" }, "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA=="], - - "solidjs/vite/esbuild/@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.21.5", "", { "os": "linux", "cpu": "arm64" }, "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q=="], - - "solidjs/vite/esbuild/@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.21.5", "", { "os": "linux", "cpu": "ia32" }, "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg=="], - - "solidjs/vite/esbuild/@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.21.5", "", { "os": "linux", "cpu": "none" }, "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg=="], - - "solidjs/vite/esbuild/@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.21.5", "", { "os": "linux", "cpu": "none" }, "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg=="], - - "solidjs/vite/esbuild/@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.21.5", "", { "os": "linux", "cpu": "ppc64" }, "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w=="], - - "solidjs/vite/esbuild/@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.21.5", "", { "os": "linux", "cpu": "none" }, "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA=="], - - "solidjs/vite/esbuild/@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.21.5", "", { "os": "linux", "cpu": "s390x" }, "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A=="], - - "solidjs/vite/esbuild/@esbuild/linux-x64": ["@esbuild/linux-x64@0.21.5", "", { "os": "linux", "cpu": "x64" }, "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ=="], - - "solidjs/vite/esbuild/@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.21.5", "", { "os": "none", "cpu": "x64" }, "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg=="], - - "solidjs/vite/esbuild/@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.21.5", "", { "os": "openbsd", "cpu": "x64" }, "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow=="], - - "solidjs/vite/esbuild/@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.21.5", "", { "os": "sunos", "cpu": "x64" }, "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg=="], - - "solidjs/vite/esbuild/@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.21.5", "", { "os": "win32", "cpu": "arm64" }, "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A=="], - - "solidjs/vite/esbuild/@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.21.5", "", { "os": "win32", "cpu": "ia32" }, "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA=="], - - "solidjs/vite/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.21.5", "", { "os": "win32", "cpu": "x64" }, "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw=="], - - "solidjs/vitest/@vitest/runner/strip-literal": ["strip-literal@3.1.0", "", { "dependencies": { "js-tokens": "^9.0.1" } }, "sha512-8r3mkIM/2+PpjHoOtiAW8Rg3jJLHaV7xPwG+YRGrv6FP0wwk/toTpATxWYOW0BKdWwl82VT2tFYi5DlROa0Mxg=="], - - "solidjs/vitest/@vitest/spy/tinyspy": ["tinyspy@4.0.4", "", {}, "sha512-azl+t0z7pw/z958Gy9svOTuzqIk6xq+NSheJzn5MMWtWTFywIacg2wUlzKFGtt3cthx0r2SxMK0yzJOR0IES7Q=="], - - "solidjs/vitest/@vitest/utils/loupe": ["loupe@3.2.1", "", {}, "sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ=="], - - "solidjs/vitest/chai/assertion-error": ["assertion-error@2.0.1", "", {}, "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA=="], - - "solidjs/vitest/chai/check-error": ["check-error@2.1.1", "", {}, "sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw=="], - - "solidjs/vitest/chai/deep-eql": ["deep-eql@5.0.2", "", {}, "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q=="], - - "solidjs/vitest/chai/loupe": ["loupe@3.2.1", "", {}, "sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ=="], - - "solidjs/vitest/chai/pathval": ["pathval@2.0.1", "", {}, "sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ=="], - "temp/rimraf/glob/minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="], "vite-node/vite/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.21.5", "", { "os": "aix", "cpu": "ppc64" }, "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ=="], @@ -5264,52 +4775,6 @@ "@react-native/babel-plugin-codegen/@react-native/codegen/glob/minimatch/brace-expansion": ["brace-expansion@1.1.12", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="], - "agora-app/@sveltejs/vite-plugin-svelte/vite/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.21.5", "", { "os": "aix", "cpu": "ppc64" }, "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ=="], - - "agora-app/@sveltejs/vite-plugin-svelte/vite/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.21.5", "", { "os": "android", "cpu": "arm" }, "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg=="], - - "agora-app/@sveltejs/vite-plugin-svelte/vite/esbuild/@esbuild/android-arm64": ["@esbuild/android-arm64@0.21.5", "", { "os": "android", "cpu": "arm64" }, "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A=="], - - "agora-app/@sveltejs/vite-plugin-svelte/vite/esbuild/@esbuild/android-x64": ["@esbuild/android-x64@0.21.5", "", { "os": "android", "cpu": "x64" }, "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA=="], - - "agora-app/@sveltejs/vite-plugin-svelte/vite/esbuild/@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.21.5", "", { "os": "darwin", "cpu": "arm64" }, "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ=="], - - "agora-app/@sveltejs/vite-plugin-svelte/vite/esbuild/@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.21.5", "", { "os": "darwin", "cpu": "x64" }, "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw=="], - - "agora-app/@sveltejs/vite-plugin-svelte/vite/esbuild/@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.21.5", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g=="], - - "agora-app/@sveltejs/vite-plugin-svelte/vite/esbuild/@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.21.5", "", { "os": "freebsd", "cpu": "x64" }, "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ=="], - - "agora-app/@sveltejs/vite-plugin-svelte/vite/esbuild/@esbuild/linux-arm": ["@esbuild/linux-arm@0.21.5", "", { "os": "linux", "cpu": "arm" }, "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA=="], - - "agora-app/@sveltejs/vite-plugin-svelte/vite/esbuild/@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.21.5", "", { "os": "linux", "cpu": "arm64" }, "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q=="], - - "agora-app/@sveltejs/vite-plugin-svelte/vite/esbuild/@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.21.5", "", { "os": "linux", "cpu": "ia32" }, "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg=="], - - "agora-app/@sveltejs/vite-plugin-svelte/vite/esbuild/@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.21.5", "", { "os": "linux", "cpu": "none" }, "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg=="], - - "agora-app/@sveltejs/vite-plugin-svelte/vite/esbuild/@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.21.5", "", { "os": "linux", "cpu": "none" }, "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg=="], - - "agora-app/@sveltejs/vite-plugin-svelte/vite/esbuild/@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.21.5", "", { "os": "linux", "cpu": "ppc64" }, "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w=="], - - "agora-app/@sveltejs/vite-plugin-svelte/vite/esbuild/@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.21.5", "", { "os": "linux", "cpu": "none" }, "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA=="], - - "agora-app/@sveltejs/vite-plugin-svelte/vite/esbuild/@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.21.5", "", { "os": "linux", "cpu": "s390x" }, "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A=="], - - "agora-app/@sveltejs/vite-plugin-svelte/vite/esbuild/@esbuild/linux-x64": ["@esbuild/linux-x64@0.21.5", "", { "os": "linux", "cpu": "x64" }, "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ=="], - - "agora-app/@sveltejs/vite-plugin-svelte/vite/esbuild/@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.21.5", "", { "os": "none", "cpu": "x64" }, "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg=="], - - "agora-app/@sveltejs/vite-plugin-svelte/vite/esbuild/@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.21.5", "", { "os": "openbsd", "cpu": "x64" }, "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow=="], - - "agora-app/@sveltejs/vite-plugin-svelte/vite/esbuild/@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.21.5", "", { "os": "sunos", "cpu": "x64" }, "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg=="], - - "agora-app/@sveltejs/vite-plugin-svelte/vite/esbuild/@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.21.5", "", { "os": "win32", "cpu": "arm64" }, "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A=="], - - "agora-app/@sveltejs/vite-plugin-svelte/vite/esbuild/@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.21.5", "", { "os": "win32", "cpu": "ia32" }, "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA=="], - - "agora-app/@sveltejs/vite-plugin-svelte/vite/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.21.5", "", { "os": "win32", "cpu": "x64" }, "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw=="], - "chromium-edge-launcher/rimraf/glob/minimatch/brace-expansion": ["brace-expansion@1.1.12", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="], "del/rimraf/glob/minimatch/brace-expansion": ["brace-expansion@1.1.12", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="], diff --git a/bun.lockb b/bun.lockb deleted file mode 100755 index 3dee859a1a8f14403b94e2587a4f2c6bb1e0282d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 765300 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 diff --git a/package.json b/package.json index 5402f17f3..292c59058 100755 --- a/package.json +++ b/package.json @@ -1,61 +1,61 @@ { - "name": "@nostr-dev-kit/monorepo", - "private": true, - "scripts": { - "build": "turbo build", - "build:libs": "turbo build --filter='./core' --filter='./wallet' --filter='./react' --filter='./mobile' --filter='./blossom' --filter='./cache-*' --filter='./cache-memory' --filter='./sessions' --filter='./sync' --filter='./wot' --filter='./svelte'", - "build:core": "cd core && bun run build", - "build:wallet": "cd wallet && bun run build", - "build:hooks": "cd react && bun run build", - "build:mobile": "cd mobile && bun run build", - "build:blossom": "cd blossom && bun run build", - "build:cache-sqlite": "cd cache-sqlite && bun run build", - "dev": "turbo dev --no-cache --continue", - "lint": "biome check .", - "clean": "turbo clean && rm -rf node_modules", - "format": "biome format --write .", - "docs:dev": "npx vitepress dev", - "docs:build": "vitepress build", - "changeset": "changeset", - "cs": "changeset", - "cs:ver": "changeset version", - "test": "bun vitest", - "cs:pub": "changeset publish", - "version-packages": "changeset version", - "release": "turbo build --filter=docs^... && changeset publish", - "docs:preview": "vitepress preview" - }, - "type": "module", - "workspaces": [ - "core", - "cache-dexie", - "cache-memory", - "cache-redis", - "cache-nostr", - "cache-sqlite", - "react", - "mobile", - "sessions", - "sync", - "svelte", - "wallet", - "wot", - "blossom", - "cache-sqlite-wasm" - ], - "engines": { - "node": ">=16.0" - }, - "packageManager": "bun@1.0.0", - "devDependencies": { - "svelte": "^5.39.9", - "turbo": "^2.5.3" - }, - "dependencies": { - "@biomejs/biome": "^2.2.5", - "esbuild": "^0.25.10", - "mermaid": "^11.6.0", - "vitepress": "^1.6.3", - "vitepress-plugin-mermaid": "^2.0.17" - } + "name": "@nostr-dev-kit/monorepo", + "private": true, + "scripts": { + "build": "turbo build", + "build:libs": "turbo build --filter='./core' --filter='./wallet' --filter='./react' --filter='./mobile' --filter='./blossom' --filter='./cache-*' --filter='./cache-memory' --filter='./sessions' --filter='./sync' --filter='./wot' --filter='./svelte'", + "build:core": "cd core && bun run build", + "build:wallet": "cd wallet && bun run build", + "build:hooks": "cd react && bun run build", + "build:mobile": "cd mobile && bun run build", + "build:blossom": "cd blossom && bun run build", + "build:cache-sqlite": "cd cache-sqlite && bun run build", + "dev": "turbo dev --no-cache --continue", + "lint": "biome check .", + "clean": "turbo clean && rm -rf node_modules", + "format": "biome format --write .", + "docs:dev": "npx vitepress dev", + "docs:build": "vitepress build", + "changeset": "changeset", + "cs": "changeset", + "cs:ver": "changeset version", + "test": "bun vitest", + "cs:pub": "changeset publish", + "version-packages": "changeset version", + "release": "turbo build --filter=docs^... && changeset publish", + "docs:preview": "vitepress preview" + }, + "type": "module", + "workspaces": [ + "core", + "cache-dexie", + "cache-memory", + "cache-redis", + "cache-nostr", + "cache-sqlite", + "react", + "mobile", + "sessions", + "sync", + "svelte", + "wallet", + "wot", + "blossom", + "cache-sqlite-wasm" + ], + "engines": { + "node": ">=16.0" + }, + "packageManager": "bun@1.0.0", + "devDependencies": { + "svelte": "^5.39.9", + "turbo": "^2.5.3" + }, + "dependencies": { + "@biomejs/biome": "^2.2.5", + "esbuild": "^0.25.10", + "mermaid": "^11.6.0", + "vitepress": "^1.6.3", + "vitepress-plugin-mermaid": "^2.0.17" + } } From 949f258004e6ecaaed600dc0fde261727a23f66b Mon Sep 17 00:00:00 2001 From: digitalbase Date: Sat, 11 Oct 2025 13:21:03 +0200 Subject: [PATCH 014/139] Update bun lockfile --- bun.lock | 308 ++++++++++++------------------------------------------- 1 file changed, 64 insertions(+), 244 deletions(-) diff --git a/bun.lock b/bun.lock index 89f927616..5ed81e56d 100644 --- a/bun.lock +++ b/bun.lock @@ -947,29 +947,27 @@ "@polka/url": ["@polka/url@1.0.0-next.29", "", {}, "sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww=="], - "@react-native/assets-registry": ["@react-native/assets-registry@0.82.0", "", {}, "sha512-SHRZxH+VHb6RwcHNskxyjso6o91Lq0DPgOpE5cDrppn1ziYhI723rjufFgh59RcpH441eci0/cXs/b0csXTtnw=="], + "@react-native/assets-registry": ["@react-native/assets-registry@0.79.2", "", {}, "sha512-5h2Z7/+/HL/0h88s0JHOdRCW4CXMCJoROxqzHqxdrjGL6EBD1DdaB4ZqkCOEVSW4Vjhir5Qb97C8i/MPWEYPtg=="], "@react-native/babel-plugin-codegen": ["@react-native/babel-plugin-codegen@0.76.9", "", { "dependencies": { "@react-native/codegen": "0.76.9" } }, "sha512-vxL/vtDEIYHfWKm5oTaEmwcnNGsua/i9OjIxBDBFiJDu5i5RU3bpmDiXQm/bJxrJNPRp5lW0I0kpGihVhnMAIQ=="], "@react-native/babel-preset": ["@react-native/babel-preset@0.76.9", "", { "dependencies": { "@babel/core": "^7.25.2", "@babel/plugin-proposal-export-default-from": "^7.24.7", "@babel/plugin-syntax-dynamic-import": "^7.8.3", "@babel/plugin-syntax-export-default-from": "^7.24.7", "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", "@babel/plugin-syntax-optional-chaining": "^7.8.3", "@babel/plugin-transform-arrow-functions": "^7.24.7", "@babel/plugin-transform-async-generator-functions": "^7.25.4", "@babel/plugin-transform-async-to-generator": "^7.24.7", "@babel/plugin-transform-block-scoping": "^7.25.0", "@babel/plugin-transform-class-properties": "^7.25.4", "@babel/plugin-transform-classes": "^7.25.4", "@babel/plugin-transform-computed-properties": "^7.24.7", "@babel/plugin-transform-destructuring": "^7.24.8", "@babel/plugin-transform-flow-strip-types": "^7.25.2", "@babel/plugin-transform-for-of": "^7.24.7", "@babel/plugin-transform-function-name": "^7.25.1", "@babel/plugin-transform-literals": "^7.25.2", "@babel/plugin-transform-logical-assignment-operators": "^7.24.7", "@babel/plugin-transform-modules-commonjs": "^7.24.8", "@babel/plugin-transform-named-capturing-groups-regex": "^7.24.7", "@babel/plugin-transform-nullish-coalescing-operator": "^7.24.7", "@babel/plugin-transform-numeric-separator": "^7.24.7", "@babel/plugin-transform-object-rest-spread": "^7.24.7", "@babel/plugin-transform-optional-catch-binding": "^7.24.7", "@babel/plugin-transform-optional-chaining": "^7.24.8", "@babel/plugin-transform-parameters": "^7.24.7", "@babel/plugin-transform-private-methods": "^7.24.7", "@babel/plugin-transform-private-property-in-object": "^7.24.7", "@babel/plugin-transform-react-display-name": "^7.24.7", "@babel/plugin-transform-react-jsx": "^7.25.2", "@babel/plugin-transform-react-jsx-self": "^7.24.7", "@babel/plugin-transform-react-jsx-source": "^7.24.7", "@babel/plugin-transform-regenerator": "^7.24.7", "@babel/plugin-transform-runtime": "^7.24.7", "@babel/plugin-transform-shorthand-properties": "^7.24.7", "@babel/plugin-transform-spread": "^7.24.7", "@babel/plugin-transform-sticky-regex": "^7.24.7", "@babel/plugin-transform-typescript": "^7.25.2", "@babel/plugin-transform-unicode-regex": "^7.24.7", "@babel/template": "^7.25.0", "@react-native/babel-plugin-codegen": "0.76.9", "babel-plugin-syntax-hermes-parser": "^0.25.1", "babel-plugin-transform-flow-enums": "^0.0.2", "react-refresh": "^0.14.0" } }, "sha512-TbSeCplCM6WhL3hR2MjC/E1a9cRnMLz7i767T7mP90oWkklEjyPxWl+0GGoVGnJ8FC/jLUupg/HvREKjjif6lw=="], - "@react-native/codegen": ["@react-native/codegen@0.82.0", "", { "dependencies": { "@babel/core": "^7.25.2", "@babel/parser": "^7.25.3", "glob": "^7.1.1", "hermes-parser": "0.32.0", "invariant": "^2.2.4", "nullthrows": "^1.1.1", "yargs": "^17.6.2" } }, "sha512-DJKDwyr6s0EtoPKmAaOsx2EnS2sV/qICNWn/KA+8lohSY6gJF1wuA+DOjitivBfU0soADoo8tqGq50C5rlzmCA=="], + "@react-native/codegen": ["@react-native/codegen@0.79.2", "", { "dependencies": { "glob": "^7.1.1", "hermes-parser": "0.25.1", "invariant": "^2.2.4", "nullthrows": "^1.1.1", "yargs": "^17.6.2" }, "peerDependencies": { "@babel/core": "*" } }, "sha512-8JTlGLuLi1p8Jx2N/enwwEd7/2CfrqJpv90Cp77QLRX3VHF2hdyavRIxAmXMwN95k+Me7CUuPtqn2X3IBXOWYg=="], - "@react-native/community-cli-plugin": ["@react-native/community-cli-plugin@0.82.0", "", { "dependencies": { "@react-native/dev-middleware": "0.82.0", "debug": "^4.4.0", "invariant": "^2.2.4", "metro": "^0.83.1", "metro-config": "^0.83.1", "metro-core": "^0.83.1", "semver": "^7.1.3" }, "peerDependencies": { "@react-native-community/cli": "*", "@react-native/metro-config": "*" }, "optionalPeers": ["@react-native-community/cli", "@react-native/metro-config"] }, "sha512-n5dxQowsRAjruG5sNl6MEPUzANUiVERaL7w4lHGmm/pz/ey1JOM9sFxL6RpZp1FJSVu4QKmbx6sIHrKb2MCekg=="], + "@react-native/community-cli-plugin": ["@react-native/community-cli-plugin@0.79.2", "", { "dependencies": { "@react-native/dev-middleware": "0.79.2", "chalk": "^4.0.0", "debug": "^2.2.0", "invariant": "^2.2.4", "metro": "^0.82.0", "metro-config": "^0.82.0", "metro-core": "^0.82.0", "semver": "^7.1.3" }, "peerDependencies": { "@react-native-community/cli": "*" }, "optionalPeers": ["@react-native-community/cli"] }, "sha512-E+YEY2dL+68HyR2iahsZdyBKBUi9QyPyaN9vsnda1jNgCjNpSPk2yAF5cXsho+zKK5ZQna3JSeE1Kbi2IfGJbw=="], "@react-native/debugger-frontend": ["@react-native/debugger-frontend@0.79.6", "", {}, "sha512-lIK/KkaH7ueM22bLO0YNaQwZbT/oeqhaghOvmZacaNVbJR1Cdh/XAqjT8FgCS+7PUnbxA8B55NYNKGZG3O2pYw=="], - "@react-native/debugger-shell": ["@react-native/debugger-shell@0.82.0", "", { "dependencies": { "cross-spawn": "^7.0.6", "fb-dotslash": "0.5.8" } }, "sha512-XbXABIMzaH7SvapNWcW+zix1uHeSX/MoXYKKWWTs99a12TgwNuTeLKKTEj/ZkAjWtaCCqb/sMI4aJDLXKppCGg=="], - "@react-native/dev-middleware": ["@react-native/dev-middleware@0.79.6", "", { "dependencies": { "@isaacs/ttlcache": "^1.4.1", "@react-native/debugger-frontend": "0.79.6", "chrome-launcher": "^0.15.2", "chromium-edge-launcher": "^0.2.0", "connect": "^3.6.5", "debug": "^2.2.0", "invariant": "^2.2.4", "nullthrows": "^1.1.1", "open": "^7.0.3", "serve-static": "^1.16.2", "ws": "^6.2.3" } }, "sha512-BK3GZBa9c7XSNR27EDRtxrgyyA3/mf1j3/y+mPk7Ac0Myu85YNrXnC9g3mL5Ytwo0g58TKrAIgs1fF2Q5Mn6mQ=="], - "@react-native/gradle-plugin": ["@react-native/gradle-plugin@0.82.0", "", {}, "sha512-PTfmQ6cYsJgMXJ49NzB4Sz/DmRUtwatGtcA6MuskRvQpSinno/00Sns7wxphkTVMHGAwk3Xh0t0SFNd1d1HCyw=="], + "@react-native/gradle-plugin": ["@react-native/gradle-plugin@0.79.2", "", {}, "sha512-6MJFemrwR0bOT0QM+2BxX9k3/pvZQNmJ3Js5pF/6owsA0cUDiCO57otiEU8Fz+UywWEzn1FoQfOfQ8vt2GYmoA=="], - "@react-native/js-polyfills": ["@react-native/js-polyfills@0.82.0", "", {}, "sha512-7K1K64rfq0sKoGxB2DTsZROxal0B04Q+ftia0JyCOGOto/tyBQIQqiQgVtMVEBZSEXZyXmGx3HzF4EEPlvrEtw=="], + "@react-native/js-polyfills": ["@react-native/js-polyfills@0.79.2", "", {}, "sha512-IaY87Ckd4GTPMkO1/Fe8fC1IgIx3vc3q9Tyt/6qS3Mtk9nC0x9q4kSR5t+HHq0/MuvGtu8HpdxXGy5wLaM+zUw=="], - "@react-native/normalize-colors": ["@react-native/normalize-colors@0.82.0", "", {}, "sha512-oinsK6TYEz5RnFTSk9P+hJ/N/E0pOG76O0euU0Gf3BlXArDpS8m3vrGcTjqeQvajRIaYVHIRAY9hCO6q+exyLg=="], + "@react-native/normalize-colors": ["@react-native/normalize-colors@0.79.2", "", {}, "sha512-+b+GNrupWrWw1okHnEENz63j7NSMqhKeFMOyzYLBwKcprG8fqJQhDIGXfizKdxeIa5NnGSAevKL1Ev1zJ56X8w=="], - "@react-native/virtualized-lists": ["@react-native/virtualized-lists@0.82.0", "", { "dependencies": { "invariant": "^2.2.4", "nullthrows": "^1.1.1" }, "peerDependencies": { "@types/react": "^19.1.1", "react": "*", "react-native": "*" }, "optionalPeers": ["@types/react"] }, "sha512-fReDITtqwWdN29doPHhmeQEqa12ATJ4M2Y1MrT8Q1Hoy5a0H3oEn9S7fknGr7Pj+/I77yHyJajUbCupnJ8vkFA=="], + "@react-native/virtualized-lists": ["@react-native/virtualized-lists@0.79.2", "", { "dependencies": { "invariant": "^2.2.4", "nullthrows": "^1.1.1" }, "peerDependencies": { "@types/react": "^19.0.0", "react": "*", "react-native": "*" }, "optionalPeers": ["@types/react"] }, "sha512-9G6ROJeP+rdw9Bvr5ruOlag11ET7j1z/En1riFFNo6W3xZvJY+alCuH1ttm12y9+zBm4n8jwCk4lGhjYaV4dKw=="], "@remirror/core-constants": ["@remirror/core-constants@3.0.0", "", {}, "sha512-42aWfPrimMfDKDi4YegyS7x+/0tlzaqwPQCULLanv3DMIlu96KTJR0fM5isWX2UViOqlGnX6YFgqWepcX+XMNg=="], @@ -1315,8 +1313,6 @@ "@vitest/spy": ["@vitest/spy@1.6.1", "", { "dependencies": { "tinyspy": "^2.2.0" } }, "sha512-MGcMmpGkZebsMZhbQKkAf9CX5zGvjkBTqf8Zx3ApYWXr3wG+QvEu2eXWfnIIWYSJExIp4V9FCKDEeygzkYrXMw=="], - "@vitest/ui": ["@vitest/ui@3.2.4", "", { "dependencies": { "@vitest/utils": "3.2.4", "fflate": "^0.8.2", "flatted": "^3.3.3", "pathe": "^2.0.3", "sirv": "^3.0.1", "tinyglobby": "^0.2.14", "tinyrainbow": "^2.0.0" }, "peerDependencies": { "vitest": "3.2.4" } }, "sha512-hGISOaP18plkzbWEcP/QvtRW1xDXF2+96HbEX6byqQhAUbiS5oH6/9JwW+QsQCIYON2bI6QZBF+2PvOmrRZ9wA=="], - "@vitest/utils": ["@vitest/utils@1.6.1", "", { "dependencies": { "diff-sequences": "^29.6.3", "estree-walker": "^3.0.3", "loupe": "^2.3.7", "pretty-format": "^29.7.0" } }, "sha512-jOrrUvXM4Av9ZWiG1EajNto0u96kWAhJ1LmPmJhXXQx/32MecEKd10pOLYgS2BQx1TgkGhloPU1ArDW2vvaY6g=="], "@vue/compiler-core": ["@vue/compiler-core@3.5.22", "", { "dependencies": { "@babel/parser": "^7.28.4", "@vue/shared": "3.5.22", "entities": "^4.5.0", "estree-walker": "^2.0.2", "source-map-js": "^1.2.1" } }, "sha512-jQ0pFPmZwTEiRNSb+i9Ow/I/cHv2tXYqsnHKKyCQ08irI2kdF5qmYedmF8si8mA7zepUFmJ2hqzS8CQmNOWOkQ=="], @@ -1379,7 +1375,7 @@ "ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], - "ansi-styles": ["ansi-styles@5.2.0", "", {}, "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA=="], + "ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], "any-promise": ["any-promise@1.3.0", "", {}, "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A=="], @@ -1447,7 +1443,7 @@ "base64-js": ["base64-js@1.5.1", "", {}, "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="], - "baseline-browser-mapping": ["baseline-browser-mapping@2.8.15", "", { "bin": { "baseline-browser-mapping": "dist/cli.js" } }, "sha512-qsJ8/X+UypqxHXN75M7dF88jNK37dLBRW7LeUzCPz+TNs37G8cfWy9nWzS+LS//g600zrt2le9KuXt0rWfDz5Q=="], + "baseline-browser-mapping": ["baseline-browser-mapping@2.8.16", "", { "bin": { "baseline-browser-mapping": "dist/cli.js" } }, "sha512-OMu3BGQ4E7P1ErFsIPpbJh0qvDudM/UuJeHgkAvfWe+0HFJCXh+t/l8L6fVLR55RI/UbKrVLnAXZSVwd9ysWYw=="], "better-opn": ["better-opn@3.0.2", "", { "dependencies": { "open": "^8.0.4" } }, "sha512-aVNobHnJqLiUelTaHat9DZ1qM2w0C0Eym4LPI/3JxOnSokGVdsl1T1kN7TFvsEAD8G47A6VKQ0TVHqbBnYMJlQ=="], @@ -1549,9 +1545,9 @@ "cluster-key-slot": ["cluster-key-slot@1.1.2", "", {}, "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA=="], - "color-convert": ["color-convert@1.9.3", "", { "dependencies": { "color-name": "1.1.3" } }, "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg=="], + "color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="], - "color-name": ["color-name@1.1.3", "", {}, "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="], + "color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="], "comma-separated-tokens": ["comma-separated-tokens@2.0.3", "", {}, "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg=="], @@ -1741,8 +1737,6 @@ "diff-sequences": ["diff-sequences@29.6.3", "", {}, "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q=="], - "dijkstrajs": ["dijkstrajs@1.0.3", "", {}, "sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA=="], - "dir-glob": ["dir-glob@3.0.1", "", { "dependencies": { "path-type": "^4.0.0" } }, "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA=="], "dom-accessibility-api": ["dom-accessibility-api@0.5.16", "", {}, "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg=="], @@ -1881,13 +1875,13 @@ "expo-sqlite": ["expo-sqlite@15.2.14", "", { "dependencies": { "await-lock": "^2.2.2" }, "peerDependencies": { "expo": "*", "react": "*", "react-native": "*" } }, "sha512-6tWnEE0fcir30/e7eVwjeC7eKdncfVnIgo2JvnKpRndedyiFMXLMyOQWNVGnuhnSrPV2BHvGGjLByS/j5VgH4w=="], - "exponential-backoff": ["exponential-backoff@3.1.2", "", {}, "sha512-8QxYTVXUkuy7fIIoitQkPwGonB8F3Zj8eEO8Sqg9Zv/bkI7RJAzowee4gr81Hak/dUTpA2Z7VfQgoijjPNlUZA=="], + "exponential-backoff": ["exponential-backoff@3.1.3", "", {}, "sha512-ZgEeZXj30q+I0EN+CbSSpIyPaJ5HVQD18Z1m+u1FXbAeT94mr1zw50q4q6jiiC447Nl/YTcIYSAftiGqetwXCA=="], "exsolve": ["exsolve@1.0.7", "", {}, "sha512-VO5fQUzZtI6C+vx4w/4BWJpg3s/5l+6pRQEHzFRM8WFi4XffSP1Z+4qi7GbjWbvRQEbdIco5mIMq+zX4rPuLrw=="], "ext": ["ext@1.7.0", "", { "dependencies": { "type": "^2.7.2" } }, "sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw=="], - "fake-indexeddb": ["fake-indexeddb@6.2.2", "", {}, "sha512-SGbf7fzjeHz3+12NO1dYigcYn4ivviaeULV5yY5rdGihBvvgwMds4r4UBbNIUMwkze57KTDm32rq3j1Az8mzEw=="], + "fake-indexeddb": ["fake-indexeddb@6.2.3", "", {}, "sha512-idzJXFtDIHNShFZ9ssS8IdsRgAP0t9zwWvSdCKsWK2dgh2xcXA6/2Oteaxar5GJqmwzZXCrKRO6F5IEiR4yJzw=="], "fast-base64-decode": ["fast-base64-decode@1.0.0", "", {}, "sha512-qwaScUgUGBYeDNRnbc/KyllVU88Jk1pRHPStuF/lO7B0/RTRLj7U0lkdTAutlBblY08rwZDff6tNU9cjv6j//Q=="], @@ -1901,14 +1895,10 @@ "fastq": ["fastq@1.19.1", "", { "dependencies": { "reusify": "^1.0.4" } }, "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ=="], - "fb-dotslash": ["fb-dotslash@0.5.8", "", { "bin": { "dotslash": "bin/dotslash" } }, "sha512-XHYLKk9J4BupDxi9bSEhkfss0m+Vr9ChTrjhf9l2iw3jB5C7BnY4GVPoMcqbrTutsKJso6yj2nAB6BI/F2oZaA=="], - "fb-watchman": ["fb-watchman@2.0.2", "", { "dependencies": { "bser": "2.1.1" } }, "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA=="], "fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="], - "fflate": ["fflate@0.8.2", "", {}, "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A=="], - "file-entry-cache": ["file-entry-cache@8.0.0", "", { "dependencies": { "flat-cache": "^4.0.0" } }, "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ=="], "file-uri-to-path": ["file-uri-to-path@1.0.0", "", {}, "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw=="], @@ -2017,8 +2007,6 @@ "hast-util-whitespace": ["hast-util-whitespace@3.0.0", "", { "dependencies": { "@types/hast": "^3.0.0" } }, "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw=="], - "hermes-compiler": ["hermes-compiler@0.0.0", "", {}, "sha512-boVFutx6ME/Km2mB6vvsQcdnazEYYI/jV1pomx1wcFUG/EVqTkr5CU0CW9bKipOA/8Hyu3NYwW3THg2Q1kNCfA=="], - "hermes-estree": ["hermes-estree@0.28.1", "", {}, "sha512-w3nxl/RGM7LBae0v8LH2o36+8VqwOZGv9rX1wyoWT6YaKZLqpJZ0YQ5P0LVr3tuRpf7vCx0iIG4i/VmBJejxTQ=="], "hermes-parser": ["hermes-parser@0.28.1", "", { "dependencies": { "hermes-estree": "0.28.1" } }, "sha512-nf8o+hE8g7UJWParnccljHumE9Vlq8F7MqIdeahl+4x0tvCUJYRrT0L7h0MMg/X9YJmkNwsfbaNNrzPtFXOscg=="], @@ -2065,7 +2053,7 @@ "internal-slot": ["internal-slot@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "hasown": "^2.0.2", "side-channel": "^1.1.0" } }, "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw=="], - "internmap": ["internmap@2.0.3", "", {}, "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg=="], + "internmap": ["internmap@1.0.1", "", {}, "sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw=="], "invariant": ["invariant@2.2.4", "", { "dependencies": { "loose-envify": "^1.0.0" } }, "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA=="], @@ -2209,8 +2197,6 @@ "jimp-compact": ["jimp-compact@0.16.1", "", {}, "sha512-dZ6Ra7u1G8c4Letq/B5EzAxj4tLFHL+cGtdpR+PVm4yzPDj+lCk+AbivWt1eOM+ikzkowtyV7qSqX6qr3t71Ww=="], - "jiti": ["jiti@2.6.1", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ=="], - "joycon": ["joycon@3.1.1", "", {}, "sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw=="], "js-base64": ["js-base64@3.7.8", "", {}, "sha512-hNngCeKxIUQiEUN3GPJOkz4wF/YvdUdbNL9hsBcMQTkKzboD7T/q3OYOuuPZLUE6dBxSGpwhk5mwuDud7JVAow=="], @@ -2379,33 +2365,33 @@ "mermaid": ["mermaid@11.12.0", "", { "dependencies": { "@braintree/sanitize-url": "^7.1.1", "@iconify/utils": "^3.0.1", "@mermaid-js/parser": "^0.6.2", "@types/d3": "^7.4.3", "cytoscape": "^3.29.3", "cytoscape-cose-bilkent": "^4.1.0", "cytoscape-fcose": "^2.2.0", "d3": "^7.9.0", "d3-sankey": "^0.12.3", "dagre-d3-es": "7.0.11", "dayjs": "^1.11.18", "dompurify": "^3.2.5", "katex": "^0.16.22", "khroma": "^2.1.0", "lodash-es": "^4.17.21", "marked": "^16.2.1", "roughjs": "^4.6.6", "stylis": "^4.3.6", "ts-dedent": "^2.2.0", "uuid": "^11.1.0" } }, "sha512-ZudVx73BwrMJfCFmSSJT84y6u5brEoV8DOItdHomNLz32uBjNrelm7mg95X7g+C6UoQH/W6mBLGDEDv73JdxBg=="], - "metro": ["metro@0.83.3", "", { "dependencies": { "@babel/code-frame": "^7.24.7", "@babel/core": "^7.25.2", "@babel/generator": "^7.25.0", "@babel/parser": "^7.25.3", "@babel/template": "^7.25.0", "@babel/traverse": "^7.25.3", "@babel/types": "^7.25.2", "accepts": "^1.3.7", "chalk": "^4.0.0", "ci-info": "^2.0.0", "connect": "^3.6.5", "debug": "^4.4.0", "error-stack-parser": "^2.0.6", "flow-enums-runtime": "^0.0.6", "graceful-fs": "^4.2.4", "hermes-parser": "0.32.0", "image-size": "^1.0.2", "invariant": "^2.2.4", "jest-worker": "^29.7.0", "jsc-safe-url": "^0.2.2", "lodash.throttle": "^4.1.1", "metro-babel-transformer": "0.83.3", "metro-cache": "0.83.3", "metro-cache-key": "0.83.3", "metro-config": "0.83.3", "metro-core": "0.83.3", "metro-file-map": "0.83.3", "metro-resolver": "0.83.3", "metro-runtime": "0.83.3", "metro-source-map": "0.83.3", "metro-symbolicate": "0.83.3", "metro-transform-plugins": "0.83.3", "metro-transform-worker": "0.83.3", "mime-types": "^2.1.27", "nullthrows": "^1.1.1", "serialize-error": "^2.1.0", "source-map": "^0.5.6", "throat": "^5.0.0", "ws": "^7.5.10", "yargs": "^17.6.2" }, "bin": { "metro": "src/cli.js" } }, "sha512-+rP+/GieOzkt97hSJ0MrPOuAH/jpaS21ZDvL9DJ35QYRDlQcwzcvUlGUf79AnQxq/2NPiS/AULhhM4TKutIt8Q=="], + "metro": ["metro@0.82.5", "", { "dependencies": { "@babel/code-frame": "^7.24.7", "@babel/core": "^7.25.2", "@babel/generator": "^7.25.0", "@babel/parser": "^7.25.3", "@babel/template": "^7.25.0", "@babel/traverse": "^7.25.3", "@babel/types": "^7.25.2", "accepts": "^1.3.7", "chalk": "^4.0.0", "ci-info": "^2.0.0", "connect": "^3.6.5", "debug": "^4.4.0", "error-stack-parser": "^2.0.6", "flow-enums-runtime": "^0.0.6", "graceful-fs": "^4.2.4", "hermes-parser": "0.29.1", "image-size": "^1.0.2", "invariant": "^2.2.4", "jest-worker": "^29.7.0", "jsc-safe-url": "^0.2.2", "lodash.throttle": "^4.1.1", "metro-babel-transformer": "0.82.5", "metro-cache": "0.82.5", "metro-cache-key": "0.82.5", "metro-config": "0.82.5", "metro-core": "0.82.5", "metro-file-map": "0.82.5", "metro-resolver": "0.82.5", "metro-runtime": "0.82.5", "metro-source-map": "0.82.5", "metro-symbolicate": "0.82.5", "metro-transform-plugins": "0.82.5", "metro-transform-worker": "0.82.5", "mime-types": "^2.1.27", "nullthrows": "^1.1.1", "serialize-error": "^2.1.0", "source-map": "^0.5.6", "throat": "^5.0.0", "ws": "^7.5.10", "yargs": "^17.6.2" }, "bin": { "metro": "src/cli.js" } }, "sha512-8oAXxL7do8QckID/WZEKaIFuQJFUTLzfVcC48ghkHhNK2RGuQq8Xvf4AVd+TUA0SZtX0q8TGNXZ/eba1ckeGCg=="], - "metro-babel-transformer": ["metro-babel-transformer@0.83.3", "", { "dependencies": { "@babel/core": "^7.25.2", "flow-enums-runtime": "^0.0.6", "hermes-parser": "0.32.0", "nullthrows": "^1.1.1" } }, "sha512-1vxlvj2yY24ES1O5RsSIvg4a4WeL7PFXgKOHvXTXiW0deLvQr28ExXj6LjwCCDZ4YZLhq6HddLpZnX4dEdSq5g=="], + "metro-babel-transformer": ["metro-babel-transformer@0.82.5", "", { "dependencies": { "@babel/core": "^7.25.2", "flow-enums-runtime": "^0.0.6", "hermes-parser": "0.29.1", "nullthrows": "^1.1.1" } }, "sha512-W/scFDnwJXSccJYnOFdGiYr9srhbHPdxX9TvvACOFsIXdLilh3XuxQl/wXW6jEJfgIb0jTvoTlwwrqvuwymr6Q=="], - "metro-cache": ["metro-cache@0.83.3", "", { "dependencies": { "exponential-backoff": "^3.1.1", "flow-enums-runtime": "^0.0.6", "https-proxy-agent": "^7.0.5", "metro-core": "0.83.3" } }, "sha512-3jo65X515mQJvKqK3vWRblxDEcgY55Sk3w4xa6LlfEXgQ9g1WgMh9m4qVZVwgcHoLy0a2HENTPCCX4Pk6s8c8Q=="], + "metro-cache": ["metro-cache@0.82.5", "", { "dependencies": { "exponential-backoff": "^3.1.1", "flow-enums-runtime": "^0.0.6", "https-proxy-agent": "^7.0.5", "metro-core": "0.82.5" } }, "sha512-AwHV9607xZpedu1NQcjUkua8v7HfOTKfftl6Vc9OGr/jbpiJX6Gpy8E/V9jo/U9UuVYX2PqSUcVNZmu+LTm71Q=="], - "metro-cache-key": ["metro-cache-key@0.83.3", "", { "dependencies": { "flow-enums-runtime": "^0.0.6" } }, "sha512-59ZO049jKzSmvBmG/B5bZ6/dztP0ilp0o988nc6dpaDsU05Cl1c/lRf+yx8m9WW/JVgbmfO5MziBU559XjI5Zw=="], + "metro-cache-key": ["metro-cache-key@0.82.5", "", { "dependencies": { "flow-enums-runtime": "^0.0.6" } }, "sha512-qpVmPbDJuRLrT4kcGlUouyqLGssJnbTllVtvIgXfR7ZuzMKf0mGS+8WzcqzNK8+kCyakombQWR0uDd8qhWGJcA=="], - "metro-config": ["metro-config@0.83.3", "", { "dependencies": { "connect": "^3.6.5", "flow-enums-runtime": "^0.0.6", "jest-validate": "^29.7.0", "metro": "0.83.3", "metro-cache": "0.83.3", "metro-core": "0.83.3", "metro-runtime": "0.83.3", "yaml": "^2.6.1" } }, "sha512-mTel7ipT0yNjKILIan04bkJkuCzUUkm2SeEaTads8VfEecCh+ltXchdq6DovXJqzQAXuR2P9cxZB47Lg4klriA=="], + "metro-config": ["metro-config@0.82.5", "", { "dependencies": { "connect": "^3.6.5", "cosmiconfig": "^5.0.5", "flow-enums-runtime": "^0.0.6", "jest-validate": "^29.7.0", "metro": "0.82.5", "metro-cache": "0.82.5", "metro-core": "0.82.5", "metro-runtime": "0.82.5" } }, "sha512-/r83VqE55l0WsBf8IhNmc/3z71y2zIPe5kRSuqA5tY/SL/ULzlHUJEMd1szztd0G45JozLwjvrhAzhDPJ/Qo/g=="], - "metro-core": ["metro-core@0.83.3", "", { "dependencies": { "flow-enums-runtime": "^0.0.6", "lodash.throttle": "^4.1.1", "metro-resolver": "0.83.3" } }, "sha512-M+X59lm7oBmJZamc96usuF1kusd5YimqG/q97g4Ac7slnJ3YiGglW5CsOlicTR5EWf8MQFxxjDoB6ytTqRe8Hw=="], + "metro-core": ["metro-core@0.82.5", "", { "dependencies": { "flow-enums-runtime": "^0.0.6", "lodash.throttle": "^4.1.1", "metro-resolver": "0.82.5" } }, "sha512-OJL18VbSw2RgtBm1f2P3J5kb892LCVJqMvslXxuxjAPex8OH7Eb8RBfgEo7VZSjgb/LOf4jhC4UFk5l5tAOHHA=="], - "metro-file-map": ["metro-file-map@0.83.3", "", { "dependencies": { "debug": "^4.4.0", "fb-watchman": "^2.0.0", "flow-enums-runtime": "^0.0.6", "graceful-fs": "^4.2.4", "invariant": "^2.2.4", "jest-worker": "^29.7.0", "micromatch": "^4.0.4", "nullthrows": "^1.1.1", "walker": "^1.0.7" } }, "sha512-jg5AcyE0Q9Xbbu/4NAwwZkmQn7doJCKGW0SLeSJmzNB9Z24jBe0AL2PHNMy4eu0JiKtNWHz9IiONGZWq7hjVTA=="], + "metro-file-map": ["metro-file-map@0.82.5", "", { "dependencies": { "debug": "^4.4.0", "fb-watchman": "^2.0.0", "flow-enums-runtime": "^0.0.6", "graceful-fs": "^4.2.4", "invariant": "^2.2.4", "jest-worker": "^29.7.0", "micromatch": "^4.0.4", "nullthrows": "^1.1.1", "walker": "^1.0.7" } }, "sha512-vpMDxkGIB+MTN8Af5hvSAanc6zXQipsAUO+XUx3PCQieKUfLwdoa8qaZ1WAQYRpaU+CJ8vhBcxtzzo3d9IsCIQ=="], - "metro-minify-terser": ["metro-minify-terser@0.83.3", "", { "dependencies": { "flow-enums-runtime": "^0.0.6", "terser": "^5.15.0" } }, "sha512-O2BmfWj6FSfzBLrNCXt/rr2VYZdX5i6444QJU0fFoc7Ljg+Q+iqebwE3K0eTvkI6TRjELsXk1cjU+fXwAR4OjQ=="], + "metro-minify-terser": ["metro-minify-terser@0.82.5", "", { "dependencies": { "flow-enums-runtime": "^0.0.6", "terser": "^5.15.0" } }, "sha512-v6Nx7A4We6PqPu/ta1oGTqJ4Usz0P7c+3XNeBxW9kp8zayS3lHUKR0sY0wsCHInxZlNAEICx791x+uXytFUuwg=="], - "metro-resolver": ["metro-resolver@0.83.3", "", { "dependencies": { "flow-enums-runtime": "^0.0.6" } }, "sha512-0js+zwI5flFxb1ktmR///bxHYg7OLpRpWZlBBruYG8OKYxeMP7SV0xQ/o/hUelrEMdK4LJzqVtHAhBm25LVfAQ=="], + "metro-resolver": ["metro-resolver@0.82.5", "", { "dependencies": { "flow-enums-runtime": "^0.0.6" } }, "sha512-kFowLnWACt3bEsuVsaRNgwplT8U7kETnaFHaZePlARz4Fg8tZtmRDUmjaD68CGAwc0rwdwNCkWizLYpnyVcs2g=="], - "metro-runtime": ["metro-runtime@0.83.3", "", { "dependencies": { "@babel/runtime": "^7.25.0", "flow-enums-runtime": "^0.0.6" } }, "sha512-JHCJb9ebr9rfJ+LcssFYA2x1qPYuSD/bbePupIGhpMrsla7RCwC/VL3yJ9cSU+nUhU4c9Ixxy8tBta+JbDeZWw=="], + "metro-runtime": ["metro-runtime@0.82.5", "", { "dependencies": { "@babel/runtime": "^7.25.0", "flow-enums-runtime": "^0.0.6" } }, "sha512-rQZDoCUf7k4Broyw3Ixxlq5ieIPiR1ULONdpcYpbJQ6yQ5GGEyYjtkztGD+OhHlw81LCR2SUAoPvtTus2WDK5g=="], - "metro-source-map": ["metro-source-map@0.83.3", "", { "dependencies": { "@babel/traverse": "^7.25.3", "@babel/traverse--for-generate-function-map": "npm:@babel/traverse@^7.25.3", "@babel/types": "^7.25.2", "flow-enums-runtime": "^0.0.6", "invariant": "^2.2.4", "metro-symbolicate": "0.83.3", "nullthrows": "^1.1.1", "ob1": "0.83.3", "source-map": "^0.5.6", "vlq": "^1.0.0" } }, "sha512-xkC3qwUBh2psVZgVavo8+r2C9Igkk3DibiOXSAht1aYRRcztEZNFtAMtfSB7sdO2iFMx2Mlyu++cBxz/fhdzQg=="], + "metro-source-map": ["metro-source-map@0.82.5", "", { "dependencies": { "@babel/traverse": "^7.25.3", "@babel/traverse--for-generate-function-map": "npm:@babel/traverse@^7.25.3", "@babel/types": "^7.25.2", "flow-enums-runtime": "^0.0.6", "invariant": "^2.2.4", "metro-symbolicate": "0.82.5", "nullthrows": "^1.1.1", "ob1": "0.82.5", "source-map": "^0.5.6", "vlq": "^1.0.0" } }, "sha512-wH+awTOQJVkbhn2SKyaw+0cd+RVSCZ3sHVgyqJFQXIee/yLs3dZqKjjeKKhhVeudgjXo7aE/vSu/zVfcQEcUfw=="], - "metro-symbolicate": ["metro-symbolicate@0.83.3", "", { "dependencies": { "flow-enums-runtime": "^0.0.6", "invariant": "^2.2.4", "metro-source-map": "0.83.3", "nullthrows": "^1.1.1", "source-map": "^0.5.6", "vlq": "^1.0.0" }, "bin": { "metro-symbolicate": "src/index.js" } }, "sha512-F/YChgKd6KbFK3eUR5HdUsfBqVsanf5lNTwFd4Ca7uuxnHgBC3kR/Hba/RGkenR3pZaGNp5Bu9ZqqP52Wyhomw=="], + "metro-symbolicate": ["metro-symbolicate@0.82.5", "", { "dependencies": { "flow-enums-runtime": "^0.0.6", "invariant": "^2.2.4", "metro-source-map": "0.82.5", "nullthrows": "^1.1.1", "source-map": "^0.5.6", "vlq": "^1.0.0" }, "bin": { "metro-symbolicate": "src/index.js" } }, "sha512-1u+07gzrvYDJ/oNXuOG1EXSvXZka/0JSW1q2EYBWerVKMOhvv9JzDGyzmuV7hHbF2Hg3T3S2uiM36sLz1qKsiw=="], - "metro-transform-plugins": ["metro-transform-plugins@0.83.3", "", { "dependencies": { "@babel/core": "^7.25.2", "@babel/generator": "^7.25.0", "@babel/template": "^7.25.0", "@babel/traverse": "^7.25.3", "flow-enums-runtime": "^0.0.6", "nullthrows": "^1.1.1" } }, "sha512-eRGoKJU6jmqOakBMH5kUB7VitEWiNrDzBHpYbkBXW7C5fUGeOd2CyqrosEzbMK5VMiZYyOcNFEphvxk3OXey2A=="], + "metro-transform-plugins": ["metro-transform-plugins@0.82.5", "", { "dependencies": { "@babel/core": "^7.25.2", "@babel/generator": "^7.25.0", "@babel/template": "^7.25.0", "@babel/traverse": "^7.25.3", "flow-enums-runtime": "^0.0.6", "nullthrows": "^1.1.1" } }, "sha512-57Bqf3rgq9nPqLrT2d9kf/2WVieTFqsQ6qWHpEng5naIUtc/Iiw9+0bfLLWSAw0GH40iJ4yMjFcFJDtNSYynMA=="], - "metro-transform-worker": ["metro-transform-worker@0.83.3", "", { "dependencies": { "@babel/core": "^7.25.2", "@babel/generator": "^7.25.0", "@babel/parser": "^7.25.3", "@babel/types": "^7.25.2", "flow-enums-runtime": "^0.0.6", "metro": "0.83.3", "metro-babel-transformer": "0.83.3", "metro-cache": "0.83.3", "metro-cache-key": "0.83.3", "metro-minify-terser": "0.83.3", "metro-source-map": "0.83.3", "metro-transform-plugins": "0.83.3", "nullthrows": "^1.1.1" } }, "sha512-Ztekew9t/gOIMZX1tvJOgX7KlSLL5kWykl0Iwu2cL2vKMKVALRl1hysyhUw0vjpAvLFx+Kfq9VLjnHIkW32fPA=="], + "metro-transform-worker": ["metro-transform-worker@0.82.5", "", { "dependencies": { "@babel/core": "^7.25.2", "@babel/generator": "^7.25.0", "@babel/parser": "^7.25.3", "@babel/types": "^7.25.2", "flow-enums-runtime": "^0.0.6", "metro": "0.82.5", "metro-babel-transformer": "0.82.5", "metro-cache": "0.82.5", "metro-cache-key": "0.82.5", "metro-minify-terser": "0.82.5", "metro-source-map": "0.82.5", "metro-transform-plugins": "0.82.5", "nullthrows": "^1.1.1" } }, "sha512-mx0grhAX7xe+XUQH6qoHHlWedI8fhSpDGsfga7CpkO9Lk9W+aPitNtJWNGrW8PfjKEWbT9Uz9O50dkI8bJqigw=="], "micromark-util-character": ["micromark-util-character@2.1.1", "", { "dependencies": { "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q=="], @@ -2511,7 +2497,7 @@ "nwsapi": ["nwsapi@2.2.22", "", {}, "sha512-ujSMe1OWVn55euT1ihwCI1ZcAaAU3nxUiDwfDQldc51ZXaB9m2AyOn6/jh1BLe2t/G8xd6uKG1UBF2aZJeg2SQ=="], - "ob1": ["ob1@0.83.3", "", { "dependencies": { "flow-enums-runtime": "^0.0.6" } }, "sha512-egUxXCDwoWG06NGCS5s5AdcpnumHKJlfd3HH06P3m9TEMwwScfcY35wpQxbm9oHof+dM/lVH9Rfyu1elTVelSA=="], + "ob1": ["ob1@0.82.5", "", { "dependencies": { "flow-enums-runtime": "^0.0.6" } }, "sha512-QyQQ6e66f+Ut/qUVjEce0E/wux5nAGLXYZDn1jr15JWstHsCH3l6VVrg8NKDptW9NEiBXKOJeGF/ydxeSDF3IQ=="], "object-assign": ["object-assign@4.1.1", "", {}, "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="], @@ -2611,7 +2597,7 @@ "plur": ["plur@4.0.0", "", { "dependencies": { "irregular-plurals": "^3.2.0" } }, "sha512-4UGewrYgqDFw9vV6zNV+ADmPAUAfJPKtGvb/VdpQAx25X5f3xXdGdyOEVFwkl8Hl/tl7+xbeHqSEM+D5/TirUg=="], - "pngjs": ["pngjs@5.0.0", "", {}, "sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw=="], + "pngjs": ["pngjs@3.4.0", "", {}, "sha512-NCrCHhWmnQklfH4MtJMRjZ2a8c80qXeMlQMv2uVp9ISJMTt562SbGd6n2oq0PaPgKm7Z6pL9E2UlLIhC+SHL3w=="], "points-on-curve": ["points-on-curve@0.2.0", "", {}, "sha512-0mYKnYYe9ZcqMCWhUjItv/oHjvgEsfKvnUTg8sAtnHr3GVy7rGkXCb6d5cSyqrWqL4k81b9CPg3urd+T7aop3A=="], @@ -2697,8 +2683,6 @@ "punycode.js": ["punycode.js@2.3.1", "", {}, "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA=="], - "qrcode": ["qrcode@1.5.4", "", { "dependencies": { "dijkstrajs": "^1.0.1", "pngjs": "^5.0.0", "yargs": "^15.3.1" }, "bin": { "qrcode": "bin/qrcode" } }, "sha512-1ca71Zgiu6ORjHqFBDpnSMTR2ReToX4l1Au1VFLyVeBTFavzQnv5JxMFr3ukHVKpSrSA2MCk0lNJSykjUfz7Zg=="], - "qrcode-terminal": ["qrcode-terminal@0.11.0", "", { "bin": { "qrcode-terminal": "./bin/qrcode-terminal.js" } }, "sha512-Uu7ii+FQy4Qf82G4xu7ShHhjhGahEpCWc3x8UavY3CTcWV+ufmmCtwkr7ZKsX42jdL0kr1B5FKUeqJvAn51jzQ=="], "quansync": ["quansync@0.2.11", "", {}, "sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA=="], @@ -2721,7 +2705,7 @@ "react-is": ["react-is@18.3.1", "", {}, "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg=="], - "react-native": ["react-native@0.82.0", "", { "dependencies": { "@jest/create-cache-key-function": "^29.7.0", "@react-native/assets-registry": "0.82.0", "@react-native/codegen": "0.82.0", "@react-native/community-cli-plugin": "0.82.0", "@react-native/gradle-plugin": "0.82.0", "@react-native/js-polyfills": "0.82.0", "@react-native/normalize-colors": "0.82.0", "@react-native/virtualized-lists": "0.82.0", "abort-controller": "^3.0.0", "anser": "^1.4.9", "ansi-regex": "^5.0.0", "babel-jest": "^29.7.0", "babel-plugin-syntax-hermes-parser": "0.32.0", "base64-js": "^1.5.1", "commander": "^12.0.0", "flow-enums-runtime": "^0.0.6", "glob": "^7.1.1", "hermes-compiler": "0.0.0", "invariant": "^2.2.4", "jest-environment-node": "^29.7.0", "memoize-one": "^5.0.0", "metro-runtime": "^0.83.1", "metro-source-map": "^0.83.1", "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.26.0", "semver": "^7.1.3", "stacktrace-parser": "^0.1.10", "whatwg-fetch": "^3.0.0", "ws": "^6.2.3", "yargs": "^17.6.2" }, "peerDependencies": { "@types/react": "^19.1.1", "react": "^19.1.1" }, "optionalPeers": ["@types/react"], "bin": { "react-native": "cli.js" } }, "sha512-E+sBFDgpwzoZzPn86gSGRBGLnS9Q6r4y6Xk5I57/QbkqkDOxmQb/bzQq/oCdUCdHImKiow2ldC3WJfnvAKIfzg=="], + "react-native": ["react-native@0.79.2", "", { "dependencies": { "@jest/create-cache-key-function": "^29.7.0", "@react-native/assets-registry": "0.79.2", "@react-native/codegen": "0.79.2", "@react-native/community-cli-plugin": "0.79.2", "@react-native/gradle-plugin": "0.79.2", "@react-native/js-polyfills": "0.79.2", "@react-native/normalize-colors": "0.79.2", "@react-native/virtualized-lists": "0.79.2", "abort-controller": "^3.0.0", "anser": "^1.4.9", "ansi-regex": "^5.0.0", "babel-jest": "^29.7.0", "babel-plugin-syntax-hermes-parser": "0.25.1", "base64-js": "^1.5.1", "chalk": "^4.0.0", "commander": "^12.0.0", "event-target-shim": "^5.0.1", "flow-enums-runtime": "^0.0.6", "glob": "^7.1.1", "invariant": "^2.2.4", "jest-environment-node": "^29.7.0", "memoize-one": "^5.0.0", "metro-runtime": "^0.82.0", "metro-source-map": "^0.82.0", "nullthrows": "^1.1.1", "pretty-format": "^29.7.0", "promise": "^8.3.0", "react-devtools-core": "^6.1.1", "react-refresh": "^0.14.0", "regenerator-runtime": "^0.13.2", "scheduler": "0.25.0", "semver": "^7.1.3", "stacktrace-parser": "^0.1.10", "whatwg-fetch": "^3.0.0", "ws": "^6.2.3", "yargs": "^17.6.2" }, "peerDependencies": { "@types/react": "^19.0.0", "react": "^19.0.0" }, "optionalPeers": ["@types/react"], "bin": { "react-native": "cli.js" } }, "sha512-AnGzb56JvU5YCL7cAwg10+ewDquzvmgrMddiBM0GAWLwQM/6DJfGd2ZKrMuKKehHerpDDZgG+EY64gk3x3dEkw=="], "react-native-builder-bob": ["react-native-builder-bob@0.40.13", "", { "dependencies": { "@babel/core": "^7.25.2", "@babel/plugin-transform-flow-strip-types": "^7.26.5", "@babel/plugin-transform-strict-mode": "^7.24.7", "@babel/preset-env": "^7.25.2", "@babel/preset-react": "^7.24.7", "@babel/preset-typescript": "^7.24.7", "arktype": "^2.1.15", "babel-plugin-syntax-hermes-parser": "^0.28.0", "browserslist": "^4.20.4", "cross-spawn": "^7.0.3", "dedent": "^0.7.0", "del": "^6.1.1", "escape-string-regexp": "^4.0.0", "fs-extra": "^10.1.0", "glob": "^8.0.3", "is-git-dirty": "^2.0.1", "json5": "^2.2.1", "kleur": "^4.1.4", "prompts": "^2.4.2", "react-native-monorepo-config": "^0.1.8", "which": "^2.0.2", "yargs": "^17.5.1" }, "bin": { "bob": "bin/bob" } }, "sha512-CtucAJ5PMLH3GPNlg3TB5rb3UPot6VjkD9T8Uhz/AAWit/DmWll0zG33ZZeka69E2569saAjShDz3IKAoYGFtA=="], @@ -2781,8 +2765,6 @@ "require-from-string": ["require-from-string@2.0.2", "", {}, "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw=="], - "require-main-filename": ["require-main-filename@2.0.0", "", {}, "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg=="], - "requireg": ["requireg@0.2.2", "", { "dependencies": { "nested-error-stacks": "~2.0.1", "rc": "~1.2.7", "resolve": "~1.7.1" } }, "sha512-nYzyjnFcPNGR3lx9lwPPPnuQxv6JWEZd2Ci0u9opN7N5zUEPIhY/GbL3vMGOr2UXwEg9WwSyV9X9Y/kLFgPsOg=="], "resolve": ["resolve@1.22.10", "", { "dependencies": { "is-core-module": "^2.16.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w=="], @@ -2831,7 +2813,7 @@ "saxes": ["saxes@6.0.0", "", { "dependencies": { "xmlchars": "^2.2.0" } }, "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA=="], - "scheduler": ["scheduler@0.26.0", "", {}, "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA=="], + "scheduler": ["scheduler@0.25.0", "", {}, "sha512-xFVuu11jh+xcO7JOAGJNOXld8/TcEHK/4CituBUeUb5hqxJLj9YuemAEuvm9gQ/+pgXYfbQuqAkiYu+u7YEsNA=="], "scule": ["scule@1.3.0", "", {}, "sha512-6FtHJEvt+pVMIB9IBY+IcCJ6Z5f1iQnytgyfKMhDKgmzYG+TeH/wx1y3l27rshSbLiSanrR9ffZDrEsmjlQF2g=="], @@ -2845,8 +2827,6 @@ "serve-static": ["serve-static@1.16.2", "", { "dependencies": { "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "parseurl": "~1.3.3", "send": "0.19.0" } }, "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw=="], - "set-blocking": ["set-blocking@2.0.0", "", {}, "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw=="], - "set-cookie-parser": ["set-cookie-parser@2.7.1", "", {}, "sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ=="], "set-function-length": ["set-function-length@1.2.2", "", { "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", "function-bind": "^1.1.2", "get-intrinsic": "^1.2.4", "gopd": "^1.0.1", "has-property-descriptors": "^1.0.2" } }, "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg=="], @@ -3103,7 +3083,7 @@ "typedarray-to-buffer": ["typedarray-to-buffer@3.1.5", "", { "dependencies": { "is-typedarray": "^1.0.0" } }, "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q=="], - "typedoc": ["typedoc@0.28.13", "", { "dependencies": { "@gerrit0/mini-shiki": "^3.12.0", "lunr": "^2.3.9", "markdown-it": "^14.1.0", "minimatch": "^9.0.5", "yaml": "^2.8.1" }, "peerDependencies": { "typescript": "5.0.x || 5.1.x || 5.2.x || 5.3.x || 5.4.x || 5.5.x || 5.6.x || 5.7.x || 5.8.x || 5.9.x" }, "bin": { "typedoc": "bin/typedoc" } }, "sha512-dNWY8msnYB2a+7Audha+aTF1Pu3euiE7ySp53w8kEsXoYw7dMouV5A1UsTUY345aB152RHnmRMDiovuBi7BD+w=="], + "typedoc": ["typedoc@0.28.14", "", { "dependencies": { "@gerrit0/mini-shiki": "^3.12.0", "lunr": "^2.3.9", "markdown-it": "^14.1.0", "minimatch": "^9.0.5", "yaml": "^2.8.1" }, "peerDependencies": { "typescript": "5.0.x || 5.1.x || 5.2.x || 5.3.x || 5.4.x || 5.5.x || 5.6.x || 5.7.x || 5.8.x || 5.9.x" }, "bin": { "typedoc": "bin/typedoc" } }, "sha512-ftJYPvpVfQvFzpkoSfHLkJybdA/geDJ8BGQt/ZnkkhnBYoYW6lBgPQXu6vqLxO4X75dA55hX8Af847H5KXlEFA=="], "typedoc-plugin-markdown": ["typedoc-plugin-markdown@4.9.0", "", { "peerDependencies": { "typedoc": "0.28.x" } }, "sha512-9Uu4WR9L7ZBgAl60N/h+jqmPxxvnC9nQAlnnO/OujtG2ubjnKTVUFY1XDhcMY+pCqlX3N2HsQM2QTYZIU9tJuw=="], @@ -3239,8 +3219,6 @@ "which-collection": ["which-collection@1.0.2", "", { "dependencies": { "is-map": "^2.0.3", "is-set": "^2.0.3", "is-weakmap": "^2.0.2", "is-weakset": "^2.0.3" } }, "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw=="], - "which-module": ["which-module@2.0.1", "", {}, "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ=="], - "which-typed-array": ["which-typed-array@1.1.19", "", { "dependencies": { "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.8", "call-bound": "^1.0.4", "for-each": "^0.3.5", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-tostringtag": "^1.0.2" } }, "sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw=="], "why-is-node-running": ["why-is-node-running@2.3.0", "", { "dependencies": { "siginfo": "^2.0.0", "stackback": "0.0.2" }, "bin": { "why-is-node-running": "cli.js" } }, "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w=="], @@ -3413,9 +3391,11 @@ "@react-native/codegen/glob": ["glob@7.2.3", "", { "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } }, "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q=="], - "@react-native/codegen/hermes-parser": ["hermes-parser@0.32.0", "", { "dependencies": { "hermes-estree": "0.32.0" } }, "sha512-g4nBOWFpuiTqjR3LZdRxKUkij9iyveWeuks7INEsMX741f3r9xxrOe8TeQfUxtda0eXmiIFiMQzoeSQEno33Hw=="], + "@react-native/codegen/hermes-parser": ["hermes-parser@0.25.1", "", { "dependencies": { "hermes-estree": "0.25.1" } }, "sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA=="], - "@react-native/community-cli-plugin/@react-native/dev-middleware": ["@react-native/dev-middleware@0.82.0", "", { "dependencies": { "@isaacs/ttlcache": "^1.4.1", "@react-native/debugger-frontend": "0.82.0", "@react-native/debugger-shell": "0.82.0", "chrome-launcher": "^0.15.2", "chromium-edge-launcher": "^0.2.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": "^6.2.3" } }, "sha512-SHvpo89RSzH06yZCmY3Xwr1J82EdUljC2lcO4YvXfHmytFG453Nz6kyZQcDEqGCfWDjznIUFUyT2UcLErmRWQg=="], + "@react-native/community-cli-plugin/@react-native/dev-middleware": ["@react-native/dev-middleware@0.79.2", "", { "dependencies": { "@isaacs/ttlcache": "^1.4.1", "@react-native/debugger-frontend": "0.79.2", "chrome-launcher": "^0.15.2", "chromium-edge-launcher": "^0.2.0", "connect": "^3.6.5", "debug": "^2.2.0", "invariant": "^2.2.4", "nullthrows": "^1.1.1", "open": "^7.0.3", "serve-static": "^1.16.2", "ws": "^6.2.3" } }, "sha512-9q4CpkklsAs1L0Bw8XYCoqqyBSrfRALGEw4/r0EkR38Y/6fVfNfdsjSns0pTLO6h0VpxswK34L/hm4uK3MoLHw=="], + + "@react-native/community-cli-plugin/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="], "@react-native/dev-middleware/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="], @@ -3475,12 +3455,6 @@ "@vitest/mocker/@vitest/spy": ["@vitest/spy@3.2.4", "", { "dependencies": { "tinyspy": "^4.0.3" } }, "sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw=="], - "@vitest/ui/@vitest/utils": ["@vitest/utils@3.2.4", "", { "dependencies": { "@vitest/pretty-format": "3.2.4", "loupe": "^3.1.4", "tinyrainbow": "^2.0.0" } }, "sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA=="], - - "@vitest/ui/pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="], - - "@vitest/ui/vitest": ["vitest@3.2.4", "", { "dependencies": { "@types/chai": "^5.2.2", "@vitest/expect": "3.2.4", "@vitest/mocker": "3.2.4", "@vitest/pretty-format": "^3.2.4", "@vitest/runner": "3.2.4", "@vitest/snapshot": "3.2.4", "@vitest/spy": "3.2.4", "@vitest/utils": "3.2.4", "chai": "^5.2.0", "debug": "^4.4.1", "expect-type": "^1.2.1", "magic-string": "^0.30.17", "pathe": "^2.0.3", "picomatch": "^4.0.2", "std-env": "^3.9.0", "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", "why-is-node-running": "^2.3.0" }, "peerDependencies": { "@edge-runtime/vm": "*", "@types/debug": "^4.1.12", "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "@vitest/browser": "3.2.4", "@vitest/ui": "3.2.4", "happy-dom": "*", "jsdom": "*" }, "optionalPeers": ["@edge-runtime/vm", "@types/debug", "@types/node", "@vitest/browser", "@vitest/ui", "happy-dom", "jsdom"], "bin": { "vitest": "vitest.mjs" } }, "sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A=="], - "@vue/compiler-core/estree-walker": ["estree-walker@2.0.2", "", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="], "@vue/compiler-sfc/estree-walker": ["estree-walker@2.0.2", "", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="], @@ -3501,8 +3475,6 @@ "camelcase-keys/camelcase": ["camelcase@5.3.1", "", {}, "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg=="], - "chalk/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], - "chrome-launcher/@types/node": ["@types/node@24.7.1", "", { "dependencies": { "undici-types": "~7.14.0" } }, "sha512-CmyhGZanP88uuC5GpWU9q+fI61j2SkhO3UGMUdfYRE6Bcy0ccyzn1Rqj9YAB/ZY4kOXmNf0ocah5GtphmLMP6Q=="], "chromium-edge-launcher/@types/node": ["@types/node@24.7.1", "", { "dependencies": { "undici-types": "~7.14.0" } }, "sha512-CmyhGZanP88uuC5GpWU9q+fI61j2SkhO3UGMUdfYRE6Bcy0ccyzn1Rqj9YAB/ZY4kOXmNf0ocah5GtphmLMP6Q=="], @@ -3547,8 +3519,6 @@ "expo-nip55/react": ["react@19.0.0", "", {}, "sha512-V8AVnmPIICiWpGfm6GLzCR/W5FXLchHop40W4nXBmdlEceh16rCN8O8LNWm5bh5XUX91fh7KpA+W0TgMKmgTpQ=="], - "expo-nip55/react-native": ["react-native@0.79.2", "", { "dependencies": { "@jest/create-cache-key-function": "^29.7.0", "@react-native/assets-registry": "0.79.2", "@react-native/codegen": "0.79.2", "@react-native/community-cli-plugin": "0.79.2", "@react-native/gradle-plugin": "0.79.2", "@react-native/js-polyfills": "0.79.2", "@react-native/normalize-colors": "0.79.2", "@react-native/virtualized-lists": "0.79.2", "abort-controller": "^3.0.0", "anser": "^1.4.9", "ansi-regex": "^5.0.0", "babel-jest": "^29.7.0", "babel-plugin-syntax-hermes-parser": "0.25.1", "base64-js": "^1.5.1", "chalk": "^4.0.0", "commander": "^12.0.0", "event-target-shim": "^5.0.1", "flow-enums-runtime": "^0.0.6", "glob": "^7.1.1", "invariant": "^2.2.4", "jest-environment-node": "^29.7.0", "memoize-one": "^5.0.0", "metro-runtime": "^0.82.0", "metro-source-map": "^0.82.0", "nullthrows": "^1.1.1", "pretty-format": "^29.7.0", "promise": "^8.3.0", "react-devtools-core": "^6.1.1", "react-refresh": "^0.14.0", "regenerator-runtime": "^0.13.2", "scheduler": "0.25.0", "semver": "^7.1.3", "stacktrace-parser": "^0.1.10", "whatwg-fetch": "^3.0.0", "ws": "^6.2.3", "yargs": "^17.6.2" }, "peerDependencies": { "@types/react": "^19.0.0", "react": "^19.0.0" }, "optionalPeers": ["@types/react"], "bin": { "react-native": "cli.js" } }, "sha512-AnGzb56JvU5YCL7cAwg10+ewDquzvmgrMddiBM0GAWLwQM/6DJfGd2ZKrMuKKehHerpDDZgG+EY64gk3x3dEkw=="], - "fast-glob/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], "finalhandler/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="], @@ -3607,13 +3577,13 @@ "metro/ci-info": ["ci-info@2.0.0", "", {}, "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ=="], - "metro/hermes-parser": ["hermes-parser@0.32.0", "", { "dependencies": { "hermes-estree": "0.32.0" } }, "sha512-g4nBOWFpuiTqjR3LZdRxKUkij9iyveWeuks7INEsMX741f3r9xxrOe8TeQfUxtda0eXmiIFiMQzoeSQEno33Hw=="], + "metro/hermes-parser": ["hermes-parser@0.29.1", "", { "dependencies": { "hermes-estree": "0.29.1" } }, "sha512-xBHWmUtRC5e/UL0tI7Ivt2riA/YBq9+SiYFU7C1oBa/j2jYGlIF9043oak1F47ihuDIxQ5nbsKueYJDRY02UgA=="], "metro/source-map": ["source-map@0.5.7", "", {}, "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ=="], "metro/ws": ["ws@7.5.10", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": "^5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ=="], - "metro-babel-transformer/hermes-parser": ["hermes-parser@0.32.0", "", { "dependencies": { "hermes-estree": "0.32.0" } }, "sha512-g4nBOWFpuiTqjR3LZdRxKUkij9iyveWeuks7INEsMX741f3r9xxrOe8TeQfUxtda0eXmiIFiMQzoeSQEno33Hw=="], + "metro-babel-transformer/hermes-parser": ["hermes-parser@0.29.1", "", { "dependencies": { "hermes-estree": "0.29.1" } }, "sha512-xBHWmUtRC5e/UL0tI7Ivt2riA/YBq9+SiYFU7C1oBa/j2jYGlIF9043oak1F47ihuDIxQ5nbsKueYJDRY02UgA=="], "metro-source-map/source-map": ["source-map@0.5.7", "", {}, "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ=="], @@ -3653,8 +3623,6 @@ "p-locate/p-limit": ["p-limit@3.1.0", "", { "dependencies": { "yocto-queue": "^0.1.0" } }, "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ=="], - "parse-png/pngjs": ["pngjs@3.4.0", "", {}, "sha512-NCrCHhWmnQklfH4MtJMRjZ2a8c80qXeMlQMv2uVp9ISJMTt562SbGd6n2oq0PaPgKm7Z6pL9E2UlLIhC+SHL3w=="], - "parse5/entities": ["entities@6.0.1", "", {}, "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g=="], "path-scurry/lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="], @@ -3665,9 +3633,9 @@ "playwright/fsevents": ["fsevents@2.3.2", "", { "os": "darwin" }, "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA=="], - "prompts/kleur": ["kleur@3.0.3", "", {}, "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w=="], + "pretty-format/ansi-styles": ["ansi-styles@5.2.0", "", {}, "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA=="], - "qrcode/yargs": ["yargs@15.4.1", "", { "dependencies": { "cliui": "^6.0.0", "decamelize": "^1.2.0", "find-up": "^4.1.0", "get-caller-file": "^2.0.1", "require-directory": "^2.1.1", "require-main-filename": "^2.0.0", "set-blocking": "^2.0.0", "string-width": "^4.2.0", "which-module": "^2.0.0", "y18n": "^4.0.0", "yargs-parser": "^18.1.2" } }, "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A=="], + "prompts/kleur": ["kleur@3.0.3", "", {}, "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w=="], "rc/strip-json-comments": ["strip-json-comments@2.0.1", "", {}, "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ=="], @@ -3675,7 +3643,7 @@ "react-dom/scheduler": ["scheduler@0.23.2", "", { "dependencies": { "loose-envify": "^1.1.0" } }, "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ=="], - "react-native/babel-plugin-syntax-hermes-parser": ["babel-plugin-syntax-hermes-parser@0.32.0", "", { "dependencies": { "hermes-parser": "0.32.0" } }, "sha512-m5HthL++AbyeEA2FcdwOLfVFvWYECOBObLHNqdR8ceY4TsEdn4LdX2oTvbB2QJSSElE2AWA/b2MXZ/PF/CqLZg=="], + "react-native/babel-plugin-syntax-hermes-parser": ["babel-plugin-syntax-hermes-parser@0.25.1", "", { "dependencies": { "hermes-parser": "0.25.1" } }, "sha512-IVNpGzboFLfXZUAwkLFcI/bnqVbwky0jP3eBno4HKtqvQJAHBLdgxiG6lQ4to0+Q/YCN3PO0od5NZwIKyY4REQ=="], "react-native/glob": ["glob@7.2.3", "", { "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } }, "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q=="], @@ -3757,10 +3725,6 @@ "whatwg-url-without-unicode/webidl-conversions": ["webidl-conversions@5.0.0", "", {}, "sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA=="], - "wrap-ansi/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], - - "wrap-ansi-cjs/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], - "write-file-atomic/signal-exit": ["signal-exit@3.0.7", "", {}, "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="], "xcode/uuid": ["uuid@7.0.3", "", { "bin": { "uuid": "dist/bin/uuid" } }, "sha512-DPSke0pXhTZgoF/d+WSt2QaKMCFSfx7QegxEWT+JOuHF5aWrKEn0G+ztjuJg/gG8/ItK+rbPCD/yNv8yyih6Cg=="], @@ -3945,18 +3909,22 @@ "@react-native/codegen/glob/minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="], - "@react-native/codegen/hermes-parser/hermes-estree": ["hermes-estree@0.32.0", "", {}, "sha512-KWn3BqnlDOl97Xe1Yviur6NbgIZ+IP+UVSpshlZWkq+EtoHg6/cwiDj/osP9PCEgFE15KBm1O55JRwbMEm5ejQ=="], + "@react-native/codegen/hermes-parser/hermes-estree": ["hermes-estree@0.25.1", "", {}, "sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw=="], - "@react-native/community-cli-plugin/@react-native/dev-middleware/@react-native/debugger-frontend": ["@react-native/debugger-frontend@0.82.0", "", {}, "sha512-rlTDcjf0ecjOHmygdBACAQCqPG0z/itAxnbhk8ZiQts7m4gRJiA/iCGFyC8/T9voUA0azAX6QCl4tHlnuUy7mQ=="], + "@react-native/community-cli-plugin/@react-native/dev-middleware/@react-native/debugger-frontend": ["@react-native/debugger-frontend@0.79.2", "", {}, "sha512-cGmC7X6kju76DopSBNc+PRAEetbd7TWF9J9o84hOp/xL3ahxR2kuxJy0oJX8Eg8oehhGGEXTuMKHzNa3rDBeSg=="], "@react-native/community-cli-plugin/@react-native/dev-middleware/ws": ["ws@6.2.3", "", { "dependencies": { "async-limiter": "~1.0.0" } }, "sha512-jmTjYU0j60B+vHey6TfR3Z7RD61z/hmxBS3VMSGIrroOWXQEneK1zNuotOUrGyBHQj0yrpsLHPWtigEFd13ndA=="], + "@react-native/community-cli-plugin/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="], + "@react-native/dev-middleware/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="], "@scure/bip32/@noble/curves/@noble/hashes": ["@noble/hashes@1.3.1", "", {}, "sha512-EbqwksQwz9xDRGfDST86whPBgM65E0OH/pCgqW0GBVzO22bNE+NuIbeTb714+IfSjU3aRk47EUvXIb5bTsenKA=="], "@shikijs/core/@shikijs/engine-javascript/oniguruma-to-es": ["oniguruma-to-es@3.1.1", "", { "dependencies": { "emoji-regex-xs": "^1.0.0", "regex": "^6.0.1", "regex-recursion": "^6.0.2" } }, "sha512-bUH8SDvPkH3ho3dvwJwfonjlQ4R80vjyvrU8YpxuROddv55vAEJrTuCuCVUhhsHbtlD9tGGbaNApGQckXhS8iQ=="], + "@testing-library/dom/pretty-format/ansi-styles": ["ansi-styles@5.2.0", "", {}, "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA=="], + "@testing-library/dom/pretty-format/react-is": ["react-is@17.0.2", "", {}, "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w=="], "@types/better-sqlite3/@types/node/undici-types": ["undici-types@7.14.0", "", {}, "sha512-QQiYxHuyZ9gQUIrmPo3IA+hUl4KYk8uSA7cHrcKd/l3p1OTpZcM0Tbp9x7FAtXdAYhlasd60ncPpgu6ihG6TOA=="], @@ -4013,30 +3981,10 @@ "@vitest/mocker/@vitest/spy/tinyspy": ["tinyspy@4.0.4", "", {}, "sha512-azl+t0z7pw/z958Gy9svOTuzqIk6xq+NSheJzn5MMWtWTFywIacg2wUlzKFGtt3cthx0r2SxMK0yzJOR0IES7Q=="], - "@vitest/ui/@vitest/utils/loupe": ["loupe@3.2.1", "", {}, "sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ=="], - - "@vitest/ui/vitest/@vitest/expect": ["@vitest/expect@3.2.4", "", { "dependencies": { "@types/chai": "^5.2.2", "@vitest/spy": "3.2.4", "@vitest/utils": "3.2.4", "chai": "^5.2.0", "tinyrainbow": "^2.0.0" } }, "sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig=="], - - "@vitest/ui/vitest/@vitest/runner": ["@vitest/runner@3.2.4", "", { "dependencies": { "@vitest/utils": "3.2.4", "pathe": "^2.0.3", "strip-literal": "^3.0.0" } }, "sha512-oukfKT9Mk41LreEW09vt45f8wx7DordoWUZMYdY/cyAk7w5TWkTRCNZYF7sX7n2wB7jyGAl74OxgwhPgKaqDMQ=="], - - "@vitest/ui/vitest/@vitest/snapshot": ["@vitest/snapshot@3.2.4", "", { "dependencies": { "@vitest/pretty-format": "3.2.4", "magic-string": "^0.30.17", "pathe": "^2.0.3" } }, "sha512-dEYtS7qQP2CjU27QBC5oUOxLE/v5eLkGqPE0ZKEIDGMs4vKWe7IjgLOeauHsR0D5YuuycGRO5oSRXnwnmA78fQ=="], - - "@vitest/ui/vitest/@vitest/spy": ["@vitest/spy@3.2.4", "", { "dependencies": { "tinyspy": "^4.0.3" } }, "sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw=="], - - "@vitest/ui/vitest/chai": ["chai@5.3.3", "", { "dependencies": { "assertion-error": "^2.0.1", "check-error": "^2.1.1", "deep-eql": "^5.0.1", "loupe": "^3.1.0", "pathval": "^2.0.0" } }, "sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw=="], - - "@vitest/ui/vitest/tinypool": ["tinypool@1.1.1", "", {}, "sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg=="], - - "@vitest/ui/vitest/vite": ["vite@7.1.9", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.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", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-4nVGliEpxmhCL8DslSAUdxlB6+SMrhB0a1v5ijlh1xB1nEPuy1mxaHxysVucLHuWryAxLWg6a5ei+U4TLn/rFg=="], - - "@vitest/ui/vitest/vite-node": ["vite-node@3.2.4", "", { "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" } }, "sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg=="], - "babel-plugin-istanbul/test-exclude/glob": ["glob@7.2.3", "", { "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } }, "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q=="], "babel-plugin-istanbul/test-exclude/minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="], - "chalk/ansi-styles/color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="], - "chrome-launcher/@types/node/undici-types": ["undici-types@7.14.0", "", {}, "sha512-QQiYxHuyZ9gQUIrmPo3IA+hUl4KYk8uSA7cHrcKd/l3p1OTpZcM0Tbp9x7FAtXdAYhlasd60ncPpgu6ihG6TOA=="], "chromium-edge-launcher/@types/node/undici-types": ["undici-types@7.14.0", "", {}, "sha512-QQiYxHuyZ9gQUIrmPo3IA+hUl4KYk8uSA7cHrcKd/l3p1OTpZcM0Tbp9x7FAtXdAYhlasd60ncPpgu6ihG6TOA=="], @@ -4053,8 +4001,6 @@ "cytoscape-fcose/cose-base/layout-base": ["layout-base@2.0.1", "", {}, "sha512-dp3s92+uNI1hWIpPGH3jK2kxE2lMjdXdr+DH8ynZHpd6PUlH6x6cbuXnoMmiNumznqaNO31xu9e79F0uuZ0JFg=="], - "d3-sankey/d3-array/internmap": ["internmap@1.0.1", "", {}, "sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw=="], - "d3-sankey/d3-shape/d3-path": ["d3-path@1.0.9", "", {}, "sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg=="], "del/rimraf/glob": ["glob@7.2.3", "", { "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } }, "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q=="], @@ -4065,32 +4011,6 @@ "eslint/minimatch/brace-expansion": ["brace-expansion@1.1.12", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="], - "expo-nip55/react-native/@react-native/assets-registry": ["@react-native/assets-registry@0.79.2", "", {}, "sha512-5h2Z7/+/HL/0h88s0JHOdRCW4CXMCJoROxqzHqxdrjGL6EBD1DdaB4ZqkCOEVSW4Vjhir5Qb97C8i/MPWEYPtg=="], - - "expo-nip55/react-native/@react-native/codegen": ["@react-native/codegen@0.79.2", "", { "dependencies": { "glob": "^7.1.1", "hermes-parser": "0.25.1", "invariant": "^2.2.4", "nullthrows": "^1.1.1", "yargs": "^17.6.2" }, "peerDependencies": { "@babel/core": "*" } }, "sha512-8JTlGLuLi1p8Jx2N/enwwEd7/2CfrqJpv90Cp77QLRX3VHF2hdyavRIxAmXMwN95k+Me7CUuPtqn2X3IBXOWYg=="], - - "expo-nip55/react-native/@react-native/community-cli-plugin": ["@react-native/community-cli-plugin@0.79.2", "", { "dependencies": { "@react-native/dev-middleware": "0.79.2", "chalk": "^4.0.0", "debug": "^2.2.0", "invariant": "^2.2.4", "metro": "^0.82.0", "metro-config": "^0.82.0", "metro-core": "^0.82.0", "semver": "^7.1.3" }, "peerDependencies": { "@react-native-community/cli": "*" }, "optionalPeers": ["@react-native-community/cli"] }, "sha512-E+YEY2dL+68HyR2iahsZdyBKBUi9QyPyaN9vsnda1jNgCjNpSPk2yAF5cXsho+zKK5ZQna3JSeE1Kbi2IfGJbw=="], - - "expo-nip55/react-native/@react-native/gradle-plugin": ["@react-native/gradle-plugin@0.79.2", "", {}, "sha512-6MJFemrwR0bOT0QM+2BxX9k3/pvZQNmJ3Js5pF/6owsA0cUDiCO57otiEU8Fz+UywWEzn1FoQfOfQ8vt2GYmoA=="], - - "expo-nip55/react-native/@react-native/js-polyfills": ["@react-native/js-polyfills@0.79.2", "", {}, "sha512-IaY87Ckd4GTPMkO1/Fe8fC1IgIx3vc3q9Tyt/6qS3Mtk9nC0x9q4kSR5t+HHq0/MuvGtu8HpdxXGy5wLaM+zUw=="], - - "expo-nip55/react-native/@react-native/normalize-colors": ["@react-native/normalize-colors@0.79.2", "", {}, "sha512-+b+GNrupWrWw1okHnEENz63j7NSMqhKeFMOyzYLBwKcprG8fqJQhDIGXfizKdxeIa5NnGSAevKL1Ev1zJ56X8w=="], - - "expo-nip55/react-native/@react-native/virtualized-lists": ["@react-native/virtualized-lists@0.79.2", "", { "dependencies": { "invariant": "^2.2.4", "nullthrows": "^1.1.1" }, "peerDependencies": { "@types/react": "^19.0.0", "react": "*", "react-native": "*" }, "optionalPeers": ["@types/react"] }, "sha512-9G6ROJeP+rdw9Bvr5ruOlag11ET7j1z/En1riFFNo6W3xZvJY+alCuH1ttm12y9+zBm4n8jwCk4lGhjYaV4dKw=="], - - "expo-nip55/react-native/babel-plugin-syntax-hermes-parser": ["babel-plugin-syntax-hermes-parser@0.25.1", "", { "dependencies": { "hermes-parser": "0.25.1" } }, "sha512-IVNpGzboFLfXZUAwkLFcI/bnqVbwky0jP3eBno4HKtqvQJAHBLdgxiG6lQ4to0+Q/YCN3PO0od5NZwIKyY4REQ=="], - - "expo-nip55/react-native/glob": ["glob@7.2.3", "", { "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } }, "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q=="], - - "expo-nip55/react-native/metro-runtime": ["metro-runtime@0.82.5", "", { "dependencies": { "@babel/runtime": "^7.25.0", "flow-enums-runtime": "^0.0.6" } }, "sha512-rQZDoCUf7k4Broyw3Ixxlq5ieIPiR1ULONdpcYpbJQ6yQ5GGEyYjtkztGD+OhHlw81LCR2SUAoPvtTus2WDK5g=="], - - "expo-nip55/react-native/metro-source-map": ["metro-source-map@0.82.5", "", { "dependencies": { "@babel/traverse": "^7.25.3", "@babel/traverse--for-generate-function-map": "npm:@babel/traverse@^7.25.3", "@babel/types": "^7.25.2", "flow-enums-runtime": "^0.0.6", "invariant": "^2.2.4", "metro-symbolicate": "0.82.5", "nullthrows": "^1.1.1", "ob1": "0.82.5", "source-map": "^0.5.6", "vlq": "^1.0.0" } }, "sha512-wH+awTOQJVkbhn2SKyaw+0cd+RVSCZ3sHVgyqJFQXIee/yLs3dZqKjjeKKhhVeudgjXo7aE/vSu/zVfcQEcUfw=="], - - "expo-nip55/react-native/scheduler": ["scheduler@0.25.0", "", {}, "sha512-xFVuu11jh+xcO7JOAGJNOXld8/TcEHK/4CituBUeUb5hqxJLj9YuemAEuvm9gQ/+pgXYfbQuqAkiYu+u7YEsNA=="], - - "expo-nip55/react-native/ws": ["ws@6.2.3", "", { "dependencies": { "async-limiter": "~1.0.0" } }, "sha512-jmTjYU0j60B+vHey6TfR3Z7RD61z/hmxBS3VMSGIrroOWXQEneK1zNuotOUrGyBHQj0yrpsLHPWtigEFd13ndA=="], - "expo/babel-preset-expo/@react-native/babel-preset": ["@react-native/babel-preset@0.79.6", "", { "dependencies": { "@babel/core": "^7.25.2", "@babel/plugin-proposal-export-default-from": "^7.24.7", "@babel/plugin-syntax-dynamic-import": "^7.8.3", "@babel/plugin-syntax-export-default-from": "^7.24.7", "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", "@babel/plugin-syntax-optional-chaining": "^7.8.3", "@babel/plugin-transform-arrow-functions": "^7.24.7", "@babel/plugin-transform-async-generator-functions": "^7.25.4", "@babel/plugin-transform-async-to-generator": "^7.24.7", "@babel/plugin-transform-block-scoping": "^7.25.0", "@babel/plugin-transform-class-properties": "^7.25.4", "@babel/plugin-transform-classes": "^7.25.4", "@babel/plugin-transform-computed-properties": "^7.24.7", "@babel/plugin-transform-destructuring": "^7.24.8", "@babel/plugin-transform-flow-strip-types": "^7.25.2", "@babel/plugin-transform-for-of": "^7.24.7", "@babel/plugin-transform-function-name": "^7.25.1", "@babel/plugin-transform-literals": "^7.25.2", "@babel/plugin-transform-logical-assignment-operators": "^7.24.7", "@babel/plugin-transform-modules-commonjs": "^7.24.8", "@babel/plugin-transform-named-capturing-groups-regex": "^7.24.7", "@babel/plugin-transform-nullish-coalescing-operator": "^7.24.7", "@babel/plugin-transform-numeric-separator": "^7.24.7", "@babel/plugin-transform-object-rest-spread": "^7.24.7", "@babel/plugin-transform-optional-catch-binding": "^7.24.7", "@babel/plugin-transform-optional-chaining": "^7.24.8", "@babel/plugin-transform-parameters": "^7.24.7", "@babel/plugin-transform-private-methods": "^7.24.7", "@babel/plugin-transform-private-property-in-object": "^7.24.7", "@babel/plugin-transform-react-display-name": "^7.24.7", "@babel/plugin-transform-react-jsx": "^7.25.2", "@babel/plugin-transform-react-jsx-self": "^7.24.7", "@babel/plugin-transform-react-jsx-source": "^7.24.7", "@babel/plugin-transform-regenerator": "^7.24.7", "@babel/plugin-transform-runtime": "^7.24.7", "@babel/plugin-transform-shorthand-properties": "^7.24.7", "@babel/plugin-transform-spread": "^7.24.7", "@babel/plugin-transform-sticky-regex": "^7.24.7", "@babel/plugin-transform-typescript": "^7.25.2", "@babel/plugin-transform-unicode-regex": "^7.24.7", "@babel/template": "^7.25.0", "@react-native/babel-plugin-codegen": "0.79.6", "babel-plugin-syntax-hermes-parser": "0.25.1", "babel-plugin-transform-flow-enums": "^0.0.2", "react-refresh": "^0.14.0" } }, "sha512-H+FRO+r2Ql6b5IwfE0E7D52JhkxjeGSBSUpCXAI5zQ60zSBJ54Hwh2bBJOohXWl4J+C7gKYSAd2JHMUETu+c/A=="], "expo/babel-preset-expo/babel-plugin-syntax-hermes-parser": ["babel-plugin-syntax-hermes-parser@0.25.1", "", { "dependencies": { "hermes-parser": "0.25.1" } }, "sha512-IVNpGzboFLfXZUAwkLFcI/bnqVbwky0jP3eBno4HKtqvQJAHBLdgxiG6lQ4to0+Q/YCN3PO0od5NZwIKyY4REQ=="], @@ -4143,9 +4063,9 @@ "lighthouse-logger/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="], - "metro-babel-transformer/hermes-parser/hermes-estree": ["hermes-estree@0.32.0", "", {}, "sha512-KWn3BqnlDOl97Xe1Yviur6NbgIZ+IP+UVSpshlZWkq+EtoHg6/cwiDj/osP9PCEgFE15KBm1O55JRwbMEm5ejQ=="], + "metro-babel-transformer/hermes-parser/hermes-estree": ["hermes-estree@0.29.1", "", {}, "sha512-jl+x31n4/w+wEqm0I2r4CMimukLbLQEYpisys5oCre611CI5fc9TxhqkBBCJ1edDG4Kza0f7CgNz8xVMLZQOmQ=="], - "metro/hermes-parser/hermes-estree": ["hermes-estree@0.32.0", "", {}, "sha512-KWn3BqnlDOl97Xe1Yviur6NbgIZ+IP+UVSpshlZWkq+EtoHg6/cwiDj/osP9PCEgFE15KBm1O55JRwbMEm5ejQ=="], + "metro/hermes-parser/hermes-estree": ["hermes-estree@0.29.1", "", {}, "sha512-jl+x31n4/w+wEqm0I2r4CMimukLbLQEYpisys5oCre611CI5fc9TxhqkBBCJ1edDG4Kza0f7CgNz8xVMLZQOmQ=="], "node-dir/minimatch/brace-expansion": ["brace-expansion@1.1.12", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="], @@ -4153,6 +4073,8 @@ "nostr-tools/@noble/curves/@noble/hashes": ["@noble/hashes@1.3.2", "", {}, "sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ=="], + "npm-run-all/ansi-styles/color-convert": ["color-convert@1.9.3", "", { "dependencies": { "color-name": "1.1.3" } }, "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg=="], + "npm-run-all/chalk/escape-string-regexp": ["escape-string-regexp@1.0.5", "", {}, "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg=="], "npm-run-all/chalk/supports-color": ["supports-color@5.5.0", "", { "dependencies": { "has-flag": "^3.0.0" } }, "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow=="], @@ -4179,17 +4101,9 @@ "pkg-dir/find-up/locate-path": ["locate-path@3.0.0", "", { "dependencies": { "p-locate": "^3.0.0", "path-exists": "^3.0.0" } }, "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A=="], - "qrcode/yargs/cliui": ["cliui@6.0.0", "", { "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.0", "wrap-ansi": "^6.2.0" } }, "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ=="], - - "qrcode/yargs/find-up": ["find-up@4.1.0", "", { "dependencies": { "locate-path": "^5.0.0", "path-exists": "^4.0.0" } }, "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw=="], - - "qrcode/yargs/y18n": ["y18n@4.0.3", "", {}, "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ=="], - - "qrcode/yargs/yargs-parser": ["yargs-parser@18.1.3", "", { "dependencies": { "camelcase": "^5.0.0", "decamelize": "^1.2.0" } }, "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ=="], - "react-native-builder-bob/glob/minimatch": ["minimatch@5.1.6", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g=="], - "react-native/babel-plugin-syntax-hermes-parser/hermes-parser": ["hermes-parser@0.32.0", "", { "dependencies": { "hermes-estree": "0.32.0" } }, "sha512-g4nBOWFpuiTqjR3LZdRxKUkij9iyveWeuks7INEsMX741f3r9xxrOe8TeQfUxtda0eXmiIFiMQzoeSQEno33Hw=="], + "react-native/babel-plugin-syntax-hermes-parser/hermes-parser": ["hermes-parser@0.25.1", "", { "dependencies": { "hermes-estree": "0.25.1" } }, "sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA=="], "react-native/glob/minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="], @@ -4301,9 +4215,7 @@ "websocket/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="], - "wrap-ansi-cjs/ansi-styles/color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="], - - "wrap-ansi/ansi-styles/color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="], + "@babel/highlight/chalk/ansi-styles/color-convert": ["color-convert@1.9.3", "", { "dependencies": { "color-name": "1.1.3" } }, "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg=="], "@babel/highlight/chalk/supports-color/has-flag": ["has-flag@3.0.0", "", {}, "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw=="], @@ -4487,6 +4399,8 @@ "@vitejs/plugin-vue/vite/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.21.5", "", { "os": "win32", "cpu": "x64" }, "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw=="], + "@vitest/browser/@testing-library/dom/pretty-format/ansi-styles": ["ansi-styles@5.2.0", "", {}, "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA=="], + "@vitest/browser/@testing-library/dom/pretty-format/react-is": ["react-is@17.0.2", "", {}, "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w=="], "@vitest/browser/vitest/@vitest/runner/strip-literal": ["strip-literal@3.1.0", "", { "dependencies": { "js-tokens": "^9.0.1" } }, "sha512-8r3mkIM/2+PpjHoOtiAW8Rg3jJLHaV7xPwG+YRGrv6FP0wwk/toTpATxWYOW0BKdWwl82VT2tFYi5DlROa0Mxg=="], @@ -4519,50 +4433,12 @@ "@vitest/coverage-v8/vitest/chai/pathval": ["pathval@2.0.1", "", {}, "sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ=="], - "@vitest/ui/vitest/@vitest/runner/strip-literal": ["strip-literal@3.1.0", "", { "dependencies": { "js-tokens": "^9.0.1" } }, "sha512-8r3mkIM/2+PpjHoOtiAW8Rg3jJLHaV7xPwG+YRGrv6FP0wwk/toTpATxWYOW0BKdWwl82VT2tFYi5DlROa0Mxg=="], - - "@vitest/ui/vitest/@vitest/spy/tinyspy": ["tinyspy@4.0.4", "", {}, "sha512-azl+t0z7pw/z958Gy9svOTuzqIk6xq+NSheJzn5MMWtWTFywIacg2wUlzKFGtt3cthx0r2SxMK0yzJOR0IES7Q=="], - - "@vitest/ui/vitest/chai/assertion-error": ["assertion-error@2.0.1", "", {}, "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA=="], - - "@vitest/ui/vitest/chai/check-error": ["check-error@2.1.1", "", {}, "sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw=="], - - "@vitest/ui/vitest/chai/deep-eql": ["deep-eql@5.0.2", "", {}, "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q=="], - - "@vitest/ui/vitest/chai/loupe": ["loupe@3.2.1", "", {}, "sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ=="], - - "@vitest/ui/vitest/chai/pathval": ["pathval@2.0.1", "", {}, "sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ=="], - "babel-plugin-istanbul/test-exclude/minimatch/brace-expansion": ["brace-expansion@1.1.12", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="], - "chalk/ansi-styles/color-convert/color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="], - "chromium-edge-launcher/rimraf/glob/minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="], "del/rimraf/glob/minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="], - "expo-nip55/react-native/@react-native/codegen/hermes-parser": ["hermes-parser@0.25.1", "", { "dependencies": { "hermes-estree": "0.25.1" } }, "sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA=="], - - "expo-nip55/react-native/@react-native/community-cli-plugin/@react-native/dev-middleware": ["@react-native/dev-middleware@0.79.2", "", { "dependencies": { "@isaacs/ttlcache": "^1.4.1", "@react-native/debugger-frontend": "0.79.2", "chrome-launcher": "^0.15.2", "chromium-edge-launcher": "^0.2.0", "connect": "^3.6.5", "debug": "^2.2.0", "invariant": "^2.2.4", "nullthrows": "^1.1.1", "open": "^7.0.3", "serve-static": "^1.16.2", "ws": "^6.2.3" } }, "sha512-9q4CpkklsAs1L0Bw8XYCoqqyBSrfRALGEw4/r0EkR38Y/6fVfNfdsjSns0pTLO6h0VpxswK34L/hm4uK3MoLHw=="], - - "expo-nip55/react-native/@react-native/community-cli-plugin/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="], - - "expo-nip55/react-native/@react-native/community-cli-plugin/metro": ["metro@0.82.5", "", { "dependencies": { "@babel/code-frame": "^7.24.7", "@babel/core": "^7.25.2", "@babel/generator": "^7.25.0", "@babel/parser": "^7.25.3", "@babel/template": "^7.25.0", "@babel/traverse": "^7.25.3", "@babel/types": "^7.25.2", "accepts": "^1.3.7", "chalk": "^4.0.0", "ci-info": "^2.0.0", "connect": "^3.6.5", "debug": "^4.4.0", "error-stack-parser": "^2.0.6", "flow-enums-runtime": "^0.0.6", "graceful-fs": "^4.2.4", "hermes-parser": "0.29.1", "image-size": "^1.0.2", "invariant": "^2.2.4", "jest-worker": "^29.7.0", "jsc-safe-url": "^0.2.2", "lodash.throttle": "^4.1.1", "metro-babel-transformer": "0.82.5", "metro-cache": "0.82.5", "metro-cache-key": "0.82.5", "metro-config": "0.82.5", "metro-core": "0.82.5", "metro-file-map": "0.82.5", "metro-resolver": "0.82.5", "metro-runtime": "0.82.5", "metro-source-map": "0.82.5", "metro-symbolicate": "0.82.5", "metro-transform-plugins": "0.82.5", "metro-transform-worker": "0.82.5", "mime-types": "^2.1.27", "nullthrows": "^1.1.1", "serialize-error": "^2.1.0", "source-map": "^0.5.6", "throat": "^5.0.0", "ws": "^7.5.10", "yargs": "^17.6.2" }, "bin": { "metro": "src/cli.js" } }, "sha512-8oAXxL7do8QckID/WZEKaIFuQJFUTLzfVcC48ghkHhNK2RGuQq8Xvf4AVd+TUA0SZtX0q8TGNXZ/eba1ckeGCg=="], - - "expo-nip55/react-native/@react-native/community-cli-plugin/metro-config": ["metro-config@0.82.5", "", { "dependencies": { "connect": "^3.6.5", "cosmiconfig": "^5.0.5", "flow-enums-runtime": "^0.0.6", "jest-validate": "^29.7.0", "metro": "0.82.5", "metro-cache": "0.82.5", "metro-core": "0.82.5", "metro-runtime": "0.82.5" } }, "sha512-/r83VqE55l0WsBf8IhNmc/3z71y2zIPe5kRSuqA5tY/SL/ULzlHUJEMd1szztd0G45JozLwjvrhAzhDPJ/Qo/g=="], - - "expo-nip55/react-native/@react-native/community-cli-plugin/metro-core": ["metro-core@0.82.5", "", { "dependencies": { "flow-enums-runtime": "^0.0.6", "lodash.throttle": "^4.1.1", "metro-resolver": "0.82.5" } }, "sha512-OJL18VbSw2RgtBm1f2P3J5kb892LCVJqMvslXxuxjAPex8OH7Eb8RBfgEo7VZSjgb/LOf4jhC4UFk5l5tAOHHA=="], - - "expo-nip55/react-native/babel-plugin-syntax-hermes-parser/hermes-parser": ["hermes-parser@0.25.1", "", { "dependencies": { "hermes-estree": "0.25.1" } }, "sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA=="], - - "expo-nip55/react-native/glob/minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="], - - "expo-nip55/react-native/metro-source-map/metro-symbolicate": ["metro-symbolicate@0.82.5", "", { "dependencies": { "flow-enums-runtime": "^0.0.6", "invariant": "^2.2.4", "metro-source-map": "0.82.5", "nullthrows": "^1.1.1", "source-map": "^0.5.6", "vlq": "^1.0.0" }, "bin": { "metro-symbolicate": "src/index.js" } }, "sha512-1u+07gzrvYDJ/oNXuOG1EXSvXZka/0JSW1q2EYBWerVKMOhvv9JzDGyzmuV7hHbF2Hg3T3S2uiM36sLz1qKsiw=="], - - "expo-nip55/react-native/metro-source-map/ob1": ["ob1@0.82.5", "", { "dependencies": { "flow-enums-runtime": "^0.0.6" } }, "sha512-QyQQ6e66f+Ut/qUVjEce0E/wux5nAGLXYZDn1jr15JWstHsCH3l6VVrg8NKDptW9NEiBXKOJeGF/ydxeSDF3IQ=="], - - "expo-nip55/react-native/metro-source-map/source-map": ["source-map@0.5.7", "", {}, "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ=="], - "expo/babel-preset-expo/@react-native/babel-preset/@react-native/babel-plugin-codegen": ["@react-native/babel-plugin-codegen@0.79.6", "", { "dependencies": { "@babel/traverse": "^7.25.3", "@react-native/codegen": "0.79.6" } }, "sha512-CS5OrgcMPixOyUJ/Sk/HSsKsKgyKT5P7y3CojimOQzWqRZBmoQfxdST4ugj7n1H+ebM2IKqbgovApFbqXsoX0g=="], "expo/babel-preset-expo/babel-plugin-syntax-hermes-parser/hermes-parser": ["hermes-parser@0.25.1", "", { "dependencies": { "hermes-estree": "0.25.1" } }, "sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA=="], @@ -4573,23 +4449,21 @@ "normalize-package-data/hosted-git-info/lru-cache/yallist": ["yallist@4.0.0", "", {}, "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="], + "npm-run-all/ansi-styles/color-convert/color-name": ["color-name@1.1.3", "", {}, "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="], + "npm-run-all/chalk/supports-color/has-flag": ["has-flag@3.0.0", "", {}, "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw=="], "npm-run-all/cross-spawn/shebang-command/shebang-regex": ["shebang-regex@1.0.0", "", {}, "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ=="], + "ora/chalk/ansi-styles/color-convert": ["color-convert@1.9.3", "", { "dependencies": { "color-name": "1.1.3" } }, "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg=="], + "ora/chalk/supports-color/has-flag": ["has-flag@3.0.0", "", {}, "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw=="], "pkg-dir/find-up/locate-path/p-locate": ["p-locate@3.0.0", "", { "dependencies": { "p-limit": "^2.0.0" } }, "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ=="], "pkg-dir/find-up/locate-path/path-exists": ["path-exists@3.0.0", "", {}, "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ=="], - "qrcode/yargs/cliui/wrap-ansi": ["wrap-ansi@6.2.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA=="], - - "qrcode/yargs/find-up/locate-path": ["locate-path@5.0.0", "", { "dependencies": { "p-locate": "^4.1.0" } }, "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g=="], - - "qrcode/yargs/yargs-parser/camelcase": ["camelcase@5.3.1", "", {}, "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg=="], - - "react-native/babel-plugin-syntax-hermes-parser/hermes-parser/hermes-estree": ["hermes-estree@0.32.0", "", {}, "sha512-KWn3BqnlDOl97Xe1Yviur6NbgIZ+IP+UVSpshlZWkq+EtoHg6/cwiDj/osP9PCEgFE15KBm1O55JRwbMEm5ejQ=="], + "react-native/babel-plugin-syntax-hermes-parser/hermes-parser/hermes-estree": ["hermes-estree@0.25.1", "", {}, "sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw=="], "react-native/glob/minimatch/brace-expansion": ["brace-expansion@1.1.12", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="], @@ -4759,9 +4633,7 @@ "vitest/vite/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.21.5", "", { "os": "win32", "cpu": "x64" }, "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw=="], - "wrap-ansi-cjs/ansi-styles/color-convert/color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="], - - "wrap-ansi/ansi-styles/color-convert/color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="], + "@babel/highlight/chalk/ansi-styles/color-convert/color-name": ["color-name@1.1.3", "", {}, "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="], "@istanbuljs/load-nyc-config/find-up/locate-path/p-locate/p-limit": ["p-limit@2.3.0", "", { "dependencies": { "p-try": "^2.0.0" } }, "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w=="], @@ -4779,78 +4651,26 @@ "del/rimraf/glob/minimatch/brace-expansion": ["brace-expansion@1.1.12", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="], - "expo-nip55/react-native/@react-native/codegen/hermes-parser/hermes-estree": ["hermes-estree@0.25.1", "", {}, "sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw=="], - - "expo-nip55/react-native/@react-native/community-cli-plugin/@react-native/dev-middleware/@react-native/debugger-frontend": ["@react-native/debugger-frontend@0.79.2", "", {}, "sha512-cGmC7X6kju76DopSBNc+PRAEetbd7TWF9J9o84hOp/xL3ahxR2kuxJy0oJX8Eg8oehhGGEXTuMKHzNa3rDBeSg=="], - - "expo-nip55/react-native/@react-native/community-cli-plugin/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="], - - "expo-nip55/react-native/@react-native/community-cli-plugin/metro/ci-info": ["ci-info@2.0.0", "", {}, "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ=="], - - "expo-nip55/react-native/@react-native/community-cli-plugin/metro/debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], - - "expo-nip55/react-native/@react-native/community-cli-plugin/metro/hermes-parser": ["hermes-parser@0.29.1", "", { "dependencies": { "hermes-estree": "0.29.1" } }, "sha512-xBHWmUtRC5e/UL0tI7Ivt2riA/YBq9+SiYFU7C1oBa/j2jYGlIF9043oak1F47ihuDIxQ5nbsKueYJDRY02UgA=="], - - "expo-nip55/react-native/@react-native/community-cli-plugin/metro/metro-babel-transformer": ["metro-babel-transformer@0.82.5", "", { "dependencies": { "@babel/core": "^7.25.2", "flow-enums-runtime": "^0.0.6", "hermes-parser": "0.29.1", "nullthrows": "^1.1.1" } }, "sha512-W/scFDnwJXSccJYnOFdGiYr9srhbHPdxX9TvvACOFsIXdLilh3XuxQl/wXW6jEJfgIb0jTvoTlwwrqvuwymr6Q=="], - - "expo-nip55/react-native/@react-native/community-cli-plugin/metro/metro-cache": ["metro-cache@0.82.5", "", { "dependencies": { "exponential-backoff": "^3.1.1", "flow-enums-runtime": "^0.0.6", "https-proxy-agent": "^7.0.5", "metro-core": "0.82.5" } }, "sha512-AwHV9607xZpedu1NQcjUkua8v7HfOTKfftl6Vc9OGr/jbpiJX6Gpy8E/V9jo/U9UuVYX2PqSUcVNZmu+LTm71Q=="], - - "expo-nip55/react-native/@react-native/community-cli-plugin/metro/metro-cache-key": ["metro-cache-key@0.82.5", "", { "dependencies": { "flow-enums-runtime": "^0.0.6" } }, "sha512-qpVmPbDJuRLrT4kcGlUouyqLGssJnbTllVtvIgXfR7ZuzMKf0mGS+8WzcqzNK8+kCyakombQWR0uDd8qhWGJcA=="], - - "expo-nip55/react-native/@react-native/community-cli-plugin/metro/metro-file-map": ["metro-file-map@0.82.5", "", { "dependencies": { "debug": "^4.4.0", "fb-watchman": "^2.0.0", "flow-enums-runtime": "^0.0.6", "graceful-fs": "^4.2.4", "invariant": "^2.2.4", "jest-worker": "^29.7.0", "micromatch": "^4.0.4", "nullthrows": "^1.1.1", "walker": "^1.0.7" } }, "sha512-vpMDxkGIB+MTN8Af5hvSAanc6zXQipsAUO+XUx3PCQieKUfLwdoa8qaZ1WAQYRpaU+CJ8vhBcxtzzo3d9IsCIQ=="], - - "expo-nip55/react-native/@react-native/community-cli-plugin/metro/metro-resolver": ["metro-resolver@0.82.5", "", { "dependencies": { "flow-enums-runtime": "^0.0.6" } }, "sha512-kFowLnWACt3bEsuVsaRNgwplT8U7kETnaFHaZePlARz4Fg8tZtmRDUmjaD68CGAwc0rwdwNCkWizLYpnyVcs2g=="], - - "expo-nip55/react-native/@react-native/community-cli-plugin/metro/metro-symbolicate": ["metro-symbolicate@0.82.5", "", { "dependencies": { "flow-enums-runtime": "^0.0.6", "invariant": "^2.2.4", "metro-source-map": "0.82.5", "nullthrows": "^1.1.1", "source-map": "^0.5.6", "vlq": "^1.0.0" }, "bin": { "metro-symbolicate": "src/index.js" } }, "sha512-1u+07gzrvYDJ/oNXuOG1EXSvXZka/0JSW1q2EYBWerVKMOhvv9JzDGyzmuV7hHbF2Hg3T3S2uiM36sLz1qKsiw=="], - - "expo-nip55/react-native/@react-native/community-cli-plugin/metro/metro-transform-plugins": ["metro-transform-plugins@0.82.5", "", { "dependencies": { "@babel/core": "^7.25.2", "@babel/generator": "^7.25.0", "@babel/template": "^7.25.0", "@babel/traverse": "^7.25.3", "flow-enums-runtime": "^0.0.6", "nullthrows": "^1.1.1" } }, "sha512-57Bqf3rgq9nPqLrT2d9kf/2WVieTFqsQ6qWHpEng5naIUtc/Iiw9+0bfLLWSAw0GH40iJ4yMjFcFJDtNSYynMA=="], - - "expo-nip55/react-native/@react-native/community-cli-plugin/metro/metro-transform-worker": ["metro-transform-worker@0.82.5", "", { "dependencies": { "@babel/core": "^7.25.2", "@babel/generator": "^7.25.0", "@babel/parser": "^7.25.3", "@babel/types": "^7.25.2", "flow-enums-runtime": "^0.0.6", "metro": "0.82.5", "metro-babel-transformer": "0.82.5", "metro-cache": "0.82.5", "metro-cache-key": "0.82.5", "metro-minify-terser": "0.82.5", "metro-source-map": "0.82.5", "metro-transform-plugins": "0.82.5", "nullthrows": "^1.1.1" } }, "sha512-mx0grhAX7xe+XUQH6qoHHlWedI8fhSpDGsfga7CpkO9Lk9W+aPitNtJWNGrW8PfjKEWbT9Uz9O50dkI8bJqigw=="], - - "expo-nip55/react-native/@react-native/community-cli-plugin/metro/source-map": ["source-map@0.5.7", "", {}, "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ=="], - - "expo-nip55/react-native/@react-native/community-cli-plugin/metro/ws": ["ws@7.5.10", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": "^5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ=="], - - "expo-nip55/react-native/@react-native/community-cli-plugin/metro-config/metro-cache": ["metro-cache@0.82.5", "", { "dependencies": { "exponential-backoff": "^3.1.1", "flow-enums-runtime": "^0.0.6", "https-proxy-agent": "^7.0.5", "metro-core": "0.82.5" } }, "sha512-AwHV9607xZpedu1NQcjUkua8v7HfOTKfftl6Vc9OGr/jbpiJX6Gpy8E/V9jo/U9UuVYX2PqSUcVNZmu+LTm71Q=="], - - "expo-nip55/react-native/@react-native/community-cli-plugin/metro-core/metro-resolver": ["metro-resolver@0.82.5", "", { "dependencies": { "flow-enums-runtime": "^0.0.6" } }, "sha512-kFowLnWACt3bEsuVsaRNgwplT8U7kETnaFHaZePlARz4Fg8tZtmRDUmjaD68CGAwc0rwdwNCkWizLYpnyVcs2g=="], - - "expo-nip55/react-native/babel-plugin-syntax-hermes-parser/hermes-parser/hermes-estree": ["hermes-estree@0.25.1", "", {}, "sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw=="], - - "expo-nip55/react-native/glob/minimatch/brace-expansion": ["brace-expansion@1.1.12", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="], - "expo/babel-preset-expo/@react-native/babel-preset/@react-native/babel-plugin-codegen/@react-native/codegen": ["@react-native/codegen@0.79.6", "", { "dependencies": { "@babel/core": "^7.25.2", "@babel/parser": "^7.25.3", "glob": "^7.1.1", "hermes-parser": "0.25.1", "invariant": "^2.2.4", "nullthrows": "^1.1.1", "yargs": "^17.6.2" } }, "sha512-iRBX8Lgbqypwnfba7s6opeUwVyaR23mowh9ILw7EcT2oLz3RqMmjJdrbVpWhGSMGq2qkPfqAH7bhO8C7O+xfjQ=="], "expo/babel-preset-expo/babel-plugin-syntax-hermes-parser/hermes-parser/hermes-estree": ["hermes-estree@0.25.1", "", {}, "sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw=="], - "pkg-dir/find-up/locate-path/p-locate/p-limit": ["p-limit@2.3.0", "", { "dependencies": { "p-try": "^2.0.0" } }, "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w=="], - - "qrcode/yargs/cliui/wrap-ansi/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], + "ora/chalk/ansi-styles/color-convert/color-name": ["color-name@1.1.3", "", {}, "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="], - "qrcode/yargs/find-up/locate-path/p-locate": ["p-locate@4.1.0", "", { "dependencies": { "p-limit": "^2.2.0" } }, "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A=="], + "pkg-dir/find-up/locate-path/p-locate/p-limit": ["p-limit@2.3.0", "", { "dependencies": { "p-try": "^2.0.0" } }, "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w=="], "read-pkg-up/find-up/locate-path/p-locate/p-limit": ["p-limit@2.3.0", "", { "dependencies": { "p-try": "^2.0.0" } }, "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w=="], "temp/rimraf/glob/minimatch/brace-expansion": ["brace-expansion@1.1.12", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="], - "expo-nip55/react-native/@react-native/community-cli-plugin/metro/hermes-parser/hermes-estree": ["hermes-estree@0.29.1", "", {}, "sha512-jl+x31n4/w+wEqm0I2r4CMimukLbLQEYpisys5oCre611CI5fc9TxhqkBBCJ1edDG4Kza0f7CgNz8xVMLZQOmQ=="], - - "expo-nip55/react-native/@react-native/community-cli-plugin/metro/metro-transform-worker/metro-minify-terser": ["metro-minify-terser@0.82.5", "", { "dependencies": { "flow-enums-runtime": "^0.0.6", "terser": "^5.15.0" } }, "sha512-v6Nx7A4We6PqPu/ta1oGTqJ4Usz0P7c+3XNeBxW9kp8zayS3lHUKR0sY0wsCHInxZlNAEICx791x+uXytFUuwg=="], - "expo/babel-preset-expo/@react-native/babel-preset/@react-native/babel-plugin-codegen/@react-native/codegen/glob": ["glob@7.2.3", "", { "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } }, "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q=="], "expo/babel-preset-expo/@react-native/babel-preset/@react-native/babel-plugin-codegen/@react-native/codegen/hermes-parser": ["hermes-parser@0.25.1", "", { "dependencies": { "hermes-estree": "0.25.1" } }, "sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA=="], - "qrcode/yargs/cliui/wrap-ansi/ansi-styles/color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="], - - "qrcode/yargs/find-up/locate-path/p-locate/p-limit": ["p-limit@2.3.0", "", { "dependencies": { "p-try": "^2.0.0" } }, "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w=="], - "expo/babel-preset-expo/@react-native/babel-preset/@react-native/babel-plugin-codegen/@react-native/codegen/glob/minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="], "expo/babel-preset-expo/@react-native/babel-preset/@react-native/babel-plugin-codegen/@react-native/codegen/hermes-parser/hermes-estree": ["hermes-estree@0.25.1", "", {}, "sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw=="], - "qrcode/yargs/cliui/wrap-ansi/ansi-styles/color-convert/color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="], - "expo/babel-preset-expo/@react-native/babel-preset/@react-native/babel-plugin-codegen/@react-native/codegen/glob/minimatch/brace-expansion": ["brace-expansion@1.1.12", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="], } } From f69ff69aab24a9cb47a2ed409120a1b793d4fcd1 Mon Sep 17 00:00:00 2001 From: digitalbase Date: Sat, 11 Oct 2025 13:22:02 +0200 Subject: [PATCH 015/139] Update bun package manager --- package.json | 118 +++++++++++++++++++++++++-------------------------- 1 file changed, 59 insertions(+), 59 deletions(-) diff --git a/package.json b/package.json index 292c59058..d6573713c 100755 --- a/package.json +++ b/package.json @@ -1,61 +1,61 @@ { - "name": "@nostr-dev-kit/monorepo", - "private": true, - "scripts": { - "build": "turbo build", - "build:libs": "turbo build --filter='./core' --filter='./wallet' --filter='./react' --filter='./mobile' --filter='./blossom' --filter='./cache-*' --filter='./cache-memory' --filter='./sessions' --filter='./sync' --filter='./wot' --filter='./svelte'", - "build:core": "cd core && bun run build", - "build:wallet": "cd wallet && bun run build", - "build:hooks": "cd react && bun run build", - "build:mobile": "cd mobile && bun run build", - "build:blossom": "cd blossom && bun run build", - "build:cache-sqlite": "cd cache-sqlite && bun run build", - "dev": "turbo dev --no-cache --continue", - "lint": "biome check .", - "clean": "turbo clean && rm -rf node_modules", - "format": "biome format --write .", - "docs:dev": "npx vitepress dev", - "docs:build": "vitepress build", - "changeset": "changeset", - "cs": "changeset", - "cs:ver": "changeset version", - "test": "bun vitest", - "cs:pub": "changeset publish", - "version-packages": "changeset version", - "release": "turbo build --filter=docs^... && changeset publish", - "docs:preview": "vitepress preview" - }, - "type": "module", - "workspaces": [ - "core", - "cache-dexie", - "cache-memory", - "cache-redis", - "cache-nostr", - "cache-sqlite", - "react", - "mobile", - "sessions", - "sync", - "svelte", - "wallet", - "wot", - "blossom", - "cache-sqlite-wasm" - ], - "engines": { - "node": ">=16.0" - }, - "packageManager": "bun@1.0.0", - "devDependencies": { - "svelte": "^5.39.9", - "turbo": "^2.5.3" - }, - "dependencies": { - "@biomejs/biome": "^2.2.5", - "esbuild": "^0.25.10", - "mermaid": "^11.6.0", - "vitepress": "^1.6.3", - "vitepress-plugin-mermaid": "^2.0.17" - } + "name": "@nostr-dev-kit/monorepo", + "private": true, + "scripts": { + "build": "turbo build", + "build:libs": "turbo build --filter='./core' --filter='./wallet' --filter='./react' --filter='./mobile' --filter='./blossom' --filter='./cache-*' --filter='./cache-memory' --filter='./sessions' --filter='./sync' --filter='./wot' --filter='./svelte'", + "build:core": "cd core && bun run build", + "build:wallet": "cd wallet && bun run build", + "build:hooks": "cd react && bun run build", + "build:mobile": "cd mobile && bun run build", + "build:blossom": "cd blossom && bun run build", + "build:cache-sqlite": "cd cache-sqlite && bun run build", + "dev": "turbo dev --no-cache --continue", + "lint": "biome check .", + "clean": "turbo clean && rm -rf node_modules", + "format": "biome format --write .", + "docs:dev": "npx vitepress dev", + "docs:build": "vitepress build", + "changeset": "changeset", + "cs": "changeset", + "cs:ver": "changeset version", + "test": "bun vitest", + "cs:pub": "changeset publish", + "version-packages": "changeset version", + "release": "turbo build --filter=docs^... && changeset publish", + "docs:preview": "vitepress preview" + }, + "type": "module", + "workspaces": [ + "core", + "cache-dexie", + "cache-memory", + "cache-redis", + "cache-nostr", + "cache-sqlite", + "react", + "mobile", + "sessions", + "sync", + "svelte", + "wallet", + "wot", + "blossom", + "cache-sqlite-wasm" + ], + "engines": { + "node": ">=16.0" + }, + "packageManager": "bun@1.3.0", + "devDependencies": { + "svelte": "^5.39.9", + "turbo": "^2.5.3" + }, + "dependencies": { + "@biomejs/biome": "^2.2.5", + "esbuild": "^0.25.10", + "mermaid": "^11.6.0", + "vitepress": "^1.6.3", + "vitepress-plugin-mermaid": "^2.0.17" + } } From 71b8346a9785d8c3fedb2a35bc090feedb7dca16 Mon Sep 17 00:00:00 2001 From: digitalbase Date: Sat, 11 Oct 2025 13:23:44 +0200 Subject: [PATCH 016/139] Bump node version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index d6573713c..33b8a47ec 100755 --- a/package.json +++ b/package.json @@ -44,7 +44,7 @@ "cache-sqlite-wasm" ], "engines": { - "node": ">=16.0" + "node": ">=22.0" }, "packageManager": "bun@1.3.0", "devDependencies": { From 151627253171b48a2427e38a418235ff93420fe6 Mon Sep 17 00:00:00 2001 From: digitalbase Date: Sat, 11 Oct 2025 16:58:59 +0200 Subject: [PATCH 017/139] Remove /ndk subpath --- .vitepress/config.mts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.vitepress/config.mts b/.vitepress/config.mts index cf18f9f36..8d7b6f54b 100644 --- a/.vitepress/config.mts +++ b/.vitepress/config.mts @@ -4,7 +4,7 @@ import { defineConfig } from "vitepress"; export default defineConfig({ title: "NDK", description: "NDK Docs", - base: "/ndk/", + // base: "/ndk/", ignoreDeadLinks: true, markdown: { theme: { From 30b187683158bc8bc27e55fa8d8426b8661ad372 Mon Sep 17 00:00:00 2001 From: digitalbase Date: Sat, 11 Oct 2025 17:24:35 +0200 Subject: [PATCH 018/139] Move readmes to /advanced folders + fixed blossom docs --- .vitepress/config.mts | 53 ++++-- {docs => blossom/docs}/error-handling.md | 0 {docs => blossom/docs}/getting-started.md | 0 {docs => blossom/docs}/mirroring.md | 0 {docs => blossom/docs}/optimization.md | 0 .../ai-guardrails-examples.ts} | 0 core/docs/{ => advanced}/ai-guardrails.md | 0 .../event-class-registration.md | 0 .../{ => advanced}/relay-metadata-caching.md | 0 .../subscription-internals.md} | 0 core/docs/api-examples.md | 49 ----- core/docs/index.md | 18 -- core/docs/tutorial/{zaps/index.md => zaps.md} | 0 docs/event-class-registration.md | 173 ------------------ {docs/sync => sync/docs}/index.md | 0 15 files changed, 36 insertions(+), 257 deletions(-) rename {docs => blossom/docs}/error-handling.md (100%) rename {docs => blossom/docs}/getting-started.md (100%) rename {docs => blossom/docs}/mirroring.md (100%) rename {docs => blossom/docs}/optimization.md (100%) rename core/docs/{examples/ai-guardrails-example.ts => advanced/ai-guardrails-examples.ts} (100%) rename core/docs/{ => advanced}/ai-guardrails.md (100%) rename core/docs/{ => advanced}/event-class-registration.md (100%) rename core/docs/{ => advanced}/relay-metadata-caching.md (100%) rename core/docs/{internals/subscriptions.md => advanced/subscription-internals.md} (100%) delete mode 100644 core/docs/api-examples.md delete mode 100644 core/docs/index.md rename core/docs/tutorial/{zaps/index.md => zaps.md} (100%) delete mode 100644 docs/event-class-registration.md rename {docs/sync => sync/docs}/index.md (100%) diff --git a/.vitepress/config.mts b/.vitepress/config.mts index 8d7b6f54b..e8101165f 100644 --- a/.vitepress/config.mts +++ b/.vitepress/config.mts @@ -15,7 +15,9 @@ export default defineConfig({ rewrites: { 'docs/:slug.md': ':slug.md', 'core/docs/:slug*': 'core/:slug*', - 'core/docs/:subdir/:slug*': 'core/:subdir/:slug*' + 'core/docs/:subdir/:slug*': 'core/:subdir/:slug*', + 'sync/docs/:slug*': 'sync/:slug*', + 'blossom/docs/:slug*': 'blossom/:slug*', }, themeConfig: { // https://vitepress.dev/reference/default-theme-config @@ -40,16 +42,16 @@ export default defineConfig({ text: "Tutorial", collapsed: true, items: [ - { text: "Local-first", link: "/tutorial/local-first" }, - { text: "Publishing", link: "/tutorial/publishing" }, + { text: "Local-first", link: "/core/tutorial/local-first" }, + { text: "Publishing", link: "/core/tutorial/publishing" }, { text: "Subscription Management", - link: "/tutorial/subscription-management", + link: "/core/tutorial/subscription-management", }, - { text: "Mute Filtering", link: "/tutorial/mute-filtering" }, - { text: "Signer Persistence", link: "/tutorial/signer-persistence" }, - { text: "Speed", link: "/tutorial/speed" }, - { text: "Zaps", link: "/tutorial/zaps" }, + { text: "Mute Filtering", link: "/core/tutorial/mute-filtering" }, + { text: "Signer Persistence", link: "/core/tutorial/signer-persistence" }, + { text: "Speed", link: "/core/tutorial/speed" }, + { text: "Zaps", link: "/core/tutorial/zaps" }, ], }, { @@ -78,13 +80,6 @@ export default defineConfig({ { text: "Nutzaps", link: "/wallet/nutzaps" }, ], }, - { - text: "Sync & Negentropy", - collapsed: true, - items: [ - { text: "Introduction", link: "/sync/index" }, - ], - }, { text: "Web of Trust", collapsed: true, @@ -132,12 +127,36 @@ export default defineConfig({ collapsed: true, items: [ { text: "Introduction", link: "/blossom/getting-started" }, + { text: "Error Handling", link: "/blossom/error-handling" }, + { text: "Mirroring", link: "/blossom/mirroring" }, + { text: "Optimization", link: "/blossom/optimization" }, ] }, { - text: "Internals", + text: "Advanced Topics", collapsed: true, - items: [{ text: "Subscription Lifecycle", link: "/internals/subscriptions" }], + items: [ + { + text: "AI Guardrails", + link: "/core/advanced/ai-guardrails" + }, + { + text: "Subscription Lifecycle", + link: "/core/advanced/subscription-internals" + }, + { + text: "Event Class Registration", + link: "/core/advanced/event-class-registration" + }, + { + text: "Relay Metadata Caching", + link: "/core/advanced/relay-metadata-caching" + }, + { + text: "Sync & Negentropy", + link: "/sync" + }, + ], }, ], diff --git a/docs/error-handling.md b/blossom/docs/error-handling.md similarity index 100% rename from docs/error-handling.md rename to blossom/docs/error-handling.md diff --git a/docs/getting-started.md b/blossom/docs/getting-started.md similarity index 100% rename from docs/getting-started.md rename to blossom/docs/getting-started.md diff --git a/docs/mirroring.md b/blossom/docs/mirroring.md similarity index 100% rename from docs/mirroring.md rename to blossom/docs/mirroring.md diff --git a/docs/optimization.md b/blossom/docs/optimization.md similarity index 100% rename from docs/optimization.md rename to blossom/docs/optimization.md diff --git a/core/docs/examples/ai-guardrails-example.ts b/core/docs/advanced/ai-guardrails-examples.ts similarity index 100% rename from core/docs/examples/ai-guardrails-example.ts rename to core/docs/advanced/ai-guardrails-examples.ts diff --git a/core/docs/ai-guardrails.md b/core/docs/advanced/ai-guardrails.md similarity index 100% rename from core/docs/ai-guardrails.md rename to core/docs/advanced/ai-guardrails.md diff --git a/core/docs/event-class-registration.md b/core/docs/advanced/event-class-registration.md similarity index 100% rename from core/docs/event-class-registration.md rename to core/docs/advanced/event-class-registration.md diff --git a/core/docs/relay-metadata-caching.md b/core/docs/advanced/relay-metadata-caching.md similarity index 100% rename from core/docs/relay-metadata-caching.md rename to core/docs/advanced/relay-metadata-caching.md diff --git a/core/docs/internals/subscriptions.md b/core/docs/advanced/subscription-internals.md similarity index 100% rename from core/docs/internals/subscriptions.md rename to core/docs/advanced/subscription-internals.md diff --git a/core/docs/api-examples.md b/core/docs/api-examples.md deleted file mode 100644 index 6bd8bb5c1..000000000 --- a/core/docs/api-examples.md +++ /dev/null @@ -1,49 +0,0 @@ ---- -outline: deep ---- - -# Runtime API Examples - -This page demonstrates usage of some of the runtime APIs provided by VitePress. - -The main `useData()` API can be used to access site, theme, and page data for the current page. It works in both `.md` and `.vue` files: - -```md - - -## Results - -### Theme Data -

{{ theme }}
- -### Page Data -
{{ page }}
- -### Page Frontmatter -
{{ frontmatter }}
-``` - - - -## Results - -### Theme Data -
{{ theme }}
- -### Page Data -
{{ page }}
- -### Page Frontmatter -
{{ frontmatter }}
- -## More - -Check out the documentation for the [full list of runtime APIs](https://vitepress.dev/reference/runtime-api#usedata). diff --git a/core/docs/index.md b/core/docs/index.md deleted file mode 100644 index 52fac990a..000000000 --- a/core/docs/index.md +++ /dev/null @@ -1,18 +0,0 @@ ---- -# https://vitepress.dev/reference/default-theme-home-page -layout: home - -hero: - name: "NDK Documentation" - tagline: "Nostr Development Kit Docs" - actions: - - theme: brand - text: Getting Started - link: /getting-started/introduction.html - - theme: secondary - text: References - link: https://github.com/nostr-dev-kit/ndk/blob/master/REFERENCES.md - ---- - -NDK is a nostr development kit that makes the experience of building Nostr-related applications, whether they are relays, clients, or anything in between, better, more reliable and overall nicer to work with than existing solutions. \ No newline at end of file diff --git a/core/docs/tutorial/zaps/index.md b/core/docs/tutorial/zaps.md similarity index 100% rename from core/docs/tutorial/zaps/index.md rename to core/docs/tutorial/zaps.md diff --git a/docs/event-class-registration.md b/docs/event-class-registration.md deleted file mode 100644 index 367cce8fe..000000000 --- a/docs/event-class-registration.md +++ /dev/null @@ -1,173 +0,0 @@ -# Custom Event Class Registration - -NDK provides a registration system that allows you to register custom event classes to work with the `wrapEvent()` function. This enables you to create your own event types that integrate seamlessly with NDK's event wrapping system. - -## Overview - -The `wrapEvent()` function automatically wraps raw `NDKEvent` objects into more specific event types based on their `kind` property. By default, NDK includes many built-in event classes (like `NDKArticle`, `NDKImage`, etc.), but you can also register your own custom event classes. - -## Registering Custom Event Classes - -### Requirements - -Your custom event class must meet these requirements: - -1. **Extend NDKEvent**: Your class should extend the `NDKEvent` base class -2. **Static `kinds` property**: An array of event kind numbers this class handles -3. **Static `from` method**: A factory method that creates an instance from an `NDKEvent` - -### Example - -```typescript -import { NDKEvent, registerEventClass } from "@nostr-dev-kit/ndk"; - -class MyCustomEvent extends NDKEvent { - static kinds = [12345]; // Custom event kind number - - static from(event: NDKEvent) { - return new MyCustomEvent(event.ndk, event); - } - - constructor(ndk: NDK | undefined, rawEvent?: NostrEvent | NDKEvent) { - super(ndk, rawEvent); - this.kind ??= 12345; - } - - // Add your custom methods and properties - get customProperty(): string | undefined { - return this.tagValue("custom"); - } - - set customProperty(value: string | undefined) { - this.removeTag("custom"); - if (value) this.tags.push(["custom", value]); - } -} - -// Register the class -registerEventClass(MyCustomEvent); -``` - -### Multiple Kinds - -Your custom event class can handle multiple event kinds: - -```typescript -class MyMultiKindEvent extends NDKEvent { - static kinds = [12345, 12346, 12347]; - - static from(event: NDKEvent) { - return new MyMultiKindEvent(event.ndk, event); - } - - // Implementation... -} - -registerEventClass(MyMultiKindEvent); -``` - -## API Reference - -### `registerEventClass(eventClass)` - -Registers a custom event class to be used with `wrapEvent()`. - -**Parameters:** -- `eventClass`: An object that implements the `NDKEventClass` interface - -**Example:** -```typescript -registerEventClass(MyCustomEvent); -``` - -### `unregisterEventClass(eventClass)` - -Removes a previously registered event class. - -**Parameters:** -- `eventClass`: The event class to unregister - -**Example:** -```typescript -unregisterEventClass(MyCustomEvent); -``` - -### `getRegisteredEventClasses()` - -Returns a Set of all currently registered custom event classes. - -**Returns:** -- `Set`: Set of registered event classes - -**Example:** -```typescript -const registeredClasses = getRegisteredEventClasses(); -console.log(`${registeredClasses.size} custom classes registered`); -``` - -## How It Works - -When you call `wrapEvent()` on an `NDKEvent`, the function: - -1. Creates a mapping of event kinds to their corresponding classes -2. Includes both built-in NDK classes and your registered custom classes -3. Looks up the event's `kind` in this mapping -4. If found, calls the class's `from()` method to create a wrapped instance -5. If not found, returns the original `NDKEvent` - -```typescript -// This will now return a MyCustomEvent instance for kind 12345 -const wrappedEvent = wrapEvent(rawEvent); // rawEvent.kind === 12345 -``` - -## Best Practices - -1. **Choose unique kind numbers**: Make sure your custom event kinds don't conflict with existing Nostr event kinds -2. **Follow NIP specifications**: If you're implementing a NIP, follow its specifications for event structure -3. **Extend existing functionality**: Build upon NDK's existing event capabilities rather than replacing them -4. **Handle errors gracefully**: Your `from()` method should handle invalid or malformed events -5. **Test thoroughly**: Test your custom event classes with various input scenarios - -## Example: Custom Chat Message Event - -```typescript -import { NDKEvent, registerEventClass, NDKKind } from "@nostr-dev-kit/ndk"; - -class NDKChatMessage extends NDKEvent { - static kinds = [42]; // Using kind 42 for chat messages - - static from(event: NDKEvent) { - return new NDKChatMessage(event.ndk, event); - } - - constructor(ndk: NDK | undefined, rawEvent?: NostrEvent | NDKEvent) { - super(ndk, rawEvent); - this.kind ??= 42; - } - - get roomId(): string | undefined { - return this.tagValue("room"); - } - - set roomId(roomId: string | undefined) { - this.removeTag("room"); - if (roomId) this.tags.push(["room", roomId]); - } - - get replyTo(): string | undefined { - return this.tagValue("reply"); - } - - set replyTo(eventId: string | undefined) { - this.removeTag("reply"); - if (eventId) this.tags.push(["reply", eventId]); - } -} - -// Register the custom chat message class -registerEventClass(NDKChatMessage); - -// Now wrapEvent will automatically create NDKChatMessage instances for kind 42 events -``` - -This registration system provides a flexible way to extend NDK with your own event types while maintaining compatibility with the existing event wrapping infrastructure. \ No newline at end of file diff --git a/docs/sync/index.md b/sync/docs/index.md similarity index 100% rename from docs/sync/index.md rename to sync/docs/index.md From 63ff0c5ee5c76f2f80b32bafeabc52461d83fddd Mon Sep 17 00:00:00 2001 From: digitalbase Date: Sat, 11 Oct 2025 17:28:01 +0200 Subject: [PATCH 019/139] Fixed blossom readmes --- .vitepress/config.mts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.vitepress/config.mts b/.vitepress/config.mts index e8101165f..348c4ad32 100644 --- a/.vitepress/config.mts +++ b/.vitepress/config.mts @@ -17,6 +17,7 @@ export default defineConfig({ 'core/docs/:slug*': 'core/:slug*', 'core/docs/:subdir/:slug*': 'core/:subdir/:slug*', 'sync/docs/:slug*': 'sync/:slug*', + 'blossom/:slug.md': 'blossom/:slug.md', 'blossom/docs/:slug*': 'blossom/:slug*', }, themeConfig: { @@ -126,7 +127,8 @@ export default defineConfig({ text: "Blossom (Media)", collapsed: true, items: [ - { text: "Introduction", link: "/blossom/getting-started" }, + { text: "Intro", link: "/blossom/README" }, + { text: "Getting Started", link: "/blossom/getting-started" }, { text: "Error Handling", link: "/blossom/error-handling" }, { text: "Mirroring", link: "/blossom/mirroring" }, { text: "Optimization", link: "/blossom/optimization" }, From a0eb8fe415245f522554690b397814bb1b99c536 Mon Sep 17 00:00:00 2001 From: digitalbase Date: Sat, 11 Oct 2025 17:30:22 +0200 Subject: [PATCH 020/139] Mobile docs --- .vitepress/config.mts | 9 +++++++-- mobile/docs/index.md | 31 ------------------------------- 2 files changed, 7 insertions(+), 33 deletions(-) delete mode 100644 mobile/docs/index.md diff --git a/.vitepress/config.mts b/.vitepress/config.mts index 348c4ad32..93674708f 100644 --- a/.vitepress/config.mts +++ b/.vitepress/config.mts @@ -19,6 +19,8 @@ export default defineConfig({ 'sync/docs/:slug*': 'sync/:slug*', 'blossom/:slug.md': 'blossom/:slug.md', 'blossom/docs/:slug*': 'blossom/:slug*', + 'mobile/:slug.md': 'mobile/:slug.md', + 'mobile/docs/:slug*': 'mobile/:slug*', }, themeConfig: { // https://vitepress.dev/reference/default-theme-config @@ -118,16 +120,19 @@ export default defineConfig({ text: "Mobile", collapsed: true, items: [ - { text: "Introduction", link: "/mobile/index" }, + { text: "Introduction", link: "/mobile/README" }, { text: "Session", link: "/mobile/session" }, { text: "Wallet", link: "/mobile/wallet" }, + { text: "Subscriptions", link: "/mobile/subscriptions" }, + { text: "Nutzaps", link: "/mobile/nutzaps" }, + { text: "Mint", link: "/mobile/mint" }, ], }, { text: "Blossom (Media)", collapsed: true, items: [ - { text: "Intro", link: "/blossom/README" }, + { text: "Introduction", link: "/blossom/README" }, { text: "Getting Started", link: "/blossom/getting-started" }, { text: "Error Handling", link: "/blossom/error-handling" }, { text: "Mirroring", link: "/blossom/mirroring" }, diff --git a/mobile/docs/index.md b/mobile/docs/index.md deleted file mode 100644 index 79c465355..000000000 --- a/mobile/docs/index.md +++ /dev/null @@ -1,31 +0,0 @@ -# NDK Mobile - -A React Native/Expo implementation of [NDK (Nostr Development Kit)](https://github.com/nostr-dev-kit/ndk) that provides a complete toolkit for building Nostr applications on mobile platforms. - -## Features - -- ๐Ÿ” Multiple signer implementations supported via NDK Core (NIP-07, NIP-46, Private Key) and NDK Mobile (NIP-55). -- ๐Ÿ’พ SQLite-based caching for offline support (`NDKCacheAdapterSqlite`). -- ๐Ÿ”„ Subscription management with automatic reconnection. -- ๐Ÿ“ฑ React Native and Expo compatibility. -- ๐Ÿช React hooks for easy state management (`useNDKStore`, `useNDKSessions`, `useSubscribe`, etc. via `@nostr-dev-kit/react`). -- ๐Ÿ‘› Integrated wallet support (via `@nostr-dev-kit/ndk-wallet`). -- ๐Ÿ”„ **Persistent Sessions:** Automatically saves and loads user sessions and signers using `expo-secure-store`. - -## Installation - -```sh -# Install NDK Core, Hooks, Wallet, and Mobile -npm install @nostr-dev-kit/ndk @nostr-dev-kit/react @nostr-dev-kit/ndk-wallet @nostr-dev-kit/mobile expo-secure-store react-native-get-random-values @bacons/text-decoder expo-sqlite expo-crypto expo-file-system -# Ensure peer dependencies for expo-sqlite are met -npm install expo-sqlite/next -``` -*Note: Ensure all necessary peer dependencies for Expo modules like `expo-sqlite` are installed.* - -## Usage - -When using this library, you primarily interact with the core `NDK` instance and hooks from `@nostr-dev-kit/react`. `ndk-mobile` provides the `NDKCacheAdapterSqlite` for persistence, the `useSessionMonitor` hook for automatic session persistence, and the NIP-55 signer. - -## Example - -For a real application using this look at [Olas](https://github.com/pablof7z/olas). From 06546d6bb78241d7c999282e7779277c559f7041 Mon Sep 17 00:00:00 2001 From: digitalbase Date: Sat, 11 Oct 2025 17:32:08 +0200 Subject: [PATCH 021/139] Migrate session docs --- .vitepress/config.mts | 4 +- sessions/docs/index.md | 141 ----------------------------------------- 2 files changed, 3 insertions(+), 142 deletions(-) delete mode 100644 sessions/docs/index.md diff --git a/.vitepress/config.mts b/.vitepress/config.mts index 93674708f..6c53b0e1b 100644 --- a/.vitepress/config.mts +++ b/.vitepress/config.mts @@ -21,6 +21,8 @@ export default defineConfig({ 'blossom/docs/:slug*': 'blossom/:slug*', 'mobile/:slug.md': 'mobile/:slug.md', 'mobile/docs/:slug*': 'mobile/:slug*', + 'sessions/:slug.md': 'sessions/:slug.md', + 'sessions/docs/:slug*': 'sessions/:slug*', }, themeConfig: { // https://vitepress.dev/reference/default-theme-config @@ -110,7 +112,7 @@ export default defineConfig({ text: "Sessions", collapsed: true, items: [ - { text: "Introduction", link: "/sessions/index" }, + { text: "Introduction", link: "/sessions/README" }, { text: "Quick Start", link: "/sessions/quick-start" }, { text: "API Reference", link: "/sessions/api" }, { text: "Migration Guide", link: "/sessions/migration" } diff --git a/sessions/docs/index.md b/sessions/docs/index.md deleted file mode 100644 index abf57f631..000000000 --- a/sessions/docs/index.md +++ /dev/null @@ -1,141 +0,0 @@ -# Sessions - -`@nostr-dev-kit/sessions` is a framework-agnostic session management library for NDK that provides multi-account support, automatic data fetching, and flexible persistence. - -## Why Sessions? - -Managing user authentication and session state in Nostr applications can be complex. The sessions package simplifies: - -- **Multi-account management** - Let users switch between multiple Nostr identities seamlessly -- **Automatic data fetching** - Automatically fetch and cache follows, mutes, relay lists, and more -- **Persistence** - Save and restore sessions across app restarts -- **Framework agnostic** - Works with React, Svelte, Vue, vanilla JS, Node.js, etc. - -## Key Features - -### ๐Ÿ” Multiple Account Support - -Users can log in with multiple Nostr accounts and switch between them instantly. Perfect for: -- Personal and business accounts -- Testing with multiple identities -- Content creators managing multiple personas - -### ๐Ÿ’พ Flexible Storage - -Built-in storage adapters for: -- **LocalStorage** - Browser-based persistence -- **FileStorage** - Node.js/CLI applications -- **MemoryStorage** - Testing or temporary sessions -- **Custom** - Implement your own storage backend - -### ๐Ÿ”„ Auto-Fetch User Data - -On login, automatically fetch: -- Contact list (kind 3 follows) -- Mute lists (kind 10000) -- Relay lists (kind 10002) -- Blocked relay lists (kind 10001) -- NIP-60 wallet data (kind 17375) -- Any custom replaceable event kinds - -### ๐ŸŽฏ Framework Integration - -Works seamlessly with: -- React (via `@nostr-dev-kit/react`) -- Svelte 5 (via `@nostr-dev-kit/ndk-svelte5`) -- Mobile (React Native via `@nostr-dev-kit/mobile`) -- Vanilla JavaScript -- Node.js/CLI applications - -## Installation - -```bash -npm install @nostr-dev-kit/sessions -# or -bun add @nostr-dev-kit/sessions -``` - -## Quick Example - -```typescript -import NDK from '@nostr-dev-kit/ndk'; -import { NDKSessionManager, LocalStorage } from '@nostr-dev-kit/sessions'; -import { NDKPrivateKeySigner } from '@nostr-dev-kit/ndk'; - -const ndk = new NDK({ explicitRelayUrls: ['wss://relay.damus.io'] }); -await ndk.connect(); - -const sessions = new NDKSessionManager(ndk, { - storage: new LocalStorage(), - autoSave: true, - fetches: { - follows: true, - mutes: true, - relayList: true - } -}); - -// Restore previous sessions -await sessions.restore(); - -// Login with auto-fetch -const signer = new NDKPrivateKeySigner(nsec); -await sessions.login(signer); - -console.log('Active user:', sessions.activeUser); -console.log('Follows:', sessions.activeSession?.followSet?.size); -``` - -## Next Steps - -- [Quick Start Guide](./quick-start) - Get up and running -- [API Reference](./api) - Complete API documentation -- [Migration Guide](./migration) - Migrating from ndk-hooks - -## Use Cases - -### Browser Applications -Perfect for web apps that need: -- User login/logout -- Multi-account switching -- Persistent sessions across page reloads -- Automatic relay and follow list management - -### Node.js/CLI Tools -Ideal for command-line tools that need: -- Saved credentials -- Multiple identity management -- Automated publishing with saved accounts - -### Mobile Applications -Great for React Native apps needing: -- Secure session storage -- Multi-account support -- Offline-first data caching - -## Architecture - -The sessions package is built on three core components: - -1. **NDKSessionManager** - Main API for managing sessions -2. **SessionStorage** - Pluggable storage backends -3. **NDKSession** - Individual session state and data - -All session state changes are observable via the subscribe pattern, making it easy to integrate with any reactive framework. - -## Security Considerations - -โš ๏ธ **Important:** Session serialization stores private keys. In production: - -1. Use encrypted storage when possible -2. Never commit session files to version control -3. Use environment variables for sensitive keys -4. Consider NIP-07 (browser extensions) or NIP-46 (remote signers) for better security - -## Framework-Specific Documentation - -For framework-specific implementations using sessions: - -- **React** - See [`@nostr-dev-kit/react` hooks documentation](/hooks/session-management) -- **Svelte 5** - See [`@nostr-dev-kit/ndk-svelte5` documentation](/wrappers/svelte) -- **Mobile** - See [`@nostr-dev-kit/mobile` documentation](/mobile/session) From 09b21029271c407581a90151f4eb8311920b55a2 Mon Sep 17 00:00:00 2001 From: digitalbase Date: Sat, 11 Oct 2025 17:42:56 +0200 Subject: [PATCH 022/139] Fixed cache adapters --- .vitepress/config.mts | 32 +- .../{docs/INDEX.md => README.md} | 0 wot/docs/index.md | 340 ------------------ 3 files changed, 19 insertions(+), 353 deletions(-) rename cache-sqlite-wasm/{docs/INDEX.md => README.md} (100%) delete mode 100644 wot/docs/index.md diff --git a/.vitepress/config.mts b/.vitepress/config.mts index 6c53b0e1b..5322f44ea 100644 --- a/.vitepress/config.mts +++ b/.vitepress/config.mts @@ -14,6 +14,13 @@ export default defineConfig({ }, rewrites: { 'docs/:slug.md': ':slug.md', + 'cache-dexie/:slug.md': 'cache/dexie/:slug.md', + 'cache-memory/:slug.md': 'cache/memory/:slug.md', + 'cache-nostr/:slug.md': 'cache/nostr/:slug.md', + 'cache-redis/:slug.md': 'cache/redis/:slug.md', + 'cache-sqlite/:slug.md': 'cache/sqlite/:slug.md', + 'cache-sqlite-wasm/:slug.md': 'cache/sqlite-wasm/:slug.md', + 'cache-sqlite-wasm/docs/:slug.md': 'cache/sqlite-wasm/:slug.md', 'core/docs/:slug*': 'core/:slug*', 'core/docs/:subdir/:slug*': 'core/:subdir/:slug*', 'sync/docs/:slug*': 'sync/:slug*', @@ -23,6 +30,7 @@ export default defineConfig({ 'mobile/docs/:slug*': 'mobile/:slug*', 'sessions/:slug.md': 'sessions/:slug.md', 'sessions/docs/:slug*': 'sessions/:slug*', + 'wot/:slug.md': 'wot/:slug.md' }, themeConfig: { // https://vitepress.dev/reference/default-theme-config @@ -63,12 +71,14 @@ export default defineConfig({ text: "Cache Adapters", collapsed: true, items: [ - { text: "In-memory LRU", link: "/cache/memory" }, - { text: "In-memory + dexie", link: "/cache/dexie" }, - { text: "Local Nostr Relay", link: "/cache/nostr" }, + { text: "Memory / LRU", link: "/cache/memory/README" }, + { text: "Dexie / IndexedDB", link: "cache/dexie/README" }, + { text: "Local Nostr Relay", link: "/cache/nostr/README" }, + { text: "Redis", link: "/cache/redis/README" }, + { text: "SQLite", link: "/cache/sqlite/README" }, { - text: "SQLite (WASM)", - link: "/cache/sqlite-wasm/INDEX", + text: "SQLite WASM", + link: "/cache/sqlite-wasm/README", items: [ { text: "Bundling", link: "/cache/sqlite-wasm/bundling" }, { text: "Web Worker Setup", link: "/cache/sqlite-wasm/web-worker-setup" } @@ -85,14 +95,6 @@ export default defineConfig({ { text: "Nutzaps", link: "/wallet/nutzaps" }, ], }, - { - text: "Web of Trust", - collapsed: true, - items: [ - { text: "Introduction", link: "/wot/index" }, - { text: "Negentropy Integration", link: "/wot/negentropy" }, - ], - }, { text: "Wrappers", collapsed: true, @@ -165,6 +167,10 @@ export default defineConfig({ text: "Sync & Negentropy", link: "/sync" }, + { + text: "Web of Trust (WOT)", + link: "/wot/README" + }, ], }, ], diff --git a/cache-sqlite-wasm/docs/INDEX.md b/cache-sqlite-wasm/README.md similarity index 100% rename from cache-sqlite-wasm/docs/INDEX.md rename to cache-sqlite-wasm/README.md diff --git a/wot/docs/index.md b/wot/docs/index.md deleted file mode 100644 index 518a36bac..000000000 --- a/wot/docs/index.md +++ /dev/null @@ -1,340 +0,0 @@ -# Web of Trust (WOT) - -`@nostr-dev-kit/wot` provides Web of Trust utilities for filtering and ranking content based on your social graph. - -## Installation - -```bash -npm install @nostr-dev-kit/wot -``` - -## Quick Start - -```typescript -import NDK from "@nostr-dev-kit/ndk"; -import { NDKWoT, filterByWoT, rankByWoT } from "@nostr-dev-kit/wot"; - -const ndk = new NDK(); -await ndk.connect(); - -// Build WOT graph from your perspective -const wot = new NDKWoT(ndk, myPubkey); -await wot.load({ - depth: 2, // 2 hops out from you - maxFollows: 1000, // limit follows per user - timeout: 30000 // 30s timeout -}); - -console.log(`WOT has ${wot.size} users`); -``` - -## Filtering Events - -The key insight of WOT is to **fetch broadly, filter locally**. This lets you adjust WOT strictness without re-fetching from relays. - -```typescript -// Fetch events broadly -const events = await ndk.fetchEvents({ - kinds: [1], - limit: 500 -}); - -// Filter by WOT -const filtered = filterByWoT(wot, events, { - maxDepth: 2, // only users within 2 hops - minScore: 0.5, // minimum WOT score (0-1) - includeUnknown: false // exclude users outside WOT -}); - -// Easily adjust view without re-fetching -const broader = filterByWoT(wot, events, { maxDepth: 3 }); -const strictest = filterByWoT(wot, events, { maxDepth: 1 }); -``` - -## WOT as Automatic Mute Filter - -You can integrate WOT with NDK's `muteFilter` to automatically filter out users outside your web of trust at the NDK level. This means all subscriptions and event fetches will automatically exclude non-WOT users. - -```typescript -import NDK, { NDKEvent } from "@nostr-dev-kit/ndk"; -import { NDKWoT } from "@nostr-dev-kit/wot"; - -const ndk = new NDK(); -await ndk.connect(); - -// Build WOT graph -const wot = new NDKWoT(ndk, myPubkey); -await wot.load({ depth: 2 }); - -// Set WOT-based mute filter -ndk.muteFilter = (event: NDKEvent) => { - // Check manual mutes first - if (ndk.mutedIds.has(event.pubkey)) return true; - if (event.id && ndk.mutedIds.has(event.id)) return true; - - // Auto-mute users outside WOT - if (!wot.includes(event.pubkey, { maxDepth: 2 })) { - return true; // Mute this event - } - - return false; // Don't mute -}; - -// Now all subscriptions automatically filter by WOT -const sub = ndk.subscribe({ kinds: [1], limit: 100 }); -sub.on('event', (event) => { - // Only events from WOT users will appear here - console.log(event.content); -}); -``` - -### Hybrid Approach: WOT + Manual Mutes + Keywords - -Combine WOT with manual mutes and content filtering: - -```typescript -ndk.muteFilter = (event: NDKEvent) => { - // 1. Manual mutes (highest priority) - if (ndk.mutedIds.has(event.pubkey)) return true; - if (event.id && ndk.mutedIds.has(event.id)) return true; - - // 2. WOT check (only for unknown users) - if (!wot.includes(event.pubkey, { maxDepth: 2 })) { - return true; // Auto-mute non-WOT users - } - - // 3. Content filtering (even for WOT users) - const blockedWords = ['spam', 'scam']; - if (blockedWords.some(word => event.content.toLowerCase().includes(word))) { - return true; - } - - return false; -}; -``` - -### Adjustable WOT Strictness - -Allow users to adjust WOT strictness dynamically: - -```typescript -let wotDepth = 2; // Default: 2 hops - -ndk.muteFilter = (event: NDKEvent) => { - if (ndk.mutedIds.has(event.pubkey)) return true; - - // Use current depth setting - return !wot.includes(event.pubkey, { maxDepth: wotDepth }); -}; - -// User can adjust strictness -function setWoTStrictness(depth: number) { - wotDepth = depth; - // Trigger UI refresh to re-apply filter -} - -// Slider: 1 (strictest) -> 3 (most permissive) -setWoTStrictness(1); // Only direct follows -setWoTStrictness(2); // Friends of friends -setWoTStrictness(3); // 3 hops out -``` - -### WOT-Based Score Thresholds - -Filter by WOT score instead of depth: - -```typescript -const MIN_WOT_SCORE = 0.5; // 0-1 scale - -ndk.muteFilter = (event: NDKEvent) => { - if (ndk.mutedIds.has(event.pubkey)) return true; - - // Mute if score too low - const score = wot.getScore(event.pubkey); - if (score < MIN_WOT_SCORE) { - return true; - } - - return false; -}; -``` - -## Ranking Events - -Sort events by WOT proximity: - -```typescript -// Rank by distance (closer = higher) -const ranked = rankByWoT(wot, events, { - algorithm: "distance", // "distance", "score", or "followers" - unknownsLast: true // put unknown users at the end -}); - -// Custom ranking -const custom = rankByWoT(wot, events, { - comparator: (a, b) => { - const aScore = wot.getScore(a.pubkey); - const bScore = wot.getScore(b.pubkey); - return bScore - aScore; - } -}); -``` - -## WOT Queries - -```typescript -// Check if user is in WOT -if (wot.includes(pubkey, { maxDepth: 2 })) { - console.log("User is in WOT"); -} - -// Get WOT score (0-1, higher = closer) -const score = wot.getScore(pubkey); - -// Get distance (hops from root) -const distance = wot.getDistance(pubkey); // returns number or null - -// Get scores for multiple users -const scores = wot.getScores([pubkey1, pubkey2, pubkey3]); - -// Get all pubkeys in WOT -const allUsers = wot.getAllPubkeys({ maxDepth: 2 }); -``` - -## Ranking Algorithms - -### Distance -Ranks by graph distance from root user. Closer users rank higher. - -```typescript -rankByWoT(wot, events, { algorithm: "distance" }); -``` - -### Score -Ranks by WOT score (inverse of depth: `1 / (depth + 1)`). Higher scores rank higher. - -```typescript -rankByWoT(wot, events, { algorithm: "score" }); -``` - -### Followers -Ranks by number of WOT users following them. More popular within your WOT ranks higher. - -```typescript -rankByWoT(wot, events, { algorithm: "followers" }); -``` - -## Integration with Reactive Frameworks - -Use with `ndk-svelte5` or `ndk-hooks` for reactive WOT views: - -### Svelte 5 (coming soon) -```typescript -import { wotView } from '@nostr-dev-kit/ndk-svelte5'; - -const view = wotView(wot, events, { - maxDepth: $state(2), - showUnknowns: $state(false) -}); - -// Reactive -$effect(() => { - console.log(view.filtered); -}); -``` - -### React (coming soon) -```typescript -import { useWoTView } from '@nostr-dev-kit/react'; - -function Feed() { - const [depth, setDepth] = useState(2); - - const { filtered, ranked } = useWoTView(wot, events, { - maxDepth: depth - }); - - return ; -} -``` - -## API Reference - -### NDKWoT - -- `constructor(ndk: NDK, rootPubkey: string)` - Create WOT instance -- `load(options: WoTBuildOptions): Promise` - Build the graph -- `getScore(pubkey: string): number` - Get WOT score (0-1) -- `getDistance(pubkey: string): number | null` - Get hops from root -- `includes(pubkey: string, options?): boolean` - Check if in WOT -- `getAllPubkeys(options?): string[]` - Get all pubkeys in WOT -- `getScores(pubkeys: string[]): Map` - Batch scores -- `getNode(pubkey: string): WoTNode | null` - Get WOT node details -- `size: number` - Total nodes in graph -- `isLoaded(): boolean` - Check if graph is loaded - -### Filter Functions - -#### filterByWoT -```typescript -filterByWoT( - wot: NDKWoT, - events: NDKEvent[], - options: WoTFilterOptions -): NDKEvent[] -``` - -#### rankByWoT -```typescript -rankByWoT( - wot: NDKWoT, - events: NDKEvent[], - options: WoTRankOptions -): NDKEvent[] -``` - -#### createWoTComparator -```typescript -createWoTComparator( - wot: NDKWoT, - options: WoTRankOptions -): (a: NDKEvent, b: NDKEvent) => number -``` - -## Types - -### WoTBuildOptions -```typescript -interface WoTBuildOptions { - depth: number; // Max hops to traverse - maxFollows?: number; // Max follows per user - timeout?: number; // Timeout in ms -} -``` - -### WoTFilterOptions -```typescript -interface WoTFilterOptions { - maxDepth?: number; // Max depth to include - minScore?: number; // Min WOT score (0-1) - includeUnknown?: boolean; // Include non-WOT users -} -``` - -### WoTRankOptions -```typescript -interface WoTRankOptions { - algorithm?: "distance" | "score" | "followers"; - unknownsLast?: boolean; - comparator?: (a: NDKEvent, b: NDKEvent) => number; -} -``` - -### WoTNode -```typescript -interface WoTNode { - pubkey: string; - depth: number; // Hops from root - followedBy: Set; // Who follows this user -} -``` From 32c3e10e788cdc99871a51dfaeceb335998ea47a Mon Sep 17 00:00:00 2001 From: digitalbase Date: Sat, 11 Oct 2025 17:44:41 +0200 Subject: [PATCH 023/139] Moved cache adapters to advanced --- .vitepress/config.mts | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/.vitepress/config.mts b/.vitepress/config.mts index 5322f44ea..98208c3eb 100644 --- a/.vitepress/config.mts +++ b/.vitepress/config.mts @@ -67,25 +67,6 @@ export default defineConfig({ { text: "Zaps", link: "/core/tutorial/zaps" }, ], }, - { - text: "Cache Adapters", - collapsed: true, - items: [ - { text: "Memory / LRU", link: "/cache/memory/README" }, - { text: "Dexie / IndexedDB", link: "cache/dexie/README" }, - { text: "Local Nostr Relay", link: "/cache/nostr/README" }, - { text: "Redis", link: "/cache/redis/README" }, - { text: "SQLite", link: "/cache/sqlite/README" }, - { - text: "SQLite WASM", - link: "/cache/sqlite-wasm/README", - items: [ - { text: "Bundling", link: "/cache/sqlite-wasm/bundling" }, - { text: "Web Worker Setup", link: "/cache/sqlite-wasm/web-worker-setup" } - ] - }, - ], - }, { text: "Wallet", collapsed: true, @@ -171,6 +152,25 @@ export default defineConfig({ text: "Web of Trust (WOT)", link: "/wot/README" }, + { + text: "Cache Adapters", + collapsed: true, + items: [ + { text: "Memory / LRU", link: "/cache/memory/README" }, + { text: "Dexie / IndexedDB", link: "cache/dexie/README" }, + { text: "Local Nostr Relay", link: "/cache/nostr/README" }, + { text: "Redis", link: "/cache/redis/README" }, + { text: "SQLite", link: "/cache/sqlite/README" }, + { + text: "SQLite WASM", + link: "/cache/sqlite-wasm/README", + items: [ + { text: "Bundling", link: "/cache/sqlite-wasm/bundling" }, + { text: "Web Worker Setup", link: "/cache/sqlite-wasm/web-worker-setup" } + ] + }, + ], + }, ], }, ], From 94a88addfd54b86556b9d378cf3f8747fb39a83a Mon Sep 17 00:00:00 2001 From: digitalbase Date: Sat, 11 Oct 2025 17:50:50 +0200 Subject: [PATCH 024/139] Wallet docs --- .vitepress/config.mts | 16 +++++++++++++--- wallet/docs/index.md | 27 --------------------------- 2 files changed, 13 insertions(+), 30 deletions(-) delete mode 100644 wallet/docs/index.md diff --git a/.vitepress/config.mts b/.vitepress/config.mts index 98208c3eb..412ba86cb 100644 --- a/.vitepress/config.mts +++ b/.vitepress/config.mts @@ -30,6 +30,8 @@ export default defineConfig({ 'mobile/docs/:slug*': 'mobile/:slug*', 'sessions/:slug.md': 'sessions/:slug.md', 'sessions/docs/:slug*': 'sessions/:slug*', + 'wallet/:slug.md': 'wallet/:slug.md', + 'wallet/docs/:slug*': 'wallet/:slug*', 'wot/:slug.md': 'wot/:slug.md' }, themeConfig: { @@ -71,9 +73,17 @@ export default defineConfig({ text: "Wallet", collapsed: true, items: [ - { text: "Introduction", link: "/wallet/index" }, - { text: "Nutsack (NIP-60)", link: "/wallet/nutsack" }, - { text: "Nutzaps", link: "/wallet/nutzaps" }, + { text: "Introduction", link: "/wallet/README" }, + { + text: "NDKCashuWallet (NIP-60)", + link: "/wallet/nip60-configuration", + items: [ + { text: "Nutsack", link: "/wallet/nutsack" }, + { text: "Nutzaps", link: "/wallet/nutzaps" }, + { text: "Nutzap Monitor", link: "/wallet/nutzap-monitor" }, + { text: "Monitor State Store", link: "/wallet/nutzap-monitor-state-store" }, + ] + }, ], }, { diff --git a/wallet/docs/index.md b/wallet/docs/index.md deleted file mode 100644 index 6b582294f..000000000 --- a/wallet/docs/index.md +++ /dev/null @@ -1,27 +0,0 @@ -# Wallet - -NDK provides an optional `@nostr-dev-kit/ndk-wallet` package, which provides common interfaces and functionalities to interface with different wallet adapters. - -Currently ndk-wallet supports: - -- NIP-60 wallets (nutsacks) -- NIP-47 connectors (NWC) -- WebLN (when available) - -## Connecting NDK with a wallet - -As a developer, the first thing you need to do to use a wallet in your app is to choose how you will connect to your wallet by using one of the wallet adapters. - -Once you instantiate the desired wallet, you simply pass it to ndk. - -```ts -const wallet = new NDKNWCWallet(ndk, { timeout: 5000, pairingCode: "nostr+walletconnect:...." }); -ndk.wallet = wallet; -wallet.on("timeout", (method: string) => console.log('Unable to complete the operation in time', { method })) -``` - -Now whenever you want to pay something, the wallet will be called. Refer to the Nutsack adapter to see more details of the interface. - -## Configuration - -- [NIP-60 Wallet Configuration](./nip60-configuration.md) - How to add/remove mints and relays From 1ebe06cf054bcc78bd87440a5447da6d1ab2894c Mon Sep 17 00:00:00 2001 From: digitalbase Date: Sat, 11 Oct 2025 19:45:50 +0200 Subject: [PATCH 025/139] Rename getting started --- .vitepress/config.mts | 21 ++++++++++++++++----- react/docs/{index.md => getting-started.md} | 0 2 files changed, 16 insertions(+), 5 deletions(-) rename react/docs/{index.md => getting-started.md} (100%) diff --git a/.vitepress/config.mts b/.vitepress/config.mts index 412ba86cb..63eae48c3 100644 --- a/.vitepress/config.mts +++ b/.vitepress/config.mts @@ -32,6 +32,10 @@ export default defineConfig({ 'sessions/docs/:slug*': 'sessions/:slug*', 'wallet/:slug.md': 'wallet/:slug.md', 'wallet/docs/:slug*': 'wallet/:slug*', + 'react/:slug.md': 'react/:slug.md', + 'react/docs/:slug*': 'react/:slug*', + 'svelte/:slug.md': 'svelte/:slug.md', + 'svelte/docs/:slug*': 'svelte/:slug*', 'wot/:slug.md': 'wot/:slug.md' }, themeConfig: { @@ -90,13 +94,20 @@ export default defineConfig({ text: "Wrappers", collapsed: true, items: [ - { text: "NDK Svelte", link: "/wrappers/svelte" }, { - text: "NDK React Hooks", - link: "/hooks/index", + text: "Svelte", + link: "/svelte/README", items: [ - { text: "Session Management", link: "/hooks/session-management" }, - { text: "Muting", link: "/hooks/muting" } + { text: "Blossom", link: "/svelte/blossom-upload" }, + ] + }, + { + text: "React", + link: "/react/README", + items: [ + { text: "Getting Started", link: "/react/getting-started" }, + { text: "Muting", link: "/react/muting" }, + { text: "Session Management", link: "/react/session-management" }, ] } ], diff --git a/react/docs/index.md b/react/docs/getting-started.md similarity index 100% rename from react/docs/index.md rename to react/docs/getting-started.md From fe513d2d59d514320d725195158c145a3cc28b97 Mon Sep 17 00:00:00 2001 From: digitalbase Date: Sat, 11 Oct 2025 20:07:33 +0200 Subject: [PATCH 026/139] Fix wallet docs --- .vitepress/config.mts | 16 +++++++++------- ...ip60-configuration.md => NDKCashuWallet.md} | 0 wallet/docs/NDKNWCWallet.md | 18 ++++++++++++++++++ wallet/docs/NDKWebLNWallet.md | 18 ++++++++++++++++++ wallet/docs/nutsack.md | 2 +- 5 files changed, 46 insertions(+), 8 deletions(-) rename wallet/docs/{nip60-configuration.md => NDKCashuWallet.md} (100%) create mode 100644 wallet/docs/NDKNWCWallet.md create mode 100644 wallet/docs/NDKWebLNWallet.md diff --git a/.vitepress/config.mts b/.vitepress/config.mts index 63eae48c3..1145c5419 100644 --- a/.vitepress/config.mts +++ b/.vitepress/config.mts @@ -31,7 +31,7 @@ export default defineConfig({ 'sessions/:slug.md': 'sessions/:slug.md', 'sessions/docs/:slug*': 'sessions/:slug*', 'wallet/:slug.md': 'wallet/:slug.md', - 'wallet/docs/:slug*': 'wallet/:slug*', + 'wallet/docs/:slug*': 'wallet/docs/:slug*', 'react/:slug.md': 'react/:slug.md', 'react/docs/:slug*': 'react/:slug*', 'svelte/:slug.md': 'svelte/:slug.md', @@ -78,14 +78,16 @@ export default defineConfig({ collapsed: true, items: [ { text: "Introduction", link: "/wallet/README" }, + { text: "WebLN Wallet", link: '/wallet/docs/NDKWebLNWallet'}, + { text: "NWC Wallet", link: '/wallet/docs/NDKNWCWallet'}, { - text: "NDKCashuWallet (NIP-60)", - link: "/wallet/nip60-configuration", + text: "CashuWallet (NIP-60)", + link: "/wallet/docs/NDKCashuWallet", items: [ - { text: "Nutsack", link: "/wallet/nutsack" }, - { text: "Nutzaps", link: "/wallet/nutzaps" }, - { text: "Nutzap Monitor", link: "/wallet/nutzap-monitor" }, - { text: "Monitor State Store", link: "/wallet/nutzap-monitor-state-store" }, + { text: "Nutsack", link: "/wallet/docs/nutsack" }, + { text: "Nutzaps", link: "/wallet/docs/nutzaps" }, + { text: "Nutzap Monitor", link: "/wallet/docs/nutzap-monitor" }, + { text: "Monitor State Store", link: "/wallet/docs/nutzap-monitor-state-store" }, ] }, ], diff --git a/wallet/docs/nip60-configuration.md b/wallet/docs/NDKCashuWallet.md similarity index 100% rename from wallet/docs/nip60-configuration.md rename to wallet/docs/NDKCashuWallet.md diff --git a/wallet/docs/NDKNWCWallet.md b/wallet/docs/NDKNWCWallet.md new file mode 100644 index 000000000..adf20dac8 --- /dev/null +++ b/wallet/docs/NDKNWCWallet.md @@ -0,0 +1,18 @@ + +### NWC Client (`NDKWalletNWC`) + +The `NDKWalletNWC` implements the NIP-47 specification for Nostr Wallet Connect: + +- Connect to NWC-compatible wallets +- Send payment requests +- Query wallet information +- Handle payment responses + +```typescript +import { NDKWalletNWC } from "@nostr-dev-kit/ndk-wallet"; + +// Create an NWC wallet +const wallet = new NDKWalletNWC(ndk, nwcConnectionInfo); + +// Pay an invoice +await wallet.pay({ invoice: "lnbc..." }); \ No newline at end of file diff --git a/wallet/docs/NDKWebLNWallet.md b/wallet/docs/NDKWebLNWallet.md new file mode 100644 index 000000000..25fdbbea6 --- /dev/null +++ b/wallet/docs/NDKWebLNWallet.md @@ -0,0 +1,18 @@ + +### WebLN Client (`NDKWebLNWallet`) + +The `NDKWebLNWallet` implements the NIP-57 specification and allows you to zap using WebLN: + +- Connect to WebLn-compatible wallets +- Send payment requests +- Query wallet information +- Handle payment responses + +```typescript +import { NDKWebLNWallet } from "@nostr-dev-kit/ndk-wallet"; + +// Create an WebLN wallet +const wallet = new NDKWebLNWallet(ndk); + +// Pay an invoice +await wallet.pay({ invoice: "lnbc..." }); \ No newline at end of file diff --git a/wallet/docs/nutsack.md b/wallet/docs/nutsack.md index 60735054c..a511995b5 100644 --- a/wallet/docs/nutsack.md +++ b/wallet/docs/nutsack.md @@ -1,4 +1,4 @@ -# NIP-60 (Nutack) wallets +# NIP-60 (Nutsack) wallets NIP-60 provides wallets that are available to any nostr application immediately; the goal of NIP-60 is to provide the same seamless experience nostr users expect from their apps with regards to the immediate aailability of their data, to their money. From edae9e0fff7df0dc9c6fcb25b15a6eb3451002ef Mon Sep 17 00:00:00 2001 From: digitalbase Date: Sun, 12 Oct 2025 09:35:59 +0200 Subject: [PATCH 027/139] Reorganise Docs --- .vitepress/config.mts | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/.vitepress/config.mts b/.vitepress/config.mts index 1145c5419..9ae5765de 100644 --- a/.vitepress/config.mts +++ b/.vitepress/config.mts @@ -54,22 +54,21 @@ export default defineConfig({ items: [ { text: "Introduction", link: "/core/getting-started/introduction" }, { text: "Usage", link: "/core/getting-started/usage" }, - { text: "Signers", link: "/core/getting-started/signers" }, + ], }, { - text: "Tutorial", - collapsed: true, + text: "Fundamentals", + collapsed: false, items: [ { text: "Local-first", link: "/core/tutorial/local-first" }, + { text: "Signers", link: "/core/getting-started/signers" }, { text: "Publishing", link: "/core/tutorial/publishing" }, { text: "Subscription Management", link: "/core/tutorial/subscription-management", }, { text: "Mute Filtering", link: "/core/tutorial/mute-filtering" }, - { text: "Signer Persistence", link: "/core/tutorial/signer-persistence" }, - { text: "Speed", link: "/core/tutorial/speed" }, { text: "Zaps", link: "/core/tutorial/zaps" }, ], }, @@ -151,6 +150,14 @@ export default defineConfig({ text: "Advanced Topics", collapsed: true, items: [ + { + text: "Signer Persistence", + link: "/core/tutorial/signer-persistence" + }, + { + text: "Speed / Performance", + link: "/core/tutorial/speed" + }, { text: "AI Guardrails", link: "/core/advanced/ai-guardrails" From dd58ecffdc611d36dcb7103a4357b1e04dfe3a83 Mon Sep 17 00:00:00 2001 From: digitalbase Date: Sun, 12 Oct 2025 21:50:38 +0200 Subject: [PATCH 028/139] Improve introduction --- .vitepress/config.mts | 7 +-- core/docs/getting-started/debugging.md | 27 +++++++++++ core/docs/getting-started/introduction.md | 59 +++++++++++++++-------- 3 files changed, 70 insertions(+), 23 deletions(-) create mode 100644 core/docs/getting-started/debugging.md diff --git a/.vitepress/config.mts b/.vitepress/config.mts index 9ae5765de..91f3d58de 100644 --- a/.vitepress/config.mts +++ b/.vitepress/config.mts @@ -42,9 +42,9 @@ export default defineConfig({ // https://vitepress.dev/reference/default-theme-config nav: [ { text: "Home", link: "/" }, - { text: "API Reference", link: "/api/", target: "_blank" }, - { text: "Cookbook", link: "/cookbook/" }, - { text: "Snippets", link: "/snippets/" }, + // { text: "API Reference", link: "/api/", target: "_blank" }, + // { text: "Cookbook", link: "/cookbook/" }, + { text: "Snippets", link: "/core/snippets/" }, { text: "Wiki", link: "https://wikifreedia.xyz/?c=NDK", target: "_blank" }, ], @@ -54,6 +54,7 @@ export default defineConfig({ items: [ { text: "Introduction", link: "/core/getting-started/introduction" }, { text: "Usage", link: "/core/getting-started/usage" }, + { text: "Debugging", link: "/core/getting-started/debugging" }, ], }, diff --git a/core/docs/getting-started/debugging.md b/core/docs/getting-started/debugging.md new file mode 100644 index 000000000..e03a9798c --- /dev/null +++ b/core/docs/getting-started/debugging.md @@ -0,0 +1,27 @@ +# Debugging + +NDK uses the `debug` package to assist in understanding what's happening behind the hood. If you are building a package +that runs on the server define the `DEBUG` envionment variable like + +```sh +export DEBUG='ndk:*' +``` + +or in the browser enable it by writing in the DevTools console + +```sh +localStorage.debug = 'ndk:*' +``` + +## Network Debugging + +You can construct NDK passing a netDebug callback to receive network traffic events, particularly useful for debugging applications not running in a browser. + +```ts +const netDebug = (msg: string, relay: NDKRelay, direction?: "send" | "recv") = { + const hostname = new URL(relay.url).hostname; + netDebug(hostname, msg, direction); +} + +ndk = new NDK({ netDebug }); +``` diff --git a/core/docs/getting-started/introduction.md b/core/docs/getting-started/introduction.md index 50c63b8f5..cba47c522 100644 --- a/core/docs/getting-started/introduction.md +++ b/core/docs/getting-started/introduction.md @@ -2,34 +2,53 @@ ## Installation -```sh -npm add @nostr-dev-kit/ndk -``` +You can install NDK core using your favorite package manager. -## Debugging +::: code-group -NDK uses the `debug` package to assist in understanding what's happening behind the hood. If you are building a package -that runs on the server define the `DEBUG` envionment variable like +```sh [npm] +npm i @nostr-dev-kit/ndk +``` -```sh -export DEBUG='ndk:*' +```sh [pnpm] +pnpm add @nostr-dev-kit/ndk ``` -or in the browser enable it by writing in the DevTools console +```sh [yarn] +yarn add @nostr-dev-kit/ndk +``` -```sh -localStorage.debug = 'ndk:*' +```sh [bun] +bun add @nostr-dev-kit/ndk ``` -## Network Debugging +NDK is compatible with node v16+ -You can construct NDK passing a netDebug callback to receive network traffic events, particularly useful for debugging applications not running in a browser. +::: -```ts -const netDebug = (msg: string, relay: NDKRelay, direction?: "send" | "recv") = { - const hostname = new URL(relay.url).hostname; - netDebug(hostname, msg, direction); -} +## Other packages -ndk = new NDK({ netDebug }); -``` +For other functionality you might need additional packages: + +### Extras +* [@nostr-dev-kit/blossom](/blossom/README.html): Blossom Protocol Support for assets +* [@nostr-dev-kit/sessions](/sessions/README.html): Session Management with Multi-Account support +* [@nostr-dev-kit/sync](/sync): Event synchronization using Negentropy +* [@nostr-dev-kit/wallet](/wallet/README.html): Support for WebLN, NWC, Cashu/eCash wallets +* [@nostr-dev-kit/wot](/wot/README.html): Web of Trust (WOT) utilities + +### Framework Integrations +* [@nostr-dev-kit/react](/react/README): Hooks and utilities to integrate Nostr into your React applications +* [@nostr-dev-kit/svelte](/svelte/README): Modern, performant, and beautiful Svelte 5 integration + +### Cache Adapters + +These NDK adapters are used to store and retrieve data from a cache so relays do not need to be +re-queried for the same data. + +* [@nostr-dev-kit/cache-memory](/cache/memory/README.html): In-memory LRU cache adapter +* [@nostr-dev-kit/cache-nostr](/cache/nostr/README.html): Local Nostr relay cache adapter +* [@nostr-dev-kit/cache-redis](/cache/redis/README.html): A cache adapter for Redis +* [@nostr-dev-kit/cache-dexie](/cache/dexie/README.html): Dexie (IndexedDB, in browser database) adapter +* [@nostr-dev-kit/cache-sqlite](/cache/sqlite/README.html): SQLite (better-sqlite3) adapter +* [@nostr-dev-kit/cache-sqlite-wasm](/cache/sqlite-wasm/README.html): In browser (WASM) SQLite adapter From 6e0d58c760aa3adab580a5e70f644e3b2afc0015 Mon Sep 17 00:00:00 2001 From: digitalbase Date: Mon, 13 Oct 2025 09:28:37 +0200 Subject: [PATCH 029/139] Update usage and debugging docs --- core/docs/getting-started/debugging.md | 20 ++ core/docs/getting-started/usage.md | 254 ++++------------------- core/docs/getting-started/usage_old.md | 272 +++++++++++++++++++++++++ 3 files changed, 333 insertions(+), 213 deletions(-) create mode 100644 core/docs/getting-started/usage_old.md diff --git a/core/docs/getting-started/debugging.md b/core/docs/getting-started/debugging.md index e03a9798c..8824b8ff8 100644 --- a/core/docs/getting-started/debugging.md +++ b/core/docs/getting-started/debugging.md @@ -1,5 +1,7 @@ # Debugging +## Enable Debug Mode + NDK uses the `debug` package to assist in understanding what's happening behind the hood. If you are building a package that runs on the server define the `DEBUG` envionment variable like @@ -25,3 +27,21 @@ const netDebug = (msg: string, relay: NDKRelay, direction?: "send" | "recv") = { ndk = new NDK({ netDebug }); ``` + +## Development Relays + +When you're developing an application you can initialise NDK with the `devWriteRelayUrls` property to tell the NDK +instance to write to specific relays: + +```ts +import NDK from "@nostr-dev-kit/ndk"; + +const ndk = new NDK({ + devWriteRelayUrls: ["wss://staging.relay", "wss://another.test.relay"], +}); + +await ndk.connect(); +``` + +This will write new events to those relays only. Note that if you have provided relays in +`explicitRelayUrls` these will also be used to write events to. diff --git a/core/docs/getting-started/usage.md b/core/docs/getting-started/usage.md index ae7204c38..04517478f 100644 --- a/core/docs/getting-started/usage.md +++ b/core/docs/getting-started/usage.md @@ -1,40 +1,32 @@ # Usage -## Instantiate an NDK instance +Instructions on how to get started with NDK. +If you're using [React](/react/README.html) or [Svelte](/svelte/README.html) make sure to check out the wrapper section +of the documentation. -You can pass an object with several options to a newly created instance of NDK. +## Instantiate + +To start using NDK, you need to create an instance of it. -- `explicitRelayUrls` โ€“ an array of relay URLs. -- `signer` - an instance of a [signer](#signers). -- `cacheAdapter` - an instance of a [Cache Adapter](#caching) -- `debug` - Debug instance to use for logging. Defaults to `debug("ndk")`. ```ts // Import the package import NDK from "@nostr-dev-kit/ndk"; -// Create a new NDK instance with explicit relays -const ndk = new NDK({ - explicitRelayUrls: ["wss://a.relay", "wss://another.relay"], -}); +// Create a new NDK instance +const ndk = new NDK(); ``` -If the signer implements the `getRelays()` method, NDK will use the relays returned by that method as the explicit relays. +This by itself won't do much as there are no connected relays but there might be cases +where you want to have a NDK instance without connecting to any relays. -```ts -// Import the package -import NDK, { NDKNip07Signer } from "@nostr-dev-kit/ndk"; +## Connecting to Relays -// Create a new NDK instance with just a signer (provided the signer implements the getRelays() method) -const nip07signer = new NDKNip07Signer(); -const ndk = new NDK({ signer: nip07signer }); -``` +This section will briefly explain the different mechanmisms through which NDK can connect to relays. -Note: In normal client use, it's best practice to instantiate NDK as a singleton class. [See more below](#architecture-decisions--suggestions). +### Specify Relays -## Connecting - -After you've instatiated NDK, you need to tell it to connect before you'll be able to interact with any relays. +The simplest way to get NDK to connect to relays is to specify them: ```ts // Import the package @@ -44,215 +36,51 @@ import NDK from "@nostr-dev-kit/ndk"; const ndk = new NDK({ explicitRelayUrls: ["wss://a.relay", "wss://another.relay"], }); + // Now connect to specified relays await ndk.connect(); ``` -## Creating Users - -NDK provides flexible ways to fetch user objects, including support for NIP-19 encoded identifiers and NIP-05 addresses: - -```typescript -// From hex pubkey -const user1 = await ndk.fetchUser("3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d"); - -// From npub (NIP-19 encoded) -const user2 = await ndk.fetchUser("npub1n0sturny6w9zn2wwexju3m6asu7zh7jnv2jt2kx6tlmfhs7thq0qnflahe"); - -// From nprofile (includes relay hints) -const user3 = await ndk.fetchUser("nprofile1qqsrhuxx8l9ex335q7he0f09aej04zpazpl0ne2cgukyawd24mayt8gpp4mhxue69uhhytnc9e3k7mgpz4mhxue69uhkg6nzv9ejuumpv34kytnrdaksjlyr9p"); - -// From NIP-05 identifier -const user4 = await ndk.fetchUser("pablo@test.com"); -const user5 = await ndk.fetchUser("test.com"); // Uses _@test.com - -// The method automatically detects the format -const user6 = await ndk.fetchUser("deadbeef..."); // Assumes hex pubkey -``` - -Note: `fetchUser` is async and returns a Promise. For NIP-05 lookups, it may return `undefined` if the address cannot be resolved. - -## Working with NIP-19 Identifiers - -NDK re-exports NIP-19 utilities for encoding and decoding Nostr identifiers: - -```typescript -import { nip19 } from '@nostr-dev-kit/ndk'; - -// Encode a pubkey as npub -const npub = nip19.npubEncode("3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d"); - -// Decode any NIP-19 identifier -const decoded = nip19.decode("npub1..."); -console.log(decoded.type); // "npub" -console.log(decoded.data); // hex pubkey - -// Encode events -const nevent = nip19.neventEncode({ - id: eventId, - relays: ["wss://relay.example.com"], - author: authorPubkey -}); -``` - -See the [NIP-19 tutorial](/tutorial/nip19.html) for comprehensive examples and use cases. - -## Usage with React Hooks (`ndk-hooks`) - -When using the `ndk-hooks` package in a React application, the initialization process involves creating the NDK instance and then using the `useNDKInit` hook to make it available to the rest of your application via Zustand stores. - -This hook ensures that both the core NDK store and dependent stores (like the user profiles store) are properly initialized with the NDK instance. +Make sure to wait for the `connect()` promise to resolve before using NDK after which +you can start interacting with relays. -It's recommended to create and connect your NDK instance outside of your React components, potentially in a dedicated setup file or at the root of your application. Then, use the `useNDKInit` hook within your main App component or a context provider to initialize the stores once the component mounts. +Explicit relays can also be added using the `addExplicitRelay()` method. -```tsx -import React, { useEffect } from 'react'; // Removed useState -import NDK from '@nostr-dev-kit/ndk'; -import { useNDKInit } from '@nostr-dev-kit/ndk-hooks'; // Assuming package name - -// 1. Configure your NDK instance (e.g., in src/ndk.ts or similar) -const ndk = new NDK({ - explicitRelayUrls: ['wss://relay.damus.io', 'wss://relay.primal.net'], - // Add signer or cache adapter if needed -}); - -// 2. Connect the instance immediately -ndk.connect() - .then(() => console.log('NDK connected')) - .catch((e) => console.error('NDK connection error:', e)); - -// Example: App component or Context Provider that initializes NDK stores -function App() { - const initializeNDK = useNDKInit(); // Hook returns the function directly +```ts +// Import the package +import NDK from "@nostr-dev-kit/ndk"; - useEffect(() => { - // 3. Initialize stores once the component mounts - initializeNDK(ndk); - }, [initializeNDK]); // Dependency ensures this runs if initializeNDK changes, though unlikely +// Create a new NDK instance with explicit relays +const ndk = new NDK(); - // Your application components can now use other ndk-hooks - // No need to wait for connection state here, as hooks handle NDK readiness internally - return ( -
- {/* ... Your app content using useProfile, useSubscribe, etc. ... */} -
- ); -} +ndk.addExplicitRelay("wss://a.relay"); +ndk.addExplicitRelay("wss://another.relay"); -export default App; +// Now connect to specified relays +await ndk.connect(); ``` -**Key Points:** - -* Create and configure your `NDK` instance globally or outside components. -* Call `ndk.connect()` immediately after creation. Connection happens in the background. -* In your main App or Provider component, get the `initializeNDK` function from `useNDKInit`. -* Use `useEffect` with an empty dependency array (or `[initializeNDK]`) to call `initializeNDK(ndk)` once on mount. -* This sets up the necessary Zustand stores. Other `ndk-hooks` will access the initialized `ndk` instance from the store and handle its readiness internally. - ---- - - -## Architecture decisions & suggestions - -- Users of NDK should instantiate a single NDK instance. -- That instance tracks state with all relays connected, explicit and otherwise. -- All relays are tracked in a single pool that handles connection errors/reconnection logic. -- RelaySets are assembled ad-hoc as needed depending on the queries set, although some RelaySets might be long-lasting, like the `explicitRelayUrls` specified by the user. -- RelaySets are always a subset of the pool of all available relays. - +### Using a Signer -## Subscribing to Events +A [signer](/core/getting-started/signers.html) is used to sign events. You could say it's some sort of an +authentication mechanism. If the attached signer implements the `getRelays()` method, those relays will be used as +the explicit relays. -Once connected, you can subscribe to events using `ndk.subscribe()`. You provide filters to specify the events you're interested in. - -### Preferred Method: Direct Event Handlers - -The **recommended** way to handle events is to provide handler functions directly when calling `ndk.subscribe()`. This is done using the third argument (`autoStart`), which accepts an object containing `onEvent`, `onEvents`, and/or `onEose` callbacks. - -**Why is this preferred?** Subscriptions can start receiving events (especially from a fast cache) almost immediately after `ndk.subscribe()` is called. By providing handlers directly, you ensure they are attached *before* any events are emitted, preventing potential race conditions where you might miss the first few events if you attached handlers later using `.on()`. - -```typescript -// Example with default relay calculation -ndk.subscribe( - { kinds: [1], authors: [pubkey] }, // Filters - { closeOnEose: true }, // Options (no explicit relays specified) - { // Direct handlers via autoStart parameter (now the 3rd argument) - onEvent: (event: NDKEvent, relay?: NDKRelay) => { - // Called for events received from relays after the initial cache load (if onEvents is used) - console.log("Received event from relay (id):", event.id); - }, - onEvents: (events: NDKEvent[]) => { // Parameter renamed to 'events' - console.log(`Received ${events.length} events from cache initially.`); - }, - onEose: (subscription: NDKSubscription) => { - console.log("Subscription reached EOSE:", subscription.internalId); - } - } -); - -// Example specifying explicit relays using relayUrls option -ndk.subscribe( - { kinds: [0], authors: [pubkey] }, // Filters - { // Options object now includes relayUrls - closeOnEose: true, - relayUrls: ["wss://explicit1.relay", "wss://explicit2.relay"] - }, - { // Direct handlers - onEvent: (event: NDKEvent) => { /* ... */ } - } -); +```ts +// Import NDK + NIP07 signer +import NDK, { NDKNip07Signer } from "@nostr-dev-kit/ndk"; -// Example specifying explicit relays using relaySet option -const explicitRelaySet = NDKRelaySet.fromRelayUrls(["wss://explicit.relay"], ndk); -ndk.subscribe( - { kinds: [7], authors: [pubkey] }, // Filters - { // Options object now includes relaySet - closeOnEose: true, - relaySet: explicitRelaySet - }, - { // Direct handlers - onEvent: (event: NDKEvent) => { /* ... */ } - } -); +// Create a new NDK instance with signer (provided the signer implements the getRelays() method) +const nip07signer = new NDKNip07Signer(); +const ndk = new NDK({ signer: nip07signer }); ``` -### Efficient Cache Handling with `onEvents` - -Using the `onEvents` handler provides an efficient way to process events loaded from the cache. When you provide `onEvents`: - -1. If NDK finds matching events in its cache *synchronously* when the subscription starts, `onEvents` is called **once** with an array of all those cached events. -2. The `onEvent` handler is **skipped** for this initial batch of cached events. -3. `onEvent` will still be called for any subsequent events received from relays or later asynchronous cache updates. - -This is ideal for scenarios like populating initial UI state, as it allows you to process the cached data in a single batch, preventing potentially numerous individual updates that would occur if `onEvent` were called for each cached item. +### Outbox Model -If you *don't* provide `onEvents`, the standard `onEvent` handler will be triggered for every event, whether it comes from the cache or a relay. +Explain why how. -### Alternative Method: Attaching Handlers with `.on()` +## Additional Resources -You can also attach event listeners *after* creating the subscription using the `.on()` method. While functional, be mindful of the potential race condition mentioned above, especially if you rely on immediate cache results. +There is much more to NDK and Nostr -```typescript -// Subscribe using default relay calculation -const subscription = ndk.subscribe( - { kinds: [1], authors: [pubkey] }, - { closeOnEose: true } // Options -); - -// Subscribe using explicit relays via options -const subscriptionWithRelays = ndk.subscribe( - { kinds: [0], authors: [pubkey] }, - { relayUrls: ["wss://explicit.relay"] } // Options with explicit relays -); - -// Attach handlers later -subscription.on("event", (event) => { - console.log("Received event:", event.id); -}); -subscription.on("eose", () => { - console.log("Initial events loaded"); -}); -// Remember to stop the subscription when it's no longer needed -// setTimeout(() => subscription.stop(), 5000); diff --git a/core/docs/getting-started/usage_old.md b/core/docs/getting-started/usage_old.md new file mode 100644 index 000000000..bc9a526a3 --- /dev/null +++ b/core/docs/getting-started/usage_old.md @@ -0,0 +1,272 @@ +# Usage + +## Instantiate + +To start using NDK, you need to create an instance of it. + + +```ts +// Import the package +import NDK from "@nostr-dev-kit/ndk"; + +// Create a new NDK instance +const ndk = new NDK(); +``` + +This by itself won't do much as there are no connected relays but there might be cases +where you want to have a NDK instance without connecting to any relays. + +## Connecting to Relays + +The simplest way to get NDK to connect to relays is to specify them: + +```ts +// Import the package +import NDK from "@nostr-dev-kit/ndk"; + +// Create a new NDK instance with explicit relays +const ndk = new NDK({ + explicitRelayUrls: ["wss://a.relay", "wss://another.relay"], +}); + +// Now connect to specified relays +await ndk.connect(); +``` + +Make sure to wait for the `connect()` promise to resolve before using NDK after which +you can start interacting with relays. + + + +--- + +You can pass an object with several options to a newly created instance of NDK. + +- `explicitRelayUrls` โ€“ an array of relay URLs. +- `signer` - an instance of a [signer](#signers). +- `cacheAdapter` - an instance of a [Cache Adapter](#caching) +- `debug` - Debug instance to use for logging. Defaults to `debug("ndk")`. + + + +If the signer implements the `getRelays()` method, NDK will use the relays returned by that method as the explicit relays. + +```ts +// Import the package +import NDK, { NDKNip07Signer } from "@nostr-dev-kit/ndk"; + +// Create a new NDK instance with just a signer (provided the signer implements the getRelays() method) +const nip07signer = new NDKNip07Signer(); +const ndk = new NDK({ signer: nip07signer }); +``` + +Note: In normal client use, it's best practice to instantiate NDK as a singleton class. [See more below](#architecture-decisions--suggestions). + +## Creating Users + +NDK provides flexible ways to fetch user objects, including support for NIP-19 encoded identifiers and NIP-05 addresses: + +```typescript +// From hex pubkey +const user1 = await ndk.fetchUser("3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d"); + +// From npub (NIP-19 encoded) +const user2 = await ndk.fetchUser("npub1n0sturny6w9zn2wwexju3m6asu7zh7jnv2jt2kx6tlmfhs7thq0qnflahe"); + +// From nprofile (includes relay hints) +const user3 = await ndk.fetchUser("nprofile1qqsrhuxx8l9ex335q7he0f09aej04zpazpl0ne2cgukyawd24mayt8gpp4mhxue69uhhytnc9e3k7mgpz4mhxue69uhkg6nzv9ejuumpv34kytnrdaksjlyr9p"); + +// From NIP-05 identifier +const user4 = await ndk.fetchUser("pablo@test.com"); +const user5 = await ndk.fetchUser("test.com"); // Uses _@test.com + +// The method automatically detects the format +const user6 = await ndk.fetchUser("deadbeef..."); // Assumes hex pubkey +``` + +Note: `fetchUser` is async and returns a Promise. For NIP-05 lookups, it may return `undefined` if the address cannot be resolved. + +## Working with NIP-19 Identifiers + +NDK re-exports NIP-19 utilities for encoding and decoding Nostr identifiers: + +```typescript +import { nip19 } from '@nostr-dev-kit/ndk'; + +// Encode a pubkey as npub +const npub = nip19.npubEncode("3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d"); + +// Decode any NIP-19 identifier +const decoded = nip19.decode("npub1..."); +console.log(decoded.type); // "npub" +console.log(decoded.data); // hex pubkey + +// Encode events +const nevent = nip19.neventEncode({ + id: eventId, + relays: ["wss://relay.example.com"], + author: authorPubkey +}); +``` + +See the [NIP-19 tutorial](/tutorial/nip19.html) for comprehensive examples and use cases. + +## Usage with React Hooks (`ndk-hooks`) + +When using the `ndk-hooks` package in a React application, the initialization process involves creating the NDK instance and then using the `useNDKInit` hook to make it available to the rest of your application via Zustand stores. + +This hook ensures that both the core NDK store and dependent stores (like the user profiles store) are properly initialized with the NDK instance. + +It's recommended to create and connect your NDK instance outside of your React components, potentially in a dedicated setup file or at the root of your application. Then, use the `useNDKInit` hook within your main App component or a context provider to initialize the stores once the component mounts. + +```tsx +import React, { useEffect } from 'react'; // Removed useState +import NDK from '@nostr-dev-kit/ndk'; +import { useNDKInit } from '@nostr-dev-kit/ndk-hooks'; // Assuming package name + +// 1. Configure your NDK instance (e.g., in src/ndk.ts or similar) +const ndk = new NDK({ + explicitRelayUrls: ['wss://relay.damus.io', 'wss://relay.primal.net'], + // Add signer or cache adapter if needed +}); + +// 2. Connect the instance immediately +ndk.connect() + .then(() => console.log('NDK connected')) + .catch((e) => console.error('NDK connection error:', e)); + +// Example: App component or Context Provider that initializes NDK stores +function App() { + const initializeNDK = useNDKInit(); // Hook returns the function directly + + useEffect(() => { + // 3. Initialize stores once the component mounts + initializeNDK(ndk); + }, [initializeNDK]); // Dependency ensures this runs if initializeNDK changes, though unlikely + + // Your application components can now use other ndk-hooks + // No need to wait for connection state here, as hooks handle NDK readiness internally + return ( +
+ {/* ... Your app content using useProfile, useSubscribe, etc. ... */} +
+ ); +} + +export default App; +``` + +**Key Points:** + +* Create and configure your `NDK` instance globally or outside components. +* Call `ndk.connect()` immediately after creation. Connection happens in the background. +* In your main App or Provider component, get the `initializeNDK` function from `useNDKInit`. +* Use `useEffect` with an empty dependency array (or `[initializeNDK]`) to call `initializeNDK(ndk)` once on mount. +* This sets up the necessary Zustand stores. Other `ndk-hooks` will access the initialized `ndk` instance from the store and handle its readiness internally. + +--- + + +## Architecture decisions & suggestions + +- Users of NDK should instantiate a single NDK instance. +- That instance tracks state with all relays connected, explicit and otherwise. +- All relays are tracked in a single pool that handles connection errors/reconnection logic. +- RelaySets are assembled ad-hoc as needed depending on the queries set, although some RelaySets might be long-lasting, like the `explicitRelayUrls` specified by the user. +- RelaySets are always a subset of the pool of all available relays. + + +## Subscribing to Events + +Once connected, you can subscribe to events using `ndk.subscribe()`. You provide filters to specify the events you're interested in. + +### Preferred Method: Direct Event Handlers + +The **recommended** way to handle events is to provide handler functions directly when calling `ndk.subscribe()`. This is done using the third argument (`autoStart`), which accepts an object containing `onEvent`, `onEvents`, and/or `onEose` callbacks. + +**Why is this preferred?** Subscriptions can start receiving events (especially from a fast cache) almost immediately after `ndk.subscribe()` is called. By providing handlers directly, you ensure they are attached *before* any events are emitted, preventing potential race conditions where you might miss the first few events if you attached handlers later using `.on()`. + +```typescript +// Example with default relay calculation +ndk.subscribe( + { kinds: [1], authors: [pubkey] }, // Filters + { closeOnEose: true }, // Options (no explicit relays specified) + { // Direct handlers via autoStart parameter (now the 3rd argument) + onEvent: (event: NDKEvent, relay?: NDKRelay) => { + // Called for events received from relays after the initial cache load (if onEvents is used) + console.log("Received event from relay (id):", event.id); + }, + onEvents: (events: NDKEvent[]) => { // Parameter renamed to 'events' + console.log(`Received ${events.length} events from cache initially.`); + }, + onEose: (subscription: NDKSubscription) => { + console.log("Subscription reached EOSE:", subscription.internalId); + } + } +); + +// Example specifying explicit relays using relayUrls option +ndk.subscribe( + { kinds: [0], authors: [pubkey] }, // Filters + { // Options object now includes relayUrls + closeOnEose: true, + relayUrls: ["wss://explicit1.relay", "wss://explicit2.relay"] + }, + { // Direct handlers + onEvent: (event: NDKEvent) => { /* ... */ } + } +); + +// Example specifying explicit relays using relaySet option +const explicitRelaySet = NDKRelaySet.fromRelayUrls(["wss://explicit.relay"], ndk); +ndk.subscribe( + { kinds: [7], authors: [pubkey] }, // Filters + { // Options object now includes relaySet + closeOnEose: true, + relaySet: explicitRelaySet + }, + { // Direct handlers + onEvent: (event: NDKEvent) => { /* ... */ } + } +); +``` + +### Efficient Cache Handling with `onEvents` + +Using the `onEvents` handler provides an efficient way to process events loaded from the cache. When you provide `onEvents`: + +1. If NDK finds matching events in its cache *synchronously* when the subscription starts, `onEvents` is called **once** with an array of all those cached events. +2. The `onEvent` handler is **skipped** for this initial batch of cached events. +3. `onEvent` will still be called for any subsequent events received from relays or later asynchronous cache updates. + +This is ideal for scenarios like populating initial UI state, as it allows you to process the cached data in a single batch, preventing potentially numerous individual updates that would occur if `onEvent` were called for each cached item. + +If you *don't* provide `onEvents`, the standard `onEvent` handler will be triggered for every event, whether it comes from the cache or a relay. + +### Alternative Method: Attaching Handlers with `.on()` + +You can also attach event listeners *after* creating the subscription using the `.on()` method. While functional, be mindful of the potential race condition mentioned above, especially if you rely on immediate cache results. + +```typescript +// Subscribe using default relay calculation +const subscription = ndk.subscribe( + { kinds: [1], authors: [pubkey] }, + { closeOnEose: true } // Options +); + +// Subscribe using explicit relays via options +const subscriptionWithRelays = ndk.subscribe( + { kinds: [0], authors: [pubkey] }, + { relayUrls: ["wss://explicit.relay"] } // Options with explicit relays +); + +// Attach handlers later +subscription.on("event", (event) => { + console.log("Received event:", event.id); +}); +subscription.on("eose", () => { + console.log("Initial events loaded"); +}); + +// Remember to stop the subscription when it's no longer needed +// setTimeout(() => subscription.stop(), 5000); From 7f0bf80548c0218d63ad9246f366baac90c1bf4e Mon Sep 17 00:00:00 2001 From: digitalbase Date: Mon, 13 Oct 2025 09:28:48 +0200 Subject: [PATCH 030/139] Enable more levels on righgt side 'on this page' menu --- .vitepress/config.mts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.vitepress/config.mts b/.vitepress/config.mts index 91f3d58de..cd38b0c7d 100644 --- a/.vitepress/config.mts +++ b/.vitepress/config.mts @@ -205,6 +205,9 @@ export default defineConfig({ ], }, ], + outline: { + level: [2, 3], + }, socialLinks: [{ icon: "github", link: "https://github.com/nostr-dev-kit/ndk" }], }, From 0dc4ad9bd97619b722e0e62e8174595776cc2ff1 Mon Sep 17 00:00:00 2001 From: digitalbase Date: Mon, 13 Oct 2025 09:52:47 +0200 Subject: [PATCH 031/139] Remove connecting section from getting started + Add tip on connecting --- .vitepress/config.mts | 5 +- core/docs/fundamentals/connecting.md | 73 ++++++++++++++++++++++++++++ core/docs/getting-started/usage.md | 45 +---------------- 3 files changed, 78 insertions(+), 45 deletions(-) create mode 100644 core/docs/fundamentals/connecting.md diff --git a/.vitepress/config.mts b/.vitepress/config.mts index cd38b0c7d..0e5d6df2c 100644 --- a/.vitepress/config.mts +++ b/.vitepress/config.mts @@ -62,15 +62,16 @@ export default defineConfig({ text: "Fundamentals", collapsed: false, items: [ - { text: "Local-first", link: "/core/tutorial/local-first" }, + { text: "Connecting", link: "/core/fundamentals/connecting" }, { text: "Signers", link: "/core/getting-started/signers" }, { text: "Publishing", link: "/core/tutorial/publishing" }, + { text: "Zaps", link: "/core/tutorial/zaps" }, + { text: "Local-first", link: "/core/tutorial/local-first" }, { text: "Subscription Management", link: "/core/tutorial/subscription-management", }, { text: "Mute Filtering", link: "/core/tutorial/mute-filtering" }, - { text: "Zaps", link: "/core/tutorial/zaps" }, ], }, { diff --git a/core/docs/fundamentals/connecting.md b/core/docs/fundamentals/connecting.md new file mode 100644 index 000000000..7cdefd209 --- /dev/null +++ b/core/docs/fundamentals/connecting.md @@ -0,0 +1,73 @@ +# Connecting + +This section will briefly explain the different mechanmisms through which NDK can connect to relays. + + +::: tip +Because NOSTR is decentralized and comprised of thousands of relays, it's important to read up on the +advised ways of connecting to relays. + +We strongly advise you to use the "Outbox Model" in addition or replacement of specifying explicit relays. +::: + +## Specify Relays + +The simplest way to get NDK to connect to relays is to specify them: + +```ts +// Import the package +import NDK from "@nostr-dev-kit/ndk"; + +// Create a new NDK instance with explicit relays +const ndk = new NDK({ + explicitRelayUrls: ["wss://a.relay", "wss://another.relay"], +}); + +// Now connect to specified relays +await ndk.connect(); +``` + +Make sure to wait for the `connect()` promise to resolve before using NDK after which +you can start interacting with relays. + +Explicit relays can also be added using the `addExplicitRelay()` method. + +```ts +// Import the package +import NDK from "@nostr-dev-kit/ndk"; + +// Create a new NDK instance with explicit relays +const ndk = new NDK(); + +ndk.addExplicitRelay("wss://a.relay"); +ndk.addExplicitRelay("wss://another.relay"); + +// Now connect to specified relays +await ndk.connect(); +``` + +## Using a Signer + +A [signer](/core/getting-started/signers.html) is used to sign events. You could say it's some sort of an +authentication mechanism. If the attached signer implements the `getRelays()` method, those relays will be used as +the explicit relays. + +```ts +// Import NDK + NIP07 signer +import NDK, { NDKNip07Signer } from "@nostr-dev-kit/ndk"; + +// Create a new NDK instance with signer (provided the signer implements the getRelays() method) +const nip07signer = new NDKNip07Signer(); +const ndk = new NDK({ signer: nip07signer }); +``` + +## Outbox Model + +https://mikedilger.com/gossip-model/ +https://how-nostr-works.pages.dev/#/outbox +https://github.com/nostr-protocol/nips/blob/master/65.md +https://primal.net/e/nevent1qqs2txvkjpa6fdlhxdtqmyk2tzzchpaa4vrfgx7h20539u5k9lzgqwgfjnlen + +Explain why how. + + diff --git a/core/docs/getting-started/usage.md b/core/docs/getting-started/usage.md index 04517478f..b1523b512 100644 --- a/core/docs/getting-started/usage.md +++ b/core/docs/getting-started/usage.md @@ -22,11 +22,7 @@ where you want to have a NDK instance without connecting to any relays. ## Connecting to Relays -This section will briefly explain the different mechanmisms through which NDK can connect to relays. - -### Specify Relays - -The simplest way to get NDK to connect to relays is to specify them: +The simplest (but not advised) way to get NDK to connect to relays is to specify them: ```ts // Import the package @@ -44,43 +40,6 @@ await ndk.connect(); Make sure to wait for the `connect()` promise to resolve before using NDK after which you can start interacting with relays. -Explicit relays can also be added using the `addExplicitRelay()` method. - -```ts -// Import the package -import NDK from "@nostr-dev-kit/ndk"; - -// Create a new NDK instance with explicit relays -const ndk = new NDK(); - -ndk.addExplicitRelay("wss://a.relay"); -ndk.addExplicitRelay("wss://another.relay"); - -// Now connect to specified relays -await ndk.connect(); -``` - -### Using a Signer - -A [signer](/core/getting-started/signers.html) is used to sign events. You could say it's some sort of an -authentication mechanism. If the attached signer implements the `getRelays()` method, those relays will be used as -the explicit relays. - -```ts -// Import NDK + NIP07 signer -import NDK, { NDKNip07Signer } from "@nostr-dev-kit/ndk"; - -// Create a new NDK instance with signer (provided the signer implements the getRelays() method) -const nip07signer = new NDKNip07Signer(); -const ndk = new NDK({ signer: nip07signer }); -``` - -### Outbox Model - -Explain why how. - -## Additional Resources - -There is much more to NDK and Nostr +Different ways to connect to relays are explained in the [connecting](/core/fundamentals/connecting) section. From 128f435efc4afd89f3eab68a7f385e70e4a2f2f6 Mon Sep 17 00:00:00 2001 From: digitalbase Date: Mon, 13 Oct 2025 10:10:17 +0200 Subject: [PATCH 032/139] Linking to outbox model --- core/docs/fundamentals/connecting.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/docs/fundamentals/connecting.md b/core/docs/fundamentals/connecting.md index 7cdefd209..739702f55 100644 --- a/core/docs/fundamentals/connecting.md +++ b/core/docs/fundamentals/connecting.md @@ -7,7 +7,7 @@ This section will briefly explain the different mechanmisms through which NDK ca Because NOSTR is decentralized and comprised of thousands of relays, it's important to read up on the advised ways of connecting to relays. -We strongly advise you to use the "Outbox Model" in addition or replacement of specifying explicit relays. +We strongly advise you to use the "[Outbox Model](/core/fundamentals/connecting.html#outbox-model)" in addition or replacement of specifying explicit relays. ::: ## Specify Relays From 6718d388e8b2afd42dece4c9003af4bbd82105aa Mon Sep 17 00:00:00 2001 From: digitalbase Date: Mon, 13 Oct 2025 10:10:51 +0200 Subject: [PATCH 033/139] Minor improvement to TIP --- core/docs/fundamentals/connecting.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/core/docs/fundamentals/connecting.md b/core/docs/fundamentals/connecting.md index 739702f55..54adbd17b 100644 --- a/core/docs/fundamentals/connecting.md +++ b/core/docs/fundamentals/connecting.md @@ -5,9 +5,7 @@ This section will briefly explain the different mechanmisms through which NDK ca ::: tip Because NOSTR is decentralized and comprised of thousands of relays, it's important to read up on the -advised ways of connecting to relays. - -We strongly advise you to use the "[Outbox Model](/core/fundamentals/connecting.html#outbox-model)" in addition or replacement of specifying explicit relays. +advised ways of connecting to relays. We strongly advise you to use the "[Outbox Model](/core/fundamentals/connecting.html#outbox-model)" in addition or replacement of specifying explicit relays. ::: ## Specify Relays From b10a8bd6ab4e5aded890f4cf3df975e4374f9d3c Mon Sep 17 00:00:00 2001 From: digitalbase Date: Mon, 13 Oct 2025 20:33:54 +0200 Subject: [PATCH 034/139] Update WebLN docs --- core/docs/getting-started/introduction.md | 2 +- wallet/README.md | 23 ++-- wallet/docs/NDKNWCWallet.md | 7 +- wallet/docs/NDKWebLNWallet.md | 161 +++++++++++++++++++++- 4 files changed, 178 insertions(+), 15 deletions(-) diff --git a/core/docs/getting-started/introduction.md b/core/docs/getting-started/introduction.md index cba47c522..9124af2b5 100644 --- a/core/docs/getting-started/introduction.md +++ b/core/docs/getting-started/introduction.md @@ -21,10 +21,10 @@ yarn add @nostr-dev-kit/ndk ```sh [bun] bun add @nostr-dev-kit/ndk ``` +::: NDK is compatible with node v16+ -::: ## Other packages diff --git a/wallet/README.md b/wallet/README.md index 67641c0e9..068076774 100644 --- a/wallet/README.md +++ b/wallet/README.md @@ -127,17 +127,24 @@ For more detailed documentation on specific components: - [Nutzap Monitor State Store](./docs/nutzap-monitor-state-store.md) - [NWC Client](./docs/nwc-client.md) -## Installation +## Install -```bash -npm install @nostr-dev-kit/ndk-wallet +::: code-group + +```sh [npm] +npm i @nostr-dev-kit/ndk-wallet ``` -## Requirements +```sh [pnpm] +pnpm add @nostr-dev-kit/ndk-wallet +``` -- `@nostr-dev-kit/ndk`: Peer dependency -- Modern browser or Node.js environment +```sh [yarn] +yarn add @nostr-dev-kit/ndk-wallet +``` -## License +```sh [bun] +bun add @nostr-dev-kit/ndk-wallet +``` +::: -MIT diff --git a/wallet/docs/NDKNWCWallet.md b/wallet/docs/NDKNWCWallet.md index adf20dac8..300beeb84 100644 --- a/wallet/docs/NDKNWCWallet.md +++ b/wallet/docs/NDKNWCWallet.md @@ -1,5 +1,4 @@ - -### NWC Client (`NDKWalletNWC`) +# NWC Client (`NDKWalletNWC`) The `NDKWalletNWC` implements the NIP-47 specification for Nostr Wallet Connect: @@ -8,6 +7,7 @@ The `NDKWalletNWC` implements the NIP-47 specification for Nostr Wallet Connect: - Query wallet information - Handle payment responses + ```typescript import { NDKWalletNWC } from "@nostr-dev-kit/ndk-wallet"; @@ -15,4 +15,5 @@ import { NDKWalletNWC } from "@nostr-dev-kit/ndk-wallet"; const wallet = new NDKWalletNWC(ndk, nwcConnectionInfo); // Pay an invoice -await wallet.pay({ invoice: "lnbc..." }); \ No newline at end of file +await wallet.pay({ invoice: "lnbc..." }); +``` \ No newline at end of file diff --git a/wallet/docs/NDKWebLNWallet.md b/wallet/docs/NDKWebLNWallet.md index 25fdbbea6..ad37a107c 100644 --- a/wallet/docs/NDKWebLNWallet.md +++ b/wallet/docs/NDKWebLNWallet.md @@ -1,18 +1,173 @@ +# WebLN Client (`NDKWebLNWallet`) -### WebLN Client (`NDKWebLNWallet`) +The `NDKWebLNWallet` implements the NIP-57 specification and allows you to zap using WebLN. -The `NDKWebLNWallet` implements the NIP-57 specification and allows you to zap using WebLN: +## Usage + +The WebLN wallet can be used to: - Connect to WebLn-compatible wallets - Send payment requests - Query wallet information - Handle payment responses +## Install + +To initialise WebLN ensure you have the ndk-wallet installed + +::: code-group + +```sh [npm] +npm i @nostr-dev-kit/ndk-wallet +``` + +```sh [pnpm] +pnpm add @nostr-dev-kit/ndk-wallet +``` + +```sh [yarn] +yarn add @nostr-dev-kit/ndk-wallet +``` + +```sh [bun] +bun add @nostr-dev-kit/ndk-wallet +``` +::: + +## Initialising + + ```typescript +import NDK from "@nostr-dev-kit/ndk"; import { NDKWebLNWallet } from "@nostr-dev-kit/ndk-wallet"; +const ndk = new NDK(); + // Create an WebLN wallet const wallet = new NDKWebLNWallet(ndk); +ndk.wallet = wallet; +``` + +## Pay an invoice + +To pay an invoice you can use the wallet instance directly: + +```typescript +// Generate payment request +const paymentRequest = { pr: "lnbc..."} as LnPaymentInfo; + // Pay an invoice -await wallet.pay({ invoice: "lnbc..." }); \ No newline at end of file +await wallet.pay(paymentRequest); +``` + +Or using the NDK instance: + +```typescript +// Generate payment request +const paymentRequest = { pr: "lnbc..."} as LnPaymentInfo; + +// Pay an invoice +await ndk.wallet.pay(paymentRequest); +``` + +## Retrieve + +To retrieve the balance you can use the wallet instance directly or using the NDK instance; + +```typescript +console.log(wallet.balance); + +console.log(ndk.wallet.balance); +``` + +## Update balance + +To refresh the balance from the linked WebLN entity use the WebLN wallet instance directly or using the NDK instance; + +```typescript +await wallet.updateBalance; + +await ndk.wallet.balance; +``` + +Make sure to await the promise to fully refresh the balance. + +## Nostr Zaps + +To send Zaps using the WebLn wallet there are a few other steps to follow: + +```typescript + +import { + generateZapRequest, + getNip57ZapSpecFromLud, + NDKUser, + NDKZapper +} from "@nostr-dev-kit/ndk"; + +const lud06 = ''; +const lud16 = 'pablof7z@primal.net'; + +// retrieve lightning callback data for user +const lnMeta = await getNip57ZapSpecFromLud( + { + lud16, + lud06, + }, + ndk, // pass NDK instance +); + +const target = new NDKUser({ npub: '' }); // User you want to zap +const amount = 1000; // amount in MSats + +// generate zap request, this event is not published to relays +const zapRequest = await generateZapRequest( + target, + ndk, + lnMeta, + ndk.activeUser.npub, + amount, + relays, // optional relays to send zapReceipt to + message, // message +); + +// create zapper instance and get lightning invoice +const zapper = new NDKZapper(target, amount, "msat", { ndk }); + +// retrieve the lightning invoice +const invoice = await zapper.getLnInvoice(zapRequest, amount, lnMeta); + +// pay the invoice +await wallet.lnPay({ + pr: invoice, +}); + +// send a zap to confirm +setCurrentlyDoing("Publishing Zap Request"); + +const invoiceDecoded = decode(invoice); +const timestampSection = invoiceDecoded.sections.filter( + (section) => section.name === "timestamp", +); + +const invoiceTimestamp = timestampSection[0] + ? Number(timestampSection[0].value) + : Math.floor(Date.now() / 1000); + +const zapReceiptEvent = new NDKEvent(ndk); +zapReceiptEvent.content = ""; +zapReceiptEvent.kind = NDKKind.Zap; +zapReceiptEvent.created_at = invoiceTimestamp; +zapReceiptEvent.tags.push(["p", hexpubkey]); +zapReceiptEvent.tags.push(["bolt11", invoice]); +zapReceiptEvent.tags.push(["client", "asknostr.site"]); +zapReceiptEvent.tags.push([ + "description", + JSON.stringify(zapRequest?.rawEvent()), +]); + +await zapReceiptEvent.publish(); + +``` + From b69ecf7aebb50c01eef556be25e8486ea2edb8f6 Mon Sep 17 00:00:00 2001 From: digitalbase Date: Mon, 13 Oct 2025 20:38:40 +0200 Subject: [PATCH 035/139] Update docs --- wallet/docs/NDKWebLNWallet.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/wallet/docs/NDKWebLNWallet.md b/wallet/docs/NDKWebLNWallet.md index ad37a107c..4a5f91a15 100644 --- a/wallet/docs/NDKWebLNWallet.md +++ b/wallet/docs/NDKWebLNWallet.md @@ -143,9 +143,7 @@ await wallet.lnPay({ pr: invoice, }); -// send a zap to confirm -setCurrentlyDoing("Publishing Zap Request"); - +// extract the timestamp from the invoice const invoiceDecoded = decode(invoice); const timestampSection = invoiceDecoded.sections.filter( (section) => section.name === "timestamp", From 92870790ce37eaabe22b70314ffe2b8fe6f52f6c Mon Sep 17 00:00:00 2001 From: digitalbase Date: Mon, 13 Oct 2025 20:47:57 +0200 Subject: [PATCH 036/139] Refer to Nip057 docs --- wallet/docs/NDKWebLNWallet.md | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/wallet/docs/NDKWebLNWallet.md b/wallet/docs/NDKWebLNWallet.md index 4a5f91a15..345008de0 100644 --- a/wallet/docs/NDKWebLNWallet.md +++ b/wallet/docs/NDKWebLNWallet.md @@ -95,7 +95,10 @@ Make sure to await the promise to fully refresh the balance. ## Nostr Zaps -To send Zaps using the WebLn wallet there are a few other steps to follow: +Lightning Zaps on the Nostr network are described in[ NIP-57](https://nostr-nips.com/nip-57). + +The full protocol (Step 1 to 9) is described in the respective docs. In the below example we will +refer to the steps described in the Nostr Implementation Protocol (NIP). ```typescript @@ -106,6 +109,7 @@ import { NDKZapper } from "@nostr-dev-kit/ndk"; +// Step 01: Calculate lnurl const lud06 = ''; const lud16 = 'pablof7z@primal.net'; @@ -118,6 +122,7 @@ const lnMeta = await getNip57ZapSpecFromLud( ndk, // pass NDK instance ); +// Step 03: General Zap request const target = new NDKUser({ npub: '' }); // User you want to zap const amount = 1000; // amount in MSats @@ -132,6 +137,8 @@ const zapRequest = await generateZapRequest( message, // message ); +// Step 04 to 07: Retrieve invoice + // create zapper instance and get lightning invoice const zapper = new NDKZapper(target, amount, "msat", { ndk }); @@ -153,6 +160,8 @@ const invoiceTimestamp = timestampSection[0] ? Number(timestampSection[0].value) : Math.floor(Date.now() / 1000); + +// Step 08: Publish zap receipt const zapReceiptEvent = new NDKEvent(ndk); zapReceiptEvent.content = ""; zapReceiptEvent.kind = NDKKind.Zap; From 6a3fa403ec816dffeb759052109a31e7c925c080 Mon Sep 17 00:00:00 2001 From: digitalbase Date: Tue, 14 Oct 2025 08:21:35 +0200 Subject: [PATCH 037/139] Update NWC and WebLN docs --- wallet/docs/NDKNWCWallet.md | 171 +++++++++++++++++++++++++++++++++- wallet/docs/NDKWebLNWallet.md | 2 +- 2 files changed, 167 insertions(+), 6 deletions(-) diff --git a/wallet/docs/NDKNWCWallet.md b/wallet/docs/NDKNWCWallet.md index 300beeb84..9918e8a5d 100644 --- a/wallet/docs/NDKNWCWallet.md +++ b/wallet/docs/NDKNWCWallet.md @@ -1,19 +1,180 @@ # NWC Client (`NDKWalletNWC`) -The `NDKWalletNWC` implements the NIP-47 specification for Nostr Wallet Connect: +The `NDKWalletNWC` implements the [NIP-47](https://nostr-nips.com/nip-47) specification and allows you to zap using [Nostr +Web Connect (NWC)](https://nwc.dev/). -- Connect to NWC-compatible wallets +## Usage + +The NWC wallet can be used to: + +- Connect to NWC Wallets - Send payment requests - Query wallet information - Handle payment responses +## Install + +To initialise WebLN ensure you have the ndk-wallet installed + +::: code-group + +```sh [npm] +npm i @nostr-dev-kit/ndk-wallet +``` + +```sh [pnpm] +pnpm add @nostr-dev-kit/ndk-wallet +``` + +```sh [yarn] +yarn add @nostr-dev-kit/ndk-wallet +``` + +```sh [bun] +bun add @nostr-dev-kit/ndk-wallet +``` +::: + +## Initialising + ```typescript +import NDK from "@nostr-dev-kit/ndk"; import { NDKWalletNWC } from "@nostr-dev-kit/ndk-wallet"; -// Create an NWC wallet -const wallet = new NDKWalletNWC(ndk, nwcConnectionInfo); +const ndk = new NDK(); + +// Create an WebLN wallet +const wallet = new NDKWalletNWC(ndk); + +ndk.wallet = wallet; +``` + +## Pay an invoice + +To pay an invoice you can use the wallet instance directly: + +```typescript +// Generate payment request +const paymentRequest = { pr: "lnbc..."} as LnPaymentInfo; // Pay an invoice -await wallet.pay({ invoice: "lnbc..." }); +await wallet.pay(paymentRequest); +``` + +Or using the NDK instance: + +```typescript +// Generate payment request +const paymentRequest = { pr: "lnbc..."} as LnPaymentInfo; + +// Pay an invoice +await ndk.wallet.pay(paymentRequest); +``` + +## Retrieve + +To retrieve the balance you can use the wallet instance directly or using the NDK instance; + +```typescript +console.log(wallet.balance); + +console.log(ndk.wallet.balance); +``` + +## Update balance + +To refresh the balance from the linked wallet use the wallet instance directly or using the NDK instance; + +```typescript +await wallet.updateBalance; + +await ndk.wallet.balance; +``` + +Make sure to await the promise to fully refresh the balance. + +## Nostr Zaps + +Lightning Zaps using NWC are described in [NIP-47](https://nostr-nips.com/nip-47). + +The full protocol (Step 1 to 9) is described in the respective docs. In the below example we will +refer to the steps described in the Nostr Implementation Protocol (NIP). + +```typescript + +import { + generateZapRequest, + getNip57ZapSpecFromLud, + NDKUser, + NDKZapper +} from "@nostr-dev-kit/ndk"; + +// Step 01: Calculate lnurl +const lud06 = ''; +const lud16 = 'pablof7z@primal.net'; + +// retrieve lightning callback data for user +const lnMeta = await getNip57ZapSpecFromLud( + { + lud16, + lud06, + }, + ndk, // pass NDK instance +); + +// Step 03: General Zap request +const target = new NDKUser({ npub: '' }); // User you want to zap +const amount = 1000; // amount in MSats + +// generate zap request, this event is not published to relays +const zapRequest = await generateZapRequest( + target, + ndk, + lnMeta, + ndk.activeUser.npub, + amount, + relays, // optional relays to send zapReceipt to + message, // message +); + +// Step 04 to 07: Retrieve invoice + +// create zapper instance and get lightning invoice +const zapper = new NDKZapper(target, amount, "msat", { ndk }); + +// retrieve the lightning invoice +const invoice = await zapper.getLnInvoice(zapRequest, amount, lnMeta); + +// pay the invoice +await wallet.lnPay({ + pr: invoice, +}); + +// extract the timestamp from the invoice +const invoiceDecoded = decode(invoice); +const timestampSection = invoiceDecoded.sections.filter( + (section) => section.name === "timestamp", +); + +const invoiceTimestamp = timestampSection[0] + ? Number(timestampSection[0].value) + : Math.floor(Date.now() / 1000); + + +// Step 08: Publish zap receipt +const zapReceiptEvent = new NDKEvent(ndk); +zapReceiptEvent.content = ""; +zapReceiptEvent.kind = NDKKind.Zap; +zapReceiptEvent.created_at = invoiceTimestamp; +zapReceiptEvent.tags.push(["p", hexpubkey]); +zapReceiptEvent.tags.push(["bolt11", invoice]); +zapReceiptEvent.tags.push(["client", "asknostr.site"]); +zapReceiptEvent.tags.push([ + "description", + JSON.stringify(zapRequest?.rawEvent()), +]); + +await zapReceiptEvent.publish(); + ``` \ No newline at end of file diff --git a/wallet/docs/NDKWebLNWallet.md b/wallet/docs/NDKWebLNWallet.md index 345008de0..acd39509f 100644 --- a/wallet/docs/NDKWebLNWallet.md +++ b/wallet/docs/NDKWebLNWallet.md @@ -95,7 +95,7 @@ Make sure to await the promise to fully refresh the balance. ## Nostr Zaps -Lightning Zaps on the Nostr network are described in[ NIP-57](https://nostr-nips.com/nip-57). +Lightning Zaps on the Nostr network are described in [NIP-57](https://nostr-nips.com/nip-57). The full protocol (Step 1 to 9) is described in the respective docs. In the below example we will refer to the steps described in the Nostr Implementation Protocol (NIP). From 2311f38d154d987ad6e02d6640d1d276ebda30fc Mon Sep 17 00:00:00 2001 From: digitalbase Date: Tue, 14 Oct 2025 16:05:57 +0200 Subject: [PATCH 038/139] Update nutsack docs https://github.com/nostr-dev-kit/ndk/pull/298/files --- wallet/docs/nutsack.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wallet/docs/nutsack.md b/wallet/docs/nutsack.md index a511995b5..4a0f4037b 100644 --- a/wallet/docs/nutsack.md +++ b/wallet/docs/nutsack.md @@ -58,7 +58,7 @@ ndk.wallet = wallet; Now that we have a wallet, some funds, and we have ndk prepared to use that wallet, we'll send a zap. NDK provides a convenient `wallet` setter that allows ```ts -const user = await NDKUser.fronNip05("_@f7z.io"); +const user = await NDKUser.fromNip05("_@f7z.io", ndk); const zapper = new NDKZapper(user, 1, "sat", { comment: "hello from my wallet!", }); From b694aaa322ad3cef3d1ad9c127f25d7e5ea08b3c Mon Sep 17 00:00:00 2001 From: digitalbase Date: Tue, 14 Oct 2025 16:16:42 +0200 Subject: [PATCH 039/139] Revert mobile changelog --- mobile/CHANGELOG.md | 855 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 855 insertions(+) diff --git a/mobile/CHANGELOG.md b/mobile/CHANGELOG.md index e69de29bb..d28419738 100644 --- a/mobile/CHANGELOG.md +++ b/mobile/CHANGELOG.md @@ -0,0 +1,855 @@ +# @nostr-dev-kit/ndk-mobile + +## 0.9.1 + +### Patch Changes + +- Updated dependencies +- Updated dependencies + - @nostr-dev-kit/ndk@2.17.6 + +## 0.9.0 + +### Minor Changes + +- Add NIP-05 verification caching to SQLite adapters + + Implements NIP-05 verification result caching in both cache-sqlite-wasm and mobile SQLite adapters to prevent unnecessary network requests. Both successful and failed verifications are now cached with smart expiration: + - Successful verifications are cached indefinitely + - Failed verifications are cached for 1 hour (configurable) to prevent hammering down/non-existent endpoints + - Expired failed verifications return "missing" to trigger retry + - Fresh failed verifications return null to prevent re-fetch + + This brings feature parity with cache-dexie and cache-memory adapters. + +### Patch Changes + +- Updated dependencies +- Updated dependencies +- Updated dependencies +- Updated dependencies + - @nostr-dev-kit/ndk@2.17.5 + +## 0.8.56 + +### Patch Changes + +- Updated dependencies + - @nostr-dev-kit/ndk@2.17.4 + +## 0.8.55 + +### Patch Changes + +- Updated dependencies + - @nostr-dev-kit/wallet@0.8.6 + - @nostr-dev-kit/react@1.3.12 + +## 0.8.54 + +### Patch Changes + +- Updated dependencies + - @nostr-dev-kit/wallet@0.8.5 + - @nostr-dev-kit/react@1.3.11 + +## 0.8.53 + +### Patch Changes + +- Updated dependencies + - @nostr-dev-kit/wallet@0.8.4 + - @nostr-dev-kit/react@1.3.10 + +## 0.8.52 + +### Patch Changes + +- Updated dependencies [8678b1f] +- Updated dependencies [c901395] + - @nostr-dev-kit/ndk@2.17.3 + +## 0.8.51 + +### Patch Changes + +- Updated dependencies + - @nostr-dev-kit/wallet@0.8.3 + - @nostr-dev-kit/react@1.3.9 + +## 0.8.50 + +### Patch Changes + +- Updated dependencies [8315d5e] +- Updated dependencies [d9d5662] +- Updated dependencies [6fb3a7f] +- Updated dependencies [028367b] + - @nostr-dev-kit/ndk@2.18.0 + +## 0.8.49 + +### Patch Changes + +- Updated dependencies + - @nostr-dev-kit/ndk@2.17.1 + +## 0.8.48 + +### Patch Changes + +- Updated dependencies [344c313] +- Updated dependencies [3407126] +- Updated dependencies [344c313] + - @nostr-dev-kit/ndk@2.17.0 + - @nostr-dev-kit/wallet@0.8.2 + - @nostr-dev-kit/react@1.3.8 + +## 0.8.47 + +### Patch Changes + +- Updated dependencies + - @nostr-dev-kit/wallet@0.8.1 + - @nostr-dev-kit/react@1.3.7 + +## 0.8.46 + +### Patch Changes + +- Updated dependencies + - @nostr-dev-kit/ndk@2.16.1 + +## 0.8.45 + +### Patch Changes + +- Updated dependencies [e596023] +- Updated dependencies [e596023] + - @nostr-dev-kit/ndk@2.16.0 + - @nostr-dev-kit/wallet@0.8.0 + - @nostr-dev-kit/ndk-hooks@1.3.6 + +## 0.8.44 + +### Patch Changes + +- Updated dependencies [a912a2c] + - @nostr-dev-kit/ndk@2.15.3 + - @nostr-dev-kit/ndk-wallet@0.7.2 + - @nostr-dev-kit/ndk-hooks@1.3.5 + +## 0.8.43 + +### Patch Changes + +- Updated dependencies [b7e7f92] + - @nostr-dev-kit/ndk@2.17.1 + +## 0.8.42 + +### Patch Changes + +- Updated dependencies [73c6a2f] +- Updated dependencies [fad1f3d] + - @nostr-dev-kit/ndk@2.17.0 + +## 0.8.40 + +### Patch Changes + +- Updated dependencies +- Updated dependencies +- Updated dependencies +- Updated dependencies + - @nostr-dev-kit/ndk-wallet@0.7.0 + - @nostr-dev-kit/ndk@2.15.0 + - @nostr-dev-kit/ndk-hooks@1.3.3 + +## 0.8.39 + +### Patch Changes + +- Updated dependencies + - @nostr-dev-kit/ndk-hooks@1.3.2 + +## 0.8.38 + +### Patch Changes + +- Updated dependencies + - @nostr-dev-kit/ndk@2.14.38 + +## 0.8.37 + +### Patch Changes + +- Updated dependencies [2886111] +- Updated dependencies [96341c3] + - @nostr-dev-kit/ndk@2.14.37 + - @nostr-dev-kit/ndk-wallet@0.6.3 + - @nostr-dev-kit/ndk-hooks@1.3.1 + +## 0.8.36 + +### Patch Changes + +- Updated dependencies [8bd22bd] +- Updated dependencies + - @nostr-dev-kit/ndk@2.14.36 + - @nostr-dev-kit/ndk-hooks@1.3.0 + +## 0.8.35 + +### Patch Changes + +- Updated dependencies + - @nostr-dev-kit/ndk-hooks@1.2.5 + +## 0.8.34 + +### Patch Changes + +- Updated dependencies + - @nostr-dev-kit/ndk@2.14.35 + +## 0.8.33 + +### Patch Changes + +- Updated dependencies + - @nostr-dev-kit/ndk-hooks@1.2.4 + +## 0.8.32 + +### Patch Changes + +- Updated dependencies [d89dbc6] +- Updated dependencies [fff020a] + - @nostr-dev-kit/ndk@2.14.34 + +## 0.8.31 + +### Patch Changes + +- Updated dependencies + - @nostr-dev-kit/ndk@2.14.33 + +## 0.8.30 + +### Patch Changes + +- Updated dependencies [9cb8407] + - @nostr-dev-kit/ndk@2.14.32 + +## 0.8.29 + +### Patch Changes + +- Updated dependencies + - @nostr-dev-kit/ndk@2.14.31 + +## 0.8.28 + +### Patch Changes + +- Updated dependencies + - @nostr-dev-kit/ndk@2.14.30 + +## 0.8.27 + +### Patch Changes + +- Updated dependencies + - @nostr-dev-kit/ndk-hooks@1.2.3 + +## 0.8.26 + +### Patch Changes + +- Updated dependencies + - @nostr-dev-kit/ndk@2.14.29 + +## 0.8.25 + +### Patch Changes + +- Updated dependencies + - @nostr-dev-kit/ndk@2.14.28 + +## 0.8.24 + +### Patch Changes + +- Updated dependencies + - @nostr-dev-kit/ndk@2.14.27 + +## 0.8.23 + +### Patch Changes + +- Updated dependencies + - @nostr-dev-kit/ndk@2.14.26 + +## 0.8.22 + +### Patch Changes + +- Updated dependencies + - @nostr-dev-kit/ndk@2.14.25 + +## 0.8.21 + +### Patch Changes + +- Updated dependencies + - @nostr-dev-kit/ndk-wallet@0.6.2 + - @nostr-dev-kit/ndk-hooks@1.2.2 + +## 0.8.20 + +### Patch Changes + +- Updated dependencies + - @nostr-dev-kit/ndk-wallet@0.6.1 + - @nostr-dev-kit/ndk-hooks@1.2.1 + +## 0.8.19 + +### Patch Changes + +- Updated dependencies + - @nostr-dev-kit/ndk@2.14.24 + +## 0.8.18 + +### Patch Changes + +- Updated dependencies + - @nostr-dev-kit/ndk-hooks@1.2.0 + +## 0.8.17 + +### Patch Changes + +- Updated dependencies [7476407] +- Updated dependencies [7476407] + - @nostr-dev-kit/ndk@2.14.23 + - @nostr-dev-kit/ndk-hooks@1.1.45 + +## 0.8.16 + +### Patch Changes + +- Updated dependencies + - @nostr-dev-kit/ndk@2.14.22 + +## 0.8.15 + +### Patch Changes + +- Updated dependencies + - @nostr-dev-kit/ndk@2.14.21 + +## 0.8.14 + +### Patch Changes + +- Updated dependencies + - @nostr-dev-kit/ndk@2.14.20 + +## 0.8.13 + +### Patch Changes + +- Updated dependencies + - @nostr-dev-kit/ndk@2.14.19 + +## 0.8.12 + +### Patch Changes + +- Updated dependencies + - @nostr-dev-kit/ndk-hooks@1.1.44 + +## 0.8.11 + +### Patch Changes + +- Updated dependencies + - @nostr-dev-kit/ndk-hooks@1.1.43 + +## 0.8.10 + +### Patch Changes + +- Updated dependencies + - @nostr-dev-kit/ndk@2.14.18 + +## 0.8.9 + +### Patch Changes + +- Updated dependencies + - @nostr-dev-kit/ndk@2.14.17 + +## 0.8.8 + +### Patch Changes + +- Updated dependencies + - @nostr-dev-kit/ndk@2.14.16 + +## 0.8.7 + +### Patch Changes + +- Updated dependencies + - @nostr-dev-kit/ndk-hooks@1.1.42 + +## 0.8.6 + +### Patch Changes + +- Updated dependencies + - @nostr-dev-kit/ndk@2.14.15 + +## 0.8.5 + +### Patch Changes + +- Updated dependencies + - @nostr-dev-kit/ndk@2.14.14 + +## 0.8.4 + +### Patch Changes + +- Updated dependencies + - @nostr-dev-kit/ndk@2.14.13 + +## 0.8.3 + +### Patch Changes + +- Updated dependencies + - @nostr-dev-kit/ndk-hooks@1.1.41 + +## 0.8.2 + +### Patch Changes + +- Updated dependencies + - @nostr-dev-kit/ndk-hooks@1.1.40 + +## 0.8.1 + +### Patch Changes + +- Updated dependencies + - @nostr-dev-kit/ndk-hooks@1.1.39 + +## 0.8.0 + +### Minor Changes + +- bump + +### Patch Changes + +- Updated dependencies + - @nostr-dev-kit/ndk-wallet@0.6.0 + - @nostr-dev-kit/ndk-hooks@1.1.38 + +## 0.7.15 + +### Patch Changes + +- Updated dependencies + - @nostr-dev-kit/ndk-wallet@0.5.14 + - @nostr-dev-kit/ndk-hooks@1.1.37 + +## 0.7.14 + +### Patch Changes + +- Updated dependencies + - @nostr-dev-kit/ndk-hooks@1.1.36 + +## 0.7.13 + +### Patch Changes + +- Updated dependencies + - @nostr-dev-kit/ndk@2.14.12 + +## 0.7.12 + +### Patch Changes + +- update expo-nip55 + +## 0.7.11 + +### Patch Changes + +- Updated dependencies + - @nostr-dev-kit/ndk@2.14.11 + +## 0.7.10 + +### Patch Changes + +- Updated dependencies + - @nostr-dev-kit/ndk@2.14.10 + +## 0.7.9 + +### Patch Changes + +- Updated dependencies + - @nostr-dev-kit/ndk@2.14.8 + +## 0.7.8 + +### Patch Changes + +- Updated dependencies + - @nostr-dev-kit/ndk@2.14.7 + - @nostr-dev-kit/ndk-wallet@0.5.11 + - @nostr-dev-kit/ndk-hooks@1.1.26 + +## 0.7.7 + +### Patch Changes + +- Updated dependencies + - @nostr-dev-kit/ndk@2.14.6 + - @nostr-dev-kit/ndk-wallet@0.5.10 + - @nostr-dev-kit/ndk-hooks@1.1.25 + +## 0.7.6 + +### Patch Changes + +- Updated dependencies + - @nostr-dev-kit/ndk@2.14.5 + - @nostr-dev-kit/ndk-wallet@0.5.9 + - @nostr-dev-kit/ndk-hooks@1.1.24 + +## 0.7.5 + +### Patch Changes + +- Updated dependencies +- Updated dependencies + - @nostr-dev-kit/ndk@2.14.4 + - @nostr-dev-kit/ndk-wallet@0.5.8 + - @nostr-dev-kit/ndk-hooks@1.1.23 + +## 0.7.4 + +### Patch Changes + +- Updated dependencies + - @nostr-dev-kit/ndk@2.14.3 + - @nostr-dev-kit/ndk-wallet@0.5.7 + - @nostr-dev-kit/ndk-hooks@1.1.22 + +## 0.7.3 + +### Patch Changes + +- Updated dependencies + - @nostr-dev-kit/ndk-hooks@1.1.21 + +## 0.7.2 + +### Patch Changes + +- Updated dependencies + - @nostr-dev-kit/ndk-hooks@1.1.20 + +## 0.7.1 + +### Patch Changes + +- Updated dependencies + - @nostr-dev-kit/ndk-hooks@1.1.19 + +## 0.6.19 + +### Patch Changes + +- Updated dependencies + - @nostr-dev-kit/ndk-hooks@1.1.17 + +## 0.6.18 + +### Patch Changes + +- Updated dependencies + - @nostr-dev-kit/ndk-hooks@1.1.16 + +## 0.6.17 + +### Patch Changes + +- Updated dependencies + - @nostr-dev-kit/ndk-hooks@1.1.15 + +## 0.6.16 + +### Patch Changes + +- Updated dependencies + - @nostr-dev-kit/ndk-hooks@1.1.14 + +## 0.6.15 + +### Patch Changes + +- Updated dependencies + - @nostr-dev-kit/ndk-hooks@1.1.13 + +## 0.6.14 + +### Patch Changes + +- Updated dependencies + - @nostr-dev-kit/ndk-hooks@1.1.12 + +## 0.6.13 + +### Patch Changes + +- Updated dependencies + - @nostr-dev-kit/ndk-wallet@0.5.6 + - @nostr-dev-kit/ndk-hooks@1.1.11 + +## 0.6.12 + +### Patch Changes + +- Updated dependencies + - @nostr-dev-kit/ndk-hooks@1.1.10 + +## 0.6.11 + +### Patch Changes + +- redo imports +- Updated dependencies + - @nostr-dev-kit/ndk-hooks@1.1.9 + +## 0.6.10 + +### Patch Changes + +- Updated dependencies + - @nostr-dev-kit/ndk@2.14.2 + - @nostr-dev-kit/ndk-hooks@1.1.7 + - @nostr-dev-kit/ndk-wallet@0.5.5 + +## 0.6.9 + +### Patch Changes + +- Updated dependencies + - @nostr-dev-kit/ndk-hooks@1.1.6 + +## 0.6.8 + +### Patch Changes + +- update docs +- Updated dependencies + - @nostr-dev-kit/ndk-hooks@1.1.5 + +## 0.6.7 + +### Patch Changes + +- fiddle with exports +- Updated dependencies + - @nostr-dev-kit/ndk-hooks@1.1.3 + +## 0.6.6 + +### Patch Changes + +- Updated dependencies + - @nostr-dev-kit/ndk-hooks@1.1.3 + +## 0.6.5 + +### Patch Changes + +- export NDK + +## 0.6.4 + +### Patch Changes + +- Updated dependencies + - @nostr-dev-kit/ndk-hooks@1.1.1 + +## 0.6.3 + +### Patch Changes + +- Updated dependencies + - @nostr-dev-kit/ndk-hooks@1.1.0 + +## 0.6.2 + +### Patch Changes + +- Updated dependencies + - @nostr-dev-kit/ndk-wallet@0.5.4 + - @nostr-dev-kit/ndk-hooks@1.0.2 + +## 0.6.1 + +### Patch Changes + +- Updated dependencies + - @nostr-dev-kit/ndk@2.14.1 + - @nostr-dev-kit/ndk-hooks@1.0.1 + - @nostr-dev-kit/ndk-wallet@0.5.3 + +## 0.6.0 + +### Minor Changes + +- 5ab19ef: feat: Refactor session management and add persistence + - **ndk-core:** Added signer serialization (`toPayload`, `fromPayload`) and deserialization (`ndkSignerFromPayload`, `signerRegistry`) framework. + - **ndk-hooks:** (Breaking Change) Refactored session state into `useNDKSessions` store with new management functions (`addSigner`, `startSession`, `switchToUser`, etc.), removing old session logic. + - **ndk-mobile:** Added persistent session storage using `expo-secure-store` (`session-storage.ts`, `useSessionMonitor`, `bootNDK`). Updated `NDKNip55Signer` for serialization and registration. + +### Patch Changes + +- c83166a: bump +- 6e16e06: Enhance SQLite adapter to support decrypted events storage and retrieval. +- import changes +- df73b9b: add component +- Updated dependencies [c83166a] +- Updated dependencies [5ab19ef] +- Updated dependencies [6e16e06] +- Updated dependencies +- Updated dependencies [5ab19ef] + - @nostr-dev-kit/ndk-wallet@0.5.2 + - @nostr-dev-kit/ndk-hooks@1.0.0 + - @nostr-dev-kit/ndk@2.14.0 + +## 2.3.1-rc1.0 + +### Patch Changes + +- add component + +## 0.4.4 + +### Patch Changes + +- Updated dependencies +- Updated dependencies +- Updated dependencies + - @nostr-dev-kit/ndk-wallet@0.5.0 + +## 0.4.3 + +### Patch Changes + +- Updated dependencies + - @nostr-dev-kit/ndk@2.12.2 + - @nostr-dev-kit/ndk-wallet@0.4.3 + +## 0.4.2 + +### Patch Changes + +- sqlite search profile support +- e667a60: store parsed profile in sqlite adapter +- Updated dependencies [3ea9695] +- Updated dependencies [cca3357] +- Updated dependencies [1235f69] + - @nostr-dev-kit/ndk@2.12.1 + - @nostr-dev-kit/ndk-wallet@0.4.2 + +## 0.4.1 + +### Patch Changes + +- d87d886: Leverage synchronous cache adapter to load events in one go in useSubscribe hook +- Updated dependencies [f255a07] +- Updated dependencies [f255a07] +- Updated dependencies [2171140] +- Updated dependencies [72c8492] +- Updated dependencies [72c8492] + - @nostr-dev-kit/ndk@2.12.0 + - @nostr-dev-kit/ndk-wallet@0.4.1 + +## 0.4.0 + +### Minor Changes + +- changes to the initialization hook to allow for more fine grained database initialization logic + +### Patch Changes + +- Updated dependencies +- Updated dependencies +- Updated dependencies +- Updated dependencies + - @nostr-dev-kit/ndk@2.11.2 + - @nostr-dev-kit/ndk-wallet@0.4.0 + +## 0.3.0 + +### Minor Changes + +- NIP-55 support (thanks to nostr:npub1ehhfg09mr8z34wz85ek46a6rww4f7c7jsujxhdvmpqnl5hnrwsqq2szjqv !) + +### Patch Changes + +- Updated dependencies + - @nostr-dev-kit/ndk@2.11.1 + - @nostr-dev-kit/ndk-wallet@0.3.17 + +## 0.2.2 + +### Patch Changes + +- f2b307d: add useUserProfile hook +- 6b3ea8b: add LRU cache for profiles +- 1301db9: add sync profile fetching from cache +- Updated dependencies [35987be] +- Updated dependencies [689305c] +- Updated dependencies [35987be] +- Updated dependencies [35987be] +- Updated dependencies [689305c] +- Updated dependencies +- Updated dependencies [4ed75a6] + - @nostr-dev-kit/ndk@2.11.0 + - @nostr-dev-kit/ndk-wallet@0.3.16 + +## 0.2.1 + +### Patch Changes + +- Updated dependencies + - @nostr-dev-kit/ndk@2.10.7 + - @nostr-dev-kit/ndk-wallet@0.3.15 + +## 0.2.0 + +### Minor Changes + +- add the very handy useNDKSessionEventKind + +## 0.1.5 + +### Patch Changes + +- add default export \ No newline at end of file From cd9749bdff486fd8603d372262fca87bdafeb92b Mon Sep 17 00:00:00 2001 From: digitalbase Date: Tue, 14 Oct 2025 16:17:52 +0200 Subject: [PATCH 040/139] Update bun lock --- bun.lock | 1000 ++++++++---------------------------------------------- 1 file changed, 134 insertions(+), 866 deletions(-) diff --git a/bun.lock b/bun.lock index e27b3b673..f43fefb13 100644 --- a/bun.lock +++ b/bun.lock @@ -20,88 +20,88 @@ "name": "@nostr-dev-kit/blossom", "version": "6.0.4", "dependencies": { - "@nostr-dev-kit/ndk": "2.17.6", + "@nostr-dev-kit/ndk": "^2.17.6", }, "devDependencies": { - "@types/node": "^20.0.0", + "@types/node": "^24.7.0", "rimraf": "^5.0.0", - "typescript": "^5.0.0", - "vitest": "^1.0.0", + "typescript": "^5.9.3", + "vitest": "^3.2.4", }, "peerDependencies": { - "@nostr-dev-kit/ndk": "2.17.6", + "@nostr-dev-kit/ndk": "^2.17.6", }, }, "cache-dexie": { "name": "@nostr-dev-kit/cache-dexie", "version": "2.7.6", "dependencies": { - "@nostr-dev-kit/ndk": "2.17.6", + "@nostr-dev-kit/ndk": "^2.17.6", "debug": "^4.3.7", "dexie": "^4.0.8", - "nostr-tools": "^2.4.0", + "nostr-tools": "^2.17.0", "typescript-lru-cache": "^2.0.0", }, "devDependencies": { "@types/debug": "^4.1.12", - "@types/node": "^22.6.1", + "@types/node": "^24.7.0", "fake-indexeddb": "^6.0.0", "prettier": "^3.3.3", "ts-node": "^10.9.2", - "tsup": "^8.3.0", - "typedoc": "^0.28.0", - "typedoc-plugin-markdown": "^4.5.0", - "vitest": "^1.2.0", + "tsup": "^8.4.0", + "typedoc": "^0.28.13", + "typedoc-plugin-markdown": "^4.9.0", + "vitest": "^3.2.4", }, }, "cache-memory": { "name": "@nostr-dev-kit/cache-memory", "version": "2.7.6", "dependencies": { - "@nostr-dev-kit/ndk": "2.17.6", + "@nostr-dev-kit/ndk": "^2.17.6", "debug": "^4.3.7", "nostr-tools": "^2.17.0", "typescript-lru-cache": "^2.0.0", }, "devDependencies": { "@types/debug": "^4.1.12", - "@types/node": "^22.6.1", + "@types/node": "^24.7.0", "prettier": "^3.3.3", "ts-node": "^10.9.2", - "tsup": "^8.3.0", - "typedoc": "^0.28.0", - "typedoc-plugin-markdown": "^4.5.0", - "vitest": "^1.2.0", + "tsup": "^8.4.0", + "typedoc": "^0.28.13", + "typedoc-plugin-markdown": "^4.9.0", + "vitest": "^3.2.4", }, }, "cache-nostr": { "name": "@nostr-dev-kit/cache-nostr", "version": "0.1.65", "dependencies": { - "@nostr-dev-kit/ndk": "2.17.6", - "debug": "^4.3.4", - "typescript": "^5.8.2", + "@nostr-dev-kit/ndk": "^2.17.6", + "debug": "^4.3.7", + "typescript": "^5.9.3", "websocket-polyfill": "^0.0.3", }, "devDependencies": { - "@types/debug": "^4.1.7", - "@types/node": "^18.15.11", + "@types/debug": "^4.1.12", + "@types/node": "^24.7.0", "ts-node": "^10.9.2", - "tsup": "^7.2.0", + "tsup": "^8.4.0", }, }, "cache-redis": { "name": "@nostr-dev-kit/cache-redis", "version": "0.1.51", "dependencies": { - "@nostr-dev-kit/ndk": "2.17.2", + "@nostr-dev-kit/ndk": "^2.17.6", "ioredis": "^5.0.0", }, "devDependencies": { "@types/ioredis": "^5.0.0", - "@types/node": "^20.0.0", - "tsup": "^8", - "typescript": "^5.8.2", + "@types/node": "^24.7.0", + "tsup": "^8.4.0", + "typescript": "^5.9.3", }, }, "cache-sqlite": { @@ -113,9 +113,9 @@ "devDependencies": { "@nostr-dev-kit/ndk": "^2.17.6", "@types/better-sqlite3": "^7.6.8", - "tsup": "^8.0.0", - "typescript": "^5.0.0", - "vitest": "^1.0.0", + "tsup": "^8.4.0", + "typescript": "^5.9.3", + "vitest": "^3.2.4", }, "peerDependencies": { "@nostr-dev-kit/ndk": "^2.17.6", @@ -132,7 +132,7 @@ "@types/node": "^24.7.0", "@types/sql.js": "^1.4.9", "jsdom": "^27.0.0", - "typescript": "^5.0.0", + "typescript": "^5.9.3", "vitest": "^3.2.4", }, }, @@ -145,29 +145,29 @@ "@noble/hashes": "^1.5.0", "@noble/secp256k1": "^2.1.0", "@scure/base": "^1.1.9", - "debug": "^4.3.6", + "debug": "^4.3.7", "light-bolt11-decoder": "^3.2.0", "shiki": "^3.13.0", "tseep": "^1.3.1", - "typescript-lru-cache": "^2", + "typescript-lru-cache": "^2.0.0", }, "devDependencies": { "@types/debug": "^4.1.12", - "@types/node": "^22.13.10", - "@vitest/coverage-v8": "3.1.2", - "esbuild": "^0.25.1", + "@types/node": "^24.7.0", + "@vitest/coverage-v8": "^3.1.2", + "esbuild": "^0.25.10", "esbuild-plugin-alias": "^0.2.1", "esm-loader-typescript": "^1.0.6", - "nostr-tools": "2.7.1", + "nostr-tools": "^2.17.0", "ts-node": "^10.9.2", "tsd": "^0.31.2", "tsup": "^8.4.0", - "typedoc": "^0.28.0", + "typedoc": "^0.28.13", "typedoc-plugin-rename-defaults": "^0.7.3", - "vitest": "^3.1.2", + "vitest": "^3.2.4", }, "peerDependencies": { - "nostr-tools": "^2", + "nostr-tools": "^2.17.0", }, }, "mobile": { @@ -175,50 +175,50 @@ "version": "0.9.1", "dependencies": { "@bacons/text-decoder": "^0.0.0", - "@nostr-dev-kit/ndk": "2.17.2", - "@nostr-dev-kit/react": "1.3.8", - "@nostr-dev-kit/wallet": "0.8.2", + "@nostr-dev-kit/ndk": "^2.17.6", + "@nostr-dev-kit/react": "^1.3.12", + "@nostr-dev-kit/wallet": "^0.8.6", "buffer": "^6.0.3", - "expo-image": "~2.0.7", + "expo-image": "^2.0.7", "expo-nip55": "^0.1.7", - "expo-secure-store": "~14.0.1", - "react-native-get-random-values": "~1.11.0", + "expo-secure-store": "^14.0.1", + "react-native-get-random-values": "^1.11.0", "typescript-lru-cache": "^2.0.0", - "zustand": "^5.0.2", + "zustand": "^5.0.3", }, "devDependencies": { "@types/react-native": "^0.73.0", "babel-preset-expo": "^12.0.9", "expo-sqlite": "^15.1.4", "react-native-builder-bob": "^0.40.6", - "typescript": "^5.8.3", + "typescript": "^5.9.3", }, "peerDependencies": { "expo": "^53", - "expo-sqlite": "^15", - "react-native": ">=0.71.0", - "react-native-reanimated": ">=3.16.0", + "expo-sqlite": "^15.1.4", + "react-native": "^0.71.0", + "react-native-reanimated": "^3.16.0", }, }, "react": { "name": "@nostr-dev-kit/react", "version": "1.3.12", "dependencies": { - "@nostr-dev-kit/ndk": "^2.17.0", - "@nostr-dev-kit/wallet": "0.8.2", + "@nostr-dev-kit/ndk": "^2.17.6", + "@nostr-dev-kit/wallet": "^0.8.6", "@testing-library/react": "^14.1.2", - "zustand": "^5", + "zustand": "^5.0.3", }, "devDependencies": { "@types/react": "^18.2.0", - "esbuild": "^0.17.19", - "jsdom": "^26.1.0", - "react": "^18.3.1", + "esbuild": "^0.25.10", + "jsdom": "^27.0.0", + "react": "*", "react-dom": "^18.3.1", - "tsup": "^8", - "typescript": "^5.8.2", - "vite": "^4.4.9", - "vitest": "^1.6.1", + "tsup": "^8.4.0", + "typescript": "^5.9.3", + "vite": "^7.1.9", + "vitest": "^3.2.4", "zustand": "^5.0.3", }, "peerDependencies": { @@ -230,13 +230,13 @@ "version": "0.6.0", "dependencies": { "@nostr-dev-kit/ndk": "^2.17.6", - "zustand": "^5", + "zustand": "^5.0.3", }, "devDependencies": { - "tsup": "^8", - "typescript": "^5.8.2", - "vite": "^4.4.9", - "vitest": "^1.6.1", + "tsup": "^8.4.0", + "typescript": "^5.9.3", + "vite": "^7.1.9", + "vitest": "^3.2.4", }, }, "svelte": { @@ -267,19 +267,19 @@ "@sveltejs/kit": "^2.0.0", "@sveltejs/package": "^2.0.0", "@sveltejs/vite-plugin-svelte": "^6.2.1", - "@types/node": "^22.18.8", + "@types/node": "^24.7.0", "@vitest/browser": "^3.2.4", "eslint": "^9.37.0", "eslint-plugin-svelte": "^3.12.4", "globals": "^16.4.0", "playwright": "^1.55.1", - "prettier": "^3.0.0", + "prettier": "^3.3.3", "prettier-plugin-svelte": "^3.0.0", "svelte": "^5.39.9", "svelte-check": "^4.0.0", "svelte-eslint-parser": "^1.3.3", "tslib": "^2.6.0", - "typescript": "^5.0.0", + "typescript": "^5.9.3", "typescript-eslint": "^8.46.0", "vite": "^7.1.9", "vitest": "^3.2.4", @@ -287,7 +287,7 @@ }, "peerDependencies": { "@nostr-dev-kit/ndk": "^2.17.6", - "svelte": "^5.0.0", + "svelte": "^5.39.9", }, }, "svelte/examples/component-browser": { @@ -298,10 +298,10 @@ "@nostr-dev-kit/svelte": "^2.0.2", }, "devDependencies": { - "@sveltejs/vite-plugin-svelte": "6.2.1", + "@sveltejs/vite-plugin-svelte": "^6.2.1", "esbuild": "^0.25.10", "svelte": "^5.39.9", - "typescript": "^5.7.3", + "typescript": "^5.9.3", "vite": "^7.1.9", }, }, @@ -331,7 +331,7 @@ "devDependencies": { "@sveltejs/vite-plugin-svelte": "^6.2.1", "svelte": "^5.39.9", - "typescript": "^5.0.0", + "typescript": "^5.9.3", "vite": "^7.1.9", }, }, @@ -373,17 +373,17 @@ "version": "0.3.4", "dependencies": { "@nostr-dev-kit/ndk": "^2.17.6", - "nostr-tools": "^2.10.2", + "nostr-tools": "^2.17.0", "tseep": "^1.3.1", }, "devDependencies": { "@nostr-dev-kit/cache-dexie": "^2.7.6", "@nostr-dev-kit/cache-memory": "^2.7.6", "@nostr-dev-kit/cache-nostr": "^0.1.65", - "tsup": "^8", - "typescript": "^5.8.2", - "vite": "^4.4.9", - "vitest": "^1.6.1", + "tsup": "^8.4.0", + "typescript": "^5.9.3", + "vite": "^7.1.9", + "vitest": "^3.2.4", }, }, "wallet": { @@ -392,16 +392,16 @@ "dependencies": { "@nostr-dev-kit/ndk": "^2.17.6", "@nostr-dev-kit/sync": "^0.3.4", - "debug": "^4.3.4", - "light-bolt11-decoder": "^3.0.0", + "debug": "^4.3.7", + "light-bolt11-decoder": "^3.2.0", "tseep": "^1.3.1", - "typescript": "^5.8.2", + "typescript": "^5.9.3", "webln": "^0.3.2", - "zustand": "^5.0.2", + "zustand": "^5.0.3", }, "devDependencies": { - "@cashu/cashu-ts": "2.1.0", - "@cashu/crypto": "0.3.4", + "@cashu/cashu-ts": "^2.1", + "@cashu/crypto": "*", "@webbtc/webln-types": "^3.0.0", "nock": "^13.5.6", "tsup": "^8.4.0", @@ -419,8 +419,8 @@ "dependencies": { "@nostr-dev-kit/ndk": "^2.17.6", "@nostr-dev-kit/sync": "^0.3.4", - "debug": "^4.3.4", - "typescript": "^5.8.2", + "debug": "^4.3.7", + "typescript": "^5.9.3", }, "devDependencies": { "tsup": "^8.4.0", @@ -1326,7 +1326,7 @@ "@types/ms": ["@types/ms@2.1.0", "", {}, "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA=="], - "@types/node": ["@types/node@20.19.20", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-2Q7WS25j4pS1cS8yw3d6buNCVJukOTeQ39bAnwR6sOJbaxvyCGebzTMypDFN82CxBLnl+lSWVdCCWbRY6y9yZQ=="], + "@types/node": ["@types/node@24.7.1", "", { "dependencies": { "undici-types": "~7.14.0" } }, "sha512-CmyhGZanP88uuC5GpWU9q+fI61j2SkhO3UGMUdfYRE6Bcy0ccyzn1Rqj9YAB/ZY4kOXmNf0ocah5GtphmLMP6Q=="], "@types/normalize-package-data": ["@types/normalize-package-data@2.4.4", "", {}, "sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA=="], @@ -1386,21 +1386,21 @@ "@vitest/coverage-v8": ["@vitest/coverage-v8@3.1.2", "", { "dependencies": { "@ampproject/remapping": "^2.3.0", "@bcoe/v8-coverage": "^1.0.2", "debug": "^4.4.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" }, "peerDependencies": { "@vitest/browser": "3.1.2", "vitest": "3.1.2" }, "optionalPeers": ["@vitest/browser"] }, "sha512-XDdaDOeaTMAMYW7N63AqoK32sYUWbXnTkC6tEbVcu3RlU1bB9of32T+PGf8KZvxqLNqeXhafDFqCkwpf2+dyaQ=="], - "@vitest/expect": ["@vitest/expect@1.6.1", "", { "dependencies": { "@vitest/spy": "1.6.1", "@vitest/utils": "1.6.1", "chai": "^4.3.10" } }, "sha512-jXL+9+ZNIJKruofqXuuTClf44eSpcHlgj3CiuNihUF3Ioujtmc0zIa3UJOW5RjDK1YLBJZnWBlPuqhYycLioog=="], + "@vitest/expect": ["@vitest/expect@3.2.4", "", { "dependencies": { "@types/chai": "^5.2.2", "@vitest/spy": "3.2.4", "@vitest/utils": "3.2.4", "chai": "^5.2.0", "tinyrainbow": "^2.0.0" } }, "sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig=="], "@vitest/mocker": ["@vitest/mocker@3.2.4", "", { "dependencies": { "@vitest/spy": "3.2.4", "estree-walker": "^3.0.3", "magic-string": "^0.30.17" }, "peerDependencies": { "msw": "^2.4.9", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" }, "optionalPeers": ["msw", "vite"] }, "sha512-46ryTE9RZO/rfDd7pEqFl7etuyzekzEhUbTW3BvmeO/BcCMEgq59BKhek3dXDWgAj4oMK6OZi+vRr1wPW6qjEQ=="], "@vitest/pretty-format": ["@vitest/pretty-format@3.2.4", "", { "dependencies": { "tinyrainbow": "^2.0.0" } }, "sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA=="], - "@vitest/runner": ["@vitest/runner@1.6.1", "", { "dependencies": { "@vitest/utils": "1.6.1", "p-limit": "^5.0.0", "pathe": "^1.1.1" } }, "sha512-3nSnYXkVkf3mXFfE7vVyPmi3Sazhb/2cfZGGs0JRzFsPFvAMBEcrweV1V1GsrstdXeKCTXlJbvnQwGWgEIHmOA=="], + "@vitest/runner": ["@vitest/runner@3.2.4", "", { "dependencies": { "@vitest/utils": "3.2.4", "pathe": "^2.0.3", "strip-literal": "^3.0.0" } }, "sha512-oukfKT9Mk41LreEW09vt45f8wx7DordoWUZMYdY/cyAk7w5TWkTRCNZYF7sX7n2wB7jyGAl74OxgwhPgKaqDMQ=="], - "@vitest/snapshot": ["@vitest/snapshot@1.6.1", "", { "dependencies": { "magic-string": "^0.30.5", "pathe": "^1.1.1", "pretty-format": "^29.7.0" } }, "sha512-WvidQuWAzU2p95u8GAKlRMqMyN1yOJkGHnx3M1PL9Raf7AQ1kwLKg04ADlCa3+OXUZE7BceOhVZiuWAbzCKcUQ=="], + "@vitest/snapshot": ["@vitest/snapshot@3.2.4", "", { "dependencies": { "@vitest/pretty-format": "3.2.4", "magic-string": "^0.30.17", "pathe": "^2.0.3" } }, "sha512-dEYtS7qQP2CjU27QBC5oUOxLE/v5eLkGqPE0ZKEIDGMs4vKWe7IjgLOeauHsR0D5YuuycGRO5oSRXnwnmA78fQ=="], - "@vitest/spy": ["@vitest/spy@1.6.1", "", { "dependencies": { "tinyspy": "^2.2.0" } }, "sha512-MGcMmpGkZebsMZhbQKkAf9CX5zGvjkBTqf8Zx3ApYWXr3wG+QvEu2eXWfnIIWYSJExIp4V9FCKDEeygzkYrXMw=="], + "@vitest/spy": ["@vitest/spy@3.2.4", "", { "dependencies": { "tinyspy": "^4.0.3" } }, "sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw=="], "@vitest/ui": ["@vitest/ui@3.2.4", "", { "dependencies": { "@vitest/utils": "3.2.4", "fflate": "^0.8.2", "flatted": "^3.3.3", "pathe": "^2.0.3", "sirv": "^3.0.1", "tinyglobby": "^0.2.14", "tinyrainbow": "^2.0.0" }, "peerDependencies": { "vitest": "3.2.4" } }, "sha512-hGISOaP18plkzbWEcP/QvtRW1xDXF2+96HbEX6byqQhAUbiS5oH6/9JwW+QsQCIYON2bI6QZBF+2PvOmrRZ9wA=="], - "@vitest/utils": ["@vitest/utils@1.6.1", "", { "dependencies": { "diff-sequences": "^29.6.3", "estree-walker": "^3.0.3", "loupe": "^2.3.7", "pretty-format": "^29.7.0" } }, "sha512-jOrrUvXM4Av9ZWiG1EajNto0u96kWAhJ1LmPmJhXXQx/32MecEKd10pOLYgS2BQx1TgkGhloPU1ArDW2vvaY6g=="], + "@vitest/utils": ["@vitest/utils@3.2.4", "", { "dependencies": { "@vitest/pretty-format": "3.2.4", "loupe": "^3.1.4", "tinyrainbow": "^2.0.0" } }, "sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA=="], "@vue/compiler-core": ["@vue/compiler-core@3.5.22", "", { "dependencies": { "@babel/parser": "^7.28.4", "@vue/shared": "3.5.22", "entities": "^4.5.0", "estree-walker": "^2.0.2", "source-map-js": "^1.2.1" } }, "sha512-jQ0pFPmZwTEiRNSb+i9Ow/I/cHv2tXYqsnHKKyCQ08irI2kdF5qmYedmF8si8mA7zepUFmJ2hqzS8CQmNOWOkQ=="], @@ -1488,7 +1488,7 @@ "asap": ["asap@2.0.6", "", {}, "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA=="], - "assertion-error": ["assertion-error@1.1.0", "", {}, "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw=="], + "assertion-error": ["assertion-error@2.0.1", "", {}, "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA=="], "ast-types": ["ast-types@0.15.2", "", { "dependencies": { "tslib": "^2.0.1" } }, "sha512-c27loCv9QkZinsa5ProX751khO9DJl/AcB5c2KNtA6NRvHKS0PgLfcftz72KVq504vB0Gku5s2kUZzDBvQWvHg=="], @@ -1542,8 +1542,6 @@ "big-integer": ["big-integer@1.6.52", "", {}, "sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg=="], - "binary-extensions": ["binary-extensions@2.3.0", "", {}, "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw=="], - "bindings": ["bindings@1.5.0", "", { "dependencies": { "file-uri-to-path": "1.0.0" } }, "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ=="], "birpc": ["birpc@2.6.1", "", {}, "sha512-LPnFhlDpdSH6FJhJyn4M0kFO7vtQ5iPw24FnG0y21q09xC7e8+1LeR31S1MAIrDAHp4m7aas4bEkTDTvMAtebQ=="], @@ -1594,7 +1592,7 @@ "ccount": ["ccount@2.0.1", "", {}, "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg=="], - "chai": ["chai@4.5.0", "", { "dependencies": { "assertion-error": "^1.1.0", "check-error": "^1.0.3", "deep-eql": "^4.1.3", "get-func-name": "^2.0.2", "loupe": "^2.3.6", "pathval": "^1.1.1", "type-detect": "^4.1.0" } }, "sha512-RITGBfijLkBddZvnn8jdqoTypxvqbOLYQkGGxXzeFjVHvudaPw0HNFD9x928/eUwYWd2dPCugVqspGALTZZQKw=="], + "chai": ["chai@5.3.3", "", { "dependencies": { "assertion-error": "^2.0.1", "check-error": "^2.1.1", "deep-eql": "^5.0.1", "loupe": "^3.1.0", "pathval": "^2.0.0" } }, "sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw=="], "chalk": ["chalk@5.6.2", "", {}, "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA=="], @@ -1604,7 +1602,7 @@ "character-entities-legacy": ["character-entities-legacy@3.0.0", "", {}, "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ=="], - "check-error": ["check-error@1.0.3", "", { "dependencies": { "get-func-name": "^2.0.2" } }, "sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg=="], + "check-error": ["check-error@2.1.1", "", {}, "sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw=="], "chevrotain": ["chevrotain@11.0.3", "", { "dependencies": { "@chevrotain/cst-dts-gen": "11.0.3", "@chevrotain/gast": "11.0.3", "@chevrotain/regexp-to-ast": "11.0.3", "@chevrotain/types": "11.0.3", "@chevrotain/utils": "11.0.3", "lodash-es": "4.17.21" } }, "sha512-ci2iJH6LeIkvP9eJW6gpueU8cnZhv85ELY8w8WiFtNjMHA5ad6pQLaJo9mEly/9qUyCpvqX8/POVUTf18/HFdw=="], @@ -1652,7 +1650,7 @@ "concat-map": ["concat-map@0.0.1", "", {}, "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="], - "confbox": ["confbox@0.1.8", "", {}, "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w=="], + "confbox": ["confbox@0.2.2", "", {}, "sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ=="], "connect": ["connect@3.7.0", "", { "dependencies": { "debug": "2.6.9", "finalhandler": "1.1.2", "parseurl": "~1.3.3", "utils-merge": "1.0.1" } }, "sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ=="], @@ -1786,7 +1784,7 @@ "dedent-js": ["dedent-js@1.0.1", "", {}, "sha512-OUepMozQULMLUmhxS95Vudo0jb0UchLimi3+pQ2plj61Fcy8axbP9hbiD4Sz6DPqn6XG3kfmziVfQ1rSys5AJQ=="], - "deep-eql": ["deep-eql@4.1.4", "", { "dependencies": { "type-detect": "^4.0.0" } }, "sha512-SUwdGfqdKOwxCPeVYjwSyRpJ7Z+fhpwIAtmCUdZIWZ/YP5R9WAsyuSgpLVDi9bjWoN2LXHNss/dk3urXtdQxGg=="], + "deep-eql": ["deep-eql@5.0.2", "", {}, "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q=="], "deep-equal": ["deep-equal@2.2.3", "", { "dependencies": { "array-buffer-byte-length": "^1.0.0", "call-bind": "^1.0.5", "es-get-iterator": "^1.1.3", "get-intrinsic": "^1.2.2", "is-arguments": "^1.1.1", "is-array-buffer": "^3.0.2", "is-date-object": "^1.0.5", "is-regex": "^1.1.4", "is-shared-array-buffer": "^1.0.2", "isarray": "^2.0.5", "object-is": "^1.1.5", "object-keys": "^1.1.1", "object.assign": "^4.1.4", "regexp.prototype.flags": "^1.5.1", "side-channel": "^1.0.4", "which-boxed-primitive": "^1.0.2", "which-collection": "^1.0.1", "which-typed-array": "^1.1.13" } }, "sha512-ZIwpnevOurS8bpT4192sqAowWM76JDKSHYzMLty3BZGSswgq6pBaH3DhCSW5xVAZICZyKdOBPjwww5wfgT/6PA=="], @@ -1946,7 +1944,7 @@ "exec-async": ["exec-async@2.2.0", "", {}, "sha512-87OpwcEiMia/DeiKFzaQNBNFeN3XkkpYIh9FyOqq5mS2oKv3CBE67PXoEKcr6nodWdXNogTiQ0jE2NGuoffXPw=="], - "execa": ["execa@8.0.1", "", { "dependencies": { "cross-spawn": "^7.0.3", "get-stream": "^8.0.1", "human-signals": "^5.0.0", "is-stream": "^3.0.0", "merge-stream": "^2.0.0", "npm-run-path": "^5.1.0", "onetime": "^6.0.0", "signal-exit": "^4.1.0", "strip-final-newline": "^3.0.0" } }, "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg=="], + "execa": ["execa@4.1.0", "", { "dependencies": { "cross-spawn": "^7.0.0", "get-stream": "^5.0.0", "human-signals": "^1.1.1", "is-stream": "^2.0.0", "merge-stream": "^2.0.0", "npm-run-path": "^4.0.0", "onetime": "^5.1.0", "signal-exit": "^3.0.2", "strip-final-newline": "^2.0.0" } }, "sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA=="], "expand-template": ["expand-template@2.0.3", "", {}, "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg=="], @@ -2064,15 +2062,13 @@ "get-east-asian-width": ["get-east-asian-width@1.4.0", "", {}, "sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q=="], - "get-func-name": ["get-func-name@2.0.2", "", {}, "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ=="], - "get-intrinsic": ["get-intrinsic@1.3.0", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "math-intrinsics": "^1.1.0" } }, "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ=="], "get-package-type": ["get-package-type@0.1.0", "", {}, "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q=="], "get-proto": ["get-proto@1.0.1", "", { "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" } }, "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g=="], - "get-stream": ["get-stream@8.0.1", "", {}, "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA=="], + "get-stream": ["get-stream@5.2.0", "", { "dependencies": { "pump": "^3.0.0" } }, "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA=="], "get-symbol-description": ["get-symbol-description@1.1.0", "", { "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6" } }, "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg=="], @@ -2140,7 +2136,7 @@ "https-proxy-agent": ["https-proxy-agent@7.0.6", "", { "dependencies": { "agent-base": "^7.1.2", "debug": "4" } }, "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw=="], - "human-signals": ["human-signals@5.0.0", "", {}, "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ=="], + "human-signals": ["human-signals@1.1.1", "", {}, "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw=="], "iconv-lite": ["iconv-lite@0.6.3", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw=="], @@ -2186,8 +2182,6 @@ "is-bigint": ["is-bigint@1.1.0", "", { "dependencies": { "has-bigints": "^1.0.2" } }, "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ=="], - "is-binary-path": ["is-binary-path@2.1.0", "", { "dependencies": { "binary-extensions": "^2.0.0" } }, "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw=="], - "is-boolean-object": ["is-boolean-object@1.2.2", "", { "dependencies": { "call-bound": "^1.0.3", "has-tostringtag": "^1.0.2" } }, "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A=="], "is-callable": ["is-callable@1.2.7", "", {}, "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA=="], @@ -2246,7 +2240,7 @@ "is-shared-array-buffer": ["is-shared-array-buffer@1.0.4", "", { "dependencies": { "call-bound": "^1.0.3" } }, "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A=="], - "is-stream": ["is-stream@3.0.0", "", {}, "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA=="], + "is-stream": ["is-stream@2.0.1", "", {}, "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg=="], "is-string": ["is-string@1.1.1", "", { "dependencies": { "call-bound": "^1.0.3", "has-tostringtag": "^1.0.2" } }, "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA=="], @@ -2318,7 +2312,7 @@ "js-base64": ["js-base64@3.7.8", "", {}, "sha512-hNngCeKxIUQiEUN3GPJOkz4wF/YvdUdbNL9hsBcMQTkKzboD7T/q3OYOuuPZLUE6dBxSGpwhk5mwuDud7JVAow=="], - "js-tokens": ["js-tokens@9.0.1", "", {}, "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ=="], + "js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="], "js-yaml": ["js-yaml@4.1.0", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA=="], @@ -2410,7 +2404,7 @@ "load-tsconfig": ["load-tsconfig@0.2.5", "", {}, "sha512-IXO6OCs9yg8tMKzfPZ1YmheJbZCiEsnBdcB03l0OcfK9prKnJb96siuHCr5Fl37/yo9DnKU+TLpxzTUspw9shg=="], - "local-pkg": ["local-pkg@0.5.1", "", { "dependencies": { "mlly": "^1.7.3", "pkg-types": "^1.2.1" } }, "sha512-9rrA30MRRP3gBD3HTGnC6cDFpaE1kVDWxWgqWJUN0RvDNAo+Nz/9GxB+nHOH0ifbVFy0hSA1V6vFDvnx54lTEQ=="], + "local-pkg": ["local-pkg@1.1.2", "", { "dependencies": { "mlly": "^1.7.4", "pkg-types": "^2.3.0", "quansync": "^0.2.11" } }, "sha512-arhlxbFRmoQHl33a0Zkle/YWlmNwoyt6QNZEIJcqNbdrsix5Lvc4HyyI3EnwxTYlZYc32EbYrQ8SzEZ7dqgg9A=="], "locate-character": ["locate-character@3.0.0", "", {}, "sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA=="], @@ -2434,7 +2428,7 @@ "loose-envify": ["loose-envify@1.4.0", "", { "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" }, "bin": { "loose-envify": "cli.js" } }, "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q=="], - "loupe": ["loupe@2.3.7", "", { "dependencies": { "get-func-name": "^2.0.1" } }, "sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA=="], + "loupe": ["loupe@3.2.1", "", {}, "sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ=="], "lru-cache": ["lru-cache@11.2.2", "", {}, "sha512-F9ODfyqML2coTIsQpSkRHnLSZMtkU8Q+mSfcaIyKwy58u+8k5nvAYeiNhsyMARvzNcXJ9QfWVrcPsC9e9rAxtg=="], @@ -2530,7 +2524,7 @@ "mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="], - "mimic-fn": ["mimic-fn@4.0.0", "", {}, "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw=="], + "mimic-fn": ["mimic-fn@2.1.0", "", {}, "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg=="], "mimic-function": ["mimic-function@5.0.1", "", {}, "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA=="], @@ -2612,12 +2606,10 @@ "npm-run-all": ["npm-run-all@4.1.5", "", { "dependencies": { "ansi-styles": "^3.2.1", "chalk": "^2.4.1", "cross-spawn": "^6.0.5", "memorystream": "^0.3.1", "minimatch": "^3.0.4", "pidtree": "^0.3.0", "read-pkg": "^3.0.0", "shell-quote": "^1.6.1", "string.prototype.padend": "^3.0.0" }, "bin": { "run-p": "bin/run-p/index.js", "run-s": "bin/run-s/index.js", "npm-run-all": "bin/npm-run-all/index.js" } }, "sha512-Oo82gJDAVcaMdi3nuoKFavkIHBRVqQ1qvMb+9LHk/cF4P6B2m8aP04hGf7oL6wZ9BuGwX1onlLhpuoofSyoQDQ=="], - "npm-run-path": ["npm-run-path@5.3.0", "", { "dependencies": { "path-key": "^4.0.0" } }, "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ=="], + "npm-run-path": ["npm-run-path@4.0.1", "", { "dependencies": { "path-key": "^3.0.0" } }, "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw=="], "nullthrows": ["nullthrows@1.1.1", "", {}, "sha512-2vPPEi+Z7WqML2jZYddDIfy5Dqb0r2fze2zTxNNknZaFpVHU3mFB3R+DWeJWGVx0ecvttSGlJTI+WG+8Z4cDWw=="], - "nwsapi": ["nwsapi@2.2.22", "", {}, "sha512-ujSMe1OWVn55euT1ihwCI1ZcAaAU3nxUiDwfDQldc51ZXaB9m2AyOn6/jh1BLe2t/G8xd6uKG1UBF2aZJeg2SQ=="], - "ob1": ["ob1@0.83.3", "", { "dependencies": { "flow-enums-runtime": "^0.0.6" } }, "sha512-egUxXCDwoWG06NGCS5s5AdcpnumHKJlfd3HH06P3m9TEMwwScfcY35wpQxbm9oHof+dM/lVH9Rfyu1elTVelSA=="], "object-assign": ["object-assign@4.1.1", "", {}, "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="], @@ -2636,7 +2628,7 @@ "once": ["once@1.4.0", "", { "dependencies": { "wrappy": "1" } }, "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w=="], - "onetime": ["onetime@6.0.0", "", { "dependencies": { "mimic-fn": "^4.0.0" } }, "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ=="], + "onetime": ["onetime@5.1.2", "", { "dependencies": { "mimic-fn": "^2.1.0" } }, "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg=="], "oniguruma-parser": ["oniguruma-parser@0.12.1", "", {}, "sha512-8Unqkvk1RYc6yq2WBYRj4hdnsAxVze8i7iPfQr8e4uSP3tRv0rpZcbGUDvxfQQcdwHt/e9PrMvGCsa8OqG9X3w=="], @@ -2654,7 +2646,7 @@ "own-keys": ["own-keys@1.0.1", "", { "dependencies": { "get-intrinsic": "^1.2.6", "object-keys": "^1.1.1", "safe-push-apply": "^1.0.0" } }, "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg=="], - "p-limit": ["p-limit@5.0.0", "", { "dependencies": { "yocto-queue": "^1.0.0" } }, "sha512-/Eaoq+QyLSiXQ4lyYV23f14mZRQcXnxfHrN0vCai+ak9G0pp9iEQukIIZq5NccEvwRB8PUnZT0KsOoDCINS1qQ=="], + "p-limit": ["p-limit@3.1.0", "", { "dependencies": { "yocto-queue": "^0.1.0" } }, "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ=="], "p-locate": ["p-locate@5.0.0", "", { "dependencies": { "p-limit": "^3.0.2" } }, "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw=="], @@ -2690,9 +2682,9 @@ "path-type": ["path-type@6.0.0", "", {}, "sha512-Vj7sf++t5pBD637NSfkxpHSMfWaeig5+DKWLhcqIYx6mWQz5hdJTGDVMQiJcw1ZYkhs7AazKDGpRVji1LJCZUQ=="], - "pathe": ["pathe@1.1.2", "", {}, "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ=="], + "pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="], - "pathval": ["pathval@1.1.1", "", {}, "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ=="], + "pathval": ["pathval@2.0.1", "", {}, "sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ=="], "perfect-debounce": ["perfect-debounce@1.0.0", "", {}, "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA=="], @@ -2708,7 +2700,7 @@ "pkg-dir": ["pkg-dir@3.0.0", "", { "dependencies": { "find-up": "^3.0.0" } }, "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw=="], - "pkg-types": ["pkg-types@1.3.1", "", { "dependencies": { "confbox": "^0.1.8", "mlly": "^1.7.4", "pathe": "^2.0.1" } }, "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ=="], + "pkg-types": ["pkg-types@2.3.0", "", { "dependencies": { "confbox": "^0.2.2", "exsolve": "^1.0.7", "pathe": "^2.0.3" } }, "sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig=="], "playwright": ["playwright@1.56.0", "", { "dependencies": { "playwright-core": "1.56.0" }, "optionalDependencies": { "fsevents": "2.3.2" }, "bin": { "playwright": "cli.js" } }, "sha512-X5Q1b8lOdWIE4KAoHpW3SE8HvUB+ZZsUoN64ZhjnN8dOb1UpujxBtENGiZFE+9F/yhzJwYa+ca3u43FeLbboHA=="], @@ -2822,7 +2814,7 @@ "rc": ["rc@1.2.8", "", { "dependencies": { "deep-extend": "^0.6.0", "ini": "~1.3.0", "minimist": "^1.2.0", "strip-json-comments": "~2.0.1" }, "bin": { "rc": "./cli.js" } }, "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw=="], - "react": ["react@18.3.1", "", { "dependencies": { "loose-envify": "^1.1.0" } }, "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ=="], + "react": ["react@19.0.0", "", {}, "sha512-V8AVnmPIICiWpGfm6GLzCR/W5FXLchHop40W4nXBmdlEceh16rCN8O8LNWm5bh5XUX91fh7KpA+W0TgMKmgTpQ=="], "react-devtools-core": ["react-devtools-core@6.1.5", "", { "dependencies": { "shell-quote": "^1.6.1", "ws": "^7" } }, "sha512-ePrwPfxAnB+7hgnEr8vpKxL9cmnp7F322t8oqcPshbIQQhDKgFDW4tjhF2wjVbdXF9O/nyuy3sQWd9JGpiLPvA=="], @@ -3072,13 +3064,13 @@ "strip-bom": ["strip-bom@4.0.0", "", {}, "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w=="], - "strip-final-newline": ["strip-final-newline@3.0.0", "", {}, "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw=="], + "strip-final-newline": ["strip-final-newline@2.0.0", "", {}, "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA=="], "strip-indent": ["strip-indent@3.0.0", "", { "dependencies": { "min-indent": "^1.0.0" } }, "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ=="], "strip-json-comments": ["strip-json-comments@3.1.1", "", {}, "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig=="], - "strip-literal": ["strip-literal@2.1.1", "", { "dependencies": { "js-tokens": "^9.0.1" } }, "sha512-631UJ6O00eNGfMiWG78ck80dfBab8X6IVFB51jZK5Icd7XAs60Z5y7QdSd/wGIklnWvRbUNloVzhOKKmutxQ6Q=="], + "strip-literal": ["strip-literal@3.1.0", "", { "dependencies": { "js-tokens": "^9.0.1" } }, "sha512-8r3mkIM/2+PpjHoOtiAW8Rg3jJLHaV7xPwG+YRGrv6FP0wwk/toTpATxWYOW0BKdWwl82VT2tFYi5DlROa0Mxg=="], "structured-headers": ["structured-headers@0.4.1", "", {}, "sha512-0MP/Cxx5SzeeZ10p/bZI0S6MpgD+yxAhi1BOQ34jgnMXsCq3j1t6tQnZu+KdlL7dvJTLT3g9xN8tl10TqgFMcg=="], @@ -3144,11 +3136,11 @@ "tinyglobby": ["tinyglobby@0.2.15", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" } }, "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ=="], - "tinypool": ["tinypool@0.8.4", "", {}, "sha512-i11VH5gS6IFeLY3gMBQ00/MmLncVP7JLXOw1vlgkytLmJK7QnEr7NXf0LBdxfmNPAeyetukOk0bOYrJrFGjYJQ=="], + "tinypool": ["tinypool@1.1.1", "", {}, "sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg=="], "tinyrainbow": ["tinyrainbow@2.0.0", "", {}, "sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw=="], - "tinyspy": ["tinyspy@2.2.1", "", {}, "sha512-KYad6Vy5VDWV4GH3fjpseMQ/XU2BhIYP7Vzd0LG44qRWm/Yt2WCOTicFdvmgo6gWaqooMQCawTtILVQJupKu7A=="], + "tinyspy": ["tinyspy@4.0.4", "", {}, "sha512-azl+t0z7pw/z958Gy9svOTuzqIk6xq+NSheJzn5MMWtWTFywIacg2wUlzKFGtt3cthx0r2SxMK0yzJOR0IES7Q=="], "tiptap-markdown": ["tiptap-markdown@0.8.10", "", { "dependencies": { "@types/markdown-it": "^13.0.7", "markdown-it": "^14.1.0", "markdown-it-task-lists": "^2.1.1", "prosemirror-markdown": "^1.11.1" }, "peerDependencies": { "@tiptap/core": "^2.0.3" } }, "sha512-iDVkR2BjAqkTDtFX0h94yVvE2AihCXlF0Q7RIXSJPRSR5I0PA1TMuAg6FHFpmqTn4tPxJ0by0CK7PUMlnFLGEQ=="], @@ -3214,7 +3206,7 @@ "type-check": ["type-check@0.4.0", "", { "dependencies": { "prelude-ls": "^1.2.1" } }, "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew=="], - "type-detect": ["type-detect@4.1.0", "", {}, "sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw=="], + "type-detect": ["type-detect@4.0.8", "", {}, "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g=="], "type-fest": ["type-fest@0.7.1", "", {}, "sha512-Ne2YiiGN8bmrmJJEuTWTLJR32nh/JdL1+PSicowtNb0WFpn59GK8/lfD61bVtzguz7b3PBt74nxpv/Pw5po5Rg=="], @@ -3250,7 +3242,7 @@ "undici": ["undici@6.22.0", "", {}, "sha512-hU/10obOIu62MGYjdskASR3CUAiYaFTtC9Pa6vHyf//mAipSvSQg6od2CnJswq7fvzNS3zJhxoRkgNVaHurWKw=="], - "undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], + "undici-types": ["undici-types@7.14.0", "", {}, "sha512-QQiYxHuyZ9gQUIrmPo3IA+hUl4KYk8uSA7cHrcKd/l3p1OTpZcM0Tbp9x7FAtXdAYhlasd60ncPpgu6ihG6TOA=="], "unicode-canonical-property-names-ecmascript": ["unicode-canonical-property-names-ecmascript@2.0.1", "", {}, "sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg=="], @@ -3302,9 +3294,9 @@ "vfile-message": ["vfile-message@4.0.3", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-stringify-position": "^4.0.0" } }, "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw=="], - "vite": ["vite@4.5.14", "", { "dependencies": { "esbuild": "^0.18.10", "postcss": "^8.4.27", "rollup": "^3.27.1" }, "optionalDependencies": { "fsevents": "~2.3.2" }, "peerDependencies": { "@types/node": ">= 14", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "stylus": "*", "sugarss": "*", "terser": "^5.4.0" }, "optionalPeers": ["@types/node", "less", "lightningcss", "sass", "stylus", "sugarss", "terser"], "bin": { "vite": "bin/vite.js" } }, "sha512-+v57oAaoYNnO3hIu5Z/tJRZjq5aHM2zDve9YZ8HngVHbhk66RStobhb1sqPMIPEleV6cNKYK4eGrAbE9Ulbl2g=="], + "vite": ["vite@7.1.9", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.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", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-4nVGliEpxmhCL8DslSAUdxlB6+SMrhB0a1v5ijlh1xB1nEPuy1mxaHxysVucLHuWryAxLWg6a5ei+U4TLn/rFg=="], - "vite-node": ["vite-node@1.6.1", "", { "dependencies": { "cac": "^6.7.14", "debug": "^4.3.4", "pathe": "^1.1.1", "picocolors": "^1.0.0", "vite": "^5.0.0" }, "bin": { "vite-node": "vite-node.mjs" } }, "sha512-YAXkfvGtuTzwWbDSACdJSg4A4DZiAqckWe90Zapc/sEX3XvHcw1NdurM/6od8J207tSDqNbSsgdCacBgvJKFuA=="], + "vite-node": ["vite-node@3.2.4", "", { "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" } }, "sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg=="], "vitefu": ["vitefu@1.1.1", "", { "peerDependencies": { "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0-beta.0" }, "optionalPeers": ["vite"] }, "sha512-B/Fegf3i8zh0yFbpzZ21amWzHmuNlLlmJT6n7bu5e+pCHUKQIfXSYokrqOBGEMMe9UG2sostKQF9mml/vYaWJQ=="], @@ -3312,7 +3304,7 @@ "vitepress-plugin-mermaid": ["vitepress-plugin-mermaid@2.0.17", "", { "optionalDependencies": { "@mermaid-js/mermaid-mindmap": "^9.3.0" }, "peerDependencies": { "mermaid": "10 || 11", "vitepress": "^1.0.0 || ^1.0.0-alpha" } }, "sha512-IUzYpwf61GC6k0XzfmAmNrLvMi9TRrVRMsUyCA8KNXhg/mQ1VqWnO0/tBVPiX5UoKF1mDUwqn5QV4qAJl6JnUg=="], - "vitest": ["vitest@1.6.1", "", { "dependencies": { "@vitest/expect": "1.6.1", "@vitest/runner": "1.6.1", "@vitest/snapshot": "1.6.1", "@vitest/spy": "1.6.1", "@vitest/utils": "1.6.1", "acorn-walk": "^8.3.2", "chai": "^4.3.10", "debug": "^4.3.4", "execa": "^8.0.1", "local-pkg": "^0.5.0", "magic-string": "^0.30.5", "pathe": "^1.1.1", "picocolors": "^1.0.0", "std-env": "^3.5.0", "strip-literal": "^2.0.0", "tinybench": "^2.5.1", "tinypool": "^0.8.3", "vite": "^5.0.0", "vite-node": "1.6.1", "why-is-node-running": "^2.2.2" }, "peerDependencies": { "@edge-runtime/vm": "*", "@types/node": "^18.0.0 || >=20.0.0", "@vitest/browser": "1.6.1", "@vitest/ui": "1.6.1", "happy-dom": "*", "jsdom": "*" }, "optionalPeers": ["@edge-runtime/vm", "@types/node", "@vitest/browser", "@vitest/ui", "happy-dom", "jsdom"], "bin": { "vitest": "vitest.mjs" } }, "sha512-Ljb1cnSJSivGN0LqXd/zmDbWEM0RNNg2t1QW/XUhYl/qPqyu7CsqeWtqQXHVaJsecLPuDoak2oJcZN2QoRIOag=="], + "vitest": ["vitest@3.2.4", "", { "dependencies": { "@types/chai": "^5.2.2", "@vitest/expect": "3.2.4", "@vitest/mocker": "3.2.4", "@vitest/pretty-format": "^3.2.4", "@vitest/runner": "3.2.4", "@vitest/snapshot": "3.2.4", "@vitest/spy": "3.2.4", "@vitest/utils": "3.2.4", "chai": "^5.2.0", "debug": "^4.4.1", "expect-type": "^1.2.1", "magic-string": "^0.30.17", "pathe": "^2.0.3", "picomatch": "^4.0.2", "std-env": "^3.9.0", "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", "why-is-node-running": "^2.3.0" }, "peerDependencies": { "@edge-runtime/vm": "*", "@types/debug": "^4.1.12", "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "@vitest/browser": "3.2.4", "@vitest/ui": "3.2.4", "happy-dom": "*", "jsdom": "*" }, "optionalPeers": ["@edge-runtime/vm", "@types/debug", "@types/node", "@vitest/browser", "@vitest/ui", "happy-dom", "jsdom"], "bin": { "vitest": "vitest.mjs" } }, "sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A=="], "vitest-browser-svelte": ["vitest-browser-svelte@1.1.0", "", { "peerDependencies": { "@vitest/browser": "^2.1.0 || ^3.0.0 || ^4.0.0-0", "svelte": "^3 || ^4 || ^5 || ^5.0.0-next.0", "vitest": "^2.1.0 || ^3.0.0 || ^4.0.0-0" } }, "sha512-o98mCzKkWBjvmaGzi69rvyBd1IJ7zFPGI0jcID9vI4F5DmdG//YxkIbeQ7TS27hAVR+MULnBZNja2DUiuUBZyA=="], @@ -3410,7 +3402,7 @@ "yn": ["yn@3.1.1", "", {}, "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q=="], - "yocto-queue": ["yocto-queue@1.2.1", "", {}, "sha512-AyeEbWOu/TAXdxlV9wmGcR0+yh2j3vYPGOECcIj2S7MkrLyC7ne+oye2BKTItt0ii2PHk4cDy+95+LshzbXnGg=="], + "yocto-queue": ["yocto-queue@0.1.0", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="], "zimmerframe": ["zimmerframe@1.1.4", "", {}, "sha512-B58NGBEoc8Y9MWWCQGl/gq9xBCe4IiKM0a2x7GZdQKOW5Exr8S1W24J6OgM1njK8xCRGvAJIL/MxXHf6SkmQKQ=="], @@ -3420,8 +3412,6 @@ "@antfu/install-pkg/tinyexec": ["tinyexec@1.0.1", "", {}, "sha512-5uC6DDlmeqiOwCPmK9jMSdOuZTh8bU39Ys6yidB+UTt5hfZUPGAypSgFRiEp+jbi9qH40BLDvy85jIU88wKSqw=="], - "@babel/code-frame/js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="], - "@babel/core/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], "@babel/helper-compilation-targets/lru-cache": ["lru-cache@5.1.1", "", { "dependencies": { "yallist": "^3.0.2" } }, "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w=="], @@ -3434,8 +3424,6 @@ "@babel/highlight/chalk": ["chalk@2.4.2", "", { "dependencies": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", "supports-color": "^5.3.0" } }, "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ=="], - "@babel/highlight/js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="], - "@babel/plugin-transform-runtime/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], "@babel/preset-env/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], @@ -3450,6 +3438,8 @@ "@cspotcode/source-map-support/@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.9", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.0.3", "@jridgewell/sourcemap-codec": "^1.4.10" } }, "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ=="], + "@docsearch/react/react": ["react@18.3.1", "", { "dependencies": { "loose-envify": "^1.1.0" } }, "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ=="], + "@eslint-community/eslint-utils/eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="], "@eslint/config-array/minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="], @@ -3482,8 +3472,6 @@ "@expo/fingerprint/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], - "@expo/fingerprint/p-limit": ["p-limit@3.1.0", "", { "dependencies": { "yocto-queue": "^0.1.0" } }, "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ=="], - "@expo/image-utils/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], "@expo/json-file/@babel/code-frame": ["@babel/code-frame@7.10.4", "", { "dependencies": { "@babel/highlight": "^7.10.4" } }, "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg=="], @@ -3510,8 +3498,6 @@ "@iconify/utils/globals": ["globals@15.15.0", "", {}, "sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg=="], - "@iconify/utils/local-pkg": ["local-pkg@1.1.2", "", { "dependencies": { "mlly": "^1.7.4", "pkg-types": "^2.3.0", "quansync": "^0.2.11" } }, "sha512-arhlxbFRmoQHl33a0Zkle/YWlmNwoyt6QNZEIJcqNbdrsix5Lvc4HyyI3EnwxTYlZYc32EbYrQ8SzEZ7dqgg9A=="], - "@isaacs/cliui/string-width": ["string-width@5.1.2", "", { "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", "strip-ansi": "^7.0.1" } }, "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA=="], "@isaacs/cliui/strip-ansi": ["strip-ansi@7.1.2", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA=="], @@ -3524,48 +3510,14 @@ "@istanbuljs/load-nyc-config/js-yaml": ["js-yaml@3.14.1", "", { "dependencies": { "argparse": "^1.0.7", "esprima": "^4.0.0" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g=="], - "@jest/environment/@types/node": ["@types/node@24.7.1", "", { "dependencies": { "undici-types": "~7.14.0" } }, "sha512-CmyhGZanP88uuC5GpWU9q+fI61j2SkhO3UGMUdfYRE6Bcy0ccyzn1Rqj9YAB/ZY4kOXmNf0ocah5GtphmLMP6Q=="], - - "@jest/fake-timers/@types/node": ["@types/node@24.7.1", "", { "dependencies": { "undici-types": "~7.14.0" } }, "sha512-CmyhGZanP88uuC5GpWU9q+fI61j2SkhO3UGMUdfYRE6Bcy0ccyzn1Rqj9YAB/ZY4kOXmNf0ocah5GtphmLMP6Q=="], - "@jest/transform/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], "@jest/transform/slash": ["slash@3.0.0", "", {}, "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q=="], - "@jest/types/@types/node": ["@types/node@24.7.1", "", { "dependencies": { "undici-types": "~7.14.0" } }, "sha512-CmyhGZanP88uuC5GpWU9q+fI61j2SkhO3UGMUdfYRE6Bcy0ccyzn1Rqj9YAB/ZY4kOXmNf0ocah5GtphmLMP6Q=="], - "@jest/types/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], "@mermaid-js/mermaid-mindmap/@braintree/sanitize-url": ["@braintree/sanitize-url@6.0.4", "", {}, "sha512-s3jaWicZd0pkP0jf5ysyHUI/RE7MHos6qlToFcGWXVp+ykHOy77OUMrfbgJ9it2C5bow7OIQwYYaHjk9XlBQ2A=="], - "@nostr-dev-kit/cache-dexie/@types/node": ["@types/node@22.18.9", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-5yBtK0k/q8PjkMXbTfeIEP/XVYnz1R9qZJ3yUicdEW7ppdDJfe+MqXEhpqDL3mtn4Wvs1u0KLEG0RXzCgNpsSg=="], - - "@nostr-dev-kit/cache-memory/@types/node": ["@types/node@22.18.9", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-5yBtK0k/q8PjkMXbTfeIEP/XVYnz1R9qZJ3yUicdEW7ppdDJfe+MqXEhpqDL3mtn4Wvs1u0KLEG0RXzCgNpsSg=="], - - "@nostr-dev-kit/cache-nostr/@types/node": ["@types/node@18.19.130", "", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-GRaXQx6jGfL8sKfaIDD6OupbIHBr9jv7Jnaml9tB7l4v068PAOXqfcujMMo5PhbIs6ggR1XODELqahT2R8v0fg=="], - - "@nostr-dev-kit/cache-nostr/tsup": ["tsup@7.3.0", "", { "dependencies": { "bundle-require": "^4.0.0", "cac": "^6.7.12", "chokidar": "^3.5.1", "debug": "^4.3.1", "esbuild": "^0.19.2", "execa": "^5.0.0", "globby": "^11.0.3", "joycon": "^3.0.1", "postcss-load-config": "^4.0.1", "resolve-from": "^5.0.0", "rollup": "^4.0.2", "source-map": "0.8.0-beta.0", "sucrase": "^3.20.3", "tree-kill": "^1.2.2" }, "peerDependencies": { "@swc/core": "^1", "postcss": "^8.4.12", "typescript": ">=4.5.0" }, "optionalPeers": ["@swc/core", "postcss", "typescript"], "bin": { "tsup": "dist/cli-default.js", "tsup-node": "dist/cli-node.js" } }, "sha512-Ja1eaSRrE+QarmATlNO5fse2aOACYMBX+IZRKy1T+gpyH+jXgRrl5l4nHIQJQ1DoDgEjHDTw8cpE085UdBZuWQ=="], - - "@nostr-dev-kit/cache-sqlite-wasm/@types/node": ["@types/node@24.7.1", "", { "dependencies": { "undici-types": "~7.14.0" } }, "sha512-CmyhGZanP88uuC5GpWU9q+fI61j2SkhO3UGMUdfYRE6Bcy0ccyzn1Rqj9YAB/ZY4kOXmNf0ocah5GtphmLMP6Q=="], - - "@nostr-dev-kit/cache-sqlite-wasm/vitest": ["vitest@3.2.4", "", { "dependencies": { "@types/chai": "^5.2.2", "@vitest/expect": "3.2.4", "@vitest/mocker": "3.2.4", "@vitest/pretty-format": "^3.2.4", "@vitest/runner": "3.2.4", "@vitest/snapshot": "3.2.4", "@vitest/spy": "3.2.4", "@vitest/utils": "3.2.4", "chai": "^5.2.0", "debug": "^4.4.1", "expect-type": "^1.2.1", "magic-string": "^0.30.17", "pathe": "^2.0.3", "picomatch": "^4.0.2", "std-env": "^3.9.0", "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", "why-is-node-running": "^2.3.0" }, "peerDependencies": { "@edge-runtime/vm": "*", "@types/debug": "^4.1.12", "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "@vitest/browser": "3.2.4", "@vitest/ui": "3.2.4", "happy-dom": "*", "jsdom": "*" }, "optionalPeers": ["@edge-runtime/vm", "@types/debug", "@types/node", "@vitest/browser", "@vitest/ui", "happy-dom", "jsdom"], "bin": { "vitest": "vitest.mjs" } }, "sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A=="], - - "@nostr-dev-kit/ndk/@types/node": ["@types/node@22.18.9", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-5yBtK0k/q8PjkMXbTfeIEP/XVYnz1R9qZJ3yUicdEW7ppdDJfe+MqXEhpqDL3mtn4Wvs1u0KLEG0RXzCgNpsSg=="], - - "@nostr-dev-kit/ndk/nostr-tools": ["nostr-tools@2.7.1", "", { "dependencies": { "@noble/ciphers": "^0.5.1", "@noble/curves": "1.2.0", "@noble/hashes": "1.3.1", "@scure/base": "1.1.1", "@scure/bip32": "1.3.1", "@scure/bip39": "1.2.1" }, "optionalDependencies": { "nostr-wasm": "v0.1.0" }, "peerDependencies": { "typescript": ">=5.0.0" }, "optionalPeers": ["typescript"] }, "sha512-4qAvlHSqBAA8lQMwRWE6dalSNdQT77Xut9lPiJZgEcb9RAlR69wR2+KVBAgnZVaabVYH7FJ7gOQXLw/jQBAYBg=="], - - "@nostr-dev-kit/ndk/vitest": ["vitest@3.2.4", "", { "dependencies": { "@types/chai": "^5.2.2", "@vitest/expect": "3.2.4", "@vitest/mocker": "3.2.4", "@vitest/pretty-format": "^3.2.4", "@vitest/runner": "3.2.4", "@vitest/snapshot": "3.2.4", "@vitest/spy": "3.2.4", "@vitest/utils": "3.2.4", "chai": "^5.2.0", "debug": "^4.4.1", "expect-type": "^1.2.1", "magic-string": "^0.30.17", "pathe": "^2.0.3", "picomatch": "^4.0.2", "std-env": "^3.9.0", "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", "why-is-node-running": "^2.3.0" }, "peerDependencies": { "@edge-runtime/vm": "*", "@types/debug": "^4.1.12", "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "@vitest/browser": "3.2.4", "@vitest/ui": "3.2.4", "happy-dom": "*", "jsdom": "*" }, "optionalPeers": ["@edge-runtime/vm", "@types/debug", "@types/node", "@vitest/browser", "@vitest/ui", "happy-dom", "jsdom"], "bin": { "vitest": "vitest.mjs" } }, "sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A=="], - - "@nostr-dev-kit/react/esbuild": ["esbuild@0.17.19", "", { "optionalDependencies": { "@esbuild/android-arm": "0.17.19", "@esbuild/android-arm64": "0.17.19", "@esbuild/android-x64": "0.17.19", "@esbuild/darwin-arm64": "0.17.19", "@esbuild/darwin-x64": "0.17.19", "@esbuild/freebsd-arm64": "0.17.19", "@esbuild/freebsd-x64": "0.17.19", "@esbuild/linux-arm": "0.17.19", "@esbuild/linux-arm64": "0.17.19", "@esbuild/linux-ia32": "0.17.19", "@esbuild/linux-loong64": "0.17.19", "@esbuild/linux-mips64el": "0.17.19", "@esbuild/linux-ppc64": "0.17.19", "@esbuild/linux-riscv64": "0.17.19", "@esbuild/linux-s390x": "0.17.19", "@esbuild/linux-x64": "0.17.19", "@esbuild/netbsd-x64": "0.17.19", "@esbuild/openbsd-x64": "0.17.19", "@esbuild/sunos-x64": "0.17.19", "@esbuild/win32-arm64": "0.17.19", "@esbuild/win32-ia32": "0.17.19", "@esbuild/win32-x64": "0.17.19" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-XQ0jAPFkK/u3LcVRcvVHQcTIqD6E2H1fvZMA5dQPSOWb3suUbWbfbRf94pjc0bNzRYLfIrDRQXr7X+LHIm5oHw=="], - - "@nostr-dev-kit/react/jsdom": ["jsdom@26.1.0", "", { "dependencies": { "cssstyle": "^4.2.1", "data-urls": "^5.0.0", "decimal.js": "^10.5.0", "html-encoding-sniffer": "^4.0.0", "http-proxy-agent": "^7.0.2", "https-proxy-agent": "^7.0.6", "is-potential-custom-element-name": "^1.0.1", "nwsapi": "^2.2.16", "parse5": "^7.2.1", "rrweb-cssom": "^0.8.0", "saxes": "^6.0.0", "symbol-tree": "^3.2.4", "tough-cookie": "^5.1.1", "w3c-xmlserializer": "^5.0.0", "webidl-conversions": "^7.0.0", "whatwg-encoding": "^3.1.1", "whatwg-mimetype": "^4.0.0", "whatwg-url": "^14.1.1", "ws": "^8.18.0", "xml-name-validator": "^5.0.0" }, "peerDependencies": { "canvas": "^3.0.0" }, "optionalPeers": ["canvas"] }, "sha512-Cvc9WUhxSMEo4McES3P7oK3QaXldCfNWp7pl2NNeiIFlCoLr3kfq9kb1fxftiwk1FLV7CvpvDfonxtzUDeSOPg=="], - - "@nostr-dev-kit/svelte/@types/node": ["@types/node@22.18.9", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-5yBtK0k/q8PjkMXbTfeIEP/XVYnz1R9qZJ3yUicdEW7ppdDJfe+MqXEhpqDL3mtn4Wvs1u0KLEG0RXzCgNpsSg=="], - - "@nostr-dev-kit/svelte/vite": ["vite@7.1.9", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.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", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-4nVGliEpxmhCL8DslSAUdxlB6+SMrhB0a1v5ijlh1xB1nEPuy1mxaHxysVucLHuWryAxLWg6a5ei+U4TLn/rFg=="], - - "@nostr-dev-kit/svelte/vitest": ["vitest@3.2.4", "", { "dependencies": { "@types/chai": "^5.2.2", "@vitest/expect": "3.2.4", "@vitest/mocker": "3.2.4", "@vitest/pretty-format": "^3.2.4", "@vitest/runner": "3.2.4", "@vitest/snapshot": "3.2.4", "@vitest/spy": "3.2.4", "@vitest/utils": "3.2.4", "chai": "^5.2.0", "debug": "^4.4.1", "expect-type": "^1.2.1", "magic-string": "^0.30.17", "pathe": "^2.0.3", "picomatch": "^4.0.2", "std-env": "^3.9.0", "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", "why-is-node-running": "^2.3.0" }, "peerDependencies": { "@edge-runtime/vm": "*", "@types/debug": "^4.1.12", "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "@vitest/browser": "3.2.4", "@vitest/ui": "3.2.4", "happy-dom": "*", "jsdom": "*" }, "optionalPeers": ["@edge-runtime/vm", "@types/debug", "@types/node", "@vitest/browser", "@vitest/ui", "happy-dom", "jsdom"], "bin": { "vitest": "vitest.mjs" } }, "sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A=="], - "@react-native/babel-plugin-codegen/@react-native/codegen": ["@react-native/codegen@0.76.9", "", { "dependencies": { "@babel/parser": "^7.25.3", "glob": "^7.1.1", "hermes-parser": "0.23.1", "invariant": "^2.2.4", "jscodeshift": "^0.14.0", "mkdirp": "^0.5.1", "nullthrows": "^1.1.1", "yargs": "^17.6.2" }, "peerDependencies": { "@babel/preset-env": "^7.1.6" } }, "sha512-AzlCHMTKrAVC2709V4ZGtBXmGVtWTpWm3Ruv5vXcd3/anH4mGucfJ4rjbWKdaYQJMpXa3ytGomQrsIsT/s8kgA=="], "@react-native/babel-preset/babel-plugin-syntax-hermes-parser": ["babel-plugin-syntax-hermes-parser@0.25.1", "", { "dependencies": { "hermes-parser": "0.25.1" } }, "sha512-IVNpGzboFLfXZUAwkLFcI/bnqVbwky0jP3eBno4HKtqvQJAHBLdgxiG6lQ4to0+Q/YCN3PO0od5NZwIKyY4REQ=="], @@ -3602,27 +3554,13 @@ "@shikijs/themes/@shikijs/types": ["@shikijs/types@3.13.0", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-oM9P+NCFri/mmQ8LoFGVfVyemm5Hi27330zuOBp0annwJdKH1kOLndw3zCtAVDehPLg9fKqoEx3Ht/wNZxolfw=="], - "@sinonjs/commons/type-detect": ["type-detect@4.0.8", "", {}, "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g=="], - - "@sveltejs/kit/vite": ["vite@7.1.9", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.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", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-4nVGliEpxmhCL8DslSAUdxlB6+SMrhB0a1v5ijlh1xB1nEPuy1mxaHxysVucLHuWryAxLWg6a5ei+U4TLn/rFg=="], - - "@sveltejs/vite-plugin-svelte/vite": ["vite@7.1.9", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.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", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-4nVGliEpxmhCL8DslSAUdxlB6+SMrhB0a1v5ijlh1xB1nEPuy1mxaHxysVucLHuWryAxLWg6a5ei+U4TLn/rFg=="], - - "@sveltejs/vite-plugin-svelte-inspector/vite": ["vite@7.1.9", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.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", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-4nVGliEpxmhCL8DslSAUdxlB6+SMrhB0a1v5ijlh1xB1nEPuy1mxaHxysVucLHuWryAxLWg6a5ei+U4TLn/rFg=="], - "@testing-library/dom/aria-query": ["aria-query@5.1.3", "", { "dependencies": { "deep-equal": "^2.0.5" } }, "sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ=="], "@testing-library/dom/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], "@testing-library/dom/pretty-format": ["pretty-format@27.5.1", "", { "dependencies": { "ansi-regex": "^5.0.1", "ansi-styles": "^5.0.0", "react-is": "^17.0.1" } }, "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ=="], - "@types/better-sqlite3/@types/node": ["@types/node@24.7.1", "", { "dependencies": { "undici-types": "~7.14.0" } }, "sha512-CmyhGZanP88uuC5GpWU9q+fI61j2SkhO3UGMUdfYRE6Bcy0ccyzn1Rqj9YAB/ZY4kOXmNf0ocah5GtphmLMP6Q=="], - - "@types/graceful-fs/@types/node": ["@types/node@24.7.1", "", { "dependencies": { "undici-types": "~7.14.0" } }, "sha512-CmyhGZanP88uuC5GpWU9q+fI61j2SkhO3UGMUdfYRE6Bcy0ccyzn1Rqj9YAB/ZY4kOXmNf0ocah5GtphmLMP6Q=="], - - "@types/qrcode/@types/node": ["@types/node@24.7.1", "", { "dependencies": { "undici-types": "~7.14.0" } }, "sha512-CmyhGZanP88uuC5GpWU9q+fI61j2SkhO3UGMUdfYRE6Bcy0ccyzn1Rqj9YAB/ZY4kOXmNf0ocah5GtphmLMP6Q=="], - - "@types/sql.js/@types/node": ["@types/node@24.7.1", "", { "dependencies": { "undici-types": "~7.14.0" } }, "sha512-CmyhGZanP88uuC5GpWU9q+fI61j2SkhO3UGMUdfYRE6Bcy0ccyzn1Rqj9YAB/ZY4kOXmNf0ocah5GtphmLMP6Q=="], + "@testing-library/react/react": ["react@18.3.1", "", { "dependencies": { "loose-envify": "^1.1.0" } }, "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ=="], "@typescript-eslint/eslint-plugin/ignore": ["ignore@7.0.5", "", {}, "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg=="], @@ -3630,20 +3568,6 @@ "@vitest/browser/@testing-library/dom": ["@testing-library/dom@10.4.1", "", { "dependencies": { "@babel/code-frame": "^7.10.4", "@babel/runtime": "^7.12.5", "@types/aria-query": "^5.0.1", "aria-query": "5.3.0", "dom-accessibility-api": "^0.5.9", "lz-string": "^1.5.0", "picocolors": "1.1.1", "pretty-format": "^27.0.2" } }, "sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg=="], - "@vitest/browser/@vitest/utils": ["@vitest/utils@3.2.4", "", { "dependencies": { "@vitest/pretty-format": "3.2.4", "loupe": "^3.1.4", "tinyrainbow": "^2.0.0" } }, "sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA=="], - - "@vitest/browser/vitest": ["vitest@3.2.4", "", { "dependencies": { "@types/chai": "^5.2.2", "@vitest/expect": "3.2.4", "@vitest/mocker": "3.2.4", "@vitest/pretty-format": "^3.2.4", "@vitest/runner": "3.2.4", "@vitest/snapshot": "3.2.4", "@vitest/spy": "3.2.4", "@vitest/utils": "3.2.4", "chai": "^5.2.0", "debug": "^4.4.1", "expect-type": "^1.2.1", "magic-string": "^0.30.17", "pathe": "^2.0.3", "picomatch": "^4.0.2", "std-env": "^3.9.0", "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", "why-is-node-running": "^2.3.0" }, "peerDependencies": { "@edge-runtime/vm": "*", "@types/debug": "^4.1.12", "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "@vitest/browser": "3.2.4", "@vitest/ui": "3.2.4", "happy-dom": "*", "jsdom": "*" }, "optionalPeers": ["@edge-runtime/vm", "@types/debug", "@types/node", "@vitest/browser", "@vitest/ui", "happy-dom", "jsdom"], "bin": { "vitest": "vitest.mjs" } }, "sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A=="], - - "@vitest/coverage-v8/vitest": ["vitest@3.2.4", "", { "dependencies": { "@types/chai": "^5.2.2", "@vitest/expect": "3.2.4", "@vitest/mocker": "3.2.4", "@vitest/pretty-format": "^3.2.4", "@vitest/runner": "3.2.4", "@vitest/snapshot": "3.2.4", "@vitest/spy": "3.2.4", "@vitest/utils": "3.2.4", "chai": "^5.2.0", "debug": "^4.4.1", "expect-type": "^1.2.1", "magic-string": "^0.30.17", "pathe": "^2.0.3", "picomatch": "^4.0.2", "std-env": "^3.9.0", "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", "why-is-node-running": "^2.3.0" }, "peerDependencies": { "@edge-runtime/vm": "*", "@types/debug": "^4.1.12", "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "@vitest/browser": "3.2.4", "@vitest/ui": "3.2.4", "happy-dom": "*", "jsdom": "*" }, "optionalPeers": ["@edge-runtime/vm", "@types/debug", "@types/node", "@vitest/browser", "@vitest/ui", "happy-dom", "jsdom"], "bin": { "vitest": "vitest.mjs" } }, "sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A=="], - - "@vitest/mocker/@vitest/spy": ["@vitest/spy@3.2.4", "", { "dependencies": { "tinyspy": "^4.0.3" } }, "sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw=="], - - "@vitest/ui/@vitest/utils": ["@vitest/utils@3.2.4", "", { "dependencies": { "@vitest/pretty-format": "3.2.4", "loupe": "^3.1.4", "tinyrainbow": "^2.0.0" } }, "sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA=="], - - "@vitest/ui/pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="], - - "@vitest/ui/vitest": ["vitest@3.2.4", "", { "dependencies": { "@types/chai": "^5.2.2", "@vitest/expect": "3.2.4", "@vitest/mocker": "3.2.4", "@vitest/pretty-format": "^3.2.4", "@vitest/runner": "3.2.4", "@vitest/snapshot": "3.2.4", "@vitest/spy": "3.2.4", "@vitest/utils": "3.2.4", "chai": "^5.2.0", "debug": "^4.4.1", "expect-type": "^1.2.1", "magic-string": "^0.30.17", "pathe": "^2.0.3", "picomatch": "^4.0.2", "std-env": "^3.9.0", "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", "why-is-node-running": "^2.3.0" }, "peerDependencies": { "@edge-runtime/vm": "*", "@types/debug": "^4.1.12", "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "@vitest/browser": "3.2.4", "@vitest/ui": "3.2.4", "happy-dom": "*", "jsdom": "*" }, "optionalPeers": ["@edge-runtime/vm", "@types/debug", "@types/node", "@vitest/browser", "@vitest/ui", "happy-dom", "jsdom"], "bin": { "vitest": "vitest.mjs" } }, "sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A=="], - "@vue/compiler-core/estree-walker": ["estree-walker@2.0.2", "", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="], "@vue/compiler-sfc/estree-walker": ["estree-walker@2.0.2", "", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="], @@ -3668,10 +3592,6 @@ "camelcase-keys/camelcase": ["camelcase@5.3.1", "", {}, "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg=="], - "chrome-launcher/@types/node": ["@types/node@24.7.1", "", { "dependencies": { "undici-types": "~7.14.0" } }, "sha512-CmyhGZanP88uuC5GpWU9q+fI61j2SkhO3UGMUdfYRE6Bcy0ccyzn1Rqj9YAB/ZY4kOXmNf0ocah5GtphmLMP6Q=="], - - "chromium-edge-launcher/@types/node": ["@types/node@24.7.1", "", { "dependencies": { "undici-types": "~7.14.0" } }, "sha512-CmyhGZanP88uuC5GpWU9q+fI61j2SkhO3UGMUdfYRE6Bcy0ccyzn1Rqj9YAB/ZY4kOXmNf0ocah5GtphmLMP6Q=="], - "chromium-edge-launcher/mkdirp": ["mkdirp@1.0.4", "", { "bin": { "mkdirp": "bin/cmd.js" } }, "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw=="], "chromium-edge-launcher/rimraf": ["rimraf@3.0.2", "", { "dependencies": { "glob": "^7.1.3" }, "bin": { "rimraf": "bin.js" } }, "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA=="], @@ -3716,7 +3636,7 @@ "eslint-plugin-svelte/postcss-load-config": ["postcss-load-config@3.1.4", "", { "dependencies": { "lilconfig": "^2.0.5", "yaml": "^1.10.2" }, "peerDependencies": { "postcss": ">=8.0.9", "ts-node": ">=9.0.0" }, "optionalPeers": ["postcss", "ts-node"] }, "sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg=="], - "event-graph-example/vite": ["vite@7.1.9", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.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", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-4nVGliEpxmhCL8DslSAUdxlB6+SMrhB0a1v5ijlh1xB1nEPuy1mxaHxysVucLHuWryAxLWg6a5ei+U4TLn/rFg=="], + "execa/signal-exit": ["signal-exit@3.0.7", "", {}, "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="], "expo/babel-preset-expo": ["babel-preset-expo@13.2.4", "", { "dependencies": { "@babel/helper-module-imports": "^7.25.9", "@babel/plugin-proposal-decorators": "^7.12.9", "@babel/plugin-proposal-export-default-from": "^7.24.7", "@babel/plugin-syntax-export-default-from": "^7.24.7", "@babel/plugin-transform-export-namespace-from": "^7.25.9", "@babel/plugin-transform-flow-strip-types": "^7.25.2", "@babel/plugin-transform-modules-commonjs": "^7.24.8", "@babel/plugin-transform-object-rest-spread": "^7.24.7", "@babel/plugin-transform-parameters": "^7.24.7", "@babel/plugin-transform-private-methods": "^7.24.7", "@babel/plugin-transform-private-property-in-object": "^7.24.7", "@babel/plugin-transform-runtime": "^7.24.7", "@babel/preset-react": "^7.22.15", "@babel/preset-typescript": "^7.23.0", "@react-native/babel-preset": "0.79.6", "babel-plugin-react-native-web": "~0.19.13", "babel-plugin-syntax-hermes-parser": "^0.25.1", "babel-plugin-transform-flow-enums": "^0.0.2", "debug": "^4.3.4", "react-refresh": "^0.14.2", "resolve-from": "^5.0.0" }, "peerDependencies": { "babel-plugin-react-compiler": "^19.0.0-beta-e993439-20250405" }, "optionalPeers": ["babel-plugin-react-compiler"] }, "sha512-3IKORo3KR+4qtLdCkZNDj8KeA43oBn7RRQejFGWfiZgu/NeaRUSri8YwYjZqybm7hn3nmMv9OLahlvXBX23o5Q=="], @@ -3724,14 +3644,10 @@ "expo-modules-autolinking/commander": ["commander@7.2.0", "", {}, "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw=="], - "expo-nip55/react": ["react@19.0.0", "", {}, "sha512-V8AVnmPIICiWpGfm6GLzCR/W5FXLchHop40W4nXBmdlEceh16rCN8O8LNWm5bh5XUX91fh7KpA+W0TgMKmgTpQ=="], - "expo-nip55/react-native": ["react-native@0.79.2", "", { "dependencies": { "@jest/create-cache-key-function": "^29.7.0", "@react-native/assets-registry": "0.79.2", "@react-native/codegen": "0.79.2", "@react-native/community-cli-plugin": "0.79.2", "@react-native/gradle-plugin": "0.79.2", "@react-native/js-polyfills": "0.79.2", "@react-native/normalize-colors": "0.79.2", "@react-native/virtualized-lists": "0.79.2", "abort-controller": "^3.0.0", "anser": "^1.4.9", "ansi-regex": "^5.0.0", "babel-jest": "^29.7.0", "babel-plugin-syntax-hermes-parser": "0.25.1", "base64-js": "^1.5.1", "chalk": "^4.0.0", "commander": "^12.0.0", "event-target-shim": "^5.0.1", "flow-enums-runtime": "^0.0.6", "glob": "^7.1.1", "invariant": "^2.2.4", "jest-environment-node": "^29.7.0", "memoize-one": "^5.0.0", "metro-runtime": "^0.82.0", "metro-source-map": "^0.82.0", "nullthrows": "^1.1.1", "pretty-format": "^29.7.0", "promise": "^8.3.0", "react-devtools-core": "^6.1.1", "react-refresh": "^0.14.0", "regenerator-runtime": "^0.13.2", "scheduler": "0.25.0", "semver": "^7.1.3", "stacktrace-parser": "^0.1.10", "whatwg-fetch": "^3.0.0", "ws": "^6.2.3", "yargs": "^17.6.2" }, "peerDependencies": { "@types/react": "^19.0.0", "react": "^19.0.0" }, "optionalPeers": ["@types/react"], "bin": { "react-native": "cli.js" } }, "sha512-AnGzb56JvU5YCL7cAwg10+ewDquzvmgrMddiBM0GAWLwQM/6DJfGd2ZKrMuKKehHerpDDZgG+EY64gk3x3dEkw=="], "fast-glob/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], - "feed-viewer-example/vite": ["vite@7.1.9", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.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", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-4nVGliEpxmhCL8DslSAUdxlB6+SMrhB0a1v5ijlh1xB1nEPuy1mxaHxysVucLHuWryAxLWg6a5ei+U4TLn/rFg=="], - "finalhandler/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="], "finalhandler/encodeurl": ["encodeurl@1.0.2", "", {}, "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w=="], @@ -3748,26 +3664,14 @@ "import-fresh/resolve-from": ["resolve-from@4.0.0", "", {}, "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="], - "is-git-dirty/execa": ["execa@4.1.0", "", { "dependencies": { "cross-spawn": "^7.0.0", "get-stream": "^5.0.0", "human-signals": "^1.1.1", "is-stream": "^2.0.0", "merge-stream": "^2.0.0", "npm-run-path": "^4.0.0", "onetime": "^5.1.0", "signal-exit": "^3.0.2", "strip-final-newline": "^2.0.0" } }, "sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA=="], - - "is-git-repository/execa": ["execa@4.1.0", "", { "dependencies": { "cross-spawn": "^7.0.0", "get-stream": "^5.0.0", "human-signals": "^1.1.1", "is-stream": "^2.0.0", "merge-stream": "^2.0.0", "npm-run-path": "^4.0.0", "onetime": "^5.1.0", "signal-exit": "^3.0.2", "strip-final-newline": "^2.0.0" } }, "sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA=="], - "istanbul-lib-instrument/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], "jest-diff/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], - "jest-environment-node/@types/node": ["@types/node@24.7.1", "", { "dependencies": { "undici-types": "~7.14.0" } }, "sha512-CmyhGZanP88uuC5GpWU9q+fI61j2SkhO3UGMUdfYRE6Bcy0ccyzn1Rqj9YAB/ZY4kOXmNf0ocah5GtphmLMP6Q=="], - - "jest-haste-map/@types/node": ["@types/node@24.7.1", "", { "dependencies": { "undici-types": "~7.14.0" } }, "sha512-CmyhGZanP88uuC5GpWU9q+fI61j2SkhO3UGMUdfYRE6Bcy0ccyzn1Rqj9YAB/ZY4kOXmNf0ocah5GtphmLMP6Q=="], - "jest-message-util/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], "jest-message-util/slash": ["slash@3.0.0", "", {}, "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q=="], - "jest-mock/@types/node": ["@types/node@24.7.1", "", { "dependencies": { "undici-types": "~7.14.0" } }, "sha512-CmyhGZanP88uuC5GpWU9q+fI61j2SkhO3UGMUdfYRE6Bcy0ccyzn1Rqj9YAB/ZY4kOXmNf0ocah5GtphmLMP6Q=="], - - "jest-util/@types/node": ["@types/node@24.7.1", "", { "dependencies": { "undici-types": "~7.14.0" } }, "sha512-CmyhGZanP88uuC5GpWU9q+fI61j2SkhO3UGMUdfYRE6Bcy0ccyzn1Rqj9YAB/ZY4kOXmNf0ocah5GtphmLMP6Q=="], - "jest-util/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], "jest-util/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], @@ -3776,8 +3680,6 @@ "jest-validate/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], - "jest-worker/@types/node": ["@types/node@24.7.1", "", { "dependencies": { "undici-types": "~7.14.0" } }, "sha512-CmyhGZanP88uuC5GpWU9q+fI61j2SkhO3UGMUdfYRE6Bcy0ccyzn1Rqj9YAB/ZY4kOXmNf0ocah5GtphmLMP6Q=="], - "jest-worker/supports-color": ["supports-color@8.1.1", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q=="], "jscodeshift/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], @@ -3798,8 +3700,6 @@ "log-symbols/is-unicode-supported": ["is-unicode-supported@1.3.0", "", {}, "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ=="], - "loose-envify/js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="], - "meow/type-fest": ["type-fest@0.18.1", "", {}, "sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw=="], "meow/yargs-parser": ["yargs-parser@20.2.9", "", {}, "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w=="], @@ -3824,7 +3724,7 @@ "mime-types/mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="], - "mlly/pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="], + "mlly/pkg-types": ["pkg-types@1.3.1", "", { "dependencies": { "confbox": "^0.1.8", "mlly": "^1.7.4", "pathe": "^2.0.1" } }, "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ=="], "node-dir/minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="], @@ -3844,12 +3744,8 @@ "npm-run-all/minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="], - "npm-run-path/path-key": ["path-key@4.0.0", "", {}, "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ=="], - "ora/strip-ansi": ["strip-ansi@7.1.2", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA=="], - "p-locate/p-limit": ["p-limit@3.1.0", "", { "dependencies": { "yocto-queue": "^0.1.0" } }, "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ=="], - "parse-png/pngjs": ["pngjs@3.4.0", "", {}, "sha512-NCrCHhWmnQklfH4MtJMRjZ2a8c80qXeMlQMv2uVp9ISJMTt562SbGd6n2oq0PaPgKm7Z6pL9E2UlLIhC+SHL3w=="], "parse5/entities": ["entities@6.0.1", "", {}, "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g=="], @@ -3858,8 +3754,6 @@ "pkg-dir/find-up": ["find-up@3.0.0", "", { "dependencies": { "locate-path": "^3.0.0" } }, "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg=="], - "pkg-types/pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="], - "playwright/fsevents": ["fsevents@2.3.2", "", { "os": "darwin" }, "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA=="], "prompts/kleur": ["kleur@3.0.3", "", {}, "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w=="], @@ -3870,6 +3764,8 @@ "react-devtools-core/ws": ["ws@7.5.10", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": "^5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ=="], + "react-dom/react": ["react@18.3.1", "", { "dependencies": { "loose-envify": "^1.1.0" } }, "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ=="], + "react-dom/scheduler": ["scheduler@0.23.2", "", { "dependencies": { "loose-envify": "^1.1.0" } }, "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ=="], "react-native/babel-plugin-syntax-hermes-parser": ["babel-plugin-syntax-hermes-parser@0.32.0", "", { "dependencies": { "hermes-parser": "0.32.0" } }, "sha512-m5HthL++AbyeEA2FcdwOLfVFvWYECOBObLHNqdR8ceY4TsEdn4LdX2oTvbB2QJSSElE2AWA/b2MXZ/PF/CqLZg=="], @@ -3878,8 +3774,6 @@ "react-native/glob": ["glob@7.2.3", "", { "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } }, "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q=="], - "react-native/react": ["react@19.0.0", "", {}, "sha512-V8AVnmPIICiWpGfm6GLzCR/W5FXLchHop40W4nXBmdlEceh16rCN8O8LNWm5bh5XUX91fh7KpA+W0TgMKmgTpQ=="], - "react-native/ws": ["ws@6.2.3", "", { "dependencies": { "async-limiter": "~1.0.0" } }, "sha512-jmTjYU0j60B+vHey6TfR3Z7RD61z/hmxBS3VMSGIrroOWXQEneK1zNuotOUrGyBHQj0yrpsLHPWtigEFd13ndA=="], "react-native-builder-bob/glob": ["glob@8.1.0", "", { "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^5.0.1", "once": "^1.3.0" } }, "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ=="], @@ -3928,13 +3822,9 @@ "string-width-cjs/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], - "sucrase/commander": ["commander@4.1.1", "", {}, "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA=="], - - "svelte-component-browser/vite": ["vite@7.1.9", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.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", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-4nVGliEpxmhCL8DslSAUdxlB6+SMrhB0a1v5ijlh1xB1nEPuy1mxaHxysVucLHuWryAxLWg6a5ei+U4TLn/rFg=="], - - "svelte-nutsack-wallet/vite": ["vite@7.1.9", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.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", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-4nVGliEpxmhCL8DslSAUdxlB6+SMrhB0a1v5ijlh1xB1nEPuy1mxaHxysVucLHuWryAxLWg6a5ei+U4TLn/rFg=="], + "strip-literal/js-tokens": ["js-tokens@9.0.1", "", {}, "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ=="], - "svelte-sessions-demo/vite": ["vite@7.1.9", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.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", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-4nVGliEpxmhCL8DslSAUdxlB6+SMrhB0a1v5ijlh1xB1nEPuy1mxaHxysVucLHuWryAxLWg6a5ei+U4TLn/rFg=="], + "sucrase/commander": ["commander@4.1.1", "", {}, "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA=="], "tar/chownr": ["chownr@3.0.0", "", {}, "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g=="], @@ -3946,20 +3836,10 @@ "tsd/globby": ["globby@11.1.0", "", { "dependencies": { "array-union": "^2.1.0", "dir-glob": "^3.0.1", "fast-glob": "^3.2.9", "ignore": "^5.2.0", "merge2": "^1.4.1", "slash": "^3.0.0" } }, "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g=="], - "vite/esbuild": ["esbuild@0.18.20", "", { "optionalDependencies": { "@esbuild/android-arm": "0.18.20", "@esbuild/android-arm64": "0.18.20", "@esbuild/android-x64": "0.18.20", "@esbuild/darwin-arm64": "0.18.20", "@esbuild/darwin-x64": "0.18.20", "@esbuild/freebsd-arm64": "0.18.20", "@esbuild/freebsd-x64": "0.18.20", "@esbuild/linux-arm": "0.18.20", "@esbuild/linux-arm64": "0.18.20", "@esbuild/linux-ia32": "0.18.20", "@esbuild/linux-loong64": "0.18.20", "@esbuild/linux-mips64el": "0.18.20", "@esbuild/linux-ppc64": "0.18.20", "@esbuild/linux-riscv64": "0.18.20", "@esbuild/linux-s390x": "0.18.20", "@esbuild/linux-x64": "0.18.20", "@esbuild/netbsd-x64": "0.18.20", "@esbuild/openbsd-x64": "0.18.20", "@esbuild/sunos-x64": "0.18.20", "@esbuild/win32-arm64": "0.18.20", "@esbuild/win32-ia32": "0.18.20", "@esbuild/win32-x64": "0.18.20" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA=="], - - "vite/rollup": ["rollup@3.29.5", "", { "optionalDependencies": { "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-GVsDdsbJzzy4S/v3dqWPJ7EfvZJfCHiDqe80IyrF59LYuP+e6U1LJoUqeuqRbwAWoMNoXivMNeNAOf5E22VA1w=="], - - "vite-node/vite": ["vite@5.4.20", "", { "dependencies": { "esbuild": "^0.21.3", "postcss": "^8.4.43", "rollup": "^4.20.0" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || >=20.0.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.4.0" }, "optionalPeers": ["@types/node", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser"], "bin": { "vite": "bin/vite.js" } }, "sha512-j3lYzGC3P+B5Yfy/pfKNgVEg4+UtcIJcVRt2cDjIOmhLourAqPqf8P7acgxeiSgUB7E3p2P8/3gNIgDLpwzs4g=="], - "vitepress/shiki": ["shiki@2.5.0", "", { "dependencies": { "@shikijs/core": "2.5.0", "@shikijs/engine-javascript": "2.5.0", "@shikijs/engine-oniguruma": "2.5.0", "@shikijs/langs": "2.5.0", "@shikijs/themes": "2.5.0", "@shikijs/types": "2.5.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-mI//trrsaiCIPsja5CNfsyNOqgAZUb6VpJA+340toL42UpzQlXpwRV9nch69X6gaUxrr9kaOOa6e3y3uAkGFxQ=="], "vitepress/vite": ["vite@5.4.20", "", { "dependencies": { "esbuild": "^0.21.3", "postcss": "^8.4.43", "rollup": "^4.20.0" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || >=20.0.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.4.0" }, "optionalPeers": ["@types/node", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser"], "bin": { "vite": "bin/vite.js" } }, "sha512-j3lYzGC3P+B5Yfy/pfKNgVEg4+UtcIJcVRt2cDjIOmhLourAqPqf8P7acgxeiSgUB7E3p2P8/3gNIgDLpwzs4g=="], - "vitest/vite": ["vite@5.4.20", "", { "dependencies": { "esbuild": "^0.21.3", "postcss": "^8.4.43", "rollup": "^4.20.0" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || >=20.0.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.4.0" }, "optionalPeers": ["@types/node", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser"], "bin": { "vite": "bin/vite.js" } }, "sha512-j3lYzGC3P+B5Yfy/pfKNgVEg4+UtcIJcVRt2cDjIOmhLourAqPqf8P7acgxeiSgUB7E3p2P8/3gNIgDLpwzs4g=="], - - "vitest-browser-svelte/vitest": ["vitest@3.2.4", "", { "dependencies": { "@types/chai": "^5.2.2", "@vitest/expect": "3.2.4", "@vitest/mocker": "3.2.4", "@vitest/pretty-format": "^3.2.4", "@vitest/runner": "3.2.4", "@vitest/snapshot": "3.2.4", "@vitest/spy": "3.2.4", "@vitest/utils": "3.2.4", "chai": "^5.2.0", "debug": "^4.4.1", "expect-type": "^1.2.1", "magic-string": "^0.30.17", "pathe": "^2.0.3", "picomatch": "^4.0.2", "std-env": "^3.9.0", "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", "why-is-node-running": "^2.3.0" }, "peerDependencies": { "@edge-runtime/vm": "*", "@types/debug": "^4.1.12", "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "@vitest/browser": "3.2.4", "@vitest/ui": "3.2.4", "happy-dom": "*", "jsdom": "*" }, "optionalPeers": ["@edge-runtime/vm", "@types/debug", "@types/node", "@vitest/browser", "@vitest/ui", "happy-dom", "jsdom"], "bin": { "vitest": "vitest.mjs" } }, "sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A=="], - "websocket/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="], "whatwg-url-without-unicode/buffer": ["buffer@5.7.1", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.1.13" } }, "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ=="], @@ -4020,8 +3900,6 @@ "@expo/fingerprint/chalk/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], - "@expo/fingerprint/p-limit/yocto-queue": ["yocto-queue@0.1.0", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="], - "@expo/image-utils/chalk/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], "@expo/metro-config/chalk/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], @@ -4046,8 +3924,6 @@ "@expo/xcpretty/chalk/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], - "@iconify/utils/local-pkg/pkg-types": ["pkg-types@2.3.0", "", { "dependencies": { "confbox": "^0.2.2", "exsolve": "^1.0.7", "pathe": "^2.0.3" } }, "sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig=="], - "@isaacs/cliui/string-width/emoji-regex": ["emoji-regex@9.2.2", "", {}, "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="], "@isaacs/cliui/strip-ansi/ansi-regex": ["ansi-regex@6.2.2", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="], @@ -4058,150 +3934,10 @@ "@istanbuljs/load-nyc-config/js-yaml/argparse": ["argparse@1.0.10", "", { "dependencies": { "sprintf-js": "~1.0.2" } }, "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg=="], - "@jest/environment/@types/node/undici-types": ["undici-types@7.14.0", "", {}, "sha512-QQiYxHuyZ9gQUIrmPo3IA+hUl4KYk8uSA7cHrcKd/l3p1OTpZcM0Tbp9x7FAtXdAYhlasd60ncPpgu6ihG6TOA=="], - - "@jest/fake-timers/@types/node/undici-types": ["undici-types@7.14.0", "", {}, "sha512-QQiYxHuyZ9gQUIrmPo3IA+hUl4KYk8uSA7cHrcKd/l3p1OTpZcM0Tbp9x7FAtXdAYhlasd60ncPpgu6ihG6TOA=="], - "@jest/transform/chalk/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], - "@jest/types/@types/node/undici-types": ["undici-types@7.14.0", "", {}, "sha512-QQiYxHuyZ9gQUIrmPo3IA+hUl4KYk8uSA7cHrcKd/l3p1OTpZcM0Tbp9x7FAtXdAYhlasd60ncPpgu6ihG6TOA=="], - "@jest/types/chalk/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], - "@nostr-dev-kit/cache-nostr/@types/node/undici-types": ["undici-types@5.26.5", "", {}, "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="], - - "@nostr-dev-kit/cache-nostr/tsup/bundle-require": ["bundle-require@4.2.1", "", { "dependencies": { "load-tsconfig": "^0.2.3" }, "peerDependencies": { "esbuild": ">=0.17" } }, "sha512-7Q/6vkyYAwOmQNRw75x+4yRtZCZJXUDmHHlFdkiV0wgv/reNjtJwpu1jPJ0w2kbEpIM0uoKI3S4/f39dU7AjSA=="], - - "@nostr-dev-kit/cache-nostr/tsup/chokidar": ["chokidar@3.6.0", "", { "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", "glob-parent": "~5.1.2", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", "readdirp": "~3.6.0" }, "optionalDependencies": { "fsevents": "~2.3.2" } }, "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw=="], - - "@nostr-dev-kit/cache-nostr/tsup/esbuild": ["esbuild@0.19.12", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.19.12", "@esbuild/android-arm": "0.19.12", "@esbuild/android-arm64": "0.19.12", "@esbuild/android-x64": "0.19.12", "@esbuild/darwin-arm64": "0.19.12", "@esbuild/darwin-x64": "0.19.12", "@esbuild/freebsd-arm64": "0.19.12", "@esbuild/freebsd-x64": "0.19.12", "@esbuild/linux-arm": "0.19.12", "@esbuild/linux-arm64": "0.19.12", "@esbuild/linux-ia32": "0.19.12", "@esbuild/linux-loong64": "0.19.12", "@esbuild/linux-mips64el": "0.19.12", "@esbuild/linux-ppc64": "0.19.12", "@esbuild/linux-riscv64": "0.19.12", "@esbuild/linux-s390x": "0.19.12", "@esbuild/linux-x64": "0.19.12", "@esbuild/netbsd-x64": "0.19.12", "@esbuild/openbsd-x64": "0.19.12", "@esbuild/sunos-x64": "0.19.12", "@esbuild/win32-arm64": "0.19.12", "@esbuild/win32-ia32": "0.19.12", "@esbuild/win32-x64": "0.19.12" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg=="], - - "@nostr-dev-kit/cache-nostr/tsup/execa": ["execa@5.1.1", "", { "dependencies": { "cross-spawn": "^7.0.3", "get-stream": "^6.0.0", "human-signals": "^2.1.0", "is-stream": "^2.0.0", "merge-stream": "^2.0.0", "npm-run-path": "^4.0.1", "onetime": "^5.1.2", "signal-exit": "^3.0.3", "strip-final-newline": "^2.0.0" } }, "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg=="], - - "@nostr-dev-kit/cache-nostr/tsup/globby": ["globby@11.1.0", "", { "dependencies": { "array-union": "^2.1.0", "dir-glob": "^3.0.1", "fast-glob": "^3.2.9", "ignore": "^5.2.0", "merge2": "^1.4.1", "slash": "^3.0.0" } }, "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g=="], - - "@nostr-dev-kit/cache-nostr/tsup/postcss-load-config": ["postcss-load-config@4.0.2", "", { "dependencies": { "lilconfig": "^3.0.0", "yaml": "^2.3.4" }, "peerDependencies": { "postcss": ">=8.0.9", "ts-node": ">=9.0.0" }, "optionalPeers": ["postcss", "ts-node"] }, "sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ=="], - - "@nostr-dev-kit/cache-sqlite-wasm/@types/node/undici-types": ["undici-types@7.14.0", "", {}, "sha512-QQiYxHuyZ9gQUIrmPo3IA+hUl4KYk8uSA7cHrcKd/l3p1OTpZcM0Tbp9x7FAtXdAYhlasd60ncPpgu6ihG6TOA=="], - - "@nostr-dev-kit/cache-sqlite-wasm/vitest/@vitest/expect": ["@vitest/expect@3.2.4", "", { "dependencies": { "@types/chai": "^5.2.2", "@vitest/spy": "3.2.4", "@vitest/utils": "3.2.4", "chai": "^5.2.0", "tinyrainbow": "^2.0.0" } }, "sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig=="], - - "@nostr-dev-kit/cache-sqlite-wasm/vitest/@vitest/runner": ["@vitest/runner@3.2.4", "", { "dependencies": { "@vitest/utils": "3.2.4", "pathe": "^2.0.3", "strip-literal": "^3.0.0" } }, "sha512-oukfKT9Mk41LreEW09vt45f8wx7DordoWUZMYdY/cyAk7w5TWkTRCNZYF7sX7n2wB7jyGAl74OxgwhPgKaqDMQ=="], - - "@nostr-dev-kit/cache-sqlite-wasm/vitest/@vitest/snapshot": ["@vitest/snapshot@3.2.4", "", { "dependencies": { "@vitest/pretty-format": "3.2.4", "magic-string": "^0.30.17", "pathe": "^2.0.3" } }, "sha512-dEYtS7qQP2CjU27QBC5oUOxLE/v5eLkGqPE0ZKEIDGMs4vKWe7IjgLOeauHsR0D5YuuycGRO5oSRXnwnmA78fQ=="], - - "@nostr-dev-kit/cache-sqlite-wasm/vitest/@vitest/spy": ["@vitest/spy@3.2.4", "", { "dependencies": { "tinyspy": "^4.0.3" } }, "sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw=="], - - "@nostr-dev-kit/cache-sqlite-wasm/vitest/@vitest/utils": ["@vitest/utils@3.2.4", "", { "dependencies": { "@vitest/pretty-format": "3.2.4", "loupe": "^3.1.4", "tinyrainbow": "^2.0.0" } }, "sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA=="], - - "@nostr-dev-kit/cache-sqlite-wasm/vitest/chai": ["chai@5.3.3", "", { "dependencies": { "assertion-error": "^2.0.1", "check-error": "^2.1.1", "deep-eql": "^5.0.1", "loupe": "^3.1.0", "pathval": "^2.0.0" } }, "sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw=="], - - "@nostr-dev-kit/cache-sqlite-wasm/vitest/pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="], - - "@nostr-dev-kit/cache-sqlite-wasm/vitest/tinypool": ["tinypool@1.1.1", "", {}, "sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg=="], - - "@nostr-dev-kit/cache-sqlite-wasm/vitest/vite": ["vite@7.1.9", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.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", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-4nVGliEpxmhCL8DslSAUdxlB6+SMrhB0a1v5ijlh1xB1nEPuy1mxaHxysVucLHuWryAxLWg6a5ei+U4TLn/rFg=="], - - "@nostr-dev-kit/cache-sqlite-wasm/vitest/vite-node": ["vite-node@3.2.4", "", { "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" } }, "sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg=="], - - "@nostr-dev-kit/ndk/nostr-tools/@noble/curves": ["@noble/curves@1.2.0", "", { "dependencies": { "@noble/hashes": "1.3.2" } }, "sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw=="], - - "@nostr-dev-kit/ndk/nostr-tools/@noble/hashes": ["@noble/hashes@1.3.1", "", {}, "sha512-EbqwksQwz9xDRGfDST86whPBgM65E0OH/pCgqW0GBVzO22bNE+NuIbeTb714+IfSjU3aRk47EUvXIb5bTsenKA=="], - - "@nostr-dev-kit/ndk/nostr-tools/@scure/base": ["@scure/base@1.1.1", "", {}, "sha512-ZxOhsSyxYwLJj3pLZCefNitxsj093tb2vq90mp2txoYeBqbcjDjqFhyM8eUjq/uFm6zJ+mUuqxlS2FkuSY1MTA=="], - - "@nostr-dev-kit/ndk/vitest/@vitest/expect": ["@vitest/expect@3.2.4", "", { "dependencies": { "@types/chai": "^5.2.2", "@vitest/spy": "3.2.4", "@vitest/utils": "3.2.4", "chai": "^5.2.0", "tinyrainbow": "^2.0.0" } }, "sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig=="], - - "@nostr-dev-kit/ndk/vitest/@vitest/runner": ["@vitest/runner@3.2.4", "", { "dependencies": { "@vitest/utils": "3.2.4", "pathe": "^2.0.3", "strip-literal": "^3.0.0" } }, "sha512-oukfKT9Mk41LreEW09vt45f8wx7DordoWUZMYdY/cyAk7w5TWkTRCNZYF7sX7n2wB7jyGAl74OxgwhPgKaqDMQ=="], - - "@nostr-dev-kit/ndk/vitest/@vitest/snapshot": ["@vitest/snapshot@3.2.4", "", { "dependencies": { "@vitest/pretty-format": "3.2.4", "magic-string": "^0.30.17", "pathe": "^2.0.3" } }, "sha512-dEYtS7qQP2CjU27QBC5oUOxLE/v5eLkGqPE0ZKEIDGMs4vKWe7IjgLOeauHsR0D5YuuycGRO5oSRXnwnmA78fQ=="], - - "@nostr-dev-kit/ndk/vitest/@vitest/spy": ["@vitest/spy@3.2.4", "", { "dependencies": { "tinyspy": "^4.0.3" } }, "sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw=="], - - "@nostr-dev-kit/ndk/vitest/@vitest/utils": ["@vitest/utils@3.2.4", "", { "dependencies": { "@vitest/pretty-format": "3.2.4", "loupe": "^3.1.4", "tinyrainbow": "^2.0.0" } }, "sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA=="], - - "@nostr-dev-kit/ndk/vitest/chai": ["chai@5.3.3", "", { "dependencies": { "assertion-error": "^2.0.1", "check-error": "^2.1.1", "deep-eql": "^5.0.1", "loupe": "^3.1.0", "pathval": "^2.0.0" } }, "sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw=="], - - "@nostr-dev-kit/ndk/vitest/pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="], - - "@nostr-dev-kit/ndk/vitest/tinypool": ["tinypool@1.1.1", "", {}, "sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg=="], - - "@nostr-dev-kit/ndk/vitest/vite": ["vite@7.1.9", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.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", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-4nVGliEpxmhCL8DslSAUdxlB6+SMrhB0a1v5ijlh1xB1nEPuy1mxaHxysVucLHuWryAxLWg6a5ei+U4TLn/rFg=="], - - "@nostr-dev-kit/ndk/vitest/vite-node": ["vite-node@3.2.4", "", { "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" } }, "sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg=="], - - "@nostr-dev-kit/react/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.17.19", "", { "os": "android", "cpu": "arm" }, "sha512-rIKddzqhmav7MSmoFCmDIb6e2W57geRsM94gV2l38fzhXMwq7hZoClug9USI2pFRGL06f4IOPHHpFNOkWieR8A=="], - - "@nostr-dev-kit/react/esbuild/@esbuild/android-arm64": ["@esbuild/android-arm64@0.17.19", "", { "os": "android", "cpu": "arm64" }, "sha512-KBMWvEZooR7+kzY0BtbTQn0OAYY7CsiydT63pVEaPtVYF0hXbUaOyZog37DKxK7NF3XacBJOpYT4adIJh+avxA=="], - - "@nostr-dev-kit/react/esbuild/@esbuild/android-x64": ["@esbuild/android-x64@0.17.19", "", { "os": "android", "cpu": "x64" }, "sha512-uUTTc4xGNDT7YSArp/zbtmbhO0uEEK9/ETW29Wk1thYUJBz3IVnvgEiEwEa9IeLyvnpKrWK64Utw2bgUmDveww=="], - - "@nostr-dev-kit/react/esbuild/@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.17.19", "", { "os": "darwin", "cpu": "arm64" }, "sha512-80wEoCfF/hFKM6WE1FyBHc9SfUblloAWx6FJkFWTWiCoht9Mc0ARGEM47e67W9rI09YoUxJL68WHfDRYEAvOhg=="], - - "@nostr-dev-kit/react/esbuild/@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.17.19", "", { "os": "darwin", "cpu": "x64" }, "sha512-IJM4JJsLhRYr9xdtLytPLSH9k/oxR3boaUIYiHkAawtwNOXKE8KoU8tMvryogdcT8AU+Bflmh81Xn6Q0vTZbQw=="], - - "@nostr-dev-kit/react/esbuild/@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.17.19", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-pBwbc7DufluUeGdjSU5Si+P3SoMF5DQ/F/UmTSb8HXO80ZEAJmrykPyzo1IfNbAoaqw48YRpv8shwd1NoI0jcQ=="], - - "@nostr-dev-kit/react/esbuild/@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.17.19", "", { "os": "freebsd", "cpu": "x64" }, "sha512-4lu+n8Wk0XlajEhbEffdy2xy53dpR06SlzvhGByyg36qJw6Kpfk7cp45DR/62aPH9mtJRmIyrXAS5UWBrJT6TQ=="], - - "@nostr-dev-kit/react/esbuild/@esbuild/linux-arm": ["@esbuild/linux-arm@0.17.19", "", { "os": "linux", "cpu": "arm" }, "sha512-cdmT3KxjlOQ/gZ2cjfrQOtmhG4HJs6hhvm3mWSRDPtZ/lP5oe8FWceS10JaSJC13GBd4eH/haHnqf7hhGNLerA=="], - - "@nostr-dev-kit/react/esbuild/@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.17.19", "", { "os": "linux", "cpu": "arm64" }, "sha512-ct1Tg3WGwd3P+oZYqic+YZF4snNl2bsnMKRkb3ozHmnM0dGWuxcPTTntAF6bOP0Sp4x0PjSF+4uHQ1xvxfRKqg=="], - - "@nostr-dev-kit/react/esbuild/@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.17.19", "", { "os": "linux", "cpu": "ia32" }, "sha512-w4IRhSy1VbsNxHRQpeGCHEmibqdTUx61Vc38APcsRbuVgK0OPEnQ0YD39Brymn96mOx48Y2laBQGqgZ0j9w6SQ=="], - - "@nostr-dev-kit/react/esbuild/@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.17.19", "", { "os": "linux", "cpu": "none" }, "sha512-2iAngUbBPMq439a+z//gE+9WBldoMp1s5GWsUSgqHLzLJ9WoZLZhpwWuym0u0u/4XmZ3gpHmzV84PonE+9IIdQ=="], - - "@nostr-dev-kit/react/esbuild/@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.17.19", "", { "os": "linux", "cpu": "none" }, "sha512-LKJltc4LVdMKHsrFe4MGNPp0hqDFA1Wpt3jE1gEyM3nKUvOiO//9PheZZHfYRfYl6AwdTH4aTcXSqBerX0ml4A=="], - - "@nostr-dev-kit/react/esbuild/@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.17.19", "", { "os": "linux", "cpu": "ppc64" }, "sha512-/c/DGybs95WXNS8y3Ti/ytqETiW7EU44MEKuCAcpPto3YjQbyK3IQVKfF6nbghD7EcLUGl0NbiL5Rt5DMhn5tg=="], - - "@nostr-dev-kit/react/esbuild/@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.17.19", "", { "os": "linux", "cpu": "none" }, "sha512-FC3nUAWhvFoutlhAkgHf8f5HwFWUL6bYdvLc/TTuxKlvLi3+pPzdZiFKSWz/PF30TB1K19SuCxDTI5KcqASJqA=="], - - "@nostr-dev-kit/react/esbuild/@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.17.19", "", { "os": "linux", "cpu": "s390x" }, "sha512-IbFsFbxMWLuKEbH+7sTkKzL6NJmG2vRyy6K7JJo55w+8xDk7RElYn6xvXtDW8HCfoKBFK69f3pgBJSUSQPr+4Q=="], - - "@nostr-dev-kit/react/esbuild/@esbuild/linux-x64": ["@esbuild/linux-x64@0.17.19", "", { "os": "linux", "cpu": "x64" }, "sha512-68ngA9lg2H6zkZcyp22tsVt38mlhWde8l3eJLWkyLrp4HwMUr3c1s/M2t7+kHIhvMjglIBrFpncX1SzMckomGw=="], - - "@nostr-dev-kit/react/esbuild/@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.17.19", "", { "os": "none", "cpu": "x64" }, "sha512-CwFq42rXCR8TYIjIfpXCbRX0rp1jo6cPIUPSaWwzbVI4aOfX96OXY8M6KNmtPcg7QjYeDmN+DD0Wp3LaBOLf4Q=="], - - "@nostr-dev-kit/react/esbuild/@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.17.19", "", { "os": "openbsd", "cpu": "x64" }, "sha512-cnq5brJYrSZ2CF6c35eCmviIN3k3RczmHz8eYaVlNasVqsNY+JKohZU5MKmaOI+KkllCdzOKKdPs762VCPC20g=="], - - "@nostr-dev-kit/react/esbuild/@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.17.19", "", { "os": "sunos", "cpu": "x64" }, "sha512-vCRT7yP3zX+bKWFeP/zdS6SqdWB8OIpaRq/mbXQxTGHnIxspRtigpkUcDMlSCOejlHowLqII7K2JKevwyRP2rg=="], - - "@nostr-dev-kit/react/esbuild/@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.17.19", "", { "os": "win32", "cpu": "arm64" }, "sha512-yYx+8jwowUstVdorcMdNlzklLYhPxjniHWFKgRqH7IFlUEa0Umu3KuYplf1HUZZ422e3NU9F4LGb+4O0Kdcaag=="], - - "@nostr-dev-kit/react/esbuild/@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.17.19", "", { "os": "win32", "cpu": "ia32" }, "sha512-eggDKanJszUtCdlVs0RB+h35wNlb5v4TWEkq4vZcmVt5u/HiDZrTXe2bWFQUez3RgNHwx/x4sk5++4NSSicKkw=="], - - "@nostr-dev-kit/react/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.17.19", "", { "os": "win32", "cpu": "x64" }, "sha512-lAhycmKnVOuRYNtRtatQR1LPQf2oYCkRGkSFnseDAKPl8lu5SOsK/e1sXe5a0Pc5kHIHe6P2I/ilntNv2xf3cA=="], - - "@nostr-dev-kit/react/jsdom/cssstyle": ["cssstyle@4.6.0", "", { "dependencies": { "@asamuzakjp/css-color": "^3.2.0", "rrweb-cssom": "^0.8.0" } }, "sha512-2z+rWdzbbSZv6/rhtvzvqeZQHrBaqgogqt85sqFNbabZOuFbCVFb8kPeEtZjiKkbrm395irpNKiYeFeLiQnFPg=="], - - "@nostr-dev-kit/react/jsdom/data-urls": ["data-urls@5.0.0", "", { "dependencies": { "whatwg-mimetype": "^4.0.0", "whatwg-url": "^14.0.0" } }, "sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg=="], - - "@nostr-dev-kit/react/jsdom/tough-cookie": ["tough-cookie@5.1.2", "", { "dependencies": { "tldts": "^6.1.32" } }, "sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A=="], - - "@nostr-dev-kit/react/jsdom/webidl-conversions": ["webidl-conversions@7.0.0", "", {}, "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g=="], - - "@nostr-dev-kit/react/jsdom/whatwg-url": ["whatwg-url@14.2.0", "", { "dependencies": { "tr46": "^5.1.0", "webidl-conversions": "^7.0.0" } }, "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw=="], - - "@nostr-dev-kit/svelte/vitest/@vitest/expect": ["@vitest/expect@3.2.4", "", { "dependencies": { "@types/chai": "^5.2.2", "@vitest/spy": "3.2.4", "@vitest/utils": "3.2.4", "chai": "^5.2.0", "tinyrainbow": "^2.0.0" } }, "sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig=="], - - "@nostr-dev-kit/svelte/vitest/@vitest/runner": ["@vitest/runner@3.2.4", "", { "dependencies": { "@vitest/utils": "3.2.4", "pathe": "^2.0.3", "strip-literal": "^3.0.0" } }, "sha512-oukfKT9Mk41LreEW09vt45f8wx7DordoWUZMYdY/cyAk7w5TWkTRCNZYF7sX7n2wB7jyGAl74OxgwhPgKaqDMQ=="], - - "@nostr-dev-kit/svelte/vitest/@vitest/snapshot": ["@vitest/snapshot@3.2.4", "", { "dependencies": { "@vitest/pretty-format": "3.2.4", "magic-string": "^0.30.17", "pathe": "^2.0.3" } }, "sha512-dEYtS7qQP2CjU27QBC5oUOxLE/v5eLkGqPE0ZKEIDGMs4vKWe7IjgLOeauHsR0D5YuuycGRO5oSRXnwnmA78fQ=="], - - "@nostr-dev-kit/svelte/vitest/@vitest/spy": ["@vitest/spy@3.2.4", "", { "dependencies": { "tinyspy": "^4.0.3" } }, "sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw=="], - - "@nostr-dev-kit/svelte/vitest/@vitest/utils": ["@vitest/utils@3.2.4", "", { "dependencies": { "@vitest/pretty-format": "3.2.4", "loupe": "^3.1.4", "tinyrainbow": "^2.0.0" } }, "sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA=="], - - "@nostr-dev-kit/svelte/vitest/chai": ["chai@5.3.3", "", { "dependencies": { "assertion-error": "^2.0.1", "check-error": "^2.1.1", "deep-eql": "^5.0.1", "loupe": "^3.1.0", "pathval": "^2.0.0" } }, "sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw=="], - - "@nostr-dev-kit/svelte/vitest/pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="], - - "@nostr-dev-kit/svelte/vitest/tinypool": ["tinypool@1.1.1", "", {}, "sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg=="], - - "@nostr-dev-kit/svelte/vitest/vite-node": ["vite-node@3.2.4", "", { "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" } }, "sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg=="], - "@react-native/babel-plugin-codegen/@react-native/codegen/glob": ["glob@7.2.3", "", { "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } }, "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q=="], "@react-native/babel-plugin-codegen/@react-native/codegen/hermes-parser": ["hermes-parser@0.23.1", "", { "dependencies": { "hermes-estree": "0.23.1" } }, "sha512-oxl5h2DkFW83hT4DAUJorpah8ou4yvmweUzLJmmr6YV2cezduCdlil1AvU/a/xSsAFo4WUcNA4GoV5Bvq6JffA=="], @@ -4226,90 +3962,18 @@ "@testing-library/dom/pretty-format/react-is": ["react-is@17.0.2", "", {}, "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w=="], - "@types/better-sqlite3/@types/node/undici-types": ["undici-types@7.14.0", "", {}, "sha512-QQiYxHuyZ9gQUIrmPo3IA+hUl4KYk8uSA7cHrcKd/l3p1OTpZcM0Tbp9x7FAtXdAYhlasd60ncPpgu6ihG6TOA=="], - - "@types/graceful-fs/@types/node/undici-types": ["undici-types@7.14.0", "", {}, "sha512-QQiYxHuyZ9gQUIrmPo3IA+hUl4KYk8uSA7cHrcKd/l3p1OTpZcM0Tbp9x7FAtXdAYhlasd60ncPpgu6ihG6TOA=="], - - "@types/qrcode/@types/node/undici-types": ["undici-types@7.14.0", "", {}, "sha512-QQiYxHuyZ9gQUIrmPo3IA+hUl4KYk8uSA7cHrcKd/l3p1OTpZcM0Tbp9x7FAtXdAYhlasd60ncPpgu6ihG6TOA=="], - - "@types/sql.js/@types/node/undici-types": ["undici-types@7.14.0", "", {}, "sha512-QQiYxHuyZ9gQUIrmPo3IA+hUl4KYk8uSA7cHrcKd/l3p1OTpZcM0Tbp9x7FAtXdAYhlasd60ncPpgu6ihG6TOA=="], - "@vitejs/plugin-vue/vite/esbuild": ["esbuild@0.21.5", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.21.5", "@esbuild/android-arm": "0.21.5", "@esbuild/android-arm64": "0.21.5", "@esbuild/android-x64": "0.21.5", "@esbuild/darwin-arm64": "0.21.5", "@esbuild/darwin-x64": "0.21.5", "@esbuild/freebsd-arm64": "0.21.5", "@esbuild/freebsd-x64": "0.21.5", "@esbuild/linux-arm": "0.21.5", "@esbuild/linux-arm64": "0.21.5", "@esbuild/linux-ia32": "0.21.5", "@esbuild/linux-loong64": "0.21.5", "@esbuild/linux-mips64el": "0.21.5", "@esbuild/linux-ppc64": "0.21.5", "@esbuild/linux-riscv64": "0.21.5", "@esbuild/linux-s390x": "0.21.5", "@esbuild/linux-x64": "0.21.5", "@esbuild/netbsd-x64": "0.21.5", "@esbuild/openbsd-x64": "0.21.5", "@esbuild/sunos-x64": "0.21.5", "@esbuild/win32-arm64": "0.21.5", "@esbuild/win32-ia32": "0.21.5", "@esbuild/win32-x64": "0.21.5" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw=="], "@vitest/browser/@testing-library/dom/aria-query": ["aria-query@5.3.0", "", { "dependencies": { "dequal": "^2.0.3" } }, "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A=="], "@vitest/browser/@testing-library/dom/pretty-format": ["pretty-format@27.5.1", "", { "dependencies": { "ansi-regex": "^5.0.1", "ansi-styles": "^5.0.0", "react-is": "^17.0.1" } }, "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ=="], - "@vitest/browser/@vitest/utils/loupe": ["loupe@3.2.1", "", {}, "sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ=="], - - "@vitest/browser/vitest/@vitest/expect": ["@vitest/expect@3.2.4", "", { "dependencies": { "@types/chai": "^5.2.2", "@vitest/spy": "3.2.4", "@vitest/utils": "3.2.4", "chai": "^5.2.0", "tinyrainbow": "^2.0.0" } }, "sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig=="], - - "@vitest/browser/vitest/@vitest/runner": ["@vitest/runner@3.2.4", "", { "dependencies": { "@vitest/utils": "3.2.4", "pathe": "^2.0.3", "strip-literal": "^3.0.0" } }, "sha512-oukfKT9Mk41LreEW09vt45f8wx7DordoWUZMYdY/cyAk7w5TWkTRCNZYF7sX7n2wB7jyGAl74OxgwhPgKaqDMQ=="], - - "@vitest/browser/vitest/@vitest/snapshot": ["@vitest/snapshot@3.2.4", "", { "dependencies": { "@vitest/pretty-format": "3.2.4", "magic-string": "^0.30.17", "pathe": "^2.0.3" } }, "sha512-dEYtS7qQP2CjU27QBC5oUOxLE/v5eLkGqPE0ZKEIDGMs4vKWe7IjgLOeauHsR0D5YuuycGRO5oSRXnwnmA78fQ=="], - - "@vitest/browser/vitest/@vitest/spy": ["@vitest/spy@3.2.4", "", { "dependencies": { "tinyspy": "^4.0.3" } }, "sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw=="], - - "@vitest/browser/vitest/chai": ["chai@5.3.3", "", { "dependencies": { "assertion-error": "^2.0.1", "check-error": "^2.1.1", "deep-eql": "^5.0.1", "loupe": "^3.1.0", "pathval": "^2.0.0" } }, "sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw=="], - - "@vitest/browser/vitest/pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="], - - "@vitest/browser/vitest/tinypool": ["tinypool@1.1.1", "", {}, "sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg=="], - - "@vitest/browser/vitest/vite": ["vite@7.1.9", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.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", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-4nVGliEpxmhCL8DslSAUdxlB6+SMrhB0a1v5ijlh1xB1nEPuy1mxaHxysVucLHuWryAxLWg6a5ei+U4TLn/rFg=="], - - "@vitest/browser/vitest/vite-node": ["vite-node@3.2.4", "", { "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" } }, "sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg=="], - - "@vitest/coverage-v8/vitest/@vitest/expect": ["@vitest/expect@3.2.4", "", { "dependencies": { "@types/chai": "^5.2.2", "@vitest/spy": "3.2.4", "@vitest/utils": "3.2.4", "chai": "^5.2.0", "tinyrainbow": "^2.0.0" } }, "sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig=="], - - "@vitest/coverage-v8/vitest/@vitest/runner": ["@vitest/runner@3.2.4", "", { "dependencies": { "@vitest/utils": "3.2.4", "pathe": "^2.0.3", "strip-literal": "^3.0.0" } }, "sha512-oukfKT9Mk41LreEW09vt45f8wx7DordoWUZMYdY/cyAk7w5TWkTRCNZYF7sX7n2wB7jyGAl74OxgwhPgKaqDMQ=="], - - "@vitest/coverage-v8/vitest/@vitest/snapshot": ["@vitest/snapshot@3.2.4", "", { "dependencies": { "@vitest/pretty-format": "3.2.4", "magic-string": "^0.30.17", "pathe": "^2.0.3" } }, "sha512-dEYtS7qQP2CjU27QBC5oUOxLE/v5eLkGqPE0ZKEIDGMs4vKWe7IjgLOeauHsR0D5YuuycGRO5oSRXnwnmA78fQ=="], - - "@vitest/coverage-v8/vitest/@vitest/spy": ["@vitest/spy@3.2.4", "", { "dependencies": { "tinyspy": "^4.0.3" } }, "sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw=="], - - "@vitest/coverage-v8/vitest/@vitest/utils": ["@vitest/utils@3.2.4", "", { "dependencies": { "@vitest/pretty-format": "3.2.4", "loupe": "^3.1.4", "tinyrainbow": "^2.0.0" } }, "sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA=="], - - "@vitest/coverage-v8/vitest/chai": ["chai@5.3.3", "", { "dependencies": { "assertion-error": "^2.0.1", "check-error": "^2.1.1", "deep-eql": "^5.0.1", "loupe": "^3.1.0", "pathval": "^2.0.0" } }, "sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw=="], - - "@vitest/coverage-v8/vitest/pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="], - - "@vitest/coverage-v8/vitest/tinypool": ["tinypool@1.1.1", "", {}, "sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg=="], - - "@vitest/coverage-v8/vitest/vite": ["vite@7.1.9", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.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", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-4nVGliEpxmhCL8DslSAUdxlB6+SMrhB0a1v5ijlh1xB1nEPuy1mxaHxysVucLHuWryAxLWg6a5ei+U4TLn/rFg=="], - - "@vitest/coverage-v8/vitest/vite-node": ["vite-node@3.2.4", "", { "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" } }, "sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg=="], - - "@vitest/mocker/@vitest/spy/tinyspy": ["tinyspy@4.0.4", "", {}, "sha512-azl+t0z7pw/z958Gy9svOTuzqIk6xq+NSheJzn5MMWtWTFywIacg2wUlzKFGtt3cthx0r2SxMK0yzJOR0IES7Q=="], - - "@vitest/ui/@vitest/utils/loupe": ["loupe@3.2.1", "", {}, "sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ=="], - - "@vitest/ui/vitest/@vitest/expect": ["@vitest/expect@3.2.4", "", { "dependencies": { "@types/chai": "^5.2.2", "@vitest/spy": "3.2.4", "@vitest/utils": "3.2.4", "chai": "^5.2.0", "tinyrainbow": "^2.0.0" } }, "sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig=="], - - "@vitest/ui/vitest/@vitest/runner": ["@vitest/runner@3.2.4", "", { "dependencies": { "@vitest/utils": "3.2.4", "pathe": "^2.0.3", "strip-literal": "^3.0.0" } }, "sha512-oukfKT9Mk41LreEW09vt45f8wx7DordoWUZMYdY/cyAk7w5TWkTRCNZYF7sX7n2wB7jyGAl74OxgwhPgKaqDMQ=="], - - "@vitest/ui/vitest/@vitest/snapshot": ["@vitest/snapshot@3.2.4", "", { "dependencies": { "@vitest/pretty-format": "3.2.4", "magic-string": "^0.30.17", "pathe": "^2.0.3" } }, "sha512-dEYtS7qQP2CjU27QBC5oUOxLE/v5eLkGqPE0ZKEIDGMs4vKWe7IjgLOeauHsR0D5YuuycGRO5oSRXnwnmA78fQ=="], - - "@vitest/ui/vitest/@vitest/spy": ["@vitest/spy@3.2.4", "", { "dependencies": { "tinyspy": "^4.0.3" } }, "sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw=="], - - "@vitest/ui/vitest/chai": ["chai@5.3.3", "", { "dependencies": { "assertion-error": "^2.0.1", "check-error": "^2.1.1", "deep-eql": "^5.0.1", "loupe": "^3.1.0", "pathval": "^2.0.0" } }, "sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw=="], - - "@vitest/ui/vitest/tinypool": ["tinypool@1.1.1", "", {}, "sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg=="], - - "@vitest/ui/vitest/vite": ["vite@7.1.9", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.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", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-4nVGliEpxmhCL8DslSAUdxlB6+SMrhB0a1v5ijlh1xB1nEPuy1mxaHxysVucLHuWryAxLWg6a5ei+U4TLn/rFg=="], - - "@vitest/ui/vitest/vite-node": ["vite-node@3.2.4", "", { "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" } }, "sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg=="], - "babel-jest/chalk/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], "babel-plugin-istanbul/test-exclude/glob": ["glob@7.2.3", "", { "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } }, "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q=="], "babel-plugin-istanbul/test-exclude/minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="], - "chrome-launcher/@types/node/undici-types": ["undici-types@7.14.0", "", {}, "sha512-QQiYxHuyZ9gQUIrmPo3IA+hUl4KYk8uSA7cHrcKd/l3p1OTpZcM0Tbp9x7FAtXdAYhlasd60ncPpgu6ihG6TOA=="], - - "chromium-edge-launcher/@types/node/undici-types": ["undici-types@7.14.0", "", {}, "sha512-QQiYxHuyZ9gQUIrmPo3IA+hUl4KYk8uSA7cHrcKd/l3p1OTpZcM0Tbp9x7FAtXdAYhlasd60ncPpgu6ihG6TOA=="], - "chromium-edge-launcher/rimraf/glob": ["glob@7.2.3", "", { "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } }, "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q=="], "cliui/string-width/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], @@ -4382,52 +4046,14 @@ "find-cache-dir/make-dir/semver": ["semver@5.7.2", "", { "bin": { "semver": "bin/semver" } }, "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g=="], - "is-git-dirty/execa/get-stream": ["get-stream@5.2.0", "", { "dependencies": { "pump": "^3.0.0" } }, "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA=="], - - "is-git-dirty/execa/human-signals": ["human-signals@1.1.1", "", {}, "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw=="], - - "is-git-dirty/execa/is-stream": ["is-stream@2.0.1", "", {}, "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg=="], - - "is-git-dirty/execa/npm-run-path": ["npm-run-path@4.0.1", "", { "dependencies": { "path-key": "^3.0.0" } }, "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw=="], - - "is-git-dirty/execa/onetime": ["onetime@5.1.2", "", { "dependencies": { "mimic-fn": "^2.1.0" } }, "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg=="], - - "is-git-dirty/execa/signal-exit": ["signal-exit@3.0.7", "", {}, "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="], - - "is-git-dirty/execa/strip-final-newline": ["strip-final-newline@2.0.0", "", {}, "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA=="], - - "is-git-repository/execa/get-stream": ["get-stream@5.2.0", "", { "dependencies": { "pump": "^3.0.0" } }, "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA=="], - - "is-git-repository/execa/human-signals": ["human-signals@1.1.1", "", {}, "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw=="], - - "is-git-repository/execa/is-stream": ["is-stream@2.0.1", "", {}, "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg=="], - - "is-git-repository/execa/npm-run-path": ["npm-run-path@4.0.1", "", { "dependencies": { "path-key": "^3.0.0" } }, "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw=="], - - "is-git-repository/execa/onetime": ["onetime@5.1.2", "", { "dependencies": { "mimic-fn": "^2.1.0" } }, "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg=="], - - "is-git-repository/execa/signal-exit": ["signal-exit@3.0.7", "", {}, "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="], - - "is-git-repository/execa/strip-final-newline": ["strip-final-newline@2.0.0", "", {}, "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA=="], - "jest-diff/chalk/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], - "jest-environment-node/@types/node/undici-types": ["undici-types@7.14.0", "", {}, "sha512-QQiYxHuyZ9gQUIrmPo3IA+hUl4KYk8uSA7cHrcKd/l3p1OTpZcM0Tbp9x7FAtXdAYhlasd60ncPpgu6ihG6TOA=="], - - "jest-haste-map/@types/node/undici-types": ["undici-types@7.14.0", "", {}, "sha512-QQiYxHuyZ9gQUIrmPo3IA+hUl4KYk8uSA7cHrcKd/l3p1OTpZcM0Tbp9x7FAtXdAYhlasd60ncPpgu6ihG6TOA=="], - "jest-message-util/chalk/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], - "jest-mock/@types/node/undici-types": ["undici-types@7.14.0", "", {}, "sha512-QQiYxHuyZ9gQUIrmPo3IA+hUl4KYk8uSA7cHrcKd/l3p1OTpZcM0Tbp9x7FAtXdAYhlasd60ncPpgu6ihG6TOA=="], - - "jest-util/@types/node/undici-types": ["undici-types@7.14.0", "", {}, "sha512-QQiYxHuyZ9gQUIrmPo3IA+hUl4KYk8uSA7cHrcKd/l3p1OTpZcM0Tbp9x7FAtXdAYhlasd60ncPpgu6ihG6TOA=="], - "jest-util/chalk/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], "jest-validate/chalk/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], - "jest-worker/@types/node/undici-types": ["undici-types@7.14.0", "", {}, "sha512-QQiYxHuyZ9gQUIrmPo3IA+hUl4KYk8uSA7cHrcKd/l3p1OTpZcM0Tbp9x7FAtXdAYhlasd60ncPpgu6ihG6TOA=="], - "jscodeshift/chalk/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], "jscodeshift/write-file-atomic/signal-exit": ["signal-exit@3.0.7", "", {}, "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="], @@ -4440,6 +4066,8 @@ "metro/hermes-parser/hermes-estree": ["hermes-estree@0.32.0", "", {}, "sha512-KWn3BqnlDOl97Xe1Yviur6NbgIZ+IP+UVSpshlZWkq+EtoHg6/cwiDj/osP9PCEgFE15KBm1O55JRwbMEm5ejQ=="], + "mlly/pkg-types/confbox": ["confbox@0.1.8", "", {}, "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w=="], + "node-dir/minimatch/brace-expansion": ["brace-expansion@1.1.12", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="], "normalize-package-data/hosted-git-info/lru-cache": ["lru-cache@6.0.0", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA=="], @@ -4462,8 +4090,6 @@ "ora/strip-ansi/ansi-regex": ["ansi-regex@6.2.2", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="], - "p-locate/p-limit/yocto-queue": ["yocto-queue@0.1.0", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="], - "pkg-dir/find-up/locate-path": ["locate-path@3.0.0", "", { "dependencies": { "p-locate": "^3.0.0", "path-exists": "^3.0.0" } }, "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A=="], "qrcode/yargs/cliui": ["cliui@6.0.0", "", { "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.0", "wrap-ansi": "^6.2.0" } }, "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ=="], @@ -4512,52 +4138,6 @@ "tsd/globby/slash": ["slash@3.0.0", "", {}, "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q=="], - "vite-node/vite/esbuild": ["esbuild@0.21.5", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.21.5", "@esbuild/android-arm": "0.21.5", "@esbuild/android-arm64": "0.21.5", "@esbuild/android-x64": "0.21.5", "@esbuild/darwin-arm64": "0.21.5", "@esbuild/darwin-x64": "0.21.5", "@esbuild/freebsd-arm64": "0.21.5", "@esbuild/freebsd-x64": "0.21.5", "@esbuild/linux-arm": "0.21.5", "@esbuild/linux-arm64": "0.21.5", "@esbuild/linux-ia32": "0.21.5", "@esbuild/linux-loong64": "0.21.5", "@esbuild/linux-mips64el": "0.21.5", "@esbuild/linux-ppc64": "0.21.5", "@esbuild/linux-riscv64": "0.21.5", "@esbuild/linux-s390x": "0.21.5", "@esbuild/linux-x64": "0.21.5", "@esbuild/netbsd-x64": "0.21.5", "@esbuild/openbsd-x64": "0.21.5", "@esbuild/sunos-x64": "0.21.5", "@esbuild/win32-arm64": "0.21.5", "@esbuild/win32-ia32": "0.21.5", "@esbuild/win32-x64": "0.21.5" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw=="], - - "vite/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.18.20", "", { "os": "android", "cpu": "arm" }, "sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw=="], - - "vite/esbuild/@esbuild/android-arm64": ["@esbuild/android-arm64@0.18.20", "", { "os": "android", "cpu": "arm64" }, "sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ=="], - - "vite/esbuild/@esbuild/android-x64": ["@esbuild/android-x64@0.18.20", "", { "os": "android", "cpu": "x64" }, "sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg=="], - - "vite/esbuild/@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.18.20", "", { "os": "darwin", "cpu": "arm64" }, "sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA=="], - - "vite/esbuild/@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.18.20", "", { "os": "darwin", "cpu": "x64" }, "sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ=="], - - "vite/esbuild/@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.18.20", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw=="], - - "vite/esbuild/@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.18.20", "", { "os": "freebsd", "cpu": "x64" }, "sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ=="], - - "vite/esbuild/@esbuild/linux-arm": ["@esbuild/linux-arm@0.18.20", "", { "os": "linux", "cpu": "arm" }, "sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg=="], - - "vite/esbuild/@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.18.20", "", { "os": "linux", "cpu": "arm64" }, "sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA=="], - - "vite/esbuild/@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.18.20", "", { "os": "linux", "cpu": "ia32" }, "sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA=="], - - "vite/esbuild/@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.18.20", "", { "os": "linux", "cpu": "none" }, "sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg=="], - - "vite/esbuild/@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.18.20", "", { "os": "linux", "cpu": "none" }, "sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ=="], - - "vite/esbuild/@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.18.20", "", { "os": "linux", "cpu": "ppc64" }, "sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA=="], - - "vite/esbuild/@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.18.20", "", { "os": "linux", "cpu": "none" }, "sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A=="], - - "vite/esbuild/@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.18.20", "", { "os": "linux", "cpu": "s390x" }, "sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ=="], - - "vite/esbuild/@esbuild/linux-x64": ["@esbuild/linux-x64@0.18.20", "", { "os": "linux", "cpu": "x64" }, "sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w=="], - - "vite/esbuild/@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.18.20", "", { "os": "none", "cpu": "x64" }, "sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A=="], - - "vite/esbuild/@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.18.20", "", { "os": "openbsd", "cpu": "x64" }, "sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg=="], - - "vite/esbuild/@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.18.20", "", { "os": "sunos", "cpu": "x64" }, "sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ=="], - - "vite/esbuild/@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.18.20", "", { "os": "win32", "cpu": "arm64" }, "sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg=="], - - "vite/esbuild/@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.18.20", "", { "os": "win32", "cpu": "ia32" }, "sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g=="], - - "vite/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.18.20", "", { "os": "win32", "cpu": "x64" }, "sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ=="], - "vitepress/shiki/@shikijs/engine-javascript": ["@shikijs/engine-javascript@2.5.0", "", { "dependencies": { "@shikijs/types": "2.5.0", "@shikijs/vscode-textmate": "^10.0.2", "oniguruma-to-es": "^3.1.0" } }, "sha512-VjnOpnQf8WuCEZtNUdjjwGUbtAVKuZkVQ/5cHy/tojVVRIRtlWMYVjyWhxOmIq05AlSOv72z7hRNRGVBgQOl0w=="], "vitepress/shiki/@shikijs/engine-oniguruma": ["@shikijs/engine-oniguruma@2.5.0", "", { "dependencies": { "@shikijs/types": "2.5.0", "@shikijs/vscode-textmate": "^10.0.2" } }, "sha512-pGd1wRATzbo/uatrCIILlAdFVKdxImWJGQ5rFiB5VZi2ve5xj3Ax9jny8QvkaV93btQEwR/rSz5ERFpC5mKNIw=="], @@ -4568,28 +4148,6 @@ "vitepress/vite/esbuild": ["esbuild@0.21.5", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.21.5", "@esbuild/android-arm": "0.21.5", "@esbuild/android-arm64": "0.21.5", "@esbuild/android-x64": "0.21.5", "@esbuild/darwin-arm64": "0.21.5", "@esbuild/darwin-x64": "0.21.5", "@esbuild/freebsd-arm64": "0.21.5", "@esbuild/freebsd-x64": "0.21.5", "@esbuild/linux-arm": "0.21.5", "@esbuild/linux-arm64": "0.21.5", "@esbuild/linux-ia32": "0.21.5", "@esbuild/linux-loong64": "0.21.5", "@esbuild/linux-mips64el": "0.21.5", "@esbuild/linux-ppc64": "0.21.5", "@esbuild/linux-riscv64": "0.21.5", "@esbuild/linux-s390x": "0.21.5", "@esbuild/linux-x64": "0.21.5", "@esbuild/netbsd-x64": "0.21.5", "@esbuild/openbsd-x64": "0.21.5", "@esbuild/sunos-x64": "0.21.5", "@esbuild/win32-arm64": "0.21.5", "@esbuild/win32-ia32": "0.21.5", "@esbuild/win32-x64": "0.21.5" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw=="], - "vitest-browser-svelte/vitest/@vitest/expect": ["@vitest/expect@3.2.4", "", { "dependencies": { "@types/chai": "^5.2.2", "@vitest/spy": "3.2.4", "@vitest/utils": "3.2.4", "chai": "^5.2.0", "tinyrainbow": "^2.0.0" } }, "sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig=="], - - "vitest-browser-svelte/vitest/@vitest/runner": ["@vitest/runner@3.2.4", "", { "dependencies": { "@vitest/utils": "3.2.4", "pathe": "^2.0.3", "strip-literal": "^3.0.0" } }, "sha512-oukfKT9Mk41LreEW09vt45f8wx7DordoWUZMYdY/cyAk7w5TWkTRCNZYF7sX7n2wB7jyGAl74OxgwhPgKaqDMQ=="], - - "vitest-browser-svelte/vitest/@vitest/snapshot": ["@vitest/snapshot@3.2.4", "", { "dependencies": { "@vitest/pretty-format": "3.2.4", "magic-string": "^0.30.17", "pathe": "^2.0.3" } }, "sha512-dEYtS7qQP2CjU27QBC5oUOxLE/v5eLkGqPE0ZKEIDGMs4vKWe7IjgLOeauHsR0D5YuuycGRO5oSRXnwnmA78fQ=="], - - "vitest-browser-svelte/vitest/@vitest/spy": ["@vitest/spy@3.2.4", "", { "dependencies": { "tinyspy": "^4.0.3" } }, "sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw=="], - - "vitest-browser-svelte/vitest/@vitest/utils": ["@vitest/utils@3.2.4", "", { "dependencies": { "@vitest/pretty-format": "3.2.4", "loupe": "^3.1.4", "tinyrainbow": "^2.0.0" } }, "sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA=="], - - "vitest-browser-svelte/vitest/chai": ["chai@5.3.3", "", { "dependencies": { "assertion-error": "^2.0.1", "check-error": "^2.1.1", "deep-eql": "^5.0.1", "loupe": "^3.1.0", "pathval": "^2.0.0" } }, "sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw=="], - - "vitest-browser-svelte/vitest/pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="], - - "vitest-browser-svelte/vitest/tinypool": ["tinypool@1.1.1", "", {}, "sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg=="], - - "vitest-browser-svelte/vitest/vite": ["vite@7.1.9", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.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", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-4nVGliEpxmhCL8DslSAUdxlB6+SMrhB0a1v5ijlh1xB1nEPuy1mxaHxysVucLHuWryAxLWg6a5ei+U4TLn/rFg=="], - - "vitest-browser-svelte/vitest/vite-node": ["vite-node@3.2.4", "", { "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" } }, "sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg=="], - - "vitest/vite/esbuild": ["esbuild@0.21.5", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.21.5", "@esbuild/android-arm": "0.21.5", "@esbuild/android-arm64": "0.21.5", "@esbuild/android-x64": "0.21.5", "@esbuild/darwin-arm64": "0.21.5", "@esbuild/darwin-x64": "0.21.5", "@esbuild/freebsd-arm64": "0.21.5", "@esbuild/freebsd-x64": "0.21.5", "@esbuild/linux-arm": "0.21.5", "@esbuild/linux-arm64": "0.21.5", "@esbuild/linux-ia32": "0.21.5", "@esbuild/linux-loong64": "0.21.5", "@esbuild/linux-mips64el": "0.21.5", "@esbuild/linux-ppc64": "0.21.5", "@esbuild/linux-riscv64": "0.21.5", "@esbuild/linux-s390x": "0.21.5", "@esbuild/linux-x64": "0.21.5", "@esbuild/netbsd-x64": "0.21.5", "@esbuild/openbsd-x64": "0.21.5", "@esbuild/sunos-x64": "0.21.5", "@esbuild/win32-arm64": "0.21.5", "@esbuild/win32-ia32": "0.21.5", "@esbuild/win32-x64": "0.21.5" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw=="], - "websocket/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="], "wrap-ansi-cjs/ansi-styles/color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="], @@ -4644,138 +4202,12 @@ "@expo/xcpretty/chalk/ansi-styles/color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="], - "@iconify/utils/local-pkg/pkg-types/confbox": ["confbox@0.2.2", "", {}, "sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ=="], - - "@iconify/utils/local-pkg/pkg-types/pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="], - "@istanbuljs/load-nyc-config/find-up/locate-path/p-locate": ["p-locate@4.1.0", "", { "dependencies": { "p-limit": "^2.2.0" } }, "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A=="], "@jest/transform/chalk/ansi-styles/color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="], "@jest/types/chalk/ansi-styles/color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="], - "@nostr-dev-kit/cache-nostr/tsup/chokidar/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], - - "@nostr-dev-kit/cache-nostr/tsup/chokidar/readdirp": ["readdirp@3.6.0", "", { "dependencies": { "picomatch": "^2.2.1" } }, "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA=="], - - "@nostr-dev-kit/cache-nostr/tsup/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.19.12", "", { "os": "aix", "cpu": "ppc64" }, "sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA=="], - - "@nostr-dev-kit/cache-nostr/tsup/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.19.12", "", { "os": "android", "cpu": "arm" }, "sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w=="], - - "@nostr-dev-kit/cache-nostr/tsup/esbuild/@esbuild/android-arm64": ["@esbuild/android-arm64@0.19.12", "", { "os": "android", "cpu": "arm64" }, "sha512-P0UVNGIienjZv3f5zq0DP3Nt2IE/3plFzuaS96vihvD0Hd6H/q4WXUGpCxD/E8YrSXfNyRPbpTq+T8ZQioSuPA=="], - - "@nostr-dev-kit/cache-nostr/tsup/esbuild/@esbuild/android-x64": ["@esbuild/android-x64@0.19.12", "", { "os": "android", "cpu": "x64" }, "sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew=="], - - "@nostr-dev-kit/cache-nostr/tsup/esbuild/@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.19.12", "", { "os": "darwin", "cpu": "arm64" }, "sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g=="], - - "@nostr-dev-kit/cache-nostr/tsup/esbuild/@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.19.12", "", { "os": "darwin", "cpu": "x64" }, "sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A=="], - - "@nostr-dev-kit/cache-nostr/tsup/esbuild/@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.19.12", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-4aRvFIXmwAcDBw9AueDQ2YnGmz5L6obe5kmPT8Vd+/+x/JMVKCgdcRwH6APrbpNXsPz+K653Qg8HB/oXvXVukA=="], - - "@nostr-dev-kit/cache-nostr/tsup/esbuild/@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.19.12", "", { "os": "freebsd", "cpu": "x64" }, "sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg=="], - - "@nostr-dev-kit/cache-nostr/tsup/esbuild/@esbuild/linux-arm": ["@esbuild/linux-arm@0.19.12", "", { "os": "linux", "cpu": "arm" }, "sha512-J5jPms//KhSNv+LO1S1TX1UWp1ucM6N6XuL6ITdKWElCu8wXP72l9MM0zDTzzeikVyqFE6U8YAV9/tFyj0ti+w=="], - - "@nostr-dev-kit/cache-nostr/tsup/esbuild/@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.19.12", "", { "os": "linux", "cpu": "arm64" }, "sha512-EoTjyYyLuVPfdPLsGVVVC8a0p1BFFvtpQDB/YLEhaXyf/5bczaGeN15QkR+O4S5LeJ92Tqotve7i1jn35qwvdA=="], - - "@nostr-dev-kit/cache-nostr/tsup/esbuild/@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.19.12", "", { "os": "linux", "cpu": "ia32" }, "sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA=="], - - "@nostr-dev-kit/cache-nostr/tsup/esbuild/@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.19.12", "", { "os": "linux", "cpu": "none" }, "sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA=="], - - "@nostr-dev-kit/cache-nostr/tsup/esbuild/@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.19.12", "", { "os": "linux", "cpu": "none" }, "sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w=="], - - "@nostr-dev-kit/cache-nostr/tsup/esbuild/@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.19.12", "", { "os": "linux", "cpu": "ppc64" }, "sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg=="], - - "@nostr-dev-kit/cache-nostr/tsup/esbuild/@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.19.12", "", { "os": "linux", "cpu": "none" }, "sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg=="], - - "@nostr-dev-kit/cache-nostr/tsup/esbuild/@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.19.12", "", { "os": "linux", "cpu": "s390x" }, "sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg=="], - - "@nostr-dev-kit/cache-nostr/tsup/esbuild/@esbuild/linux-x64": ["@esbuild/linux-x64@0.19.12", "", { "os": "linux", "cpu": "x64" }, "sha512-B71g1QpxfwBvNrfyJdVDexenDIt1CiDN1TIXLbhOw0KhJzE78KIFGX6OJ9MrtC0oOqMWf+0xop4qEU8JrJTwCg=="], - - "@nostr-dev-kit/cache-nostr/tsup/esbuild/@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.19.12", "", { "os": "none", "cpu": "x64" }, "sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA=="], - - "@nostr-dev-kit/cache-nostr/tsup/esbuild/@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.19.12", "", { "os": "openbsd", "cpu": "x64" }, "sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw=="], - - "@nostr-dev-kit/cache-nostr/tsup/esbuild/@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.19.12", "", { "os": "sunos", "cpu": "x64" }, "sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA=="], - - "@nostr-dev-kit/cache-nostr/tsup/esbuild/@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.19.12", "", { "os": "win32", "cpu": "arm64" }, "sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A=="], - - "@nostr-dev-kit/cache-nostr/tsup/esbuild/@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.19.12", "", { "os": "win32", "cpu": "ia32" }, "sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ=="], - - "@nostr-dev-kit/cache-nostr/tsup/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.19.12", "", { "os": "win32", "cpu": "x64" }, "sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA=="], - - "@nostr-dev-kit/cache-nostr/tsup/execa/get-stream": ["get-stream@6.0.1", "", {}, "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg=="], - - "@nostr-dev-kit/cache-nostr/tsup/execa/human-signals": ["human-signals@2.1.0", "", {}, "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw=="], - - "@nostr-dev-kit/cache-nostr/tsup/execa/is-stream": ["is-stream@2.0.1", "", {}, "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg=="], - - "@nostr-dev-kit/cache-nostr/tsup/execa/npm-run-path": ["npm-run-path@4.0.1", "", { "dependencies": { "path-key": "^3.0.0" } }, "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw=="], - - "@nostr-dev-kit/cache-nostr/tsup/execa/onetime": ["onetime@5.1.2", "", { "dependencies": { "mimic-fn": "^2.1.0" } }, "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg=="], - - "@nostr-dev-kit/cache-nostr/tsup/execa/signal-exit": ["signal-exit@3.0.7", "", {}, "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="], - - "@nostr-dev-kit/cache-nostr/tsup/execa/strip-final-newline": ["strip-final-newline@2.0.0", "", {}, "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA=="], - - "@nostr-dev-kit/cache-nostr/tsup/globby/slash": ["slash@3.0.0", "", {}, "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q=="], - - "@nostr-dev-kit/cache-sqlite-wasm/vitest/@vitest/runner/strip-literal": ["strip-literal@3.1.0", "", { "dependencies": { "js-tokens": "^9.0.1" } }, "sha512-8r3mkIM/2+PpjHoOtiAW8Rg3jJLHaV7xPwG+YRGrv6FP0wwk/toTpATxWYOW0BKdWwl82VT2tFYi5DlROa0Mxg=="], - - "@nostr-dev-kit/cache-sqlite-wasm/vitest/@vitest/spy/tinyspy": ["tinyspy@4.0.4", "", {}, "sha512-azl+t0z7pw/z958Gy9svOTuzqIk6xq+NSheJzn5MMWtWTFywIacg2wUlzKFGtt3cthx0r2SxMK0yzJOR0IES7Q=="], - - "@nostr-dev-kit/cache-sqlite-wasm/vitest/@vitest/utils/loupe": ["loupe@3.2.1", "", {}, "sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ=="], - - "@nostr-dev-kit/cache-sqlite-wasm/vitest/chai/assertion-error": ["assertion-error@2.0.1", "", {}, "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA=="], - - "@nostr-dev-kit/cache-sqlite-wasm/vitest/chai/check-error": ["check-error@2.1.1", "", {}, "sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw=="], - - "@nostr-dev-kit/cache-sqlite-wasm/vitest/chai/deep-eql": ["deep-eql@5.0.2", "", {}, "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q=="], - - "@nostr-dev-kit/cache-sqlite-wasm/vitest/chai/loupe": ["loupe@3.2.1", "", {}, "sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ=="], - - "@nostr-dev-kit/cache-sqlite-wasm/vitest/chai/pathval": ["pathval@2.0.1", "", {}, "sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ=="], - - "@nostr-dev-kit/ndk/nostr-tools/@noble/curves/@noble/hashes": ["@noble/hashes@1.3.2", "", {}, "sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ=="], - - "@nostr-dev-kit/ndk/vitest/@vitest/runner/strip-literal": ["strip-literal@3.1.0", "", { "dependencies": { "js-tokens": "^9.0.1" } }, "sha512-8r3mkIM/2+PpjHoOtiAW8Rg3jJLHaV7xPwG+YRGrv6FP0wwk/toTpATxWYOW0BKdWwl82VT2tFYi5DlROa0Mxg=="], - - "@nostr-dev-kit/ndk/vitest/@vitest/spy/tinyspy": ["tinyspy@4.0.4", "", {}, "sha512-azl+t0z7pw/z958Gy9svOTuzqIk6xq+NSheJzn5MMWtWTFywIacg2wUlzKFGtt3cthx0r2SxMK0yzJOR0IES7Q=="], - - "@nostr-dev-kit/ndk/vitest/@vitest/utils/loupe": ["loupe@3.2.1", "", {}, "sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ=="], - - "@nostr-dev-kit/ndk/vitest/chai/assertion-error": ["assertion-error@2.0.1", "", {}, "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA=="], - - "@nostr-dev-kit/ndk/vitest/chai/check-error": ["check-error@2.1.1", "", {}, "sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw=="], - - "@nostr-dev-kit/ndk/vitest/chai/deep-eql": ["deep-eql@5.0.2", "", {}, "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q=="], - - "@nostr-dev-kit/ndk/vitest/chai/loupe": ["loupe@3.2.1", "", {}, "sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ=="], - - "@nostr-dev-kit/ndk/vitest/chai/pathval": ["pathval@2.0.1", "", {}, "sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ=="], - - "@nostr-dev-kit/react/jsdom/cssstyle/@asamuzakjp/css-color": ["@asamuzakjp/css-color@3.2.0", "", { "dependencies": { "@csstools/css-calc": "^2.1.3", "@csstools/css-color-parser": "^3.0.9", "@csstools/css-parser-algorithms": "^3.0.4", "@csstools/css-tokenizer": "^3.0.3", "lru-cache": "^10.4.3" } }, "sha512-K1A6z8tS3XsmCMM86xoWdn7Fkdn9m6RSVtocUrJYIwZnFVkng/PvkEoWtOWmP+Scc6saYWHWZYbndEEXxl24jw=="], - - "@nostr-dev-kit/react/jsdom/tough-cookie/tldts": ["tldts@6.1.86", "", { "dependencies": { "tldts-core": "^6.1.86" }, "bin": { "tldts": "bin/cli.js" } }, "sha512-WMi/OQ2axVTf/ykqCQgXiIct+mSQDFdH2fkwhPwgEwvJ1kSzZRiinb0zF2Xb8u4+OqPChmyI6MEu4EezNJz+FQ=="], - - "@nostr-dev-kit/react/jsdom/whatwg-url/tr46": ["tr46@5.1.1", "", { "dependencies": { "punycode": "^2.3.1" } }, "sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw=="], - - "@nostr-dev-kit/svelte/vitest/@vitest/runner/strip-literal": ["strip-literal@3.1.0", "", { "dependencies": { "js-tokens": "^9.0.1" } }, "sha512-8r3mkIM/2+PpjHoOtiAW8Rg3jJLHaV7xPwG+YRGrv6FP0wwk/toTpATxWYOW0BKdWwl82VT2tFYi5DlROa0Mxg=="], - - "@nostr-dev-kit/svelte/vitest/@vitest/spy/tinyspy": ["tinyspy@4.0.4", "", {}, "sha512-azl+t0z7pw/z958Gy9svOTuzqIk6xq+NSheJzn5MMWtWTFywIacg2wUlzKFGtt3cthx0r2SxMK0yzJOR0IES7Q=="], - - "@nostr-dev-kit/svelte/vitest/@vitest/utils/loupe": ["loupe@3.2.1", "", {}, "sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ=="], - - "@nostr-dev-kit/svelte/vitest/chai/assertion-error": ["assertion-error@2.0.1", "", {}, "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA=="], - - "@nostr-dev-kit/svelte/vitest/chai/check-error": ["check-error@2.1.1", "", {}, "sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw=="], - - "@nostr-dev-kit/svelte/vitest/chai/deep-eql": ["deep-eql@5.0.2", "", {}, "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q=="], - - "@nostr-dev-kit/svelte/vitest/chai/loupe": ["loupe@3.2.1", "", {}, "sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ=="], - - "@nostr-dev-kit/svelte/vitest/chai/pathval": ["pathval@2.0.1", "", {}, "sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ=="], - "@react-native/babel-plugin-codegen/@react-native/codegen/glob/minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="], "@react-native/babel-plugin-codegen/@react-native/codegen/hermes-parser/hermes-estree": ["hermes-estree@0.23.1", "", {}, "sha512-eT5MU3f5aVhTqsfIReZ6n41X5sYn4IdQL0nvz6yO+MMlPxw49aSARHLg/MSehQftyjnrE8X6bYregzSumqc6cg=="], @@ -4834,50 +4266,6 @@ "@vitest/browser/@testing-library/dom/pretty-format/react-is": ["react-is@17.0.2", "", {}, "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w=="], - "@vitest/browser/vitest/@vitest/runner/strip-literal": ["strip-literal@3.1.0", "", { "dependencies": { "js-tokens": "^9.0.1" } }, "sha512-8r3mkIM/2+PpjHoOtiAW8Rg3jJLHaV7xPwG+YRGrv6FP0wwk/toTpATxWYOW0BKdWwl82VT2tFYi5DlROa0Mxg=="], - - "@vitest/browser/vitest/@vitest/spy/tinyspy": ["tinyspy@4.0.4", "", {}, "sha512-azl+t0z7pw/z958Gy9svOTuzqIk6xq+NSheJzn5MMWtWTFywIacg2wUlzKFGtt3cthx0r2SxMK0yzJOR0IES7Q=="], - - "@vitest/browser/vitest/chai/assertion-error": ["assertion-error@2.0.1", "", {}, "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA=="], - - "@vitest/browser/vitest/chai/check-error": ["check-error@2.1.1", "", {}, "sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw=="], - - "@vitest/browser/vitest/chai/deep-eql": ["deep-eql@5.0.2", "", {}, "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q=="], - - "@vitest/browser/vitest/chai/loupe": ["loupe@3.2.1", "", {}, "sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ=="], - - "@vitest/browser/vitest/chai/pathval": ["pathval@2.0.1", "", {}, "sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ=="], - - "@vitest/coverage-v8/vitest/@vitest/runner/strip-literal": ["strip-literal@3.1.0", "", { "dependencies": { "js-tokens": "^9.0.1" } }, "sha512-8r3mkIM/2+PpjHoOtiAW8Rg3jJLHaV7xPwG+YRGrv6FP0wwk/toTpATxWYOW0BKdWwl82VT2tFYi5DlROa0Mxg=="], - - "@vitest/coverage-v8/vitest/@vitest/spy/tinyspy": ["tinyspy@4.0.4", "", {}, "sha512-azl+t0z7pw/z958Gy9svOTuzqIk6xq+NSheJzn5MMWtWTFywIacg2wUlzKFGtt3cthx0r2SxMK0yzJOR0IES7Q=="], - - "@vitest/coverage-v8/vitest/@vitest/utils/loupe": ["loupe@3.2.1", "", {}, "sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ=="], - - "@vitest/coverage-v8/vitest/chai/assertion-error": ["assertion-error@2.0.1", "", {}, "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA=="], - - "@vitest/coverage-v8/vitest/chai/check-error": ["check-error@2.1.1", "", {}, "sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw=="], - - "@vitest/coverage-v8/vitest/chai/deep-eql": ["deep-eql@5.0.2", "", {}, "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q=="], - - "@vitest/coverage-v8/vitest/chai/loupe": ["loupe@3.2.1", "", {}, "sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ=="], - - "@vitest/coverage-v8/vitest/chai/pathval": ["pathval@2.0.1", "", {}, "sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ=="], - - "@vitest/ui/vitest/@vitest/runner/strip-literal": ["strip-literal@3.1.0", "", { "dependencies": { "js-tokens": "^9.0.1" } }, "sha512-8r3mkIM/2+PpjHoOtiAW8Rg3jJLHaV7xPwG+YRGrv6FP0wwk/toTpATxWYOW0BKdWwl82VT2tFYi5DlROa0Mxg=="], - - "@vitest/ui/vitest/@vitest/spy/tinyspy": ["tinyspy@4.0.4", "", {}, "sha512-azl+t0z7pw/z958Gy9svOTuzqIk6xq+NSheJzn5MMWtWTFywIacg2wUlzKFGtt3cthx0r2SxMK0yzJOR0IES7Q=="], - - "@vitest/ui/vitest/chai/assertion-error": ["assertion-error@2.0.1", "", {}, "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA=="], - - "@vitest/ui/vitest/chai/check-error": ["check-error@2.1.1", "", {}, "sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw=="], - - "@vitest/ui/vitest/chai/deep-eql": ["deep-eql@5.0.2", "", {}, "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q=="], - - "@vitest/ui/vitest/chai/loupe": ["loupe@3.2.1", "", {}, "sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ=="], - - "@vitest/ui/vitest/chai/pathval": ["pathval@2.0.1", "", {}, "sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ=="], - "babel-jest/chalk/ansi-styles/color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="], "babel-plugin-istanbul/test-exclude/minimatch/brace-expansion": ["brace-expansion@1.1.12", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="], @@ -4920,10 +4308,6 @@ "expo/babel-preset-expo/babel-plugin-syntax-hermes-parser/hermes-parser": ["hermes-parser@0.25.1", "", { "dependencies": { "hermes-estree": "0.25.1" } }, "sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA=="], - "is-git-dirty/execa/onetime/mimic-fn": ["mimic-fn@2.1.0", "", {}, "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg=="], - - "is-git-repository/execa/onetime/mimic-fn": ["mimic-fn@2.1.0", "", {}, "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg=="], - "jest-diff/chalk/ansi-styles/color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="], "jest-message-util/chalk/ansi-styles/color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="], @@ -4968,52 +4352,6 @@ "temp/rimraf/glob/minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="], - "vite-node/vite/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.21.5", "", { "os": "aix", "cpu": "ppc64" }, "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ=="], - - "vite-node/vite/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.21.5", "", { "os": "android", "cpu": "arm" }, "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg=="], - - "vite-node/vite/esbuild/@esbuild/android-arm64": ["@esbuild/android-arm64@0.21.5", "", { "os": "android", "cpu": "arm64" }, "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A=="], - - "vite-node/vite/esbuild/@esbuild/android-x64": ["@esbuild/android-x64@0.21.5", "", { "os": "android", "cpu": "x64" }, "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA=="], - - "vite-node/vite/esbuild/@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.21.5", "", { "os": "darwin", "cpu": "arm64" }, "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ=="], - - "vite-node/vite/esbuild/@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.21.5", "", { "os": "darwin", "cpu": "x64" }, "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw=="], - - "vite-node/vite/esbuild/@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.21.5", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g=="], - - "vite-node/vite/esbuild/@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.21.5", "", { "os": "freebsd", "cpu": "x64" }, "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ=="], - - "vite-node/vite/esbuild/@esbuild/linux-arm": ["@esbuild/linux-arm@0.21.5", "", { "os": "linux", "cpu": "arm" }, "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA=="], - - "vite-node/vite/esbuild/@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.21.5", "", { "os": "linux", "cpu": "arm64" }, "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q=="], - - "vite-node/vite/esbuild/@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.21.5", "", { "os": "linux", "cpu": "ia32" }, "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg=="], - - "vite-node/vite/esbuild/@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.21.5", "", { "os": "linux", "cpu": "none" }, "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg=="], - - "vite-node/vite/esbuild/@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.21.5", "", { "os": "linux", "cpu": "none" }, "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg=="], - - "vite-node/vite/esbuild/@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.21.5", "", { "os": "linux", "cpu": "ppc64" }, "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w=="], - - "vite-node/vite/esbuild/@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.21.5", "", { "os": "linux", "cpu": "none" }, "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA=="], - - "vite-node/vite/esbuild/@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.21.5", "", { "os": "linux", "cpu": "s390x" }, "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A=="], - - "vite-node/vite/esbuild/@esbuild/linux-x64": ["@esbuild/linux-x64@0.21.5", "", { "os": "linux", "cpu": "x64" }, "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ=="], - - "vite-node/vite/esbuild/@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.21.5", "", { "os": "none", "cpu": "x64" }, "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg=="], - - "vite-node/vite/esbuild/@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.21.5", "", { "os": "openbsd", "cpu": "x64" }, "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow=="], - - "vite-node/vite/esbuild/@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.21.5", "", { "os": "sunos", "cpu": "x64" }, "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg=="], - - "vite-node/vite/esbuild/@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.21.5", "", { "os": "win32", "cpu": "arm64" }, "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A=="], - - "vite-node/vite/esbuild/@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.21.5", "", { "os": "win32", "cpu": "ia32" }, "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA=="], - - "vite-node/vite/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.21.5", "", { "os": "win32", "cpu": "x64" }, "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw=="], - "vitepress/shiki/@shikijs/engine-javascript/oniguruma-to-es": ["oniguruma-to-es@3.1.1", "", { "dependencies": { "emoji-regex-xs": "^1.0.0", "regex": "^6.0.1", "regex-recursion": "^6.0.2" } }, "sha512-bUH8SDvPkH3ho3dvwJwfonjlQ4R80vjyvrU8YpxuROddv55vAEJrTuCuCVUhhsHbtlD9tGGbaNApGQckXhS8iQ=="], "vitepress/vite/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.21.5", "", { "os": "aix", "cpu": "ppc64" }, "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ=="], @@ -5062,68 +4400,6 @@ "vitepress/vite/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.21.5", "", { "os": "win32", "cpu": "x64" }, "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw=="], - "vitest-browser-svelte/vitest/@vitest/runner/strip-literal": ["strip-literal@3.1.0", "", { "dependencies": { "js-tokens": "^9.0.1" } }, "sha512-8r3mkIM/2+PpjHoOtiAW8Rg3jJLHaV7xPwG+YRGrv6FP0wwk/toTpATxWYOW0BKdWwl82VT2tFYi5DlROa0Mxg=="], - - "vitest-browser-svelte/vitest/@vitest/spy/tinyspy": ["tinyspy@4.0.4", "", {}, "sha512-azl+t0z7pw/z958Gy9svOTuzqIk6xq+NSheJzn5MMWtWTFywIacg2wUlzKFGtt3cthx0r2SxMK0yzJOR0IES7Q=="], - - "vitest-browser-svelte/vitest/@vitest/utils/loupe": ["loupe@3.2.1", "", {}, "sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ=="], - - "vitest-browser-svelte/vitest/chai/assertion-error": ["assertion-error@2.0.1", "", {}, "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA=="], - - "vitest-browser-svelte/vitest/chai/check-error": ["check-error@2.1.1", "", {}, "sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw=="], - - "vitest-browser-svelte/vitest/chai/deep-eql": ["deep-eql@5.0.2", "", {}, "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q=="], - - "vitest-browser-svelte/vitest/chai/loupe": ["loupe@3.2.1", "", {}, "sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ=="], - - "vitest-browser-svelte/vitest/chai/pathval": ["pathval@2.0.1", "", {}, "sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ=="], - - "vitest/vite/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.21.5", "", { "os": "aix", "cpu": "ppc64" }, "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ=="], - - "vitest/vite/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.21.5", "", { "os": "android", "cpu": "arm" }, "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg=="], - - "vitest/vite/esbuild/@esbuild/android-arm64": ["@esbuild/android-arm64@0.21.5", "", { "os": "android", "cpu": "arm64" }, "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A=="], - - "vitest/vite/esbuild/@esbuild/android-x64": ["@esbuild/android-x64@0.21.5", "", { "os": "android", "cpu": "x64" }, "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA=="], - - "vitest/vite/esbuild/@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.21.5", "", { "os": "darwin", "cpu": "arm64" }, "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ=="], - - "vitest/vite/esbuild/@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.21.5", "", { "os": "darwin", "cpu": "x64" }, "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw=="], - - "vitest/vite/esbuild/@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.21.5", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g=="], - - "vitest/vite/esbuild/@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.21.5", "", { "os": "freebsd", "cpu": "x64" }, "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ=="], - - "vitest/vite/esbuild/@esbuild/linux-arm": ["@esbuild/linux-arm@0.21.5", "", { "os": "linux", "cpu": "arm" }, "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA=="], - - "vitest/vite/esbuild/@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.21.5", "", { "os": "linux", "cpu": "arm64" }, "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q=="], - - "vitest/vite/esbuild/@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.21.5", "", { "os": "linux", "cpu": "ia32" }, "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg=="], - - "vitest/vite/esbuild/@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.21.5", "", { "os": "linux", "cpu": "none" }, "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg=="], - - "vitest/vite/esbuild/@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.21.5", "", { "os": "linux", "cpu": "none" }, "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg=="], - - "vitest/vite/esbuild/@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.21.5", "", { "os": "linux", "cpu": "ppc64" }, "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w=="], - - "vitest/vite/esbuild/@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.21.5", "", { "os": "linux", "cpu": "none" }, "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA=="], - - "vitest/vite/esbuild/@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.21.5", "", { "os": "linux", "cpu": "s390x" }, "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A=="], - - "vitest/vite/esbuild/@esbuild/linux-x64": ["@esbuild/linux-x64@0.21.5", "", { "os": "linux", "cpu": "x64" }, "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ=="], - - "vitest/vite/esbuild/@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.21.5", "", { "os": "none", "cpu": "x64" }, "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg=="], - - "vitest/vite/esbuild/@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.21.5", "", { "os": "openbsd", "cpu": "x64" }, "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow=="], - - "vitest/vite/esbuild/@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.21.5", "", { "os": "sunos", "cpu": "x64" }, "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg=="], - - "vitest/vite/esbuild/@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.21.5", "", { "os": "win32", "cpu": "arm64" }, "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A=="], - - "vitest/vite/esbuild/@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.21.5", "", { "os": "win32", "cpu": "ia32" }, "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA=="], - - "vitest/vite/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.21.5", "", { "os": "win32", "cpu": "x64" }, "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw=="], - "wrap-ansi-cjs/ansi-styles/color-convert/color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="], "wrap-ansi/ansi-styles/color-convert/color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="], @@ -5162,14 +4438,6 @@ "@jest/types/chalk/ansi-styles/color-convert/color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="], - "@nostr-dev-kit/cache-nostr/tsup/chokidar/readdirp/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], - - "@nostr-dev-kit/cache-nostr/tsup/execa/onetime/mimic-fn": ["mimic-fn@2.1.0", "", {}, "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg=="], - - "@nostr-dev-kit/react/jsdom/cssstyle/@asamuzakjp/css-color/lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="], - - "@nostr-dev-kit/react/jsdom/tough-cookie/tldts/tldts-core": ["tldts-core@6.1.86", "", {}, "sha512-Je6p7pkk+KMzMv2XXKmAE3McmolOQFdxkKw0R8EYNr7sELW46JqnNeTX8ybPiQgvg1ymCoF8LXs5fzFaZvJPTA=="], - "@react-native/babel-plugin-codegen/@react-native/codegen/glob/minimatch/brace-expansion": ["brace-expansion@1.1.12", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="], "@testing-library/dom/chalk/ansi-styles/color-convert/color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="], From 72aabc0331fd7d8c2b5769e1f0bbcdf65f454978 Mon Sep 17 00:00:00 2001 From: digitalbase Date: Tue, 14 Oct 2025 16:35:53 +0200 Subject: [PATCH 041/139] Remove unused docs-styles --- core/docs-styles.css | 37 ------------------------------------- 1 file changed, 37 deletions(-) delete mode 100644 core/docs-styles.css diff --git a/core/docs-styles.css b/core/docs-styles.css deleted file mode 100644 index 8dfe07744..000000000 --- a/core/docs-styles.css +++ /dev/null @@ -1,37 +0,0 @@ -html, -body { - font-family: - ui-sans-serif, - system-ui, - -apple-system, - BlinkMacSystemFont, - "Segoe UI", - Roboto, - "Helvetica Neue", - Arial, - "Noto Sans", - sans-serif, - "Apple Color Emoji", - "Segoe UI Emoji", - "Segoe UI Symbol", - "Noto Color Emoji"; - background: #222; -} - -header.tsd-page-toolbar .tsd-toolbar-contents { - height: auto; - padding: 1rem; -} - -header.tsd-page-toolbar a.title { - background-image: url("https://raw.githubusercontent.com/nvk/ndk.fyi/master/ndk.svg"); - width: 40px; - height: 40px; - display: block; - background-size: contain; - color: transparent; -} - -header.tsd-page-toolbar .tsd-toolbar-links a { - font-weight: bold; -} From 8577440f7d0a4d6d920f810018ba619c27147990 Mon Sep 17 00:00:00 2001 From: digitalbase Date: Tue, 14 Oct 2025 16:36:08 +0200 Subject: [PATCH 042/139] Remove license (will be done globally) --- cache-dexie/README.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/cache-dexie/README.md b/cache-dexie/README.md index 7a1d45e61..b0af13f04 100644 --- a/cache-dexie/README.md +++ b/cache-dexie/README.md @@ -22,7 +22,3 @@ const ndk = new NDK({cacheAdapter: dexieAdapter, ...other config options}); ``` ๐Ÿšจ Because Dexie only exists client-side, this cache adapter will not work in pure node.js environments. You'll need to make sure that you're using the right cache adapter in the right place (e.g. Redis on the backend, Dexie on the frontend). - -# License - -MIT From 099859dec79454eda0d41a76a39754fce9f89f34 Mon Sep 17 00:00:00 2001 From: digitalbase Date: Tue, 14 Oct 2025 16:36:19 +0200 Subject: [PATCH 043/139] Added changelog index page --- .vitepress/config.mts | 3 ++- docs/changelogs.md | 27 +++++++++++++++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) create mode 100644 docs/changelogs.md diff --git a/.vitepress/config.mts b/.vitepress/config.mts index 0e5d6df2c..5c57dff22 100644 --- a/.vitepress/config.mts +++ b/.vitepress/config.mts @@ -55,7 +55,7 @@ export default defineConfig({ { text: "Introduction", link: "/core/getting-started/introduction" }, { text: "Usage", link: "/core/getting-started/usage" }, { text: "Debugging", link: "/core/getting-started/debugging" }, - + { text: "Changelog", link: "/docs/changelogs.md" }, ], }, { @@ -102,6 +102,7 @@ export default defineConfig({ link: "/svelte/README", items: [ { text: "Blossom", link: "/svelte/blossom-upload" }, + { text: "Changelog", link: "/svelte/CHANGELOG.md" }, ] }, { diff --git a/docs/changelogs.md b/docs/changelogs.md new file mode 100644 index 000000000..37d44fe04 --- /dev/null +++ b/docs/changelogs.md @@ -0,0 +1,27 @@ +# Changelogs + +We maintain changelogs for each package in the multi-repo. Please consult the individual changelogs for more details. + +## Core + +* [@nostr-dev-kit/core](/core/README.md) -> [CHANGELOG.md](/core/CHANGELOG) + +### Extras +* [@nostr-dev-kit/blossom](/blossom/README.html) -> [CHANGELOG.md](/blossom/CHANGELOG) +* [@nostr-dev-kit/sessions](/sessions/README.html) -> [CHANGELOG.md](/sessions/CHANGELOG) +* [@nostr-dev-kit/sync](/sync) -> [CHANGELOG.md](/sync/CHANGELOG) +* [@nostr-dev-kit/wallet](/wallet/README.html) -> [CHANGELOG.md](/wallet/CHANGELOG) +* [@nostr-dev-kit/wot](/wot/README.html) -> [CHANGELOG.md](/wot/CHANGELOG) + +### Framework Integrations +* [@nostr-dev-kit/react](/react/README) -> [CHANGELOG.md](/react/CHANGELOG) +* [@nostr-dev-kit/svelte](/svelte/README) -> [CHANGELOG.md](/svelte/CHANGELOG) + +### Cache Adapters + +* [@nostr-dev-kit/cache-memory](/cache/memory/README.html) -> [CHANGELOG.md](/cache-memory/CHANGELOG) +* [@nostr-dev-kit/cache-nostr](/cache/nostr/README.html) -> [CHANGELOG.md](/cache-nostr/CHANGELOG) +* [@nostr-dev-kit/cache-redis](/cache/redis/README.html) -> [CHANGELOG.md](/cache-redis/CHANGELOG) +* [@nostr-dev-kit/cache-dexie](/cache/dexie/README.html) -> [CHANGELOG.md](/cache-dexie/CHANGELOG) +* [@nostr-dev-kit/cache-sqlite](/cache/sqlite/README.html) -> [CHANGELOG.md](/cache-sqlite/CHANGELOG) +* [@nostr-dev-kit/cache-sqlite-wasm](/cache/sqlite-wasm/README.html) -> [CHANGELOG.md](/cache-sqlite-wasm/CHANGELOG) From d32faecba50cfe7e3a77bfc58e63cb3a3aeae4e0 Mon Sep 17 00:00:00 2001 From: digitalbase Date: Tue, 14 Oct 2025 16:37:37 +0200 Subject: [PATCH 044/139] Update changelog --- docs/changelogs.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/changelogs.md b/docs/changelogs.md index 37d44fe04..d7417265f 100644 --- a/docs/changelogs.md +++ b/docs/changelogs.md @@ -6,18 +6,19 @@ We maintain changelogs for each package in the multi-repo. Please consult the in * [@nostr-dev-kit/core](/core/README.md) -> [CHANGELOG.md](/core/CHANGELOG) -### Extras +## Extras + * [@nostr-dev-kit/blossom](/blossom/README.html) -> [CHANGELOG.md](/blossom/CHANGELOG) * [@nostr-dev-kit/sessions](/sessions/README.html) -> [CHANGELOG.md](/sessions/CHANGELOG) * [@nostr-dev-kit/sync](/sync) -> [CHANGELOG.md](/sync/CHANGELOG) * [@nostr-dev-kit/wallet](/wallet/README.html) -> [CHANGELOG.md](/wallet/CHANGELOG) * [@nostr-dev-kit/wot](/wot/README.html) -> [CHANGELOG.md](/wot/CHANGELOG) -### Framework Integrations +## Framework Integrations * [@nostr-dev-kit/react](/react/README) -> [CHANGELOG.md](/react/CHANGELOG) * [@nostr-dev-kit/svelte](/svelte/README) -> [CHANGELOG.md](/svelte/CHANGELOG) -### Cache Adapters +## Cache Adapters * [@nostr-dev-kit/cache-memory](/cache/memory/README.html) -> [CHANGELOG.md](/cache-memory/CHANGELOG) * [@nostr-dev-kit/cache-nostr](/cache/nostr/README.html) -> [CHANGELOG.md](/cache-nostr/CHANGELOG) From fd511a051e2849afbdac474dcd15189960c8b894 Mon Sep 17 00:00:00 2001 From: digitalbase Date: Tue, 14 Oct 2025 21:36:24 +0200 Subject: [PATCH 045/139] Made all links absolute + removed rewrites --- .vitepress/config.mts | 58 ++++++------------- core/docs/fundamentals/events.md | 19 ++++++ .../signers.md | 0 core/docs/getting-started/introduction.md | 26 ++++----- core/docs/getting-started/usage.md | 2 +- core/docs/snippets/create_event.ts | 8 +++ core/docs/snippets/sign_event.ts | 9 +++ core/snippets/event/basic.md | 18 ------ docs/changelogs.md | 28 ++++----- docs/index.md | 2 +- 10 files changed, 83 insertions(+), 87 deletions(-) create mode 100644 core/docs/fundamentals/events.md rename core/docs/{getting-started => fundamentals}/signers.md (100%) create mode 100644 core/docs/snippets/create_event.ts create mode 100644 core/docs/snippets/sign_event.ts delete mode 100644 core/snippets/event/basic.md diff --git a/.vitepress/config.mts b/.vitepress/config.mts index 5c57dff22..2af970c10 100644 --- a/.vitepress/config.mts +++ b/.vitepress/config.mts @@ -14,29 +14,6 @@ export default defineConfig({ }, rewrites: { 'docs/:slug.md': ':slug.md', - 'cache-dexie/:slug.md': 'cache/dexie/:slug.md', - 'cache-memory/:slug.md': 'cache/memory/:slug.md', - 'cache-nostr/:slug.md': 'cache/nostr/:slug.md', - 'cache-redis/:slug.md': 'cache/redis/:slug.md', - 'cache-sqlite/:slug.md': 'cache/sqlite/:slug.md', - 'cache-sqlite-wasm/:slug.md': 'cache/sqlite-wasm/:slug.md', - 'cache-sqlite-wasm/docs/:slug.md': 'cache/sqlite-wasm/:slug.md', - 'core/docs/:slug*': 'core/:slug*', - 'core/docs/:subdir/:slug*': 'core/:subdir/:slug*', - 'sync/docs/:slug*': 'sync/:slug*', - 'blossom/:slug.md': 'blossom/:slug.md', - 'blossom/docs/:slug*': 'blossom/:slug*', - 'mobile/:slug.md': 'mobile/:slug.md', - 'mobile/docs/:slug*': 'mobile/:slug*', - 'sessions/:slug.md': 'sessions/:slug.md', - 'sessions/docs/:slug*': 'sessions/:slug*', - 'wallet/:slug.md': 'wallet/:slug.md', - 'wallet/docs/:slug*': 'wallet/docs/:slug*', - 'react/:slug.md': 'react/:slug.md', - 'react/docs/:slug*': 'react/:slug*', - 'svelte/:slug.md': 'svelte/:slug.md', - 'svelte/docs/:slug*': 'svelte/:slug*', - 'wot/:slug.md': 'wot/:slug.md' }, themeConfig: { // https://vitepress.dev/reference/default-theme-config @@ -44,7 +21,7 @@ export default defineConfig({ { text: "Home", link: "/" }, // { text: "API Reference", link: "/api/", target: "_blank" }, // { text: "Cookbook", link: "/cookbook/" }, - { text: "Snippets", link: "/core/snippets/" }, + { text: "Snippets", link: "/core/docs/snippets/" }, { text: "Wiki", link: "https://wikifreedia.xyz/?c=NDK", target: "_blank" }, ], @@ -52,9 +29,9 @@ export default defineConfig({ { text: "Getting Started", items: [ - { text: "Introduction", link: "/core/getting-started/introduction" }, - { text: "Usage", link: "/core/getting-started/usage" }, - { text: "Debugging", link: "/core/getting-started/debugging" }, + { text: "Introduction", link: "/core/docs/getting-started/introduction" }, + { text: "Usage", link: "/core/docs/getting-started/usage" }, + { text: "Debugging", link: "/core/docs/getting-started/debugging" }, { text: "Changelog", link: "/docs/changelogs.md" }, ], }, @@ -62,16 +39,17 @@ export default defineConfig({ text: "Fundamentals", collapsed: false, items: [ - { text: "Connecting", link: "/core/fundamentals/connecting" }, - { text: "Signers", link: "/core/getting-started/signers" }, - { text: "Publishing", link: "/core/tutorial/publishing" }, - { text: "Zaps", link: "/core/tutorial/zaps" }, - { text: "Local-first", link: "/core/tutorial/local-first" }, + { text: "Events", link: "/core/docs/fundamentals/events" }, + { text: "Connecting", link: "/core/docs/fundamentals/connecting" }, + { text: "Signers", link: "/core/docs/fundamentals/signers" }, + { text: "Publishing", link: "/core/docs/tutorial/publishing" }, + { text: "Zaps", link: "/core/docs/tutorial/zaps" }, + { text: "Local-first", link: "/core/docs/tutorial/local-first" }, { text: "Subscription Management", - link: "/core/tutorial/subscription-management", + link: "/core/docs/tutorial/subscription-management", }, - { text: "Mute Filtering", link: "/core/tutorial/mute-filtering" }, + { text: "Mute Filtering", link: "/core/docs/tutorial/mute-filtering" }, ], }, { @@ -155,27 +133,27 @@ export default defineConfig({ items: [ { text: "Signer Persistence", - link: "/core/tutorial/signer-persistence" + link: "/core/docs/tutorial/signer-persistence" }, { text: "Speed / Performance", - link: "/core/tutorial/speed" + link: "/core/docs/tutorial/speed" }, { text: "AI Guardrails", - link: "/core/advanced/ai-guardrails" + link: "/core/docs/advanced/ai-guardrails" }, { text: "Subscription Lifecycle", - link: "/core/advanced/subscription-internals" + link: "/core/docs/advanced/subscription-internals" }, { text: "Event Class Registration", - link: "/core/advanced/event-class-registration" + link: "/core/docs/advanced/event-class-registration" }, { text: "Relay Metadata Caching", - link: "/core/advanced/relay-metadata-caching" + link: "/core/docs/advanced/relay-metadata-caching" }, { text: "Sync & Negentropy", diff --git a/core/docs/fundamentals/events.md b/core/docs/fundamentals/events.md new file mode 100644 index 000000000..37e94cdb2 --- /dev/null +++ b/core/docs/fundamentals/events.md @@ -0,0 +1,19 @@ +# Events + +Events are at the heart of Nostr and the NDK framework. + +## Creating an event + +This is a basic example of creating an event. + +<<< @/core/docs/snippets/create_event.ts + +There is no need to fill in the event's `id`, `tags`, `pubkey`, `created_at`, `sig` -- when these are empty, NDK will automatically fill them in with the appropriate values. + +## Signing Events + +NDK uses the default signer `ndk.signer` to sign events. + +<<< @/core/docs/snippets/sign_event.ts + +Read more about signers in [the signer documentation](/core/docs/fundamentals/signers.md) \ No newline at end of file diff --git a/core/docs/getting-started/signers.md b/core/docs/fundamentals/signers.md similarity index 100% rename from core/docs/getting-started/signers.md rename to core/docs/fundamentals/signers.md diff --git a/core/docs/getting-started/introduction.md b/core/docs/getting-started/introduction.md index 9124af2b5..1b69a8d43 100644 --- a/core/docs/getting-started/introduction.md +++ b/core/docs/getting-started/introduction.md @@ -31,24 +31,24 @@ NDK is compatible with node v16+ For other functionality you might need additional packages: ### Extras -* [@nostr-dev-kit/blossom](/blossom/README.html): Blossom Protocol Support for assets -* [@nostr-dev-kit/sessions](/sessions/README.html): Session Management with Multi-Account support -* [@nostr-dev-kit/sync](/sync): Event synchronization using Negentropy -* [@nostr-dev-kit/wallet](/wallet/README.html): Support for WebLN, NWC, Cashu/eCash wallets -* [@nostr-dev-kit/wot](/wot/README.html): Web of Trust (WOT) utilities +* [@nostr-dev-kit/blossom](/blossom/README.md): Blossom Protocol Support for assets +* [@nostr-dev-kit/sessions](/sessions/README.md): Session Management with Multi-Account support +* [@nostr-dev-kit/sync](/sync/README.md): Event synchronization using Negentropy +* [@nostr-dev-kit/wallet](/wallet/README.md): Support for WebLN, NWC, Cashu/eCash wallets +* [@nostr-dev-kit/wot](/wot/README.md): Web of Trust (WOT) utilities ### Framework Integrations -* [@nostr-dev-kit/react](/react/README): Hooks and utilities to integrate Nostr into your React applications -* [@nostr-dev-kit/svelte](/svelte/README): Modern, performant, and beautiful Svelte 5 integration +* [@nostr-dev-kit/react](/react/README.md): Hooks and utilities to integrate Nostr into your React applications +* [@nostr-dev-kit/svelte](/svelte/README.md): Modern, performant, and beautiful Svelte 5 integration ### Cache Adapters These NDK adapters are used to store and retrieve data from a cache so relays do not need to be re-queried for the same data. -* [@nostr-dev-kit/cache-memory](/cache/memory/README.html): In-memory LRU cache adapter -* [@nostr-dev-kit/cache-nostr](/cache/nostr/README.html): Local Nostr relay cache adapter -* [@nostr-dev-kit/cache-redis](/cache/redis/README.html): A cache adapter for Redis -* [@nostr-dev-kit/cache-dexie](/cache/dexie/README.html): Dexie (IndexedDB, in browser database) adapter -* [@nostr-dev-kit/cache-sqlite](/cache/sqlite/README.html): SQLite (better-sqlite3) adapter -* [@nostr-dev-kit/cache-sqlite-wasm](/cache/sqlite-wasm/README.html): In browser (WASM) SQLite adapter +* [@nostr-dev-kit/cache-memory](/cache-memory/README.md): In-memory LRU cache adapter +* [@nostr-dev-kit/cache-nostr](/cache-nostr/README.md): Local Nostr relay cache adapter +* [@nostr-dev-kit/cache-redis](/cache-redis/README.md): A cache adapter for Redis +* [@nostr-dev-kit/cache-dexie](/cache-dexie/README.md): Dexie (IndexedDB, in browser database) adapter +* [@nostr-dev-kit/cache-sqlite](/cache-sqlite/README.md): SQLite (better-sqlite3) adapter +* [@nostr-dev-kit/cache-sqlite-wasm](/cache-sqlite-wasm/md): In browser (WASM) SQLite adapter diff --git a/core/docs/getting-started/usage.md b/core/docs/getting-started/usage.md index b1523b512..6967ee9ce 100644 --- a/core/docs/getting-started/usage.md +++ b/core/docs/getting-started/usage.md @@ -40,6 +40,6 @@ await ndk.connect(); Make sure to wait for the `connect()` promise to resolve before using NDK after which you can start interacting with relays. -Different ways to connect to relays are explained in the [connecting](/core/fundamentals/connecting) section. +Different ways to connect to relays are explained in the [connecting](/core/docs/fundamentals/connecting.md) section. diff --git a/core/docs/snippets/create_event.ts b/core/docs/snippets/create_event.ts new file mode 100644 index 000000000..543904251 --- /dev/null +++ b/core/docs/snippets/create_event.ts @@ -0,0 +1,8 @@ +import NDK, { NDKEvent, NDKKind } from "@nostr-dev-kit/ndk"; + +const ndk = new NDK(/* initialization options for the ndk singleton */); + +const event = new NDKEvent(ndk, { + kind: NDKKind.Text, + content: "Hello world", +}); diff --git a/core/docs/snippets/sign_event.ts b/core/docs/snippets/sign_event.ts new file mode 100644 index 000000000..efc8def1a --- /dev/null +++ b/core/docs/snippets/sign_event.ts @@ -0,0 +1,9 @@ +import NDK, { NDKEvent, NDKNip07Signer } from "@nostr-dev-kit/ndk"; + +const nip07signer = new NDKNip07Signer(); +const ndk = new NDK({ signer: nip07signer }); + +const event = new NDKEvent(ndk); +event.kind = 1; +event.content = "Hello world"; +await event.sign(); diff --git a/core/snippets/event/basic.md b/core/snippets/event/basic.md deleted file mode 100644 index ffc0368da..000000000 --- a/core/snippets/event/basic.md +++ /dev/null @@ -1,18 +0,0 @@ -# Basic Nostr Event generation - -NDK uses `NDKEvent` as the basic interface to generate and handle nostr events. - -## Generating a basic event - -```ts -import NDK, { NDKEvent, NDKKind } from "@nostr-dev-kit/ndk"; - -const ndk = new NDK(/* initialization options for the ndk singleton */); - -const event = new NDKEvent(ndk, { - kind: NDKKind.Text, - content: "Hello world", -}); -``` - -There is no need to fill in the event's `id`, `tags`, `pubkey`, `created_at`, `sig` -- when these are empty, NDK will automatically fill them in with the appropriate values. diff --git a/docs/changelogs.md b/docs/changelogs.md index d7417265f..7ee5e0161 100644 --- a/docs/changelogs.md +++ b/docs/changelogs.md @@ -4,25 +4,25 @@ We maintain changelogs for each package in the multi-repo. Please consult the in ## Core -* [@nostr-dev-kit/core](/core/README.md) -> [CHANGELOG.md](/core/CHANGELOG) +* [@nostr-dev-kit/core](/core/README.md) -> [CHANGELOG.md](/core/CHANGELOG.md) ## Extras -* [@nostr-dev-kit/blossom](/blossom/README.html) -> [CHANGELOG.md](/blossom/CHANGELOG) -* [@nostr-dev-kit/sessions](/sessions/README.html) -> [CHANGELOG.md](/sessions/CHANGELOG) -* [@nostr-dev-kit/sync](/sync) -> [CHANGELOG.md](/sync/CHANGELOG) -* [@nostr-dev-kit/wallet](/wallet/README.html) -> [CHANGELOG.md](/wallet/CHANGELOG) -* [@nostr-dev-kit/wot](/wot/README.html) -> [CHANGELOG.md](/wot/CHANGELOG) +* [@nostr-dev-kit/blossom](/blossom/README.html) -> [CHANGELOG.md](/blossom/CHANGELOG.md) +* [@nostr-dev-kit/sessions](/sessions/README.html) -> [CHANGELOG.md](/sessions/CHANGELOG.md) +* [@nostr-dev-kit/sync](/sync) -> [CHANGELOG.md](/sync/CHANGELOG.md) +* [@nostr-dev-kit/wallet](/wallet/README.html) -> [CHANGELOG.md](/wallet/CHANGELOG.md) +* [@nostr-dev-kit/wot](/wot/README.html) -> [CHANGELOG.md](/wot/CHANGELOG.md) ## Framework Integrations -* [@nostr-dev-kit/react](/react/README) -> [CHANGELOG.md](/react/CHANGELOG) -* [@nostr-dev-kit/svelte](/svelte/README) -> [CHANGELOG.md](/svelte/CHANGELOG) +* [@nostr-dev-kit/react](/react/README.md) -> [CHANGELOG.md](/react/CHANGELOG.md) +* [@nostr-dev-kit/svelte](/svelte/README.md) -> [CHANGELOG.md](/svelte/CHANGELOG.md) ## Cache Adapters -* [@nostr-dev-kit/cache-memory](/cache/memory/README.html) -> [CHANGELOG.md](/cache-memory/CHANGELOG) -* [@nostr-dev-kit/cache-nostr](/cache/nostr/README.html) -> [CHANGELOG.md](/cache-nostr/CHANGELOG) -* [@nostr-dev-kit/cache-redis](/cache/redis/README.html) -> [CHANGELOG.md](/cache-redis/CHANGELOG) -* [@nostr-dev-kit/cache-dexie](/cache/dexie/README.html) -> [CHANGELOG.md](/cache-dexie/CHANGELOG) -* [@nostr-dev-kit/cache-sqlite](/cache/sqlite/README.html) -> [CHANGELOG.md](/cache-sqlite/CHANGELOG) -* [@nostr-dev-kit/cache-sqlite-wasm](/cache/sqlite-wasm/README.html) -> [CHANGELOG.md](/cache-sqlite-wasm/CHANGELOG) +* [@nostr-dev-kit/cache-memory](/cache-memory/README.md) -> [CHANGELOG.md](/cache-memory/CHANGELOG.md) +* [@nostr-dev-kit/cache-nostr](/cache-nostr/README.md) -> [CHANGELOG.md](/cache-nostr/CHANGELOG.md) +* [@nostr-dev-kit/cache-redis](/cache-redis/README.md) -> [CHANGELOG.md](/cache-redis/CHANGELOG.md) +* [@nostr-dev-kit/cache-dexie](/cache-dexie/README.md) -> [CHANGELOG.md](/cache-dexie/CHANGELOG.md) +* [@nostr-dev-kit/cache-sqlite](/cache-sqlite/README.md) -> [CHANGELOG.md](/cache-sqlite/CHANGELOG.md) +* [@nostr-dev-kit/cache-sqlite-wasm](/cache-sqlite-wasm/README.md) -> [CHANGELOG.md](/cache-sqlite-wasm/CHANGELOG.md) diff --git a/docs/index.md b/docs/index.md index 7f3f1094c..2545adc54 100644 --- a/docs/index.md +++ b/docs/index.md @@ -8,7 +8,7 @@ hero: actions: - theme: brand text: Documentation - link: /core/getting-started/introduction + link: /core/docs/getting-started/introduction.md - theme: secondary text: Github Repository link: https://github.com/nostr-dev-kit/ndk/ From cf043ffec4e3ef8b031649abc26821f2bf9c46f4 Mon Sep 17 00:00:00 2001 From: digitalbase Date: Tue, 14 Oct 2025 22:27:14 +0200 Subject: [PATCH 046/139] Introduce events, publishing fundamental docs and moved those snippets to dedicated files --- core/docs/fundamentals/events.md | 27 +++++++++++- core/docs/fundamentals/publishing.md | 41 +++++++++++++++++++ core/docs/snippets/publish_event.ts | 9 ++++ core/docs/snippets/replace_event.ts | 17 ++++++++ .../sign_event_with_other_signers.ts} | 10 +---- core/docs/tutorial/publishing.md | 24 ----------- 6 files changed, 94 insertions(+), 34 deletions(-) create mode 100644 core/docs/fundamentals/publishing.md create mode 100644 core/docs/snippets/publish_event.ts create mode 100644 core/docs/snippets/replace_event.ts rename core/{snippets/event/signing-with-different-signers.md => docs/snippets/sign_event_with_other_signers.ts} (64%) delete mode 100644 core/docs/tutorial/publishing.md diff --git a/core/docs/fundamentals/events.md b/core/docs/fundamentals/events.md index 37e94cdb2..5d941baa0 100644 --- a/core/docs/fundamentals/events.md +++ b/core/docs/fundamentals/events.md @@ -10,10 +10,35 @@ This is a basic example of creating an event. There is no need to fill in the event's `id`, `tags`, `pubkey`, `created_at`, `sig` -- when these are empty, NDK will automatically fill them in with the appropriate values. +## Tagging users (or events) + +NDK automatically adds the appropriate tags for mentions in the content. + +If the user wants to mention a user or an event, NDK will automatically add the appropriate tags: + +<<< @/core/docs/snippets/tag_user.ts + +Calling `event.sign()` will finalize the event, adding the appropriate tags, The resulting event will look like: + +<<< @/core/docs/snippets/tag_user_result.json + ## Signing Events +> [!NOTE] +> Please note that the behavior of `.sign()` is assuming you have a valid signer instance. More about +> the different signers in the [signer documentation](/core/docs/fundamentals/signers.md). + + +### Default Signer + NDK uses the default signer `ndk.signer` to sign events. <<< @/core/docs/snippets/sign_event.ts -Read more about signers in [the signer documentation](/core/docs/fundamentals/signers.md) \ No newline at end of file +Read more about signers in [the signer documentation](/core/docs/fundamentals/signers.md) + +### Other signers + +You can specify the use of a different signer to sign with different pubkeys. + +<<< @/core/docs/snippets/sign_event_with_other_signers.ts diff --git a/core/docs/fundamentals/publishing.md b/core/docs/fundamentals/publishing.md new file mode 100644 index 000000000..12a91d880 --- /dev/null +++ b/core/docs/fundamentals/publishing.md @@ -0,0 +1,41 @@ +# Publishing Events + +NDK provides easy ways to publish events and manage the status of that event. + +> [!NOTE] +> Please note that the behavior of `.publish()` requires a valid signer and will only publish the events to the +configured relays. More about relay interaction in the [connecting documentation](/core/docs/fundamentals/connecting.md). + +## Publishing Events + +The easiest way to publish an event is to use the `publish()` method on the event object. + +<<< @/core/docs/snippets/publish_event.ts + +This will sign the event and send it to the network. + +## Publishing Replaceable Events + +Some events in Nostr allow for replacement. + +Kinds `0`, `3`, range `10000-19999`. Range `30000-39999` is dedicated for parameterized replaceable events, +which means that multiple events of the same kind under the same pubkey can exist and are differentiated via +their `d` tag. + +Since replaceable events depend on having a newer `created_at`, NDK provides a convenience method to reset `id`, `sig`, +and `created_at` to allow for easy replacement: `event.publishReplaceable()` + +<<< @/core/docs/snippets/replace_event.ts + +## Event Status Properties + +- `event.publishedToRelays` - Array of relay URLs where the event was successfully published +- `event.failedPublishesToRelays` - Map of relay URLs to their errors +- `event.publishRelayStatus` - Map of all relay URLs to their detailed status +- `event.wasPublishedTo(url)` - Check if successfully published to a specific relay +- `event.publishStatus` - Overall status: "pending", "success", or "error" +- `event.publishError` - Error if the overall publish failed + +## More + +Read more about the [local-first](local-first.md) mode of operation. \ No newline at end of file diff --git a/core/docs/snippets/publish_event.ts b/core/docs/snippets/publish_event.ts new file mode 100644 index 000000000..83ce08be3 --- /dev/null +++ b/core/docs/snippets/publish_event.ts @@ -0,0 +1,9 @@ +import NDK, { NDKEvent, NDKNip07Signer } from "@nostr-dev-kit/ndk"; + +const nip07signer = new NDKNip07Signer(); +const ndk = new NDK({ signer: nip07signer }); + +const event = new NDKEvent(ndk); +event.kind = 1; +event.content = "Hello world"; +await event.publish(); diff --git a/core/docs/snippets/replace_event.ts b/core/docs/snippets/replace_event.ts new file mode 100644 index 000000000..7a62d932b --- /dev/null +++ b/core/docs/snippets/replace_event.ts @@ -0,0 +1,17 @@ +import NDK, { NDKEvent, NDKKind } from "@nostr-dev-kit/ndk"; + +const ndk = new NDK(); +const event = new NDKEvent(ndk); +event.kind = NDKKind.Metadata; +event.content = JSON.stringify({ + name: "Johnny", + about: "I come from nowhere", +}); +// first publish +await event.publish(); + +// this will republish/broadcast the same event +await event.publish(); + +// this will create a new event and publish it +await event.publishReplaceable(); diff --git a/core/snippets/event/signing-with-different-signers.md b/core/docs/snippets/sign_event_with_other_signers.ts similarity index 64% rename from core/snippets/event/signing-with-different-signers.md rename to core/docs/snippets/sign_event_with_other_signers.ts index 1f5b5fffd..b2eafca7f 100644 --- a/core/snippets/event/signing-with-different-signers.md +++ b/core/docs/snippets/sign_event_with_other_signers.ts @@ -1,11 +1,4 @@ -# Signing events with different signers - -NDK uses the default signer `ndk.signer` to sign events. - -But you can specify the use of a different signer to sign with different pubkeys. - -```ts -import { NDKPrivateKeySigner, NDKEvent } from "@nostr-dev-kit/ndk"; +import { NDKEvent, NDKPrivateKeySigner } from "@nostr-dev-kit/ndk"; const signer1 = NDKPrivateKeySigner.generate(); const pubkey1 = signer1.pubkey; @@ -26,4 +19,3 @@ event2.content = "Hello world"; await event2.sign(signer2); event2.pubkey === pubkey2; // true -``` diff --git a/core/docs/tutorial/publishing.md b/core/docs/tutorial/publishing.md deleted file mode 100644 index 73d16c044..000000000 --- a/core/docs/tutorial/publishing.md +++ /dev/null @@ -1,24 +0,0 @@ -# Publishing Events - -## Optimistic publish lifecycle - -Read more about the [local-first](local-first.md) mode of operation. - -## Publishing Replaceable Events - -Some events in Nostr allow for replacement. - -Kinds `0`, `3`, range `10000-19999`. - -Range `30000-39999` is parameterized replaceable events, which means that multiple events of the same kind under the same pubkey can exist and are differentiated via their `d` tag. - -Since replaceable events depend on having a newer `created_at`, NDK provides a convenience method to reset `id`, `sig`, and `created_at` to allow for easy replacement: `event.publishReplaceable()` - -```ts -const existingEvent = await ndk.fetchEvent({ kinds: [0], authors: []}); // fetch the event to replace -existingEvent.tags.push( - [ "p", "fa984bd7dbb282f07e16e7ae87b26a2a7b9b90b7246a44771f0cf5ae58018f52" ] // follow a new user -); -existingEvent.publish(); // this will NOT work -existingEvent.publishReplaceable(); // this WILL work -``` From f2cdb5a1c07405e6a5eaf0bd6232fb65fc5a2d0b Mon Sep 17 00:00:00 2001 From: digitalbase Date: Tue, 14 Oct 2025 22:27:24 +0200 Subject: [PATCH 047/139] More examples --- core/docs/snippets/tag_user.ts | 9 ++++++ core/docs/snippets/tag_user_result.json | 9 ++++++ .../event/tagging-users-and-events.md | 32 ------------------- 3 files changed, 18 insertions(+), 32 deletions(-) create mode 100644 core/docs/snippets/tag_user.ts create mode 100644 core/docs/snippets/tag_user_result.json delete mode 100644 core/snippets/event/tagging-users-and-events.md diff --git a/core/docs/snippets/tag_user.ts b/core/docs/snippets/tag_user.ts new file mode 100644 index 000000000..7ac334689 --- /dev/null +++ b/core/docs/snippets/tag_user.ts @@ -0,0 +1,9 @@ +import NDK, { NDKEvent, NDKKind } from "@nostr-dev-kit/ndk"; + +const ndk = new NDK(); +const event = new NDKEvent(ndk, { + kind: NDKKind.Text, + content: + "Hello, nostr:npub1l2vyh47mk2p0qlsku7hg0vn29faehy9hy34ygaclpn66ukqp3afqutajft this is a test from an NDK snippet.", +}); +await event.sign(); diff --git a/core/docs/snippets/tag_user_result.json b/core/docs/snippets/tag_user_result.json new file mode 100644 index 000000000..016f3614e --- /dev/null +++ b/core/docs/snippets/tag_user_result.json @@ -0,0 +1,9 @@ +{ + "created_at": 1742904504, + "content": "Hello, nostr:npub1l2vyh47mk2p0qlsku7hg0vn29faehy9hy34ygaclpn66ukqp3afqutajft this is a test from an NDK snippet.", + "tags": [["p", "fa984bd7dbb282f07e16e7ae87b26a2a7b9b90b7246a44771f0cf5ae58018f52"]], + "kind": 1, + "pubkey": "cbf66fa8cf9877ba98cd218a96d77bed5abdbfd56fdd3d0393d7859d58a313fb", + "id": "26df08155ceb82de8995081bf63a36017cbfd3a616fe49820d8427d22e0af20f", + "sig": "eb6125248cf4375d650b13fa284e81f4270eaa8cb3cae6366ab8cda27dc99c1babe5b5a2782244a9673644f53efa72aba6973ac3fc5465cf334413d90f4ea1b0" +} diff --git a/core/snippets/event/tagging-users-and-events.md b/core/snippets/event/tagging-users-and-events.md deleted file mode 100644 index 1604e0108..000000000 --- a/core/snippets/event/tagging-users-and-events.md +++ /dev/null @@ -1,32 +0,0 @@ -# Tagging users and events - -NDK automatically adds the appropriate tags for mentions in the content. - -If the user wants to mention a user or an event, NDK will automatically add the appropriate tags: - -## Tagging a user - -```ts -import { NDKEvent, NDKKind } from "@nostr-dev-kit/ndk"; - -const event = new NDKEvent(ndk, { - kind: NDKKind.Text, - content: - "Hello, nostr:npub1l2vyh47mk2p0qlsku7hg0vn29faehy9hy34ygaclpn66ukqp3afqutajft this is a test from an NDK snippet.", -}); -await event.sign(); -``` - -Calling `event.sign()` will finalize the event, adding the appropriate tags, The resulting event will look like: - -```json -{ - "created_at": 1742904504, - "content": "Hello, nostr:npub1l2vyh47mk2p0qlsku7hg0vn29faehy9hy34ygaclpn66ukqp3afqutajft this is a test from an NDK snippet.", - "tags": [["p", "fa984bd7dbb282f07e16e7ae87b26a2a7b9b90b7246a44771f0cf5ae58018f52"]], - "kind": 1, - "pubkey": "cbf66fa8cf9877ba98cd218a96d77bed5abdbfd56fdd3d0393d7859d58a313fb", - "id": "26df08155ceb82de8995081bf63a36017cbfd3a616fe49820d8427d22e0af20f", - "sig": "eb6125248cf4375d650b13fa284e81f4270eaa8cb3cae6366ab8cda27dc99c1babe5b5a2782244a9673644f53efa72aba6973ac3fc5465cf334413d90f4ea1b0" -} -``` From 1f1a8b371f05a012be15256656d6f7a7f58aa101 Mon Sep 17 00:00:00 2001 From: digitalbase Date: Tue, 14 Oct 2025 22:27:44 +0200 Subject: [PATCH 048/139] Moved snippets from /core file into /docs --- .vitepress/config.mts | 4 +-- core/snippets/index.md => docs/snippets.md | 38 ++++++++++++++++++---- 2 files changed, 33 insertions(+), 9 deletions(-) rename core/snippets/index.md => docs/snippets.md (73%) diff --git a/.vitepress/config.mts b/.vitepress/config.mts index 2af970c10..6b6f81712 100644 --- a/.vitepress/config.mts +++ b/.vitepress/config.mts @@ -21,7 +21,7 @@ export default defineConfig({ { text: "Home", link: "/" }, // { text: "API Reference", link: "/api/", target: "_blank" }, // { text: "Cookbook", link: "/cookbook/" }, - { text: "Snippets", link: "/core/docs/snippets/" }, + { text: "Snippets", link: "/docs/snippets.md" }, { text: "Wiki", link: "https://wikifreedia.xyz/?c=NDK", target: "_blank" }, ], @@ -40,9 +40,9 @@ export default defineConfig({ collapsed: false, items: [ { text: "Events", link: "/core/docs/fundamentals/events" }, + { text: "Publishing", link: "/core/docs/fundamentals/publishing" }, { text: "Connecting", link: "/core/docs/fundamentals/connecting" }, { text: "Signers", link: "/core/docs/fundamentals/signers" }, - { text: "Publishing", link: "/core/docs/tutorial/publishing" }, { text: "Zaps", link: "/core/docs/tutorial/zaps" }, { text: "Local-first", link: "/core/docs/tutorial/local-first" }, { diff --git a/core/snippets/index.md b/docs/snippets.md similarity index 73% rename from core/snippets/index.md rename to docs/snippets.md index b72b4a309..0c90323df 100644 --- a/core/snippets/index.md +++ b/docs/snippets.md @@ -1,17 +1,41 @@ # Code Snippets -This section contains a growing collection of code snippets demonstrating how to perform specific tasks with NDK. Each snippet is focused on a single, targeted use case to help you quickly find solutions for common implementation needs. +This section contains a growing collection of code snippets demonstrating how to perform specific tasks with NDK. +Each snippet is focused on a single, targeted use case to help you find solutions for common implementation needs. -## Categories +Snippets are grouped by category. Some of them are listed in more than one category. + +## Events + +### Creating a basic event + +<<< @/core/docs/snippets/create_event.ts + +### Tagging users + +<<< @/core/docs/snippets/tag_user.ts + +### Signing events + +<<< @/core/docs/snippets/sign_event.ts + +## Signers + +### Signing events + +<<< @/core/docs/snippets/sign_event.ts + +### Different signers + +<<< @/core/docs/snippets/sign_event_with_other_signers.ts + + +## Not migrated yet -Snippets are organized into the following categories: - [User](./user/) - [Generate Keys](./user/generate-keys.md) - Generate a new key pair and obtain all formats (private key, public key, nsec, npub) - [Get Profile](./user/get-profile.md) - Fetch and handle user profile information -- [Event](./event/) - - [Basic](./event/basic.md) - Generate a basic Nostr event - - [Tagging Users and Events](./event/tagging-users-and-events.md) - Add tags to mention users and events - [Mobile](./mobile/) - [Basics] - [Initialize NDK + SQLite cache](./mobile/ndk/initializing-ndk.md) - Set up NDK with SQLite caching for mobile apps @@ -30,4 +54,4 @@ Snippets are organized into the following categories: - [Mock Relays](./testing/mock-relays.md) - Create and use mock relays for testing NDK applications - [Event Generation](./testing/event-generation.md) - Generate test events with different kinds and content - [Nutzap Testing](./testing/nutzap-testing.md) - Test Cashu token and Nutzap functionality - - [Relay Pool Testing](./testing/relay-pool-testing.md) - Test relay pool behavior and event handling + - [Relay Pool Testing](./testing/relay-pool-testing.md) - Test relay pool behavior and event handling \ No newline at end of file From e3cd700389e4e9e40a27848b3dbad8c32f6b38e1 Mon Sep 17 00:00:00 2001 From: digitalbase Date: Tue, 14 Oct 2025 22:27:55 +0200 Subject: [PATCH 049/139] Updated tsconfig so examples don't complain over path --- core/tsconfig.json | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/core/tsconfig.json b/core/tsconfig.json index 130035a59..d385cf731 100644 --- a/core/tsconfig.json +++ b/core/tsconfig.json @@ -1,6 +1,6 @@ { "compilerOptions": { - "baseUrl": ".", // Add baseUrl + "baseUrl": ".", "target": "ESNext", "module": "ESNext", "composite": false, @@ -16,11 +16,14 @@ "preserveWatchOutput": true, "skipLibCheck": true, "strict": true, - "types": ["vitest/globals"], // Add vitest globals + "types": ["vitest/globals"], "allowJs": true, "checkJs": true, "lib": ["dom", "dom.iterable", "esnext"], "emitDeclarationOnly": true, - "outDir": "lib" + "outDir": "lib", + "paths": { + "@nostr-dev-kit/ndk": ["src/index.ts"] + } } } From 5b418b1ca5a9702ebd70dbb107b64a85aaf0b67f Mon Sep 17 00:00:00 2001 From: digitalbase Date: Tue, 14 Oct 2025 22:28:04 +0200 Subject: [PATCH 050/139] Linked snippets to /docs/snippets.md --- biome.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/biome.json b/biome.json index 3286591e9..5ba8523cf 100644 --- a/biome.json +++ b/biome.json @@ -20,7 +20,7 @@ } }, "files": { - "includes": ["**/*.{js,ts,jsx,tsx,json}"], + "includes": ["**/*.{js,ts,jsx,tsx,json}", "!/core/docs/snippets"], "ignoreUnknown": true }, "vcs": { From f1608a572768dad3cbbb8d1252400fa73d15433b Mon Sep 17 00:00:00 2001 From: digitalbase Date: Wed, 15 Oct 2025 07:52:10 +0200 Subject: [PATCH 051/139] Fix build --- mobile/CHANGELOG.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/mobile/CHANGELOG.md b/mobile/CHANGELOG.md index d28419738..7e547225f 100644 --- a/mobile/CHANGELOG.md +++ b/mobile/CHANGELOG.md @@ -732,7 +732,6 @@ - c83166a: bump - 6e16e06: Enhance SQLite adapter to support decrypted events storage and retrieval. - import changes -- df73b9b: add component - Updated dependencies [c83166a] - Updated dependencies [5ab19ef] - Updated dependencies [6e16e06] @@ -746,7 +745,7 @@ ### Patch Changes -- add component +- add `EventContent` component ## 0.4.4 From f0c76487a879122e804f02b3c228537594beed72 Mon Sep 17 00:00:00 2001 From: digitalbase Date: Wed, 15 Oct 2025 08:45:13 +0200 Subject: [PATCH 052/139] Improve events docs: - add interest_event - link to additional snippets - link to NIPS where relevant - improve copy --- core/docs/fundamentals/events.md | 34 ++++++++++++++++++++-------- core/docs/snippets/interest_event.ts | 30 ++++++++++++++++++++++++ 2 files changed, 55 insertions(+), 9 deletions(-) create mode 100644 core/docs/snippets/interest_event.ts diff --git a/core/docs/fundamentals/events.md b/core/docs/fundamentals/events.md index 5d941baa0..bded35fea 100644 --- a/core/docs/fundamentals/events.md +++ b/core/docs/fundamentals/events.md @@ -1,33 +1,45 @@ # Events -Events are at the heart of Nostr and the NDK framework. +Events are at the heart of the Nostr protocol and described in [NIP-01.](https://nostr-nips.com/nip-01). To support +a wide range of functionality, Nostr comes with [a number of event types](https://nostr-nips.com/#event-kinds) but you +can also create your own. ## Creating an event -This is a basic example of creating an event. +This is the simplest example of creating a text note [`kind:1`](https://nostr-nips.com/nip-01#kinds) event. <<< @/core/docs/snippets/create_event.ts -There is no need to fill in the event's `id`, `tags`, `pubkey`, `created_at`, `sig` -- when these are empty, NDK will automatically fill them in with the appropriate values. +No need to fill in event's `id`, `tags`, `pubkey`, `created_at`, NDK will do that (if not set). ## Tagging users (or events) -NDK automatically adds the appropriate tags for mentions in the content. +Tags tell the protocol about related entities like mentioned users, relays, topics, other events, etc. Details about +tags can be found in the [tags section of NIP-01](https://nostr-nips.com/nip-01#tags) and in [the reference list of +standardized tags](https://nostr-nips.com/#standardized-tags). -If the user wants to mention a user or an event, NDK will automatically add the appropriate tags: +NDK automatically adds the appropriate tags for mentions in the content when a user or event is mentioned. <<< @/core/docs/snippets/tag_user.ts -Calling `event.sign()` will finalize the event, adding the appropriate tags, The resulting event will look like: +Calling `event.sign()` will finalize the event, adding the appropriate tags. + +The resulting event will look like: <<< @/core/docs/snippets/tag_user_result.json +## Interest event + +Interest events are used to tell the network about your interest in a particular topic. Those events and are making use +of the [NIP-51](https://nostr-nips.com/nip-51) specification `kind:10015` events. + +<<< @/core/docs/snippets/interest_event.ts + ## Signing Events > [!NOTE] -> Please note that the behavior of `.sign()` is assuming you have a valid signer instance. More about -> the different signers in the [signer documentation](/core/docs/fundamentals/signers.md). - +> Please note that the behavior of `.sign()` is assuming you have a valid signer instance. +> More about the different signers in the [signer documentation](/core/docs/fundamentals/signers.md). ### Default Signer @@ -42,3 +54,7 @@ Read more about signers in [the signer documentation](/core/docs/fundamentals/si You can specify the use of a different signer to sign with different pubkeys. <<< @/core/docs/snippets/sign_event_with_other_signers.ts + +## More examples + +Additional example snippets of events can be found in the [snippets directory](/docs/snippets.md). \ No newline at end of file diff --git a/core/docs/snippets/interest_event.ts b/core/docs/snippets/interest_event.ts new file mode 100644 index 000000000..207eb2f5a --- /dev/null +++ b/core/docs/snippets/interest_event.ts @@ -0,0 +1,30 @@ +import NDK, { NDKInterestList, NDKPrivateKeySigner } from "@nostr-dev-kit/ndk"; + +const ndk = new NDK({ + explicitRelayUrls: ["wss://relay.damus.io", "wss://relay.primal.net"], +}); + +// Create a signer (in production, use proper key management) +const signer = NDKPrivateKeySigner.generate(); +ndk.signer = signer; + +await ndk.connect(); + +// Create a new interest list +const interestList = new NDKInterestList(ndk); + +// Add individual interests +interestList.addInterest("nostr"); +interestList.addInterest("bitcoin"); +interestList.addInterest("technology"); +interestList.addInterest("privacy"); + +// Set a title for the list (optional) +interestList.title = "My Interests"; +interestList.description = "Topics I'm passionate about"; + +console.log("Has 'nostr'?", interestList.hasInterest("nostr")); +console.log("Has 'ethereum'?", interestList.hasInterest("ethereum")); + +// Publish the list (which also signs) +await interestList.publish(); From a8b6fc165d5aa82cbf1a007f809adc729f42ab1f Mon Sep 17 00:00:00 2001 From: digitalbase Date: Wed, 15 Oct 2025 09:21:56 +0200 Subject: [PATCH 053/139] Fix paths of home/changelog --- .vitepress/config.mts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.vitepress/config.mts b/.vitepress/config.mts index 6b6f81712..ad2e246f3 100644 --- a/.vitepress/config.mts +++ b/.vitepress/config.mts @@ -13,12 +13,12 @@ export default defineConfig({ } }, rewrites: { - 'docs/:slug.md': ':slug.md', + 'docs/index.md': 'index.md', }, themeConfig: { // https://vitepress.dev/reference/default-theme-config nav: [ - { text: "Home", link: "/" }, + { text: "Home", link: "/docs/index.md" }, // { text: "API Reference", link: "/api/", target: "_blank" }, // { text: "Cookbook", link: "/cookbook/" }, { text: "Snippets", link: "/docs/snippets.md" }, @@ -40,9 +40,9 @@ export default defineConfig({ collapsed: false, items: [ { text: "Events", link: "/core/docs/fundamentals/events" }, + { text: "Signers", link: "/core/docs/fundamentals/signers" }, { text: "Publishing", link: "/core/docs/fundamentals/publishing" }, { text: "Connecting", link: "/core/docs/fundamentals/connecting" }, - { text: "Signers", link: "/core/docs/fundamentals/signers" }, { text: "Zaps", link: "/core/docs/tutorial/zaps" }, { text: "Local-first", link: "/core/docs/tutorial/local-first" }, { From fbfa11419dfefae34e73c28011df53b2d150a9c5 Mon Sep 17 00:00:00 2001 From: digitalbase Date: Wed, 15 Oct 2025 09:28:05 +0200 Subject: [PATCH 054/139] Update some docs --- core/docs/fundamentals/publishing.md | 3 + core/docs/fundamentals/signers.md | 114 ++++++++++++++++++--------- 2 files changed, 79 insertions(+), 38 deletions(-) diff --git a/core/docs/fundamentals/publishing.md b/core/docs/fundamentals/publishing.md index 12a91d880..973b0bcd7 100644 --- a/core/docs/fundamentals/publishing.md +++ b/core/docs/fundamentals/publishing.md @@ -1,5 +1,8 @@ # Publishing Events +Publishing events means sending them to one or multiple relays as described in +[NIP-01](https://nostr-nips.com/nip-01#communication-between-clients-and-relays). + NDK provides easy ways to publish events and manage the status of that event. > [!NOTE] diff --git a/core/docs/fundamentals/signers.md b/core/docs/fundamentals/signers.md index f20840a0b..59617c454 100644 --- a/core/docs/fundamentals/signers.md +++ b/core/docs/fundamentals/signers.md @@ -1,43 +1,58 @@ # Signers -NDK uses signers _optionally_ passed in to sign events. Note that it is possible to use NDK without signing events (e.g. [to get someone's profile](https://github.com/nostr-dev-kit/ndk-cli/blob/master/src/commands/profile.ts)). +All events on the Nostr protocol are signed through a keypair +(described in [NIP-01](https://nostr-nips.com/nip-01#events-and-signatures)). -Signing adapters can be passed in when NDK is instantiated or later during runtime. +In NDK this is taken care of by the `NDKSigner` interface that can be passed in during initialization or later during +runtime. -### Using a NIP-07 browser extension (e.g. Alby, nos2x) +## Different Signing Methods -Instatiate NDK with a NIP-07 signer +### Browser Extensions + +A common way to use NDK is to use a browser extension which is described in [NIP-07](https://nostr-nips.com/nip-07). +This mechanism allows the user to sign events with a browser extension to not share their private key +with the application. + +The most used browser extensions are [Nos2x](https://github.com/fiatjaf/nos2x) and [Alby](https://getalby.com/alby-extension). ```ts // Import the package, NIP-07 signer and NDK event import NDK, { NDKEvent, NDKNip07Signer } from "@nostr-dev-kit/ndk"; -const nip07signer = new NDKNip07Signer(); -const ndk = new NDK({ signer: nip07signer }); +const signer = new NDKNip07Signer(); +const ndk = new NDK({ signer }); ``` -NDK can now ask for permission, via their NIP-07 extension, to... +Anytime you call `sign()` or `publish()` on an [NDK Event](/core/docs/fundamentals/events.html) the browser +extension will prompt the user to sign the event. -**Read the user's public key** +### Private Key Signer -```ts -nip07signer.user().then(async (user) => { - if (!!user.npub) { - console.log("Permission granted to read their public key:", user.npub); - } -}); -``` +NDK provides `NDKPrivateKeySigner` for managing in-memory private keys. This is useful for development, testing, or applications that manage keys locally. + +> [!WARNING] +> We strongly recommend not using this in production. Requiring users to share their private key is a security +> risk and should be avoided in favor of using [a browser extension](/core/docs/fundamentals/signers.html#browser-extensions) +> or [a remote signer](/core/docs/fundamentals/signers.html#remote-signer). -**Sign & publish events** +The private key signer takes the private key in the `nsec` format. ```ts -const ndkEvent = new NDKEvent(ndk); -ndkEvent.kind = 1; -ndkEvent.content = "Hello, world!"; -ndkEvent.publish(); // This will trigger the extension to ask the user to confirm signing. +import { NDKPrivateKeySigner } from "@nostr-dev-kit/ndk"; + +// Generate a new private key +const signer = NDKPrivateKeySigner.generate(); +console.log("nsec:", signer.nsec); +console.log("npub:", signer.npub); ``` -### Using a Remote Signer (NIP-46) +This library can also [help with generating new keys](/core/docs/fundamentals/signers.html#generate-keys). + + +### Remote Signer + +(NIP-46) #### bunker:// * Create a `NDKNip46Signer`, optionally providing the local signer if you are restoring a connection that was already generated in your app: @@ -79,23 +94,6 @@ console.log("Welcome", user.npub); // if you didn't have a localNsec you should store it for future sessions of your app save(signer.localSigner.nsec) ``` -### Using a Private Key Signer - -NDK provides `NDKPrivateKeySigner` for managing in-memory private keys. This is useful for development, testing, or applications that manage keys locally. - -#### Basic Usage - -```ts -import { NDKPrivateKeySigner } from "@nostr-dev-kit/ndk"; - -// Generate a new private key -const signer = NDKPrivateKeySigner.generate(); -console.log("nsec:", signer.nsec); -console.log("npub:", signer.npub); - -// Or load from an existing key -const signer = new NDKPrivateKeySigner("nsec1..."); -``` #### Password-Protected Keys (NIP-49) @@ -148,3 +146,43 @@ const ncryptsec = nip49.encrypt(privateKeyBytes, password); // Decrypt to raw bytes const decryptedBytes = nip49.decrypt(ncryptsec, password); ``` + +## Sign Events + +Once the signer is initialized, you can use it to sign and [publish](/core/docs/fundamentals/publishing.html) events: + +```ts +const ndkEvent = new NDKEvent(ndk); +ndkEvent.kind = 1; +ndkEvent.content = "Hello, world!"; +await ndkEvent.sign(); +``` + +## Read Public key + + +**Read the user's public key** + +```ts +nip07signer.user().then(async (user) => { + if (!!user.npub) { + console.log("Permission granted to read their public key:", user.npub); + } +}); +``` + +## Generate Keys + +Perhaps the only time we really want you to use the `NDKPrivateKeySigner` is to help you generate new keys as the signer +provides helper methods to do just that: + +```ts +import { NDKPrivateKeySigner } from "@nostr-dev-kit/ndk"; + +// Generate a new private key +const signer = NDKPrivateKeySigner.generate(); +console.log("nsec:", signer.nsec); +console.log("npub:", signer.npub); +``` + + From 15586c915184772bbad5c5e0dddef3d0c5f0786a Mon Sep 17 00:00:00 2001 From: digitalbase Date: Wed, 15 Oct 2025 17:20:01 +0200 Subject: [PATCH 055/139] Add .idea to gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 6e70b6f33..bb6aa2f1f 100644 --- a/.gitignore +++ b/.gitignore @@ -25,6 +25,7 @@ _local_ docs/.vitepress/cache docs/.vitepress/dist .ngit +.idea **/.repomix-output.txt cursor-tools.config.json coverage From 2fc7c5d0c0b4ec136c2e8c98738219539a0d76bd Mon Sep 17 00:00:00 2001 From: digitalbase Date: Wed, 15 Oct 2025 17:39:54 +0200 Subject: [PATCH 056/139] Update bun lock (installing from 1.3.0) --- bun.lock | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/bun.lock b/bun.lock index c277e6e8d..6128f3078 100644 --- a/bun.lock +++ b/bun.lock @@ -18,9 +18,9 @@ }, "blossom": { "name": "@nostr-dev-kit/blossom", - "version": "6.0.4", + "version": "6.1.0", "dependencies": { - "@nostr-dev-kit/ndk": "^2.17.6", + "@nostr-dev-kit/ndk": "^2.17.7", }, "devDependencies": { "@types/node": "^24.7.0", @@ -29,7 +29,7 @@ "vitest": "^3.2.4", }, "peerDependencies": { - "@nostr-dev-kit/ndk": "^2.17.6", + "@nostr-dev-kit/ndk": "^2.17.7", }, }, "cache-dexie": { @@ -106,19 +106,19 @@ }, "cache-sqlite": { "name": "@nostr-dev-kit/cache-sqlite", - "version": "6.0.0", + "version": "6.1.0", "dependencies": { "better-sqlite3": "^12.4.1", }, "devDependencies": { - "@nostr-dev-kit/ndk": "^2.17.6", + "@nostr-dev-kit/ndk": "^2.17.7", "@types/better-sqlite3": "^7.6.8", "tsup": "^8.4.0", "typescript": "^5.9.3", "vitest": "^3.2.4", }, "peerDependencies": { - "@nostr-dev-kit/ndk": "^2.17.6", + "@nostr-dev-kit/ndk": "^2.17.7", }, }, "cache-sqlite-wasm": { @@ -138,7 +138,7 @@ }, "core": { "name": "@nostr-dev-kit/ndk", - "version": "2.17.6", + "version": "2.17.7", "dependencies": { "@codesandbox/sandpack-client": "^2.19.8", "@noble/curves": "^1.6.0", @@ -172,7 +172,7 @@ }, "messages": { "name": "@nostr-dev-kit/messages", - "version": "0.1.3", + "version": "0.1.0", "dependencies": { "eventemitter3": "^5.0.1", }, @@ -258,12 +258,12 @@ }, "svelte": { "name": "@nostr-dev-kit/svelte", - "version": "2.0.12", + "version": "2.1.0", "dependencies": { "@nostr-dev-kit/cache-sqlite-wasm": "^0.8.1", - "@nostr-dev-kit/ndk": "^2.17.6", + "@nostr-dev-kit/ndk": "^2.17.7", "@nostr-dev-kit/sessions": "^0.6.1", - "@nostr-dev-kit/sync": "^0.3.4", + "@nostr-dev-kit/sync": "^0.3.5", "@nostr-dev-kit/wallet": "^0.8.6", "@nostr-dev-kit/wot": "^0.3.6", "@tiptap/core": "^2.6.6", @@ -303,15 +303,15 @@ "vitest-browser-svelte": "^1.1.0", }, "peerDependencies": { - "@nostr-dev-kit/ndk": "^2.17.6", + "@nostr-dev-kit/ndk": "^2.17.7", "svelte": "^5.39.9", }, }, "sync": { "name": "@nostr-dev-kit/sync", - "version": "0.3.4", + "version": "0.3.5", "dependencies": { - "@nostr-dev-kit/ndk": "^2.17.6", + "@nostr-dev-kit/ndk": "^2.17.7", "nostr-tools": "^2.17.0", "tseep": "^1.3.1", }, From 3ddde64f19ebb47fe9e51a42f6836ceb79665146 Mon Sep 17 00:00:00 2001 From: digitalbase Date: Wed, 15 Oct 2025 17:52:23 +0200 Subject: [PATCH 057/139] Doc build command --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 842d2bec3..0d73002fd 100755 --- a/package.json +++ b/package.json @@ -33,8 +33,8 @@ "cs:pub": "bun cs:check && changeset publish", "cs:ver": "changeset version && bunx syncpack fix-mismatches", "dev": "turbo dev --no-cache --continue", - "docs:build": "vitepress build docs", - "docs:dev": "npx vitepress dev docs", + "docs:build": "vitepress build", + "docs:dev": "npx vitepress dev", "docs:preview": "vitepress preview", "format": "biome format --write .", "lint": "biome check .", From 6e73900d2dd037b08badc02c6979d32dd3969cff Mon Sep 17 00:00:00 2001 From: digitalbase Date: Wed, 15 Oct 2025 18:16:38 +0200 Subject: [PATCH 058/139] Add about nostr + contributing guidelines --- .vitepress/config.mts | 16 ++++-- core/docs/about-nostr.md | 33 +++++++++++++ .../{introduction.md => installing.md} | 0 core/docs/introduction.md | 49 +++++++++++++++++++ docs/changelogs.md | 2 +- docs/contributing.md | 13 +++++ docs/index.md | 2 +- 7 files changed, 109 insertions(+), 6 deletions(-) create mode 100644 core/docs/about-nostr.md rename core/docs/getting-started/{introduction.md => installing.md} (100%) create mode 100644 core/docs/introduction.md create mode 100644 docs/contributing.md diff --git a/.vitepress/config.mts b/.vitepress/config.mts index ad2e246f3..d021ec024 100644 --- a/.vitepress/config.mts +++ b/.vitepress/config.mts @@ -1,4 +1,4 @@ -import { defineConfig } from "vitepress"; +import {defineConfig} from "vitepress"; // https://vitepress.dev/reference/site-config export default defineConfig({ @@ -19,6 +19,7 @@ export default defineConfig({ // https://vitepress.dev/reference/default-theme-config nav: [ { text: "Home", link: "/docs/index.md" }, + {text: "About Nostr", link: "/core/docs/about-nostr"}, // { text: "API Reference", link: "/api/", target: "_blank" }, // { text: "Cookbook", link: "/cookbook/" }, { text: "Snippets", link: "/docs/snippets.md" }, @@ -26,13 +27,20 @@ export default defineConfig({ ], sidebar: [ + { + text: "Nostr Development Kit", + items: [ + {text: "Introduction", link: "/core/docs/introduction"}, + {text: "Contributing", link: "/docs/contributing.md"}, + {text: "Changelog", link: "/docs/changelogs.md"}, + ], + }, { text: "Getting Started", items: [ - { text: "Introduction", link: "/core/docs/getting-started/introduction" }, + {text: "Installing", link: "/core/docs/getting-started/installing"}, + {text: "Debugging", link: "/core/docs/getting-started/debugging"}, { text: "Usage", link: "/core/docs/getting-started/usage" }, - { text: "Debugging", link: "/core/docs/getting-started/debugging" }, - { text: "Changelog", link: "/docs/changelogs.md" }, ], }, { diff --git a/core/docs/about-nostr.md b/core/docs/about-nostr.md new file mode 100644 index 000000000..bd8341f72 --- /dev/null +++ b/core/docs/about-nostr.md @@ -0,0 +1,33 @@ +# About Nostr + +Nostr (Notes and Other Stuff Transmitted by Relays) is an open, censorship-resistant protocol for publishing and +subscribing to events. Itโ€™s not a platform or companyโ€”it's a simple, extensible standard that anyone can implement. + +## Core Concepts + +- Identities with keys: + - You are your keys. A private key signs your messages; a public key identifies you. + - No accounts, emails, or servers required. +- Events: + - The atomic unit of data (notes, profiles, likes, zaps, etc.). + - Each event is a JSON object signed by the creatorโ€™s private key. +- Relays: + - Dumb servers that store and forward events. + - You can publish to many relays and read from many relays. + - Relays donโ€™t authenticate; they verify signatures and apply their own policies. +- Clients: + - Apps that let you create, sign, publish, and read events. + - You can use multiple clients with the same keysโ€”your identity is portable. + +## Why Nostr? + +- **Censorship resistance**: No single relay or company can silence you. +- **Portability**: Your identity and data travel with your keys. +- **Interoperability**: One protocol, many appsโ€”social, chat, marketplaces, media. +- **Simplicity**: Minimal spec, easy to implement, composable extensions. + +## Read more + +- [The nostr protocol](https://github.com/nostr-protocol/nostr) +- [Network Implementation Proposals (NIPs)](https://github.com/nostr-protocol/nips) +- [Awesome Nostr](https://github.com/aljazceru/awesome-nostr) \ No newline at end of file diff --git a/core/docs/getting-started/introduction.md b/core/docs/getting-started/installing.md similarity index 100% rename from core/docs/getting-started/introduction.md rename to core/docs/getting-started/installing.md diff --git a/core/docs/introduction.md b/core/docs/introduction.md new file mode 100644 index 000000000..583df3ecf --- /dev/null +++ b/core/docs/introduction.md @@ -0,0 +1,49 @@ +# NDK (Nostr Development Kit) + +NDK is a TypeScript/JavaScript library that simplifies building Nostr clients, relays, and related applications. + +## Features + +- Outbox model support +- Relay connection pool with automatic reconnection and failover +- Flexible subscription API with caching, batching, and auto-closing +- Event creation, validation, and wrappers for major NIPs (e.g., NIP-01, NIP-04, NIP-07, NIP-18, NIP-49, NIP-57, NIP-60, + NIP-61) +- Signer adapters: private key, encrypted keys (NIP-49), browser extension (NIP-07), remote signing (NIP-46) +- Pluggable cache adapters (Redis, Dexie, SQLite, etc.) +- Data Vending Machine support (NIP-90) +- Zap utilities (NIP-57, NIP-61) +- Threading, event kinds, and utility functions (URL normalization, metadata tags, filters) +- Modular design with many pluggable packages for different frameworks (Mobile, Svelte 4 and 5, React) + +## Multi-Repo + +NDK is a monorepo with different packages. The main package is `@nostr-dev-kit/core` and contains the core +functionality. + +For other functionality you might need additional packages: + +### Extras + +* [@nostr-dev-kit/blossom](/blossom/README.md): Blossom Protocol Support for assets +* [@nostr-dev-kit/sessions](/sessions/README.md): Session Management with Multi-Account support +* [@nostr-dev-kit/sync](/sync/README.md): Event synchronization using Negentropy +* [@nostr-dev-kit/wallet](/wallet/README.md): Support for WebLN, NWC, Cashu/eCash wallets +* [@nostr-dev-kit/wot](/wot/README.md): Web of Trust (WOT) utilities + +### Framework Integrations + +* [@nostr-dev-kit/react](/react/README.md): Hooks and utilities to integrate Nostr into your React applications +* [@nostr-dev-kit/svelte](/svelte/README.md): Modern, performant, and beautiful Svelte 5 integration + +### Cache Adapters + +These NDK adapters are used to store and retrieve data from a cache so relays do not need to be +re-queried for the same data. + +* [@nostr-dev-kit/cache-memory](/cache-memory/README.md): In-memory LRU cache adapter +* [@nostr-dev-kit/cache-nostr](/cache-nostr/README.md): Local Nostr relay cache adapter +* [@nostr-dev-kit/cache-redis](/cache-redis/README.md): A cache adapter for Redis +* [@nostr-dev-kit/cache-dexie](/cache-dexie/README.md): Dexie (IndexedDB, in browser database) adapter +* [@nostr-dev-kit/cache-sqlite](/cache-sqlite/README.md): SQLite (better-sqlite3) adapter +* [@nostr-dev-kit/cache-sqlite-wasm](/cache-sqlite-wasm/md): In browser (WASM) SQLite adapter diff --git a/docs/changelogs.md b/docs/changelogs.md index 7ee5e0161..d229fc85d 100644 --- a/docs/changelogs.md +++ b/docs/changelogs.md @@ -1,6 +1,6 @@ # Changelogs -We maintain changelogs for each package in the multi-repo. Please consult the individual changelogs for more details. +We maintain changelogs for each package in the multi-repo. Consult the individual changelogs for more details. ## Core diff --git a/docs/contributing.md b/docs/contributing.md new file mode 100644 index 000000000..8026c9aa3 --- /dev/null +++ b/docs/contributing.md @@ -0,0 +1,13 @@ +# Contributing + +We work in GitHub using pull requests (PRs). + +- Fork [the repo](https://github.com/nostr-dev-kit/ndk/issues), create a feature branch, make your changes. +- Keep PRs small and focused; include a clear description and rationale. +- Ensure builds/tests pass and follow the projectโ€™s style. +- Open a PR to the main branch; reference related issues. +- Be responsive to review feedback; update your PR as needed. + +Bugs can be filed on the [issue tracker](https://github.com/nostr-dev-kit/ndk/issues). + +Thank you for contributing! \ No newline at end of file diff --git a/docs/index.md b/docs/index.md index 2545adc54..7624ed4e1 100644 --- a/docs/index.md +++ b/docs/index.md @@ -8,7 +8,7 @@ hero: actions: - theme: brand text: Documentation - link: /core/docs/getting-started/introduction.md + link: /core/docs/introduction.html - theme: secondary text: Github Repository link: https://github.com/nostr-dev-kit/ndk/ From 39f9ca790352f7e589f59f52687c27a71ea9c202 Mon Sep 17 00:00:00 2001 From: digitalbase Date: Wed, 15 Oct 2025 18:29:21 +0200 Subject: [PATCH 059/139] Move advanced signing docs into signers.md Use code focus blocks where it makes sense --- core/docs/fundamentals/events.md | 11 ----------- core/docs/fundamentals/signers.md | 8 +++++++- core/docs/snippets/publish_event.ts | 2 +- core/docs/snippets/replace_event.ts | 2 +- core/docs/snippets/sign_event.ts | 2 +- 5 files changed, 10 insertions(+), 15 deletions(-) diff --git a/core/docs/fundamentals/events.md b/core/docs/fundamentals/events.md index bded35fea..c807e5d15 100644 --- a/core/docs/fundamentals/events.md +++ b/core/docs/fundamentals/events.md @@ -37,23 +37,12 @@ of the [NIP-51](https://nostr-nips.com/nip-51) specification `kind:10015` events ## Signing Events -> [!NOTE] -> Please note that the behavior of `.sign()` is assuming you have a valid signer instance. -> More about the different signers in the [signer documentation](/core/docs/fundamentals/signers.md). - -### Default Signer - NDK uses the default signer `ndk.signer` to sign events. <<< @/core/docs/snippets/sign_event.ts Read more about signers in [the signer documentation](/core/docs/fundamentals/signers.md) -### Other signers - -You can specify the use of a different signer to sign with different pubkeys. - -<<< @/core/docs/snippets/sign_event_with_other_signers.ts ## More examples diff --git a/core/docs/fundamentals/signers.md b/core/docs/fundamentals/signers.md index 59617c454..ee5337907 100644 --- a/core/docs/fundamentals/signers.md +++ b/core/docs/fundamentals/signers.md @@ -155,9 +155,15 @@ Once the signer is initialized, you can use it to sign and [publish](/core/docs/ const ndkEvent = new NDKEvent(ndk); ndkEvent.kind = 1; ndkEvent.content = "Hello, world!"; -await ndkEvent.sign(); +await ndkEvent.sign(); // [!code focus] ``` +## Combining signers + +You can specify the use of a different signer to sign with different pubkeys. + +<<< @/core/docs/snippets/sign_event_with_other_signers.ts + ## Read Public key diff --git a/core/docs/snippets/publish_event.ts b/core/docs/snippets/publish_event.ts index 83ce08be3..bee9ad119 100644 --- a/core/docs/snippets/publish_event.ts +++ b/core/docs/snippets/publish_event.ts @@ -6,4 +6,4 @@ const ndk = new NDK({ signer: nip07signer }); const event = new NDKEvent(ndk); event.kind = 1; event.content = "Hello world"; -await event.publish(); +await event.publish(); // [!code focus] diff --git a/core/docs/snippets/replace_event.ts b/core/docs/snippets/replace_event.ts index 7a62d932b..58725c876 100644 --- a/core/docs/snippets/replace_event.ts +++ b/core/docs/snippets/replace_event.ts @@ -14,4 +14,4 @@ await event.publish(); await event.publish(); // this will create a new event and publish it -await event.publishReplaceable(); +await event.publishReplaceable(); // [!code focus] diff --git a/core/docs/snippets/sign_event.ts b/core/docs/snippets/sign_event.ts index efc8def1a..c53073de0 100644 --- a/core/docs/snippets/sign_event.ts +++ b/core/docs/snippets/sign_event.ts @@ -6,4 +6,4 @@ const ndk = new NDK({ signer: nip07signer }); const event = new NDKEvent(ndk); event.kind = 1; event.content = "Hello world"; -await event.sign(); +await event.sign(); // [!code focus] From da7e4cc23f88c4895a7cd04a45f250a8dbb9f224 Mon Sep 17 00:00:00 2001 From: digitalbase Date: Thu, 16 Oct 2025 08:46:45 +0200 Subject: [PATCH 060/139] Signing docs --- core/docs/fundamentals/signers.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/core/docs/fundamentals/signers.md b/core/docs/fundamentals/signers.md index ee5337907..e2c3834e6 100644 --- a/core/docs/fundamentals/signers.md +++ b/core/docs/fundamentals/signers.md @@ -52,7 +52,11 @@ This library can also [help with generating new keys](/core/docs/fundamentals/si ### Remote Signer -(NIP-46) +A Nostr remote signer is an application or device that securely stores your private key and signs Nostr events on +your behalf, preventing you from having to expose the key clients. + +It works by establishing a secure connection, as described in [NIP-46](https://nostr-nips.com/nip-46), with a Nostr +client and then receiving signing requests via push notifications to approve or deny. #### bunker:// * Create a `NDKNip46Signer`, optionally providing the local signer if you are restoring a connection that was already generated in your app: From 542d72aa563e2cb63b09ba5f44bd600ecd40b8b3 Mon Sep 17 00:00:00 2001 From: digitalbase Date: Wed, 22 Oct 2025 20:00:04 +0200 Subject: [PATCH 061/139] Working on docs --- .vitepress/config.mts | 2 +- core/docs/fundamentals/connecting.md | 125 ++++++++- core/docs/fundamentals/signers.md | 106 ++------ core/docs/fundamentals/signers_old.md | 149 +++++++++++ react/docs/getting-started.md | 185 +++++++------ react/docs/hooks.md | 357 ++++++++++++++++++++++++++ react/docs/multi-account.md | 47 ++++ 7 files changed, 788 insertions(+), 183 deletions(-) create mode 100644 core/docs/fundamentals/signers_old.md create mode 100644 react/docs/hooks.md create mode 100644 react/docs/multi-account.md diff --git a/.vitepress/config.mts b/.vitepress/config.mts index d021ec024..70db57e70 100644 --- a/.vitepress/config.mts +++ b/.vitepress/config.mts @@ -95,7 +95,7 @@ export default defineConfig({ text: "React", link: "/react/README", items: [ - { text: "Getting Started", link: "/react/getting-started" }, + {text: "Getting Started", link: "/react/docs/getting-started"}, { text: "Muting", link: "/react/muting" }, { text: "Session Management", link: "/react/session-management" }, ] diff --git a/core/docs/fundamentals/connecting.md b/core/docs/fundamentals/connecting.md index 54adbd17b..d1dd9051c 100644 --- a/core/docs/fundamentals/connecting.md +++ b/core/docs/fundamentals/connecting.md @@ -1,14 +1,31 @@ # Connecting -This section will briefly explain the different mechanmisms through which NDK can connect to relays. +This section will briefly explain the different mechanmisms through which NDK can connect to relays. +## Connecting + +Connecting to relays in itself is super easy. + +```ts +// Import the package +import NDK from "@nostr-dev-kit/ndk"; + +// ... set up signer, specify relays, ... + +await ndk.connect(); // [!code focus] +``` + +On connection changes NDK will emit +[a number of connection events](/core/docs/fundamentals/connecting.html#connection-events). + +## Connection types ::: tip -Because NOSTR is decentralized and comprised of thousands of relays, it's important to read up on the +Because NOSTR is decentralized and comprised of thousands of relays, it's important to read up on the advised ways of connecting to relays. We strongly advise you to use the "[Outbox Model](/core/fundamentals/connecting.html#outbox-model)" in addition or replacement of specifying explicit relays. ::: -## Specify Relays +### Specific Relays The simplest way to get NDK to connect to relays is to specify them: @@ -44,11 +61,12 @@ ndk.addExplicitRelay("wss://another.relay"); await ndk.connect(); ``` -## Using a Signer +### User preferred relays -A [signer](/core/getting-started/signers.html) is used to sign events. You could say it's some sort of an -authentication mechanism. If the attached signer implements the `getRelays()` method, those relays will be used as -the explicit relays. +A [signer](/core/getting-started/signers.html) is used to sign events and tells NDK about your pubkey and related +settings. Once you +link up a signer and you have `autoConnectUserRelays` enabled (on by default) NDK will fetch your `kind:10002` event +([NIP-65](https://nostr-nips.com/nip-65)) and add discovered relays specified to a 2nd pool of connected relays. ```ts // Import NDK + NIP07 signer @@ -59,13 +77,96 @@ const nip07signer = new NDKNip07Signer(); const ndk = new NDK({ signer: nip07signer }); ``` -## Outbox Model +### Outbox Model + +Outbox (previously known as [the Gossip Model](https://mikedilger.com/gossip-model/)) is a more elaborate way of +dynamically connecting to relays based on who you are interacting with. +More about [the outbox model](https://how-nostr-works.pages.dev/#/outbox). + +The outbox model works similarly to the web/RSS model: + +- **Outbox Relays**: You post your content to your own designated relays +- **Inbox Relays**: You designate relays where you want to receive messages +- **Dynamic Discovery**: Clients discover and connect to relays based on where users actually post + +The protocol is formalized in ([NIP-65](https://nostr-nips.com/nip-65)), which defines: + +- **`kind:10002` events**: Relay list metadata containing read/write relay preferences +- **Relay tags**: "r" tags with optional "read"/"write" markers +- **Fallback to Kind 3**: Contact list events can contain relay information in their content + +By enabling `enableOutboxModel` (off by default) NDK will add an extra `outboxPool` to the ndk pool AND (@TODO Explain) -https://mikedilger.com/gossip-model/ -https://how-nostr-works.pages.dev/#/outbox -https://github.com/nostr-protocol/nips/blob/master/65.md https://primal.net/e/nevent1qqs2txvkjpa6fdlhxdtqmyk2tzzchpaa4vrfgx7h20539u5k9lzgqwgfjnlen -Explain why how. +### Dev Write Relays + +During local development you might want to specify a list of relays to write to. THis can be done by using +`devWriteRelayUrls` which will + +```ts +import NDK from "@nostr-dev-kit/ndk"; + +const ndk = new NDK({ + devWriteRelayUrls: ["wss://staging.relay", "wss://another.test.relay"], +}); + +await ndk.connect(); +``` + +This will write new events to those relays only. Note that if you have provided relays in +`explicitRelayUrls` these will also be used to write events to. + +## Relay Sets + +Under the hood NDK uses different sets of relays to send and receive messages. You can tap into that pool logic by +using the `NDKPool` class. + +```typescript + +import NDK, {NDKEvent, NDKPool} from "@nostr-dev-kit/ndk"; + +const ndk = new NDK(); + +const largeRelays = new NDKPool([ + `wss://relay.damus.io`, + 'wss://premium.primal.net' +], ndk); +largeRelays.addRelay(`wss://nos.lol`) + +const nicheRelays = new NDKPool([ + `wss://asad`, + 'wss://premisadasdum.primal.net' +], ndk); + +largeRelays.connect(); + +ndk.pools.length; // 2 + +``` + +Note that if you have outbox enabled you will have an extra pool in the `ndk.pools` array reserved for user provided +relays. + +## Connection Events + +```typescript +// Main pool events +ndk.pool.on("relay:connecting", (relay: NDKRelay) => { + console.log(`โŸณ [Main Pool] Connecting to relay: ${relay.url}`); +}); + +ndk.pool.on("relay:connect", (relay: NDKRelay) => { + connectedRelays.add(relay.url); + console.log(`โœ“ [Main Pool] Connected to relay: ${relay.url}`); + console.log(`Total connected relays: ${connectedRelays.size}`); +}); + +ndk.pool.on("relay:disconnect", (relay: NDKRelay) => { + connectedRelays.delete(relay.url); + console.log(`โœ— [Main Pool] Disconnected from relay: ${relay.url}`); + console.log(`Total connected relays: ${connectedRelays.size}`); +}); +``` diff --git a/core/docs/fundamentals/signers.md b/core/docs/fundamentals/signers.md index e2c3834e6..3e9bbefdc 100644 --- a/core/docs/fundamentals/signers.md +++ b/core/docs/fundamentals/signers.md @@ -52,104 +52,40 @@ This library can also [help with generating new keys](/core/docs/fundamentals/si ### Remote Signer -A Nostr remote signer is an application or device that securely stores your private key and signs Nostr events on -your behalf, preventing you from having to expose the key clients. +A Nostr remote signer (aka `bunker`) is an application or device that securely stores your private key and signs Nostr +events on +your behalf, preventing you from having to expose the private key. It works by establishing a secure connection (over +Nostr relays) +, as described in [NIP-46](https://github.com/nostr-protocol/nips/blob/master/46.md), where the bunker implementation +can approve +or deny requests. -It works by establishing a secure connection, as described in [NIP-46](https://nostr-nips.com/nip-46), with a Nostr -client and then receiving signing requests via push notifications to approve or deny. +To add remote signing support to your application, there are a few things you need: -#### bunker:// -* Create a `NDKNip46Signer`, optionally providing the local signer if you are restoring a connection that was already generated in your app: +* a bunker:// connection string provided by the user +* A local (client) keypair used to communicate with + remote-signer. [Can be generated by NDK](/core/docs/fundamentals/signers.html#generate-keys) -```ts -const signerConnectionString = 'bunker://....'; // ask the user for the bunker connection string -const localNsec = `nsec1....`; // Restore this from whatever storage your app is using, if you have it -const signer = NDKNip46Signer.bunker(ndk, signerConnectionString, localNsec); -const user = await signer.blockUntilReady(); - -console.log("Welcome", user.npub); - -// if you didn't have a localNsec you should store it for future sessions of your app -save(signer.localSigner.nsec) -``` - -#### nostrconnect:// -The `nostrconnect://` flow is the reverse of the `bunker://` flow; the app generates a connection string and is sent to the signer out of band (such as scanning a QR code). +Create a `NDKNip46Signer` with the bunker connection string and local keypair. ```ts -const relay = 'wss://relay.primal.net'; // choose a relay to be used to communicate with the signer -const localNsec = `nsec1....`; // Restore this from whatever storage your app is using, if you have it - -// instantiate the signer -const signer = NDKNip46Signer.nostrconnect(ndk, relay, localNsec, { - name: "", - icon: "", - perms: [ 'sign_event:1,sign_event:30023' ] // permissions to request (e.g. sign event kind:1 and kind:30023) -}); +// provided by the user +const signerConnectionString = 'bunker://....'; -// Open the signer or show the signer.nostrConnectUri URI as a QR code -open(signer.nostrConnectUri); +// local keypair generated when signer if first initialised +const clientKeypair = NDKPrivateKeySigner.generate(); // +const clientNsec = clientKeypair.nsec; -// Wait for the user to connect +// initiate NIP-46 signer +const signer = NDKNip46Signer.bunker(ndk, signerConnectionString, clientNsec); + +// promise will resolve once the `kind:24133` event is received const user = await signer.blockUntilReady(); console.log("Welcome", user.npub); - -// if you didn't have a localNsec you should store it for future sessions of your app -save(signer.localSigner.nsec) -``` - -#### Password-Protected Keys (NIP-49) - -NDK supports NIP-49 encrypted private keys (ncryptsec format) for secure storage: - -```ts -import { NDKPrivateKeySigner } from "@nostr-dev-kit/ndk"; - -// Encrypt a private key with a password -const signer = NDKPrivateKeySigner.generate(); -const password = "user-chosen-password"; -const ncryptsec = signer.encryptToNcryptsec(password); - -// Store ncryptsec securely (e.g., in localStorage) -localStorage.setItem("encrypted_key", ncryptsec); - -// Later, restore the signer from encrypted key -const storedKey = localStorage.getItem("encrypted_key"); -const restoredSigner = NDKPrivateKeySigner.fromNcryptsec(storedKey, password); -console.log("Restored pubkey:", restoredSigner.pubkey); -``` - -**Security Parameters:** - -The `encryptToNcryptsec` method accepts optional parameters for security tuning: - -```ts -// Higher security (slower, more resistant to brute force) -const ncryptsec = signer.encryptToNcryptsec(password, 20); // log_n = 20 (~2 seconds, 1GB memory) - -// Default security (faster) -const ncryptsec = signer.encryptToNcryptsec(password, 16); // log_n = 16 (~100ms, 64MB memory) - -// With key security byte (0x00, 0x01, or 0x02) -const ncryptsec = signer.encryptToNcryptsec(password, 16, 0x02); ``` -**Direct NIP-49 utilities:** - -NDK also re-exports NIP-49 utilities for advanced use cases: -```ts -import { nip49 } from "@nostr-dev-kit/ndk"; -import { hexToBytes, bytesToHex } from "@noble/hashes/utils"; - -// Encrypt raw bytes -const privateKeyBytes = hexToBytes("14c226dbdd865d5e..."); -const ncryptsec = nip49.encrypt(privateKeyBytes, password); - -// Decrypt to raw bytes -const decryptedBytes = nip49.decrypt(ncryptsec, password); -``` ## Sign Events diff --git a/core/docs/fundamentals/signers_old.md b/core/docs/fundamentals/signers_old.md new file mode 100644 index 000000000..0994ce9ea --- /dev/null +++ b/core/docs/fundamentals/signers_old.md @@ -0,0 +1,149 @@ +### Remote Signer + +A Nostr remote signer is an application or device that securely stores your private key and signs Nostr events on +your behalf, preventing you from having to expose the key clients. + +It works by establishing a secure connection, as described in [NIP-46](https://nostr-nips.com/nip-46), with a Nostr +client and then receiving signing requests via push notifications to approve or deny. + +#### bunker:// + +* Create a `NDKNip46Signer`, optionally providing the local signer if you are restoring a connection that was already + generated in your app: + +```ts +const signerConnectionString = 'bunker://....'; // ask the user for the bunker connection string +const localNsec = `nsec1....`; // Restore this from whatever storage your app is using, if you have it +const signer = NDKNip46Signer.bunker(ndk, signerConnectionString, localNsec); +const user = await signer.blockUntilReady(); + +console.log("Welcome", user.npub); + +// if you didn't have a localNsec you should store it for future sessions of your app +save(signer.localSigner.nsec) +``` + +#### nostrconnect:// + +The `nostrconnect://` flow is the reverse of the `bunker://` flow; the app generates a connection string and is sent to +the signer out of band (such as scanning a QR code). + +```ts +const relay = 'wss://relay.primal.net'; // choose a relay to be used to communicate with the signer +const localNsec = `nsec1....`; // Restore this from whatever storage your app is using, if you have it + +// instantiate the signer +const signer = NDKNip46Signer.nostrconnect(ndk, relay, localNsec, { + name: "", + icon: "", + perms: ['sign_event:1,sign_event:30023'] // permissions to request (e.g. sign event kind:1 and kind:30023) +}); + +// Open the signer or show the signer.nostrConnectUri URI as a QR code +open(signer.nostrConnectUri); + +// Wait for the user to connect +const user = await signer.blockUntilReady(); + +console.log("Welcome", user.npub); + +// if you didn't have a localNsec you should store it for future sessions of your app +save(signer.localSigner.nsec) +``` + +#### Password-Protected Keys (NIP-49) + +NDK supports NIP-49 encrypted private keys (ncryptsec format) for secure storage: + +```ts +import {NDKPrivateKeySigner} from "@nostr-dev-kit/ndk"; + +// Encrypt a private key with a password +const signer = NDKPrivateKeySigner.generate(); +const password = "user-chosen-password"; +const ncryptsec = signer.encryptToNcryptsec(password); + +// Store ncryptsec securely (e.g., in localStorage) +localStorage.setItem("encrypted_key", ncryptsec); + +// Later, restore the signer from encrypted key +const storedKey = localStorage.getItem("encrypted_key"); +const restoredSigner = NDKPrivateKeySigner.fromNcryptsec(storedKey, password); +console.log("Restored pubkey:", restoredSigner.pubkey); +``` + +**Security Parameters:** + +The `encryptToNcryptsec` method accepts optional parameters for security tuning: + +```ts +// Higher security (slower, more resistant to brute force) +const ncryptsec = signer.encryptToNcryptsec(password, 20); // log_n = 20 (~2 seconds, 1GB memory) + +// Default security (faster) +const ncryptsec = signer.encryptToNcryptsec(password, 16); // log_n = 16 (~100ms, 64MB memory) + +// With key security byte (0x00, 0x01, or 0x02) +const ncryptsec = signer.encryptToNcryptsec(password, 16, 0x02); +``` + +**Direct NIP-49 utilities:** + +NDK also re-exports NIP-49 utilities for advanced use cases: + +```ts +import {nip49} from "@nostr-dev-kit/ndk"; +import {hexToBytes, bytesToHex} from "@noble/hashes/utils"; + +// Encrypt raw bytes +const privateKeyBytes = hexToBytes("14c226dbdd865d5e..."); +const ncryptsec = nip49.encrypt(privateKeyBytes, password); + +// Decrypt to raw bytes +const decryptedBytes = nip49.decrypt(ncryptsec, password); +``` + +## Sign Events + +Once the signer is initialized, you can use it to sign and [publish](/core/docs/fundamentals/publishing.html) events: + +```ts +const ndkEvent = new NDKEvent(ndk); +ndkEvent.kind = 1; +ndkEvent.content = "Hello, world!"; +await ndkEvent.sign(); // [!code focus] +``` + +## Combining signers + +You can specify the use of a different signer to sign with different pubkeys. + +<<< @/core/docs/snippets/sign_event_with_other_signers.ts + +## Read Public key + +**Read the user's public key** + +```ts +nip07signer.user().then(async (user) => { + if (!!user.npub) { + console.log("Permission granted to read their public key:", user.npub); + } +}); +``` + +## Generate Keys + +Perhaps the only time we really want you to use the `NDKPrivateKeySigner` is to help you generate new keys as the signer +provides helper methods to do just that: + +```ts +import {NDKPrivateKeySigner} from "@nostr-dev-kit/ndk"; + +// Generate a new private key +const signer = NDKPrivateKeySigner.generate(); +console.log("nsec:", signer.nsec); +console.log("npub:", signer.npub); +``` + + diff --git a/react/docs/getting-started.md b/react/docs/getting-started.md index 40911655f..198aa2496 100644 --- a/react/docs/getting-started.md +++ b/react/docs/getting-started.md @@ -1,82 +1,111 @@ -# NDK React Hooks +# NDK React -`@nostr-dev-kit/react` provides a set of React hooks and utilities to seamlessly integrate Nostr functionality into your React applications using the Nostr Development Kit (NDK). This library simplifies managing Nostr data, user sessions, and event subscriptions within your React components. +`@nostr-dev-kit/react` provides a set of React hooks and utilities to seamlessly integrate Nostr functionality into your +React applications using the Nostr Development Kit (NDK). -## Initialization +This library simplifies managing Nostr data, user sessions, and subscriptions within a React application. -The core of `@nostr-dev-kit/react` revolves around a shared NDK instance. Initialize it once at the root of your application using the `useNDKInit` hook. This ensures all hooks and stores use the same NDK configuration. +## Initialization -```tsx -// components/ndk.tsx -'use client'; +NDK React revolves around a shared (singleton) NDK instance. Initialize it at the root of your application using +the `useNDKInit` hook. -// Here we will initialize NDK and configure it to be available throughout the application -import NDK, { NDKNip07Signer, NDKPrivateKeySigner, NDKSigner } from "@nostr-dev-kit/ndk"; +This ensures all hooks and stores use the same/shared NDK instance and configuration. -// An optional in-browser cache adapter -import NDKCacheAdapterDexie from "@nostr-dev-kit/cache-dexie"; -import { NDKSessionLocalStorage, useNDKInit, useNDKSessionMonitor } from "@nostr-dev-kit/react"; -import { useEffect } from "react"; +### Provider Pattern -// Define explicit relays or use defaults -const explicitRelayUrls = ["wss://relay.primal.net", "wss://nos.lol", "wss://purplepag.es"]; +A common way to make react components available to all other components in your application is to make use of [the +provider pattern](https://www.patterns.dev/vanilla/provider-pattern/). -// Setup Dexie cache adapter (Client-side only) -let cacheAdapter: NDKCacheAdapterDexie | undefined; -if (typeof window !== "undefined") { - cacheAdapter = new NDKCacheAdapterDexie({ dbName: "your-app-name" }); -} +Create the provider component and put it somewhere close to your application layout: -// Create the singleton NDK instance -const ndk = new NDK({ explicitRelayUrls, cacheAdapter }); +```tsx +// app/NDKProvider.tsx +'use client'; -// Connect to relays on initialization (client-side) -if (typeof window !== "undefined") ndk.connect(); +import { useNDK, useNDKCurrentUser, useNDKInit } from "@nostr-dev-kit/react"; +import { useEffect } from "react"; -// Use the browser's localStorage for session storage -const sessionStorage = new NDKSessionLocalStorage(); +type ProvidersProps = { + children: React.ReactNode; +}; -/** - * Use an NDKHeadless component to initialize NDK in order to prevent application-rerenders - * when there are changes to the NDK or session state. - * - * Include this headless component in your app layout to initialize NDK correctly. - * @returns - */ -export default function NDKHeadless() { - const initNDK = useNDKInit(); +export function NDKProvider({ children }: ProvidersProps) { + + const initializeNDK = useNDKInit(); + const { ndk } = useNDK(); + + // Connect only when there is an active user + const shouldConnect = useNDKCurrentUser(); - useNDKSessionMonitor(sessionStorage, { - profile: true, // automatically fetch profile information for the active user - follows: true, // automatically fetch follows of the active user - }); + useEffect(() => { + initializeNDK({ + explicitRelayUrls: ['wss://relay.damus.io'], + }); + }, [initializeNDK]); useEffect(() => { - if (ndk) initNDK(ndk); - }, [initNDK]) - - return null; -} + if (!shouldConnect) return; + + // This will also reconnect when the instance changes + ndk?.connect(); + }, [ndk, shouldConnect]); + + return <>{children}; +} ``` +Then load that provider in the main application layout (can be server component): + ```tsx -// src/App.tsx -import React, { useEffect } from 'react'; -import { useNDKInit } from '@nostr-dev-kit/react'; -import { NDKSessionLocalStorage, useNDKSessionMonitor } from '@nostr-dev-kit/react'; -import YourMainApp from './YourMainApp'; // Your main application component -import NDKHeadless from "components/ndk.tsx"; +// src/Layout.tsx + +// Your main application component +import YourApp from './App'; +import NDKProvider from "./NDKProvider.tsx"; function App() { - return - - - + return ( + + + + + ); } export default App; ``` +### Headless Component + +```tsx +'use client'; + +import { useNDK, useNDKInit } from "@nostr-dev-kit/react"; + +/** + * Use an NDKHeadless component to initialize NDK to prevent application-rerenders + * when there are changes to the NDK or session state. Include this headless component in your app layout + * to initialize NDK correctly. + */ +export function NDKHeadless() { + const initializeNDK = useNDKInit(); + const { ndk } = useNDK(); + + useEffect(() => { + initializeNDK({ + explicitRelayUrls: [ 'wss://relay.damus.io'], + }); + }, [initializeNDK]); + + return null; +} +``` + +Notice that this NDK instance is not connected. [Read about connecting](/core/docs/fundamentals/connecting.html). + +## Using the NDK Instance + Once initialized, you can access the NDK instance anywhere in your component tree using the `useNDK` hook if needed, although many hooks use it implicitly. ```tsx @@ -89,34 +118,30 @@ function MyComponent() { } ``` -## Logging In -`@nostr-dev-kit/react` supports multiple-sessions. Use `useNDKSessionLogin()` to add a session. +## Logging in -Sessions can be read-only (logging by providing an `NDKUser`) or full-sessions with signing access (logging by providing an `NDKSigner`). +NDK can be initialised without [a signer](/core/docs/fundamentals/signers.html) as demonstrated +[in the first snippet](/react/docs/getting-started.html#headless-component). To log in with a signer the +`useNDKSessionLogin` +hook can be used which will: + +1. Create a new session +2. Activate [the signer](/core/docs/fundamentals/signers.html) +3. If successful, return and tell NDK about the current user ```tsx -import {useNDKSessionLogin, useNDKCurrentUser} from '@nostr-dev-kit/react'; -import {NDKPrivateKeySigner} from '@nostr-dev-kit/ndk'; +import { useNDK, useNDKSessionLogin } from '@nostr-dev-kit/react'; -function Signin() { +function MyComponent() { + const { ndk } = useNDK(); const login = useNDKSessionLogin(); - const nsec = "nsec1...."; // ask the user to enter their key or the preferred login method. - const currentUser = useNDKCurrentUser(); - const handleLogin = useCallback(async () => { - const signer = new NDKPrivateKeySigner(nsec); + const handleLoginWithNsec = async (nsec: string) => { + const signer = new NDKPrivateKeySigner(nsec); - await login(signer) - aloert("hello!") - }, []) - - useEffect(() => { - if (!currentUser) { - console.log('you are not logged in) - } else { - console.log('you are now logged in with user with pubkey', currentUser.pubkey) - } - }, [currentUser]) + await login(signer) + alert("User Signed in!") + }; } ``` @@ -219,17 +244,7 @@ function UserCard({ userIdentifier }: UserCardProps) { export default UserCard; ``` -## Managing User Sessions (Login & Multi-Account) - -`@nostr-dev-kit/react` provides robust session management, supporting both single and multiple user accounts. You can use the session monitoring functionality to automatically persist and restore user sessions across page reloads. - -The session monitor will: -1. Automatically restore sessions from storage when your app loads -2. Persist new sessions when users log in -3. Update storage when sessions change -4. Remove sessions from storage when users log out -You can use this alongside the other session management hooks like `useNDKSessionLogin`, `useNDKSessionLogout`, and `useNDKSessionSwitch`. ## Subscribing to Events diff --git a/react/docs/hooks.md b/react/docs/hooks.md new file mode 100644 index 000000000..d5d02850d --- /dev/null +++ b/react/docs/hooks.md @@ -0,0 +1,357 @@ +# NDK React + +`@nostr-dev-kit/react` provides a set of React hooks and utilities to seamlessly integrate Nostr functionality into your +React applications using the Nostr Development Kit (NDK). + +This library simplifies managing Nostr data, user sessions, and event subscriptions within a React application. + +## Initialization + +NDK React package revolves around a shared NDK instance. Initialize it once at the root of your application using the +`useNDKInit` hook. + +This ensures all hooks and stores use the same NDK configuration. + +### Headless Component + +```tsx +'use client'; + +import { useNDK, useNDKInit } from "@nostr-dev-kit/react"; + +/** + * Use an NDKHeadless component to initialize NDK to prevent application-rerenders + * when there are changes to the NDK or session state. Include this headless component in your app layout + * to initialize NDK correctly. + */ +export function NDKHeadless() { + const initializeNDK = useNDKInit(); + const { ndk } = useNDK(); + + useEffect(() => { + initializeNDK({ + explicitRelayUrls: [ 'wss://relay.damus.io'], + }); + }, [initializeNDK]); + + return null; +} +``` + +Notice that this NDK instance is not connected. [Read about connecting](/core/docs/fundamentals/connecting.html). + +### Provider Pattern + +A common way to make react components available to all other components in your application is to make use of [the +provider pattern](https://www.patterns.dev/vanilla/provider-pattern/). + +Create the provider component and put it somewhere close to your application layout: + +```tsx +// app/NDKProvider.tsx +'use client'; + +import { useNDK, useNDKCurrentUser, useNDKInit } from "@nostr-dev-kit/react"; +import { useEffect } from "react"; + +type ProvidersProps = { + children: React.ReactNode; +}; + +export function NDKProvider({ children }: ProvidersProps) { + + const initializeNDK = useNDKInit(); + const { ndk } = useNDK(); + + // Connect only when there is an active user + const shouldConnect = useNDKCurrentUser(); + + useEffect(() => { + initializeNDK({ + explicitRelayUrls: ['wss://relay.damus.io'], + }); + }, [initializeNDK]); + + useEffect(() => { + if (!shouldConnect) return; + + // This will also reconnect when the instance changes + ndk?.connect(); + }, [ndk, shouldConnect]); + + return <>{children}; +} +``` + +Then load that provider in the main application layout (can be server component): + +```tsx +// src/Layout.tsx + +// Your main application component +import YourApp from './App'; +import NDKProvider from "./NDKProvider.tsx"; + +function App() { + return ( + + + + + ); +} + +export default App; +``` + +## Using the NDK Instance + +Once initialized, you can access the NDK instance anywhere in your component tree using the `useNDK` hook if needed, +although many hooks use it implicitly. + +```tsx +import { useNDK } from '@nostr-dev-kit/react'; + +function MyComponent() { + const { ndk } = useNDK(); + + // Use ndk instance... +} +``` + +## Logging in + +NDK can be initialised without [a signer](/core/docs/fundamentals/signers.html) as demonstrated +[in the first snippet](/react/docs/getting-started.html#headless-component). To log in with a signer the +`useNDKSessionLogin` +hook can be used which will: + +1. Create a new session +2. Activate [the signer](/core/docs/fundamentals/signers.html) +3. If successful, return and tell NDK about the current user + +```tsx +import { useNDK, useNDKSessionLogin } from '@nostr-dev-kit/react'; + +function MyComponent() { + const { ndk } = useNDK(); + const login = useNDKSessionLogin(); + + const handleLoginWithNsec = async (nsec: string) => { + const signer = new NDKPrivateKeySigner(nsec); + + await login(signer) + alert("User Signed in!") + }; +} +``` + +## Working with Users + +### Using the `useUser` Hook + +The `useUser` hook resolves various user identifier formats into an NDKUser instance: + +```tsx +// Using hex pubkey +const user = useUser("abc123..."); + +// Using npub +const user = useUser("npub1..."); + +// Using nip05 +const user = useUser("alice@example.com"); + +// Using nprofile +const user = useUser("nprofile1..."); +``` + +### Fetching User Profiles + +Easily fetch and display Nostr user profiles using the `useProfileValue` hook. It handles caching and fetching logic +automatically. + +The `useProfileValue` hook accepts two parameters: + +- `userOrPubkey`: An NDKUser instance, public key string, null, or undefined +- `options`: An optional object with the following properties: + - `refresh`: A boolean indicating whether to force a refresh of the profile + - `subOpts`: NDKSubscriptionOptions to customize how the profile is fetched from relays + +```tsx +// Basic usage with pubkey +const profile = useProfileValue(pubkey); + +// Using with NDKUser from useUser hook +const user = useUser("alice@example.com"); +const profile = useProfileValue(user); + +// Force refresh the profile +const profile = useProfileValue(pubkey, { refresh: true }); + +// With subscription options +const profile = useProfileValue(user, { + refresh: false, + subOpts: { + closeOnEose: true, + // Other NDKSubscriptionOptions... + } +}); +``` + +The hook returns the user's profile (NDKUserProfile) or undefined if the profile is not available yet. + +```tsx +// src/components/UserCard.tsx +import React from 'react'; +import { useUser, useProfileValue } from '@nostr-dev-kit/react'; + +interface UserCardProps { + userIdentifier: string; // Can be pubkey, npub, nip05, or nprofile +} + +function UserCard({ userIdentifier }: UserCardProps) { + // Resolve user from any identifier format + const user = useUser(userIdentifier); + + // Fetch profile - will automatically fetch when user resolves + const profile = useProfileValue(user, { + refresh: false, // Whether to force a refresh of the profile + subOpts: { /* NDKSubscriptionOptions */ } // Options for the subscription + }); + + if (!user) { + return
Resolving user...
; + } + + if (!profile) { + return
Loading profile for {user.npub.substring(0, 12)}...
; + } + + return ( +
+ {profile.name +

{profile.displayName || profile.name || 'Anonymous'}

+

{profile.about}

+ {profile.nip05 &&

NIP-05: {profile.nip05}

} +
+ ); +} + +export default UserCard; +``` + +## Subscribing to Events + +Use the `useSubscribe` hook for subscribing to Nostr events based on filters. It returns the events found in a stable +set. + +```tsx +// src/components/NoteFeed.tsx +import React, { useState } from 'react'; +import { NDKFilter, NDKEvent, NDKKind } from '@nostr-dev-kit/ndk'; +import { useSubscribe } from '@nostr-dev-kit/react'; + +function NoteFeed() { + // there is no need to memoize filters + // Subscribe to events matching the filter + // `events` is a Set ordered by created_at (newest first by default) + const { events, eose } = useSubscribe( + [{ kinds: [NDKKind.Text] }], // no need to memoize filters, useSubscribe only depends on the explicit dependencies + { /* in case you need to pass options for the subscription */ }, // NDKSubscriptionOptions + [...dependencies] // in case you need to change the subscription + ); + + return ( +
+

Recent Notes

+ {events.length === 0 && eose &&

No notes found.

} +
    + {events.map((event: NDKEvent) => ( +
  • +

    {event.content}

    + By: + At: {new Date(event.created_at! * 1000).toLocaleString()} +
  • + ))} +
+
+ ); +} + +// Helper component (replace with actual implementation) +function UserProfileLink({ pubkey }: { pubkey: string }) { + const profile = useProfileValue(pubkey, { refresh: true }); + return {profile?.name || pubkey.substring(0, 8)}; +} + +export default NoteFeed; +``` + +### Fetching a Single Event with `useEvent` + +The `useEvent` hook allows you to fetch a single event by its ID or using a filter. This is useful when you need to +retrieve and display a specific event, such as an article, note, or any other Nostr content. + +```tsx +// src/components/EventViewer.tsx +import React from 'react'; +import { NDKEvent, NDKArticle } from '@nostr-dev-kit/ndk'; +import { useEvent } from '@nostr-dev-kit/react'; + +function EventViewer({ eventId }: { eventId: string }) { + // Fetch a single event by ID or filter + // Returns undefined while loading, null if not found, or the event if found + const event = useEvent( + eventId, + { wrap: true }, // Optional: UseSubscribeOptions + [] // Optional: dependencies array + ); + + if (event === undefined) return
Loading event...
; + if (event === null) return
Event not found
; + + return ( +
+

{event.title || 'Untitled'}

+

{event.content}

+ Published: {new Date(event.created_at! * 1000).toLocaleString()} +
+ ); +} + +export default EventViewer; +``` + +The `useEvent` hook accepts three parameters: + +- `idOrFilter`: A string ID, an NDKFilter object, or an array of NDKFilter objects to fetch the event +- `opts`: Optional UseSubscribeOptions to customize how the event is fetched +- `dependencies`: Optional array of dependencies that will trigger a refetch when changed + +The hook returns: + +- `undefined` while the event is being loaded +- `null` if the event was not found +- The event object if it was found + +This makes it easy to handle loading states and display appropriate UI for each case. + +## Other Useful Hooks + +`@nostr-dev-kit/react` provides several other specialized hooks: + +* `useFollows()`: Fetches the follow list of the active user. +* `useNDKWallet()`: Manages wallet connections (e.g., NWC) (via `import of "@nostr-dev-kit/react/wallet"`) +* `useNDKNutzapMonitor()`: Monitors for incoming zaps via Nutzap. (via `import of "@nostr-dev-kit/react/wallet"`) + +## Muting + +See [Muting Documentation](./muting.md) for details on how to mute, unmute, and check mute status for users, events, +hashtags, and words using NDK React hooks. \ No newline at end of file diff --git a/react/docs/multi-account.md b/react/docs/multi-account.md new file mode 100644 index 000000000..97e4d112c --- /dev/null +++ b/react/docs/multi-account.md @@ -0,0 +1,47 @@ +## Sessions / Multi-Account + +`@nostr-dev-kit/react` supports multiple-sessions. Use `useNDKSessionLogin()` to add a session. + +Sessions can be read-only (logging by providing an `NDKUser`) or full-sessions with signing access (logging by providing +an `NDKSigner`). + +```tsx +import {useNDKSessionLogin, useNDKCurrentUser} from '@nostr-dev-kit/react'; +import {NDKPrivateKeySigner} from '@nostr-dev-kit/ndk'; + +function Signin() { + const login = useNDKSessionLogin(); + const nsec = "nsec1...."; // ask the user to enter their key or the preferred login method. + const currentUser = useNDKCurrentUser(); + + const handleLogin = useCallback(async () => { + const signer = new NDKPrivateKeySigner(nsec); + + await login(signer) + aloert("hello!") + }, []) + + useEffect(() => { + if (!currentUser) { + console.log('you are not logged in) + } else { + console.log('you are now logged in with user with pubkey', currentUser.pubkey) + } + }, [currentUser]) +} +``` + +## Managing User Sessions (Login & Multi-Account) + +`@nostr-dev-kit/react` provides robust session management, supporting both single and multiple user accounts. You can +use the session monitoring functionality to automatically persist and restore user sessions across page reloads. + +The session monitor will: + +1. Automatically restore sessions from storage when your app loads +2. Persist new sessions when users log in +3. Update storage when sessions change +4. Remove sessions from storage when users log out + +You can use this alongside the other session management hooks like `useNDKSessionLogin`, `useNDKSessionLogout`, and +`useNDKSessionSwitch`. \ No newline at end of file From 8686c29b6ba9a98c30e17892b51491bc152acd9c Mon Sep 17 00:00:00 2001 From: digitalbase Date: Wed, 22 Oct 2025 20:38:20 +0200 Subject: [PATCH 062/139] Remove copied docs from /docs folder --- docs/cookbook/index.md | 0 docs/cookbook/svelte5/basic-authentication.md | 0 .../svelte5/multi-session-management.md | 0 docs/sessions/api.md | 555 ------------------ docs/sessions/index.md | 141 ----- docs/sessions/migration.md | 467 --------------- docs/sessions/quick-start.md | 260 -------- docs/wot/index.md | 340 ----------- 8 files changed, 1763 deletions(-) delete mode 100644 docs/cookbook/index.md delete mode 100644 docs/cookbook/svelte5/basic-authentication.md delete mode 100644 docs/cookbook/svelte5/multi-session-management.md delete mode 100644 docs/sessions/api.md delete mode 100644 docs/sessions/index.md delete mode 100644 docs/sessions/migration.md delete mode 100644 docs/sessions/quick-start.md delete mode 100644 docs/wot/index.md diff --git a/docs/cookbook/index.md b/docs/cookbook/index.md deleted file mode 100644 index e69de29bb..000000000 diff --git a/docs/cookbook/svelte5/basic-authentication.md b/docs/cookbook/svelte5/basic-authentication.md deleted file mode 100644 index e69de29bb..000000000 diff --git a/docs/cookbook/svelte5/multi-session-management.md b/docs/cookbook/svelte5/multi-session-management.md deleted file mode 100644 index e69de29bb..000000000 diff --git a/docs/sessions/api.md b/docs/sessions/api.md deleted file mode 100644 index 6ac8c5dc6..000000000 --- a/docs/sessions/api.md +++ /dev/null @@ -1,555 +0,0 @@ -# API Reference - -Complete API documentation for `@nostr-dev-kit/sessions`. - -## NDKSessionManager - -The main class for managing user sessions. - -### Constructor - -```typescript -new NDKSessionManager(ndk: NDK, options?: SessionManagerOptions) -``` - -**Parameters:** -- `ndk: NDK` - The NDK instance to use -- `options?: SessionManagerOptions` - Configuration options - -**SessionManagerOptions:** - -```typescript -interface SessionManagerOptions { - storage?: SessionStorage; // Storage backend (default: MemoryStorage) - autoSave?: boolean; // Auto-persist on changes (default: true) - saveDebounceMs?: number; // Debounce time for auto-save (default: 500ms) - fetches?: SessionStartOptions; // What to fetch on login/restore -} -``` - -**Example:** - -```typescript -import { NDKSessionManager, LocalStorage } from '@nostr-dev-kit/sessions'; - -const sessions = new NDKSessionManager(ndk, { - storage: new LocalStorage(), - autoSave: true, - saveDebounceMs: 500, - fetches: { - follows: true, - mutes: true - } -}); -``` - -### Methods - -#### login() - -Login with a signer or user. - -```typescript -async login( - userOrSigner: NDKUser | NDKSigner, - options?: { setActive?: boolean } -): Promise -``` - -**Parameters:** -- `userOrSigner` - An NDKSigner for full sessions or NDKUser for read-only -- `options?: { setActive?: boolean }` - Whether to set as active session (default: true) - -**Returns:** `Promise` - The public key of the logged-in user - -**Example:** - -```typescript -const signer = new NDKPrivateKeySigner(nsec); - -// Configure fetches in constructor -const sessions = new NDKSessionManager(ndk, { - fetches: { - follows: true, - mutes: true, - relayList: true, - wallet: true - } -}); - -const pubkey = await sessions.login(signer); -console.log('Logged in:', pubkey); - -// Or don't set as active -const pubkey2 = await sessions.login(signer2, { setActive: false }); -``` - -#### logout() - -Remove a session. If no pubkey provided, removes the active session. - -```typescript -logout(pubkey?: Hexpubkey): void -``` - -**Parameters:** -- `pubkey?: Hexpubkey` - Public key of session to remove (optional) - -**Example:** - -```typescript -// Logout specific user -sessions.logout(somePubkey); - -// Logout active user -sessions.logout(); -``` - -#### switchTo() - -Switch the active session to a different user. - -```typescript -switchTo(pubkey: Hexpubkey): void -``` - -**Parameters:** -- `pubkey: Hexpubkey` - Public key of session to activate - -**Example:** - -```typescript -sessions.switchTo(pubkey); -console.log('Now active:', sessions.activePubkey); -``` - -#### restore() - -Restore sessions from storage. - -```typescript -async restore(): Promise -``` - -**Example:** - -```typescript -await sessions.restore(); - -if (sessions.activeUser) { - console.log('Restored session for', sessions.activeUser.npub); -} -``` - -#### persist() - -Manually persist sessions to storage. - -```typescript -async persist(): Promise -``` - -**Example:** - -```typescript -await sessions.persist(); -``` - -#### clear() - -Clear all sessions from storage. - -```typescript -async clear(): Promise -``` - -**Example:** - -```typescript -await sessions.clear(); -``` - -#### subscribe() - -Subscribe to session state changes. - -```typescript -subscribe(callback: (state: SessionState) => void): UnsubscribeFn -``` - -**Parameters:** -- `callback` - Function called when state changes - -**Returns:** `UnsubscribeFn` - Function to unsubscribe - -**SessionState:** - -```typescript -interface SessionState { - sessions: Map; - activePubkey?: Hexpubkey; -} -``` - -**Example:** - -```typescript -const unsubscribe = sessions.subscribe((state) => { - console.log('Active:', state.activePubkey); - console.log('Sessions:', state.sessions.size); -}); - -// Later... -unsubscribe(); -``` - -#### destroy() - -Cleanup and stop all subscriptions and timers. - -```typescript -destroy(): void -``` - -**Example:** - -```typescript -sessions.destroy(); -``` - -#### getSessions() - -Get all sessions. - -```typescript -getSessions(): Map -``` - -**Example:** - -```typescript -const allSessions = sessions.getSessions(); -for (const [pubkey, session] of allSessions) { - console.log(pubkey, session.user.profile?.name); -} -``` - -#### getSession() - -Get a specific session by pubkey. - -```typescript -getSession(pubkey: Hexpubkey): NDKSession | undefined -``` - -**Parameters:** -- `pubkey: Hexpubkey` - Public key of session to get - -**Example:** - -```typescript -const session = sessions.getSession(pubkey); -if (session) { - console.log('Follows:', session.followSet?.size); -} -``` - -### Properties - -#### activeSession - -Get the currently active session. - -```typescript -get activeSession(): NDKSession | undefined -``` - -**Example:** - -```typescript -const session = sessions.activeSession; -if (session) { - console.log('Follows:', session.followSet?.size); - console.log('Mutes:', session.muteSet?.size); -} -``` - -#### activeUser - -Get the currently active user. - -```typescript -get activeUser(): NDKUser | undefined -``` - -**Example:** - -```typescript -const user = sessions.activeUser; -if (user) { - console.log('Active user:', user.npub); - console.log('Profile:', user.profile?.name); -} -``` - -#### activePubkey - -Get the currently active pubkey. - -```typescript -get activePubkey(): Hexpubkey | undefined -``` - -**Example:** - -```typescript -console.log('Active pubkey:', sessions.activePubkey); -``` - -## NDKSession - -Represents an individual user session. - -### Properties - -```typescript -interface NDKSession { - // User instance - user: NDKUser; - - // Signer for this session (undefined for read-only sessions) - signer?: NDKSigner; - - // Set of followed pubkeys - followSet?: Set; - - // Set of muted items (users, events, words, hashtags) - muteSet?: Set; - - // User's relay list - relayList?: NDKRelayList; - - // Blocked relay URLs - blockedRelayUrls?: Set; - - // NIP-60 wallet event - wallet?: NDKEvent; - - // Additional fetched events by kind - events: Map; -} -``` - -## Storage Implementations - -### LocalStorage - -Browser localStorage implementation. - -```typescript -new LocalStorage(key?: string) -``` - -**Parameters:** -- `key?: string` - Storage key (default: `'ndk-sessions'`) - -**Example:** - -```typescript -import { LocalStorage } from '@nostr-dev-kit/sessions'; - -const storage = new LocalStorage('my-app-sessions'); -``` - -### FileStorage - -Node.js filesystem implementation. - -```typescript -new FileStorage(filePath?: string) -``` - -**Parameters:** -- `filePath?: string` - File path (default: `'./.ndk-sessions.json'`) - -**Example:** - -```typescript -import { FileStorage } from '@nostr-dev-kit/sessions'; - -const storage = new FileStorage('~/.config/myapp/sessions.json'); -``` - -### MemoryStorage - -In-memory implementation (no persistence). - -```typescript -new MemoryStorage() -``` - -**Example:** - -```typescript -import { MemoryStorage } from '@nostr-dev-kit/sessions'; - -const storage = new MemoryStorage(); -``` - -## Custom Storage - -Implement the `SessionStorage` interface for custom storage: - -```typescript -interface SessionStorage { - save( - sessions: Map, - activePubkey?: Hexpubkey - ): Promise; - - load(): Promise<{ - sessions: Map; - activePubkey?: Hexpubkey; - }>; - - clear(): Promise; -} -``` - -**Example:** - -```typescript -class MyCustomStorage implements SessionStorage { - async save(sessions, activePubkey) { - // Save to your backend... - } - - async load() { - // Load from your backend... - return { sessions: new Map(), activePubkey: undefined }; - } - - async clear() { - // Clear from your backend... - } -} - -const storage = new MyCustomStorage(); -const sessions = new NDKSessionManager(ndk, { storage }); -``` - -## Types - -### Hexpubkey - -```typescript -type Hexpubkey = string; -``` - -Hex-encoded public key string. - -### MuteItem - -```typescript -type MuteItem = { - type: 'user' | 'event' | 'word' | 'hashtag'; - value: string; - // ... other properties -}; -``` - -Represents a muted item. - -### SerializedSession - -```typescript -interface SerializedSession { - pubkey: Hexpubkey; - signer?: string; // Serialized signer - followSet?: Hexpubkey[]; - muteSet?: MuteItem[]; - relayList?: RelayListData; - blockedRelayUrls?: string[]; - wallet?: NostrEvent; - events?: [NDKKind, NostrEvent][]; -} -``` - -Serialized session format for storage. - -### UnsubscribeFn - -```typescript -type UnsubscribeFn = () => void; -``` - -Function to call to unsubscribe from updates. - -## Error Handling - -The sessions package may throw errors during: - -- Login (invalid signer, network errors) -- Storage operations (permissions, disk full) -- Restoration (corrupted data) - -Always wrap async operations in try-catch: - -```typescript -try { - await sessions.login(signer); -} catch (error) { - console.error('Login failed:', error); -} - -try { - await sessions.restore(); -} catch (error) { - console.error('Failed to restore sessions:', error); -} -``` - -## Best Practices - -### 1. Always Call destroy() - -```typescript -// In your cleanup code -sessions.destroy(); -``` - -### 2. Use autoSave - -```typescript -const sessions = new NDKSessionManager(ndk, { - autoSave: true, - saveDebounceMs: 500 -}); -``` - -### 3. Handle No Active Session - -```typescript -if (!sessions.activeUser) { - // Show login UI -} -``` - -### 4. Subscribe to Changes - -```typescript -const unsubscribe = sessions.subscribe((state) => { - // Update UI when sessions change -}); -``` - -### 5. Security - -```typescript -// โš ๏ธ NEVER commit .ndk-sessions.json to git! -// Add to .gitignore: -// .ndk-sessions.json - -// Use environment variables for sensitive keys -const nsec = process.env.NOSTR_NSEC; -``` diff --git a/docs/sessions/index.md b/docs/sessions/index.md deleted file mode 100644 index be5502d52..000000000 --- a/docs/sessions/index.md +++ /dev/null @@ -1,141 +0,0 @@ -# Sessions - -`@nostr-dev-kit/sessions` is a framework-agnostic session management library for NDK that provides multi-account support, automatic data fetching, and flexible persistence. - -## Why Sessions? - -Managing user authentication and session state in Nostr applications can be complex. The sessions package simplifies: - -- **Multi-account management** - Let users switch between multiple Nostr identities seamlessly -- **Automatic data fetching** - Automatically fetch and cache follows, mutes, relay lists, and more -- **Persistence** - Save and restore sessions across app restarts -- **Framework agnostic** - Works with React, Svelte, Vue, vanilla JS, Node.js, etc. - -## Key Features - -### ๐Ÿ” Multiple Account Support - -Users can log in with multiple Nostr accounts and switch between them instantly. Perfect for: -- Personal and business accounts -- Testing with multiple identities -- Content creators managing multiple personas - -### ๐Ÿ’พ Flexible Storage - -Built-in storage adapters for: -- **LocalStorage** - Browser-based persistence -- **FileStorage** - Node.js/CLI applications -- **MemoryStorage** - Testing or temporary sessions -- **Custom** - Implement your own storage backend - -### ๐Ÿ”„ Auto-Fetch User Data - -On login, automatically fetch: -- Contact list (kind 3 follows) -- Mute lists (kind 10000) -- Relay lists (kind 10002) -- Blocked relay lists (kind 10001) -- NIP-60 wallet data (kind 17375) -- Any custom replaceable event kinds - -### ๐ŸŽฏ Framework Integration - -Works seamlessly with: -- React (via `@nostr-dev-kit/react`) -- Svelte 5 (via `@nostr-dev-kit/svelte`) -- Mobile (React Native via `@nostr-dev-kit/mobile`) -- Vanilla JavaScript -- Node.js/CLI applications - -## Installation - -```bash -npm install @nostr-dev-kit/sessions -# or -bun add @nostr-dev-kit/sessions -``` - -## Quick Example - -```typescript -import NDK from '@nostr-dev-kit/ndk'; -import { NDKSessionManager, LocalStorage } from '@nostr-dev-kit/sessions'; -import { NDKPrivateKeySigner } from '@nostr-dev-kit/ndk'; - -const ndk = new NDK({ explicitRelayUrls: ['wss://relay.damus.io'] }); -await ndk.connect(); - -const sessions = new NDKSessionManager(ndk, { - storage: new LocalStorage(), - autoSave: true, - fetches: { - follows: true, - mutes: true, - relayList: true - } -}); - -// Restore previous sessions -await sessions.restore(); - -// Login with auto-fetch -const signer = new NDKPrivateKeySigner(nsec); -await sessions.login(signer); - -console.log('Active user:', sessions.activeUser); -console.log('Follows:', sessions.activeSession?.followSet?.size); -``` - -## Next Steps - -- [Quick Start Guide](./quick-start) - Get up and running -- [API Reference](./api) - Complete API documentation -- [Migration Guide](./migration) - Migrating from ndk-hooks - -## Use Cases - -### Browser Applications -Perfect for web apps that need: -- User login/logout -- Multi-account switching -- Persistent sessions across page reloads -- Automatic relay and follow list management - -### Node.js/CLI Tools -Ideal for command-line tools that need: -- Saved credentials -- Multiple identity management -- Automated publishing with saved accounts - -### Mobile Applications -Great for React Native apps needing: -- Secure session storage -- Multi-account support -- Offline-first data caching - -## Architecture - -The sessions package is built on three core components: - -1. **NDKSessionManager** - Main API for managing sessions -2. **SessionStorage** - Pluggable storage backends -3. **NDKSession** - Individual session state and data - -All session state changes are observable via the subscribe pattern, making it easy to integrate with any reactive framework. - -## Security Considerations - -โš ๏ธ **Important:** Session serialization stores private keys. In production: - -1. Use encrypted storage when possible -2. Never commit session files to version control -3. Use environment variables for sensitive keys -4. Consider NIP-07 (browser extensions) or NIP-46 (remote signers) for better security - -## Framework-Specific Documentation - -For framework-specific implementations using sessions: - -- **React** - See [`@nostr-dev-kit/react` hooks documentation](/hooks/session-management) -- **Svelte 5** - See [`@nostr-dev-kit/svelte` documentation](/wrappers/svelte) -- **Mobile** - See [`@nostr-dev-kit/mobile` documentation](/mobile/session) diff --git a/docs/sessions/migration.md b/docs/sessions/migration.md deleted file mode 100644 index c24146e08..000000000 --- a/docs/sessions/migration.md +++ /dev/null @@ -1,467 +0,0 @@ -# Migration Guide - -This guide helps you migrate from the legacy session management in `@nostr-dev-kit/react` (ndk-hooks) to the new standalone `@nostr-dev-kit/sessions` package. - -## Why Migrate? - -The new `@nostr-dev-kit/sessions` package provides: - -- **Framework agnostic** - Works with React, Svelte, Vue, vanilla JS, Node.js -- **Better separation of concerns** - Session logic is independent of UI framework -- **Improved testing** - Easier to test without framework dependencies -- **More flexible** - Better storage options and customization -- **Actively maintained** - The old hooks-based session management is deprecated - -## Overview of Changes - -### Old (ndk-hooks) - -```typescript -// Hooks-based, React-specific -import { useNDKSessionLogin, useNDKCurrentUser } from '@nostr-dev-kit/react'; - -function MyComponent() { - const login = useNDKSessionLogin(); - const currentUser = useNDKCurrentUser(); - - // Login tied to React component lifecycle - await login(signer); -} -``` - -### New (sessions package) - -```typescript -// Framework-agnostic, standalone -import { NDKSessionManager, LocalStorage } from '@nostr-dev-kit/sessions'; - -const sessions = new NDKSessionManager(ndk, { - storage: new LocalStorage() -}); - -// Session management independent of UI framework -await sessions.login(signer); -``` - -## Migration Steps - -### 1. Install New Package - -```bash -npm install @nostr-dev-kit/sessions -``` - -### 2. Update React Integration - -#### Before (ndk-hooks) - -```typescript -// In your app setup -import { useNDKInit, useNDKSessionMonitor } from '@nostr-dev-kit/react'; - -function NDKHeadless() { - const initNDK = useNDKInit(); - - useNDKSessionMonitor(sessionStorage, { - profile: true, - follows: true - }); - - useEffect(() => { - initNDK(ndk); - }, []); - - return null; -} -``` - -#### After (sessions package) - -```typescript -// In your app setup - sessions are now managed outside React -import NDK from '@nostr-dev-kit/ndk'; -import { NDKSessionManager, LocalStorage } from '@nostr-dev-kit/sessions'; - -// Create session manager once, outside component tree -const ndk = new NDK({ explicitRelayUrls: [...] }); -const sessions = new NDKSessionManager(ndk, { - storage: new LocalStorage() -}); - -// Initialize in app setup -await ndk.connect(); -await sessions.restore(); - -// Then use React hooks from @nostr-dev-kit/react for UI integration -function MyApp() { - // React hooks still available for reactive updates - const { ndk } = useNDK(); - const currentUser = useNDKCurrentUser(); - - return ; -} -``` - -### 3. Update Login/Logout Logic - -#### Before - -```typescript -import { useNDKSessionLogin, useNDKSessionLogout } from '@nostr-dev-kit/react'; - -function LoginComponent() { - const login = useNDKSessionLogin(); - const logout = useNDKSessionLogout(); - - const handleLogin = async () => { - await login(signer, { follows: true }); - }; - - const handleLogout = async () => { - await logout(); - }; -} -``` - -#### After - -```typescript -import { sessions } from './ndk-setup'; // Your session manager instance - -function LoginComponent() { - const handleLogin = async () => { - // Configure fetches in session manager constructor instead - await sessions.login(signer); - }; - - const handleLogout = () => { - sessions.logout(); - }; -} -``` - -### 4. Update Multi-Account Switching - -#### Before - -```typescript -import { useNDKSessionSwitch } from '@nostr-dev-kit/react'; - -function AccountSwitcher() { - const switchSession = useNDKSessionSwitch(); - - const handleSwitch = async (pubkey: string) => { - await switchSession(pubkey); - }; -} -``` - -#### After - -```typescript -import { sessions } from './ndk-setup'; - -function AccountSwitcher() { - const handleSwitch = (pubkey: string) => { - sessions.switchTo(pubkey); - }; -} -``` - -### 5. Update Session State Access - -#### Before - -```typescript -import { useNDKSessions, useNDKCurrentUser } from '@nostr-dev-kit/react'; - -function UserInfo() { - const allSessions = useNDKSessions(); - const currentUser = useNDKCurrentUser(); -} -``` - -#### After - -```typescript -import { sessions } from './ndk-setup'; -import { useNDKCurrentUser } from '@nostr-dev-kit/react'; - -function UserInfo() { - const allSessions = sessions.getSessions(); - const currentUser = useNDKCurrentUser(); // Still available from React hooks -} -``` - -## Storage Migration - -### Before (ndk-hooks) - -```typescript -import { NDKSessionLocalStorage } from '@nostr-dev-kit/react'; - -const storage = new NDKSessionLocalStorage(); -``` - -### After (sessions package) - -```typescript -import { LocalStorage } from '@nostr-dev-kit/sessions'; - -const storage = new LocalStorage(); -``` - -The storage interface is the same, just imported from the new package. - -## Complete Example - -### Before (ndk-hooks) - -```typescript -// components/ndk.tsx -import { useNDKInit, useNDKSessionMonitor, NDKSessionLocalStorage } from '@nostr-dev-kit/react'; - -const sessionStorage = new NDKSessionLocalStorage(); - -export default function NDKHeadless() { - const initNDK = useNDKInit(); - - useNDKSessionMonitor(sessionStorage, { - profile: true, - follows: true - }); - - useEffect(() => { - if (ndk) initNDK(ndk); - }, []); - - return null; -} - -// In components -function LoginButton() { - const login = useNDKSessionLogin(); - const currentUser = useNDKCurrentUser(); - - const handleLogin = async () => { - const signer = new NDKPrivateKeySigner(nsec); - await login(signer, { follows: true }); - }; - - return currentUser ? ( -
Logged in as {currentUser.npub}
- ) : ( - - ); -} -``` - -### After (sessions package) - -```typescript -// ndk-setup.ts -import NDK from '@nostr-dev-kit/ndk'; -import { NDKSessionManager, LocalStorage } from '@nostr-dev-kit/sessions'; - -export const ndk = new NDK({ explicitRelayUrls: [...] }); - -export const sessions = new NDKSessionManager(ndk, { - storage: new LocalStorage(), - autoSave: true, - fetches: { - follows: true, - mutes: true - } -}); - -// Initialize once -export async function initializeNDK() { - await ndk.connect(); - await sessions.restore(); - - // Subscribe to session changes to update NDK state - sessions.subscribe((state) => { - if (state.activePubkey) { - const session = state.sessions.get(state.activePubkey); - if (session?.signer) { - ndk.signer = session.signer; - } - } - }); -} - -// App.tsx -import { initializeNDK, ndk } from './ndk-setup'; -import { useNDKInit } from '@nostr-dev-kit/react'; - -function App() { - const initNDK = useNDKInit(); - - useEffect(() => { - initializeNDK().then(() => { - initNDK(ndk); // Connect React hooks to NDK instance - }); - }, []); - - return ; -} - -// In components -import { sessions } from './ndk-setup'; -import { useNDKCurrentUser } from '@nostr-dev-kit/react'; - -function LoginButton() { - const currentUser = useNDKCurrentUser(); - - const handleLogin = async () => { - const signer = new NDKPrivateKeySigner(nsec); - // Fetches configured in session manager constructor - await sessions.login(signer); - }; - - return currentUser ? ( -
Logged in as {currentUser.npub}
- ) : ( - - ); -} -``` - -## Breaking Changes - -### 1. Storage Location Changed - -The default storage key has changed from `'ndk-session'` to `'ndk-sessions'` (plural). To migrate existing sessions: - -```typescript -// Option 1: Specify old key -const storage = new LocalStorage('ndk-session'); - -// Option 2: Migrate data manually -const oldData = localStorage.getItem('ndk-session'); -if (oldData) { - localStorage.setItem('ndk-sessions', oldData); - localStorage.removeItem('ndk-session'); -} -``` - -### 2. Async Login - -Login is now always async and returns the pubkey: - -```typescript -// Before -login(signer); // Fire and forget - -// After -const pubkey = await sessions.login(signer); -``` - -### 3. No Auto-Monitoring Hook - -The `useNDKSessionMonitor` hook is replaced by creating a session manager: - -```typescript -// Before -useNDKSessionMonitor(storage, options); - -// After -const sessions = new NDKSessionManager(ndk, { - storage, - autoSave: true -}); -await sessions.restore(); -``` - -### 4. Direct Access Instead of Hooks - -Many operations now use direct method calls instead of hooks: - -```typescript -// Before: Hook-based -const logout = useNDKSessionLogout(); -logout(); - -// After: Direct call -sessions.logout(); -``` - -## Svelte Migration - -If you're using Svelte, migration is similar: - -### Before (ndk-svelte with hooks) - -```typescript -// Mixed approach, not clean -import { ndk } from '$lib/stores/ndk'; -``` - -### After (sessions with svelte) - -```typescript -// ndk.svelte.ts -import NDK from '@nostr-dev-kit/ndk'; -import { NDKSessionManager, LocalStorage } from '@nostr-dev-kit/sessions'; - -export const ndk = new NDK({ explicitRelayUrls: [...] }); - -export const sessions = new NDKSessionManager(ndk, { - storage: new LocalStorage(), - fetches: { - follows: true - } -}); - -// Initialize -await ndk.connect(); -await sessions.restore(); -``` - -## Troubleshooting - -### Sessions Not Persisting - -Make sure `autoSave` is enabled: - -```typescript -const sessions = new NDKSessionManager(ndk, { - storage: new LocalStorage(), - autoSave: true // โœ“ Enable auto-save -}); -``` - -### Signer Not Set on NDK - -Subscribe to session changes and update NDK: - -```typescript -sessions.subscribe((state) => { - if (state.activePubkey) { - const session = state.sessions.get(state.activePubkey); - if (session?.signer) { - ndk.signer = session.signer; - ndk.activeUser = session.user; - } - } -}); -``` - -### React Not Re-rendering - -Make sure you've initialized NDK with the React store: - -```typescript -import { useNDKInit } from '@nostr-dev-kit/react'; - -const initNDK = useNDKInit(); -useEffect(() => { - initNDK(ndk); -}, []); -``` - -## Need Help? - -- [Quick Start Guide](./quick-start) - Fresh start with sessions -- [API Reference](./api) - Complete API documentation -- [GitHub Issues](https://github.com/nostr-dev-kit/ndk/issues) - Report migration issues diff --git a/docs/sessions/quick-start.md b/docs/sessions/quick-start.md deleted file mode 100644 index 58290d2b5..000000000 --- a/docs/sessions/quick-start.md +++ /dev/null @@ -1,260 +0,0 @@ -# Quick Start - -Get started with NDK Sessions in minutes. - -## Installation - -```bash -npm install @nostr-dev-kit/sessions @nostr-dev-kit/ndk -# or -bun add @nostr-dev-kit/sessions @nostr-dev-kit/ndk -``` - -## Basic Setup - -### 1. Initialize NDK - -First, create and connect your NDK instance: - -```typescript -import NDK from '@nostr-dev-kit/ndk'; - -const ndk = new NDK({ - explicitRelayUrls: [ - 'wss://relay.damus.io', - 'wss://nos.lol', - 'wss://relay.nostr.band' - ] -}); - -await ndk.connect(); -``` - -### 2. Create Session Manager - -Create a session manager with your preferred storage: - -```typescript -import { NDKSessionManager, LocalStorage } from '@nostr-dev-kit/sessions'; - -const sessions = new NDKSessionManager(ndk, { - storage: new LocalStorage(), - autoSave: true, // Automatically save changes - saveDebounceMs: 500 // Debounce auto-saves -}); -``` - -### 3. Restore Previous Sessions - -Restore any previously saved sessions: - -```typescript -await sessions.restore(); - -if (sessions.activeUser) { - console.log('Welcome back!', sessions.activeUser.npub); -} -``` - -### 4. Login - -Login with a signer. To automatically fetch user data, configure `fetches` in the constructor: - -```typescript -import { NDKPrivateKeySigner } from '@nostr-dev-kit/ndk'; - -const sessions = new NDKSessionManager(ndk, { - storage: new LocalStorage(), - autoSave: true, - fetches: { - follows: true, // Fetch contact list - mutes: true, // Fetch mute list - relayList: true, // Fetch relay list - wallet: true // Fetch NIP-60 wallet - } -}); - -const signer = new NDKPrivateKeySigner(nsecKey); -await sessions.login(signer); - -// Access session data -console.log('Following:', sessions.activeSession?.followSet?.size, 'users'); -console.log('Muted:', sessions.activeSession?.muteSet?.size, 'items'); -``` - -## Storage Options - -### Browser (LocalStorage) - -```typescript -import { LocalStorage } from '@nostr-dev-kit/sessions'; - -const sessions = new NDKSessionManager(ndk, { - storage: new LocalStorage('my-app-sessions') // Custom key -}); -``` - -### Node.js (FileStorage) - -```typescript -import { FileStorage } from '@nostr-dev-kit/sessions'; - -const sessions = new NDKSessionManager(ndk, { - storage: new FileStorage('./.ndk-sessions.json') -}); -``` - -### Temporary (MemoryStorage) - -```typescript -import { MemoryStorage } from '@nostr-dev-kit/sessions'; - -const sessions = new NDKSessionManager(ndk, { - storage: new MemoryStorage(), // No persistence - autoSave: false -}); -``` - -## Multi-Account Management - -### Login Multiple Accounts - -```typescript -// Login first account (automatically active) -const signer1 = new NDKPrivateKeySigner(nsec1); -const pubkey1 = await sessions.login(signer1); - -// Login second account -const signer2 = new NDKPrivateKeySigner(nsec2); -const pubkey2 = await sessions.login(signer2, { setActive: false }); - -console.log('Accounts:', sessions.getSessions().size); -``` - -### Switch Between Accounts - -```typescript -// Switch to different account -sessions.switchTo(pubkey2); -console.log('Active:', sessions.activePubkey); - -// Switch back -sessions.switchTo(pubkey1); -``` - -### Logout - -```typescript -// Logout specific account -sessions.logout(pubkey1); - -// Or logout current active account -sessions.logout(); -``` - -## React to Changes - -Subscribe to session changes: - -```typescript -const unsubscribe = sessions.subscribe((state) => { - console.log('Active user:', state.activePubkey); - console.log('Total sessions:', state.sessions.size); - - // Update your UI... -}); - -// Later, cleanup -unsubscribe(); -``` - -## Read-Only Sessions - -Create a read-only session without a signer. Configure `fetches` in the constructor: - -```typescript -const sessions = new NDKSessionManager(ndk, { - fetches: { - follows: true, - relayList: true - } -}); - -const user = ndk.getUser({ pubkey: somePubkey }); -await sessions.login(user); - -// Data is fetched and cached, but user can't sign events -``` - -## Using with NIP-07 (Browser Extensions) - -```typescript -import { NDKNip07Signer } from '@nostr-dev-kit/ndk'; - -const sessions = new NDKSessionManager(ndk, { - fetches: { - follows: true, - mutes: true - } -}); - -const signer = new NDKNip07Signer(); -await sessions.login(signer); -``` - -## CLI Example - -Complete example for a Node.js CLI tool: - -```typescript -#!/usr/bin/env node -import NDK from '@nostr-dev-kit/ndk'; -import { NDKSessionManager, FileStorage } from '@nostr-dev-kit/sessions'; -import { NDKPrivateKeySigner } from '@nostr-dev-kit/ndk'; - -const ndk = new NDK({ explicitRelayUrls: ['wss://relay.damus.io'] }); -await ndk.connect(); - -const sessions = new NDKSessionManager(ndk, { - storage: new FileStorage('./.ndk-sessions.json'), - autoSave: true, - fetches: { - follows: true - } -}); - -// Restore previous session -await sessions.restore(); - -if (!sessions.activeUser) { - // First time - login - const nsec = process.env.NOSTR_NSEC; - if (!nsec) throw new Error('NOSTR_NSEC not set'); - - const signer = new NDKPrivateKeySigner(nsec); - await sessions.login(signer); - - console.log('Logged in as', sessions.activeUser.npub); -} else { - console.log('Welcome back', sessions.activeUser.npub); -} - -// Use the active session to publish -const event = new NDKEvent(ndk, { - kind: 1, - content: 'Hello from CLI!' -}); - -await event.publish(); -console.log('Published:', event.id); - -// Cleanup -sessions.destroy(); -``` - -## Next Steps - -- [API Reference](./api) - Complete API documentation -- [Migration Guide](./migration) - Migrating from ndk-hooks -- [React Hooks](/hooks/session-management) - Using with React -- [Svelte](/wrappers/svelte) - Using with Svelte diff --git a/docs/wot/index.md b/docs/wot/index.md deleted file mode 100644 index d2884962a..000000000 --- a/docs/wot/index.md +++ /dev/null @@ -1,340 +0,0 @@ -# Web of Trust (WOT) - -`@nostr-dev-kit/wot` provides Web of Trust utilities for filtering and ranking content based on your social graph. - -## Installation - -```bash -npm install @nostr-dev-kit/wot -``` - -## Quick Start - -```typescript -import NDK from "@nostr-dev-kit/ndk"; -import { NDKWoT, filterByWoT, rankByWoT } from "@nostr-dev-kit/wot"; - -const ndk = new NDK(); -await ndk.connect(); - -// Build WOT graph from your perspective -const wot = new NDKWoT(ndk, myPubkey); -await wot.load({ - depth: 2, // 2 hops out from you - maxFollows: 1000, // limit follows per user - timeout: 30000 // 30s timeout -}); - -console.log(`WOT has ${wot.size} users`); -``` - -## Filtering Events - -The key insight of WOT is to **fetch broadly, filter locally**. This lets you adjust WOT strictness without re-fetching from relays. - -```typescript -// Fetch events broadly -const events = await ndk.fetchEvents({ - kinds: [1], - limit: 500 -}); - -// Filter by WOT -const filtered = filterByWoT(wot, events, { - maxDepth: 2, // only users within 2 hops - minScore: 0.5, // minimum WOT score (0-1) - includeUnknown: false // exclude users outside WOT -}); - -// Easily adjust view without re-fetching -const broader = filterByWoT(wot, events, { maxDepth: 3 }); -const strictest = filterByWoT(wot, events, { maxDepth: 1 }); -``` - -## WOT as Automatic Mute Filter - -You can integrate WOT with NDK's `muteFilter` to automatically filter out users outside your web of trust at the NDK level. This means all subscriptions and event fetches will automatically exclude non-WOT users. - -```typescript -import NDK, { NDKEvent } from "@nostr-dev-kit/ndk"; -import { NDKWoT } from "@nostr-dev-kit/wot"; - -const ndk = new NDK(); -await ndk.connect(); - -// Build WOT graph -const wot = new NDKWoT(ndk, myPubkey); -await wot.load({ depth: 2 }); - -// Set WOT-based mute filter -ndk.muteFilter = (event: NDKEvent) => { - // Check manual mutes first - if (ndk.mutedIds.has(event.pubkey)) return true; - if (event.id && ndk.mutedIds.has(event.id)) return true; - - // Auto-mute users outside WOT - if (!wot.includes(event.pubkey, { maxDepth: 2 })) { - return true; // Mute this event - } - - return false; // Don't mute -}; - -// Now all subscriptions automatically filter by WOT -const sub = ndk.subscribe({ kinds: [1], limit: 100 }); -sub.on('event', (event) => { - // Only events from WOT users will appear here - console.log(event.content); -}); -``` - -### Hybrid Approach: WOT + Manual Mutes + Keywords - -Combine WOT with manual mutes and content filtering: - -```typescript -ndk.muteFilter = (event: NDKEvent) => { - // 1. Manual mutes (highest priority) - if (ndk.mutedIds.has(event.pubkey)) return true; - if (event.id && ndk.mutedIds.has(event.id)) return true; - - // 2. WOT check (only for unknown users) - if (!wot.includes(event.pubkey, { maxDepth: 2 })) { - return true; // Auto-mute non-WOT users - } - - // 3. Content filtering (even for WOT users) - const blockedWords = ['spam', 'scam']; - if (blockedWords.some(word => event.content.toLowerCase().includes(word))) { - return true; - } - - return false; -}; -``` - -### Adjustable WOT Strictness - -Allow users to adjust WOT strictness dynamically: - -```typescript -let wotDepth = 2; // Default: 2 hops - -ndk.muteFilter = (event: NDKEvent) => { - if (ndk.mutedIds.has(event.pubkey)) return true; - - // Use current depth setting - return !wot.includes(event.pubkey, { maxDepth: wotDepth }); -}; - -// User can adjust strictness -function setWoTStrictness(depth: number) { - wotDepth = depth; - // Trigger UI refresh to re-apply filter -} - -// Slider: 1 (strictest) -> 3 (most permissive) -setWoTStrictness(1); // Only direct follows -setWoTStrictness(2); // Friends of friends -setWoTStrictness(3); // 3 hops out -``` - -### WOT-Based Score Thresholds - -Filter by WOT score instead of depth: - -```typescript -const MIN_WOT_SCORE = 0.5; // 0-1 scale - -ndk.muteFilter = (event: NDKEvent) => { - if (ndk.mutedIds.has(event.pubkey)) return true; - - // Mute if score too low - const score = wot.getScore(event.pubkey); - if (score < MIN_WOT_SCORE) { - return true; - } - - return false; -}; -``` - -## Ranking Events - -Sort events by WOT proximity: - -```typescript -// Rank by distance (closer = higher) -const ranked = rankByWoT(wot, events, { - algorithm: "distance", // "distance", "score", or "followers" - unknownsLast: true // put unknown users at the end -}); - -// Custom ranking -const custom = rankByWoT(wot, events, { - comparator: (a, b) => { - const aScore = wot.getScore(a.pubkey); - const bScore = wot.getScore(b.pubkey); - return bScore - aScore; - } -}); -``` - -## WOT Queries - -```typescript -// Check if user is in WOT -if (wot.includes(pubkey, { maxDepth: 2 })) { - console.log("User is in WOT"); -} - -// Get WOT score (0-1, higher = closer) -const score = wot.getScore(pubkey); - -// Get distance (hops from root) -const distance = wot.getDistance(pubkey); // returns number or null - -// Get scores for multiple users -const scores = wot.getScores([pubkey1, pubkey2, pubkey3]); - -// Get all pubkeys in WOT -const allUsers = wot.getAllPubkeys({ maxDepth: 2 }); -``` - -## Ranking Algorithms - -### Distance -Ranks by graph distance from root user. Closer users rank higher. - -```typescript -rankByWoT(wot, events, { algorithm: "distance" }); -``` - -### Score -Ranks by WOT score (inverse of depth: `1 / (depth + 1)`). Higher scores rank higher. - -```typescript -rankByWoT(wot, events, { algorithm: "score" }); -``` - -### Followers -Ranks by number of WOT users following them. More popular within your WOT ranks higher. - -```typescript -rankByWoT(wot, events, { algorithm: "followers" }); -``` - -## Integration with Reactive Frameworks - -Use with `svelte` or `ndk-hooks` for reactive WOT views: - -### Svelte 5 (coming soon) -```typescript -import { wotView } from '@nostr-dev-kit/svelte'; - -const view = wotView(wot, events, { - maxDepth: $state(2), - showUnknowns: $state(false) -}); - -// Reactive -$effect(() => { - console.log(view.filtered); -}); -``` - -### React (coming soon) -```typescript -import { useWoTView } from '@nostr-dev-kit/react'; - -function Feed() { - const [depth, setDepth] = useState(2); - - const { filtered, ranked } = useWoTView(wot, events, { - maxDepth: depth - }); - - return ; -} -``` - -## API Reference - -### NDKWoT - -- `constructor(ndk: NDK, rootPubkey: string)` - Create WOT instance -- `load(options: WoTBuildOptions): Promise` - Build the graph -- `getScore(pubkey: string): number` - Get WOT score (0-1) -- `getDistance(pubkey: string): number | null` - Get hops from root -- `includes(pubkey: string, options?): boolean` - Check if in WOT -- `getAllPubkeys(options?): string[]` - Get all pubkeys in WOT -- `getScores(pubkeys: string[]): Map` - Batch scores -- `getNode(pubkey: string): WoTNode | null` - Get WOT node details -- `size: number` - Total nodes in graph -- `isLoaded(): boolean` - Check if graph is loaded - -### Filter Functions - -#### filterByWoT -```typescript -filterByWoT( - wot: NDKWoT, - events: NDKEvent[], - options: WoTFilterOptions -): NDKEvent[] -``` - -#### rankByWoT -```typescript -rankByWoT( - wot: NDKWoT, - events: NDKEvent[], - options: WoTRankOptions -): NDKEvent[] -``` - -#### createWoTComparator -```typescript -createWoTComparator( - wot: NDKWoT, - options: WoTRankOptions -): (a: NDKEvent, b: NDKEvent) => number -``` - -## Types - -### WoTBuildOptions -```typescript -interface WoTBuildOptions { - depth: number; // Max hops to traverse - maxFollows?: number; // Max follows per user - timeout?: number; // Timeout in ms -} -``` - -### WoTFilterOptions -```typescript -interface WoTFilterOptions { - maxDepth?: number; // Max depth to include - minScore?: number; // Min WOT score (0-1) - includeUnknown?: boolean; // Include non-WOT users -} -``` - -### WoTRankOptions -```typescript -interface WoTRankOptions { - algorithm?: "distance" | "score" | "followers"; - unknownsLast?: boolean; - comparator?: (a: NDKEvent, b: NDKEvent) => number; -} -``` - -### WoTNode -```typescript -interface WoTNode { - pubkey: string; - depth: number; // Hops from root - followedBy: Set; // Who follows this user -} -``` From 9c1418861b850729d2c9d1d54a83ab4279ef08d5 Mon Sep 17 00:00:00 2001 From: digitalbase Date: Wed, 22 Oct 2025 20:38:32 +0200 Subject: [PATCH 063/139] Do not show title on first nav link --- .vitepress/config.mts | 1 - 1 file changed, 1 deletion(-) diff --git a/.vitepress/config.mts b/.vitepress/config.mts index 70db57e70..82cdda661 100644 --- a/.vitepress/config.mts +++ b/.vitepress/config.mts @@ -28,7 +28,6 @@ export default defineConfig({ sidebar: [ { - text: "Nostr Development Kit", items: [ {text: "Introduction", link: "/core/docs/introduction"}, {text: "Contributing", link: "/docs/contributing.md"}, From f43a21b7909295706a759732ee1451c2cce49f82 Mon Sep 17 00:00:00 2001 From: digitalbase Date: Wed, 22 Oct 2025 20:38:46 +0200 Subject: [PATCH 064/139] Allow snippet reuse in different docs sections --- core/docs/fundamentals/events.md | 5 +++-- core/docs/fundamentals/signers.md | 31 +++++--------------------- core/docs/getting-started/usage.md | 4 ++-- core/docs/snippets/key_create.ts | 8 +++++++ core/docs/snippets/sign_with_bunker.ts | 14 ++++++++++++ core/snippets/user/generate-keys.md | 10 +-------- docs/cookbook/README.md | 0 docs/cookbook/TEMPLATE.md | 0 docs/snippets.md | 20 ++--------------- 9 files changed, 36 insertions(+), 56 deletions(-) create mode 100644 core/docs/snippets/key_create.ts create mode 100644 core/docs/snippets/sign_with_bunker.ts delete mode 100644 docs/cookbook/README.md delete mode 100644 docs/cookbook/TEMPLATE.md diff --git a/core/docs/fundamentals/events.md b/core/docs/fundamentals/events.md index c807e5d15..6ed1602fc 100644 --- a/core/docs/fundamentals/events.md +++ b/core/docs/fundamentals/events.md @@ -43,7 +43,8 @@ NDK uses the default signer `ndk.signer` to sign events. Read more about signers in [the signer documentation](/core/docs/fundamentals/signers.md) +## Code Snippets -## More examples +More snippets and examples can be found in the [snippets directory](/docs/snippets.md). -Additional example snippets of events can be found in the [snippets directory](/docs/snippets.md). \ No newline at end of file + diff --git a/core/docs/fundamentals/signers.md b/core/docs/fundamentals/signers.md index 3e9bbefdc..a7e8ad803 100644 --- a/core/docs/fundamentals/signers.md +++ b/core/docs/fundamentals/signers.md @@ -68,24 +68,7 @@ To add remote signing support to your application, there are a few things you ne Create a `NDKNip46Signer` with the bunker connection string and local keypair. -```ts -// provided by the user -const signerConnectionString = 'bunker://....'; - -// local keypair generated when signer if first initialised -const clientKeypair = NDKPrivateKeySigner.generate(); // -const clientNsec = clientKeypair.nsec; - -// initiate NIP-46 signer -const signer = NDKNip46Signer.bunker(ndk, signerConnectionString, clientNsec); - -// promise will resolve once the `kind:24133` event is received -const user = await signer.blockUntilReady(); - -console.log("Welcome", user.npub); -``` - - +<<< @/core/docs/snippets/sign_with_bunker.ts ## Sign Events @@ -122,13 +105,11 @@ nip07signer.user().then(async (user) => { Perhaps the only time we really want you to use the `NDKPrivateKeySigner` is to help you generate new keys as the signer provides helper methods to do just that: -```ts -import { NDKPrivateKeySigner } from "@nostr-dev-kit/ndk"; +<<< @/core/docs/snippets/key_create.ts -// Generate a new private key -const signer = NDKPrivateKeySigner.generate(); -console.log("nsec:", signer.nsec); -console.log("npub:", signer.npub); -``` +## Code Snippets + +More snippets and examples can be found in the [snippets directory](/docs/snippets.md). + diff --git a/core/docs/getting-started/usage.md b/core/docs/getting-started/usage.md index dfb9bc2cf..9e254f192 100644 --- a/core/docs/getting-started/usage.md +++ b/core/docs/getting-started/usage.md @@ -5,8 +5,8 @@ You can pass an object with several options to a newly created instance of NDK. - `explicitRelayUrls` โ€“ an array of relay URLs. -- `signer` - an instance of a [signer](#signers). -- `cacheAdapter` - an instance of a [Cache Adapter](#caching) +- `signer` - an instance of a [signer](/core/docs/fundamentals/signers.html). +- `cacheAdapter` - an instance of a [cache adapter](#caching) - `debug` - Debug instance to use for logging. Defaults to `debug("ndk")`. ```ts diff --git a/core/docs/snippets/key_create.ts b/core/docs/snippets/key_create.ts new file mode 100644 index 000000000..5d1234f57 --- /dev/null +++ b/core/docs/snippets/key_create.ts @@ -0,0 +1,8 @@ +import { NDKPrivateKeySigner } from "@nostr-dev-kit/ndk"; + +// Generate a new private key +const signer = NDKPrivateKeySigner.generate(); +const privateKey = signer.privateKey; // Get the hex private key +const publicKey = signer.pubkey; // Get the hex public key +const nsec = signer.nsec; // Get the private key in nsec format +const npub = signer.userSync.npub; // Get the public key in npub format diff --git a/core/docs/snippets/sign_with_bunker.ts b/core/docs/snippets/sign_with_bunker.ts new file mode 100644 index 000000000..dbdce5af1 --- /dev/null +++ b/core/docs/snippets/sign_with_bunker.ts @@ -0,0 +1,14 @@ +// provided by the user +const signerConnectionString = "bunker://...."; + +// local keypair generated when signer if first initialised +const clientKeypair = NDKPrivateKeySigner.generate(); // +const clientNsec = clientKeypair.nsec; + +// initiate NIP-46 signer +const signer = NDKNip46Signer.bunker(ndk, signerConnectionString, clientNsec); + +// promise will resolve once the `kind:24133` event is received +const user = await signer.blockUntilReady(); + +console.log("Welcome", user.npub); diff --git a/core/snippets/user/generate-keys.md b/core/snippets/user/generate-keys.md index 1d7c74b98..7860ff0e6 100644 --- a/core/snippets/user/generate-keys.md +++ b/core/snippets/user/generate-keys.md @@ -2,15 +2,7 @@ This snippet demonstrates how to generate a new key pair and obtain all its various formats (private key, public key, nsec, npub). -```typescript -import { NDKPrivateKeySigner } from "@nostr-dev-kit/ndk"; - -const signer = NDKPrivateKeySigner.generate(); -const privateKey = signer.privateKey!; // Get the hex private key -const publicKey = signer.pubkey; // Get the hex public key -const nsec = signer.nsec; // Get the private key in nsec format -const npub = signer.userSync.npub; // Get the public key in npub format -``` +<<< @/core/docs/snippets/key_create.ts You can use these different formats for different purposes: diff --git a/docs/cookbook/README.md b/docs/cookbook/README.md deleted file mode 100644 index e69de29bb..000000000 diff --git a/docs/cookbook/TEMPLATE.md b/docs/cookbook/TEMPLATE.md deleted file mode 100644 index e69de29bb..000000000 diff --git a/docs/snippets.md b/docs/snippets.md index 0c90323df..2b2b4c7e4 100644 --- a/docs/snippets.md +++ b/docs/snippets.md @@ -7,27 +7,11 @@ Snippets are grouped by category. Some of them are listed in more than one categ ## Events -### Creating a basic event - -<<< @/core/docs/snippets/create_event.ts - -### Tagging users - -<<< @/core/docs/snippets/tag_user.ts - -### Signing events - -<<< @/core/docs/snippets/sign_event.ts + ## Signers -### Signing events - -<<< @/core/docs/snippets/sign_event.ts - -### Different signers - -<<< @/core/docs/snippets/sign_event_with_other_signers.ts + ## Not migrated yet From cbf12c81d934df326e097066941ecd9775548ca5 Mon Sep 17 00:00:00 2001 From: digitalbase Date: Wed, 22 Oct 2025 21:08:02 +0200 Subject: [PATCH 065/139] Move more snippets + cleanup --- core/docs/fundamentals/connecting.md | 69 ++-------------------- core/docs/fundamentals/events.md | 7 --- core/docs/fundamentals/publishing.md | 20 ++++--- core/docs/fundamentals/signers.md | 17 +++--- core/docs/snippets/connect_dev_relays.ts | 7 +++ core/docs/snippets/connect_explicit.ts | 10 ++++ core/docs/snippets/connect_explicit_alt.ts | 11 ++++ core/docs/snippets/connect_nip07.ts | 7 +++ core/docs/snippets/connect_pools.ts | 12 ++++ core/docs/snippets/replace_event.ts | 2 +- docs/snippets.md | 7 +++ 11 files changed, 82 insertions(+), 87 deletions(-) create mode 100644 core/docs/snippets/connect_dev_relays.ts create mode 100644 core/docs/snippets/connect_explicit.ts create mode 100644 core/docs/snippets/connect_explicit_alt.ts create mode 100644 core/docs/snippets/connect_nip07.ts create mode 100644 core/docs/snippets/connect_pools.ts diff --git a/core/docs/fundamentals/connecting.md b/core/docs/fundamentals/connecting.md index d1dd9051c..d019bef95 100644 --- a/core/docs/fundamentals/connecting.md +++ b/core/docs/fundamentals/connecting.md @@ -29,37 +29,14 @@ advised ways of connecting to relays. We strongly advise you to use the "[Outbox The simplest way to get NDK to connect to relays is to specify them: -```ts -// Import the package -import NDK from "@nostr-dev-kit/ndk"; - -// Create a new NDK instance with explicit relays -const ndk = new NDK({ - explicitRelayUrls: ["wss://a.relay", "wss://another.relay"], -}); - -// Now connect to specified relays -await ndk.connect(); -``` +<<< @/core/docs/snippets/connect_explicit.ts Make sure to wait for the `connect()` promise to resolve before using NDK after which you can start interacting with relays. Explicit relays can also be added using the `addExplicitRelay()` method. -```ts -// Import the package -import NDK from "@nostr-dev-kit/ndk"; - -// Create a new NDK instance with explicit relays -const ndk = new NDK(); - -ndk.addExplicitRelay("wss://a.relay"); -ndk.addExplicitRelay("wss://another.relay"); - -// Now connect to specified relays -await ndk.connect(); -``` +<<< @/core/docs/snippets/connect_explicit_alt.ts ### User preferred relays @@ -68,14 +45,7 @@ settings. Once you link up a signer and you have `autoConnectUserRelays` enabled (on by default) NDK will fetch your `kind:10002` event ([NIP-65](https://nostr-nips.com/nip-65)) and add discovered relays specified to a 2nd pool of connected relays. -```ts -// Import NDK + NIP07 signer -import NDK, { NDKNip07Signer } from "@nostr-dev-kit/ndk"; - -// Create a new NDK instance with signer (provided the signer implements the getRelays() method) -const nip07signer = new NDKNip07Signer(); -const ndk = new NDK({ signer: nip07signer }); -``` +<<< @/core/docs/snippets/connect_nip07.ts ### Outbox Model @@ -104,15 +74,7 @@ https://primal.net/e/nevent1qqs2txvkjpa6fdlhxdtqmyk2tzzchpaa4vrfgx7h20539u5k9lzg During local development you might want to specify a list of relays to write to. THis can be done by using `devWriteRelayUrls` which will -```ts -import NDK from "@nostr-dev-kit/ndk"; - -const ndk = new NDK({ - devWriteRelayUrls: ["wss://staging.relay", "wss://another.test.relay"], -}); - -await ndk.connect(); -``` +<<< @/core/docs/snippets/connect_dev_relays.ts This will write new events to those relays only. Note that if you have provided relays in `explicitRelayUrls` these will also be used to write events to. @@ -122,28 +84,7 @@ This will write new events to those relays only. Note that if you have provided Under the hood NDK uses different sets of relays to send and receive messages. You can tap into that pool logic by using the `NDKPool` class. -```typescript - -import NDK, {NDKEvent, NDKPool} from "@nostr-dev-kit/ndk"; - -const ndk = new NDK(); - -const largeRelays = new NDKPool([ - `wss://relay.damus.io`, - 'wss://premium.primal.net' -], ndk); -largeRelays.addRelay(`wss://nos.lol`) - -const nicheRelays = new NDKPool([ - `wss://asad`, - 'wss://premisadasdum.primal.net' -], ndk); - -largeRelays.connect(); - -ndk.pools.length; // 2 - -``` +<<< @/core/docs/snippets/connect_pools.ts Note that if you have outbox enabled you will have an extra pool in the `ndk.pools` array reserved for user provided relays. diff --git a/core/docs/fundamentals/events.md b/core/docs/fundamentals/events.md index 6ed1602fc..21aa10c6a 100644 --- a/core/docs/fundamentals/events.md +++ b/core/docs/fundamentals/events.md @@ -28,13 +28,6 @@ The resulting event will look like: <<< @/core/docs/snippets/tag_user_result.json -## Interest event - -Interest events are used to tell the network about your interest in a particular topic. Those events and are making use -of the [NIP-51](https://nostr-nips.com/nip-51) specification `kind:10015` events. - -<<< @/core/docs/snippets/interest_event.ts - ## Signing Events NDK uses the default signer `ndk.signer` to sign events. diff --git a/core/docs/fundamentals/publishing.md b/core/docs/fundamentals/publishing.md index 973b0bcd7..37e3b0af8 100644 --- a/core/docs/fundamentals/publishing.md +++ b/core/docs/fundamentals/publishing.md @@ -1,13 +1,15 @@ # Publishing Events -Publishing events means sending them to one or multiple relays as described in -[NIP-01](https://nostr-nips.com/nip-01#communication-between-clients-and-relays). - -NDK provides easy ways to publish events and manage the status of that event. +Publishing events means sending them to one or multiple relays as described in +[NIP-01](https://nostr-nips.com/nip-01#communication-between-clients-and-relays). NDK provides easy ways to publish +events and manage the status of that event. > [!NOTE] -> Please note that the behavior of `.publish()` requires a valid signer and will only publish the events to the -configured relays. More about relay interaction in the [connecting documentation](/core/docs/fundamentals/connecting.md). +> Please note that the behavior of `.publish()` requires a valid [signer](/core/docs/fundamentals/signers.html) and +> will only publish the events to the configured relays. +> More about relay interaction in the [connecting documentation](/core/docs/fundamentals/connecting.md). + +[//]: # (Read more about the [local-first](local-first.md) mode of operation.) ## Publishing Events @@ -39,6 +41,8 @@ and `created_at` to allow for easy replacement: `event.publishReplaceable()` - `event.publishStatus` - Overall status: "pending", "success", or "error" - `event.publishError` - Error if the overall publish failed -## More +## Code Snippets + +More snippets and examples can be found in the [snippets directory](/docs/snippets.md). -Read more about the [local-first](local-first.md) mode of operation. \ No newline at end of file + diff --git a/core/docs/fundamentals/signers.md b/core/docs/fundamentals/signers.md index a7e8ad803..88198bfdd 100644 --- a/core/docs/fundamentals/signers.md +++ b/core/docs/fundamentals/signers.md @@ -75,15 +75,18 @@ Create a `NDKNip46Signer` with the bunker connection string and local keypair. Once the signer is initialized, you can use it to sign and [publish](/core/docs/fundamentals/publishing.html) events: ```ts -const ndkEvent = new NDKEvent(ndk); -ndkEvent.kind = 1; -ndkEvent.content = "Hello, world!"; -await ndkEvent.sign(); // [!code focus] +const event = new NDKEvent(ndk); +event.kind = 1; +event.content = "Hello, world!"; +await event.sign(); // [!code focus] ``` ## Combining signers -You can specify the use of a different signer to sign with different pubkeys. +You can specify the use of a different signer to sign with different keys. + +> [!TIP] +> If you plan on allowing multiple signers we recommend using [@nostr-dev-kit/sessions](/sessions/README.html). <<< @/core/docs/snippets/sign_event_with_other_signers.ts @@ -102,8 +105,8 @@ nip07signer.user().then(async (user) => { ## Generate Keys -Perhaps the only time we really want you to use the `NDKPrivateKeySigner` is to help you generate new keys as the signer -provides helper methods to do just that: +One good case where weo do want you to use `NDKPrivateKeySigner` is to help you generate keys as the signer +provides helper methods: <<< @/core/docs/snippets/key_create.ts diff --git a/core/docs/snippets/connect_dev_relays.ts b/core/docs/snippets/connect_dev_relays.ts new file mode 100644 index 000000000..e3737eb2a --- /dev/null +++ b/core/docs/snippets/connect_dev_relays.ts @@ -0,0 +1,7 @@ +import NDK from "@nostr-dev-kit/ndk"; + +const ndk = new NDK({ + devWriteRelayUrls: ["wss://staging.relay", "wss://another.test.relay"], +}); + +await ndk.connect(); diff --git a/core/docs/snippets/connect_explicit.ts b/core/docs/snippets/connect_explicit.ts new file mode 100644 index 000000000..efab0ffbf --- /dev/null +++ b/core/docs/snippets/connect_explicit.ts @@ -0,0 +1,10 @@ +// Import the package +import NDK from "@nostr-dev-kit/ndk"; + +// Create a new NDK instance with explicit relays +const ndk = new NDK({ + explicitRelayUrls: ["wss://a.relay", "wss://another.relay"], +}); + +// Now connect to specified relays +await ndk.connect(); diff --git a/core/docs/snippets/connect_explicit_alt.ts b/core/docs/snippets/connect_explicit_alt.ts new file mode 100644 index 000000000..6f67f1d9a --- /dev/null +++ b/core/docs/snippets/connect_explicit_alt.ts @@ -0,0 +1,11 @@ +// Import the package +import NDK from "@nostr-dev-kit/ndk"; + +// Create a new NDK instance with explicit relays +const ndk = new NDK(); + +ndk.addExplicitRelay("wss://a.relay"); +ndk.addExplicitRelay("wss://another.relay"); + +// Now connect to specified relays +await ndk.connect(); diff --git a/core/docs/snippets/connect_nip07.ts b/core/docs/snippets/connect_nip07.ts new file mode 100644 index 000000000..ead101567 --- /dev/null +++ b/core/docs/snippets/connect_nip07.ts @@ -0,0 +1,7 @@ +// Import NDK + NIP07 signer +import NDK, { NDKNip07Signer } from "@nostr-dev-kit/ndk"; + +// Create a new NDK instance with signer +// (provided the signer implements the getRelays() method) +const nip07signer = new NDKNip07Signer(); +const ndk = new NDK({ signer: nip07signer }); diff --git a/core/docs/snippets/connect_pools.ts b/core/docs/snippets/connect_pools.ts new file mode 100644 index 000000000..a0768ae03 --- /dev/null +++ b/core/docs/snippets/connect_pools.ts @@ -0,0 +1,12 @@ +import NDK, { NDKPool, NDKRelay } from "@nostr-dev-kit/ndk"; + +const ndk = new NDK(); + +const largeRelays = new NDKPool([`wss://relay.damus.io`, "wss://premium.primal.net"], ndk); +largeRelays.addRelay(new NDKRelay("wss://nos.lol", undefined, ndk)); + +const nicheRelays = new NDKPool([`wss://asad`, "wss://premisadasdum.primal.net"], ndk); + +nicheRelays.connect(); + +ndk.pools.length; // 2 diff --git a/core/docs/snippets/replace_event.ts b/core/docs/snippets/replace_event.ts index 58725c876..7a62d932b 100644 --- a/core/docs/snippets/replace_event.ts +++ b/core/docs/snippets/replace_event.ts @@ -14,4 +14,4 @@ await event.publish(); await event.publish(); // this will create a new event and publish it -await event.publishReplaceable(); // [!code focus] +await event.publishReplaceable(); diff --git a/docs/snippets.md b/docs/snippets.md index 2b2b4c7e4..c62f2aada 100644 --- a/docs/snippets.md +++ b/docs/snippets.md @@ -13,6 +13,13 @@ Snippets are grouped by category. Some of them are listed in more than one categ +## Publishing + + + +## Connecting + + ## Not migrated yet From af7e9cbbf426164c6a5fc245e79bb1b9b0f13e4f Mon Sep 17 00:00:00 2001 From: digitalbase Date: Wed, 22 Oct 2025 21:20:08 +0200 Subject: [PATCH 066/139] Added warnings to the docs repo --- .vitepress/config.mts | 2 +- core/docs/introduction.md | 4 ++++ docs/index.md | 7 ++++++- 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/.vitepress/config.mts b/.vitepress/config.mts index 82cdda661..5ff6bb49e 100644 --- a/.vitepress/config.mts +++ b/.vitepress/config.mts @@ -19,7 +19,6 @@ export default defineConfig({ // https://vitepress.dev/reference/default-theme-config nav: [ { text: "Home", link: "/docs/index.md" }, - {text: "About Nostr", link: "/core/docs/about-nostr"}, // { text: "API Reference", link: "/api/", target: "_blank" }, // { text: "Cookbook", link: "/cookbook/" }, { text: "Snippets", link: "/docs/snippets.md" }, @@ -30,6 +29,7 @@ export default defineConfig({ { items: [ {text: "Introduction", link: "/core/docs/introduction"}, + {text: "About Nostr", link: "/core/docs/about-nostr"}, {text: "Contributing", link: "/docs/contributing.md"}, {text: "Changelog", link: "/docs/changelogs.md"}, ], diff --git a/core/docs/introduction.md b/core/docs/introduction.md index 583df3ecf..bd40318c7 100644 --- a/core/docs/introduction.md +++ b/core/docs/introduction.md @@ -2,6 +2,10 @@ NDK is a TypeScript/JavaScript library that simplifies building Nostr clients, relays, and related applications. +> [!WARNING] +> The documentation of the NDK project is under heavy construction. +> [More information available in open pull request](https://github.com/nostr-dev-kit/ndk/pull/344). + ## Features - Outbox model support diff --git a/docs/index.md b/docs/index.md index 7624ed4e1..60dfcbed4 100644 --- a/docs/index.md +++ b/docs/index.md @@ -19,4 +19,9 @@ NDK (**Nostr Development Kit**) is a comprehensive type-safe Typescript toolkit The library is a monorepo containing different packages and tools you need to create modern, performant, and feature-rich Nostr clients and applications. The package contains relay interaction code, reactive UI bindings, wallet abstractions, -caching libraries, Web of Trust functionality, and more. \ No newline at end of file +caching libraries, Web of Trust functionality, and more. + +> [!WARNING] +> The documentation of the NDK project is under heavy construction. +> [More information available in open pull request](https://github.com/nostr-dev-kit/ndk/pull/344). +> \ No newline at end of file From 9417f0971d0ceed072a496caeb2995ee3ff52eb2 Mon Sep 17 00:00:00 2001 From: digitalbase Date: Wed, 22 Oct 2025 21:28:44 +0200 Subject: [PATCH 067/139] Updating docs in left sidebar --- .vitepress/config.mts | 134 +++++++++++++++++++++--------------------- docs/TODOS.md | 36 ++++++++++++ 2 files changed, 103 insertions(+), 67 deletions(-) create mode 100644 docs/TODOS.md diff --git a/.vitepress/config.mts b/.vitepress/config.mts index 5ff6bb49e..420841b6b 100644 --- a/.vitepress/config.mts +++ b/.vitepress/config.mts @@ -18,62 +18,62 @@ export default defineConfig({ themeConfig: { // https://vitepress.dev/reference/default-theme-config nav: [ - { text: "Home", link: "/docs/index.md" }, + {text: "Home โœ…", link: "/docs/index.md"}, // { text: "API Reference", link: "/api/", target: "_blank" }, // { text: "Cookbook", link: "/cookbook/" }, - { text: "Snippets", link: "/docs/snippets.md" }, - { text: "Wiki", link: "https://wikifreedia.xyz/?c=NDK", target: "_blank" }, + {text: "Snippets โœ…", link: "/docs/snippets.md"}, + {text: "Wiki โœ…", link: "https://wikifreedia.xyz/?c=NDK", target: "_blank"}, ], sidebar: [ { items: [ - {text: "Introduction", link: "/core/docs/introduction"}, - {text: "About Nostr", link: "/core/docs/about-nostr"}, - {text: "Contributing", link: "/docs/contributing.md"}, - {text: "Changelog", link: "/docs/changelogs.md"}, + {text: "Introduction โœ…", link: "/core/docs/introduction"}, + {text: "About Nostr โœ…", link: "/core/docs/about-nostr"}, + {text: "Contributing โŒ", link: "/docs/contributing.md"}, + {text: "Changelog โœ…", link: "/docs/changelogs.md"}, ], }, { text: "Getting Started", items: [ - {text: "Installing", link: "/core/docs/getting-started/installing"}, - {text: "Debugging", link: "/core/docs/getting-started/debugging"}, - { text: "Usage", link: "/core/docs/getting-started/usage" }, + {text: "Installing โœ…", link: "/core/docs/getting-started/installing"}, + {text: "Debugging โœ…", link: "/core/docs/getting-started/debugging"}, + {text: "Usage โŒ", link: "/core/docs/getting-started/usage"}, ], }, { text: "Fundamentals", collapsed: false, items: [ - { text: "Events", link: "/core/docs/fundamentals/events" }, - { text: "Signers", link: "/core/docs/fundamentals/signers" }, - { text: "Publishing", link: "/core/docs/fundamentals/publishing" }, - { text: "Connecting", link: "/core/docs/fundamentals/connecting" }, - { text: "Zaps", link: "/core/docs/tutorial/zaps" }, - { text: "Local-first", link: "/core/docs/tutorial/local-first" }, + {text: "Events โœ…", link: "/core/docs/fundamentals/events"}, + {text: "Signers โœ…", link: "/core/docs/fundamentals/signers"}, + {text: "Publishing โœ…", link: "/core/docs/fundamentals/publishing"}, + {text: "Connecting โœ…", link: "/core/docs/fundamentals/connecting"}, + {text: "Zaps โŒ", link: "/core/docs/tutorial/zaps"}, + {text: "Local-first โŒ", link: "/core/docs/tutorial/local-first"}, { - text: "Subscription Management", + text: "Subscription Management โŒ", link: "/core/docs/tutorial/subscription-management", }, - { text: "Mute Filtering", link: "/core/docs/tutorial/mute-filtering" }, + {text: "Mute Filtering โŒ", link: "/core/docs/tutorial/mute-filtering"}, ], }, { text: "Wallet", collapsed: true, items: [ - { text: "Introduction", link: "/wallet/README" }, - { text: "WebLN Wallet", link: '/wallet/docs/NDKWebLNWallet'}, - { text: "NWC Wallet", link: '/wallet/docs/NDKNWCWallet'}, + {text: "Introduction โŒ", link: "/wallet/README"}, + {text: "WebLN Wallet โŒ", link: '/wallet/docs/NDKWebLNWallet'}, + {text: "NWC Wallet โŒ", link: '/wallet/docs/NDKNWCWallet'}, { - text: "CashuWallet (NIP-60)", + text: "CashuWallet (NIP-60) โŒ", link: "/wallet/docs/NDKCashuWallet", items: [ - { text: "Nutsack", link: "/wallet/docs/nutsack" }, - { text: "Nutzaps", link: "/wallet/docs/nutzaps" }, - { text: "Nutzap Monitor", link: "/wallet/docs/nutzap-monitor" }, - { text: "Monitor State Store", link: "/wallet/docs/nutzap-monitor-state-store" }, + {text: "Nutsack โŒ", link: "/wallet/docs/nutsack"}, + {text: "Nutzaps โŒ", link: "/wallet/docs/nutzaps"}, + {text: "Nutzap Monitor โŒ", link: "/wallet/docs/nutzap-monitor"}, + {text: "Monitor State Store โŒ", link: "/wallet/docs/nutzap-monitor-state-store"}, ] }, ], @@ -83,20 +83,20 @@ export default defineConfig({ collapsed: true, items: [ { - text: "Svelte", - link: "/svelte/README", + text: "Svelte โ›”", + link: "/svelte/README โ›”", items: [ - { text: "Blossom", link: "/svelte/blossom-upload" }, - { text: "Changelog", link: "/svelte/CHANGELOG.md" }, + {text: "Blossom โ›”", link: "/svelte/blossom-upload"}, + {text: "Changelog โ›”", link: "/svelte/CHANGELOG.md"}, ] }, { - text: "React", + text: "React โŒ", link: "/react/README", items: [ - {text: "Getting Started", link: "/react/docs/getting-started"}, - { text: "Muting", link: "/react/muting" }, - { text: "Session Management", link: "/react/session-management" }, + {text: "Getting Started โŒ", link: "/react/docs/getting-started"}, + {text: "Muting โŒ", link: "/react/muting"}, + {text: "Session Management โŒ", link: "/react/session-management"}, ] } ], @@ -105,33 +105,33 @@ export default defineConfig({ text: "Sessions", collapsed: true, items: [ - { text: "Introduction", link: "/sessions/README" }, - { text: "Quick Start", link: "/sessions/quick-start" }, - { text: "API Reference", link: "/sessions/api" }, - { text: "Migration Guide", link: "/sessions/migration" } + {text: "Introduction โŒ", link: "/sessions/README"}, + {text: "Quick Start โ›”", link: "/sessions/quick-start"}, + {text: "API Reference โ›”", link: "/sessions/api"}, + {text: "Migration Guide โ›”", link: "/sessions/migration"} ], }, { text: "Mobile", collapsed: true, items: [ - { text: "Introduction", link: "/mobile/README" }, - { text: "Session", link: "/mobile/session" }, - { text: "Wallet", link: "/mobile/wallet" }, - { text: "Subscriptions", link: "/mobile/subscriptions" }, - { text: "Nutzaps", link: "/mobile/nutzaps" }, - { text: "Mint", link: "/mobile/mint" }, + {text: "Introduction โŒ", link: "/mobile/README"}, + {text: "Session โ›”", link: "/mobile/session"}, + {text: "Wallet โ›”", link: "/mobile/wallet"}, + {text: "Subscriptions โ›”", link: "/mobile/subscriptions"}, + {text: "Nutzaps โ›”", link: "/mobile/nutzaps"}, + {text: "Mint โ›”", link: "/mobile/mint"}, ], }, { text: "Blossom (Media)", collapsed: true, items: [ - { text: "Introduction", link: "/blossom/README" }, - { text: "Getting Started", link: "/blossom/getting-started" }, - { text: "Error Handling", link: "/blossom/error-handling" }, - { text: "Mirroring", link: "/blossom/mirroring" }, - { text: "Optimization", link: "/blossom/optimization" }, + {text: "Introduction โŒ", link: "/blossom/README"}, + {text: "Getting Started โ›”", link: "/blossom/getting-started"}, + {text: "Error Handling โ›”", link: "/blossom/error-handling"}, + {text: "Mirroring โ›”", link: "/blossom/mirroring"}, + {text: "Optimization โ›”", link: "/blossom/optimization"}, ] }, { @@ -139,52 +139,52 @@ export default defineConfig({ collapsed: true, items: [ { - text: "Signer Persistence", + text: "Signer Persistence โŒ", link: "/core/docs/tutorial/signer-persistence" }, { - text: "Speed / Performance", + text: "Speed / Performance โŒ", link: "/core/docs/tutorial/speed" }, { - text: "AI Guardrails", + text: "AI Guardrails โœ…", link: "/core/docs/advanced/ai-guardrails" }, { - text: "Subscription Lifecycle", + text: "Subscription Lifecycle โŒ", link: "/core/docs/advanced/subscription-internals" }, { - text: "Event Class Registration", + text: "Event Class Registration โŒ", link: "/core/docs/advanced/event-class-registration" }, { - text: "Relay Metadata Caching", + text: "Relay Metadata Caching โ›”", link: "/core/docs/advanced/relay-metadata-caching" }, { - text: "Sync & Negentropy", + text: "Sync & Negentropy โ›”", link: "/sync" }, { - text: "Web of Trust (WOT)", + text: "Web of Trust (WOT) โ›”", link: "/wot/README" }, { - text: "Cache Adapters", + text: "Cache Adapters โŒ", collapsed: true, items: [ - { text: "Memory / LRU", link: "/cache/memory/README" }, - { text: "Dexie / IndexedDB", link: "cache/dexie/README" }, - { text: "Local Nostr Relay", link: "/cache/nostr/README" }, - { text: "Redis", link: "/cache/redis/README" }, - { text: "SQLite", link: "/cache/sqlite/README" }, + {text: "Memory / LRU โŒ", link: "/cache/memory/README"}, + {text: "Dexie / IndexedDB โŒ", link: "cache/dexie/README"}, + {text: "Local Nostr Relay โŒ", link: "/cache/nostr/README"}, + {text: "Redis โŒ", link: "/cache/redis/README"}, + {text: "SQLite โŒ", link: "/cache/sqlite/README"}, { - text: "SQLite WASM", + text: "SQLite WASM โŒ", link: "/cache/sqlite-wasm/README", items: [ - { text: "Bundling", link: "/cache/sqlite-wasm/bundling" }, - { text: "Web Worker Setup", link: "/cache/sqlite-wasm/web-worker-setup" } + {text: "Bundling โŒ", link: "/cache/sqlite-wasm/bundling"}, + {text: "Web Worker Setup โŒ", link: "/cache/sqlite-wasm/web-worker-setup"} ] }, ], @@ -196,6 +196,6 @@ export default defineConfig({ level: [2, 3], }, - socialLinks: [{ icon: "github", link: "https://github.com/nostr-dev-kit/ndk" }], + socialLinks: [{icon: "github", link: "https://github.com/nostr-dev-kit/ndk"}], }, }); diff --git a/docs/TODOS.md b/docs/TODOS.md new file mode 100644 index 000000000..16b950256 --- /dev/null +++ b/docs/TODOS.md @@ -0,0 +1,36 @@ +# Clean up documentation + +Full PR + progress available at https://github.com/nostr-dev-kit/ndk/pull/344 + +Some notes: + +- Items with โœ… in sidebar are mostly done +- Items with โŒ in sidebar are mostly done, unless they have things highlighted below +- Items with โ›” in sidebar have docs, but need to be linked+cleaned up + +## Todo List + +- [x] Move vitepress to root and use rewrites to clean up paths +- [x] Upgrade vitepress +- [x] [Introduction/Essential pages](https://ndkdocs.asknostr.site/core/docs/getting-started/introduction.html) +- [x] [Make sure changelogs can be found](https://ndkdocs.asknostr.site/docs/changelogs.html) +- [x] [Fix snippets](https://ndkdocs.asknostr.site/docs/snippets.html). +- [x] Fundamentals + - [x] [Events](https://ndkdocs.asknostr.site/core/docs/fundamentals/events.html) + - [ ] [Signers WIP](https://ndkdocs.asknostr.site/core/docs/fundamentals/signers.html) + - [ ] Add QR code / Nostrconnect:// docs remote signer docs + - [x] [Publishing](https://ndkdocs.asknostr.site/core/docs/fundamentals/publishing.html) + - [x] [Connecting](https://ndkdocs.asknostr.site/core/docs/fundamentals/connecting.html) + - [ ] Explain/Cleanup Outbox Docs + - [ ] Wallets + - [ ] Zaps + - [ ] ... +- [x] Link to snippets on key pages +- [ ] Try to find (or remove) API reference link +- [ ] Advanced +- [ ] more + +### Changes made that need double check + +- core/tsconfig -> added paths. This makes the snippets in core/docs/snippets have biome/linting +- upgrade bun 1.3.0 and node to +22. \ No newline at end of file From ea438a0fea5550bfaaf03c18d7225ddc8a5ba6d9 Mon Sep 17 00:00:00 2001 From: digitalbase Date: Wed, 22 Oct 2025 21:31:26 +0200 Subject: [PATCH 068/139] Update TODOS --- docs/TODOS.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/TODOS.md b/docs/TODOS.md index 16b950256..6493eb603 100644 --- a/docs/TODOS.md +++ b/docs/TODOS.md @@ -27,8 +27,8 @@ Some notes: - [ ] ... - [x] Link to snippets on key pages - [ ] Try to find (or remove) API reference link -- [ ] Advanced -- [ ] more +- [ ] Remove warnings (homepage and main intro) about Docs WIP +- [ ] Remove โœ… โŒ โ›” icons ### Changes made that need double check From 40f4aae202712a272defee0316f08af4bf815a88 Mon Sep 17 00:00:00 2001 From: digitalbase Date: Fri, 24 Oct 2025 17:13:54 +0200 Subject: [PATCH 069/139] Work on docs --- .vitepress/config.mts | 25 +- core/docs/snippets/connect_explicit.ts | 2 +- docs/TODOS.md | 1 + sessions/docs/advanced.md | 99 +++++++ sessions/docs/api.md | 47 +--- sessions/docs/best-practices.md | 44 ++++ sessions/docs/introduction.md | 34 +++ sessions/docs/multi-account.md | 16 ++ sessions/docs/quick-start.md | 247 ++---------------- sessions/docs/snippets/adding_sessions.ts | 9 + .../snippets/init_sessions_local_storage.ts | 13 + .../docs/snippets/session_fetch_user_data.ts | 19 ++ sessions/docs/snippets/session_switch.ts | 6 + sessions/docs/snippets/sessions_restore.ts | 5 + sessions/docs/storage-options.md | 40 +++ sessions/tsconfig.json | 10 +- 16 files changed, 340 insertions(+), 277 deletions(-) create mode 100644 sessions/docs/advanced.md create mode 100644 sessions/docs/best-practices.md create mode 100644 sessions/docs/introduction.md create mode 100644 sessions/docs/multi-account.md create mode 100644 sessions/docs/snippets/adding_sessions.ts create mode 100644 sessions/docs/snippets/init_sessions_local_storage.ts create mode 100644 sessions/docs/snippets/session_fetch_user_data.ts create mode 100644 sessions/docs/snippets/session_switch.ts create mode 100644 sessions/docs/snippets/sessions_restore.ts create mode 100644 sessions/docs/storage-options.md diff --git a/.vitepress/config.mts b/.vitepress/config.mts index 420841b6b..dacd6e2f4 100644 --- a/.vitepress/config.mts +++ b/.vitepress/config.mts @@ -59,6 +59,21 @@ export default defineConfig({ {text: "Mute Filtering โŒ", link: "/core/docs/tutorial/mute-filtering"}, ], }, + { + text: "Sessions", + collapsed: true, + items: [ + {text: "Introduction โœ…", link: "/sessions/docs/introduction"}, + {text: "Quick Start โœ…", link: "/sessions/docs/quick-start"}, + {text: "Storage Options โœ…", link: "/sessions/docs/storage-options"}, + {text: "Multi-Account โœ…", link: "/sessions/docs/multi-account"}, + {text: "Migration Guide โœ…", link: "/sessions/docs/migration"}, + {text: "Advanced โŒ", link: "/sessions/docs/advanced"}, + {text: "Best Practices โŒ", link: "/sessions/docs/best-practices"}, + {text: "API Reference โœ…", link: "/sessions/docs/api"}, + {text: "README โ›”", link: "/sessions/README"} + ], + }, { text: "Wallet", collapsed: true, @@ -101,16 +116,6 @@ export default defineConfig({ } ], }, - { - text: "Sessions", - collapsed: true, - items: [ - {text: "Introduction โŒ", link: "/sessions/README"}, - {text: "Quick Start โ›”", link: "/sessions/quick-start"}, - {text: "API Reference โ›”", link: "/sessions/api"}, - {text: "Migration Guide โ›”", link: "/sessions/migration"} - ], - }, { text: "Mobile", collapsed: true, diff --git a/core/docs/snippets/connect_explicit.ts b/core/docs/snippets/connect_explicit.ts index efab0ffbf..97e091c78 100644 --- a/core/docs/snippets/connect_explicit.ts +++ b/core/docs/snippets/connect_explicit.ts @@ -3,7 +3,7 @@ import NDK from "@nostr-dev-kit/ndk"; // Create a new NDK instance with explicit relays const ndk = new NDK({ - explicitRelayUrls: ["wss://a.relay", "wss://another.relay"], + explicitRelayUrls: ["wss://relay.damus.io", "wss://nos.lol", "wss://relay.nostr.band"], }); // Now connect to specified relays diff --git a/docs/TODOS.md b/docs/TODOS.md index 6493eb603..011de52e4 100644 --- a/docs/TODOS.md +++ b/docs/TODOS.md @@ -1,6 +1,7 @@ # Clean up documentation Full PR + progress available at https://github.com/nostr-dev-kit/ndk/pull/344 +Preview URL https://ndkdocs.asknostr.site/ Some notes: diff --git a/sessions/docs/advanced.md b/sessions/docs/advanced.md new file mode 100644 index 000000000..8042342b0 --- /dev/null +++ b/sessions/docs/advanced.md @@ -0,0 +1,99 @@ +## React to Changes + +Subscribe to session changes: + +```typescript +const unsubscribe = sessions.subscribe((state) => { + console.log('Active user:', state.activePubkey); + console.log('Total sessions:', state.sessions.size); + + // Update your UI... +}); + +// Later, cleanup +unsubscribe(); +``` + +## Read-Only Sessions + +Create a read-only session without a signer. Configure `fetches` in the constructor: + +```typescript +const sessions = new NDKSessionManager(ndk, { + fetches: { + follows: true, + relayList: true + } +}); + +const user = ndk.getUser({pubkey: somePubkey}); +await sessions.login(user); + +// Data is fetched and cached, but user can't sign events +``` + +## Using with NIP-07 (Browser Extensions) + +```typescript +import {NDKNip07Signer} from '@nostr-dev-kit/ndk'; + +const sessions = new NDKSessionManager(ndk, { + fetches: { + follows: true, + mutes: true + } +}); + +const signer = new NDKNip07Signer(); +await sessions.login(signer); +``` + +## CLI Example + +Complete example for a Node.js CLI tool: + +```typescript +#!/usr/bin/env node +import NDK from '@nostr-dev-kit/ndk'; +import {NDKSessionManager, FileStorage} from '@nostr-dev-kit/sessions'; +import {NDKPrivateKeySigner} from '@nostr-dev-kit/ndk'; + +const ndk = new NDK({explicitRelayUrls: ['wss://relay.damus.io']}); +await ndk.connect(); + +const sessions = new NDKSessionManager(ndk, { + storage: new FileStorage('./.ndk-sessions.json'), + autoSave: true, + fetches: { + follows: true + } +}); + +// Restore previous session +await sessions.restore(); + +if (!sessions.activeUser) { + // First time - login + const nsec = process.env.NOSTR_NSEC; + if (!nsec) throw new Error('NOSTR_NSEC not set'); + + const signer = new NDKPrivateKeySigner(nsec); + await sessions.login(signer); + + console.log('Logged in as', sessions.activeUser.npub); +} else { + console.log('Welcome back', sessions.activeUser.npub); +} + +// Use the active session to publish +const event = new NDKEvent(ndk, { + kind: 1, + content: 'Hello from CLI!' +}); + +await event.publish(); +console.log('Published:', event.id); + +// Cleanup +sessions.destroy(); +``` \ No newline at end of file diff --git a/sessions/docs/api.md b/sessions/docs/api.md index 6ac8c5dc6..e5790d936 100644 --- a/sessions/docs/api.md +++ b/sessions/docs/api.md @@ -507,49 +507,4 @@ try { } catch (error) { console.error('Failed to restore sessions:', error); } -``` - -## Best Practices - -### 1. Always Call destroy() - -```typescript -// In your cleanup code -sessions.destroy(); -``` - -### 2. Use autoSave - -```typescript -const sessions = new NDKSessionManager(ndk, { - autoSave: true, - saveDebounceMs: 500 -}); -``` - -### 3. Handle No Active Session - -```typescript -if (!sessions.activeUser) { - // Show login UI -} -``` - -### 4. Subscribe to Changes - -```typescript -const unsubscribe = sessions.subscribe((state) => { - // Update UI when sessions change -}); -``` - -### 5. Security - -```typescript -// โš ๏ธ NEVER commit .ndk-sessions.json to git! -// Add to .gitignore: -// .ndk-sessions.json - -// Use environment variables for sensitive keys -const nsec = process.env.NOSTR_NSEC; -``` +``` \ No newline at end of file diff --git a/sessions/docs/best-practices.md b/sessions/docs/best-practices.md new file mode 100644 index 000000000..b3ad7c634 --- /dev/null +++ b/sessions/docs/best-practices.md @@ -0,0 +1,44 @@ +# Best Practices + +## Always Call destroy() + +```typescript +// In your cleanup code +sessions.destroy(); +``` + +## Use autoSave + +```typescript +const sessions = new NDKSessionManager(ndk, { + autoSave: true, + saveDebounceMs: 500 +}); +``` + +## Handle No Active Session + +```typescript +if (!sessions.activeUser) { + // Show login UI +} +``` + +## Subscribe to Changes + +```typescript +const unsubscribe = sessions.subscribe((state) => { + // Update UI when sessions change +}); +``` + +## Security + +```typescript +// โš ๏ธ NEVER commit .ndk-sessions.json to git! +// Add to .gitignore: +// .ndk-sessions.json + +// Use environment variables for sensitive keys +const nsec = process.env.NOSTR_NSEC; +``` diff --git a/sessions/docs/introduction.md b/sessions/docs/introduction.md new file mode 100644 index 000000000..e27f18a9e --- /dev/null +++ b/sessions/docs/introduction.md @@ -0,0 +1,34 @@ +# @nostr-dev-kit/sessions + +Framework-agnostic session management for NDK with multi-account support and persistence. + +## Features + +- ๐Ÿ” **Multi-account support** - Manage multiple Nostr accounts simultaneously +- ๐Ÿ’พ **Flexible persistence** - Built-in localStorage, filesystem, and memory storage +- ๐Ÿ”„ **Auto-sync** - Automatically fetch follows, mutes, relays, and events +- ๐ŸŽฏ **Framework-agnostic** - Works with React, Svelte, Vue, vanilla JS, Node.js, etc. +- ๐Ÿ”Œ **Minimal boilerplate** - Simple, intuitive API +- ๐ŸŽจ **Full TypeScript** - Complete type safety + +## Installation + +::: code-group + +```sh [npm] +npm i @nostr-dev-kit/sessions +``` + +```sh [pnpm] +pnpm add @nostr-dev-kit/sessions +``` + +```sh [yarn] +yarn add @nostr-dev-kit/sessions +``` + +```sh [bun] +bun add @nostr-dev-kit/sessions +``` + +::: \ No newline at end of file diff --git a/sessions/docs/multi-account.md b/sessions/docs/multi-account.md new file mode 100644 index 000000000..90f21a9c5 --- /dev/null +++ b/sessions/docs/multi-account.md @@ -0,0 +1,16 @@ +# Multi-Account Management + +The library supports multiple accounts (called sessions) through [different signers](/core/docs/fundamentals/signers). + +## Adding a Session + +Each time you use `sessions.login` NDK will create a new session if that signer isn't already an active session. + +<<< @/sessions/docs/snippets/adding_sessions.ts + +## Switch Between Accounts + +Sessions can be listed with `getSessions()` which will return a `Map`. +Switching sessions is as simple as passing in the pubkey: + +<<< @/sessions/docs/snippets/session_switch.ts \ No newline at end of file diff --git a/sessions/docs/quick-start.md b/sessions/docs/quick-start.md index 58290d2b5..6e75bce42 100644 --- a/sessions/docs/quick-start.md +++ b/sessions/docs/quick-start.md @@ -4,145 +4,61 @@ Get started with NDK Sessions in minutes. ## Installation -```bash -npm install @nostr-dev-kit/sessions @nostr-dev-kit/ndk -# or -bun add @nostr-dev-kit/sessions @nostr-dev-kit/ndk -``` - -## Basic Setup - -### 1. Initialize NDK - -First, create and connect your NDK instance: +::: code-group -```typescript -import NDK from '@nostr-dev-kit/ndk'; - -const ndk = new NDK({ - explicitRelayUrls: [ - 'wss://relay.damus.io', - 'wss://nos.lol', - 'wss://relay.nostr.band' - ] -}); - -await ndk.connect(); +```sh [npm] +npm i @nostr-dev-kit/sessions ``` -### 2. Create Session Manager - -Create a session manager with your preferred storage: - -```typescript -import { NDKSessionManager, LocalStorage } from '@nostr-dev-kit/sessions'; - -const sessions = new NDKSessionManager(ndk, { - storage: new LocalStorage(), - autoSave: true, // Automatically save changes - saveDebounceMs: 500 // Debounce auto-saves -}); +```sh [pnpm] +pnpm add @nostr-dev-kit/sessions ``` -### 3. Restore Previous Sessions - -Restore any previously saved sessions: - -```typescript -await sessions.restore(); - -if (sessions.activeUser) { - console.log('Welcome back!', sessions.activeUser.npub); -} +```sh [yarn] +yarn add @nostr-dev-kit/sessions ``` -### 4. Login - -Login with a signer. To automatically fetch user data, configure `fetches` in the constructor: - -```typescript -import { NDKPrivateKeySigner } from '@nostr-dev-kit/ndk'; - -const sessions = new NDKSessionManager(ndk, { - storage: new LocalStorage(), - autoSave: true, - fetches: { - follows: true, // Fetch contact list - mutes: true, // Fetch mute list - relayList: true, // Fetch relay list - wallet: true // Fetch NIP-60 wallet - } -}); - -const signer = new NDKPrivateKeySigner(nsecKey); -await sessions.login(signer); - -// Access session data -console.log('Following:', sessions.activeSession?.followSet?.size, 'users'); -console.log('Muted:', sessions.activeSession?.muteSet?.size, 'items'); +```sh [bun] +bun add @nostr-dev-kit/sessions ``` -## Storage Options +::: -### Browser (LocalStorage) +## Basic Setup -```typescript -import { LocalStorage } from '@nostr-dev-kit/sessions'; +### 1. Initialize NDK -const sessions = new NDKSessionManager(ndk, { - storage: new LocalStorage('my-app-sessions') // Custom key -}); -``` +First, create and [initialise your NDK instance](/core/docs/getting-started/usage.html): -### Node.js (FileStorage) +<<< @/core/docs/snippets/connect_explicit.ts -```typescript -import { FileStorage } from '@nostr-dev-kit/sessions'; +Additional information about relay connections in the [connection section](/core/docs/fundamentals/connecting.html). -const sessions = new NDKSessionManager(ndk, { - storage: new FileStorage('./.ndk-sessions.json') -}); -``` +### 2. Create Session Manager -### Temporary (MemoryStorage) +Create a session manager with your preferred storage: -```typescript -import { MemoryStorage } from '@nostr-dev-kit/sessions'; +<<< @/sessions/docs/snippets/init_sessions_local_storage.ts -const sessions = new NDKSessionManager(ndk, { - storage: new MemoryStorage(), // No persistence - autoSave: false -}); -``` +More about [storage options](/sessions/docs/storage-options.html). -## Multi-Account Management +### 3. Restore Previous Sessions -### Login Multiple Accounts +Restore any previously saved sessions: -```typescript -// Login first account (automatically active) -const signer1 = new NDKPrivateKeySigner(nsec1); -const pubkey1 = await sessions.login(signer1); +<<< @/sessions/docs/snippets/sessions_restore.ts -// Login second account -const signer2 = new NDKPrivateKeySigner(nsec2); -const pubkey2 = await sessions.login(signer2, { setActive: false }); +### 4. Login (and auto-fetch) -console.log('Accounts:', sessions.getSessions().size); -``` +Login with a signer. To automatically fetch user data, configure `fetches` in the constructor: -### Switch Between Accounts +<<< @/sessions/docs/snippets/session_fetch_user_data.ts -```typescript -// Switch to different account -sessions.switchTo(pubkey2); -console.log('Active:', sessions.activePubkey); +## Read-only Sessions -// Switch back -sessions.switchTo(pubkey1); -``` +TODO -> Write -### Logout +## Logging out ```typescript // Logout specific account @@ -151,110 +67,3 @@ sessions.logout(pubkey1); // Or logout current active account sessions.logout(); ``` - -## React to Changes - -Subscribe to session changes: - -```typescript -const unsubscribe = sessions.subscribe((state) => { - console.log('Active user:', state.activePubkey); - console.log('Total sessions:', state.sessions.size); - - // Update your UI... -}); - -// Later, cleanup -unsubscribe(); -``` - -## Read-Only Sessions - -Create a read-only session without a signer. Configure `fetches` in the constructor: - -```typescript -const sessions = new NDKSessionManager(ndk, { - fetches: { - follows: true, - relayList: true - } -}); - -const user = ndk.getUser({ pubkey: somePubkey }); -await sessions.login(user); - -// Data is fetched and cached, but user can't sign events -``` - -## Using with NIP-07 (Browser Extensions) - -```typescript -import { NDKNip07Signer } from '@nostr-dev-kit/ndk'; - -const sessions = new NDKSessionManager(ndk, { - fetches: { - follows: true, - mutes: true - } -}); - -const signer = new NDKNip07Signer(); -await sessions.login(signer); -``` - -## CLI Example - -Complete example for a Node.js CLI tool: - -```typescript -#!/usr/bin/env node -import NDK from '@nostr-dev-kit/ndk'; -import { NDKSessionManager, FileStorage } from '@nostr-dev-kit/sessions'; -import { NDKPrivateKeySigner } from '@nostr-dev-kit/ndk'; - -const ndk = new NDK({ explicitRelayUrls: ['wss://relay.damus.io'] }); -await ndk.connect(); - -const sessions = new NDKSessionManager(ndk, { - storage: new FileStorage('./.ndk-sessions.json'), - autoSave: true, - fetches: { - follows: true - } -}); - -// Restore previous session -await sessions.restore(); - -if (!sessions.activeUser) { - // First time - login - const nsec = process.env.NOSTR_NSEC; - if (!nsec) throw new Error('NOSTR_NSEC not set'); - - const signer = new NDKPrivateKeySigner(nsec); - await sessions.login(signer); - - console.log('Logged in as', sessions.activeUser.npub); -} else { - console.log('Welcome back', sessions.activeUser.npub); -} - -// Use the active session to publish -const event = new NDKEvent(ndk, { - kind: 1, - content: 'Hello from CLI!' -}); - -await event.publish(); -console.log('Published:', event.id); - -// Cleanup -sessions.destroy(); -``` - -## Next Steps - -- [API Reference](./api) - Complete API documentation -- [Migration Guide](./migration) - Migrating from ndk-hooks -- [React Hooks](/hooks/session-management) - Using with React -- [Svelte](/wrappers/svelte) - Using with Svelte diff --git a/sessions/docs/snippets/adding_sessions.ts b/sessions/docs/snippets/adding_sessions.ts new file mode 100644 index 000000000..0247ef11b --- /dev/null +++ b/sessions/docs/snippets/adding_sessions.ts @@ -0,0 +1,9 @@ +// Login first account (automatically active) +const signer1 = new NDKPrivateKeySigner(nsec1); +const pubkey1 = await sessions.login(signer1); + +// Login second account +const signer2 = new NDKPrivateKeySigner(nsec2); +const pubkey2 = await sessions.login(signer2, { setActive: false }); + +console.log("Accounts:", sessions.getSessions().size); diff --git a/sessions/docs/snippets/init_sessions_local_storage.ts b/sessions/docs/snippets/init_sessions_local_storage.ts new file mode 100644 index 000000000..fecc2b2e5 --- /dev/null +++ b/sessions/docs/snippets/init_sessions_local_storage.ts @@ -0,0 +1,13 @@ +import NDK from "@nostr-dev-kit/ndk"; +import { LocalStorage, NDKSessionManager } from "@nostr-dev-kit/sessions"; + +// Create a new NDK instance with explicit relays +const ndk = new NDK({ + explicitRelayUrls: ["wss://relay.damus.io", "wss://nos.lol", "wss://relay.nostr.band"], +}); + +const sessions = new NDKSessionManager(ndk, { + storage: new LocalStorage(), + autoSave: true, // Automatically save changes + saveDebounceMs: 500, // Debounce auto-saves +}); diff --git a/sessions/docs/snippets/session_fetch_user_data.ts b/sessions/docs/snippets/session_fetch_user_data.ts new file mode 100644 index 000000000..ee8008b77 --- /dev/null +++ b/sessions/docs/snippets/session_fetch_user_data.ts @@ -0,0 +1,19 @@ +import { NDKPrivateKeySigner } from "@nostr-dev-kit/ndk"; + +const sessions = new NDKSessionManager(ndk, { + storage: new LocalStorage(), + autoSave: true, + fetches: { + follows: true, // Fetch contact list + mutes: true, // Fetch mute list + relayList: true, // Fetch relay list + wallet: true, // Fetch NIP-60 wallet + }, +}); + +const signer = new NDKPrivateKeySigner(nsecKey); +await sessions.login(signer); + +// Access session data +console.log("Following:", sessions.activeSession?.followSet?.size, "users"); +console.log("Muted:", sessions.activeSession?.muteSet?.size, "items"); diff --git a/sessions/docs/snippets/session_switch.ts b/sessions/docs/snippets/session_switch.ts new file mode 100644 index 000000000..25780cf74 --- /dev/null +++ b/sessions/docs/snippets/session_switch.ts @@ -0,0 +1,6 @@ +// Switch to different account +sessions.switchTo(pubkey2); +console.log("Active:", sessions.activePubkey); + +// Switch back +sessions.switchTo(pubkey1); diff --git a/sessions/docs/snippets/sessions_restore.ts b/sessions/docs/snippets/sessions_restore.ts new file mode 100644 index 000000000..f1b7ea69b --- /dev/null +++ b/sessions/docs/snippets/sessions_restore.ts @@ -0,0 +1,5 @@ +await sessions.restore(); + +if (sessions.activeUser) { + console.log("Welcome back!", sessions.activeUser.npub); +} diff --git a/sessions/docs/storage-options.md b/sessions/docs/storage-options.md new file mode 100644 index 000000000..2ae9d1c50 --- /dev/null +++ b/sessions/docs/storage-options.md @@ -0,0 +1,40 @@ +# Storage Options + +To make sure NDK persists sessions, you need to provide one of the following storage options. + +## Local Storage + +Suited for browser/web-app applications. + +```typescript +import { LocalStorage } from '@nostr-dev-kit/sessions'; + +const sessions = new NDKSessionManager(ndk, { + storage: new LocalStorage('my-app-sessions') // Custom key +}); +``` + +## File Storage + +Common used for NodeJS application or CLI scripts. + +```typescript +import { FileStorage } from '@nostr-dev-kit/sessions'; + +const sessions = new NDKSessionManager(ndk, { + storage: new FileStorage('./.ndk-sessions.json') +}); +``` + +## Memory Storage + +Temporary, mostly used for testing or short lived application logic. + +```typescript +import { MemoryStorage } from '@nostr-dev-kit/sessions'; + +const sessions = new NDKSessionManager(ndk, { + storage: new MemoryStorage(), // No persistence + autoSave: false +}); +``` \ No newline at end of file diff --git a/sessions/tsconfig.json b/sessions/tsconfig.json index f414d6146..3ab1c8360 100644 --- a/sessions/tsconfig.json +++ b/sessions/tsconfig.json @@ -1,5 +1,6 @@ { "compilerOptions": { + "baseUrl": ".", "target": "ES2020", "module": "ESNext", "moduleResolution": "bundler", @@ -12,7 +13,14 @@ "skipLibCheck": true, "forceConsistentCasingInFileNames": true, "isolatedModules": true, - "types": ["vitest"] + "types": [ + "vitest" + ], + "paths": { + "@nostr-dev-kit/sessions": [ + "src/index.ts" + ] + } }, "include": ["src/**/*", "__tests__/**/*"], "exclude": ["node_modules", "dist"] From b90c8f59d3f46f20d3121893accb7fedaaa6c4ce Mon Sep 17 00:00:00 2001 From: digitalbase Date: Thu, 20 Nov 2025 21:07:01 +0100 Subject: [PATCH 070/139] Update doc dependencies --- bun.lock | 186 ++++++++++++++++++++++++++++----------------------- package.json | 8 +-- 2 files changed, 106 insertions(+), 88 deletions(-) diff --git a/bun.lock b/bun.lock index ac04ea8cb..484a33f5f 100644 --- a/bun.lock +++ b/bun.lock @@ -192,9 +192,9 @@ "version": "0.9.2", "dependencies": { "@bacons/text-decoder": "^0.0.0", - "@nostr-dev-kit/ndk": "^2.17.6", + "@nostr-dev-kit/ndk": "^2.17.9", "@nostr-dev-kit/react": "^1.3.12", - "@nostr-dev-kit/wallet": "^0.8.6", + "@nostr-dev-kit/wallet": "^0.8.7", "buffer": "^6.0.3", "expo-image": "^2.0.7", "expo-nip55": "^0.1.7", @@ -258,13 +258,13 @@ }, "svelte": { "name": "@nostr-dev-kit/svelte", - "version": "2.4.2", + "version": "2.4.3", "dependencies": { "@nostr-dev-kit/cache-sqlite-wasm": "^0.8.2", "@nostr-dev-kit/ndk": "^2.17.10", - "@nostr-dev-kit/sessions": "^0.6.1", + "@nostr-dev-kit/sessions": "^0.6.2", "@nostr-dev-kit/sync": "^0.3.5", - "@nostr-dev-kit/wallet": "^0.8.8", + "@nostr-dev-kit/wallet": "^0.8.9", "@nostr-dev-kit/wot": "^0.3.6", "@tiptap/core": "^2.6.6", "@tiptap/extension-image": "^2.6.6", @@ -281,30 +281,30 @@ }, "devDependencies": { "@sveltejs/adapter-auto": "^3.0.0", - "@sveltejs/kit": "^2.0.0", + "@sveltejs/kit": "^2.47.2", "@sveltejs/package": "^2.0.0", "@sveltejs/vite-plugin-svelte": "^6.2.1", - "@types/node": "^24.7.0", + "@types/node": "^24.9.0", "@vitest/browser": "^3.2.4", - "eslint": "^9.37.0", - "eslint-plugin-svelte": "^3.12.4", + "eslint": "^9.38.0", + "eslint-plugin-svelte": "^3.12.5", "globals": "^16.4.0", - "playwright": "^1.55.1", + "playwright": "^1.56.1", "prettier": "^3.3.3", "prettier-plugin-svelte": "^3.0.0", - "svelte": "^5.41.0", + "svelte": "^5.41.1", "svelte-check": "^4.0.0", - "svelte-eslint-parser": "^1.3.3", + "svelte-eslint-parser": "^1.4.0", "tslib": "^2.6.0", "typescript": "^5.9.3", - "typescript-eslint": "^8.46.0", - "vite": "^7.1.9", + "typescript-eslint": "^8.46.2", + "vite": "^7.1.11", "vitest": "^3.2.4", "vitest-browser-svelte": "^1.1.0", }, "peerDependencies": { "@nostr-dev-kit/ndk": "^2.17.10", - "svelte": "^5.41.0", + "svelte": "^5.41.1", }, }, "sync": { @@ -687,23 +687,23 @@ "@bcoe/v8-coverage": ["@bcoe/v8-coverage@1.0.2", "", {}, "sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA=="], - "@biomejs/biome": ["@biomejs/biome@2.2.6", "", { "optionalDependencies": { "@biomejs/cli-darwin-arm64": "2.2.6", "@biomejs/cli-darwin-x64": "2.2.6", "@biomejs/cli-linux-arm64": "2.2.6", "@biomejs/cli-linux-arm64-musl": "2.2.6", "@biomejs/cli-linux-x64": "2.2.6", "@biomejs/cli-linux-x64-musl": "2.2.6", "@biomejs/cli-win32-arm64": "2.2.6", "@biomejs/cli-win32-x64": "2.2.6" }, "bin": { "biome": "bin/biome" } }, "sha512-yKTCNGhek0rL5OEW1jbLeZX8LHaM8yk7+3JRGv08my+gkpmtb5dDE+54r2ZjZx0ediFEn1pYBOJSmOdDP9xtFw=="], + "@biomejs/biome": ["@biomejs/biome@2.3.6", "", { "optionalDependencies": { "@biomejs/cli-darwin-arm64": "2.3.6", "@biomejs/cli-darwin-x64": "2.3.6", "@biomejs/cli-linux-arm64": "2.3.6", "@biomejs/cli-linux-arm64-musl": "2.3.6", "@biomejs/cli-linux-x64": "2.3.6", "@biomejs/cli-linux-x64-musl": "2.3.6", "@biomejs/cli-win32-arm64": "2.3.6", "@biomejs/cli-win32-x64": "2.3.6" }, "bin": { "biome": "bin/biome" } }, "sha512-oqUhWyU6tae0MFsr/7iLe++QWRg+6jtUhlx9/0GmCWDYFFrK366sBLamNM7D9Y+c7YSynUFKr8lpEp1r6Sk7eA=="], - "@biomejs/cli-darwin-arm64": ["@biomejs/cli-darwin-arm64@2.2.6", "", { "os": "darwin", "cpu": "arm64" }, "sha512-UZPmn3M45CjTYulgcrFJFZv7YmK3pTxTJDrFYlNElT2FNnkkX4fsxjExTSMeWKQYoZjvekpH5cvrYZZlWu3yfA=="], + "@biomejs/cli-darwin-arm64": ["@biomejs/cli-darwin-arm64@2.3.6", "", { "os": "darwin", "cpu": "arm64" }, "sha512-P4JWE5d8UayBxYe197QJwyW4ZHp0B+zvRIGCusOm1WbxmlhpAQA1zEqQuunHgSIzvyEEp4TVxiKGXNFZPg7r9Q=="], - "@biomejs/cli-darwin-x64": ["@biomejs/cli-darwin-x64@2.2.6", "", { "os": "darwin", "cpu": "x64" }, "sha512-HOUIquhHVgh/jvxyClpwlpl/oeMqntlteL89YqjuFDiZ091P0vhHccwz+8muu3nTyHWM5FQslt+4Jdcd67+xWQ=="], + "@biomejs/cli-darwin-x64": ["@biomejs/cli-darwin-x64@2.3.6", "", { "os": "darwin", "cpu": "x64" }, "sha512-I4rTebj+F/L9K93IU7yTFs8nQ6EhaCOivxduRha4w4WEZK80yoZ8OAdR1F33m4yJ/NfUuTUbP/Wjs+vKjlCoWA=="], - "@biomejs/cli-linux-arm64": ["@biomejs/cli-linux-arm64@2.2.6", "", { "os": "linux", "cpu": "arm64" }, "sha512-BpGtuMJGN+o8pQjvYsUKZ+4JEErxdSmcRD/JG3mXoWc6zrcA7OkuyGFN1mDggO0Q1n7qXxo/PcupHk8gzijt5g=="], + "@biomejs/cli-linux-arm64": ["@biomejs/cli-linux-arm64@2.3.6", "", { "os": "linux", "cpu": "arm64" }, "sha512-JjYy83eVBnvuINZiqyFO7xx72v8Srh4hsgaacSBCjC22DwM6+ZvnX1/fj8/SBiLuUOfZ8YhU2pfq2Dzakeyg1A=="], - "@biomejs/cli-linux-arm64-musl": ["@biomejs/cli-linux-arm64-musl@2.2.6", "", { "os": "linux", "cpu": "arm64" }, "sha512-TjCenQq3N6g1C+5UT3jE1bIiJb5MWQvulpUngTIpFsL4StVAUXucWD0SL9MCW89Tm6awWfeXBbZBAhJwjyFbRQ=="], + "@biomejs/cli-linux-arm64-musl": ["@biomejs/cli-linux-arm64-musl@2.3.6", "", { "os": "linux", "cpu": "arm64" }, "sha512-oK1NpIXIixbJ/4Tcx40cwiieqah6rRUtMGOHDeK2ToT7yUFVEvXUGRKqH0O4hqZ9tW8TcXNZKfgRH6xrsjVtGg=="], - "@biomejs/cli-linux-x64": ["@biomejs/cli-linux-x64@2.2.6", "", { "os": "linux", "cpu": "x64" }, "sha512-1HaM/dpI/1Z68zp8ZdT6EiBq+/O/z97a2AiHMl+VAdv5/ELckFt9EvRb8hDHpk8hUMoz03gXkC7VPXOVtU7faA=="], + "@biomejs/cli-linux-x64": ["@biomejs/cli-linux-x64@2.3.6", "", { "os": "linux", "cpu": "x64" }, "sha512-ZjPXzy5yN9wusIoX+8Zp4p6cL8r0NzJCXg/4r1KLVveIPXd2jKVlqZ6ZyzEq385WwU3OX5KOwQYLQsOc788waQ=="], - "@biomejs/cli-linux-x64-musl": ["@biomejs/cli-linux-x64-musl@2.2.6", "", { "os": "linux", "cpu": "x64" }, "sha512-1ZcBux8zVM3JhWN2ZCPaYf0+ogxXG316uaoXJdgoPZcdK/rmRcRY7PqHdAos2ExzvjIdvhQp72UcveI98hgOog=="], + "@biomejs/cli-linux-x64-musl": ["@biomejs/cli-linux-x64-musl@2.3.6", "", { "os": "linux", "cpu": "x64" }, "sha512-QvxB8GHQeaO4FCtwJpJjCgJkbHBbWxRHUxQlod+xeaYE6gtJdSkYkuxdKAQUZEOIsec+PeaDAhW9xjzYbwmOFA=="], - "@biomejs/cli-win32-arm64": ["@biomejs/cli-win32-arm64@2.2.6", "", { "os": "win32", "cpu": "arm64" }, "sha512-h3A88G8PGM1ryTeZyLlSdfC/gz3e95EJw9BZmA6Po412DRqwqPBa2Y9U+4ZSGUAXCsnSQE00jLV8Pyrh0d+jQw=="], + "@biomejs/cli-win32-arm64": ["@biomejs/cli-win32-arm64@2.3.6", "", { "os": "win32", "cpu": "arm64" }, "sha512-YM7hLHpwjdt8R7+O2zS1Vo2cKgqEeptiXB1tWW1rgjN5LlpZovBVKtg7zfwfRrFx3i08aNZThYpTcowpTlczug=="], - "@biomejs/cli-win32-x64": ["@biomejs/cli-win32-x64@2.2.6", "", { "os": "win32", "cpu": "x64" }, "sha512-yx0CqeOhPjYQ5ZXgPfu8QYkgBhVJyvWe36as7jRuPrKPO5ylVDfwVtPQ+K/mooNTADW0IhxOZm3aPu16dP8yNQ=="], + "@biomejs/cli-win32-x64": ["@biomejs/cli-win32-x64@2.3.6", "", { "os": "win32", "cpu": "x64" }, "sha512-psgNEYgMAobY5h+QHRBVR9xvg2KocFuBKm6axZWB/aD12NWhQjiVFQUjV6wMXhlH4iT0Q9c3yK5JFRiDC/rzHA=="], "@braintree/sanitize-url": ["@braintree/sanitize-url@7.1.1", "", {}, "sha512-i1L7noDNxtFyL5DmZafWy1wRVhGehQmzZaz1HiN5e7iylJMSZR7ekOV7NsIqa5qBldlLrsKv4HbgFUVlQrz8Mw=="], @@ -745,75 +745,75 @@ "@docsearch/react": ["@docsearch/react@3.8.2", "", { "dependencies": { "@algolia/autocomplete-core": "1.17.7", "@algolia/autocomplete-preset-algolia": "1.17.7", "@docsearch/css": "3.8.2", "algoliasearch": "^5.14.2" }, "peerDependencies": { "@types/react": ">= 16.8.0 < 19.0.0", "react": ">= 16.8.0 < 19.0.0", "react-dom": ">= 16.8.0 < 19.0.0", "search-insights": ">= 1 < 3" }, "optionalPeers": ["@types/react", "react", "react-dom", "search-insights"] }, "sha512-xCRrJQlTt8N9GU0DG4ptwHRkfnSnD/YpdeaXe02iKfqs97TkZJv60yE+1eq/tjPcVnTW8dP5qLP7itifFVV5eg=="], - "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.11", "", { "os": "aix", "cpu": "ppc64" }, "sha512-Xt1dOL13m8u0WE8iplx9Ibbm+hFAO0GsU2P34UNoDGvZYkY8ifSiy6Zuc1lYxfG7svWE2fzqCUmFp5HCn51gJg=="], + "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.12", "", { "os": "aix", "cpu": "ppc64" }, "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA=="], - "@esbuild/android-arm": ["@esbuild/android-arm@0.25.11", "", { "os": "android", "cpu": "arm" }, "sha512-uoa7dU+Dt3HYsethkJ1k6Z9YdcHjTrSb5NUy66ZfZaSV8hEYGD5ZHbEMXnqLFlbBflLsl89Zke7CAdDJ4JI+Gg=="], + "@esbuild/android-arm": ["@esbuild/android-arm@0.25.12", "", { "os": "android", "cpu": "arm" }, "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg=="], - "@esbuild/android-arm64": ["@esbuild/android-arm64@0.25.11", "", { "os": "android", "cpu": "arm64" }, "sha512-9slpyFBc4FPPz48+f6jyiXOx/Y4v34TUeDDXJpZqAWQn/08lKGeD8aDp9TMn9jDz2CiEuHwfhRmGBvpnd/PWIQ=="], + "@esbuild/android-arm64": ["@esbuild/android-arm64@0.25.12", "", { "os": "android", "cpu": "arm64" }, "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg=="], - "@esbuild/android-x64": ["@esbuild/android-x64@0.25.11", "", { "os": "android", "cpu": "x64" }, "sha512-Sgiab4xBjPU1QoPEIqS3Xx+R2lezu0LKIEcYe6pftr56PqPygbB7+szVnzoShbx64MUupqoE0KyRlN7gezbl8g=="], + "@esbuild/android-x64": ["@esbuild/android-x64@0.25.12", "", { "os": "android", "cpu": "x64" }, "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg=="], - "@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.25.11", "", { "os": "darwin", "cpu": "arm64" }, "sha512-VekY0PBCukppoQrycFxUqkCojnTQhdec0vevUL/EDOCnXd9LKWqD/bHwMPzigIJXPhC59Vd1WFIL57SKs2mg4w=="], + "@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.25.12", "", { "os": "darwin", "cpu": "arm64" }, "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg=="], - "@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.25.11", "", { "os": "darwin", "cpu": "x64" }, "sha512-+hfp3yfBalNEpTGp9loYgbknjR695HkqtY3d3/JjSRUyPg/xd6q+mQqIb5qdywnDxRZykIHs3axEqU6l1+oWEQ=="], + "@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.25.12", "", { "os": "darwin", "cpu": "x64" }, "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA=="], - "@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.25.11", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-CmKjrnayyTJF2eVuO//uSjl/K3KsMIeYeyN7FyDBjsR3lnSJHaXlVoAK8DZa7lXWChbuOk7NjAc7ygAwrnPBhA=="], + "@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.25.12", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg=="], - "@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.25.11", "", { "os": "freebsd", "cpu": "x64" }, "sha512-Dyq+5oscTJvMaYPvW3x3FLpi2+gSZTCE/1ffdwuM6G1ARang/mb3jvjxs0mw6n3Lsw84ocfo9CrNMqc5lTfGOw=="], + "@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.25.12", "", { "os": "freebsd", "cpu": "x64" }, "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ=="], - "@esbuild/linux-arm": ["@esbuild/linux-arm@0.25.11", "", { "os": "linux", "cpu": "arm" }, "sha512-TBMv6B4kCfrGJ8cUPo7vd6NECZH/8hPpBHHlYI3qzoYFvWu2AdTvZNuU/7hsbKWqu/COU7NIK12dHAAqBLLXgw=="], + "@esbuild/linux-arm": ["@esbuild/linux-arm@0.25.12", "", { "os": "linux", "cpu": "arm" }, "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw=="], - "@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.25.11", "", { "os": "linux", "cpu": "arm64" }, "sha512-Qr8AzcplUhGvdyUF08A1kHU3Vr2O88xxP0Tm8GcdVOUm25XYcMPp2YqSVHbLuXzYQMf9Bh/iKx7YPqECs6ffLA=="], + "@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.25.12", "", { "os": "linux", "cpu": "arm64" }, "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ=="], - "@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.25.11", "", { "os": "linux", "cpu": "ia32" }, "sha512-TmnJg8BMGPehs5JKrCLqyWTVAvielc615jbkOirATQvWWB1NMXY77oLMzsUjRLa0+ngecEmDGqt5jiDC6bfvOw=="], + "@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.25.12", "", { "os": "linux", "cpu": "ia32" }, "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA=="], - "@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.25.11", "", { "os": "linux", "cpu": "none" }, "sha512-DIGXL2+gvDaXlaq8xruNXUJdT5tF+SBbJQKbWy/0J7OhU8gOHOzKmGIlfTTl6nHaCOoipxQbuJi7O++ldrxgMw=="], + "@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.25.12", "", { "os": "linux", "cpu": "none" }, "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng=="], - "@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.25.11", "", { "os": "linux", "cpu": "none" }, "sha512-Osx1nALUJu4pU43o9OyjSCXokFkFbyzjXb6VhGIJZQ5JZi8ylCQ9/LFagolPsHtgw6himDSyb5ETSfmp4rpiKQ=="], + "@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.25.12", "", { "os": "linux", "cpu": "none" }, "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw=="], - "@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.25.11", "", { "os": "linux", "cpu": "ppc64" }, "sha512-nbLFgsQQEsBa8XSgSTSlrnBSrpoWh7ioFDUmwo158gIm5NNP+17IYmNWzaIzWmgCxq56vfr34xGkOcZ7jX6CPw=="], + "@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.25.12", "", { "os": "linux", "cpu": "ppc64" }, "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA=="], - "@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.25.11", "", { "os": "linux", "cpu": "none" }, "sha512-HfyAmqZi9uBAbgKYP1yGuI7tSREXwIb438q0nqvlpxAOs3XnZ8RsisRfmVsgV486NdjD7Mw2UrFSw51lzUk1ww=="], + "@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.25.12", "", { "os": "linux", "cpu": "none" }, "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w=="], - "@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.25.11", "", { "os": "linux", "cpu": "s390x" }, "sha512-HjLqVgSSYnVXRisyfmzsH6mXqyvj0SA7pG5g+9W7ESgwA70AXYNpfKBqh1KbTxmQVaYxpzA/SvlB9oclGPbApw=="], + "@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.25.12", "", { "os": "linux", "cpu": "s390x" }, "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg=="], - "@esbuild/linux-x64": ["@esbuild/linux-x64@0.25.11", "", { "os": "linux", "cpu": "x64" }, "sha512-HSFAT4+WYjIhrHxKBwGmOOSpphjYkcswF449j6EjsjbinTZbp8PJtjsVK1XFJStdzXdy/jaddAep2FGY+wyFAQ=="], + "@esbuild/linux-x64": ["@esbuild/linux-x64@0.25.12", "", { "os": "linux", "cpu": "x64" }, "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw=="], - "@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.25.11", "", { "os": "none", "cpu": "arm64" }, "sha512-hr9Oxj1Fa4r04dNpWr3P8QKVVsjQhqrMSUzZzf+LZcYjZNqhA3IAfPQdEh1FLVUJSiu6sgAwp3OmwBfbFgG2Xg=="], + "@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.25.12", "", { "os": "none", "cpu": "arm64" }, "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg=="], - "@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.25.11", "", { "os": "none", "cpu": "x64" }, "sha512-u7tKA+qbzBydyj0vgpu+5h5AeudxOAGncb8N6C9Kh1N4n7wU1Xw1JDApsRjpShRpXRQlJLb9wY28ELpwdPcZ7A=="], + "@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.25.12", "", { "os": "none", "cpu": "x64" }, "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ=="], - "@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.25.11", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-Qq6YHhayieor3DxFOoYM1q0q1uMFYb7cSpLD2qzDSvK1NAvqFi8Xgivv0cFC6J+hWVw2teCYltyy9/m/14ryHg=="], + "@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.25.12", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A=="], - "@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.25.11", "", { "os": "openbsd", "cpu": "x64" }, "sha512-CN+7c++kkbrckTOz5hrehxWN7uIhFFlmS/hqziSFVWpAzpWrQoAG4chH+nN3Be+Kzv/uuo7zhX716x3Sn2Jduw=="], + "@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.25.12", "", { "os": "openbsd", "cpu": "x64" }, "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw=="], - "@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.25.11", "", { "os": "none", "cpu": "arm64" }, "sha512-rOREuNIQgaiR+9QuNkbkxubbp8MSO9rONmwP5nKncnWJ9v5jQ4JxFnLu4zDSRPf3x4u+2VN4pM4RdyIzDty/wQ=="], + "@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.25.12", "", { "os": "none", "cpu": "arm64" }, "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg=="], - "@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.25.11", "", { "os": "sunos", "cpu": "x64" }, "sha512-nq2xdYaWxyg9DcIyXkZhcYulC6pQ2FuCgem3LI92IwMgIZ69KHeY8T4Y88pcwoLIjbed8n36CyKoYRDygNSGhA=="], + "@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.25.12", "", { "os": "sunos", "cpu": "x64" }, "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w=="], - "@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.25.11", "", { "os": "win32", "cpu": "arm64" }, "sha512-3XxECOWJq1qMZ3MN8srCJ/QfoLpL+VaxD/WfNRm1O3B4+AZ/BnLVgFbUV3eiRYDMXetciH16dwPbbHqwe1uU0Q=="], + "@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.25.12", "", { "os": "win32", "cpu": "arm64" }, "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg=="], - "@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.25.11", "", { "os": "win32", "cpu": "ia32" }, "sha512-3ukss6gb9XZ8TlRyJlgLn17ecsK4NSQTmdIXRASVsiS2sQ6zPPZklNJT5GR5tE/MUarymmy8kCEf5xPCNCqVOA=="], + "@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.25.12", "", { "os": "win32", "cpu": "ia32" }, "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ=="], - "@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.11", "", { "os": "win32", "cpu": "x64" }, "sha512-D7Hpz6A2L4hzsRpPaCYkQnGOotdUpDzSGRIv9I+1ITdHROSFUWW95ZPZWQmGka1Fg7W3zFJowyn9WGwMJ0+KPA=="], + "@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.12", "", { "os": "win32", "cpu": "x64" }, "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA=="], "@eslint-community/eslint-utils": ["@eslint-community/eslint-utils@4.9.0", "", { "dependencies": { "eslint-visitor-keys": "^3.4.3" }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, "sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g=="], "@eslint-community/regexpp": ["@eslint-community/regexpp@4.12.1", "", {}, "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ=="], - "@eslint/config-array": ["@eslint/config-array@0.21.0", "", { "dependencies": { "@eslint/object-schema": "^2.1.6", "debug": "^4.3.1", "minimatch": "^3.1.2" } }, "sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ=="], + "@eslint/config-array": ["@eslint/config-array@0.21.1", "", { "dependencies": { "@eslint/object-schema": "^2.1.7", "debug": "^4.3.1", "minimatch": "^3.1.2" } }, "sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA=="], - "@eslint/config-helpers": ["@eslint/config-helpers@0.4.0", "", { "dependencies": { "@eslint/core": "^0.16.0" } }, "sha512-WUFvV4WoIwW8Bv0KeKCIIEgdSiFOsulyN0xrMu+7z43q/hkOLXjvb5u7UC9jDxvRzcrbEmuZBX5yJZz1741jog=="], + "@eslint/config-helpers": ["@eslint/config-helpers@0.4.2", "", { "dependencies": { "@eslint/core": "^0.17.0" } }, "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw=="], - "@eslint/core": ["@eslint/core@0.16.0", "", { "dependencies": { "@types/json-schema": "^7.0.15" } }, "sha512-nmC8/totwobIiFcGkDza3GIKfAw1+hLiYVrh3I1nIomQ8PEr5cxg34jnkmGawul/ep52wGRAcyeDCNtWKSOj4Q=="], + "@eslint/core": ["@eslint/core@0.17.0", "", { "dependencies": { "@types/json-schema": "^7.0.15" } }, "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ=="], "@eslint/eslintrc": ["@eslint/eslintrc@3.3.1", "", { "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", "espree": "^10.0.1", "globals": "^14.0.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.0", "minimatch": "^3.1.2", "strip-json-comments": "^3.1.1" } }, "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ=="], - "@eslint/js": ["@eslint/js@9.37.0", "", {}, "sha512-jaS+NJ+hximswBG6pjNX0uEJZkrT0zwpVi3BA3vX22aFGjJjmgSTSmPpZCRKmoBL5VY/M6p0xsSJx7rk7sy5gg=="], + "@eslint/js": ["@eslint/js@9.39.1", "", {}, "sha512-S26Stp4zCy88tH94QbBv3XCuzRQiZ9yXofEILmglYTh/Ug/a9/umqvgFtYBAo3Lp0nsI/5/qH1CCrbdK3AP1Tw=="], - "@eslint/object-schema": ["@eslint/object-schema@2.1.6", "", {}, "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA=="], + "@eslint/object-schema": ["@eslint/object-schema@2.1.7", "", {}, "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA=="], - "@eslint/plugin-kit": ["@eslint/plugin-kit@0.4.0", "", { "dependencies": { "@eslint/core": "^0.16.0", "levn": "^0.4.1" } }, "sha512-sB5uyeq+dwCWyPi31B2gQlVlo+j5brPlWx4yZBrEaRo/nhdDE8Xke1gsGgtiBdaBTxuTkceLVuVt/pclrasb0A=="], + "@eslint/plugin-kit": ["@eslint/plugin-kit@0.4.1", "", { "dependencies": { "@eslint/core": "^0.17.0", "levn": "^0.4.1" } }, "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA=="], "@expo/cli": ["@expo/cli@0.24.22", "", { "dependencies": { "@0no-co/graphql.web": "^1.0.8", "@babel/runtime": "^7.20.0", "@expo/code-signing-certificates": "^0.0.5", "@expo/config": "~11.0.13", "@expo/config-plugins": "~10.1.2", "@expo/devcert": "^1.1.2", "@expo/env": "~1.0.7", "@expo/image-utils": "^0.7.6", "@expo/json-file": "^9.1.5", "@expo/metro-config": "~0.20.17", "@expo/osascript": "^2.2.5", "@expo/package-manager": "^1.8.6", "@expo/plist": "^0.3.5", "@expo/prebuild-config": "^9.0.12", "@expo/schema-utils": "^0.1.0", "@expo/spawn-async": "^1.7.2", "@expo/ws-tunnel": "^1.0.1", "@expo/xcpretty": "^4.3.0", "@react-native/dev-middleware": "0.79.6", "@urql/core": "^5.0.6", "@urql/exchange-retry": "^1.3.0", "accepts": "^1.3.8", "arg": "^5.0.2", "better-opn": "~3.0.2", "bplist-creator": "0.1.0", "bplist-parser": "^0.3.1", "chalk": "^4.0.0", "ci-info": "^3.3.0", "compression": "^1.7.4", "connect": "^3.7.0", "debug": "^4.3.4", "env-editor": "^0.4.1", "freeport-async": "^2.0.0", "getenv": "^2.0.0", "glob": "^10.4.2", "lan-network": "^0.1.6", "minimatch": "^9.0.0", "node-forge": "^1.3.1", "npm-package-arg": "^11.0.0", "ora": "^3.4.0", "picomatch": "^3.0.1", "pretty-bytes": "^5.6.0", "pretty-format": "^29.7.0", "progress": "^2.0.3", "prompts": "^2.3.2", "qrcode-terminal": "0.11.0", "require-from-string": "^2.0.2", "requireg": "^0.2.2", "resolve": "^1.22.2", "resolve-from": "^5.0.0", "resolve.exports": "^2.0.3", "semver": "^7.6.0", "send": "^0.19.0", "slugify": "^1.3.4", "source-map-support": "~0.5.21", "stacktrace-parser": "^0.1.10", "structured-headers": "^0.4.1", "tar": "^7.4.3", "terminal-link": "^2.1.1", "undici": "^6.18.2", "wrap-ansi": "^7.0.0", "ws": "^8.12.1" }, "bin": { "expo-internal": "build/bin/cli" } }, "sha512-cEg6/F8ZWjoVkEwm0rXKReWbsCUROFbLFBYht+d5RzHnDwJoTX4QWJKx4m+TGNDPamRUIGw36U4z41Fvev0XmA=="], @@ -1073,7 +1073,7 @@ "@sveltejs/adapter-auto": ["@sveltejs/adapter-auto@3.3.1", "", { "dependencies": { "import-meta-resolve": "^4.1.0" }, "peerDependencies": { "@sveltejs/kit": "^2.0.0" } }, "sha512-5Sc7WAxYdL6q9j/+D0jJKjGREGlfIevDyHSQ2eNETHcB1TKlQWHcAo8AS8H1QdjNvSXpvOwNjykDUHPEAyGgdQ=="], - "@sveltejs/kit": ["@sveltejs/kit@2.46.4", "", { "dependencies": { "@standard-schema/spec": "^1.0.0", "@sveltejs/acorn-typescript": "^1.0.5", "@types/cookie": "^0.6.0", "acorn": "^8.14.1", "cookie": "^0.6.0", "devalue": "^5.3.2", "esm-env": "^1.2.2", "kleur": "^4.1.5", "magic-string": "^0.30.5", "mrmime": "^2.0.0", "sade": "^1.8.1", "set-cookie-parser": "^2.6.0", "sirv": "^3.0.0" }, "peerDependencies": { "@opentelemetry/api": "^1.0.0", "@sveltejs/vite-plugin-svelte": "^3.0.0 || ^4.0.0-next.1 || ^5.0.0 || ^6.0.0-next.0", "svelte": "^4.0.0 || ^5.0.0-next.0", "vite": "^5.0.3 || ^6.0.0 || ^7.0.0-beta.0" }, "optionalPeers": ["@opentelemetry/api"], "bin": { "svelte-kit": "svelte-kit.js" } }, "sha512-J1fd80WokLzIm6EAV7z7C2+/C02qVAX645LZomARARTRJkbbJSY1Jln3wtBZYibUB8c9/5Z6xqLAV39VdbtWCQ=="], + "@sveltejs/kit": ["@sveltejs/kit@2.48.6", "", { "dependencies": { "@standard-schema/spec": "^1.0.0", "@sveltejs/acorn-typescript": "^1.0.5", "@types/cookie": "^0.6.0", "acorn": "^8.14.1", "cookie": "^0.6.0", "devalue": "^5.3.2", "esm-env": "^1.2.2", "kleur": "^4.1.5", "magic-string": "^0.30.5", "mrmime": "^2.0.0", "sade": "^1.8.1", "set-cookie-parser": "^2.6.0", "sirv": "^3.0.0" }, "peerDependencies": { "@opentelemetry/api": "^1.0.0", "@sveltejs/vite-plugin-svelte": "^3.0.0 || ^4.0.0-next.1 || ^5.0.0 || ^6.0.0-next.0", "svelte": "^4.0.0 || ^5.0.0-next.0", "vite": "^5.0.3 || ^6.0.0 || ^7.0.0-beta.0" }, "optionalPeers": ["@opentelemetry/api"], "bin": { "svelte-kit": "svelte-kit.js" } }, "sha512-NMRtq+bzvnZp7+yugfKFbWZABWwo+7xySv6BMSItRrhks5C3u/4mW6y0IiZBKH3yT8C6HTMRIY4ZGljYtIYPuA=="], "@sveltejs/package": ["@sveltejs/package@2.5.4", "", { "dependencies": { "chokidar": "^4.0.3", "kleur": "^4.1.5", "sade": "^1.8.1", "semver": "^7.5.4", "svelte2tsx": "~0.7.33" }, "peerDependencies": { "svelte": "^3.44.0 || ^4.0.0 || ^5.0.0-next.1" }, "bin": { "svelte-package": "svelte-package.js" } }, "sha512-8+1hccAt0M3PPkHVPKH54Wc+cc1PNxRqCrICZiv/hEEto8KwbQVRghxNgTB4htIPyle+4CIB8RayTQH5zRQh9A=="], @@ -1293,25 +1293,25 @@ "@types/yargs-parser": ["@types/yargs-parser@21.0.3", "", {}, "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ=="], - "@typescript-eslint/eslint-plugin": ["@typescript-eslint/eslint-plugin@8.46.0", "", { "dependencies": { "@eslint-community/regexpp": "^4.10.0", "@typescript-eslint/scope-manager": "8.46.0", "@typescript-eslint/type-utils": "8.46.0", "@typescript-eslint/utils": "8.46.0", "@typescript-eslint/visitor-keys": "8.46.0", "graphemer": "^1.4.0", "ignore": "^7.0.0", "natural-compare": "^1.4.0", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "@typescript-eslint/parser": "^8.46.0", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-hA8gxBq4ukonVXPy0OKhiaUh/68D0E88GSmtC1iAEnGaieuDi38LhS7jdCHRLi6ErJBNDGCzvh5EnzdPwUc0DA=="], + "@typescript-eslint/eslint-plugin": ["@typescript-eslint/eslint-plugin@8.47.0", "", { "dependencies": { "@eslint-community/regexpp": "^4.10.0", "@typescript-eslint/scope-manager": "8.47.0", "@typescript-eslint/type-utils": "8.47.0", "@typescript-eslint/utils": "8.47.0", "@typescript-eslint/visitor-keys": "8.47.0", "graphemer": "^1.4.0", "ignore": "^7.0.0", "natural-compare": "^1.4.0", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "@typescript-eslint/parser": "^8.47.0", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-fe0rz9WJQ5t2iaLfdbDc9T80GJy0AeO453q8C3YCilnGozvOyCG5t+EZtg7j7D88+c3FipfP/x+wzGnh1xp8ZA=="], - "@typescript-eslint/parser": ["@typescript-eslint/parser@8.46.0", "", { "dependencies": { "@typescript-eslint/scope-manager": "8.46.0", "@typescript-eslint/types": "8.46.0", "@typescript-eslint/typescript-estree": "8.46.0", "@typescript-eslint/visitor-keys": "8.46.0", "debug": "^4.3.4" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-n1H6IcDhmmUEG7TNVSspGmiHHutt7iVKtZwRppD7e04wha5MrkV1h3pti9xQLcCMt6YWsncpoT0HMjkH1FNwWQ=="], + "@typescript-eslint/parser": ["@typescript-eslint/parser@8.47.0", "", { "dependencies": { "@typescript-eslint/scope-manager": "8.47.0", "@typescript-eslint/types": "8.47.0", "@typescript-eslint/typescript-estree": "8.47.0", "@typescript-eslint/visitor-keys": "8.47.0", "debug": "^4.3.4" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-lJi3PfxVmo0AkEY93ecfN+r8SofEqZNGByvHAI3GBLrvt1Cw6H5k1IM02nSzu0RfUafr2EvFSw0wAsZgubNplQ=="], - "@typescript-eslint/project-service": ["@typescript-eslint/project-service@8.46.0", "", { "dependencies": { "@typescript-eslint/tsconfig-utils": "^8.46.0", "@typescript-eslint/types": "^8.46.0", "debug": "^4.3.4" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-OEhec0mH+U5Je2NZOeK1AbVCdm0ChyapAyTeXVIYTPXDJ3F07+cu87PPXcGoYqZ7M9YJVvFnfpGg1UmCIqM+QQ=="], + "@typescript-eslint/project-service": ["@typescript-eslint/project-service@8.47.0", "", { "dependencies": { "@typescript-eslint/tsconfig-utils": "^8.47.0", "@typescript-eslint/types": "^8.47.0", "debug": "^4.3.4" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-2X4BX8hUeB5JcA1TQJ7GjcgulXQ+5UkNb0DL8gHsHUHdFoiCTJoYLTpib3LtSDPZsRET5ygN4qqIWrHyYIKERA=="], - "@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@8.46.0", "", { "dependencies": { "@typescript-eslint/types": "8.46.0", "@typescript-eslint/visitor-keys": "8.46.0" } }, "sha512-lWETPa9XGcBes4jqAMYD9fW0j4n6hrPtTJwWDmtqgFO/4HF4jmdH/Q6wggTw5qIT5TXjKzbt7GsZUBnWoO3dqw=="], + "@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@8.47.0", "", { "dependencies": { "@typescript-eslint/types": "8.47.0", "@typescript-eslint/visitor-keys": "8.47.0" } }, "sha512-a0TTJk4HXMkfpFkL9/WaGTNuv7JWfFTQFJd6zS9dVAjKsojmv9HT55xzbEpnZoY+VUb+YXLMp+ihMLz/UlZfDg=="], - "@typescript-eslint/tsconfig-utils": ["@typescript-eslint/tsconfig-utils@8.46.0", "", { "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-WrYXKGAHY836/N7zoK/kzi6p8tXFhasHh8ocFL9VZSAkvH956gfeRfcnhs3xzRy8qQ/dq3q44v1jvQieMFg2cw=="], + "@typescript-eslint/tsconfig-utils": ["@typescript-eslint/tsconfig-utils@8.47.0", "", { "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-ybUAvjy4ZCL11uryalkKxuT3w3sXJAuWhOoGS3T/Wu+iUu1tGJmk5ytSY8gbdACNARmcYEB0COksD2j6hfGK2g=="], - "@typescript-eslint/type-utils": ["@typescript-eslint/type-utils@8.46.0", "", { "dependencies": { "@typescript-eslint/types": "8.46.0", "@typescript-eslint/typescript-estree": "8.46.0", "@typescript-eslint/utils": "8.46.0", "debug": "^4.3.4", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-hy+lvYV1lZpVs2jRaEYvgCblZxUoJiPyCemwbQZ+NGulWkQRy0HRPYAoef/CNSzaLt+MLvMptZsHXHlkEilaeg=="], + "@typescript-eslint/type-utils": ["@typescript-eslint/type-utils@8.47.0", "", { "dependencies": { "@typescript-eslint/types": "8.47.0", "@typescript-eslint/typescript-estree": "8.47.0", "@typescript-eslint/utils": "8.47.0", "debug": "^4.3.4", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-QC9RiCmZ2HmIdCEvhd1aJELBlD93ErziOXXlHEZyuBo3tBiAZieya0HLIxp+DoDWlsQqDawyKuNEhORyku+P8A=="], - "@typescript-eslint/types": ["@typescript-eslint/types@8.46.0", "", {}, "sha512-bHGGJyVjSE4dJJIO5yyEWt/cHyNwga/zXGJbJJ8TiO01aVREK6gCTu3L+5wrkb1FbDkQ+TKjMNe9R/QQQP9+rA=="], + "@typescript-eslint/types": ["@typescript-eslint/types@8.47.0", "", {}, "sha512-nHAE6bMKsizhA2uuYZbEbmp5z2UpffNrPEqiKIeN7VsV6UY/roxanWfoRrf6x/k9+Obf+GQdkm0nPU+vnMXo9A=="], - "@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@8.46.0", "", { "dependencies": { "@typescript-eslint/project-service": "8.46.0", "@typescript-eslint/tsconfig-utils": "8.46.0", "@typescript-eslint/types": "8.46.0", "@typescript-eslint/visitor-keys": "8.46.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", "minimatch": "^9.0.4", "semver": "^7.6.0", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-ekDCUfVpAKWJbRfm8T1YRrCot1KFxZn21oV76v5Fj4tr7ELyk84OS+ouvYdcDAwZL89WpEkEj2DKQ+qg//+ucg=="], + "@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@8.47.0", "", { "dependencies": { "@typescript-eslint/project-service": "8.47.0", "@typescript-eslint/tsconfig-utils": "8.47.0", "@typescript-eslint/types": "8.47.0", "@typescript-eslint/visitor-keys": "8.47.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", "minimatch": "^9.0.4", "semver": "^7.6.0", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-k6ti9UepJf5NpzCjH31hQNLHQWupTRPhZ+KFF8WtTuTpy7uHPfeg2NM7cP27aCGajoEplxJDFVCEm9TGPYyiVg=="], - "@typescript-eslint/utils": ["@typescript-eslint/utils@8.46.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.7.0", "@typescript-eslint/scope-manager": "8.46.0", "@typescript-eslint/types": "8.46.0", "@typescript-eslint/typescript-estree": "8.46.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-nD6yGWPj1xiOm4Gk0k6hLSZz2XkNXhuYmyIrOWcHoPuAhjT9i5bAG+xbWPgFeNR8HPHHtpNKdYUXJl/D3x7f5g=="], + "@typescript-eslint/utils": ["@typescript-eslint/utils@8.47.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.7.0", "@typescript-eslint/scope-manager": "8.47.0", "@typescript-eslint/types": "8.47.0", "@typescript-eslint/typescript-estree": "8.47.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-g7XrNf25iL4TJOiPqatNuaChyqt49a/onq5YsJ9+hXeugK+41LVg7AxikMfM02PC6jbNtZLCJj6AUcQXJS/jGQ=="], - "@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.46.0", "", { "dependencies": { "@typescript-eslint/types": "8.46.0", "eslint-visitor-keys": "^4.2.1" } }, "sha512-FrvMpAK+hTbFy7vH5j1+tMYHMSKLE6RzluFJlkFNKD0p9YsUT75JlBSmr5so3QRzvMwU5/bIEdeNrxm8du8l3Q=="], + "@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.47.0", "", { "dependencies": { "@typescript-eslint/types": "8.47.0", "eslint-visitor-keys": "^4.2.1" } }, "sha512-SIV3/6eftCy1bNzCQoPmbWsRLujS8t5iDIZ4spZOBHqrM+yfX2ogg8Tt3PDTAVKw3sSCiUgg30uOAvK2r9zGjQ=="], "@ungap/structured-clone": ["@ungap/structured-clone@1.3.0", "", {}, "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g=="], @@ -1697,7 +1697,7 @@ "d3-zoom": ["d3-zoom@3.0.0", "", { "dependencies": { "d3-dispatch": "1 - 3", "d3-drag": "2 - 3", "d3-interpolate": "1 - 3", "d3-selection": "2 - 3", "d3-transition": "2 - 3" } }, "sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw=="], - "dagre-d3-es": ["dagre-d3-es@7.0.11", "", { "dependencies": { "d3": "^7.9.0", "lodash-es": "^4.17.21" } }, "sha512-tvlJLyQf834SylNKax8Wkzco/1ias1OPw8DcUMDE7oUIoSEW25riQVuiu/0OWEFqT0cxHT3Pa9/D82Jr47IONw=="], + "dagre-d3-es": ["dagre-d3-es@7.0.13", "", { "dependencies": { "d3": "^7.9.0", "lodash-es": "^4.17.21" } }, "sha512-efEhnxpSuwpYOKRm/L5KbqoZmNNukHa/Flty4Wp62JRvgH2ojwVgPgdYyr4twpieZnyRDdIH7PY2mopX26+j2Q=="], "data-urls": ["data-urls@6.0.0", "", { "dependencies": { "whatwg-mimetype": "^4.0.0", "whatwg-url": "^15.0.0" } }, "sha512-BnBS08aLUM+DKamupXs3w2tJJoqU+AkaE/+6vQxi/G/DPmIZFJJp9Dkb1kM03AZx8ADehDUZgsNxju3mPXZYIA=="], @@ -1829,7 +1829,7 @@ "es6-symbol": ["es6-symbol@3.1.4", "", { "dependencies": { "d": "^1.0.2", "ext": "^1.7.0" } }, "sha512-U9bFFjX8tFiATgtkJ1zg25+KviIXpgRvRHS8sau3GfhVzThRQrOeksPeT0BWW2MNZs1OEWJ1DPXOQMn0KKRkvg=="], - "esbuild": ["esbuild@0.25.11", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.11", "@esbuild/android-arm": "0.25.11", "@esbuild/android-arm64": "0.25.11", "@esbuild/android-x64": "0.25.11", "@esbuild/darwin-arm64": "0.25.11", "@esbuild/darwin-x64": "0.25.11", "@esbuild/freebsd-arm64": "0.25.11", "@esbuild/freebsd-x64": "0.25.11", "@esbuild/linux-arm": "0.25.11", "@esbuild/linux-arm64": "0.25.11", "@esbuild/linux-ia32": "0.25.11", "@esbuild/linux-loong64": "0.25.11", "@esbuild/linux-mips64el": "0.25.11", "@esbuild/linux-ppc64": "0.25.11", "@esbuild/linux-riscv64": "0.25.11", "@esbuild/linux-s390x": "0.25.11", "@esbuild/linux-x64": "0.25.11", "@esbuild/netbsd-arm64": "0.25.11", "@esbuild/netbsd-x64": "0.25.11", "@esbuild/openbsd-arm64": "0.25.11", "@esbuild/openbsd-x64": "0.25.11", "@esbuild/openharmony-arm64": "0.25.11", "@esbuild/sunos-x64": "0.25.11", "@esbuild/win32-arm64": "0.25.11", "@esbuild/win32-ia32": "0.25.11", "@esbuild/win32-x64": "0.25.11" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-KohQwyzrKTQmhXDW1PjCv3Tyspn9n5GcY2RTDqeORIdIJY8yKIF7sTSopFmn/wpMPW4rdPXI0UE5LJLuq3bx0Q=="], + "esbuild": ["esbuild@0.25.12", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.12", "@esbuild/android-arm": "0.25.12", "@esbuild/android-arm64": "0.25.12", "@esbuild/android-x64": "0.25.12", "@esbuild/darwin-arm64": "0.25.12", "@esbuild/darwin-x64": "0.25.12", "@esbuild/freebsd-arm64": "0.25.12", "@esbuild/freebsd-x64": "0.25.12", "@esbuild/linux-arm": "0.25.12", "@esbuild/linux-arm64": "0.25.12", "@esbuild/linux-ia32": "0.25.12", "@esbuild/linux-loong64": "0.25.12", "@esbuild/linux-mips64el": "0.25.12", "@esbuild/linux-ppc64": "0.25.12", "@esbuild/linux-riscv64": "0.25.12", "@esbuild/linux-s390x": "0.25.12", "@esbuild/linux-x64": "0.25.12", "@esbuild/netbsd-arm64": "0.25.12", "@esbuild/netbsd-x64": "0.25.12", "@esbuild/openbsd-arm64": "0.25.12", "@esbuild/openbsd-x64": "0.25.12", "@esbuild/openharmony-arm64": "0.25.12", "@esbuild/sunos-x64": "0.25.12", "@esbuild/win32-arm64": "0.25.12", "@esbuild/win32-ia32": "0.25.12", "@esbuild/win32-x64": "0.25.12" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg=="], "esbuild-plugin-alias": ["esbuild-plugin-alias@0.2.1", "", {}, "sha512-jyfL/pwPqaFXyKnj8lP8iLk6Z0m099uXR45aSN8Av1XD4vhvQutxxPzgA2bTcAwQpa1zCXDcWOlhFgyP3GKqhQ=="], @@ -1839,11 +1839,11 @@ "escape-string-regexp": ["escape-string-regexp@4.0.0", "", {}, "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="], - "eslint": ["eslint@9.37.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.21.0", "@eslint/config-helpers": "^0.4.0", "@eslint/core": "^0.16.0", "@eslint/eslintrc": "^3.3.1", "@eslint/js": "9.37.0", "@eslint/plugin-kit": "^0.4.0", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", "@types/estree": "^1.0.6", "@types/json-schema": "^7.0.15", "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", "eslint-scope": "^8.4.0", "eslint-visitor-keys": "^4.2.1", "espree": "^10.4.0", "esquery": "^1.5.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^8.0.0", "find-up": "^5.0.0", "glob-parent": "^6.0.2", "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "json-stable-stringify-without-jsonify": "^1.0.1", "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", "optionator": "^0.9.3" }, "peerDependencies": { "jiti": "*" }, "optionalPeers": ["jiti"], "bin": { "eslint": "bin/eslint.js" } }, "sha512-XyLmROnACWqSxiGYArdef1fItQd47weqB7iwtfr9JHwRrqIXZdcFMvvEcL9xHCmL0SNsOvF0c42lWyM1U5dgig=="], + "eslint": ["eslint@9.39.1", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.21.1", "@eslint/config-helpers": "^0.4.2", "@eslint/core": "^0.17.0", "@eslint/eslintrc": "^3.3.1", "@eslint/js": "9.39.1", "@eslint/plugin-kit": "^0.4.1", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", "@types/estree": "^1.0.6", "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", "eslint-scope": "^8.4.0", "eslint-visitor-keys": "^4.2.1", "espree": "^10.4.0", "esquery": "^1.5.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^8.0.0", "find-up": "^5.0.0", "glob-parent": "^6.0.2", "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "json-stable-stringify-without-jsonify": "^1.0.1", "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", "optionator": "^0.9.3" }, "peerDependencies": { "jiti": "*" }, "optionalPeers": ["jiti"], "bin": { "eslint": "bin/eslint.js" } }, "sha512-BhHmn2yNOFA9H9JmmIVKJmd288g9hrVRDkdoIgRCRuSySRUHH7r/DI6aAXW9T1WwUuY3DFgrcaqB+deURBLR5g=="], "eslint-formatter-pretty": ["eslint-formatter-pretty@4.1.0", "", { "dependencies": { "@types/eslint": "^7.2.13", "ansi-escapes": "^4.2.1", "chalk": "^4.1.0", "eslint-rule-docs": "^1.1.5", "log-symbols": "^4.0.0", "plur": "^4.0.0", "string-width": "^4.2.0", "supports-hyperlinks": "^2.0.0" } }, "sha512-IsUTtGxF1hrH6lMWiSl1WbGaiP01eT6kzywdY1U+zLc0MP+nwEnUiS9UI8IaOTUhTeQJLlCEWIbXINBH4YJbBQ=="], - "eslint-plugin-svelte": ["eslint-plugin-svelte@3.12.4", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.6.1", "@jridgewell/sourcemap-codec": "^1.5.0", "esutils": "^2.0.3", "globals": "^16.0.0", "known-css-properties": "^0.37.0", "postcss": "^8.4.49", "postcss-load-config": "^3.1.4", "postcss-safe-parser": "^7.0.0", "semver": "^7.6.3", "svelte-eslint-parser": "^1.3.0" }, "peerDependencies": { "eslint": "^8.57.1 || ^9.0.0", "svelte": "^3.37.0 || ^4.0.0 || ^5.0.0" }, "optionalPeers": ["svelte"] }, "sha512-hD7wPe+vrPgx3U2X2b/wyTMtWobm660PygMGKrWWYTc9lvtY8DpNFDaU2CJQn1szLjGbn/aJ3g8WiXuKakrEkw=="], + "eslint-plugin-svelte": ["eslint-plugin-svelte@3.13.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.6.1", "@jridgewell/sourcemap-codec": "^1.5.0", "esutils": "^2.0.3", "globals": "^16.0.0", "known-css-properties": "^0.37.0", "postcss": "^8.4.49", "postcss-load-config": "^3.1.4", "postcss-safe-parser": "^7.0.0", "semver": "^7.6.3", "svelte-eslint-parser": "^1.4.0" }, "peerDependencies": { "eslint": "^8.57.1 || ^9.0.0", "svelte": "^3.37.0 || ^4.0.0 || ^5.0.0" }, "optionalPeers": ["svelte"] }, "sha512-2ohCCQJJTNbIpQCSDSTWj+FN0OVfPmSO03lmSNT7ytqMaWF6kpT86LdzDqtm4sh7TVPl/OEWJ/d7R87bXP2Vjg=="], "eslint-rule-docs": ["eslint-rule-docs@1.1.235", "", {}, "sha512-+TQ+x4JdTnDoFEXXb3fDvfGOwnyNV7duH8fXWTPD1ieaBmB8omj7Gw/pMBBu4uI2uJCCU8APDaQJzWuXnTsH4A=="], @@ -2415,7 +2415,7 @@ "merge2": ["merge2@1.4.1", "", {}, "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg=="], - "mermaid": ["mermaid@11.12.0", "", { "dependencies": { "@braintree/sanitize-url": "^7.1.1", "@iconify/utils": "^3.0.1", "@mermaid-js/parser": "^0.6.2", "@types/d3": "^7.4.3", "cytoscape": "^3.29.3", "cytoscape-cose-bilkent": "^4.1.0", "cytoscape-fcose": "^2.2.0", "d3": "^7.9.0", "d3-sankey": "^0.12.3", "dagre-d3-es": "7.0.11", "dayjs": "^1.11.18", "dompurify": "^3.2.5", "katex": "^0.16.22", "khroma": "^2.1.0", "lodash-es": "^4.17.21", "marked": "^16.2.1", "roughjs": "^4.6.6", "stylis": "^4.3.6", "ts-dedent": "^2.2.0", "uuid": "^11.1.0" } }, "sha512-ZudVx73BwrMJfCFmSSJT84y6u5brEoV8DOItdHomNLz32uBjNrelm7mg95X7g+C6UoQH/W6mBLGDEDv73JdxBg=="], + "mermaid": ["mermaid@11.12.1", "", { "dependencies": { "@braintree/sanitize-url": "^7.1.1", "@iconify/utils": "^3.0.1", "@mermaid-js/parser": "^0.6.3", "@types/d3": "^7.4.3", "cytoscape": "^3.29.3", "cytoscape-cose-bilkent": "^4.1.0", "cytoscape-fcose": "^2.2.0", "d3": "^7.9.0", "d3-sankey": "^0.12.3", "dagre-d3-es": "7.0.13", "dayjs": "^1.11.18", "dompurify": "^3.2.5", "katex": "^0.16.22", "khroma": "^2.1.0", "lodash-es": "^4.17.21", "marked": "^16.2.1", "roughjs": "^4.6.6", "stylis": "^4.3.6", "ts-dedent": "^2.2.0", "uuid": "^11.1.0" } }, "sha512-UlIZrRariB11TY1RtTgUWp65tphtBv4CSq7vyS2ZZ2TgoMjs2nloq+wFqxiwcxlhHUvs7DPGgMjs2aeQxz5h9g=="], "metro": ["metro@0.83.3", "", { "dependencies": { "@babel/code-frame": "^7.24.7", "@babel/core": "^7.25.2", "@babel/generator": "^7.25.0", "@babel/parser": "^7.25.3", "@babel/template": "^7.25.0", "@babel/traverse": "^7.25.3", "@babel/types": "^7.25.2", "accepts": "^1.3.7", "chalk": "^4.0.0", "ci-info": "^2.0.0", "connect": "^3.6.5", "debug": "^4.4.0", "error-stack-parser": "^2.0.6", "flow-enums-runtime": "^0.0.6", "graceful-fs": "^4.2.4", "hermes-parser": "0.32.0", "image-size": "^1.0.2", "invariant": "^2.2.4", "jest-worker": "^29.7.0", "jsc-safe-url": "^0.2.2", "lodash.throttle": "^4.1.1", "metro-babel-transformer": "0.83.3", "metro-cache": "0.83.3", "metro-cache-key": "0.83.3", "metro-config": "0.83.3", "metro-core": "0.83.3", "metro-file-map": "0.83.3", "metro-resolver": "0.83.3", "metro-runtime": "0.83.3", "metro-source-map": "0.83.3", "metro-symbolicate": "0.83.3", "metro-transform-plugins": "0.83.3", "metro-transform-worker": "0.83.3", "mime-types": "^2.1.27", "nullthrows": "^1.1.1", "serialize-error": "^2.1.0", "source-map": "^0.5.6", "throat": "^5.0.0", "ws": "^7.5.10", "yargs": "^17.6.2" }, "bin": { "metro": "src/cli.js" } }, "sha512-+rP+/GieOzkt97hSJ0MrPOuAH/jpaS21ZDvL9DJ35QYRDlQcwzcvUlGUf79AnQxq/2NPiS/AULhhM4TKutIt8Q=="], @@ -2641,9 +2641,9 @@ "pkg-types": ["pkg-types@2.3.0", "", { "dependencies": { "confbox": "^0.2.2", "exsolve": "^1.0.7", "pathe": "^2.0.3" } }, "sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig=="], - "playwright": ["playwright@1.56.0", "", { "dependencies": { "playwright-core": "1.56.0" }, "optionalDependencies": { "fsevents": "2.3.2" }, "bin": { "playwright": "cli.js" } }, "sha512-X5Q1b8lOdWIE4KAoHpW3SE8HvUB+ZZsUoN64ZhjnN8dOb1UpujxBtENGiZFE+9F/yhzJwYa+ca3u43FeLbboHA=="], + "playwright": ["playwright@1.56.1", "", { "dependencies": { "playwright-core": "1.56.1" }, "optionalDependencies": { "fsevents": "2.3.2" }, "bin": { "playwright": "cli.js" } }, "sha512-aFi5B0WovBHTEvpM3DzXTUaeN6eN0qWnTkKx4NQaH4Wvcmc153PdaY2UBdSYKaGYw+UyWXSVyxDUg5DoPEttjw=="], - "playwright-core": ["playwright-core@1.56.0", "", { "bin": { "playwright-core": "cli.js" } }, "sha512-1SXl7pMfemAMSDn5rkPeZljxOCYAmQnYLBTExuh6E8USHXGSX3dx6lYZN/xPpTz1vimXmPA9CDnILvmJaB8aSQ=="], + "playwright-core": ["playwright-core@1.56.1", "", { "bin": { "playwright-core": "cli.js" } }, "sha512-hutraynyn31F+Bifme+Ps9Vq59hKuUCz7H1kDOcBs+2oGguKkWTU50bBWrtz34OUWmIwpBTWDxaRPXrIXkgvmQ=="], "plist": ["plist@3.1.0", "", { "dependencies": { "@xmldom/xmldom": "^0.8.8", "base64-js": "^1.5.1", "xmlbuilder": "^15.1.1" } }, "sha512-uysumyrvkUX0rX/dEVqt8gC3sTBzd4zoWfLeS29nb53imdaXVvLINYXTI2GNqzaMuvacNx4uJQ8+b3zXR0pkgQ=="], @@ -3027,11 +3027,11 @@ "supports-preserve-symlinks-flag": ["supports-preserve-symlinks-flag@1.0.0", "", {}, "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w=="], - "svelte": ["svelte@5.41.1", "", { "dependencies": { "@jridgewell/remapping": "^2.3.4", "@jridgewell/sourcemap-codec": "^1.5.0", "@sveltejs/acorn-typescript": "^1.0.5", "@types/estree": "^1.0.5", "acorn": "^8.12.1", "aria-query": "^5.3.1", "axobject-query": "^4.1.0", "clsx": "^2.1.1", "esm-env": "^1.2.1", "esrap": "^2.1.0", "is-reference": "^3.0.3", "locate-character": "^3.0.0", "magic-string": "^0.30.11", "zimmerframe": "^1.1.2" } }, "sha512-0a/huwc8e2es+7KFi70esqsReRfRbrT8h1cJSY/+z1lF0yKM6TT+//HYu28Yxstr50H7ifaqZRDGd0KuKDxP7w=="], + "svelte": ["svelte@5.43.14", "", { "dependencies": { "@jridgewell/remapping": "^2.3.4", "@jridgewell/sourcemap-codec": "^1.5.0", "@sveltejs/acorn-typescript": "^1.0.5", "@types/estree": "^1.0.5", "acorn": "^8.12.1", "aria-query": "^5.3.1", "axobject-query": "^4.1.0", "clsx": "^2.1.1", "esm-env": "^1.2.1", "esrap": "^2.1.0", "is-reference": "^3.0.3", "locate-character": "^3.0.0", "magic-string": "^0.30.11", "zimmerframe": "^1.1.2" } }, "sha512-pHeUrp1A5S6RGaXhJB7PtYjL1VVjbVrJ2EfuAoPu9/1LeoMaJa/pcdCsCSb0gS4eUHAHnhCbUDxORZyvGK6kOQ=="], "svelte-check": ["svelte-check@4.3.3", "", { "dependencies": { "@jridgewell/trace-mapping": "^0.3.25", "chokidar": "^4.0.1", "fdir": "^6.2.0", "picocolors": "^1.0.0", "sade": "^1.7.4" }, "peerDependencies": { "svelte": "^4.0.0 || ^5.0.0-next.0", "typescript": ">=5.0.0" }, "bin": { "svelte-check": "bin/svelte-check" } }, "sha512-RYP0bEwenDXzfv0P1sKAwjZSlaRyqBn0Fz1TVni58lqyEiqgwztTpmodJrGzP6ZT2aHl4MbTvWP6gbmQ3FOnBg=="], - "svelte-eslint-parser": ["svelte-eslint-parser@1.3.3", "", { "dependencies": { "eslint-scope": "^8.2.0", "eslint-visitor-keys": "^4.0.0", "espree": "^10.0.0", "postcss": "^8.4.49", "postcss-scss": "^4.0.9", "postcss-selector-parser": "^7.0.0" }, "peerDependencies": { "svelte": "^3.37.0 || ^4.0.0 || ^5.0.0" }, "optionalPeers": ["svelte"] }, "sha512-oTrDR8Z7Wnguut7QH3YKh7JR19xv1seB/bz4dxU5J/86eJtZOU4eh0/jZq4dy6tAlz/KROxnkRQspv5ZEt7t+Q=="], + "svelte-eslint-parser": ["svelte-eslint-parser@1.4.0", "", { "dependencies": { "eslint-scope": "^8.2.0", "eslint-visitor-keys": "^4.0.0", "espree": "^10.0.0", "postcss": "^8.4.49", "postcss-scss": "^4.0.9", "postcss-selector-parser": "^7.0.0" }, "peerDependencies": { "svelte": "^3.37.0 || ^4.0.0 || ^5.0.0" }, "optionalPeers": ["svelte"] }, "sha512-fjPzOfipR5S7gQ/JvI9r2H8y9gMGXO3JtmrylHLLyahEMquXI0lrebcjT+9/hNgDej0H7abTyox5HpHmW1PSWA=="], "svelte2tsx": ["svelte2tsx@0.7.45", "", { "dependencies": { "dedent-js": "^1.0.1", "scule": "^1.3.0" }, "peerDependencies": { "svelte": "^3.55 || ^4.0.0-next.0 || ^4.0 || ^5.0.0-next.0", "typescript": "^4.9.4 || ^5.0.0" } }, "sha512-cSci+mYGygYBHIZLHlm/jYlEc1acjAHqaQaDFHdEBpUueM9kSTnPpvPtSl5VkJOU1qSJ7h1K+6F/LIUYiqC8VA=="], @@ -3125,19 +3125,19 @@ "tunnel-agent": ["tunnel-agent@0.6.0", "", { "dependencies": { "safe-buffer": "^5.0.1" } }, "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w=="], - "turbo": ["turbo@2.5.8", "", { "optionalDependencies": { "turbo-darwin-64": "2.5.8", "turbo-darwin-arm64": "2.5.8", "turbo-linux-64": "2.5.8", "turbo-linux-arm64": "2.5.8", "turbo-windows-64": "2.5.8", "turbo-windows-arm64": "2.5.8" }, "bin": { "turbo": "bin/turbo" } }, "sha512-5c9Fdsr9qfpT3hA0EyYSFRZj1dVVsb6KIWubA9JBYZ/9ZEAijgUEae0BBR/Xl/wekt4w65/lYLTFaP3JmwSO8w=="], + "turbo": ["turbo@2.6.1", "", { "optionalDependencies": { "turbo-darwin-64": "2.6.1", "turbo-darwin-arm64": "2.6.1", "turbo-linux-64": "2.6.1", "turbo-linux-arm64": "2.6.1", "turbo-windows-64": "2.6.1", "turbo-windows-arm64": "2.6.1" }, "bin": { "turbo": "bin/turbo" } }, "sha512-qBwXXuDT3rA53kbNafGbT5r++BrhRgx3sAo0cHoDAeG9g1ItTmUMgltz3Hy7Hazy1ODqNpR+C7QwqL6DYB52yA=="], - "turbo-darwin-64": ["turbo-darwin-64@2.5.8", "", { "os": "darwin", "cpu": "x64" }, "sha512-Dh5bCACiHO8rUXZLpKw+m3FiHtAp2CkanSyJre+SInEvEr5kIxjGvCK/8MFX8SFRjQuhjtvpIvYYZJB4AGCxNQ=="], + "turbo-darwin-64": ["turbo-darwin-64@2.6.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-Dm0HwhyZF4J0uLqkhUyCVJvKM9Rw7M03v3J9A7drHDQW0qAbIGBrUijQ8g4Q9Cciw/BXRRd8Uzkc3oue+qn+ZQ=="], - "turbo-darwin-arm64": ["turbo-darwin-arm64@2.5.8", "", { "os": "darwin", "cpu": "arm64" }, "sha512-f1H/tQC9px7+hmXn6Kx/w8Jd/FneIUnvLlcI/7RGHunxfOkKJKvsoiNzySkoHQ8uq1pJnhJ0xNGTlYM48ZaJOQ=="], + "turbo-darwin-arm64": ["turbo-darwin-arm64@2.6.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-U0PIPTPyxdLsrC3jN7jaJUwgzX5sVUBsKLO7+6AL+OASaa1NbT1pPdiZoTkblBAALLP76FM0LlnsVQOnmjYhyw=="], - "turbo-linux-64": ["turbo-linux-64@2.5.8", "", { "os": "linux", "cpu": "x64" }, "sha512-hMyvc7w7yadBlZBGl/bnR6O+dJTx3XkTeyTTH4zEjERO6ChEs0SrN8jTFj1lueNXKIHh1SnALmy6VctKMGnWfw=="], + "turbo-linux-64": ["turbo-linux-64@2.6.1", "", { "os": "linux", "cpu": "x64" }, "sha512-eM1uLWgzv89bxlK29qwQEr9xYWBhmO/EGiH22UGfq+uXr+QW1OvNKKMogSN65Ry8lElMH4LZh0aX2DEc7eC0Mw=="], - "turbo-linux-arm64": ["turbo-linux-arm64@2.5.8", "", { "os": "linux", "cpu": "arm64" }, "sha512-LQELGa7bAqV2f+3rTMRPnj5G/OHAe2U+0N9BwsZvfMvHSUbsQ3bBMWdSQaYNicok7wOZcHjz2TkESn1hYK6xIQ=="], + "turbo-linux-arm64": ["turbo-linux-arm64@2.6.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-MFFh7AxAQAycXKuZDrbeutfWM5Ep0CEZ9u7zs4Hn2FvOViTCzIfEhmuJou3/a5+q5VX1zTxQrKGy+4Lf5cdpsA=="], - "turbo-windows-64": ["turbo-windows-64@2.5.8", "", { "os": "win32", "cpu": "x64" }, "sha512-3YdcaW34TrN1AWwqgYL9gUqmZsMT4T7g8Y5Azz+uwwEJW+4sgcJkIi9pYFyU4ZBSjBvkfuPZkGgfStir5BBDJQ=="], + "turbo-windows-64": ["turbo-windows-64@2.6.1", "", { "os": "win32", "cpu": "x64" }, "sha512-buq7/VAN7KOjMYi4tSZT5m+jpqyhbRU2EUTTvp6V0Ii8dAkY2tAAjQN1q5q2ByflYWKecbQNTqxmVploE0LVwQ=="], - "turbo-windows-arm64": ["turbo-windows-arm64@2.5.8", "", { "os": "win32", "cpu": "arm64" }, "sha512-eFC5XzLmgXJfnAK3UMTmVECCwuBcORrWdewoiXBnUm934DY6QN8YowC/srhNnROMpaKaqNeRpoB5FxCww3eteQ=="], + "turbo-windows-arm64": ["turbo-windows-arm64@2.6.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-7w+AD5vJp3R+FB0YOj1YJcNcOOvBior7bcHTodqp90S3x3bLgpr7tE6xOea1e8JkP7GK6ciKVUpQvV7psiwU5Q=="], "type": ["type@2.7.3", "", {}, "sha512-8j+1QmAbPvLZow5Qpi6NCaN8FB60p/6x8/vfNqOk/hC+HuvFZhL4+WfekuhQLiqFZXOgQdrs3B+XxEmCc6b3FQ=="], @@ -3165,7 +3165,7 @@ "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="], - "typescript-eslint": ["typescript-eslint@8.46.0", "", { "dependencies": { "@typescript-eslint/eslint-plugin": "8.46.0", "@typescript-eslint/parser": "8.46.0", "@typescript-eslint/typescript-estree": "8.46.0", "@typescript-eslint/utils": "8.46.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-6+ZrB6y2bT2DX3K+Qd9vn7OFOJR+xSLDj+Aw/N3zBwUt27uTw2sw2TE2+UcY1RiyBZkaGbTkVg9SSdPNUG6aUw=="], + "typescript-eslint": ["typescript-eslint@8.47.0", "", { "dependencies": { "@typescript-eslint/eslint-plugin": "8.47.0", "@typescript-eslint/parser": "8.47.0", "@typescript-eslint/typescript-estree": "8.47.0", "@typescript-eslint/utils": "8.47.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-Lwe8i2XQ3WoMjua/r1PHrCTpkubPYJCAfOurtn+mtTzqB6jNd+14n9UN1bJ4s3F49x9ixAm0FLflB/JzQ57M8Q=="], "typescript-lru-cache": ["typescript-lru-cache@2.0.0", "", {}, "sha512-Jp57Qyy8wXeMkdNuZiglE6v2Cypg13eDA1chHwDG6kq51X7gk4K7P7HaDdzZKCxkegXkVHNcPD0n5aW6OZH3aA=="], @@ -3455,6 +3455,16 @@ "@mermaid-js/mermaid-mindmap/@braintree/sanitize-url": ["@braintree/sanitize-url@6.0.4", "", {}, "sha512-s3jaWicZd0pkP0jf5ysyHUI/RE7MHos6qlToFcGWXVp+ykHOy77OUMrfbgJ9it2C5bow7OIQwYYaHjk9XlBQ2A=="], + "@nostr-dev-kit/svelte/@nostr-dev-kit/sessions": ["@nostr-dev-kit/sessions@0.6.3", "", { "dependencies": { "@nostr-dev-kit/ndk": "^2.17.11", "zustand": "^5.0.3" } }, "sha512-zRI+ReQPbBJcVMsTxQHwiz3KS4p1ms+c0vyxkWlrPxEpEjwv4oAhD+l9lsm1KXjzfMU4Iz3f7GB43wtCvVnd4A=="], + + "@nostr-dev-kit/svelte/@nostr-dev-kit/wallet": ["@nostr-dev-kit/wallet@0.8.10", "", { "dependencies": { "@nostr-dev-kit/ndk": "^2.18.0", "@nostr-dev-kit/sync": "^0.3.6", "debug": "^4.3.7", "light-bolt11-decoder": "^3.2.0", "tseep": "^1.3.1", "typescript": "^5.9.3", "webln": "^0.3.2", "zustand": "^5.0.3" }, "peerDependencies": { "@cashu/cashu-ts": "^2.1", "@cashu/crypto": "*" } }, "sha512-TZGQxPZjaN3pmAvsZ6FypdZrasVlIoYuW3UkkYOAyFQnuzXdQAa37FqeA+i6iP788EXFFrUXZssPtR+xcpJEMg=="], + + "@nostr-dev-kit/svelte/@types/node": ["@types/node@24.10.1", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ=="], + + "@nostr-dev-kit/svelte/vite": ["vite@7.2.4", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.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", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-NL8jTlbo0Tn4dUEXEsUg8KeyG/Lkmc4Fnzb8JXN/Ykm9G4HNImjtABMJgkQoVjOBN/j2WAwDTRytdqJbZsah7w=="], + + "@nostr-dev-kit/sync/vite": ["vite@7.2.4", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.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", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-NL8jTlbo0Tn4dUEXEsUg8KeyG/Lkmc4Fnzb8JXN/Ykm9G4HNImjtABMJgkQoVjOBN/j2WAwDTRytdqJbZsah7w=="], + "@react-native/babel-plugin-codegen/@react-native/codegen": ["@react-native/codegen@0.76.9", "", { "dependencies": { "@babel/parser": "^7.25.3", "glob": "^7.1.1", "hermes-parser": "0.23.1", "invariant": "^2.2.4", "jscodeshift": "^0.14.0", "mkdirp": "^0.5.1", "nullthrows": "^1.1.1", "yargs": "^17.6.2" }, "peerDependencies": { "@babel/preset-env": "^7.1.6" } }, "sha512-AzlCHMTKrAVC2709V4ZGtBXmGVtWTpWm3Ruv5vXcd3/anH4mGucfJ4rjbWKdaYQJMpXa3ytGomQrsIsT/s8kgA=="], "@react-native/babel-preset/babel-plugin-syntax-hermes-parser": ["babel-plugin-syntax-hermes-parser@0.25.1", "", { "dependencies": { "hermes-parser": "0.25.1" } }, "sha512-IVNpGzboFLfXZUAwkLFcI/bnqVbwky0jP3eBno4HKtqvQJAHBLdgxiG6lQ4to0+Q/YCN3PO0od5NZwIKyY4REQ=="], @@ -3881,6 +3891,14 @@ "@jest/types/chalk/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], + "@nostr-dev-kit/svelte/@nostr-dev-kit/sessions/@nostr-dev-kit/ndk": ["@nostr-dev-kit/ndk@2.18.1", "", { "dependencies": { "@codesandbox/sandpack-client": "^2.19.8", "@noble/curves": "^1.6.0", "@noble/hashes": "^1.5.0", "@noble/secp256k1": "^2.1.0", "@scure/base": "^1.1.9", "debug": "^4.3.7", "light-bolt11-decoder": "^3.2.0", "shiki": "^3.13.0", "tseep": "^1.3.1", "typescript-lru-cache": "^2.0.0" }, "peerDependencies": { "nostr-tools": "^2.17.0" } }, "sha512-LTXXheGfmyN1y8x+8v/Dmkx8YX7LqaoVk0DTSaigETB5RZsxw7dLBKK++kZd4DVIxtj0tRfmSOsTr1E+M4653Q=="], + + "@nostr-dev-kit/svelte/@nostr-dev-kit/wallet/@nostr-dev-kit/ndk": ["@nostr-dev-kit/ndk@2.18.1", "", { "dependencies": { "@codesandbox/sandpack-client": "^2.19.8", "@noble/curves": "^1.6.0", "@noble/hashes": "^1.5.0", "@noble/secp256k1": "^2.1.0", "@scure/base": "^1.1.9", "debug": "^4.3.7", "light-bolt11-decoder": "^3.2.0", "shiki": "^3.13.0", "tseep": "^1.3.1", "typescript-lru-cache": "^2.0.0" }, "peerDependencies": { "nostr-tools": "^2.17.0" } }, "sha512-LTXXheGfmyN1y8x+8v/Dmkx8YX7LqaoVk0DTSaigETB5RZsxw7dLBKK++kZd4DVIxtj0tRfmSOsTr1E+M4653Q=="], + + "@nostr-dev-kit/svelte/@nostr-dev-kit/wallet/@nostr-dev-kit/sync": ["@nostr-dev-kit/sync@0.3.6", "", { "dependencies": { "@nostr-dev-kit/ndk": "^2.18.0", "nostr-tools": "^2.17.0", "tseep": "^1.3.1" } }, "sha512-s2jKAYU1vy97hsBiYIv6fjtLkRtLRliYMyR7t1saXQim4Nbk2ozzErx+ptKkZxjX96AF6LOyAc/BHg07MTmfEg=="], + + "@nostr-dev-kit/svelte/@types/node/undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="], + "@react-native/babel-plugin-codegen/@react-native/codegen/glob": ["glob@7.2.3", "", { "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } }, "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q=="], "@react-native/babel-plugin-codegen/@react-native/codegen/hermes-parser": ["hermes-parser@0.23.1", "", { "dependencies": { "hermes-estree": "0.23.1" } }, "sha512-oxl5h2DkFW83hT4DAUJorpah8ou4yvmweUzLJmmr6YV2cezduCdlil1AvU/a/xSsAFo4WUcNA4GoV5Bvq6JffA=="], diff --git a/package.json b/package.json index 34478e4db..a71fcb1f7 100755 --- a/package.json +++ b/package.json @@ -2,15 +2,15 @@ "name": "@nostr-dev-kit/monorepo", "dependencies": { "@biomejs/biome": "^2.2.6", - "esbuild": "^0.25.11", - "mermaid": "^11.6.0", + "esbuild": "^0.25.12", + "mermaid": "^11.12.1", "vitepress": "^1.6.3", "vitepress-plugin-mermaid": "^2.0.17" }, "devDependencies": { - "svelte": "^5.41.0", + "svelte": "^5.43.14", "syncpack": "^13.0.4", - "turbo": "^2.5.3" + "turbo": "^2.6.1" }, "engines": { "node": ">=22.0" From 0498cd68ad7da6c2ebc6be6b22db51bb20691f00 Mon Sep 17 00:00:00 2001 From: digitalbase Date: Thu, 20 Nov 2025 21:26:16 +0100 Subject: [PATCH 071/139] Add quick start to getting started docs --- core/docs/getting-started/usage.md | 34 +++++---------- core/docs/snippets/initialise.ts | 7 ++++ .../snippets/quick-start-with-guardrails.ts | 41 +++++++++++++++++++ 3 files changed, 59 insertions(+), 23 deletions(-) create mode 100644 core/docs/snippets/initialise.ts create mode 100644 core/docs/snippets/quick-start-with-guardrails.ts diff --git a/core/docs/getting-started/usage.md b/core/docs/getting-started/usage.md index 9e254f192..6e9d367c1 100644 --- a/core/docs/getting-started/usage.md +++ b/core/docs/getting-started/usage.md @@ -1,35 +1,23 @@ # Usage -## Instantiate an NDK instance +## Quick Start -You can pass an object with several options to a newly created instance of NDK. +A simple example of how to use NDK in a Node.js application: -- `explicitRelayUrls` โ€“ an array of relay URLs. -- `signer` - an instance of a [signer](/core/docs/fundamentals/signers.html). -- `cacheAdapter` - an instance of a [cache adapter](#caching) -- `debug` - Debug instance to use for logging. Defaults to `debug("ndk")`. +<<< @/core/docs/snippets/quick-start-with-guardrails.ts -```ts -// Import the package -import NDK from "@nostr-dev-kit/ndk"; +## NDK Usage -// Create a new NDK instance with explicit relays -const ndk = new NDK({ - explicitRelayUrls: ["wss://a.relay", "wss://another.relay"], -}); -``` +### Instantiate NDK -If the signer implements the `getRelays()` method, NDK will use the relays returned by that method as the explicit -relays. +You can pass an object with several options to a newly created instance of NDK. -```ts -// Import the package -import NDK, { NDKNip07Signer } from "@nostr-dev-kit/ndk"; +- `explicitRelayUrls` โ€“ an array of relay URLs. +- `signer` - an instance of a signer ([signer documentation](/core/docs/fundamentals/signers.md)). +- `cacheAdapter` - an instance of a cache adapter. +- `debug` - debugger instance ([debugging documentation](/core/docs/getting-started/debugging.md)). -// Create a new NDK instance with just a signer (provided the signer implements the getRelays() method) -const nip07signer = new NDKNip07Signer(); -const ndk = new NDK({ signer: nip07signer }); -``` +<<< @/core/docs/snippets/initialise.ts Note: In normal client use, it's best practice to instantiate NDK as a singleton class. [See more below](#architecture-decisions--suggestions). diff --git a/core/docs/snippets/initialise.ts b/core/docs/snippets/initialise.ts new file mode 100644 index 000000000..2f40ff066 --- /dev/null +++ b/core/docs/snippets/initialise.ts @@ -0,0 +1,7 @@ +// Import the package +import NDK from "@nostr-dev-kit/ndk"; + +// Create a new NDK instance with explicit relays +const ndk = new NDK({ + explicitRelayUrls: ["wss://a.relay", "wss://another.relay"], +}); diff --git a/core/docs/snippets/quick-start-with-guardrails.ts b/core/docs/snippets/quick-start-with-guardrails.ts new file mode 100644 index 000000000..57c82095c --- /dev/null +++ b/core/docs/snippets/quick-start-with-guardrails.ts @@ -0,0 +1,41 @@ +import NDK, {NDKEvent, NDKPrivateKeySigner} from '@nostr-dev-kit/ndk'; + +async function main() { + const signer = NDKPrivateKeySigner.generate(); + const ndk = new NDK({ + explicitRelayUrls: ['wss://relay.primal.net'], + signer, + + // โš ๏ธ STRONGLY RECOMMENDED: Enable during development + // Catches common mistakes before they cause silent failures + aiGuardrails: true + }); + + // Connect to relays + await ndk.connect(); + + // Publish a simple text note + const event = new NDKEvent(ndk, { + kind: 1, + content: 'Hello Nostr via NDK!', + }) + await event.sign(); + event.publish(); + + // subscribe to all event interactions + ndk.subscribe(event.filter(), {closeOnEose: false}, { + onEvent: (replyEvent: NDKEvent) => console.log(replyEvent.author.npub, "interacted with our hello world with a kind", replyEvent.kind); + }) + + // Subscribe to incoming text notes + const subscription = ndk.subscribe( + {kinds: [1]}, + {closeOnEose: true}, + { + onEvent: (evt) => console.log('Received event:', evt), + onEose: () => console.log('End of stream'), + } + ); +} + +main().catch(console.error); \ No newline at end of file From d9c7a2f34f050c6e4d12898cf8e9f3ccb3202048 Mon Sep 17 00:00:00 2001 From: digitalbase Date: Thu, 20 Nov 2025 21:26:28 +0100 Subject: [PATCH 072/139] Move signer relays to signer docs --- core/docs/fundamentals/signers.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/core/docs/fundamentals/signers.md b/core/docs/fundamentals/signers.md index 88198bfdd..05c2bb11e 100644 --- a/core/docs/fundamentals/signers.md +++ b/core/docs/fundamentals/signers.md @@ -81,6 +81,20 @@ event.content = "Hello, world!"; await event.sign(); // [!code focus] ``` +## Signer Relays + +If the [signer](/core/docs/fundamentals/signers.md) implements the `getRelays()` method, +NDK will use the relays returned by that method as the explicit relays. + +```ts +// Import the package +import NDK, {NDKNip07Signer} from "@nostr-dev-kit/ndk"; + +// Create a new NDK instance with just a signer (provided the signer implements the getRelays() method) +const nip07signer = new NDKNip07Signer(); +const ndk = new NDK({signer: nip07signer}); +``` + ## Combining signers You can specify the use of a different signer to sign with different keys. From f8d279edd8ee599d08af713167c753fc88d8402d Mon Sep 17 00:00:00 2001 From: digitalbase Date: Thu, 20 Nov 2025 21:39:25 +0100 Subject: [PATCH 073/139] Fix guardrails/quick-start snippet --- .../snippets/quick-start-with-guardrails.ts | 29 +++++++++++-------- 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/core/docs/snippets/quick-start-with-guardrails.ts b/core/docs/snippets/quick-start-with-guardrails.ts index 57c82095c..28e571607 100644 --- a/core/docs/snippets/quick-start-with-guardrails.ts +++ b/core/docs/snippets/quick-start-with-guardrails.ts @@ -1,14 +1,14 @@ -import NDK, {NDKEvent, NDKPrivateKeySigner} from '@nostr-dev-kit/ndk'; +import NDK, {NDKEvent, NDKPrivateKeySigner} from "@nostr-dev-kit/ndk"; async function main() { const signer = NDKPrivateKeySigner.generate(); const ndk = new NDK({ - explicitRelayUrls: ['wss://relay.primal.net'], + explicitRelayUrls: ["wss://relay.primal.net"], signer, // โš ๏ธ STRONGLY RECOMMENDED: Enable during development // Catches common mistakes before they cause silent failures - aiGuardrails: true + aiGuardrails: true, }); // Connect to relays @@ -17,25 +17,30 @@ async function main() { // Publish a simple text note const event = new NDKEvent(ndk, { kind: 1, - content: 'Hello Nostr via NDK!', - }) + content: "Hello Nostr via NDK!", + }); await event.sign(); event.publish(); // subscribe to all event interactions - ndk.subscribe(event.filter(), {closeOnEose: false}, { - onEvent: (replyEvent: NDKEvent) => console.log(replyEvent.author.npub, "interacted with our hello world with a kind", replyEvent.kind); - }) + ndk.subscribe( + event.filter(), + {closeOnEose: false}, + { + onEvent: (replyEvent: NDKEvent) => + console.log(replyEvent.author.npub, "interacted with our hello world with a kind", replyEvent.kind), + }, + ); // Subscribe to incoming text notes const subscription = ndk.subscribe( {kinds: [1]}, {closeOnEose: true}, { - onEvent: (evt) => console.log('Received event:', evt), - onEose: () => console.log('End of stream'), - } + onEvent: (evt) => console.log("Received event:", evt), + onEose: () => console.log("End of stream"), + }, ); } -main().catch(console.error); \ No newline at end of file +main().catch(console.error); From d9cb245416f4a9f2d4f27a2235bdb520c8fa5c71 Mon Sep 17 00:00:00 2001 From: digitalbase Date: Thu, 20 Nov 2025 21:39:40 +0100 Subject: [PATCH 074/139] Move usage higher --- .vitepress/config.mts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.vitepress/config.mts b/.vitepress/config.mts index dacd6e2f4..ee38a0553 100644 --- a/.vitepress/config.mts +++ b/.vitepress/config.mts @@ -38,8 +38,8 @@ export default defineConfig({ text: "Getting Started", items: [ {text: "Installing โœ…", link: "/core/docs/getting-started/installing"}, + {text: "Usage โœ…", link: "/core/docs/getting-started/usage"}, {text: "Debugging โœ…", link: "/core/docs/getting-started/debugging"}, - {text: "Usage โŒ", link: "/core/docs/getting-started/usage"}, ], }, { From 6fcfe4cda070504997ced9ee901da3d578b88c82 Mon Sep 17 00:00:00 2001 From: digitalbase Date: Thu, 20 Nov 2025 21:39:48 +0100 Subject: [PATCH 075/139] Clean up connecting and nip-19 --- core/docs/getting-started/usage.md | 50 +++++++----------------- core/docs/snippets/connecting.ts | 9 +++++ core/docs/snippets/nip-19-identifiers.ts | 19 +++++++++ 3 files changed, 42 insertions(+), 36 deletions(-) create mode 100644 core/docs/snippets/connecting.ts create mode 100644 core/docs/snippets/nip-19-identifiers.ts diff --git a/core/docs/getting-started/usage.md b/core/docs/getting-started/usage.md index 6e9d367c1..473c8e5b2 100644 --- a/core/docs/getting-started/usage.md +++ b/core/docs/getting-started/usage.md @@ -22,25 +22,25 @@ You can pass an object with several options to a newly created instance of NDK. Note: In normal client use, it's best practice to instantiate NDK as a singleton class. [See more below](#architecture-decisions--suggestions). -## Connecting +### Connecting -After you've instatiated NDK, you need to tell it to connect before you'll be able to interact with any relays. +After you've instantiated NDK, you need to tell it to connect before you'll be able to interact with any relays. -```ts -// Import the package -import NDK from "@nostr-dev-kit/ndk"; +<<< @/core/docs/snippets/connecting.ts -// Create a new NDK instance with explicit relays -const ndk = new NDK({ - explicitRelayUrls: ["wss://a.relay", "wss://another.relay"], -}); -// Now connect to specified relays -await ndk.connect(); -``` +### Using NIP-19 Identifiers + +NDK re-exports [NIP-19](https://github.com/nostr-protocol/nips/blob/master/19.md) from the +[nostr-tools library](https://github.com/nbd-wtf/nostr-tools) which provides different utilities for encoding and +decoding Nostr identifiers: -## Creating Users +<<< @/core/docs/snippets/nip-19-identifiers.ts -NDK provides flexible ways to fetch user objects, including support for NIP-19 encoded identifiers and NIP-05 addresses: +### Managing Users + +NDK provides flexible ways to fetch user objects, including support for +[NIP-19](https://github.com/nostr-protocol/nips/blob/master/19.md) encoded identifiers +and [NIP-05](https://github.com/nostr-protocol/nips/blob/master/05.md) addresses: ```typescript // From hex pubkey @@ -63,28 +63,6 @@ const user6 = await ndk.fetchUser("deadbeef..."); // Assumes hex pubkey Note: `fetchUser` is async and returns a Promise. For NIP-05 lookups, it may return `undefined` if the address cannot be resolved. -## Working with NIP-19 Identifiers - -NDK re-exports NIP-19 utilities for encoding and decoding Nostr identifiers: - -```typescript -import { nip19 } from '@nostr-dev-kit/ndk'; - -// Encode a pubkey as npub -const npub = nip19.npubEncode("3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d"); - -// Decode any NIP-19 identifier -const decoded = nip19.decode("npub1..."); -console.log(decoded.type); // "npub" -console.log(decoded.data); // hex pubkey - -// Encode events -const nevent = nip19.neventEncode({ - id: eventId, - relays: ["wss://relay.example.com"], - author: authorPubkey -}); -``` See the [NIP-19 tutorial](/tutorial/nip19.html) for comprehensive examples and use cases. diff --git a/core/docs/snippets/connecting.ts b/core/docs/snippets/connecting.ts new file mode 100644 index 000000000..b3f0fdbe1 --- /dev/null +++ b/core/docs/snippets/connecting.ts @@ -0,0 +1,9 @@ +// Import the package +import NDK from "@nostr-dev-kit/ndk"; + +// Create a new NDK instance with explicit relays +const ndk = new NDK({ + explicitRelayUrls: ["wss://a.relay", "wss://another.relay"], +}); +// Now connect to specified relays +await ndk.connect(); diff --git a/core/docs/snippets/nip-19-identifiers.ts b/core/docs/snippets/nip-19-identifiers.ts new file mode 100644 index 000000000..9cf4e3485 --- /dev/null +++ b/core/docs/snippets/nip-19-identifiers.ts @@ -0,0 +1,19 @@ +import { nip19 } from "@nostr-dev-kit/ndk"; + +// Encode a pubkey as npub +const npub = nip19.npubEncode("3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d"); +console.log(npub); // + +// Decode any NIP-19 identifier +const decoded = nip19.decode("npub1..."); +console.log(decoded.type); // "npub" +console.log(decoded.data); // hex pubkey + +// Encode events +const nevent = nip19.neventEncode({ + id: "574033c986bea1d7493738b46fec1bb98dd6a826391d6aa893137e89790027ec", + relays: ["wss://relay.example.com"], + author: "3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d", +}); + +console.log(nevent); From b4a0f4606f7f10242c2f9c0d56d8c0537b806ae6 Mon Sep 17 00:00:00 2001 From: digitalbase Date: Thu, 20 Nov 2025 21:49:48 +0100 Subject: [PATCH 076/139] Move architecture suggestions to backup doc and remove from getting-started --- .../archiecture_suggestions.md | 8 ++ core/docs/getting-started/usage.md | 97 ++----------------- core/docs/snippets/managing-users.ts | 21 ++++ react/docs/hooks_old_docs_from_usage.md | 57 +++++++++++ 4 files changed, 95 insertions(+), 88 deletions(-) create mode 100644 core/docs/getting-started/archiecture_suggestions.md create mode 100644 core/docs/snippets/managing-users.ts create mode 100644 react/docs/hooks_old_docs_from_usage.md diff --git a/core/docs/getting-started/archiecture_suggestions.md b/core/docs/getting-started/archiecture_suggestions.md new file mode 100644 index 000000000..869227baa --- /dev/null +++ b/core/docs/getting-started/archiecture_suggestions.md @@ -0,0 +1,8 @@ +## Architecture decisions & suggestions + +- Users of NDK should instantiate a single NDK instance. +- That instance tracks state with all relays connected, explicit and otherwise. +- All relays are tracked in a single pool that handles connection errors/reconnection logic. +- RelaySets are assembled ad-hoc as needed depending on the queries set, although some RelaySets might be long-lasting, + like the `explicitRelayUrls` specified by the user. +- RelaySets are always a subset of the pool of all available relays. \ No newline at end of file diff --git a/core/docs/getting-started/usage.md b/core/docs/getting-started/usage.md index 473c8e5b2..6c1d86fd2 100644 --- a/core/docs/getting-started/usage.md +++ b/core/docs/getting-started/usage.md @@ -42,100 +42,21 @@ NDK provides flexible ways to fetch user objects, including support for [NIP-19](https://github.com/nostr-protocol/nips/blob/master/19.md) encoded identifiers and [NIP-05](https://github.com/nostr-protocol/nips/blob/master/05.md) addresses: -```typescript -// From hex pubkey -const user1 = await ndk.fetchUser("3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d"); - -// From npub (NIP-19 encoded) -const user2 = await ndk.fetchUser("npub1n0sturny6w9zn2wwexju3m6asu7zh7jnv2jt2kx6tlmfhs7thq0qnflahe"); - -// From nprofile (includes relay hints) -const user3 = await ndk.fetchUser("nprofile1qqsrhuxx8l9ex335q7he0f09aej04zpazpl0ne2cgukyawd24mayt8gpp4mhxue69uhhytnc9e3k7mgpz4mhxue69uhkg6nzv9ejuumpv34kytnrdaksjlyr9p"); - -// From NIP-05 identifier -const user4 = await ndk.fetchUser("pablo@test.com"); -const user5 = await ndk.fetchUser("test.com"); // Uses _@test.com - -// The method automatically detects the format -const user6 = await ndk.fetchUser("deadbeef..."); // Assumes hex pubkey -``` - -Note: `fetchUser` is async and returns a Promise. For NIP-05 lookups, it may return `undefined` if the address cannot be -resolved. - - -See the [NIP-19 tutorial](/tutorial/nip19.html) for comprehensive examples and use cases. - -## Usage with React Hooks (`ndk-hooks`) - -When using the `ndk-hooks` package in a React application, the initialization process involves creating the NDK instance -and then using the `useNDKInit` hook to make it available to the rest of your application via Zustand stores. - -This hook ensures that both the core NDK store and dependent stores (like the user profiles store) are properly -initialized with the NDK instance. - -It's recommended to create and connect your NDK instance outside of your React components, potentially in a dedicated -setup file or at the root of your application. Then, use the `useNDKInit` hook within your main App component or a -context provider to initialize the stores once the component mounts. - -```tsx -import React, { useEffect } from 'react'; // Removed useState -import NDK from '@nostr-dev-kit/ndk'; -import { useNDKInit } from '@nostr-dev-kit/ndk-hooks'; // Assuming package name - -// 1. Configure your NDK instance (e.g., in src/ndk.ts or similar) -const ndk = new NDK({ - explicitRelayUrls: ['wss://relay.damus.io', 'wss://relay.primal.net'], - // Add signer or cache adapter if needed -}); - -// 2. Connect the instance immediately -ndk.connect() - .then(() => console.log('NDK connected')) - .catch((e) => console.error('NDK connection error:', e)); - -// Example: App component or Context Provider that initializes NDK stores -function App() { - const initializeNDK = useNDKInit(); // Hook returns the function directly - - useEffect(() => { - // 3. Initialize stores once the component mounts - initializeNDK(ndk); - }, [initializeNDK]); // Dependency ensures this runs if initializeNDK changes, though unlikely - - // Your application components can now use other ndk-hooks - // No need to wait for connection state here, as hooks handle NDK readiness internally - return ( -
- {/* ... Your app content using useProfile, useSubscribe, etc. ... */} -
- ); -} - -export default App; -``` +<<< @/core/docs/snippets/managing-users.ts -**Key Points:** +Note: `fetchUser` is async and returns a Promise. For [NIP-05](https://github.com/nostr-protocol/nips/blob/master/05.md) +lookups, it may return `undefined` if the address cannot be resolved. -* Create and configure your `NDK` instance globally or outside components. -* Call `ndk.connect()` immediately after creation. Connection happens in the background. -* In your main App or Provider component, get the `initializeNDK` function from `useNDKInit`. -* Use `useEffect` with an empty dependency array (or `[initializeNDK]`) to call `initializeNDK(ndk)` once on mount. -* This sets up the necessary Zustand stores. Other `ndk-hooks` will access the initialized `ndk` instance from the store - and handle its readiness internally. +See the [NIP-19 tutorial](/core/docs/tutorial/nip19.md) for comprehensive examples and use cases. ---- +### Frameworks Integrations -## Architecture decisions & suggestions +If you're planning to use `NDK` in a react framework, check out the documentation for these specific packages: -- Users of NDK should instantiate a single NDK instance. -- That instance tracks state with all relays connected, explicit and otherwise. -- All relays are tracked in a single pool that handles connection errors/reconnection logic. -- RelaySets are assembled ad-hoc as needed depending on the queries set, although some RelaySets might be long-lasting, - like the `explicitRelayUrls` specified by the user. -- RelaySets are always a subset of the pool of all available relays. +* [@nostr-dev-kit/react](/react/README.md): Hooks and utilities to integrate NDK into a React applications +* [@nostr-dev-kit/svelte](/svelte/README.md): Modern, performant, and beautiful Svelte 5 integration -## Subscribing to Events +### Subscribing to Events Once connected, you can subscribe to events using `ndk.subscribe()`. You provide filters to specify the events you're interested in. diff --git a/core/docs/snippets/managing-users.ts b/core/docs/snippets/managing-users.ts new file mode 100644 index 000000000..52e12dd3d --- /dev/null +++ b/core/docs/snippets/managing-users.ts @@ -0,0 +1,21 @@ +import NDK from "@nostr-dev-kit/ndk"; + +const ndk = new NDK({}); + +// From hex pubkey +const user1 = await ndk.fetchUser("3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d"); + +// From npub (NIP-19 encoded) +const user2 = await ndk.fetchUser("npub1n0sturny6w9zn2wwexju3m6asu7zh7jnv2jt2kx6tlmfhs7thq0qnflahe"); + +// From nprofile (includes relay hints) +const user3 = await ndk.fetchUser( + "nprofile1qqsrhuxx8l9ex335q7he0f09aej04zpazpl0ne2cgukyawd24mayt8gpp4mhxue69uhhytnc9e3k7mgpz4mhxue69uhkg6nzv9ejuumpv34kytnrdaksjlyr9p", +); + +// From NIP-05 identifier +const user4 = await ndk.fetchUser("pablo@test.com"); +const user5 = await ndk.fetchUser("test.com"); // Uses _@test.com + +// The method automatically detects the format +const user6 = await ndk.fetchUser("deadbeef..."); // Assumes hex pubkey diff --git a/react/docs/hooks_old_docs_from_usage.md b/react/docs/hooks_old_docs_from_usage.md new file mode 100644 index 000000000..e244e7814 --- /dev/null +++ b/react/docs/hooks_old_docs_from_usage.md @@ -0,0 +1,57 @@ +## Usage with React Hooks (`ndk-hooks`) + +When using the `ndk-hooks` package in a React application, the initialization process involves creating the NDK instance +and then using the `useNDKInit` hook to make it available to the rest of your application via Zustand stores. + +This hook ensures that both the core NDK store and dependent stores (like the user profiles store) are properly +initialized with the NDK instance. + +It's recommended to create and connect your NDK instance outside of your React components, potentially in a dedicated +setup file or at the root of your application. Then, use the `useNDKInit` hook within your main App component or a +context provider to initialize the stores once the component mounts. + +```tsx +import React, {useEffect} from 'react'; // Removed useState +import NDK from '@nostr-dev-kit/ndk'; +import {useNDKInit} from '@nostr-dev-kit/ndk-hooks'; // Assuming package name + +// 1. Configure your NDK instance (e.g., in src/ndk.ts or similar) +const ndk = new NDK({ + explicitRelayUrls: ['wss://relay.damus.io', 'wss://relay.primal.net'], + // Add signer or cache adapter if needed +}); + +// 2. Connect the instance immediately +ndk.connect() + .then(() => console.log('NDK connected')) + .catch((e) => console.error('NDK connection error:', e)); + +// Example: App component or Context Provider that initializes NDK stores +function App() { + const initializeNDK = useNDKInit(); // Hook returns the function directly + + useEffect(() => { + // 3. Initialize stores once the component mounts + initializeNDK(ndk); + }, [initializeNDK]); // Dependency ensures this runs if initializeNDK changes, though unlikely + + // Your application components can now use other ndk-hooks + // No need to wait for connection state here, as hooks handle NDK readiness internally + return ( +
+ {/* ... Your app content using useProfile, useSubscribe, etc. ... */} +
+ ); +} + +export default App; +``` + +**Key Points:** + +* Create and configure your `NDK` instance globally or outside components. +* Call `ndk.connect()` immediately after creation. Connection happens in the background. +* In your main App or Provider component, get the `initializeNDK` function from `useNDKInit`. +* Use `useEffect` with an empty dependency array (or `[initializeNDK]`) to call `initializeNDK(ndk)` once on mount. +* This sets up the necessary Zustand stores. Other `ndk-hooks` will access the initialized `ndk` instance from the store + and handle its readiness internally. \ No newline at end of file From efb93c00757ef3e7ab92408c3bef716c45b76e81 Mon Sep 17 00:00:00 2001 From: digitalbase Date: Thu, 20 Nov 2025 21:53:12 +0100 Subject: [PATCH 077/139] Move subscribe docs to fundamentals --- .vitepress/config.mts | 1 + core/docs/fundamentals/subscribing.md | 108 +++++++++++++++++++++++++ core/docs/getting-started/usage.md | 109 +------------------------- 3 files changed, 110 insertions(+), 108 deletions(-) create mode 100644 core/docs/fundamentals/subscribing.md diff --git a/.vitepress/config.mts b/.vitepress/config.mts index ee38a0553..55293d28f 100644 --- a/.vitepress/config.mts +++ b/.vitepress/config.mts @@ -47,6 +47,7 @@ export default defineConfig({ collapsed: false, items: [ {text: "Events โœ…", link: "/core/docs/fundamentals/events"}, + {text: "Subscribing โŒ", link: "/core/docs/fundamentals/subscribing"}, {text: "Signers โœ…", link: "/core/docs/fundamentals/signers"}, {text: "Publishing โœ…", link: "/core/docs/fundamentals/publishing"}, {text: "Connecting โœ…", link: "/core/docs/fundamentals/connecting"}, diff --git a/core/docs/fundamentals/subscribing.md b/core/docs/fundamentals/subscribing.md new file mode 100644 index 000000000..53b997ddd --- /dev/null +++ b/core/docs/fundamentals/subscribing.md @@ -0,0 +1,108 @@ +### Subscribing to Events + +Once connected, you can subscribe to events using `ndk.subscribe()`. You provide filters to specify the events you're +interested in. + +### Preferred Method: Direct Event Handlers + +The **recommended** way to handle events is to provide handler functions directly when calling `ndk.subscribe()`. This +is done using the third argument (`autoStart`), which accepts an object containing `onEvent`, `onEvents`, and/or +`onEose` callbacks. + +**Why is this preferred?** Subscriptions can start receiving events (especially from a fast cache) almost immediately +after `ndk.subscribe()` is called. By providing handlers directly, you ensure they are attached *before* any events are +emitted, preventing potential race conditions where you might miss the first few events if you attached handlers later +using `.on()`. + +```typescript +// Example with default relay calculation +ndk.subscribe( + {kinds: [1], authors: [pubkey]}, // Filters + {closeOnEose: true}, // Options (no explicit relays specified) + { // Direct handlers via autoStart parameter (now the 3rd argument) + onEvent: (event: NDKEvent, relay?: NDKRelay) => { + // Called for events received from relays after the initial cache load (if onEvents is used) + console.log("Received event from relay (id):", event.id); + }, + onEvents: (events: NDKEvent[]) => { // Parameter renamed to 'events' + console.log(`Received ${events.length} events from cache initially.`); + }, + onEose: (subscription: NDKSubscription) => { + console.log("Subscription reached EOSE:", subscription.internalId); + } + } +); + +// Example specifying explicit relays using relayUrls option +ndk.subscribe( + {kinds: [0], authors: [pubkey]}, // Filters + { // Options object now includes relayUrls + closeOnEose: true, + relayUrls: ["wss://explicit1.relay", "wss://explicit2.relay"] + }, + { // Direct handlers + onEvent: (event: NDKEvent) => { /* ... */ + } + } +); + +// Example specifying explicit relays using relaySet option +const explicitRelaySet = NDKRelaySet.fromRelayUrls(["wss://explicit.relay"], ndk); +ndk.subscribe( + {kinds: [7], authors: [pubkey]}, // Filters + { // Options object now includes relaySet + closeOnEose: true, + relaySet: explicitRelaySet + }, + { // Direct handlers + onEvent: (event: NDKEvent) => { /* ... */ + } + } +); +``` + +### Efficient Cache Handling with `onEvents` + +Using the `onEvents` handler provides an efficient way to process events loaded from the cache. When you provide +`onEvents`: + +1. If NDK finds matching events in its cache *synchronously* when the subscription starts, `onEvents` is called **once** + with an array of all those cached events. +2. The `onEvent` handler is **skipped** for this initial batch of cached events. +3. `onEvent` will still be called for any subsequent events received from relays or later asynchronous cache updates. + +This is ideal for scenarios like populating initial UI state, as it allows you to process the cached data in a single +batch, preventing potentially numerous individual updates that would occur if `onEvent` were called for each cached +item. + +If you *don't* provide `onEvents`, the standard `onEvent` handler will be triggered for every event, whether it comes +from the cache or a relay. + +### Alternative Method: Attaching Handlers with `.on()` + +You can also attach event listeners *after* creating the subscription using the `.on()` method. While functional, be +mindful of the potential race condition mentioned above, especially if you rely on immediate cache results. + +```typescript +// Subscribe using default relay calculation +const subscription = ndk.subscribe( + { kinds: [1], authors: [pubkey] }, + { closeOnEose: true } // Options +); + +// Subscribe using explicit relays via options +const subscriptionWithRelays = ndk.subscribe( + { kinds: [0], authors: [pubkey] }, + { relayUrls: ["wss://explicit.relay"] } // Options with explicit relays +); + +// Attach handlers later +subscription.on("event", (event) => { + console.log("Received event:", event.id); +}); +subscription.on("eose", () => { + console.log("Initial events loaded"); +}); + +// Remember to stop the subscription when it's no longer needed +// setTimeout(() => subscription.stop(), 5000); \ No newline at end of file diff --git a/core/docs/getting-started/usage.md b/core/docs/getting-started/usage.md index 6c1d86fd2..e1804cc63 100644 --- a/core/docs/getting-started/usage.md +++ b/core/docs/getting-started/usage.md @@ -49,116 +49,9 @@ lookups, it may return `undefined` if the address cannot be resolved. See the [NIP-19 tutorial](/core/docs/tutorial/nip19.md) for comprehensive examples and use cases. -### Frameworks Integrations +## Framework Integrations If you're planning to use `NDK` in a react framework, check out the documentation for these specific packages: * [@nostr-dev-kit/react](/react/README.md): Hooks and utilities to integrate NDK into a React applications * [@nostr-dev-kit/svelte](/svelte/README.md): Modern, performant, and beautiful Svelte 5 integration - -### Subscribing to Events - -Once connected, you can subscribe to events using `ndk.subscribe()`. You provide filters to specify the events you're -interested in. - -### Preferred Method: Direct Event Handlers - -The **recommended** way to handle events is to provide handler functions directly when calling `ndk.subscribe()`. This -is done using the third argument (`autoStart`), which accepts an object containing `onEvent`, `onEvents`, and/or -`onEose` callbacks. - -**Why is this preferred?** Subscriptions can start receiving events (especially from a fast cache) almost immediately -after `ndk.subscribe()` is called. By providing handlers directly, you ensure they are attached *before* any events are -emitted, preventing potential race conditions where you might miss the first few events if you attached handlers later -using `.on()`. - -```typescript -// Example with default relay calculation -ndk.subscribe( - { kinds: [1], authors: [pubkey] }, // Filters - { closeOnEose: true }, // Options (no explicit relays specified) - { // Direct handlers via autoStart parameter (now the 3rd argument) - onEvent: (event: NDKEvent, relay?: NDKRelay) => { - // Called for events received from relays after the initial cache load (if onEvents is used) - console.log("Received event from relay (id):", event.id); - }, - onEvents: (events: NDKEvent[]) => { // Parameter renamed to 'events' - console.log(`Received ${events.length} events from cache initially.`); - }, - onEose: (subscription: NDKSubscription) => { - console.log("Subscription reached EOSE:", subscription.internalId); - } - } -); - -// Example specifying explicit relays using relayUrls option -ndk.subscribe( - { kinds: [0], authors: [pubkey] }, // Filters - { // Options object now includes relayUrls - closeOnEose: true, - relayUrls: ["wss://explicit1.relay", "wss://explicit2.relay"] - }, - { // Direct handlers - onEvent: (event: NDKEvent) => { /* ... */ } - } -); - -// Example specifying explicit relays using relaySet option -const explicitRelaySet = NDKRelaySet.fromRelayUrls(["wss://explicit.relay"], ndk); -ndk.subscribe( - { kinds: [7], authors: [pubkey] }, // Filters - { // Options object now includes relaySet - closeOnEose: true, - relaySet: explicitRelaySet - }, - { // Direct handlers - onEvent: (event: NDKEvent) => { /* ... */ } - } -); -``` - -### Efficient Cache Handling with `onEvents` - -Using the `onEvents` handler provides an efficient way to process events loaded from the cache. When you provide -`onEvents`: - -1. If NDK finds matching events in its cache *synchronously* when the subscription starts, `onEvents` is called **once** - with an array of all those cached events. -2. The `onEvent` handler is **skipped** for this initial batch of cached events. -3. `onEvent` will still be called for any subsequent events received from relays or later asynchronous cache updates. - -This is ideal for scenarios like populating initial UI state, as it allows you to process the cached data in a single -batch, preventing potentially numerous individual updates that would occur if `onEvent` were called for each cached -item. - -If you *don't* provide `onEvents`, the standard `onEvent` handler will be triggered for every event, whether it comes -from the cache or a relay. - -### Alternative Method: Attaching Handlers with `.on()` - -You can also attach event listeners *after* creating the subscription using the `.on()` method. While functional, be -mindful of the potential race condition mentioned above, especially if you rely on immediate cache results. - -```typescript -// Subscribe using default relay calculation -const subscription = ndk.subscribe( - { kinds: [1], authors: [pubkey] }, - { closeOnEose: true } // Options -); - -// Subscribe using explicit relays via options -const subscriptionWithRelays = ndk.subscribe( - { kinds: [0], authors: [pubkey] }, - { relayUrls: ["wss://explicit.relay"] } // Options with explicit relays -); - -// Attach handlers later -subscription.on("event", (event) => { - console.log("Received event:", event.id); -}); -subscription.on("eose", () => { - console.log("Initial events loaded"); -}); - -// Remember to stop the subscription when it's no longer needed -// setTimeout(() => subscription.stop(), 5000); From 443dfd71b4fef13b21e1306a4d36a64cd71eebd7 Mon Sep 17 00:00:00 2001 From: digitalbase Date: Thu, 20 Nov 2025 21:58:37 +0100 Subject: [PATCH 078/139] Move vitepress config from the root to the docs folder --- .vitepress/.gitignore | 1 - {.vitepress => docs/.vitepress}/config.mts | 1 + {.vitepress => docs/.vitepress}/theme/index.ts | 0 {.vitepress => docs/.vitepress}/theme/style.css | 0 package.json | 6 +++--- 5 files changed, 4 insertions(+), 4 deletions(-) delete mode 100644 .vitepress/.gitignore rename {.vitepress => docs/.vitepress}/config.mts (99%) rename {.vitepress => docs/.vitepress}/theme/index.ts (100%) rename {.vitepress => docs/.vitepress}/theme/style.css (100%) diff --git a/.vitepress/.gitignore b/.vitepress/.gitignore deleted file mode 100644 index 5e4659675..000000000 --- a/.vitepress/.gitignore +++ /dev/null @@ -1 +0,0 @@ -cache \ No newline at end of file diff --git a/.vitepress/config.mts b/docs/.vitepress/config.mts similarity index 99% rename from .vitepress/config.mts rename to docs/.vitepress/config.mts index 55293d28f..0d5c02f49 100644 --- a/.vitepress/config.mts +++ b/docs/.vitepress/config.mts @@ -3,6 +3,7 @@ import {defineConfig} from "vitepress"; // https://vitepress.dev/reference/site-config export default defineConfig({ title: "NDK", + srcDir: "../", description: "NDK Docs", // base: "/ndk/", ignoreDeadLinks: true, diff --git a/.vitepress/theme/index.ts b/docs/.vitepress/theme/index.ts similarity index 100% rename from .vitepress/theme/index.ts rename to docs/.vitepress/theme/index.ts diff --git a/.vitepress/theme/style.css b/docs/.vitepress/theme/style.css similarity index 100% rename from .vitepress/theme/style.css rename to docs/.vitepress/theme/style.css diff --git a/package.json b/package.json index a71fcb1f7..7372bc15f 100755 --- a/package.json +++ b/package.json @@ -33,9 +33,9 @@ "cs:pub": "bun cs:check && changeset publish", "cs:ver": "changeset version && bunx syncpack fix-mismatches", "dev": "turbo dev --no-cache --continue", - "docs:build": "vitepress build", - "docs:dev": "npx vitepress dev", - "docs:preview": "vitepress preview", + "docs:build": "cd docs && vitepress build", + "docs:dev": "cd docs && npx vitepress dev", + "docs:preview": "cd docs && vitepress preview", "format": "biome format --write .", "lint": "biome check .", "release": "bun cs:check && turbo build --filter=docs^... && changeset publish", From 89f5d10829169ca7b675c2b56b791cfd8b0e6e08 Mon Sep 17 00:00:00 2001 From: digitalbase Date: Thu, 20 Nov 2025 21:59:18 +0100 Subject: [PATCH 079/139] Clean urls for docs (no .html in the end) --- docs/.vitepress/config.mts | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/.vitepress/config.mts b/docs/.vitepress/config.mts index 0d5c02f49..97363a676 100644 --- a/docs/.vitepress/config.mts +++ b/docs/.vitepress/config.mts @@ -5,6 +5,7 @@ export default defineConfig({ title: "NDK", srcDir: "../", description: "NDK Docs", + cleanUrls: true, // base: "/ndk/", ignoreDeadLinks: true, markdown: { From 42066d610949a3bfa2e41b87b089411bff4def2b Mon Sep 17 00:00:00 2001 From: digitalbase Date: Thu, 20 Nov 2025 22:13:43 +0100 Subject: [PATCH 080/139] Clean up introduction page + break out supported nips + link them --- core/docs/introduction.md | 32 +++++++++++++++++++++++--------- docs/index.md | 2 +- 2 files changed, 24 insertions(+), 10 deletions(-) diff --git a/core/docs/introduction.md b/core/docs/introduction.md index bd40318c7..ff56921b2 100644 --- a/core/docs/introduction.md +++ b/core/docs/introduction.md @@ -8,17 +8,31 @@ NDK is a TypeScript/JavaScript library that simplifies building Nostr clients, r ## Features -- Outbox model support -- Relay connection pool with automatic reconnection and failover +- Event creation, validation, and wrappers for [major NIPs](#nip-support) - Flexible subscription API with caching, batching, and auto-closing -- Event creation, validation, and wrappers for major NIPs (e.g., NIP-01, NIP-04, NIP-07, NIP-18, NIP-49, NIP-57, NIP-60, - NIP-61) -- Signer adapters: private key, encrypted keys (NIP-49), browser extension (NIP-07), remote signing (NIP-46) -- Pluggable cache adapters (Redis, Dexie, SQLite, etc.) -- Data Vending Machine support (NIP-90) -- Zap utilities (NIP-57, NIP-61) +- Event Signing through private key, encrypted + keys ([NIP-49](https://github.com/nostr-protocol/nips/blob/master/49.md)), + browser extension ([NIP-07](https://github.com/nostr-protocol/nips/blob/master/07.md)) or remote signer + ([NIP-46](https://github.com/nostr-protocol/nips/blob/master/46.md)) +- Relay connection pool with automatic reconnection and failover +- Outbox model support +- Pluggable cache adapters (Redis, Dexie, SQLite, etc) +- Data Vending Machine support ([NIP-90](https://github.com/nostr-protocol/nips/blob/master/90.md)) +- Zap + utilities ([NIP-57](https://github.com/nostr-protocol/nips/blob/master/57.md), [NIP-61](https://github.com/nostr-protocol/nips/blob/master/61.md)) - Threading, event kinds, and utility functions (URL normalization, metadata tags, filters) -- Modular design with many pluggable packages for different frameworks (Mobile, Svelte 4 and 5, React) +- Modular design with packages for different frameworks (Mobile, Svelte, React) + +### NIP Support + +- [NIP-01](https://github.com/nostr-protocol/nips/blob/master/01.md) +- [NIP-04](https://github.com/nostr-protocol/nips/blob/master/04.md) +- [NIP-07](https://github.com/nostr-protocol/nips/blob/master/07.md) +- [NIP-18](https://github.com/nostr-protocol/nips/blob/master/18.md) +- [NIP-49](https://github.com/nostr-protocol/nips/blob/master/49.md) +- [NIP-57](https://github.com/nostr-protocol/nips/blob/master/57.md) +- [NIP-60](https://github.com/nostr-protocol/nips/blob/master/60.md) +- [NIP-61](https://github.com/nostr-protocol/nips/blob/master/61.md) ## Multi-Repo diff --git a/docs/index.md b/docs/index.md index 60dfcbed4..bd4ee11f2 100644 --- a/docs/index.md +++ b/docs/index.md @@ -22,6 +22,6 @@ Nostr clients and applications. The package contains relay interaction code, rea caching libraries, Web of Trust functionality, and more. > [!WARNING] -> The documentation of the NDK project is under heavy construction. +> The documentation of the NDK project is under heavy construction > [More information available in open pull request](https://github.com/nostr-dev-kit/ndk/pull/344). > \ No newline at end of file From d9d3d4f5a2d2caee85a477d67a2d9a33448e6010 Mon Sep 17 00:00:00 2001 From: digitalbase Date: Thu, 20 Nov 2025 22:18:21 +0100 Subject: [PATCH 081/139] Swap links to nostr-nips.com to the github source of truth --- core/docs/fundamentals/connecting.md | 5 +++-- core/docs/fundamentals/events.md | 14 +++++++++----- core/docs/fundamentals/publishing.md | 3 ++- core/docs/fundamentals/signers.md | 7 ++++--- core/docs/fundamentals/signers_old.md | 3 ++- wallet/docs/NDKNWCWallet.md | 3 ++- wallet/docs/NDKWebLNWallet.md | 2 +- 7 files changed, 23 insertions(+), 14 deletions(-) diff --git a/core/docs/fundamentals/connecting.md b/core/docs/fundamentals/connecting.md index d019bef95..5702c88fb 100644 --- a/core/docs/fundamentals/connecting.md +++ b/core/docs/fundamentals/connecting.md @@ -43,7 +43,8 @@ Explicit relays can also be added using the `addExplicitRelay()` method. A [signer](/core/getting-started/signers.html) is used to sign events and tells NDK about your pubkey and related settings. Once you link up a signer and you have `autoConnectUserRelays` enabled (on by default) NDK will fetch your `kind:10002` event -([NIP-65](https://nostr-nips.com/nip-65)) and add discovered relays specified to a 2nd pool of connected relays. +([NIP-65](https://github.com/nostr-protocol/nips/blob/master/65.md)) and add discovered relays specified to a 2nd pool +of connected relays. <<< @/core/docs/snippets/connect_nip07.ts @@ -59,7 +60,7 @@ The outbox model works similarly to the web/RSS model: - **Inbox Relays**: You designate relays where you want to receive messages - **Dynamic Discovery**: Clients discover and connect to relays based on where users actually post -The protocol is formalized in ([NIP-65](https://nostr-nips.com/nip-65)), which defines: +The protocol is formalized in ([NIP-65](https://github.com/nostr-protocol/nips/blob/master/65.md)), which defines: - **`kind:10002` events**: Relay list metadata containing read/write relay preferences - **Relay tags**: "r" tags with optional "read"/"write" markers diff --git a/core/docs/fundamentals/events.md b/core/docs/fundamentals/events.md index 21aa10c6a..14695bbac 100644 --- a/core/docs/fundamentals/events.md +++ b/core/docs/fundamentals/events.md @@ -1,12 +1,15 @@ # Events -Events are at the heart of the Nostr protocol and described in [NIP-01.](https://nostr-nips.com/nip-01). To support -a wide range of functionality, Nostr comes with [a number of event types](https://nostr-nips.com/#event-kinds) but you +Events are at the heart of the Nostr protocol and described +in [NIP-01.](https://github.com/nostr-protocol/nips/blob/master/01.md). To support +a wide range of functionality, Nostr comes +with [a number of event types](https://github.com/nostr-protocol/nips/blob/master/Readme.md#event-kinds) but you can also create your own. ## Creating an event -This is the simplest example of creating a text note [`kind:1`](https://nostr-nips.com/nip-01#kinds) event. +This is the simplest example of creating a text note [ +`kind:1`](https://github.com/nostr-protocol/nips/tree/master?tab=readme-ov-file#event-kinds) event. <<< @/core/docs/snippets/create_event.ts @@ -15,8 +18,9 @@ No need to fill in event's `id`, `tags`, `pubkey`, `created_at`, NDK will do tha ## Tagging users (or events) Tags tell the protocol about related entities like mentioned users, relays, topics, other events, etc. Details about -tags can be found in the [tags section of NIP-01](https://nostr-nips.com/nip-01#tags) and in [the reference list of -standardized tags](https://nostr-nips.com/#standardized-tags). +tags can be found in the [tags section of NIP-01](https://github.com/nostr-protocol/nips/blob/master/01.md#tags) and +in [the reference list of +standardized tags](https://github.com/nostr-protocol/nips/tree/master?tab=readme-ov-file#common-tags). NDK automatically adds the appropriate tags for mentions in the content when a user or event is mentioned. diff --git a/core/docs/fundamentals/publishing.md b/core/docs/fundamentals/publishing.md index 37e3b0af8..255ff4a2f 100644 --- a/core/docs/fundamentals/publishing.md +++ b/core/docs/fundamentals/publishing.md @@ -1,7 +1,8 @@ # Publishing Events Publishing events means sending them to one or multiple relays as described in -[NIP-01](https://nostr-nips.com/nip-01#communication-between-clients-and-relays). NDK provides easy ways to publish +[NIP-01](https://github.com/nostr-protocol/nips/blob/master/01.md#communication-between-clients-and-relays). NDK +provides easy ways to publish events and manage the status of that event. > [!NOTE] diff --git a/core/docs/fundamentals/signers.md b/core/docs/fundamentals/signers.md index 05c2bb11e..e5f547fe0 100644 --- a/core/docs/fundamentals/signers.md +++ b/core/docs/fundamentals/signers.md @@ -1,7 +1,7 @@ # Signers -All events on the Nostr protocol are signed through a keypair -(described in [NIP-01](https://nostr-nips.com/nip-01#events-and-signatures)). +All events on the Nostr protocol are signed through a keypair +(described in [NIP-01](https://github.com/nostr-protocol/nips/blob/master/01.md#events-and-signatures)). In NDK this is taken care of by the `NDKSigner` interface that can be passed in during initialization or later during runtime. @@ -10,7 +10,8 @@ runtime. ### Browser Extensions -A common way to use NDK is to use a browser extension which is described in [NIP-07](https://nostr-nips.com/nip-07). +A common way to use NDK is to use a browser extension which is described +in [NIP-07](https://github.com/nostr-protocol/nips/blob/master/07.md). This mechanism allows the user to sign events with a browser extension to not share their private key with the application. diff --git a/core/docs/fundamentals/signers_old.md b/core/docs/fundamentals/signers_old.md index 0994ce9ea..c16f5687e 100644 --- a/core/docs/fundamentals/signers_old.md +++ b/core/docs/fundamentals/signers_old.md @@ -3,7 +3,8 @@ A Nostr remote signer is an application or device that securely stores your private key and signs Nostr events on your behalf, preventing you from having to expose the key clients. -It works by establishing a secure connection, as described in [NIP-46](https://nostr-nips.com/nip-46), with a Nostr +It works by establishing a secure connection, as described +in [NIP-46](https://github.com/nostr-protocol/nips/blob/master/46.md), with a Nostr client and then receiving signing requests via push notifications to approve or deny. #### bunker:// diff --git a/wallet/docs/NDKNWCWallet.md b/wallet/docs/NDKNWCWallet.md index 9918e8a5d..6fc932348 100644 --- a/wallet/docs/NDKNWCWallet.md +++ b/wallet/docs/NDKNWCWallet.md @@ -1,6 +1,7 @@ # NWC Client (`NDKWalletNWC`) -The `NDKWalletNWC` implements the [NIP-47](https://nostr-nips.com/nip-47) specification and allows you to zap using [Nostr +The `NDKWalletNWC` implements the [NIP-47](https://github.com/nostr-protocol/nips/blob/master/47.md) specification and +allows you to zap using [Nostr Web Connect (NWC)](https://nwc.dev/). ## Usage diff --git a/wallet/docs/NDKWebLNWallet.md b/wallet/docs/NDKWebLNWallet.md index acd39509f..ff04080ef 100644 --- a/wallet/docs/NDKWebLNWallet.md +++ b/wallet/docs/NDKWebLNWallet.md @@ -95,7 +95,7 @@ Make sure to await the promise to fully refresh the balance. ## Nostr Zaps -Lightning Zaps on the Nostr network are described in [NIP-57](https://nostr-nips.com/nip-57). +Lightning Zaps on the Nostr network are described in [NIP-57](https://github.com/nostr-protocol/nips/blob/master/57.md). The full protocol (Step 1 to 9) is described in the respective docs. In the below example we will refer to the steps described in the Nostr Implementation Protocol (NIP). From c1bab9549bb7ff6f8365eff81c340a20f781057f Mon Sep 17 00:00:00 2001 From: digitalbase Date: Thu, 20 Nov 2025 23:02:00 +0100 Subject: [PATCH 082/139] Add subscribing to sidebar --- docs/.vitepress/config.mts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/.vitepress/config.mts b/docs/.vitepress/config.mts index 97363a676..8a795b3f6 100644 --- a/docs/.vitepress/config.mts +++ b/docs/.vitepress/config.mts @@ -49,7 +49,7 @@ export default defineConfig({ collapsed: false, items: [ {text: "Events โœ…", link: "/core/docs/fundamentals/events"}, - {text: "Subscribing โŒ", link: "/core/docs/fundamentals/subscribing"}, + {text: "Subscribing โœ…", link: "/core/docs/fundamentals/subscribing"}, {text: "Signers โœ…", link: "/core/docs/fundamentals/signers"}, {text: "Publishing โœ…", link: "/core/docs/fundamentals/publishing"}, {text: "Connecting โœ…", link: "/core/docs/fundamentals/connecting"}, From 798b0ca6a54240f9f909c6dd6b813add8e9e420d Mon Sep 17 00:00:00 2001 From: digitalbase Date: Thu, 20 Nov 2025 23:02:13 +0100 Subject: [PATCH 083/139] Add snippets index files --- docs/snippets/connecting.md | 19 +++++++++++++++++++ docs/snippets/events.md | 19 +++++++++++++++++++ docs/snippets/publishing.md | 7 +++++++ docs/snippets/signers.md | 15 +++++++++++++++ 4 files changed, 60 insertions(+) create mode 100644 docs/snippets/connecting.md create mode 100644 docs/snippets/events.md create mode 100644 docs/snippets/publishing.md create mode 100644 docs/snippets/signers.md diff --git a/docs/snippets/connecting.md b/docs/snippets/connecting.md new file mode 100644 index 000000000..7e36e167a --- /dev/null +++ b/docs/snippets/connecting.md @@ -0,0 +1,19 @@ +### Specific Relays + +<<< @/core/docs/snippets/connect_explicit.ts + +### Adding Relays + +<<< @/core/docs/snippets/connect_explicit_alt.ts + +### NIP-07 Relays + +<<< @/core/docs/snippets/connect_nip07.ts + +### Dev Relays + +<<< @/core/docs/snippets/connect_dev_relays.ts + +### Relay Pools + +<<< @/core/docs/snippets/connect_pools.ts \ No newline at end of file diff --git a/docs/snippets/events.md b/docs/snippets/events.md new file mode 100644 index 000000000..151f336bf --- /dev/null +++ b/docs/snippets/events.md @@ -0,0 +1,19 @@ +### Creating an event + +<<< @/core/docs/snippets/create_event.ts + +### Tagging users + +<<< @/core/docs/snippets/tag_user.ts + +### Signing events + +<<< @/core/docs/snippets/sign_event.ts + +### Interest (kind 10015) Event + +Interest events are used to tell the network about your interest in a particular topic. +Those events and are making use of the [NIP-51](https://github.com/nostr-protocol/nips/blob/master/51.md) specification +kind:10015 events. + +<<< @/core/docs/snippets/interest_event.ts diff --git a/docs/snippets/publishing.md b/docs/snippets/publishing.md new file mode 100644 index 000000000..9d44bfda4 --- /dev/null +++ b/docs/snippets/publishing.md @@ -0,0 +1,7 @@ +### Publish Event + +<<< @/core/docs/snippets/publish_event.ts + +### Replaceable Event + +<<< @/core/docs/snippets/replace_event.ts \ No newline at end of file diff --git a/docs/snippets/signers.md b/docs/snippets/signers.md new file mode 100644 index 000000000..07c6ac438 --- /dev/null +++ b/docs/snippets/signers.md @@ -0,0 +1,15 @@ +### Signing events + +<<< @/core/docs/snippets/sign_event.ts + +### Different signers + +<<< @/core/docs/snippets/sign_event_with_other_signers.ts + +### Generate Keypair + +<<< @/core/docs/snippets/key_create.ts + +### Sign using bunker + +<<< @/core/docs/snippets/sign_with_bunker.ts From 81f8a901021861ad7c26f617f5e899b633ab2b95 Mon Sep 17 00:00:00 2001 From: digitalbase Date: Thu, 20 Nov 2025 23:02:29 +0100 Subject: [PATCH 084/139] Move more snippets to snippet folder + fix syntax --- core/docs/fundamentals/signers.md | 15 +- core/docs/fundamentals/subscribing.md | 131 +++++++----------- core/docs/snippets/sign_with_bunker.ts | 3 + core/docs/snippets/subscribe.ts | 8 ++ core/docs/snippets/subscribe_event_attach.ts | 19 +++ .../docs/snippets/subscribe_event_handlers.ts | 22 +++ core/docs/snippets/subscribe_relayset.ts | 12 ++ 7 files changed, 119 insertions(+), 91 deletions(-) create mode 100644 core/docs/snippets/subscribe.ts create mode 100644 core/docs/snippets/subscribe_event_attach.ts create mode 100644 core/docs/snippets/subscribe_event_handlers.ts create mode 100644 core/docs/snippets/subscribe_relayset.ts diff --git a/core/docs/fundamentals/signers.md b/core/docs/fundamentals/signers.md index e5f547fe0..0117b0aa1 100644 --- a/core/docs/fundamentals/signers.md +++ b/core/docs/fundamentals/signers.md @@ -12,8 +12,8 @@ runtime. A common way to use NDK is to use a browser extension which is described in [NIP-07](https://github.com/nostr-protocol/nips/blob/master/07.md). -This mechanism allows the user to sign events with a browser extension to not share their private key -with the application. +This mechanism allows the user to sign events with a browser extension to not share their private key with the +application. The most used browser extensions are [Nos2x](https://github.com/fiatjaf/nos2x) and [Alby](https://getalby.com/alby-extension). @@ -54,12 +54,10 @@ This library can also [help with generating new keys](/core/docs/fundamentals/si ### Remote Signer A Nostr remote signer (aka `bunker`) is an application or device that securely stores your private key and signs Nostr -events on -your behalf, preventing you from having to expose the private key. It works by establishing a secure connection (over -Nostr relays) -, as described in [NIP-46](https://github.com/nostr-protocol/nips/blob/master/46.md), where the bunker implementation -can approve -or deny requests. +events on your behalf, preventing you from having to expose the private key. It works by establishing a secure +connection (over +Nostr relays), as described in [NIP-46](https://github.com/nostr-protocol/nips/blob/master/46.md), where the bunker +implementation can approve or deny requests. To add remote signing support to your application, there are a few things you need: @@ -107,7 +105,6 @@ You can specify the use of a different signer to sign with different keys. ## Read Public key - **Read the user's public key** ```ts diff --git a/core/docs/fundamentals/subscribing.md b/core/docs/fundamentals/subscribing.md index 53b997ddd..bb31ffda1 100644 --- a/core/docs/fundamentals/subscribing.md +++ b/core/docs/fundamentals/subscribing.md @@ -1,12 +1,33 @@ -### Subscribing to Events +# Subscribing to Events -Once connected, you can subscribe to events using `ndk.subscribe()`. You provide filters to specify the events you're -interested in. +Once [connected](/core/docs/getting-started/usage#connecting), you can subscribe to events using `ndk.subscribe()` by +providing filters you can specify the events you're interested in. -### Preferred Method: Direct Event Handlers +## Subscribe -The **recommended** way to handle events is to provide handler functions directly when calling `ndk.subscribe()`. This -is done using the third argument (`autoStart`), which accepts an object containing `onEvent`, `onEvents`, and/or +The `ndk.subscribe()` method accepts these parameters: + +- `filters`: A single or array of `NDKFilter`. + See [NIP-01](https://github.com/nostr-protocol/nips/blob/master/01.md#communication-between-clients-and-relays). +- `opts?`: Subscription options object `NDKSubscriptionOptions`. +- `autoStart?`: [Event handlers](#event-handlers) for that subscription. + +<<< @/core/docs/snippets/subscribe.ts + +## Specifying Relays + +By default, NDK will use the already connected relay set or provided through the signer. You can +override this behavior by providing explicit relays in the `relayUrls` or `relaySet` options. + +<<< @/core/docs/snippets/subscribe_relayset.ts + +## Event Handlers + +### Handler Functions + +> [!TIP] +> The **recommended** way to handle events is to provide handler functions directly when calling `ndk.subscribe()`. This +> is done using the third argument (`autoStart`), which accepts an object containing `onEvent`, `onEvents`, and/or `onEose` callbacks. **Why is this preferred?** Subscriptions can start receiving events (especially from a fast cache) almost immediately @@ -14,54 +35,25 @@ after `ndk.subscribe()` is called. By providing handlers directly, you ensure th emitted, preventing potential race conditions where you might miss the first few events if you attached handlers later using `.on()`. -```typescript -// Example with default relay calculation -ndk.subscribe( - {kinds: [1], authors: [pubkey]}, // Filters - {closeOnEose: true}, // Options (no explicit relays specified) - { // Direct handlers via autoStart parameter (now the 3rd argument) - onEvent: (event: NDKEvent, relay?: NDKRelay) => { - // Called for events received from relays after the initial cache load (if onEvents is used) - console.log("Received event from relay (id):", event.id); - }, - onEvents: (events: NDKEvent[]) => { // Parameter renamed to 'events' - console.log(`Received ${events.length} events from cache initially.`); - }, - onEose: (subscription: NDKSubscription) => { - console.log("Subscription reached EOSE:", subscription.internalId); - } - } -); - -// Example specifying explicit relays using relayUrls option -ndk.subscribe( - {kinds: [0], authors: [pubkey]}, // Filters - { // Options object now includes relayUrls - closeOnEose: true, - relayUrls: ["wss://explicit1.relay", "wss://explicit2.relay"] - }, - { // Direct handlers - onEvent: (event: NDKEvent) => { /* ... */ - } - } -); - -// Example specifying explicit relays using relaySet option -const explicitRelaySet = NDKRelaySet.fromRelayUrls(["wss://explicit.relay"], ndk); -ndk.subscribe( - {kinds: [7], authors: [pubkey]}, // Filters - { // Options object now includes relaySet - closeOnEose: true, - relaySet: explicitRelaySet - }, - { // Direct handlers - onEvent: (event: NDKEvent) => { /* ... */ - } - } -); -``` - -### Efficient Cache Handling with `onEvents` +<<< @/core/docs/snippets/subscribe_event_handlers.ts + +### Attaching Handlers + +You can also attach event listeners *after* creating the subscription using the `.on()` method. + +> [!WARNING] +> While functional, be mindful of the potential race condition mentioned above, especially if you rely on immediate +> cache results. + +<<< @/core/docs/snippets/subscribe_event_attach.ts + +## Functions + +### onEvent + +The `onEvent` handler is called for every event received from relays or the cache. + +### onEvents Using the `onEvents` handler provides an efficient way to process events loaded from the cache. When you provide `onEvents`: @@ -78,31 +70,6 @@ item. If you *don't* provide `onEvents`, the standard `onEvent` handler will be triggered for every event, whether it comes from the cache or a relay. -### Alternative Method: Attaching Handlers with `.on()` - -You can also attach event listeners *after* creating the subscription using the `.on()` method. While functional, be -mindful of the potential race condition mentioned above, especially if you rely on immediate cache results. - -```typescript -// Subscribe using default relay calculation -const subscription = ndk.subscribe( - { kinds: [1], authors: [pubkey] }, - { closeOnEose: true } // Options -); - -// Subscribe using explicit relays via options -const subscriptionWithRelays = ndk.subscribe( - { kinds: [0], authors: [pubkey] }, - { relayUrls: ["wss://explicit.relay"] } // Options with explicit relays -); - -// Attach handlers later -subscription.on("event", (event) => { - console.log("Received event:", event.id); -}); -subscription.on("eose", () => { - console.log("Initial events loaded"); -}); - -// Remember to stop the subscription when it's no longer needed -// setTimeout(() => subscription.stop(), 5000); \ No newline at end of file +### onEose + +Called when the subscription is closed. diff --git a/core/docs/snippets/sign_with_bunker.ts b/core/docs/snippets/sign_with_bunker.ts index dbdce5af1..ae7b0af0e 100644 --- a/core/docs/snippets/sign_with_bunker.ts +++ b/core/docs/snippets/sign_with_bunker.ts @@ -1,5 +1,8 @@ // provided by the user +import NDK, { NDKNip46Signer, NDKPrivateKeySigner } from "@nostr-dev-kit/ndk"; + const signerConnectionString = "bunker://...."; +const ndk = new NDK(); // local keypair generated when signer if first initialised const clientKeypair = NDKPrivateKeySigner.generate(); // diff --git a/core/docs/snippets/subscribe.ts b/core/docs/snippets/subscribe.ts new file mode 100644 index 000000000..be985dc09 --- /dev/null +++ b/core/docs/snippets/subscribe.ts @@ -0,0 +1,8 @@ +import NDK from "@nostr-dev-kit/ndk"; + +const ndk = new NDK(); + +ndk.subscribe( + { kinds: [1] }, // Filters + { closeOnEose: true }, // Options (no explicit relays specified) +); diff --git a/core/docs/snippets/subscribe_event_attach.ts b/core/docs/snippets/subscribe_event_attach.ts new file mode 100644 index 000000000..fa1287adb --- /dev/null +++ b/core/docs/snippets/subscribe_event_attach.ts @@ -0,0 +1,19 @@ +import NDK from "@nostr-dev-kit/ndk"; + +const ndk = new NDK(); + +const subscription = ndk.subscribe( + { kinds: [1] }, // Filters + { closeOnEose: true }, // Options (no explicit relays specified) +); + +// Attach handlers later +subscription.on("event", (event) => { + console.log("Received event:", event.id); +}); +subscription.on("eose", () => { + console.log("Initial events loaded"); +}); + +// Remember to stop the subscription when it's no longer needed +// setTimeout(() => subscription.stop(), 5000); diff --git a/core/docs/snippets/subscribe_event_handlers.ts b/core/docs/snippets/subscribe_event_handlers.ts new file mode 100644 index 000000000..4638ea637 --- /dev/null +++ b/core/docs/snippets/subscribe_event_handlers.ts @@ -0,0 +1,22 @@ +import NDK, { type NDKEvent, type NDKRelay, type NDKSubscription } from "@nostr-dev-kit/ndk"; + +const ndk = new NDK(); + +ndk.subscribe( + { kinds: [1] }, // Filters + { closeOnEose: true }, // Options (no explicit relays specified) + { + // Direct handlers via autoStart parameter (now the 3rd argument) + onEvent: (event: NDKEvent, relay?: NDKRelay) => { + // Called for events received from relays after the initial cache load (if onEvents is used) + console.log("Received event from relay (id):", event.id); + }, + onEvents: (events: NDKEvent[]) => { + // Parameter renamed to 'events' + console.log(`Received ${events.length} events from cache initially.`); + }, + onEose: (subscription: NDKSubscription) => { + console.log("Subscription reached EOSE:", subscription.internalId); + }, + }, +); diff --git a/core/docs/snippets/subscribe_relayset.ts b/core/docs/snippets/subscribe_relayset.ts new file mode 100644 index 000000000..58b23e456 --- /dev/null +++ b/core/docs/snippets/subscribe_relayset.ts @@ -0,0 +1,12 @@ +import NDK, {NDKRelaySet} from "@nostr-dev-kit/ndk"; + +const ndk = new NDK(); +const explicitRelaySet = NDKRelaySet.fromRelayUrls(["wss://explicit.relay"], ndk); +ndk.subscribe( + {kinds: [7]}, // Filters + { + // Options object now includes relaySet + closeOnEose: true, + relaySet: explicitRelaySet, + }, +); From fa8070460b3659e57fb75f1235288b0b395c8dba Mon Sep 17 00:00:00 2001 From: digitalbase Date: Thu, 20 Nov 2025 23:06:19 +0100 Subject: [PATCH 085/139] Fix code snippets indexes + add code snippets to subscribe --- bun.lock | 76 +++++++++++++++++++++++---- core/docs/fundamentals/subscribing.md | 6 +++ docs/snippets.md | 4 ++ docs/snippets/subscribing.md | 16 ++++++ 4 files changed, 92 insertions(+), 10 deletions(-) create mode 100644 docs/snippets/subscribing.md diff --git a/bun.lock b/bun.lock index 484a33f5f..91611419e 100644 --- a/bun.lock +++ b/bun.lock @@ -5,15 +5,15 @@ "name": "@nostr-dev-kit/monorepo", "dependencies": { "@biomejs/biome": "^2.2.6", - "esbuild": "^0.25.11", - "mermaid": "^11.6.0", + "esbuild": "^0.25.12", + "mermaid": "^11.12.1", "vitepress": "^1.6.3", "vitepress-plugin-mermaid": "^2.0.17", }, "devDependencies": { - "svelte": "^5.41.0", + "svelte": "^5.43.14", "syncpack": "^13.0.4", - "turbo": "^2.5.3", + "turbo": "^2.6.1", }, }, "blossom": { @@ -1267,7 +1267,7 @@ "@types/ms": ["@types/ms@2.1.0", "", {}, "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA=="], - "@types/node": ["@types/node@24.7.1", "", { "dependencies": { "undici-types": "~7.14.0" } }, "sha512-CmyhGZanP88uuC5GpWU9q+fI61j2SkhO3UGMUdfYRE6Bcy0ccyzn1Rqj9YAB/ZY4kOXmNf0ocah5GtphmLMP6Q=="], + "@types/node": ["@types/node@24.10.1", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ=="], "@types/normalize-package-data": ["@types/normalize-package-data@2.4.4", "", {}, "sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA=="], @@ -3179,7 +3179,7 @@ "undici": ["undici@6.22.0", "", {}, "sha512-hU/10obOIu62MGYjdskASR3CUAiYaFTtC9Pa6vHyf//mAipSvSQg6od2CnJswq7fvzNS3zJhxoRkgNVaHurWKw=="], - "undici-types": ["undici-types@7.14.0", "", {}, "sha512-QQiYxHuyZ9gQUIrmPo3IA+hUl4KYk8uSA7cHrcKd/l3p1OTpZcM0Tbp9x7FAtXdAYhlasd60ncPpgu6ihG6TOA=="], + "undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="], "unicode-canonical-property-names-ecmascript": ["unicode-canonical-property-names-ecmascript@2.0.1", "", {}, "sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg=="], @@ -3447,20 +3447,28 @@ "@istanbuljs/load-nyc-config/js-yaml": ["js-yaml@3.14.1", "", { "dependencies": { "argparse": "^1.0.7", "esprima": "^4.0.0" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g=="], + "@jest/environment/@types/node": ["@types/node@24.7.1", "", { "dependencies": { "undici-types": "~7.14.0" } }, "sha512-CmyhGZanP88uuC5GpWU9q+fI61j2SkhO3UGMUdfYRE6Bcy0ccyzn1Rqj9YAB/ZY4kOXmNf0ocah5GtphmLMP6Q=="], + + "@jest/fake-timers/@types/node": ["@types/node@24.7.1", "", { "dependencies": { "undici-types": "~7.14.0" } }, "sha512-CmyhGZanP88uuC5GpWU9q+fI61j2SkhO3UGMUdfYRE6Bcy0ccyzn1Rqj9YAB/ZY4kOXmNf0ocah5GtphmLMP6Q=="], + "@jest/transform/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], "@jest/transform/slash": ["slash@3.0.0", "", {}, "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q=="], + "@jest/types/@types/node": ["@types/node@24.7.1", "", { "dependencies": { "undici-types": "~7.14.0" } }, "sha512-CmyhGZanP88uuC5GpWU9q+fI61j2SkhO3UGMUdfYRE6Bcy0ccyzn1Rqj9YAB/ZY4kOXmNf0ocah5GtphmLMP6Q=="], + "@jest/types/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], "@mermaid-js/mermaid-mindmap/@braintree/sanitize-url": ["@braintree/sanitize-url@6.0.4", "", {}, "sha512-s3jaWicZd0pkP0jf5ysyHUI/RE7MHos6qlToFcGWXVp+ykHOy77OUMrfbgJ9it2C5bow7OIQwYYaHjk9XlBQ2A=="], + "@nostr-dev-kit/cache-redis/@types/node": ["@types/node@24.7.1", "", { "dependencies": { "undici-types": "~7.14.0" } }, "sha512-CmyhGZanP88uuC5GpWU9q+fI61j2SkhO3UGMUdfYRE6Bcy0ccyzn1Rqj9YAB/ZY4kOXmNf0ocah5GtphmLMP6Q=="], + + "@nostr-dev-kit/messages/@types/node": ["@types/node@24.7.1", "", { "dependencies": { "undici-types": "~7.14.0" } }, "sha512-CmyhGZanP88uuC5GpWU9q+fI61j2SkhO3UGMUdfYRE6Bcy0ccyzn1Rqj9YAB/ZY4kOXmNf0ocah5GtphmLMP6Q=="], + "@nostr-dev-kit/svelte/@nostr-dev-kit/sessions": ["@nostr-dev-kit/sessions@0.6.3", "", { "dependencies": { "@nostr-dev-kit/ndk": "^2.17.11", "zustand": "^5.0.3" } }, "sha512-zRI+ReQPbBJcVMsTxQHwiz3KS4p1ms+c0vyxkWlrPxEpEjwv4oAhD+l9lsm1KXjzfMU4Iz3f7GB43wtCvVnd4A=="], "@nostr-dev-kit/svelte/@nostr-dev-kit/wallet": ["@nostr-dev-kit/wallet@0.8.10", "", { "dependencies": { "@nostr-dev-kit/ndk": "^2.18.0", "@nostr-dev-kit/sync": "^0.3.6", "debug": "^4.3.7", "light-bolt11-decoder": "^3.2.0", "tseep": "^1.3.1", "typescript": "^5.9.3", "webln": "^0.3.2", "zustand": "^5.0.3" }, "peerDependencies": { "@cashu/cashu-ts": "^2.1", "@cashu/crypto": "*" } }, "sha512-TZGQxPZjaN3pmAvsZ6FypdZrasVlIoYuW3UkkYOAyFQnuzXdQAa37FqeA+i6iP788EXFFrUXZssPtR+xcpJEMg=="], - "@nostr-dev-kit/svelte/@types/node": ["@types/node@24.10.1", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ=="], - "@nostr-dev-kit/svelte/vite": ["vite@7.2.4", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.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", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-NL8jTlbo0Tn4dUEXEsUg8KeyG/Lkmc4Fnzb8JXN/Ykm9G4HNImjtABMJgkQoVjOBN/j2WAwDTRytdqJbZsah7w=="], "@nostr-dev-kit/sync/vite": ["vite@7.2.4", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.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", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-NL8jTlbo0Tn4dUEXEsUg8KeyG/Lkmc4Fnzb8JXN/Ykm9G4HNImjtABMJgkQoVjOBN/j2WAwDTRytdqJbZsah7w=="], @@ -3509,6 +3517,12 @@ "@testing-library/react/react": ["react@18.3.1", "", { "dependencies": { "loose-envify": "^1.1.0" } }, "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ=="], + "@types/better-sqlite3/@types/node": ["@types/node@24.7.1", "", { "dependencies": { "undici-types": "~7.14.0" } }, "sha512-CmyhGZanP88uuC5GpWU9q+fI61j2SkhO3UGMUdfYRE6Bcy0ccyzn1Rqj9YAB/ZY4kOXmNf0ocah5GtphmLMP6Q=="], + + "@types/graceful-fs/@types/node": ["@types/node@24.7.1", "", { "dependencies": { "undici-types": "~7.14.0" } }, "sha512-CmyhGZanP88uuC5GpWU9q+fI61j2SkhO3UGMUdfYRE6Bcy0ccyzn1Rqj9YAB/ZY4kOXmNf0ocah5GtphmLMP6Q=="], + + "@types/sql.js/@types/node": ["@types/node@24.7.1", "", { "dependencies": { "undici-types": "~7.14.0" } }, "sha512-CmyhGZanP88uuC5GpWU9q+fI61j2SkhO3UGMUdfYRE6Bcy0ccyzn1Rqj9YAB/ZY4kOXmNf0ocah5GtphmLMP6Q=="], + "@typescript-eslint/eslint-plugin/ignore": ["ignore@7.0.5", "", {}, "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg=="], "@vitejs/plugin-vue/vite": ["vite@5.4.20", "", { "dependencies": { "esbuild": "^0.21.3", "postcss": "^8.4.43", "rollup": "^4.20.0" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || >=20.0.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.4.0" }, "optionalPeers": ["@types/node", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser"], "bin": { "vite": "bin/vite.js" } }, "sha512-j3lYzGC3P+B5Yfy/pfKNgVEg4+UtcIJcVRt2cDjIOmhLourAqPqf8P7acgxeiSgUB7E3p2P8/3gNIgDLpwzs4g=="], @@ -3539,6 +3553,10 @@ "camelcase-keys/camelcase": ["camelcase@5.3.1", "", {}, "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg=="], + "chrome-launcher/@types/node": ["@types/node@24.7.1", "", { "dependencies": { "undici-types": "~7.14.0" } }, "sha512-CmyhGZanP88uuC5GpWU9q+fI61j2SkhO3UGMUdfYRE6Bcy0ccyzn1Rqj9YAB/ZY4kOXmNf0ocah5GtphmLMP6Q=="], + + "chromium-edge-launcher/@types/node": ["@types/node@24.7.1", "", { "dependencies": { "undici-types": "~7.14.0" } }, "sha512-CmyhGZanP88uuC5GpWU9q+fI61j2SkhO3UGMUdfYRE6Bcy0ccyzn1Rqj9YAB/ZY4kOXmNf0ocah5GtphmLMP6Q=="], + "chromium-edge-launcher/mkdirp": ["mkdirp@1.0.4", "", { "bin": { "mkdirp": "bin/cmd.js" } }, "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw=="], "chromium-edge-launcher/rimraf": ["rimraf@3.0.2", "", { "dependencies": { "glob": "^7.1.3" }, "bin": { "rimraf": "bin.js" } }, "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA=="], @@ -3615,10 +3633,18 @@ "jest-diff/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], + "jest-environment-node/@types/node": ["@types/node@24.7.1", "", { "dependencies": { "undici-types": "~7.14.0" } }, "sha512-CmyhGZanP88uuC5GpWU9q+fI61j2SkhO3UGMUdfYRE6Bcy0ccyzn1Rqj9YAB/ZY4kOXmNf0ocah5GtphmLMP6Q=="], + + "jest-haste-map/@types/node": ["@types/node@24.7.1", "", { "dependencies": { "undici-types": "~7.14.0" } }, "sha512-CmyhGZanP88uuC5GpWU9q+fI61j2SkhO3UGMUdfYRE6Bcy0ccyzn1Rqj9YAB/ZY4kOXmNf0ocah5GtphmLMP6Q=="], + "jest-message-util/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], "jest-message-util/slash": ["slash@3.0.0", "", {}, "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q=="], + "jest-mock/@types/node": ["@types/node@24.7.1", "", { "dependencies": { "undici-types": "~7.14.0" } }, "sha512-CmyhGZanP88uuC5GpWU9q+fI61j2SkhO3UGMUdfYRE6Bcy0ccyzn1Rqj9YAB/ZY4kOXmNf0ocah5GtphmLMP6Q=="], + + "jest-util/@types/node": ["@types/node@24.7.1", "", { "dependencies": { "undici-types": "~7.14.0" } }, "sha512-CmyhGZanP88uuC5GpWU9q+fI61j2SkhO3UGMUdfYRE6Bcy0ccyzn1Rqj9YAB/ZY4kOXmNf0ocah5GtphmLMP6Q=="], + "jest-util/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], "jest-util/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], @@ -3627,6 +3653,8 @@ "jest-validate/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], + "jest-worker/@types/node": ["@types/node@24.7.1", "", { "dependencies": { "undici-types": "~7.14.0" } }, "sha512-CmyhGZanP88uuC5GpWU9q+fI61j2SkhO3UGMUdfYRE6Bcy0ccyzn1Rqj9YAB/ZY4kOXmNf0ocah5GtphmLMP6Q=="], + "jest-worker/supports-color": ["supports-color@8.1.1", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q=="], "jscodeshift/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], @@ -3887,18 +3915,26 @@ "@istanbuljs/load-nyc-config/js-yaml/argparse": ["argparse@1.0.10", "", { "dependencies": { "sprintf-js": "~1.0.2" } }, "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg=="], + "@jest/environment/@types/node/undici-types": ["undici-types@7.14.0", "", {}, "sha512-QQiYxHuyZ9gQUIrmPo3IA+hUl4KYk8uSA7cHrcKd/l3p1OTpZcM0Tbp9x7FAtXdAYhlasd60ncPpgu6ihG6TOA=="], + + "@jest/fake-timers/@types/node/undici-types": ["undici-types@7.14.0", "", {}, "sha512-QQiYxHuyZ9gQUIrmPo3IA+hUl4KYk8uSA7cHrcKd/l3p1OTpZcM0Tbp9x7FAtXdAYhlasd60ncPpgu6ihG6TOA=="], + "@jest/transform/chalk/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], + "@jest/types/@types/node/undici-types": ["undici-types@7.14.0", "", {}, "sha512-QQiYxHuyZ9gQUIrmPo3IA+hUl4KYk8uSA7cHrcKd/l3p1OTpZcM0Tbp9x7FAtXdAYhlasd60ncPpgu6ihG6TOA=="], + "@jest/types/chalk/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], + "@nostr-dev-kit/cache-redis/@types/node/undici-types": ["undici-types@7.14.0", "", {}, "sha512-QQiYxHuyZ9gQUIrmPo3IA+hUl4KYk8uSA7cHrcKd/l3p1OTpZcM0Tbp9x7FAtXdAYhlasd60ncPpgu6ihG6TOA=="], + + "@nostr-dev-kit/messages/@types/node/undici-types": ["undici-types@7.14.0", "", {}, "sha512-QQiYxHuyZ9gQUIrmPo3IA+hUl4KYk8uSA7cHrcKd/l3p1OTpZcM0Tbp9x7FAtXdAYhlasd60ncPpgu6ihG6TOA=="], + "@nostr-dev-kit/svelte/@nostr-dev-kit/sessions/@nostr-dev-kit/ndk": ["@nostr-dev-kit/ndk@2.18.1", "", { "dependencies": { "@codesandbox/sandpack-client": "^2.19.8", "@noble/curves": "^1.6.0", "@noble/hashes": "^1.5.0", "@noble/secp256k1": "^2.1.0", "@scure/base": "^1.1.9", "debug": "^4.3.7", "light-bolt11-decoder": "^3.2.0", "shiki": "^3.13.0", "tseep": "^1.3.1", "typescript-lru-cache": "^2.0.0" }, "peerDependencies": { "nostr-tools": "^2.17.0" } }, "sha512-LTXXheGfmyN1y8x+8v/Dmkx8YX7LqaoVk0DTSaigETB5RZsxw7dLBKK++kZd4DVIxtj0tRfmSOsTr1E+M4653Q=="], "@nostr-dev-kit/svelte/@nostr-dev-kit/wallet/@nostr-dev-kit/ndk": ["@nostr-dev-kit/ndk@2.18.1", "", { "dependencies": { "@codesandbox/sandpack-client": "^2.19.8", "@noble/curves": "^1.6.0", "@noble/hashes": "^1.5.0", "@noble/secp256k1": "^2.1.0", "@scure/base": "^1.1.9", "debug": "^4.3.7", "light-bolt11-decoder": "^3.2.0", "shiki": "^3.13.0", "tseep": "^1.3.1", "typescript-lru-cache": "^2.0.0" }, "peerDependencies": { "nostr-tools": "^2.17.0" } }, "sha512-LTXXheGfmyN1y8x+8v/Dmkx8YX7LqaoVk0DTSaigETB5RZsxw7dLBKK++kZd4DVIxtj0tRfmSOsTr1E+M4653Q=="], "@nostr-dev-kit/svelte/@nostr-dev-kit/wallet/@nostr-dev-kit/sync": ["@nostr-dev-kit/sync@0.3.6", "", { "dependencies": { "@nostr-dev-kit/ndk": "^2.18.0", "nostr-tools": "^2.17.0", "tseep": "^1.3.1" } }, "sha512-s2jKAYU1vy97hsBiYIv6fjtLkRtLRliYMyR7t1saXQim4Nbk2ozzErx+ptKkZxjX96AF6LOyAc/BHg07MTmfEg=="], - "@nostr-dev-kit/svelte/@types/node/undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="], - "@react-native/babel-plugin-codegen/@react-native/codegen/glob": ["glob@7.2.3", "", { "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } }, "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q=="], "@react-native/babel-plugin-codegen/@react-native/codegen/hermes-parser": ["hermes-parser@0.23.1", "", { "dependencies": { "hermes-estree": "0.23.1" } }, "sha512-oxl5h2DkFW83hT4DAUJorpah8ou4yvmweUzLJmmr6YV2cezduCdlil1AvU/a/xSsAFo4WUcNA4GoV5Bvq6JffA=="], @@ -3923,6 +3959,12 @@ "@testing-library/dom/pretty-format/react-is": ["react-is@17.0.2", "", {}, "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w=="], + "@types/better-sqlite3/@types/node/undici-types": ["undici-types@7.14.0", "", {}, "sha512-QQiYxHuyZ9gQUIrmPo3IA+hUl4KYk8uSA7cHrcKd/l3p1OTpZcM0Tbp9x7FAtXdAYhlasd60ncPpgu6ihG6TOA=="], + + "@types/graceful-fs/@types/node/undici-types": ["undici-types@7.14.0", "", {}, "sha512-QQiYxHuyZ9gQUIrmPo3IA+hUl4KYk8uSA7cHrcKd/l3p1OTpZcM0Tbp9x7FAtXdAYhlasd60ncPpgu6ihG6TOA=="], + + "@types/sql.js/@types/node/undici-types": ["undici-types@7.14.0", "", {}, "sha512-QQiYxHuyZ9gQUIrmPo3IA+hUl4KYk8uSA7cHrcKd/l3p1OTpZcM0Tbp9x7FAtXdAYhlasd60ncPpgu6ihG6TOA=="], + "@vitejs/plugin-vue/vite/esbuild": ["esbuild@0.21.5", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.21.5", "@esbuild/android-arm": "0.21.5", "@esbuild/android-arm64": "0.21.5", "@esbuild/android-x64": "0.21.5", "@esbuild/darwin-arm64": "0.21.5", "@esbuild/darwin-x64": "0.21.5", "@esbuild/freebsd-arm64": "0.21.5", "@esbuild/freebsd-x64": "0.21.5", "@esbuild/linux-arm": "0.21.5", "@esbuild/linux-arm64": "0.21.5", "@esbuild/linux-ia32": "0.21.5", "@esbuild/linux-loong64": "0.21.5", "@esbuild/linux-mips64el": "0.21.5", "@esbuild/linux-ppc64": "0.21.5", "@esbuild/linux-riscv64": "0.21.5", "@esbuild/linux-s390x": "0.21.5", "@esbuild/linux-x64": "0.21.5", "@esbuild/netbsd-x64": "0.21.5", "@esbuild/openbsd-x64": "0.21.5", "@esbuild/sunos-x64": "0.21.5", "@esbuild/win32-arm64": "0.21.5", "@esbuild/win32-ia32": "0.21.5", "@esbuild/win32-x64": "0.21.5" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw=="], "@vitest/browser/@testing-library/dom/aria-query": ["aria-query@5.3.0", "", { "dependencies": { "dequal": "^2.0.3" } }, "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A=="], @@ -3935,6 +3977,10 @@ "babel-plugin-istanbul/test-exclude/minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="], + "chrome-launcher/@types/node/undici-types": ["undici-types@7.14.0", "", {}, "sha512-QQiYxHuyZ9gQUIrmPo3IA+hUl4KYk8uSA7cHrcKd/l3p1OTpZcM0Tbp9x7FAtXdAYhlasd60ncPpgu6ihG6TOA=="], + + "chromium-edge-launcher/@types/node/undici-types": ["undici-types@7.14.0", "", {}, "sha512-QQiYxHuyZ9gQUIrmPo3IA+hUl4KYk8uSA7cHrcKd/l3p1OTpZcM0Tbp9x7FAtXdAYhlasd60ncPpgu6ihG6TOA=="], + "chromium-edge-launcher/rimraf/glob": ["glob@7.2.3", "", { "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } }, "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q=="], "cliui/string-width/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], @@ -4009,12 +4055,22 @@ "jest-diff/chalk/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], + "jest-environment-node/@types/node/undici-types": ["undici-types@7.14.0", "", {}, "sha512-QQiYxHuyZ9gQUIrmPo3IA+hUl4KYk8uSA7cHrcKd/l3p1OTpZcM0Tbp9x7FAtXdAYhlasd60ncPpgu6ihG6TOA=="], + + "jest-haste-map/@types/node/undici-types": ["undici-types@7.14.0", "", {}, "sha512-QQiYxHuyZ9gQUIrmPo3IA+hUl4KYk8uSA7cHrcKd/l3p1OTpZcM0Tbp9x7FAtXdAYhlasd60ncPpgu6ihG6TOA=="], + "jest-message-util/chalk/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], + "jest-mock/@types/node/undici-types": ["undici-types@7.14.0", "", {}, "sha512-QQiYxHuyZ9gQUIrmPo3IA+hUl4KYk8uSA7cHrcKd/l3p1OTpZcM0Tbp9x7FAtXdAYhlasd60ncPpgu6ihG6TOA=="], + + "jest-util/@types/node/undici-types": ["undici-types@7.14.0", "", {}, "sha512-QQiYxHuyZ9gQUIrmPo3IA+hUl4KYk8uSA7cHrcKd/l3p1OTpZcM0Tbp9x7FAtXdAYhlasd60ncPpgu6ihG6TOA=="], + "jest-util/chalk/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], "jest-validate/chalk/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], + "jest-worker/@types/node/undici-types": ["undici-types@7.14.0", "", {}, "sha512-QQiYxHuyZ9gQUIrmPo3IA+hUl4KYk8uSA7cHrcKd/l3p1OTpZcM0Tbp9x7FAtXdAYhlasd60ncPpgu6ihG6TOA=="], + "jscodeshift/chalk/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], "jscodeshift/write-file-atomic/signal-exit": ["signal-exit@3.0.7", "", {}, "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="], diff --git a/core/docs/fundamentals/subscribing.md b/core/docs/fundamentals/subscribing.md index bb31ffda1..5e4e3a3eb 100644 --- a/core/docs/fundamentals/subscribing.md +++ b/core/docs/fundamentals/subscribing.md @@ -73,3 +73,9 @@ from the cache or a relay. ### onEose Called when the subscription is closed. + +## Code Snippets + +More snippets and examples can be found in the [snippets directory](/docs/snippets.md). + + diff --git a/docs/snippets.md b/docs/snippets.md index c62f2aada..3aa65896d 100644 --- a/docs/snippets.md +++ b/docs/snippets.md @@ -9,6 +9,10 @@ Snippets are grouped by category. Some of them are listed in more than one categ +## Subscribing + + + ## Signers diff --git a/docs/snippets/subscribing.md b/docs/snippets/subscribing.md new file mode 100644 index 000000000..6827aa8d9 --- /dev/null +++ b/docs/snippets/subscribing.md @@ -0,0 +1,16 @@ +### Simple Subscribe + +<<< @/core/docs/snippets/subscribe.ts + +### Subscribe on relays + +<<< @/core/docs/snippets/subscribe_relayset.ts + +### Event Handler + +<<< @/core/docs/snippets/subscribe_event_handlers.ts + +### Attaching Handlers + +<<< @/core/docs/snippets/subscribe_event_attach.ts + From 4345717b7a08bd1340dd099fa2640de185161055 Mon Sep 17 00:00:00 2001 From: digitalbase Date: Fri, 21 Nov 2025 17:24:31 +0100 Subject: [PATCH 086/139] Reorganise fundamentals --- docs/.vitepress/config.mts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/.vitepress/config.mts b/docs/.vitepress/config.mts index 8a795b3f6..d47bc16c1 100644 --- a/docs/.vitepress/config.mts +++ b/docs/.vitepress/config.mts @@ -49,10 +49,10 @@ export default defineConfig({ collapsed: false, items: [ {text: "Events โœ…", link: "/core/docs/fundamentals/events"}, + {text: "Connecting โœ…", link: "/core/docs/fundamentals/connecting"}, + {text: "Publishing โœ…", link: "/core/docs/fundamentals/publishing"}, {text: "Subscribing โœ…", link: "/core/docs/fundamentals/subscribing"}, {text: "Signers โœ…", link: "/core/docs/fundamentals/signers"}, - {text: "Publishing โœ…", link: "/core/docs/fundamentals/publishing"}, - {text: "Connecting โœ…", link: "/core/docs/fundamentals/connecting"}, {text: "Zaps โŒ", link: "/core/docs/tutorial/zaps"}, {text: "Local-first โŒ", link: "/core/docs/tutorial/local-first"}, { From 8356e5b21ab6dba409102fe1b535666696361f6e Mon Sep 17 00:00:00 2001 From: digitalbase Date: Fri, 21 Nov 2025 17:24:45 +0100 Subject: [PATCH 087/139] Clean up connecting docs --- core/docs/fundamentals/connecting.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/core/docs/fundamentals/connecting.md b/core/docs/fundamentals/connecting.md index 5702c88fb..cfaedea6c 100644 --- a/core/docs/fundamentals/connecting.md +++ b/core/docs/fundamentals/connecting.md @@ -22,7 +22,9 @@ On connection changes NDK will emit ::: tip Because NOSTR is decentralized and comprised of thousands of relays, it's important to read up on the -advised ways of connecting to relays. We strongly advise you to use the "[Outbox Model](/core/fundamentals/connecting.html#outbox-model)" in addition or replacement of specifying explicit relays. +advised ways of connecting to relays. We advise you to use +the "[Outbox Model](/core/fundamentals/connecting.html#outbox-model)" +in addition or as a replacement for specifying explicit relays. ::: ### Specific Relays From e0a17e8eac9166fe63ec3679f8897881f56ed034 Mon Sep 17 00:00:00 2001 From: digitalbase Date: Fri, 21 Nov 2025 17:25:04 +0100 Subject: [PATCH 088/139] Remove publishing tutorial doc (already in publishing) but swap out the example --- core/docs/snippets/replace_event.ts | 23 +++++++++-------------- core/docs/tutorial/publishing.md | 26 -------------------------- 2 files changed, 9 insertions(+), 40 deletions(-) delete mode 100644 core/docs/tutorial/publishing.md diff --git a/core/docs/snippets/replace_event.ts b/core/docs/snippets/replace_event.ts index 7a62d932b..78ff76d68 100644 --- a/core/docs/snippets/replace_event.ts +++ b/core/docs/snippets/replace_event.ts @@ -1,17 +1,12 @@ -import NDK, { NDKEvent, NDKKind } from "@nostr-dev-kit/ndk"; +import NDK from "@nostr-dev-kit/ndk"; const ndk = new NDK(); -const event = new NDKEvent(ndk); -event.kind = NDKKind.Metadata; -event.content = JSON.stringify({ - name: "Johnny", - about: "I come from nowhere", -}); -// first publish -await event.publish(); +const existingEvent = await ndk.fetchEvent("574033c986bea1d7493738b46fec1bb98dd6a826391d6aa893137e89790027ec"); // fetch the event to replace -// this will republish/broadcast the same event -await event.publish(); - -// this will create a new event and publish it -await event.publishReplaceable(); +if (existingEvent) { + existingEvent.tags.push( + ["p", "fa984bd7dbb282f07e16e7ae87b26a2a7b9b90b7246a44771f0cf5ae58018f52"], // follow a new user + ); + existingEvent.publish(); // this will NOT work + existingEvent.publishReplaceable(); // this WILL work +} diff --git a/core/docs/tutorial/publishing.md b/core/docs/tutorial/publishing.md deleted file mode 100644 index a36735878..000000000 --- a/core/docs/tutorial/publishing.md +++ /dev/null @@ -1,26 +0,0 @@ -# Publishing Events - -## Optimistic publish lifecycle - -Read more about the [local-first](./local-first.md) mode of operation. - -## Publishing Replaceable Events - -Some events in Nostr allow for replacement. - -Kinds `0`, `3`, range `10000-19999`. - -Range `30000-39999` is parameterized replaceable events, which means that multiple events of the same kind under the -same pubkey can exist and are differentiated via their `d` tag. - -Since replaceable events depend on having a newer `created_at`, NDK provides a convenience method to reset `id`, `sig`, -and `created_at` to allow for easy replacement: `event.publishReplaceable()` - -```ts -const existingEvent = await ndk.fetchEvent({ kinds: [0], authors: []}); // fetch the event to replace -existingEvent.tags.push( - [ "p", "fa984bd7dbb282f07e16e7ae87b26a2a7b9b90b7246a44771f0cf5ae58018f52" ] // follow a new user -); -existingEvent.publish(); // this will NOT work -existingEvent.publishReplaceable(); // this WILL work -``` From 2f02b8019e802e174f8358f767580f64208bb264 Mon Sep 17 00:00:00 2001 From: digitalbase Date: Fri, 21 Nov 2025 17:47:44 +0100 Subject: [PATCH 089/139] Fold what's left behind (on event publication status) into publishing docs --- core/docs/fundamentals/publishing.md | 21 +++ core/docs/snippets/publish_failure.ts | 16 +++ core/docs/snippets/publish_to_relayset.ts | 10 ++ core/docs/snippets/publish_tracking.ts | 17 +++ core/snippets/event/publish-tracking.md | 158 ---------------------- docs/snippets/publishing.md | 14 +- 6 files changed, 77 insertions(+), 159 deletions(-) create mode 100644 core/docs/snippets/publish_failure.ts create mode 100644 core/docs/snippets/publish_to_relayset.ts create mode 100644 core/docs/snippets/publish_tracking.ts delete mode 100644 core/snippets/event/publish-tracking.md diff --git a/core/docs/fundamentals/publishing.md b/core/docs/fundamentals/publishing.md index 255ff4a2f..7148cf8b5 100644 --- a/core/docs/fundamentals/publishing.md +++ b/core/docs/fundamentals/publishing.md @@ -33,6 +33,27 @@ and `created_at` to allow for easy replacement: `event.publishReplaceable()` <<< @/core/docs/snippets/replace_event.ts +## Specifying Relays + +NDK will publish your event to the already connected relaySets. If you want to specify where to +publish that specific event to you can pass a `NDKRelaySet` to the publish method: + +<<< @/core/docs/snippets/publish_to_relayset.ts + +## Tracking Publication Status + +You can use the event.on `published` handler to keep track of where events are +published and the status of each publish attempt: + +<<< @/core/docs/snippets/publish_tracking.ts + +## Publication Failures + +When publishing to multiple relays, some may succeed while others fail. This can be handled +through the `event:publish-failed` handler + +<<< @/core/docs/snippets/publish_failure.ts + ## Event Status Properties - `event.publishedToRelays` - Array of relay URLs where the event was successfully published diff --git a/core/docs/snippets/publish_failure.ts b/core/docs/snippets/publish_failure.ts new file mode 100644 index 000000000..9703ef5fe --- /dev/null +++ b/core/docs/snippets/publish_failure.ts @@ -0,0 +1,16 @@ +import NDK, { NDKEvent, type NDKPublishError } from "@nostr-dev-kit/ndk"; + +const ndk = new NDK({ + explicitRelayUrls: ["wss://relay.damus.io", "wss://relay.nostr.band", "wss://nos.lol"], +}); + +await ndk.connect(); + +const event = new NDKEvent(ndk, { + kind: 1, + content: "Hello Nostr!", +}); + +ndk.on(`event:publish-failed`, (event: NDKEvent, error: NDKPublishError, relays: WebSocket["url"][]) => { + console.log("Event failed to publish on", relays, error); +}); diff --git a/core/docs/snippets/publish_to_relayset.ts b/core/docs/snippets/publish_to_relayset.ts new file mode 100644 index 000000000..1c4d23d1d --- /dev/null +++ b/core/docs/snippets/publish_to_relayset.ts @@ -0,0 +1,10 @@ +import NDK, { NDKEvent, NDKRelaySet } from "@nostr-dev-kit/ndk"; + +const ndk = new NDK(); + +const event = new NDKEvent(ndk); +event.kind = 1; +event.content = "Hello world"; + +const customRelaySet = NDKRelaySet.fromRelayUrls(["wss://relay.snort.social", "wss://relay.primal.net"], ndk); +await event.publish(customRelaySet); diff --git a/core/docs/snippets/publish_tracking.ts b/core/docs/snippets/publish_tracking.ts new file mode 100644 index 000000000..5aa51016d --- /dev/null +++ b/core/docs/snippets/publish_tracking.ts @@ -0,0 +1,17 @@ +import NDK, { NDKEvent, type NDKRelay, type NDKRelaySet } from "@nostr-dev-kit/ndk"; + +const ndk = new NDK({ + explicitRelayUrls: ["wss://relay.damus.io", "wss://relay.nostr.band", "wss://nos.lol"], +}); + +await ndk.connect(); + +const event = new NDKEvent(ndk, { + kind: 1, + content: "Hello Nostr!", +}); + +event.on("published", (data: { relaySet: NDKRelaySet; publishedToRelays: Set }) => { + // Get all relays where the event was successfully published + console.log("Published to:", data.publishedToRelays); +}); diff --git a/core/snippets/event/publish-tracking.md b/core/snippets/event/publish-tracking.md deleted file mode 100644 index 9dcc57ec9..000000000 --- a/core/snippets/event/publish-tracking.md +++ /dev/null @@ -1,158 +0,0 @@ -# Event Publish Tracking - -NDK provides comprehensive tracking of where events are published and the status of each publish attempt. - -## Basic Usage - -When you publish an event, NDK tracks the status of each relay: - -```typescript -import NDK from "@nostr-dev-kit/ndk"; - -const ndk = new NDK({ - explicitRelayUrls: ["wss://relay.damus.io", "wss://relay.nostr.band", "wss://nos.lol"], -}); - -await ndk.connect(); - -const event = new NDKEvent(ndk, { - kind: 1, - content: "Hello Nostr!", -}); - -try { - await event.publish(); - - // Get all relays where the event was successfully published - console.log("Published to:", event.publishedToRelays); - // Output: ["wss://relay.damus.io", "wss://nos.lol"] - - // Check if published to a specific relay - if (event.wasPublishedTo("wss://relay.damus.io")) { - console.log("Successfully published to Damus relay"); - } -} catch (error) { - // Even if publish fails, you can see which relays succeeded - console.log("Published to:", event.publishedToRelays); - - // Get failed relays and their errors - const failures = event.failedPublishesToRelays; - for (const [relay, error] of failures) { - console.error(`Failed to publish to ${relay}:`, error.message); - } -} -``` - -## Detailed Status Information - -Each relay has detailed status information including timestamps: - -```typescript -// Get detailed status for all relays -for (const [relayUrl, status] of event.publishRelayStatus) { - console.log(`Relay: ${relayUrl}`); - console.log(` Status: ${status.status}`); // "pending", "success", or "error" - console.log(` Timestamp: ${new Date(status.timestamp)}`); - if (status.error) { - console.log(` Error: ${status.error.message}`); - } -} -``` - -## Handling Partial Failures - -When publishing to multiple relays, some may succeed while others fail: - -```typescript -try { - // Require at least 2 relays to receive the event - await event.publish(undefined, 5000, 2); -} catch (error) { - if (error instanceof NDKPublishError) { - console.log("Published to", error.publishedToRelays.size, "relays"); - console.log("Failed on", error.errors.size, "relays"); - - // The event object still tracks all statuses - console.log("Successful relays:", event.publishedToRelays); - console.log("Failed relays:", Array.from(event.failedPublishesToRelays.keys())); - } -} -``` - -## Custom Relay Sets - -You can publish to specific relay sets and track their status: - -```typescript -const customRelaySet = NDKRelaySet.fromRelayUrls( - ["wss://relay.snort.social", "wss://relay.primal.net"], - ndk, -); - -await event.publish(customRelaySet); - -// Check which of the custom relays received the event -for (const relayUrl of customRelaySet.relayUrls) { - if (event.wasPublishedTo(relayUrl)) { - console.log(`โœ“ Published to ${relayUrl}`); - } else { - console.log(`โœ— Failed to publish to ${relayUrl}`); - } -} -``` - -## Republishing Events - -When republishing an event, the relay status is cleared and updated: - -```typescript -// First publish attempt -await event.publish(); -console.log("First publish:", event.publishedToRelays); - -// Republish to different relays -const newRelaySet = NDKRelaySet.fromRelayUrls(["wss://relay.nostr.bg", "wss://nostr.wine"], ndk); - -await event.publish(newRelaySet); -console.log("Second publish:", event.publishedToRelays); -// Only shows relays from the second publish -``` - -## Monitoring Relay Performance - -You can use publish tracking to monitor relay performance: - -```typescript -const publishStats = new Map(); - -// Track multiple publishes -for (const event of events) { - await event.publish(); - - for (const [relay, status] of event.publishRelayStatus) { - const stats = publishStats.get(relay) || { success: 0, failure: 0 }; - if (status.status === "success") { - stats.success++; - } else if (status.status === "error") { - stats.failure++; - } - publishStats.set(relay, stats); - } -} - -// Analyze relay performance -for (const [relay, stats] of publishStats) { - const total = stats.success + stats.failure; - const successRate = ((stats.success / total) * 100).toFixed(1); - console.log(`${relay}: ${successRate}% success rate`); -} -``` - -## Event Status Properties - -- `event.publishedToRelays` - Array of relay URLs where the event was successfully published -- `event.failedPublishesToRelays` - Map of relay URLs to their errors -- `event.publishRelayStatus` - Map of all relay URLs to their detailed status -- `event.wasPublishedTo(url)` - Check if successfully published to a specific relay -- `event.publishStatus` - Overall status: "pending", "success", or "error" -- `event.publishError` - Error if the overall publish failed diff --git a/docs/snippets/publishing.md b/docs/snippets/publishing.md index 9d44bfda4..05337803f 100644 --- a/docs/snippets/publishing.md +++ b/docs/snippets/publishing.md @@ -4,4 +4,16 @@ ### Replaceable Event -<<< @/core/docs/snippets/replace_event.ts \ No newline at end of file +<<< @/core/docs/snippets/replace_event.ts + +### Specify Relays + +<<< @/core/docs/snippets/publish_to_relayset.ts + +### Track Publication Status + +<<< @/core/docs/snippets/publish_tracking.ts + +### Handling Failures + +<<< @/core/docs/snippets/publish_failure.ts \ No newline at end of file From 2c6203571a7d4354f76e030e558e97f64af825c7 Mon Sep 17 00:00:00 2001 From: digitalbase Date: Fri, 21 Nov 2025 18:01:35 +0100 Subject: [PATCH 090/139] Do not relist snippets in docs page but just have a section 'snippets' that links to the index --- core/docs/fundamentals/connecting.md | 24 ++++---------- core/docs/fundamentals/events.md | 4 +-- core/docs/fundamentals/publishing.md | 4 +-- core/docs/fundamentals/signers.md | 44 +++++-------------------- core/docs/fundamentals/subscribing.md | 5 ++- core/docs/snippets/connection_events.ts | 19 +++++++++++ core/docs/snippets/sign_event.ts | 2 +- core/docs/snippets/sign_event_nsec.ts | 9 +++++ docs/snippets.md | 7 ++-- docs/snippets/connecting.md | 6 +++- docs/snippets/signers.md | 12 ++++--- 11 files changed, 65 insertions(+), 71 deletions(-) create mode 100644 core/docs/snippets/connection_events.ts create mode 100644 core/docs/snippets/sign_event_nsec.ts diff --git a/core/docs/fundamentals/connecting.md b/core/docs/fundamentals/connecting.md index cfaedea6c..3bf40afd3 100644 --- a/core/docs/fundamentals/connecting.md +++ b/core/docs/fundamentals/connecting.md @@ -94,23 +94,11 @@ relays. ## Connection Events -```typescript -// Main pool events -ndk.pool.on("relay:connecting", (relay: NDKRelay) => { - console.log(`โŸณ [Main Pool] Connecting to relay: ${relay.url}`); -}); - -ndk.pool.on("relay:connect", (relay: NDKRelay) => { - connectedRelays.add(relay.url); - console.log(`โœ“ [Main Pool] Connected to relay: ${relay.url}`); - console.log(`Total connected relays: ${connectedRelays.size}`); -}); - -ndk.pool.on("relay:disconnect", (relay: NDKRelay) => { - connectedRelays.delete(relay.url); - console.log(`โœ— [Main Pool] Disconnected from relay: ${relay.url}`); - console.log(`Total connected relays: ${connectedRelays.size}`); -}); -``` +There are a number of events you can hook into to get information about relay connection +status + +<<< @/core/docs/snippets/connection_events.ts +## Code Snippets +More snippets and examples can be found in the [snippets directory](/docs/snippets.md#connecting) \ No newline at end of file diff --git a/core/docs/fundamentals/events.md b/core/docs/fundamentals/events.md index 14695bbac..358eadc2e 100644 --- a/core/docs/fundamentals/events.md +++ b/core/docs/fundamentals/events.md @@ -42,6 +42,4 @@ Read more about signers in [the signer documentation](/core/docs/fundamentals/si ## Code Snippets -More snippets and examples can be found in the [snippets directory](/docs/snippets.md). - - +More snippets and examples can be found in the [snippets directory](/docs/snippets.md#events). diff --git a/core/docs/fundamentals/publishing.md b/core/docs/fundamentals/publishing.md index 7148cf8b5..42d802835 100644 --- a/core/docs/fundamentals/publishing.md +++ b/core/docs/fundamentals/publishing.md @@ -65,6 +65,4 @@ through the `event:publish-failed` handler ## Code Snippets -More snippets and examples can be found in the [snippets directory](/docs/snippets.md). - - +More snippets and examples can be found in the [snippets directory](/docs/snippets.md#publishing) \ No newline at end of file diff --git a/core/docs/fundamentals/signers.md b/core/docs/fundamentals/signers.md index 0117b0aa1..3601daa66 100644 --- a/core/docs/fundamentals/signers.md +++ b/core/docs/fundamentals/signers.md @@ -6,7 +6,10 @@ All events on the Nostr protocol are signed through a keypair In NDK this is taken care of by the `NDKSigner` interface that can be passed in during initialization or later during runtime. -## Different Signing Methods +## Signing Methods + +Before you can sign events you need a signer set-up. There are different ways to sign events and this space is still +evolving. ### Browser Extensions @@ -17,13 +20,7 @@ application. The most used browser extensions are [Nos2x](https://github.com/fiatjaf/nos2x) and [Alby](https://getalby.com/alby-extension). -```ts -// Import the package, NIP-07 signer and NDK event -import NDK, { NDKEvent, NDKNip07Signer } from "@nostr-dev-kit/ndk"; - -const signer = new NDKNip07Signer(); -const ndk = new NDK({ signer }); -``` +<<< @/core/docs/snippets/sign_event.ts Anytime you call `sign()` or `publish()` on an [NDK Event](/core/docs/fundamentals/events.html) the browser extension will prompt the user to sign the event. @@ -39,18 +36,10 @@ NDK provides `NDKPrivateKeySigner` for managing in-memory private keys. This is The private key signer takes the private key in the `nsec` format. -```ts -import { NDKPrivateKeySigner } from "@nostr-dev-kit/ndk"; - -// Generate a new private key -const signer = NDKPrivateKeySigner.generate(); -console.log("nsec:", signer.nsec); -console.log("npub:", signer.npub); -``` +<<< @/core/docs/snippets/sign_event_nsec.ts This library can also [help with generating new keys](/core/docs/fundamentals/signers.html#generate-keys). - ### Remote Signer A Nostr remote signer (aka `bunker`) is an application or device that securely stores your private key and signs Nostr @@ -73,27 +62,13 @@ Create a `NDKNip46Signer` with the bunker connection string and local keypair. Once the signer is initialized, you can use it to sign and [publish](/core/docs/fundamentals/publishing.html) events: -```ts -const event = new NDKEvent(ndk); -event.kind = 1; -event.content = "Hello, world!"; -await event.sign(); // [!code focus] -``` +<<< @/core/docs/snippets/sign_event.ts ## Signer Relays If the [signer](/core/docs/fundamentals/signers.md) implements the `getRelays()` method, NDK will use the relays returned by that method as the explicit relays. -```ts -// Import the package -import NDK, {NDKNip07Signer} from "@nostr-dev-kit/ndk"; - -// Create a new NDK instance with just a signer (provided the signer implements the getRelays() method) -const nip07signer = new NDKNip07Signer(); -const ndk = new NDK({signer: nip07signer}); -``` - ## Combining signers You can specify the use of a different signer to sign with different keys. @@ -122,9 +97,8 @@ provides helper methods: <<< @/core/docs/snippets/key_create.ts -## Code Snippets -More snippets and examples can be found in the [snippets directory](/docs/snippets.md). +## Code Snippets - +More snippets and examples can be found in the [snippets directory](/docs/snippets.md#signers) diff --git a/core/docs/fundamentals/subscribing.md b/core/docs/fundamentals/subscribing.md index 5e4e3a3eb..e7f1e3f07 100644 --- a/core/docs/fundamentals/subscribing.md +++ b/core/docs/fundamentals/subscribing.md @@ -74,8 +74,7 @@ from the cache or a relay. Called when the subscription is closed. -## Code Snippets -More snippets and examples can be found in the [snippets directory](/docs/snippets.md). +## Code Snippets - +More snippets and examples can be found in the [snippets directory](/docs/snippets.md#subscribing) diff --git a/core/docs/snippets/connection_events.ts b/core/docs/snippets/connection_events.ts new file mode 100644 index 000000000..6a74c57fd --- /dev/null +++ b/core/docs/snippets/connection_events.ts @@ -0,0 +1,19 @@ +import NDK, { type NDKRelay } from "@nostr-dev-kit/ndk"; + +// Create a new NDK instance with explicit relays +const ndk = new NDK({ + explicitRelayUrls: ["wss://a.relay", "wss://another.relay"], +}); + +// Main pool events +ndk.pool.on("relay:connecting", (relay: NDKRelay) => { + console.log(`โŸณ [Main Pool] Connecting to relay: ${relay.url}`); +}); + +ndk.pool.on("relay:connect", (relay: NDKRelay) => { + console.log(`โœ“ [Main Pool] Connected to relay: ${relay.url}`); +}); + +ndk.pool.on("relay:disconnect", (relay: NDKRelay) => { + console.log(`โœ— [Main Pool] Disconnected from relay: ${relay.url}`); +}); diff --git a/core/docs/snippets/sign_event.ts b/core/docs/snippets/sign_event.ts index c53073de0..efc8def1a 100644 --- a/core/docs/snippets/sign_event.ts +++ b/core/docs/snippets/sign_event.ts @@ -6,4 +6,4 @@ const ndk = new NDK({ signer: nip07signer }); const event = new NDKEvent(ndk); event.kind = 1; event.content = "Hello world"; -await event.sign(); // [!code focus] +await event.sign(); diff --git a/core/docs/snippets/sign_event_nsec.ts b/core/docs/snippets/sign_event_nsec.ts new file mode 100644 index 000000000..632b142ef --- /dev/null +++ b/core/docs/snippets/sign_event_nsec.ts @@ -0,0 +1,9 @@ +import NDK, { NDKEvent, NDKPrivateKeySigner } from "@nostr-dev-kit/ndk"; + +const privateKeySigner = NDKPrivateKeySigner.generate(); +const ndk = new NDK({ signer: privateKeySigner }); + +const event = new NDKEvent(ndk); +event.kind = 1; +event.content = "Hello world"; +await event.sign(); diff --git a/docs/snippets.md b/docs/snippets.md index 3aa65896d..f24d07271 100644 --- a/docs/snippets.md +++ b/docs/snippets.md @@ -9,6 +9,10 @@ Snippets are grouped by category. Some of them are listed in more than one categ +## Connecting + + + ## Subscribing @@ -21,9 +25,6 @@ Snippets are grouped by category. Some of them are listed in more than one categ -## Connecting - - ## Not migrated yet diff --git a/docs/snippets/connecting.md b/docs/snippets/connecting.md index 7e36e167a..cd372a43e 100644 --- a/docs/snippets/connecting.md +++ b/docs/snippets/connecting.md @@ -16,4 +16,8 @@ ### Relay Pools -<<< @/core/docs/snippets/connect_pools.ts \ No newline at end of file +<<< @/core/docs/snippets/connect_pools.ts + +### Connection Events + +<<< @/core/docs/snippets/connection_events.ts diff --git a/docs/snippets/signers.md b/docs/snippets/signers.md index 07c6ac438..0dc1558f8 100644 --- a/docs/snippets/signers.md +++ b/docs/snippets/signers.md @@ -2,14 +2,18 @@ <<< @/core/docs/snippets/sign_event.ts -### Different signers +### Sign with NSec -<<< @/core/docs/snippets/sign_event_with_other_signers.ts +<<< @/core/docs/snippets/sign_event_nsec.ts -### Generate Keypair +### Different signers -<<< @/core/docs/snippets/key_create.ts +<<< @/core/docs/snippets/sign_event_with_other_signers.ts ### Sign using bunker <<< @/core/docs/snippets/sign_with_bunker.ts + +### Generate Keypair + +<<< @/core/docs/snippets/key_create.ts \ No newline at end of file From c4eb5b72aadca7c208962500fd3736aef626e4a6 Mon Sep 17 00:00:00 2001 From: digitalbase Date: Sun, 23 Nov 2025 12:54:54 +0100 Subject: [PATCH 091/139] Move exclusive relays docs + hinted from fundamtentals/subscriptions --- core/docs/advanced/exclusive-relay.md | 211 ++++++++++++++++ core/docs/fundamentals/subscribing.md | 17 ++ .../snippets/subscribe_relay_targetting.ts | 18 ++ core/snippets/subscription/exclusive-relay.md | 232 ------------------ docs/.vitepress/config.mts | 4 + 5 files changed, 250 insertions(+), 232 deletions(-) create mode 100644 core/docs/advanced/exclusive-relay.md create mode 100644 core/docs/snippets/subscribe_relay_targetting.ts delete mode 100644 core/snippets/subscription/exclusive-relay.md diff --git a/core/docs/advanced/exclusive-relay.md b/core/docs/advanced/exclusive-relay.md new file mode 100644 index 000000000..a6b573a39 --- /dev/null +++ b/core/docs/advanced/exclusive-relay.md @@ -0,0 +1,211 @@ +# Exclusive Relay Subscriptions + +By default, [NDK subscriptions](/core/docs/fundamentals/subscribing.md) use cross-subscription matching: when an event +comes in from any relay, it's delivered to +all subscriptions whose filters match, regardless of which relays the subscription was targeting. + +The `exclusiveRelay` option allows you to create subscriptions that **only** accept events from their specified relays, +ignoring events that match the filter but come from other relays. + +## Basic Usage + +<<< @/core/docs/snippets/subscribe_relay_targetting.ts + +## Default Behavior (Cross-Subscription Matching) + +Without `exclusiveRelay`, subscriptions receive events from any relay: + +```typescript +// Default behavior - accepts events from ANY relay +const normalSub = ndk.subscribe( + {kinds: [1], authors: ['pubkey...']}, + { + relayUrls: ['wss://relay-a.com'], + exclusiveRelay: false // or omit (default) + } +); + +normalSub.on('event', (event) => { + // This fires for events from relay-a.com, relay-b.com, relay-c.com + // or any other relay, as long as the filter matches +}); +``` + +## Use Cases + +### 1. Relay-Specific Data Fetching + +Fetch events exclusively from a specific relay: + +```typescript +// Only get events from a specific community relay +const communitySub = ndk.subscribe( + {kinds: [1], '#t': ['community']}, + { + relayUrls: ['wss://community-relay.example.com'], + exclusiveRelay: true + } +); +``` + +### 2. Relay Isolation Testing + +Test relay-specific behavior: + +```typescript +// Test what a specific relay returns +const testSub = ndk.subscribe( + {kinds: [1], limit: 10}, + { + relayUrls: ['wss://test-relay.com'], + exclusiveRelay: true, + closeOnEose: true + } +); + +testSub.on('eose', () => { + console.log('Finished fetching from test-relay.com'); +}); +``` + +### 3. Relay-Based Routing + +Route events based on relay provenance: + +```typescript +const publicRelaySub = ndk.subscribe( + {kinds: [1]}, + { + relayUrls: ['wss://public-relay.com'], + exclusiveRelay: true + } +); + +const privateRelaySub = ndk.subscribe( + {kinds: [1]}, + { + relayUrls: ['wss://private-relay.com'], + exclusiveRelay: true + } +); + +publicRelaySub.on('event', (event) => { + console.log('Public event:', event.content); +}); + +privateRelaySub.on('event', (event) => { + console.log('Private event:', event.content); +}); +``` + +## Using NDKRelaySet + +You can also use `NDKRelaySet` with `exclusiveRelay`: + +```typescript +import {NDKRelaySet} from '@nostr-dev-kit/ndk'; + +const relaySet = NDKRelaySet.fromRelayUrls( + ['wss://relay-a.com', 'wss://relay-b.com'], + ndk +); + +const sub = ndk.subscribe( + {kinds: [1]}, + { + relaySet, + exclusiveRelay: true + } +); + +// Only receives events from relay-a.com or relay-b.com +``` + +## Edge Cases + +### Cached Events + +Cached events are checked against their known relay provenance. If a cached event was previously seen on a relay in your +exclusive relaySet, it will be delivered: + +```typescript +const sub = ndk.subscribe( + {kinds: [1]}, + { + relayUrls: ['wss://relay-a.com'], + exclusiveRelay: true, + cacheUsage: NDKSubscriptionCacheUsage.CACHE_FIRST + } +); + +// Cached events that came from relay-a.com: โœ… Delivered +// Cached events from other relays: โŒ Rejected +``` + +### Optimistic Publishes + +Optimistic publishes (local events before relay confirmation) respect the `skipOptimisticPublishEvent` setting: + +```typescript +const sub = ndk.subscribe( + {kinds: [1]}, + { + relayUrls: ['wss://relay-a.com'], + exclusiveRelay: true, + skipOptimisticPublishEvent: false // Accept optimistic publishes + } +); + +// Optimistic publishes: โœ… Delivered (if skipOptimisticPublishEvent is false) +``` + +### No RelaySet Specified + +If `exclusiveRelay: true` but no `relaySet` or `relayUrls` is specified, the check is not applied: + +```typescript +const sub = ndk.subscribe( + {kinds: [1]}, + { + exclusiveRelay: true // Has no effect without relaySet/relayUrls + } +); + +// Behaves like a normal subscription - accepts events from any relay +``` + +## Combining Exclusive and Non-Exclusive Subscriptions + +You can mix exclusive and non-exclusive subscriptions in the same NDK instance: + +```typescript +// Exclusive subscription - only relay-a.com +const exclusiveSub = ndk.subscribe( + {kinds: [1], '#t': ['exclusive']}, + { + relayUrls: ['wss://relay-a.com'], + exclusiveRelay: true + } +); + +// Non-exclusive subscription - any relay +const globalSub = ndk.subscribe( + {kinds: [1], '#t': ['global']}, + { + exclusiveRelay: false + } +); + +// exclusiveSub: Only gets #t=exclusive events from relay-a.com +// globalSub: Gets #t=global events from any connected relay +``` + +## Performance Considerations + +The `exclusiveRelay` check happens after filter matching, so there's minimal performance impact. The check only applies +to subscriptions that have both: + +- `exclusiveRelay: true` +- A specified `relaySet` or `relayUrls` + +All other subscriptions skip the relay provenance check entirely. diff --git a/core/docs/fundamentals/subscribing.md b/core/docs/fundamentals/subscribing.md index e7f1e3f07..ea0ea1a5d 100644 --- a/core/docs/fundamentals/subscribing.md +++ b/core/docs/fundamentals/subscribing.md @@ -21,6 +21,9 @@ override this behavior by providing explicit relays in the `relayUrls` or `relay <<< @/core/docs/snippets/subscribe_relayset.ts +By default, NDK subscriptions use cross-subscription matching: when an event comes in from any relay, it's delivered to +all subscriptions whose filters match, regardless of which relays the subscription was targeting. + ## Event Handlers ### Handler Functions @@ -74,6 +77,20 @@ from the cache or a relay. Called when the subscription is closed. +## Targetting Relays + +By default, NDK subscriptions use cross-subscription matching: when an event comes in from any relay, it's delivered to +all subscriptions whose filters match, regardless of which relays the subscription was targeting. + +The `exclusiveRelay` option allows you to create subscriptions that **only** accept events from their specified relays, +ignoring events that match the filter but come from other relays. + +<<< @/core/docs/snippets/subscribe_relay_targetting.ts + +Without `exclusiveRelay`, subscriptions receive events from any relay (Cross-Subscription Matching). + +More information, use-cases and examples of exclusive relays is available in +the [advanced exclusive relay documentation](/core/docs/advanced/exclusive-relay.md#subscribing). ## Code Snippets diff --git a/core/docs/snippets/subscribe_relay_targetting.ts b/core/docs/snippets/subscribe_relay_targetting.ts new file mode 100644 index 000000000..67e67b9c0 --- /dev/null +++ b/core/docs/snippets/subscribe_relay_targetting.ts @@ -0,0 +1,18 @@ +import NDK from "@nostr-dev-kit/ndk"; + +const ndk = new NDK(); + +// Subscription that ONLY accepts events from relay-a.com +const exclusiveSub = ndk.subscribe( + {kinds: [7]}, + { + relayUrls: ["wss://relay-a.com"], + exclusiveRelay: true, // ๐Ÿ”‘ Key option + }, +); + +exclusiveSub.on("event", (event) => { + console.log("Event from relay-a.com:", event.content); + // This will ONLY fire for events from relay-a.com + // Events from relay-b.com or relay-c.com are rejected +}); diff --git a/core/snippets/subscription/exclusive-relay.md b/core/snippets/subscription/exclusive-relay.md deleted file mode 100644 index 2013b9e1e..000000000 --- a/core/snippets/subscription/exclusive-relay.md +++ /dev/null @@ -1,232 +0,0 @@ -# Exclusive Relay Subscriptions - -By default, NDK subscriptions use cross-subscription matching: when an event comes in from any relay, it's delivered to all subscriptions whose filters match, regardless of which relays the subscription was targeting. - -The `exclusiveRelay` option allows you to create subscriptions that **only** accept events from their specified relays, ignoring events that match the filter but come from other relays. - -## Basic Usage - -```typescript -import NDK from '@nostr-dev-kit/ndk'; - -const ndk = new NDK({ - explicitRelayUrls: [ - 'wss://relay-a.com', - 'wss://relay-b.com', - 'wss://relay-c.com' - ] -}); - -await ndk.connect(); - -// Subscription that ONLY accepts events from relay-a.com -const exclusiveSub = ndk.subscribe( - { kinds: [1], authors: ['pubkey...'] }, - { - relayUrls: ['wss://relay-a.com'], - exclusiveRelay: true // ๐Ÿ”‘ Key option - } -); - -exclusiveSub.on('event', (event) => { - console.log('Event from relay-a.com:', event.content); - // This will ONLY fire for events from relay-a.com - // Events from relay-b.com or relay-c.com are rejected -}); -``` - -## Default Behavior (Cross-Subscription Matching) - -Without `exclusiveRelay`, subscriptions receive events from any relay: - -```typescript -// Default behavior - accepts events from ANY relay -const normalSub = ndk.subscribe( - { kinds: [1], authors: ['pubkey...'] }, - { - relayUrls: ['wss://relay-a.com'], - exclusiveRelay: false // or omit (default) - } -); - -normalSub.on('event', (event) => { - // This fires for events from relay-a.com, relay-b.com, relay-c.com - // or any other relay, as long as the filter matches -}); -``` - -## Use Cases - -### 1. Relay-Specific Data Fetching - -Fetch events exclusively from a specific relay: - -```typescript -// Only get events from a specific community relay -const communitySub = ndk.subscribe( - { kinds: [1], '#t': ['community'] }, - { - relayUrls: ['wss://community-relay.example.com'], - exclusiveRelay: true - } -); -``` - -### 2. Relay Isolation Testing - -Test relay-specific behavior: - -```typescript -// Test what a specific relay returns -const testSub = ndk.subscribe( - { kinds: [1], limit: 10 }, - { - relayUrls: ['wss://test-relay.com'], - exclusiveRelay: true, - closeOnEose: true - } -); - -testSub.on('eose', () => { - console.log('Finished fetching from test-relay.com'); -}); -``` - -### 3. Relay-Based Routing - -Route events based on relay provenance: - -```typescript -const publicRelaySub = ndk.subscribe( - { kinds: [1] }, - { - relayUrls: ['wss://public-relay.com'], - exclusiveRelay: true - } -); - -const privateRelaySub = ndk.subscribe( - { kinds: [1] }, - { - relayUrls: ['wss://private-relay.com'], - exclusiveRelay: true - } -); - -publicRelaySub.on('event', (event) => { - console.log('Public event:', event.content); -}); - -privateRelaySub.on('event', (event) => { - console.log('Private event:', event.content); -}); -``` - -## Using NDKRelaySet - -You can also use `NDKRelaySet` with `exclusiveRelay`: - -```typescript -import { NDKRelaySet } from '@nostr-dev-kit/ndk'; - -const relaySet = NDKRelaySet.fromRelayUrls( - ['wss://relay-a.com', 'wss://relay-b.com'], - ndk -); - -const sub = ndk.subscribe( - { kinds: [1] }, - { - relaySet, - exclusiveRelay: true - } -); - -// Only receives events from relay-a.com or relay-b.com -``` - -## Edge Cases - -### Cached Events - -Cached events are checked against their known relay provenance. If a cached event was previously seen on a relay in your exclusive relaySet, it will be delivered: - -```typescript -const sub = ndk.subscribe( - { kinds: [1] }, - { - relayUrls: ['wss://relay-a.com'], - exclusiveRelay: true, - cacheUsage: NDKSubscriptionCacheUsage.CACHE_FIRST - } -); - -// Cached events that came from relay-a.com: โœ… Delivered -// Cached events from other relays: โŒ Rejected -``` - -### Optimistic Publishes - -Optimistic publishes (local events before relay confirmation) respect the `skipOptimisticPublishEvent` setting: - -```typescript -const sub = ndk.subscribe( - { kinds: [1] }, - { - relayUrls: ['wss://relay-a.com'], - exclusiveRelay: true, - skipOptimisticPublishEvent: false // Accept optimistic publishes - } -); - -// Optimistic publishes: โœ… Delivered (if skipOptimisticPublishEvent is false) -``` - -### No RelaySet Specified - -If `exclusiveRelay: true` but no `relaySet` or `relayUrls` is specified, the check is not applied: - -```typescript -const sub = ndk.subscribe( - { kinds: [1] }, - { - exclusiveRelay: true // Has no effect without relaySet/relayUrls - } -); - -// Behaves like a normal subscription - accepts events from any relay -``` - -## Combining Exclusive and Non-Exclusive Subscriptions - -You can mix exclusive and non-exclusive subscriptions in the same NDK instance: - -```typescript -// Exclusive subscription - only relay-a.com -const exclusiveSub = ndk.subscribe( - { kinds: [1], '#t': ['exclusive'] }, - { - relayUrls: ['wss://relay-a.com'], - exclusiveRelay: true - } -); - -// Non-exclusive subscription - any relay -const globalSub = ndk.subscribe( - { kinds: [1], '#t': ['global'] }, - { - exclusiveRelay: false - } -); - -// exclusiveSub: Only gets #t=exclusive events from relay-a.com -// globalSub: Gets #t=global events from any connected relay -``` - -## Performance Considerations - -The `exclusiveRelay` check happens after filter matching, so there's minimal performance impact. The check only applies to subscriptions that have both: -- `exclusiveRelay: true` -- A specified `relaySet` or `relayUrls` - -All other subscriptions skip the relay provenance check entirely. diff --git a/docs/.vitepress/config.mts b/docs/.vitepress/config.mts index d47bc16c1..48f7f3cae 100644 --- a/docs/.vitepress/config.mts +++ b/docs/.vitepress/config.mts @@ -158,6 +158,10 @@ export default defineConfig({ text: "AI Guardrails โœ…", link: "/core/docs/advanced/ai-guardrails" }, + { + text: "Exclusive Relays โœ…", + link: "/core/docs/advanced/exclusive-relay" + }, { text: "Subscription Lifecycle โŒ", link: "/core/docs/advanced/subscription-internals" From 15f2ccc513bd89b815942c279449dc38de9c2013 Mon Sep 17 00:00:00 2001 From: digitalbase Date: Sun, 23 Nov 2025 12:55:30 +0100 Subject: [PATCH 092/139] Add snippet --- docs/snippets/subscribing.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/snippets/subscribing.md b/docs/snippets/subscribing.md index 6827aa8d9..3cd55928d 100644 --- a/docs/snippets/subscribing.md +++ b/docs/snippets/subscribing.md @@ -14,3 +14,6 @@ <<< @/core/docs/snippets/subscribe_event_attach.ts +### Exclusive Relays + +<<< @/core/docs/snippets/subscribe_relay_targetting.ts From cbbde83ef018feeb7b1b809c16fe5e06ce7fc9c9 Mon Sep 17 00:00:00 2001 From: digitalbase Date: Sun, 23 Nov 2025 12:57:05 +0100 Subject: [PATCH 093/139] Move testing directory to docs --- core/{snippets => docs}/testing/event-generation.md | 0 core/{snippets => docs}/testing/mock-relays.md | 0 core/{snippets => docs}/testing/nutzap-testing.md | 0 core/{snippets => docs}/testing/relay-pool-testing.md | 0 4 files changed, 0 insertions(+), 0 deletions(-) rename core/{snippets => docs}/testing/event-generation.md (100%) rename core/{snippets => docs}/testing/mock-relays.md (100%) rename core/{snippets => docs}/testing/nutzap-testing.md (100%) rename core/{snippets => docs}/testing/relay-pool-testing.md (100%) diff --git a/core/snippets/testing/event-generation.md b/core/docs/testing/event-generation.md similarity index 100% rename from core/snippets/testing/event-generation.md rename to core/docs/testing/event-generation.md diff --git a/core/snippets/testing/mock-relays.md b/core/docs/testing/mock-relays.md similarity index 100% rename from core/snippets/testing/mock-relays.md rename to core/docs/testing/mock-relays.md diff --git a/core/snippets/testing/nutzap-testing.md b/core/docs/testing/nutzap-testing.md similarity index 100% rename from core/snippets/testing/nutzap-testing.md rename to core/docs/testing/nutzap-testing.md diff --git a/core/snippets/testing/relay-pool-testing.md b/core/docs/testing/relay-pool-testing.md similarity index 100% rename from core/snippets/testing/relay-pool-testing.md rename to core/docs/testing/relay-pool-testing.md From 6aab1e1d3f3fc534be877ebb9fac37e335b764f2 Mon Sep 17 00:00:00 2001 From: digitalbase Date: Sun, 23 Nov 2025 13:03:39 +0100 Subject: [PATCH 094/139] Move key signing/encryption to signer doc + snippets --- core/docs/fundamentals/signers.md | 34 ++++++++++++++++++----- core/docs/snippets/key_create_store.ts | 19 +++++++++++++ core/snippets/user/generate-keys.md | 37 -------------------------- docs/snippets/signers.md | 6 ++++- 4 files changed, 51 insertions(+), 45 deletions(-) create mode 100644 core/docs/snippets/key_create_store.ts delete mode 100644 core/snippets/user/generate-keys.md diff --git a/core/docs/fundamentals/signers.md b/core/docs/fundamentals/signers.md index 3601daa66..fc5f51896 100644 --- a/core/docs/fundamentals/signers.md +++ b/core/docs/fundamentals/signers.md @@ -18,7 +18,8 @@ in [NIP-07](https://github.com/nostr-protocol/nips/blob/master/07.md). This mechanism allows the user to sign events with a browser extension to not share their private key with the application. -The most used browser extensions are [Nos2x](https://github.com/fiatjaf/nos2x) and [Alby](https://getalby.com/alby-extension). +The most used browser extensions are [Nos2x](https://github.com/fiatjaf/nos2x) +and [Alby](https://getalby.com/alby-extension). <<< @/core/docs/snippets/sign_event.ts @@ -27,11 +28,13 @@ extension will prompt the user to sign the event. ### Private Key Signer -NDK provides `NDKPrivateKeySigner` for managing in-memory private keys. This is useful for development, testing, or applications that manage keys locally. +NDK provides `NDKPrivateKeySigner` for managing in-memory private keys. This is useful for development, testing, or +applications that manage keys locally. > [!WARNING] > We strongly recommend not using this in production. Requiring users to share their private key is a security -> risk and should be avoided in favor of using [a browser extension](/core/docs/fundamentals/signers.html#browser-extensions) +> risk and should be avoided in favor of +> using [a browser extension](/core/docs/fundamentals/signers.html#browser-extensions) > or [a remote signer](/core/docs/fundamentals/signers.html#remote-signer). The private key signer takes the private key in the `nsec` format. @@ -92,13 +95,30 @@ nip07signer.user().then(async (user) => { ## Generate Keys -One good case where weo do want you to use `NDKPrivateKeySigner` is to help you generate keys as the signer -provides helper methods: +One good case where you would want to use `NDKPrivateKeySigner` is to help you generate keys as the signer +provides helper methods. + +This snippet demonstrates how to generate a new key pair and obtain all its various formats (private key, public key, +nsec, npub). <<< @/core/docs/snippets/key_create.ts +You can use these different formats for different purposes: -## Code Snippets +- `privateKey`: Raw private key for cryptographic operations +- `publicKey`: Raw public key (hex format) for verification +- `nsec`: Encoded private key format (bech32) - used for secure sharing when needed +- `npub`: Encoded public key format (bech32) - used for user identification + +### Storing Keys + +For storing keys securely with password protection, +use [NIP-49](https://github.com/nostr-protocol/nips/blob/master/49.md) (ncryptsec format): -More snippets and examples can be found in the [snippets directory](/docs/snippets.md#signers) +<<< @/core/docs/snippets/key_create_store.ts + +See [Encrypted Keys (NIP-49)](./encrypted-keys-nip49.md) for more examples and best practices. + +## Code Snippets +More snippets and examples can be found in the [snippets directory](/docs/snippets.md#signers) \ No newline at end of file diff --git a/core/docs/snippets/key_create_store.ts b/core/docs/snippets/key_create_store.ts new file mode 100644 index 000000000..7198cd10b --- /dev/null +++ b/core/docs/snippets/key_create_store.ts @@ -0,0 +1,19 @@ +import { NDKPrivateKeySigner } from "@nostr-dev-kit/ndk"; + +const signer = NDKPrivateKeySigner.generate(); + +// Encrypt with a password +const password = "user-chosen-password"; +const ncryptsec = signer.encryptToNcryptsec(password); + +// Store securely (e.g., localStorage) +localStorage.setItem("encrypted_key", ncryptsec); + +const restoredButEncrypted = localStorage.getItem("encrypted_key"); + +if (restoredButEncrypted) { + // Later, restore the signer + const restoredSigner = NDKPrivateKeySigner.fromNcryptsec(restoredButEncrypted, password); + + console.log(restoredSigner); +} diff --git a/core/snippets/user/generate-keys.md b/core/snippets/user/generate-keys.md deleted file mode 100644 index 7860ff0e6..000000000 --- a/core/snippets/user/generate-keys.md +++ /dev/null @@ -1,37 +0,0 @@ -# Generate Keys - -This snippet demonstrates how to generate a new key pair and obtain all its various formats (private key, public key, nsec, npub). - -<<< @/core/docs/snippets/key_create.ts - -You can use these different formats for different purposes: - -- `privateKey`: Raw private key for cryptographic operations -- `publicKey`: Raw public key (hex format) for verification -- `nsec`: Encoded private key format (bech32) - used for secure sharing when needed -- `npub`: Encoded public key format (bech32) - used for user identification - -## Secure Storage with Password Protection - -For storing keys securely with password protection, use NIP-49 (ncryptsec format): - -```typescript -import { NDKPrivateKeySigner } from "@nostr-dev-kit/ndk"; - -const signer = NDKPrivateKeySigner.generate(); - -// Encrypt with a password -const password = "user-chosen-password"; -const ncryptsec = signer.encryptToNcryptsec(password); - -// Store securely (e.g., localStorage) -localStorage.setItem("encrypted_key", ncryptsec); - -// Later, restore the signer -const restoredSigner = NDKPrivateKeySigner.fromNcryptsec( - localStorage.getItem("encrypted_key"), - password, -); -``` - -See [Encrypted Keys (NIP-49)](./encrypted-keys-nip49.md) for more examples and best practices. diff --git a/docs/snippets/signers.md b/docs/snippets/signers.md index 0dc1558f8..6be7a920d 100644 --- a/docs/snippets/signers.md +++ b/docs/snippets/signers.md @@ -16,4 +16,8 @@ ### Generate Keypair -<<< @/core/docs/snippets/key_create.ts \ No newline at end of file +<<< @/core/docs/snippets/key_create.ts + +### Encrypt/Decrypt Keypair + +<<< @/core/docs/snippets/key_create_store.ts \ No newline at end of file From 8a3db0759b1e53ac818531048b5faece1211708d Mon Sep 17 00:00:00 2001 From: digitalbase Date: Sun, 23 Nov 2025 13:10:07 +0100 Subject: [PATCH 095/139] Move nip49 to snippets and embed in signer docs --- .../advanced}/encrypted-keys-nip49.md | 41 +------------------ core/docs/fundamentals/signers.md | 2 +- core/docs/snippets/key_create_store.ts | 3 +- core/docs/snippets/nip-49-encrypting.ts | 15 +++++++ docs/.vitepress/config.mts | 4 ++ docs/snippets/signers.md | 6 ++- 6 files changed, 29 insertions(+), 42 deletions(-) rename core/{snippets/user => docs/advanced}/encrypted-keys-nip49.md (75%) create mode 100644 core/docs/snippets/nip-49-encrypting.ts diff --git a/core/snippets/user/encrypted-keys-nip49.md b/core/docs/advanced/encrypted-keys-nip49.md similarity index 75% rename from core/snippets/user/encrypted-keys-nip49.md rename to core/docs/advanced/encrypted-keys-nip49.md index c6fa204a7..ef1c373ab 100644 --- a/core/snippets/user/encrypted-keys-nip49.md +++ b/core/docs/advanced/encrypted-keys-nip49.md @@ -4,27 +4,7 @@ This snippet demonstrates how to encrypt and decrypt private keys using NIP-49 ( ## Basic Encryption and Decryption -```typescript -import { NDKPrivateKeySigner } from "@nostr-dev-kit/ndk"; - -// Generate or load a signer -const signer = NDKPrivateKeySigner.generate(); - -// Encrypt the private key with a password -const password = "user-chosen-password"; -const ncryptsec = signer.encryptToNcryptsec(password); - -// Store the encrypted key (e.g., localStorage, database) -localStorage.setItem("encrypted_key", ncryptsec); - -// Later, restore the signer from the encrypted key -const storedKey = localStorage.getItem("encrypted_key"); -const restoredSigner = NDKPrivateKeySigner.fromNcryptsec(storedKey, password); - -console.log("Original pubkey:", signer.pubkey); -console.log("Restored pubkey:", restoredSigner.pubkey); -// Both will match! -``` +<<< @/core/docs/snippets/key_create_store.ts ## Security Parameters @@ -126,24 +106,7 @@ main().catch(console.error); For advanced use cases, you can access the raw NIP-49 functions: -```typescript -import { nip49 } from "@nostr-dev-kit/ndk"; -import { hexToBytes, bytesToHex } from "@noble/hashes/utils"; - -// Encrypt raw private key bytes -const privateKeyHex = "14c226dbdd865d5e1645e72c7470fd0a17feb42cc87b750bab6538171b3a3f8a"; -const privateKeyBytes = hexToBytes(privateKeyHex); -const password = "my-password"; - -const ncryptsec = nip49.encrypt(privateKeyBytes, password, 16, 0x02); -console.log("Encrypted:", ncryptsec); - -// Decrypt to raw bytes -const decryptedBytes = nip49.decrypt(ncryptsec, password); -const decryptedHex = bytesToHex(decryptedBytes); -console.log("Decrypted:", decryptedHex); -// Will match original privateKeyHex -``` +<<< @/core/docs/snippets/nip-49-encrypting.ts ## Security Best Practices diff --git a/core/docs/fundamentals/signers.md b/core/docs/fundamentals/signers.md index fc5f51896..a61c166d4 100644 --- a/core/docs/fundamentals/signers.md +++ b/core/docs/fundamentals/signers.md @@ -117,7 +117,7 @@ use [NIP-49](https://github.com/nostr-protocol/nips/blob/master/49.md) (ncryptse <<< @/core/docs/snippets/key_create_store.ts -See [Encrypted Keys (NIP-49)](./encrypted-keys-nip49.md) for more examples and best practices. +See [Encrypted Keys (NIP-49)](/core/docs/advanced/encrypted-keys-nip49.md) for more examples and best practices. ## Code Snippets diff --git a/core/docs/snippets/key_create_store.ts b/core/docs/snippets/key_create_store.ts index 7198cd10b..a5e64e8fb 100644 --- a/core/docs/snippets/key_create_store.ts +++ b/core/docs/snippets/key_create_store.ts @@ -15,5 +15,6 @@ if (restoredButEncrypted) { // Later, restore the signer const restoredSigner = NDKPrivateKeySigner.fromNcryptsec(restoredButEncrypted, password); - console.log(restoredSigner); + console.log("Original pubkey:", signer.pubkey); + console.log("Restored pubkey:", restoredSigner.pubkey); } diff --git a/core/docs/snippets/nip-49-encrypting.ts b/core/docs/snippets/nip-49-encrypting.ts new file mode 100644 index 000000000..b8c7115ea --- /dev/null +++ b/core/docs/snippets/nip-49-encrypting.ts @@ -0,0 +1,15 @@ +import {bytesToHex, hexToBytes} from "@noble/hashes/utils"; +import {nip49} from "@nostr-dev-kit/ndk"; + +// Encrypt raw private key bytes +const privateKeyHex = "14c226dbdd865d5e1645e72c7470fd0a17feb42cc87b750bab6538171b3a3f8a"; +const privateKeyBytes = hexToBytes(privateKeyHex); +const password = "my-password"; + +const ncryptsec = nip49.encrypt(privateKeyBytes, password, 16, 0x02); +console.log("Encrypted:", ncryptsec); + +// Decrypt to raw bytes +const decryptedBytes = nip49.decrypt(ncryptsec, password); +const decryptedHex = bytesToHex(decryptedBytes); +console.log("Decrypted:", decryptedHex); diff --git a/docs/.vitepress/config.mts b/docs/.vitepress/config.mts index 48f7f3cae..db3a97bf5 100644 --- a/docs/.vitepress/config.mts +++ b/docs/.vitepress/config.mts @@ -162,6 +162,10 @@ export default defineConfig({ text: "Exclusive Relays โœ…", link: "/core/docs/advanced/exclusive-relay" }, + { + text: "Encrypting Keys (NIP-49) โœ…", + link: "/core/docs/advanced/encrypted-keys-nip49" + }, { text: "Subscription Lifecycle โŒ", link: "/core/docs/advanced/subscription-internals" diff --git a/docs/snippets/signers.md b/docs/snippets/signers.md index 6be7a920d..af167e5b8 100644 --- a/docs/snippets/signers.md +++ b/docs/snippets/signers.md @@ -20,4 +20,8 @@ ### Encrypt/Decrypt Keypair -<<< @/core/docs/snippets/key_create_store.ts \ No newline at end of file +<<< @/core/docs/snippets/key_create_store.ts + +### NIP-49 encryption + +<<< @/core/docs/snippets/nip-49-encrypting.ts \ No newline at end of file From d33ea66a5ffdb177bd7b289a0524c829e2e847f7 Mon Sep 17 00:00:00 2001 From: digitalbase Date: Sun, 23 Nov 2025 13:16:46 +0100 Subject: [PATCH 096/139] Add helper fundamental docs + remove outdated fetchprofile example --- core/docs/fundamentals/helpers.md | 17 ++++++++++++++ core/snippets/user/get-profile.md | 38 ------------------------------- docs/.vitepress/config.mts | 1 + 3 files changed, 18 insertions(+), 38 deletions(-) create mode 100644 core/docs/fundamentals/helpers.md delete mode 100644 core/snippets/user/get-profile.md diff --git a/core/docs/fundamentals/helpers.md b/core/docs/fundamentals/helpers.md new file mode 100644 index 000000000..f0103d1d9 --- /dev/null +++ b/core/docs/fundamentals/helpers.md @@ -0,0 +1,17 @@ +# Helpers + +NDK comes with a ton of useful helpers and utilities to do things faster and easier. + +Quite a number of those utilities come from the +[nostr-tools library](https://github.com/nbd-wtf/nostr-tools) which is included as part of +NDK + +## + +### Managing Users + +<<< @/core/docs/snippets/managing-users.ts + + + + diff --git a/core/snippets/user/get-profile.md b/core/snippets/user/get-profile.md deleted file mode 100644 index 2f1358a2b..000000000 --- a/core/snippets/user/get-profile.md +++ /dev/null @@ -1,38 +0,0 @@ -# Getting Profile Information - -This snippet demonstrates how to fetch user profile information using NDK. - -## Basic Profile Fetching - -Use `NDKUser`'s `fetchProfile()` to fetch a user's profile. - -```typescript -// Get an NDKUser instance for a specific pubkey -const user = ndk.getUser({ pubkey: "user_pubkey_here" }); - -// Fetch their profile -try { - const profile = await user.fetchProfile(); - console.log("Profile loaded:", profile); -} catch (e) { - console.error("Error fetching profile:", e); -} -``` - -## Profile Data Structure - -The profile object contains standard Nostr profile fields: - -```typescript -interface NDKUserProfile { - name?: string; - displayName?: string; - image?: string; - banner?: string; - about?: string; - nip05?: string; - lud06?: string; // Lightning Address - lud16?: string; // LNURL - website?: string; -} -``` diff --git a/docs/.vitepress/config.mts b/docs/.vitepress/config.mts index db3a97bf5..aa2268a13 100644 --- a/docs/.vitepress/config.mts +++ b/docs/.vitepress/config.mts @@ -60,6 +60,7 @@ export default defineConfig({ link: "/core/docs/tutorial/subscription-management", }, {text: "Mute Filtering โŒ", link: "/core/docs/tutorial/mute-filtering"}, + {text: "Helpers โŒ", link: "/core/docs/fundamentals/helpers"}, ], }, { From 5201d4a8de83462f962e532a58d3175ad82b219a Mon Sep 17 00:00:00 2001 From: digitalbase Date: Sun, 23 Nov 2025 13:24:32 +0100 Subject: [PATCH 097/139] Move 2.15 announcement RELEASE-NOTES to changelog --- core/CHANGELOG.md | 29 +++++++++++++++++++++++++++++ core/RELEASE-NOTES.md | 21 --------------------- 2 files changed, 29 insertions(+), 21 deletions(-) delete mode 100644 core/RELEASE-NOTES.md diff --git a/core/CHANGELOG.md b/core/CHANGELOG.md index cf2117c82..68550851d 100644 --- a/core/CHANGELOG.md +++ b/core/CHANGELOG.md @@ -377,6 +377,35 @@ ## 2.15.0 +I'm happy to announce the release of NDK 2.15. This is a very significant release that's been in the works for quite a +while. The main focus of this release has been in reducing the amount of footguns in NDK. Yes, mainly thinking about +making the vibecoding scene a lot more enjoyable: LLMs are great at churning out code at insane speed. Not so much at +debugging, optimizations, performant code. So this new release of NDK focuses a lot on making sure that the most obvious +solution is the right solution. + +ndk-hooks + +I spun out a new package called ndk-hooks. Someone else had built (and abandoned) an ndk-react package, and LLMs +constantly got confused using it and produced terrible code. ndk-hooks is an offshoot of ndk-mobile, and, if you're +vibecoding Next.js apps like most people are doing, ndk-hooks provides you with all the building blocks to make sure +your agents don't lose their minds. + +ndk-core + +A lot has changed under the hood to make space for performant access points, particularly synchronous access points. + +Worthy mentions: + +- The whole codebase has been migrated to use modern tooling: bun and vitest. +- Very useful test helpers, until now, writing tests on clients was a pain, NDK now ships with @nostr-dev-kit/ndk/test + which provides a `MockRelay` that provides access to very useful testing utilities. +- Race condition bugs are now gone, no more dreaded "Not enough relays received this event" when publishing before + connecting to relays. + +ndk-sqlite-wasm-cache-adapter + +A new cache adapter leveraging SQLite WASM is now available. This should be the preferred option to + ### Minor Changes - Implement dynamic subscription relay refresh for outbox model diff --git a/core/RELEASE-NOTES.md b/core/RELEASE-NOTES.md deleted file mode 100644 index df8fc84dd..000000000 --- a/core/RELEASE-NOTES.md +++ /dev/null @@ -1,21 +0,0 @@ -# NDK 2.15 - -I'm happy to announce the release of NDK 2.15. This is a very significant release that's been in the works for quite a while. The main focus of this release has been in reducing the amount of footguns in NDK. Yes, mainly thinking about making the vibecoding scene a lot more enjoyable: LLMs are great at churning out code at insane speed. Not so much at debugging, optimizations, performant code. So this new release of NDK focuses a lot on making sure that the most obvious solution is the right solution. - -ndk-hooks - -I spun out a new package called ndk-hooks. Someone else had built (and abandoned) an ndk-reactย package, and LLMs constantly got confused using it and produced terrible code. ndk-hooksย is an offshoot of ndk-mobile, and, if you're vibecoding Next.js apps like most people are doing, ndk-hooksย provides you with all the building blocks to make sure your agents don't lose their minds. - -ndk-core - -A lot has changed under the hood to make space for performant access points, particularly synchronous access points. - -Worthy mentions: - -- The whole codebase has been migrated to use modern tooling: bun and vitest. -- Very useful test helpers, until now, writing tests on clients was a pain, NDK now ships with @nostr-dev-kit/ndk/test which provides a `MockRelay` that provides access to very useful testing utilities. -- Race condition bugs are now gone, no more dreaded "Not enough relays received this event" when publishing before connecting to relays. - -ndk-sqlite-wasm-cache-adapter - -A new cache adapter leveraging SQLite WASM is now available. This should be the preferred option to From 2266cbb1ee35ddb4e3b2f091fb76cc38d921d910 Mon Sep 17 00:00:00 2001 From: digitalbase Date: Sun, 23 Nov 2025 13:24:46 +0100 Subject: [PATCH 098/139] Moved old migration guide to docs/migration --- core/{MIGRATION-2.16.md => docs/migration/2.13-to-2.16.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename core/{MIGRATION-2.16.md => docs/migration/2.13-to-2.16.md} (100%) diff --git a/core/MIGRATION-2.16.md b/core/docs/migration/2.13-to-2.16.md similarity index 100% rename from core/MIGRATION-2.16.md rename to core/docs/migration/2.13-to-2.16.md From d0dbf0d68317a6dee3796141d84b2b743cd41932 Mon Sep 17 00:00:00 2001 From: digitalbase Date: Sun, 23 Nov 2025 13:24:59 +0100 Subject: [PATCH 099/139] Remove old usage backup markdown file --- core/docs/getting-started/usage_old.md | 272 ------------------------- 1 file changed, 272 deletions(-) delete mode 100644 core/docs/getting-started/usage_old.md diff --git a/core/docs/getting-started/usage_old.md b/core/docs/getting-started/usage_old.md deleted file mode 100644 index bc9a526a3..000000000 --- a/core/docs/getting-started/usage_old.md +++ /dev/null @@ -1,272 +0,0 @@ -# Usage - -## Instantiate - -To start using NDK, you need to create an instance of it. - - -```ts -// Import the package -import NDK from "@nostr-dev-kit/ndk"; - -// Create a new NDK instance -const ndk = new NDK(); -``` - -This by itself won't do much as there are no connected relays but there might be cases -where you want to have a NDK instance without connecting to any relays. - -## Connecting to Relays - -The simplest way to get NDK to connect to relays is to specify them: - -```ts -// Import the package -import NDK from "@nostr-dev-kit/ndk"; - -// Create a new NDK instance with explicit relays -const ndk = new NDK({ - explicitRelayUrls: ["wss://a.relay", "wss://another.relay"], -}); - -// Now connect to specified relays -await ndk.connect(); -``` - -Make sure to wait for the `connect()` promise to resolve before using NDK after which -you can start interacting with relays. - - - ---- - -You can pass an object with several options to a newly created instance of NDK. - -- `explicitRelayUrls` โ€“ an array of relay URLs. -- `signer` - an instance of a [signer](#signers). -- `cacheAdapter` - an instance of a [Cache Adapter](#caching) -- `debug` - Debug instance to use for logging. Defaults to `debug("ndk")`. - - - -If the signer implements the `getRelays()` method, NDK will use the relays returned by that method as the explicit relays. - -```ts -// Import the package -import NDK, { NDKNip07Signer } from "@nostr-dev-kit/ndk"; - -// Create a new NDK instance with just a signer (provided the signer implements the getRelays() method) -const nip07signer = new NDKNip07Signer(); -const ndk = new NDK({ signer: nip07signer }); -``` - -Note: In normal client use, it's best practice to instantiate NDK as a singleton class. [See more below](#architecture-decisions--suggestions). - -## Creating Users - -NDK provides flexible ways to fetch user objects, including support for NIP-19 encoded identifiers and NIP-05 addresses: - -```typescript -// From hex pubkey -const user1 = await ndk.fetchUser("3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d"); - -// From npub (NIP-19 encoded) -const user2 = await ndk.fetchUser("npub1n0sturny6w9zn2wwexju3m6asu7zh7jnv2jt2kx6tlmfhs7thq0qnflahe"); - -// From nprofile (includes relay hints) -const user3 = await ndk.fetchUser("nprofile1qqsrhuxx8l9ex335q7he0f09aej04zpazpl0ne2cgukyawd24mayt8gpp4mhxue69uhhytnc9e3k7mgpz4mhxue69uhkg6nzv9ejuumpv34kytnrdaksjlyr9p"); - -// From NIP-05 identifier -const user4 = await ndk.fetchUser("pablo@test.com"); -const user5 = await ndk.fetchUser("test.com"); // Uses _@test.com - -// The method automatically detects the format -const user6 = await ndk.fetchUser("deadbeef..."); // Assumes hex pubkey -``` - -Note: `fetchUser` is async and returns a Promise. For NIP-05 lookups, it may return `undefined` if the address cannot be resolved. - -## Working with NIP-19 Identifiers - -NDK re-exports NIP-19 utilities for encoding and decoding Nostr identifiers: - -```typescript -import { nip19 } from '@nostr-dev-kit/ndk'; - -// Encode a pubkey as npub -const npub = nip19.npubEncode("3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d"); - -// Decode any NIP-19 identifier -const decoded = nip19.decode("npub1..."); -console.log(decoded.type); // "npub" -console.log(decoded.data); // hex pubkey - -// Encode events -const nevent = nip19.neventEncode({ - id: eventId, - relays: ["wss://relay.example.com"], - author: authorPubkey -}); -``` - -See the [NIP-19 tutorial](/tutorial/nip19.html) for comprehensive examples and use cases. - -## Usage with React Hooks (`ndk-hooks`) - -When using the `ndk-hooks` package in a React application, the initialization process involves creating the NDK instance and then using the `useNDKInit` hook to make it available to the rest of your application via Zustand stores. - -This hook ensures that both the core NDK store and dependent stores (like the user profiles store) are properly initialized with the NDK instance. - -It's recommended to create and connect your NDK instance outside of your React components, potentially in a dedicated setup file or at the root of your application. Then, use the `useNDKInit` hook within your main App component or a context provider to initialize the stores once the component mounts. - -```tsx -import React, { useEffect } from 'react'; // Removed useState -import NDK from '@nostr-dev-kit/ndk'; -import { useNDKInit } from '@nostr-dev-kit/ndk-hooks'; // Assuming package name - -// 1. Configure your NDK instance (e.g., in src/ndk.ts or similar) -const ndk = new NDK({ - explicitRelayUrls: ['wss://relay.damus.io', 'wss://relay.primal.net'], - // Add signer or cache adapter if needed -}); - -// 2. Connect the instance immediately -ndk.connect() - .then(() => console.log('NDK connected')) - .catch((e) => console.error('NDK connection error:', e)); - -// Example: App component or Context Provider that initializes NDK stores -function App() { - const initializeNDK = useNDKInit(); // Hook returns the function directly - - useEffect(() => { - // 3. Initialize stores once the component mounts - initializeNDK(ndk); - }, [initializeNDK]); // Dependency ensures this runs if initializeNDK changes, though unlikely - - // Your application components can now use other ndk-hooks - // No need to wait for connection state here, as hooks handle NDK readiness internally - return ( -
- {/* ... Your app content using useProfile, useSubscribe, etc. ... */} -
- ); -} - -export default App; -``` - -**Key Points:** - -* Create and configure your `NDK` instance globally or outside components. -* Call `ndk.connect()` immediately after creation. Connection happens in the background. -* In your main App or Provider component, get the `initializeNDK` function from `useNDKInit`. -* Use `useEffect` with an empty dependency array (or `[initializeNDK]`) to call `initializeNDK(ndk)` once on mount. -* This sets up the necessary Zustand stores. Other `ndk-hooks` will access the initialized `ndk` instance from the store and handle its readiness internally. - ---- - - -## Architecture decisions & suggestions - -- Users of NDK should instantiate a single NDK instance. -- That instance tracks state with all relays connected, explicit and otherwise. -- All relays are tracked in a single pool that handles connection errors/reconnection logic. -- RelaySets are assembled ad-hoc as needed depending on the queries set, although some RelaySets might be long-lasting, like the `explicitRelayUrls` specified by the user. -- RelaySets are always a subset of the pool of all available relays. - - -## Subscribing to Events - -Once connected, you can subscribe to events using `ndk.subscribe()`. You provide filters to specify the events you're interested in. - -### Preferred Method: Direct Event Handlers - -The **recommended** way to handle events is to provide handler functions directly when calling `ndk.subscribe()`. This is done using the third argument (`autoStart`), which accepts an object containing `onEvent`, `onEvents`, and/or `onEose` callbacks. - -**Why is this preferred?** Subscriptions can start receiving events (especially from a fast cache) almost immediately after `ndk.subscribe()` is called. By providing handlers directly, you ensure they are attached *before* any events are emitted, preventing potential race conditions where you might miss the first few events if you attached handlers later using `.on()`. - -```typescript -// Example with default relay calculation -ndk.subscribe( - { kinds: [1], authors: [pubkey] }, // Filters - { closeOnEose: true }, // Options (no explicit relays specified) - { // Direct handlers via autoStart parameter (now the 3rd argument) - onEvent: (event: NDKEvent, relay?: NDKRelay) => { - // Called for events received from relays after the initial cache load (if onEvents is used) - console.log("Received event from relay (id):", event.id); - }, - onEvents: (events: NDKEvent[]) => { // Parameter renamed to 'events' - console.log(`Received ${events.length} events from cache initially.`); - }, - onEose: (subscription: NDKSubscription) => { - console.log("Subscription reached EOSE:", subscription.internalId); - } - } -); - -// Example specifying explicit relays using relayUrls option -ndk.subscribe( - { kinds: [0], authors: [pubkey] }, // Filters - { // Options object now includes relayUrls - closeOnEose: true, - relayUrls: ["wss://explicit1.relay", "wss://explicit2.relay"] - }, - { // Direct handlers - onEvent: (event: NDKEvent) => { /* ... */ } - } -); - -// Example specifying explicit relays using relaySet option -const explicitRelaySet = NDKRelaySet.fromRelayUrls(["wss://explicit.relay"], ndk); -ndk.subscribe( - { kinds: [7], authors: [pubkey] }, // Filters - { // Options object now includes relaySet - closeOnEose: true, - relaySet: explicitRelaySet - }, - { // Direct handlers - onEvent: (event: NDKEvent) => { /* ... */ } - } -); -``` - -### Efficient Cache Handling with `onEvents` - -Using the `onEvents` handler provides an efficient way to process events loaded from the cache. When you provide `onEvents`: - -1. If NDK finds matching events in its cache *synchronously* when the subscription starts, `onEvents` is called **once** with an array of all those cached events. -2. The `onEvent` handler is **skipped** for this initial batch of cached events. -3. `onEvent` will still be called for any subsequent events received from relays or later asynchronous cache updates. - -This is ideal for scenarios like populating initial UI state, as it allows you to process the cached data in a single batch, preventing potentially numerous individual updates that would occur if `onEvent` were called for each cached item. - -If you *don't* provide `onEvents`, the standard `onEvent` handler will be triggered for every event, whether it comes from the cache or a relay. - -### Alternative Method: Attaching Handlers with `.on()` - -You can also attach event listeners *after* creating the subscription using the `.on()` method. While functional, be mindful of the potential race condition mentioned above, especially if you rely on immediate cache results. - -```typescript -// Subscribe using default relay calculation -const subscription = ndk.subscribe( - { kinds: [1], authors: [pubkey] }, - { closeOnEose: true } // Options -); - -// Subscribe using explicit relays via options -const subscriptionWithRelays = ndk.subscribe( - { kinds: [0], authors: [pubkey] }, - { relayUrls: ["wss://explicit.relay"] } // Options with explicit relays -); - -// Attach handlers later -subscription.on("event", (event) => { - console.log("Received event:", event.id); -}); -subscription.on("eose", () => { - console.log("Initial events loaded"); -}); - -// Remember to stop the subscription when it's no longer needed -// setTimeout(() => subscription.stop(), 5000); From 7f7e1b7cab2e91b11a14cd3eaafcb021adc1d4b9 Mon Sep 17 00:00:00 2001 From: digitalbase Date: Sun, 23 Nov 2025 13:25:13 +0100 Subject: [PATCH 100/139] Move archiecture suggestions to advanced --- .../architecture_suggestions.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename core/docs/{getting-started/archiecture_suggestions.md => advanced/architecture_suggestions.md} (100%) diff --git a/core/docs/getting-started/archiecture_suggestions.md b/core/docs/advanced/architecture_suggestions.md similarity index 100% rename from core/docs/getting-started/archiecture_suggestions.md rename to core/docs/advanced/architecture_suggestions.md From bc4606695ab84698770b18bfd9ed310065a35733 Mon Sep 17 00:00:00 2001 From: digitalbase Date: Sun, 23 Nov 2025 13:25:42 +0100 Subject: [PATCH 101/139] Add NIP-49 and NIP-19 helper documentation to fundamtentals/helpers --- core/docs/fundamentals/helpers.md | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/core/docs/fundamentals/helpers.md b/core/docs/fundamentals/helpers.md index f0103d1d9..c442338a5 100644 --- a/core/docs/fundamentals/helpers.md +++ b/core/docs/fundamentals/helpers.md @@ -6,7 +6,19 @@ Quite a number of those utilities come from the [nostr-tools library](https://github.com/nbd-wtf/nostr-tools) which is included as part of NDK -## +### NIP-19 Identifiers + +NDK re-exports [NIP-19](https://github.com/nostr-protocol/nips/blob/master/19.md) from the +[nostr-tools library](https://github.com/nbd-wtf/nostr-tools): + +<<< @/core/docs/snippets/nip-19-identifiers.ts + +### NIP-49 Encryption + +NDK re-exports [NIP-49](https://github.com/nostr-protocol/nips/blob/master/49.md) from the +[nostr-tools library](https://github.com/nbd-wtf/nostr-tools): + +<<< @/core/docs/snippets/nip-49-encrypting.ts ### Managing Users From 40cc20e941ae7db6e0d0682944e6123c5d4d3e87 Mon Sep 17 00:00:00 2001 From: digitalbase Date: Sun, 23 Nov 2025 13:26:34 +0100 Subject: [PATCH 102/139] Remove LLM implementation plan for SIG-SAMPLING --- core/SIG-SAMPLING.md | 714 ------------------------------------------- 1 file changed, 714 deletions(-) delete mode 100644 core/SIG-SAMPLING.md diff --git a/core/SIG-SAMPLING.md b/core/SIG-SAMPLING.md deleted file mode 100644 index de83418c2..000000000 --- a/core/SIG-SAMPLING.md +++ /dev/null @@ -1,714 +0,0 @@ -# Signature Verification Sampling Implementation Plan - -## Overview - -This document outlines the implementation plan for adding signature verification sampling to NDK. The goal is to optimize performance by reducing the number of signature verifications while maintaining security. - -Currently, NDK verifies every signature it encounters. This is computationally expensive and unnecessary - we only need to verify a sample of signatures from each relay to determine trustworthiness. Once a relay sends an invalid signature, it can be marked as untrustworthy. - -Relays must always send valid signatures, a single failure means the relay is evil. - -All invalid-signature detectionsโ€”whether synchronous or asynchronousโ€”will delegate to a new `NDK.reportInvalidSignature(event, relay)` method. This centralizes emission of the `event:invalid-sig` (with relay context) and supports optional auto-blacklisting of malicious relays. - -## Current Architecture Analysis - -### Key Components - -1. **NDK Class** (`ndk-core/src/ndk/index.ts`): - - Contains configuration properties: - - `initialValidationRatio`: Starting validation ratio for new relays - - `lowestValidationRatio`: Minimum validation ratio for any relay - - `validationRatioFn`: Optional function to calculate validation ratio - - Emits `event:invalid-sig` events when invalid signatures are detected - -2. **NDKRelay Class** (`ndk-core/src/relay/index.ts`): - - Tracks validated and non-validated event counts - - Has methods to add validated/non-validated events - - Has `shouldValidateEvent` method (implementation needs to be enhanced) - -3. **Signature Verification** (`ndk-core/src/events/signature.ts`): - - Contains verification logic - - Maintains `verifiedSignatures` map to track already verified event IDs - -4. **NDKSubscription Class** (`ndk-core/src/subscription/index.ts`): - - Receives events from relays - - Calls verification methods on events - - Can check already verified signatures - -5. **Test Utilities** (`ndk-test-utils/src/index.ts`): - - Provides mocks and helpers for testing - - Includes `RelayMock`, `RelayPoolMock`, and `EventGenerator` - - Offers `TestFixture` and time control utilities - -## Implementation Plan - -### 1. Enhance NDKRelay Class - -#### 1.1. Add Validation Statistics Tracking - -```typescript -class NDKRelay { - // Existing properties - private validatedCount = 0; - private nonValidatedCount = 0; - private currentValidationRatio: number; - - constructor(url: string, authPolicy?: NDKAuthPolicy, ndk?: NDK) { - // Existing constructor code - this.currentValidationRatio = ndk?.initialValidationRatio || 1.0; - } - - public addValidatedEvent(): void { - this.validatedCount++; - this.updateValidationRatio(); - } - - public addNonValidatedEvent(): void { - this.nonValidatedCount++; - } - - private updateValidationRatio(): void { - if (!this.ndk) return; - - // Use custom function if provided - if (this.ndk.validationRatioFn) { - this.currentValidationRatio = this.ndk.validationRatioFn( - this, - this.validatedCount, - this.nonValidatedCount, - ); - return; - } - - // Default ratio calculation: - // Gradually decrease ratio based on number of validated events - // But never go below lowestValidationRatio - const newRatio = Math.max( - this.ndk.lowestValidationRatio, - this.ndk.initialValidationRatio * Math.exp(-0.01 * this.validatedCount), - ); - - this.currentValidationRatio = newRatio; - } - - public shouldValidateEvent(): boolean { - if (!this.ndk) return true; - - // Always validate if ratio is 1.0 - if (this.currentValidationRatio >= 1.0) return true; - - // Otherwise, randomly decide based on ratio - return Math.random() < this.currentValidationRatio; - } -} -``` - -### 2. Enhance Signature Verification Process - -#### 2.1. Update NDKSubscription's `eventReceived` Method - -Current method in `ndk-core/src/subscription/index.ts` needs modifications: - -```typescript -public eventReceived( - event: NDKEvent | NostrEvent, - relay: NDKRelay | undefined, - fromCache = false, - optimisticPublish = false, -) { - const eventId = event.id! as NDKEventId; - const eventAlreadySeen = this.eventFirstSeen.has(eventId); - let ndkEvent: NDKEvent; - - if (event instanceof NDKEvent) ndkEvent = event; - - if (!eventAlreadySeen) { - // generate the ndkEvent - ndkEvent ??= new NDKEvent(this.ndk, event); - ndkEvent.ndk = this.ndk; - ndkEvent.relay = relay; - - // Skip validation for cached or self-published events - if (!fromCache && !optimisticPublish) { - // Validate event structure - if (!this.skipValidation) { - if (!ndkEvent.isValid) { - this.debug("Event failed validation %s from relay %s", eventId, relay?.url); - return; - } - } - - // Verify signature with sampling - if (relay) { - // Check if we need to verify this event based on sampling - const shouldVerify = relay.shouldValidateEvent(); - - if (shouldVerify && !this.skipVerification) { - // Attempt verification - if (!ndkEvent.verifySignature(true) && !this.ndk.asyncSigVerification) { - this.debug("Event failed signature validation", event); - // Report the invalid signature with relay information through the centralized method - this.ndk.reportInvalidSignature(ndkEvent, relay); - return; - } - - // Track successful validation - relay.addValidatedEvent(); - } else { - // We skipped verification for this event - relay.addNonValidatedEvent(); - } - } - - // Cache the event if appropriate - if (this.ndk.cacheAdapter && !this.opts.dontSaveToCache) { - this.ndk.cacheAdapter.setEvent(ndkEvent, this.filters, relay); - } - } - - // Emit the event - if (!optimisticPublish || this.skipOptimisticPublishEvent !== true) { - this.emitEvent(this.opts?.wrap ?? false, ndkEvent, relay, fromCache, optimisticPublish); - // Mark as seen - this.eventFirstSeen.set(eventId, Date.now()); - } - } else { - // Handle duplicate events (existing code) - const timeSinceFirstSeen = Date.now() - (this.eventFirstSeen.get(eventId) || 0); - this.emit("event:dup", event, relay, timeSinceFirstSeen, this, fromCache, optimisticPublish); - - if (relay) { - // Check if we've already verified this event id's signature - const signature = verifiedSignatures.get(eventId); - if (signature && typeof signature === "string") { - // If signatures match, we count it as validated - if (event.sig === signature) { - relay.addValidatedEvent(); - } else { - // Signatures don't match - this is a malicious relay! - // One invalid signature means the relay is considered evil - this.ndk.reportInvalidSignature(ndkEvent || new NDKEvent(this.ndk, event), relay); - } - } - } - } - - this.lastEventReceivedAt = Date.now(); -} -``` - -### 3. Centralize Invalid Signature Reporting in NDK Class - -#### 3.1. Add Central Reporting Method - -In `ndk-core/src/ndk/index.ts`, add a centralized method for reporting invalid signatures: - -```typescript -export class NDK extends EventEmitter<{ - // Existing events - "signer:ready": (signer: NDKSigner) => void; - "signer:required": () => void; - - // Updated event to include the relay parameter - "event:invalid-sig": (event: NDKEvent, relay: NDKRelay) => void; - - "event:publish-failed": ( - event: NDKEvent, - error: NDKPublishError, - relays: WebSocket["url"][], - ) => void; -}> { - // Existing properties and methods - - /** - * Centralized method to report an invalid signature, identifying the relay that provided it. - * A single invalid signature means the relay is considered malicious. - * All invalid signature detections (synchronous or asynchronous) should delegate to this method. - * - * @param event The event with an invalid signature - * @param relay The relay that provided the invalid signature - */ - public reportInvalidSignature(event: NDKEvent, relay: NDKRelay): void { - this.debug(`Invalid signature detected from relay ${relay.url} for event ${event.id}`); - - // Emit event with relay information - this.emit("event:invalid-sig", event, relay); - - // If auto-blacklisting is enabled, add the relay to the blacklist - if (this.autoBlacklistInvalidRelays) { - this.blacklistRelay(relay.url); - } - } - - /** - * Add a relay URL to the blacklist as it has been identified as malicious - */ - public blacklistRelay(url: string): void { - if (!this.blacklistRelayUrls) { - this.blacklistRelayUrls = []; - } - - if (!this.blacklistRelayUrls.includes(url)) { - this.blacklistRelayUrls.push(url); - this.debug(`Added relay to blacklist: ${url}`); - - // Disconnect from this relay if connected - const relay = this.pool.getRelay(url, false, false); - if (relay) { - relay.disconnect(); - this.debug(`Disconnected from blacklisted relay: ${url}`); - } - } - } -} -``` - -### 4. Update Async Signature Verification - -In `ndk-core/src/events/signature.ts`, modify the worker message handler to use the centralized reporting: - -```typescript -function initSignatureVerification(worker: Worker) { - // ... existing code ... - - worker.onmessage = (e) => { - const { id, valid } = e.data; - const callback = callbacks.get(id); - - if (callback) { - callbacks.delete(id); - - // Get the stored event and relay information - const { event, relay, ndk } = eventContext.get(id) || {}; - eventContext.delete(id); - - if (valid) { - verifiedSignatures.set(event.id, event.sig); - callback(true); - relay?.addValidatedEvent(); - } else { - callback(false); - // If invalid, report through the centralized method - if (event && relay && ndk) { - ndk.reportInvalidSignature(event, relay); - } - } - } - }; -} -``` - -### 5. Default Validation Ratio Function - -#### 5.1. Implement a Default Algorithm - -This would be part of the NDK class constructor in `ndk-core/src/ndk/index.ts`: - -```typescript -public constructor(opts: NDKConstructorParams = {}) { - // Existing constructor code - - this.initialValidationRatio = opts.initialValidationRatio || 1.0; - this.lowestValidationRatio = opts.lowestValidationRatio || 0.1; - this.autoBlacklistInvalidRelays = opts.autoBlacklistInvalidRelays || false; - - // Set a default validation ratio function if none is provided - this.validationRatioFn = opts.validationRatioFn || this.defaultValidationRatioFn; -} - -/** - * Default function to calculate validation ratio based on historical validation results. - * The more events validated successfully, the lower the ratio goes (down to the minimum). - */ -private defaultValidationRatioFn(relay: NDKRelay, validatedCount: number, nonValidatedCount: number): number { - if (validatedCount < 10) return this.initialValidationRatio; - - // Calculate a logarithmically decreasing ratio that approaches the minimum - // as more events are validated - const totalEvents = validatedCount + nonValidatedCount; - const trustFactor = Math.min(validatedCount / 100, 1); // Caps at 100 validated events - - const calculatedRatio = this.initialValidationRatio * - (1 - trustFactor) + - this.lowestValidationRatio * trustFactor; - - return Math.max(calculatedRatio, this.lowestValidationRatio); -} -``` - -### 6. Integration Points - -#### 6.1. Update NDKConstructorParams Interface - -In `ndk-core/src/ndk/index.ts`, update: - -```typescript -export interface NDKConstructorParams { - // Existing parameters - - /** - * The signature verification validation ratio for new relays. - * A value of 1.0 means verify all signatures, 0.5 means verify half, etc. - * @default 1.0 - */ - initialValidationRatio?: number; - - /** - * The lowest validation ratio any single relay can have. - * Relays will have a sample of events verified based on this ratio. - * When using this, you MUST listen for event:invalid-sig events - * to handle invalid signatures and disconnect from evil relays. - * - * @default 0.1 - */ - lowestValidationRatio?: number; - - /** - * A function that is invoked to calculate the validation ratio for a relay. - * If not provided, a default algorithm will be used. - */ - validationRatioFn?: NDKValidationRatioFn; - - /** - * When true, automatically blacklist relays that provide events with invalid signatures. - * A single invalid signature is enough to mark a relay as malicious. - * @default false - */ - autoBlacklistInvalidRelays?: boolean; -} -``` - -### 7. Documentation Updates - -#### 7.1. Add Documentation to README.md and API Reference - -For example: - -````markdown -## Signature Verification Sampling - -NDK includes support for signature verification sampling to improve performance while maintaining security. - -### Security Model - -The security model is based on the principle that **all relays must always send valid signatures**. A single invalid signature is sufficient evidence that a relay is malicious and should be blacklisted. - -By using signature sampling, we can significantly reduce computational overhead while maintaining this security model. As a relay proves trustworthy by consistently providing valid signatures, we reduce the sampling rate, checking fewer signatures over time, down to a configurable minimum ratio. - -If at any point an invalid signature is detected, the relay is immediately reported through the centralized `reportInvalidSignature` method, which emits an `event:invalid-sig` event and optionally blacklists the relay. - -### Configuration - -```typescript -const ndk = new NDK({ - // Verify 100% of signatures from new relays - initialValidationRatio: 1.0, - - // Eventually drop to verifying only 10% of signatures from trusted relays - lowestValidationRatio: 0.1, - - // Optional custom function to determine validation ratio - validationRatioFn: (relay, validatedCount, nonValidatedCount) => { - // Custom logic to determine ratio - return Math.max(0.1, 1.0 - validatedCount / 1000); - }, - - // Automatically blacklist relays that send invalid signatures - autoBlacklistInvalidRelays: true, -}); - -// Listen for invalid signature events -ndk.on("event:invalid-sig", (event, relay) => { - console.log(`Relay ${relay.url} sent an event with invalid signature: ${event.id}`); - // Custom handling... -}); -``` -```` - -```` - -## Implementation Steps - -1. **First Phase: Core Implementation** - - Enhance NDKRelay to track validation statistics - - Implement `shouldValidateEvent()` method logic - - Add `updateValidationRatio()` method - - Implement default ratio calculation algorithm - -2. **Second Phase: Centralized Invalid Signature Handling** - - Implement the centralized `reportInvalidSignature` method in NDK - - Update `event:invalid-sig` event to include relay information - - Modify async signature verification to use centralized reporting - -3. **Third Phase: Integration with Existing Code** - - Update NDKSubscription's `eventReceived` method to use sampling - - Wire up the blacklisting functionality - - Update NDK constructor and interfaces - -4. **Fourth Phase: Testing** - - Create unit tests for ratio calculation - - Test integration with different ratio configurations - - Verify behavior with intentionally invalid signatures - -5. **Fifth Phase: Documentation and Examples** - - Update README and API documentation - - Create examples for different use cases - -## Testing Plan - -### Utilizing NDK Test Utilities - -The `ndk-test-utils` package provides several useful tools for testing our implementation: - -1. **RelayMock**: We'll use this to simulate relays sending both valid and invalid signatures -2. **EventGenerator**: Helps create test events with controlled properties -3. **TestFixture**: Provides a complete test environment with mock relays and events -4. **TimeController**: Useful for testing time-dependent behavior in our ratio calculation - -### Unit Tests - -1. **Validation Ratio Calculation** - ```typescript - import { TestFixture, EventGenerator } from "@nostr-dev-kit/ndk/test"; - - test('validation ratio decreases with successful validations', () => { - const fixture = new TestFixture(); - const ndk = fixture.ndk; - ndk.initialValidationRatio = 1.0; - ndk.lowestValidationRatio = 0.1; - - const relay = new NDKRelay('wss://example.com', undefined, ndk); - - // Initial ratio should be 1.0 - expect(relay.shouldValidateEvent()).toBe(true); - - // Add 100 validated events using EventGenerator - const eventGenerator = new EventGenerator(); - const events = eventGenerator.generateEvents(100); // Generate 100 valid events - - // Simulate validation - for (const event of events) { - relay.addValidatedEvent(); - } - - // Ratio should decrease but still be probabilistic - // Run multiple checks to verify the ratio is roughly as expected - let validationCount = 0; - for (let i = 0; i < 1000; i++) { - if (relay.shouldValidateEvent()) validationCount++; - } - - // With 100 validated events, we expect the ratio to be lower than initial - // but still above the minimum - expect(validationCount).toBeGreaterThan(100); // should be more than minimum - expect(validationCount).toBeLessThan(900); // should be less than initial - }); -```` - -2. **Custom Validation Function** - - ```typescript - import { TestFixture } from "@nostr-dev-kit/ndk/test"; - - test("custom validation function is applied", () => { - // Creating a custom function that always returns 0.5 - const customFn = () => 0.5; - - const fixture = new TestFixture({ - ndkOptions: { - initialValidationRatio: 1.0, - lowestValidationRatio: 0.1, - validationRatioFn: customFn, - }, - }); - - const relay = new NDKRelay("wss://example.com", undefined, fixture.ndk); - - // Validate multiple times to check probability is ~0.5 - let validationCount = 0; - for (let i = 0; i < 1000; i++) { - if (relay.shouldValidateEvent()) validationCount++; - } - - // Should be roughly 50% - expect(validationCount).toBeGreaterThan(400); - expect(validationCount).toBeLessThan(600); - }); - ``` - -### Integration Tests - -1. **Invalid Signature Detection** - - ```typescript - import { RelayMock, EventGenerator } from "@nostr-dev-kit/ndk/test"; - - test("detects and reports invalid signatures", async () => { - // Create NDK instance with test configuration - const ndk = new NDK({ initialValidationRatio: 1.0 }); - - // Create a mock relay - const mockRelay = new RelayMock(ndk, { url: "wss://mock.com" }); - - // Spy on reportInvalidSignature - const reportSpy = jest.spyOn(ndk, "reportInvalidSignature"); - - // Create event with invalid signature using EventGenerator - const eventGenerator = new EventGenerator(); - const eventData = eventGenerator.generateEvent(); - - // Modify signature to be invalid - eventData.sig = "invalid-signature"; - - // Create NDKEvent and subscription - const event = new NDKEvent(ndk, eventData); - const sub = new NDKSubscription(ndk, { kinds: [1] }); - - // Process the event as if received from the relay - sub.eventReceived(event, mockRelay); - - // Verify reportInvalidSignature was called with correct parameters - expect(reportSpy).toHaveBeenCalledWith(expect.any(NDKEvent), mockRelay); - - // Verify the relay is considered malicious after a single invalid signature - if (ndk.autoBlacklistInvalidRelays) { - expect(ndk.blacklistRelayUrls).toContain(mockRelay.url); - } - }); - ``` - -2. **Event Emitting and Blacklisting Test** - - ```typescript - import { RelayMock, EventGenerator } from "@nostr-dev-kit/ndk/test"; - - test("emits event:invalid-sig event with relay and can blacklist", async () => { - // Create NDK with auto blacklisting - const ndk = new NDK({ - autoBlacklistInvalidRelays: true, - }); - - // Create mock relay - const mockRelay = new RelayMock(ndk, { url: "wss://mock.com" }); - - // Create listener for the event - const listener = jest.fn(); - ndk.on("event:invalid-sig", listener); - - // Generate event - const eventGenerator = new EventGenerator(); - const event = new NDKEvent(ndk, eventGenerator.generateEvent()); - - // Trigger invalid signature report - ndk.reportInvalidSignature(event, mockRelay); - - // Verify listener was called with correct args - expect(listener).toHaveBeenCalledWith(event, mockRelay); - - // Verify relay was blacklisted - expect(ndk.blacklistRelayUrls).toContain(mockRelay.url); - }); - ``` - -3. **Testing with Time Control** - - ```typescript - import { TestFixture, withTimeControl } from "@nostr-dev-kit/ndk/test"; - - test( - "ratio calculation over time", - withTimeControl(async ({ advanceTime }) => { - const fixture = new TestFixture(); - const ndk = fixture.ndk; - ndk.initialValidationRatio = 1.0; - ndk.lowestValidationRatio = 0.1; - - const relay = new NDKRelay("wss://example.com", undefined, ndk); - - // Add validated events over simulated time - for (let i = 0; i < 5; i++) { - relay.addValidatedEvent(); - // Advance time by 1 hour - await advanceTime(60 * 60 * 1000); - } - - // Check ratio is still high with just a few validations - let validationCount = 0; - for (let i = 0; i < 100; i++) { - if (relay.shouldValidateEvent()) validationCount++; - } - - // Should still be high with just 5 events - expect(validationCount).toBeGreaterThan(80); - - // Add many more validated events - for (let i = 0; i < 95; i++) { - relay.addValidatedEvent(); - } - - // Advance time by 1 day - await advanceTime(24 * 60 * 60 * 1000); - - // Check ratio has decreased significantly - validationCount = 0; - for (let i = 0; i < 100; i++) { - if (relay.shouldValidateEvent()) validationCount++; - } - - // Should now be much lower after 100 total validated events - expect(validationCount).toBeLessThan(50); - expect(validationCount).toBeGreaterThan(10); // But not below minimum - }), - ); - ``` - -4. **Testing Async Signature Verification** - - ```typescript - import { RelayMock, EventGenerator } from "@nostr-dev-kit/ndk/test"; - - test("centralizes invalid signature reporting from async verification", async () => { - // Create NDK with async verification - const worker = new Worker("path/to/signature-worker.js"); - const ndk = new NDK({ - signatureVerificationWorker: worker, - autoBlacklistInvalidRelays: true, - }); - - // Create a mock relay - const mockRelay = new RelayMock(ndk, { url: "wss://mock.com" }); - - // Spy on reportInvalidSignature - const reportSpy = jest.spyOn(ndk, "reportInvalidSignature"); - - // Generate event with invalid signature - const eventGenerator = new EventGenerator(); - const event = eventGenerator.generateEvent(); - event.sig = "invalid-signature"; - - // Mock the worker verification process - // This would normally be handled by the worker messaging - const ndkEvent = new NDKEvent(ndk, event); - ndkEvent.relay = mockRelay; - - // Simulate worker message for invalid signature - // (In reality this would happen asynchronously) - ndk.reportInvalidSignature(ndkEvent, mockRelay); - - // Verify reportInvalidSignature was called with correct parameters - expect(reportSpy).toHaveBeenCalledWith(ndkEvent, mockRelay); - - // Verify relay was blacklisted - expect(ndk.blacklistRelayUrls).toContain(mockRelay.url); - }); - ``` - -## Conclusion - -This implementation plan provides a comprehensive approach to signature verification sampling in NDK. By validating only a sample of signatures from each relay, we can significantly improve performance while maintaining security. - -The centralized `reportInvalidSignature` method ensures consistent handling of invalid signatures, regardless of whether they are detected synchronously during event processing or asynchronously by a web worker. This maintains the security model where a single invalid signature is sufficient to identify a malicious relay. - -By extending the existing `event:invalid-sig` event to include relay information, we maintain backward compatibility while providing the necessary context to identify and potentially blacklist malicious relays. - -The implementation is flexible, allowing developers to configure the validation ratio parameters or provide their own custom ratio calculation function to suit their specific needs and threat models. From d1fc7b6316be2b73d4861f6c554a4d50de74e5fb Mon Sep 17 00:00:00 2001 From: digitalbase Date: Sun, 23 Nov 2025 13:28:27 +0100 Subject: [PATCH 103/139] Remove old outbox.md file (documented in connecting outbox) --- core/OUTBOX.md | 7 ------- 1 file changed, 7 deletions(-) delete mode 100644 core/OUTBOX.md diff --git a/core/OUTBOX.md b/core/OUTBOX.md deleted file mode 100644 index b029e38df..000000000 --- a/core/OUTBOX.md +++ /dev/null @@ -1,7 +0,0 @@ -# Outbox model - -NDK defines a set of seeding relays, these are relays that will be exclusively used to request Outbox model events. These are kept in a separate pool. - -NDK automatically fetches gossip information for users when they are included in an `authors` filter enough times or when they are explicitly scored with the right value. - -When a filter users the `authors` field From d2b82aa6180cf68d94d999c03b376ba515bce2c4 Mon Sep 17 00:00:00 2001 From: digitalbase Date: Sun, 23 Nov 2025 13:30:26 +0100 Subject: [PATCH 104/139] Move all examples (from different places) to snippets/examples --- core/docs/{ => snippets}/examples/ai-guardrails-example.ts | 0 .../snippets/{ => examples}/quick-start-with-guardrails.ts | 0 .../{ => snippets}/examples/replaceable-event-guardrail.ts | 0 core/{ => docs/snippets/examples}/test-auth-publish.ts | 6 +++--- 4 files changed, 3 insertions(+), 3 deletions(-) rename core/docs/{ => snippets}/examples/ai-guardrails-example.ts (100%) rename core/docs/snippets/{ => examples}/quick-start-with-guardrails.ts (100%) rename core/docs/{ => snippets}/examples/replaceable-event-guardrail.ts (100%) rename core/{ => docs/snippets/examples}/test-auth-publish.ts (96%) diff --git a/core/docs/examples/ai-guardrails-example.ts b/core/docs/snippets/examples/ai-guardrails-example.ts similarity index 100% rename from core/docs/examples/ai-guardrails-example.ts rename to core/docs/snippets/examples/ai-guardrails-example.ts diff --git a/core/docs/snippets/quick-start-with-guardrails.ts b/core/docs/snippets/examples/quick-start-with-guardrails.ts similarity index 100% rename from core/docs/snippets/quick-start-with-guardrails.ts rename to core/docs/snippets/examples/quick-start-with-guardrails.ts diff --git a/core/docs/examples/replaceable-event-guardrail.ts b/core/docs/snippets/examples/replaceable-event-guardrail.ts similarity index 100% rename from core/docs/examples/replaceable-event-guardrail.ts rename to core/docs/snippets/examples/replaceable-event-guardrail.ts diff --git a/core/test-auth-publish.ts b/core/docs/snippets/examples/test-auth-publish.ts similarity index 96% rename from core/test-auth-publish.ts rename to core/docs/snippets/examples/test-auth-publish.ts index 207e17846..61f92b405 100755 --- a/core/test-auth-publish.ts +++ b/core/docs/snippets/examples/test-auth-publish.ts @@ -5,9 +5,9 @@ * Usage: bun run test-auth-publish.ts --nsec --msg "your message" */ -import { NDKEvent } from "./src/events"; -import { NDK } from "./src/ndk"; -import { NDKPrivateKeySigner } from "./src/signers/private-key"; +import {NDKEvent} from "./src/events"; +import {NDK} from "./src/ndk"; +import {NDKPrivateKeySigner} from "./src/signers/private-key"; // Parse command line arguments const args = process.argv.slice(2); From da860c6444c810fae08fb36ff24f6cff5edb1315 Mon Sep 17 00:00:00 2001 From: digitalbase Date: Sun, 23 Nov 2025 13:31:34 +0100 Subject: [PATCH 105/139] Delete dupe internal doc (subscription internals) --- core/docs/internals/subscriptions.md | 325 --------------------------- 1 file changed, 325 deletions(-) delete mode 100644 core/docs/internals/subscriptions.md diff --git a/core/docs/internals/subscriptions.md b/core/docs/internals/subscriptions.md deleted file mode 100644 index b60872965..000000000 --- a/core/docs/internals/subscriptions.md +++ /dev/null @@ -1,325 +0,0 @@ -# Subscriptions Lifecycle - -When an application creates a subscription a lot of things happen under the hood. - -Say we want to see `kind:1` events from pubkeys `123`, `456`, and `678`. - -```ts -const subscription = ndk.subscribe({ kinds: [1], authors: ["123", "456", "678"] }); -``` - -Since the application level didn't explicitly provide a relay-set, which is the most common use case, NDK will calculate -a relay set based on the outbox model plus a variety of some other factors. - -## Relay Selection for Authors - -When a subscription includes an `authors` filter, NDK uses the outbox model to determine which relays to query for each -author. By default, NDK will query **2 relays** for each author, but this can be customized using the -`relayGoalPerAuthor` option: - -```ts -// Query 3 relays for each author instead of the default 2 -const subscription = ndk.subscribe( - { kinds: [1], authors: ["123", "456", "678"] }, - { relayGoalPerAuthor: 3 } -); - -// Use all available relays for each author -const subscription = ndk.subscribe( - { kinds: [1], authors: ["123", "456", "678"] }, - { relayGoalPerAuthor: Infinity } -); -``` - -Higher values improve redundancy and reduce the chance of missing events, but increase bandwidth usage and the number of -relay connections. Lower values reduce resource usage but may miss events if a relay is down or doesn't have the event. -Setting `relayGoalPerAuthor: Infinity` will query all available relays for each author. - -So the first thing we'll do before talking to relays is, decide to _which_ relays we should talk to. - -The `calculateRelaySetsFromFilters` function will take care of this and provide us with a map of relay URLs and filters -for each relay. - -This means that the query, as specified by the client might be broken into distinct queries specialized for the -different relays. - -For example, if we have 3 relays, and the query is for `kind:1` events from pubkeys `a` and `b`, the -`calculateRelaySetsFromFilters` function might return something like this: - -```ts -{ - "wss://relay1": { kinds: [1], authors: [ "a" ] }, - "wss://relay2": { kinds: [1], authors: [ "b" ] }, -} -``` - -```mermaid -flowchart TD - Client -->|"kinds: [1], authors: [a, b]"| Subscription1 - Subscription1 -->|"kinds: [1], authors: [a]"| wss://relay1 - Subscription1 -->|"kinds: [1], authors: [b]"| wss://relay2 -``` - -## Subscription bundling - -Once the subscription has been split into the filters each relay should receive, the filters are sent to the individual -`NDKRelay`'s `NDKRelaySubscriptionManager` instances. - -`NDKRelaySubscriptionManager` is responsible for keeping track of the active and scheduled subscriptions that are -pending to be executed within an individual relay. - -This is an important aspect to consider: - -> `NDKSubscription` have a different lifecycle than `NDKRelaySubscription`. For example, a subscription that is set to -> close after EOSE might still be active within the `NDKSubscription` lifecycle, but it might have been already been -> closed within the `NDKRelaySubscription` lifecycle, since NDK attempts to keep the minimum amount of open subscriptions -> at any given time. - -## NDKRelaySubscription - -Most NDK subscriptions (by default) are set to be executed with a grouping delay. Will cover what this looks like in -practice later, but for now, let's understand than when the `NDKRelaySubscriptionManager` receives an order, it might -not execute it right away. - -The different filters that can be grouped together (thus executed as a single `REQ` within a relay) are grouped within -the same `NDKRelaySubscription` instance and the execution scheduler is computed respecting what each individual -`NDKSubscription` has requested. - -(For example, if a subscription with a `groupingDelay` of `at-least` 500 millisecond has been grouped with another -subscription with a `groupingDelay` of `at-least` 1000 milliseconds, the `NDKRelaySubscriptionManager` will wait 1000 ms -before sending the `REQ` to this particular relay). - -### Execution - -Once the filter is executed at the relay level, the `REQ` is submitted into that relay's `NDKRelayConnectivity` -instance, which will take care of monitoring for responses for this particular REQ and communicate them back into the -`NDKRelaySubscription` instance. - -Each `EVENT` that comes back as a response to our `REQ` within this `NDKRelaySubscription` instance is sent to the -top-level `NDKSubscriptionManager`. This manager tracks ALL active subscriptions and when events come in dispatches the -event to all `NDKSubscription`s interested in this event. - -# Example - -If an application requests `kind:1` of pubkeys `123`, `456`, and `789`. It creates an `NDKSubscription`: - -```ts -ndk.subscribe( - { kinds: [1], authors: ["123", "456", "789"] }, - { groupableDelay: 500, groupableDelayType: "at-least" } -); -// results in NDKSubscription1 with filters { kinds: [1], authors: [ "123", "456", "789" ] } -``` - -Some other part of the application requests a kind:7 from pubkey `123` at the same time. - -```ts -ndk.subscribe( - { kinds: [7], authors: ["123"] }, - { groupableDelay: 500, groupableDelayType: "at-most" } -); -// results in NDKSubscription2 with filters { kinds: [7], authors: [ "123" ] } -``` - -```mermaid -flowchart TD - subgraph Subscriptions Lifecycle - A[Application] -->|"kinds: [1], authors: [123, 456, 678], groupingDelay: at-least 500ms"| B[NDKSubscription1] - - A2[Application] -->|"kinds: [7], authors: [123], groupingDelay: at-most 1000ms"| B2[NDKSubscription2] - end -``` - -Both subscriptions have their relayset calculated by NDK and, the resulting filters are sent into the -`NDKRelaySubscriptionManager`, which will decide what, and how filters can be grouped. - -```mermaid -flowchart TD - subgraph Subscriptions Lifecycle - A[Application] -->|"kinds: [1], authors: [123, 456, 678], groupingDelay: at-least 500ms"| B[NDKSubscription1] - B --> C{Calculate Relay Sets} - - A2[Application] -->|"kinds: [7], authors: [123], groupingDelay: at-most 1000ms"| B2[NDKSubscription2] - B2 --> C2{Calculate Relay Sets} - end - - subgraph Subscription Bundling - C -->|"kinds: [1], authors: [123]"| E1[wss://relay1 NDKRelaySubscriptionManager] - C -->|"kinds: [1], authors: [456]"| E2[wss://relay2 NDKRelaySubscriptionManager] - C -->|"kinds: [1], authors: [678]"| E3[wss://relay3 NDKRelaySubscriptionManager] - - C2 -->|"kinds: [7], authors: [123]"| E1 - end -``` - -The `NDKRelaySubscriptionManager` will create `NDKRelaySubscription` instances, or add filters to them if -`NDKRelaySubscription` with the same filter fingerprint exists. - -```mermaid -flowchart TD - subgraph Subscriptions Lifecycle - A[Application] -->|"kinds: [1], authors: [123, 456, 678], groupingDelay: at-least 500ms"| B[NDKSubscription1] - B --> C{Calculate Relay Sets} - - A2[Application] -->|"kinds: [7], authors: [123], groupingDelay: at-most 1000ms"| B2[NDKSubscription2] - B2 --> C2{Calculate Relay Sets} - end - - subgraph Subscription Bundling - C -->|"kinds: [1], authors: [123]"| E1[wss://relay1 NDKRelaySubscriptionManager] - C -->|"kinds: [1], authors: [456]"| E2[wss://relay2 NDKRelaySubscriptionManager] - C -->|"kinds: [1], authors: [678]"| E3[wss://relay3 NDKRelaySubscriptionManager] - - C2 -->|"kinds: [7], authors: [123]"| E1 - - E1 -->|"Grouping Delay: at-most 1000ms"| F1[NDKRelaySubscription] - E2 -->|"Grouping Delay: at-least 500ms"| F2[NDKRelaySubscription] - E3 -->|"Grouping Delay: at-least 500ms"| F3[NDKRelaySubscription] - end -``` - -Each individual `NDKRelaySubscription` computes the execution schedule of the filters it has received and sends them to -the `NDKRelayConnectivity` instance, which in turns sends the `REQ` to the relay. - -```mermaid -flowchart TD - subgraph Subscriptions Lifecycle - A[Application] -->|"kinds: [1], authors: [123, 456, 678], groupingDelay: at-least 500ms"| B[NDKSubscription1] - B --> C{Calculate Relay Sets} - - A2[Application] -->|"kinds: [7], authors: [123], groupingDelay: at-most 1000ms"| B2[NDKSubscription2] - B2 --> C2{Calculate Relay Sets} - end - - subgraph Subscription Bundling - C -->|"kinds: [1], authors: [123]"| E1[wss://relay1 NDKRelaySubscriptionManager] - C -->|"kinds: [1], authors: [456]"| E2[wss://relay2 NDKRelaySubscriptionManager] - C -->|"kinds: [1], authors: [678]"| E3[wss://relay3 NDKRelaySubscriptionManager] - - C2 -->|"kinds: [7], authors: [123]"| E1 - - E1 -->|"Grouping Delay: at-most 1000ms"| F1[NDKRelaySubscription] - E2 -->|"Grouping Delay: at-least 500ms"| F2[NDKRelaySubscription] - E3 -->|"Grouping Delay: at-least 500ms"| F3[NDKRelaySubscription] - - F1 -->|"REQ: kinds: [1, 7], authors: [123]"| G1[NDKRelayConnectivity] - F2 -->|"REQ: kinds: [1], authors: [456]"| G2[NDKRelayConnectivity] - F3 -->|"REQ: kinds: [1], authors: [678]"| G3[NDKRelayConnectivity] - end - - subgraph Execution - G1 -->|"Send REQ to wss://relay1 after 1000ms"| R1[Relay1] - G2 -->|"Send REQ to wss://relay2 after 500ms"| R2[Relay2] - G3 -->|"Send REQ to wss://relay3 after 500ms"| R3[Relay3] - end -``` - -As the events come from the relays, `NDKRelayConnectivity` will send them back to the `NDKRelaySubscription` instance, -which will compare the event with the filters of the `NDKSubscription` instances that have been grouped together and -send the received event back to the correct `NDKSubscription` instance. - -```mermaid -flowchart TD - subgraph Subscriptions Lifecycle - A[Application] -->|"kinds: [1], authors: [123, 456, 678], groupingDelay: at-least 500ms"| B[NDKSubscription1] - B --> C{Calculate Relay Sets} - - A2[Application] -->|"kinds: [7], authors: [123], groupingDelay: at-most 1000ms"| B2[NDKSubscription2] - B2 --> C2{Calculate Relay Sets} - end - - subgraph Subscription Bundling - C -->|"kinds: [1], authors: [123]"| E1[wss://relay1 NDKRelaySubscriptionManager] - C -->|"kinds: [1], authors: [456]"| E2[wss://relay2 NDKRelaySubscriptionManager] - C -->|"kinds: [1], authors: [678]"| E3[wss://relay3 NDKRelaySubscriptionManager] - - C2 -->|"kinds: [7], authors: [123]"| E1 - - E1 -->|"Grouping Delay: at-most 1000ms"| F1[NDKRelaySubscription] - E2 -->|"Grouping Delay: at-least 500ms"| F2[NDKRelaySubscription] - E3 -->|"Grouping Delay: at-least 500ms"| F3[NDKRelaySubscription] - - F1 -->|"REQ: kinds: [1, 7], authors: [123]"| G1[NDKRelayConnectivity] - F2 -->|"REQ: kinds: [1], authors: [456]"| G2[NDKRelayConnectivity] - F3 -->|"REQ: kinds: [1], authors: [678]"| G3[NDKRelayConnectivity] - end - - subgraph Execution - G1 -->|"Send REQ to wss://relay1 after 1000ms"| R1[Relay1] - G2 -->|"Send REQ to wss://relay2 after 500ms"| R2[Relay2] - G3 -->|"Send REQ to wss://relay3 after 500ms"| R3[Relay3] - - R1 -->|"EVENT: kinds: [1]"| H1[NDKRelaySubscription] - R1 -->|"EVENT: kinds: [7]"| H2[NDKRelaySubscription] - R2 -->|"EVENT"| H3[NDKRelaySubscription] - R3 -->|"EVENT"| H4[NDKRelaySubscription] - - H1 -->|"Matched Filters: kinds: [1]"| I1[NDKSubscription1] - H2 -->|"Matched Filters: kinds: [7]"| I2[NDKSubscription2] - H3 -->|"Matched Filters: kinds: [1]"| I1 - H4 -->|"Matched Filters: kinds: [1]"| I1 - end -``` - -## Handling Subscription Events - -When creating a subscription using `ndk.subscribe`, you can provide handlers for different stages of the subscription -lifecycle directly within the options or the `autoStart` parameter. - -```typescript -interface NDKSubscriptionEventHandlers { - /** - * Called for each individual event received after the initial cache load (if applicable) - * or for all events if onEvents is not provided. - */ - onEvent?: (event: NDKEvent, relay?: NDKRelay) => void; - - /** - * Called *once* with all events found synchronously in the cache when the subscription starts. - * If this handler is provided, `onEvent` will *not* be called for these initial cached events. - * This is useful for bulk processing or batching UI updates. - */ - onEvents?: (events: NDKEvent[]) => void; - - /** - * Called when the subscription receives an EOSE (End of Stored Events) marker - * from all connected relays for this subscription request. - */ - onEose?: (sub: NDKSubscription) => void; -} - -// Example passing handlers directly (preferred method) -ndk.subscribe( - filters, - { // Options can include explicit relays now - closeOnEose: true, - // relayUrls: ["wss://explicit.relay"] // Optionally specify relays here - }, - { // Pass handlers via the autoStart parameter (now the 3rd argument) - onEvent: (event) => { - console.log("Received event:", event.id); - }, - onEvents: (events) => { // Renamed parameter - console.log(`Received ${events.length} events from cache initially.`); - // Process the batch of cached events here - }, - onEose: (subscription) => { - console.log("Subscription reached EOSE:", subscription.internalId); - } - } -); -``` - -### Bulk Cache Event Handling (`onEvents`) - -A key feature is the behavior when using the `onEvents` handler. If NDK has a cache adapter configured and finds events -matching the subscription filter synchronously in the cache upon starting the subscription: - -1. The `onEvents` handler will be called exactly once with an array containing all these cached `NDKEvent` objects. -2. The regular `onEvent` handler will *not* be called for this initial batch of cached events. -3. After this initial batch, `onEvent` will be called for any subsequent events received from relays or asynchronous - cache updates. - -This allows applications to efficiently process the initial state from the cache in one go, which can be particularly -beneficial for UI frameworks to avoid multiple re-renders that might occur if `onEvent` were called for each cached item -individually. If `onEvents` is *not* provided, `onEvent` will be called for every event, including those from the cache. From 104e5f743b6e362d7fc3f20580cbf871796e0044 Mon Sep 17 00:00:00 2001 From: digitalbase Date: Sun, 23 Nov 2025 13:34:01 +0100 Subject: [PATCH 106/139] Link to advanced subscription internal doc --- core/docs/fundamentals/subscribing.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/core/docs/fundamentals/subscribing.md b/core/docs/fundamentals/subscribing.md index ea0ea1a5d..03b16f0d3 100644 --- a/core/docs/fundamentals/subscribing.md +++ b/core/docs/fundamentals/subscribing.md @@ -3,6 +3,9 @@ Once [connected](/core/docs/getting-started/usage#connecting), you can subscribe to events using `ndk.subscribe()` by providing filters you can specify the events you're interested in. +More about how this all works +in [the dedicated section about subscription internals](/core/docs/advanced/subscription-internals.md). + ## Subscribe The `ndk.subscribe()` method accepts these parameters: From 25e22f65e665a27713e0cd72c1e954c23c582b1e Mon Sep 17 00:00:00 2001 From: digitalbase Date: Sun, 23 Nov 2025 13:35:37 +0100 Subject: [PATCH 107/139] Delete dupe docs --- core/docs/advanced/ai-guardrails.md | 115 +++--- core/docs/ai-guardrails.md | 524 -------------------------- core/docs/event-class-registration.md | 183 --------- 3 files changed, 69 insertions(+), 753 deletions(-) delete mode 100644 core/docs/ai-guardrails.md delete mode 100644 core/docs/event-class-registration.md diff --git a/core/docs/advanced/ai-guardrails.md b/core/docs/advanced/ai-guardrails.md index aa0eca813..da5634589 100644 --- a/core/docs/advanced/ai-guardrails.md +++ b/core/docs/advanced/ai-guardrails.md @@ -1,6 +1,7 @@ # AI Guardrails -AI Guardrails is a runtime validation system designed to catch common mistakes when using NDK, especially those made by LLMs (Large Language Models) generating code. +AI Guardrails is a runtime validation system designed to catch common mistakes when using NDK, especially those made by +LLMs (Large Language Models) generating code. ## Overview @@ -10,7 +11,8 @@ AI Guardrails provides: - **Zero performance impact** - Disabled by default, opt-in only - **Granular control** - Enable all checks or selectively disable specific ones - **LLM-friendly** - Designed to help AI-generated code self-correct -- **Always visible** - Both errors and warnings throw exceptions AND log to console.error (so AIs see them even if throws get swallowed) +- **Always visible** - Both errors and warnings throw exceptions AND log to console.error (so AIs see them even if + throws get swallowed) ## Quick Start @@ -18,13 +20,13 @@ AI Guardrails provides: import NDK from "@nostr-dev-kit/ndk"; // Enable all guardrails (recommended for development) -const ndk = new NDK({ aiGuardrails: true }); +const ndk = new NDK({aiGuardrails: true}); // Or enable with exceptions const ndk = new NDK({ - aiGuardrails: { - skip: new Set(['filter-large-limit', 'fetch-events-usage']) - } + aiGuardrails: { + skip: new Set(['filter-large-limit', 'fetch-events-usage']) + } }); // Or programmatically control @@ -37,6 +39,7 @@ ndk.aiGuardrails.enable('filter-bech32-in-array'); ### Filter-Related Checks #### `filter-bech32-in-array` + **Level:** Error Catches bech32-encoded values in filter arrays. Filters expect hex values, not bech32. @@ -44,39 +47,43 @@ Catches bech32-encoded values in filter arrays. Filters expect hex values, not b ```typescript // โŒ WRONG ndk.subscribe({ - authors: ['npub1...'] // bech32 npub + authors: ['npub1...'] // bech32 npub }); // โœ… CORRECT -import { nip19 } from 'nostr-tools'; -const { data } = nip19.decode('npub1...'); +import {nip19} from 'nostr-tools'; + +const {data} = nip19.decode('npub1...'); ndk.subscribe({ - authors: [data] // hex pubkey + authors: [data] // hex pubkey }); // Or use filterFromId for complete bech32 entities -import { filterFromId } from '@nostr-dev-kit/ndk'; +import {filterFromId} from '@nostr-dev-kit/ndk'; + const filter = filterFromId('nevent1...'); ndk.subscribe(filter); ``` #### `filter-only-limit` + **Level:** Error Catches filters with only a `limit` parameter and no filtering criteria. ```typescript // โŒ WRONG - will fetch random events -ndk.subscribe({ limit: 10 }); +ndk.subscribe({limit: 10}); // โœ… CORRECT ndk.subscribe({ - kinds: [1], - limit: 10 + kinds: [1], + limit: 10 }); ``` #### `filter-large-limit` + **Level:** Warning Warns about very large limit values that can cause performance issues. @@ -84,18 +91,19 @@ Warns about very large limit values that can cause performance issues. ```typescript // โš ๏ธ WARNING ndk.subscribe({ - kinds: [1], - limit: 10000 // Too large! + kinds: [1], + limit: 10000 // Too large! }); // โœ… BETTER ndk.subscribe({ - kinds: [1], - limit: 100 // More reasonable + kinds: [1], + limit: 100 // More reasonable }); ``` #### `filter-empty` + **Level:** Error Catches completely empty filters. @@ -105,10 +113,11 @@ Catches completely empty filters. ndk.subscribe({}); // โœ… CORRECT -ndk.subscribe({ kinds: [1] }); +ndk.subscribe({kinds: [1]}); ``` #### `filter-since-after-until` + **Level:** Error Catches filters where `since` is after `until`, which would match zero events. @@ -116,18 +125,19 @@ Catches filters where `since` is after `until`, which would match zero events. ```typescript // โŒ WRONG ndk.subscribe({ - since: 1000000, - until: 500000 + since: 1000000, + until: 500000 }); // โœ… CORRECT ndk.subscribe({ - since: 500000, - until: 1000000 + since: 500000, + until: 1000000 }); ``` #### `filter-invalid-a-tag` + **Level:** Error Catches malformed `#a` tag values. Must be `kind:pubkey:d-tag` format. @@ -135,35 +145,36 @@ Catches malformed `#a` tag values. Must be `kind:pubkey:d-tag` format. ```typescript // โŒ WRONG ndk.subscribe({ - '#a': ['nevent1...'] // bech32 instead of address + '#a': ['nevent1...'] // bech32 instead of address }); // โœ… CORRECT ndk.subscribe({ - '#a': ['30023:fa984bd7...:my-article'] + '#a': ['30023:fa984bd7...:my-article'] }); ``` ### fetchEvents Anti-Pattern #### `fetch-events-usage` + **Level:** Warning Warns about using `fetchEvents()` which is a blocking operation. ```typescript // โš ๏ธ SUBOPTIMAL - blocks until EOSE -const events = await ndk.fetchEvents({ kinds: [1] }); +const events = await ndk.fetchEvents({kinds: [1]}); // โœ… BETTER - reactive, non-blocking -ndk.subscribe({ kinds: [1] }, { - onEvent: (event) => { - console.log('Got event:', event); - } +ndk.subscribe({kinds: [1]}, { + onEvent: (event) => { + console.log('Got event:', event); + } }); // Or for single events -const event = await ndk.fetchEvent({ kinds: [1] }); +const event = await ndk.fetchEvent({kinds: [1]}); ``` **When to disable:** If you truly need to block until all events arrive (rare). @@ -176,6 +187,7 @@ const events = await ndk.fetchEvents(filter); ### Event Construction Checks #### `event-missing-kind` + **Level:** Error Catches attempts to sign events without a `kind`. @@ -194,6 +206,7 @@ await event.sign(); ``` #### `event-content-is-object` + **Level:** Error Catches attempts to set event content to an object instead of a string. @@ -202,17 +215,18 @@ Catches attempts to set event content to an object instead of a string. // โŒ WRONG const event = new NDKEvent(ndk); event.kind = 30023; -event.content = { title: "My Article" }; // Object! +event.content = {title: "My Article"}; // Object! await event.sign(); // Error! // โœ… CORRECT const event = new NDKEvent(ndk); event.kind = 30023; -event.content = JSON.stringify({ title: "My Article" }); +event.content = JSON.stringify({title: "My Article"}); await event.sign(); ``` #### `event-param-replaceable-no-dtag` + **Level:** Warning Warns about parameterized replaceable events (kinds 30000-39999) without a d-tag. @@ -233,6 +247,7 @@ await event.sign(); ``` #### `event-created-at-milliseconds` + **Level:** Error Catches using milliseconds instead of seconds for `created_at`. @@ -256,6 +271,7 @@ await event.sign(); ### Tag Validation Checks #### `tag-invalid-p-tag` + **Level:** Error Catches invalid p-tags (must be 64-character hex pubkeys). @@ -275,6 +291,7 @@ await event.sign(); ``` #### `tag-invalid-e-tag` + **Level:** Error Catches invalid e-tags (must be 64-character hex event IDs). @@ -294,6 +311,7 @@ await event.sign(); ``` #### `event-manual-reply-markers` + **Level:** Warning Warns about manually adding e-tags with reply/root markers instead of using `.reply()`. @@ -344,7 +362,7 @@ ndk.aiGuardrails.setMode(true); // Or enable with specific skips ndk.aiGuardrails.setMode({ - skip: new Set(['filter-large-limit']) + skip: new Set(['filter-large-limit']) }); // Disable entirely @@ -354,6 +372,7 @@ ndk.aiGuardrails.setMode(false); ## Error Messages When a guardrail is triggered, it will: + 1. **Log to console.error** (visible even if the exception is caught) 2. **Throw an Error** (stops execution) @@ -362,6 +381,7 @@ Both "ERROR" and "WARNING" level checks throw exceptions - warnings are not just ### Fatal vs Non-Fatal Errors Some errors are **fatal** - they represent fundamental mistakes that cannot be bypassed: + - Missing event kind - Content as object instead of string - Timestamps in milliseconds instead of seconds @@ -399,8 +419,8 @@ Enable all guardrails during development: ```typescript const ndk = new NDK({ - aiGuardrails: true, - // ... other options + aiGuardrails: true, + // ... other options }); ``` @@ -410,8 +430,8 @@ Keep guardrails disabled in production for zero performance impact: ```typescript const ndk = new NDK({ - aiGuardrails: process.env.NODE_ENV === 'development', - // ... other options + aiGuardrails: process.env.NODE_ENV === 'development', + // ... other options }); ``` @@ -422,7 +442,10 @@ If you're using AI to generate code, enable guardrails and let the AI learn from ```typescript // In your prompt/system message: "When NDK throws an AI_GUARDRAILS error, read the error message carefully. -It explains what's wrong and how to fix it. Update your code accordingly." +It +explains +what +'s wrong and how to fix it. Update your code accordingly." ``` The AI can also programmatically skip checks it knows are safe: @@ -430,7 +453,7 @@ The AI can also programmatically skip checks it knows are safe: ```typescript // LLM can disable specific checks when it knows what it's doing ndk.aiGuardrails.skip('filter-large-limit'); // I know I need 5000 events -ndk.subscribe({ kinds: [1], limit: 5000 }); +ndk.subscribe({kinds: [1], limit: 5000}); ``` ## Complete Check ID Reference @@ -438,7 +461,7 @@ ndk.subscribe({ kinds: [1], limit: 5000 }); Import these for type-safe check IDs: ```typescript -import { GuardrailCheckId } from '@nostr-dev-kit/ndk'; +import {GuardrailCheckId} from '@nostr-dev-kit/ndk'; // All available check IDs: GuardrailCheckId.FILTER_BECH32_IN_ARRAY @@ -497,10 +520,10 @@ Example: ```typescript if (ndk?.aiGuardrails.isEnabled()) { - ndk.aiGuardrails.error( - GuardrailCheckId.YOUR_NEW_CHECK, - "Clear explanation of what's wrong", - "Helpful hint on how to fix it" - ); + ndk.aiGuardrails.error( + GuardrailCheckId.YOUR_NEW_CHECK, + "Clear explanation of what's wrong", + "Helpful hint on how to fix it" + ); } ``` diff --git a/core/docs/ai-guardrails.md b/core/docs/ai-guardrails.md deleted file mode 100644 index b01529bba..000000000 --- a/core/docs/ai-guardrails.md +++ /dev/null @@ -1,524 +0,0 @@ -# AI Guardrails - -AI Guardrails is a runtime validation system designed to catch common mistakes when using NDK, especially those made by -LLMs (Large Language Models) generating code. - -## Overview - -AI Guardrails provides: - -- **Educational error messages** - Clear explanations of what went wrong and how to fix it -- **Zero performance impact** - Disabled by default, opt-in only -- **Granular control** - Enable all checks or selectively disable specific ones -- **LLM-friendly** - Designed to help AI-generated code self-correct -- **Always visible** - Both errors and warnings throw exceptions AND log to console.error (so AIs see them even if - throws get swallowed) - -## Quick Start - -```typescript -import NDK from "@nostr-dev-kit/ndk"; - -// Enable all guardrails (recommended for development) -const ndk = new NDK({ aiGuardrails: true }); - -// Or enable with exceptions -const ndk = new NDK({ - aiGuardrails: { - skip: new Set(['filter-large-limit', 'fetch-events-usage']) - } -}); - -// Or programmatically control -ndk.aiGuardrails.skip('event-param-replaceable-no-dtag'); -ndk.aiGuardrails.enable('filter-bech32-in-array'); -``` - -## Available Guardrails - -### Filter-Related Checks - -#### `filter-bech32-in-array` - -**Level:** Error - -Catches bech32-encoded values in filter arrays. Filters expect hex values, not bech32. - -```typescript -// โŒ WRONG -ndk.subscribe({ - authors: ['npub1...'] // bech32 npub -}); - -// โœ… CORRECT -import { nip19 } from 'nostr-tools'; -const { data } = nip19.decode('npub1...'); -ndk.subscribe({ - authors: [data] // hex pubkey -}); - -// Or use filterFromId for complete bech32 entities -import { filterFromId } from '@nostr-dev-kit/ndk'; -const filter = filterFromId('nevent1...'); -ndk.subscribe(filter); -``` - -#### `filter-only-limit` - -**Level:** Error - -Catches filters with only a `limit` parameter and no filtering criteria. - -```typescript -// โŒ WRONG - will fetch random events -ndk.subscribe({ limit: 10 }); - -// โœ… CORRECT -ndk.subscribe({ - kinds: [1], - limit: 10 -}); -``` - -#### `filter-large-limit` - -**Level:** Warning - -Warns about very large limit values that can cause performance issues. - -```typescript -// โš ๏ธ WARNING -ndk.subscribe({ - kinds: [1], - limit: 10000 // Too large! -}); - -// โœ… BETTER -ndk.subscribe({ - kinds: [1], - limit: 100 // More reasonable -}); -``` - -#### `filter-empty` - -**Level:** Error - -Catches completely empty filters. - -```typescript -// โŒ WRONG -ndk.subscribe({}); - -// โœ… CORRECT -ndk.subscribe({ kinds: [1] }); -``` - -#### `filter-since-after-until` - -**Level:** Error - -Catches filters where `since` is after `until`, which would match zero events. - -```typescript -// โŒ WRONG -ndk.subscribe({ - since: 1000000, - until: 500000 -}); - -// โœ… CORRECT -ndk.subscribe({ - since: 500000, - until: 1000000 -}); -``` - -#### `filter-invalid-a-tag` - -**Level:** Error - -Catches malformed `#a` tag values. Must be `kind:pubkey:d-tag` format. - -```typescript -// โŒ WRONG -ndk.subscribe({ - '#a': ['nevent1...'] // bech32 instead of address -}); - -// โœ… CORRECT -ndk.subscribe({ - '#a': ['30023:fa984bd7...:my-article'] -}); -``` - -### fetchEvents Anti-Pattern - -#### `fetch-events-usage` - -**Level:** Warning - -Warns about using `fetchEvents()` which is a blocking operation. - -```typescript -// โš ๏ธ SUBOPTIMAL - blocks until EOSE -const events = await ndk.fetchEvents({ kinds: [1] }); - -// โœ… BETTER - reactive, non-blocking -ndk.subscribe({ kinds: [1] }, { - onEvent: (event) => { - console.log('Got event:', event); - } -}); - -// Or for single events -const event = await ndk.fetchEvent({ kinds: [1] }); -``` - -**When to disable:** If you truly need to block until all events arrive (rare). - -```typescript -ndk.aiGuardrails.skip('fetch-events-usage'); -const events = await ndk.fetchEvents(filter); -``` - -### Event Construction Checks - -#### `event-missing-kind` - -**Level:** Error - -Catches attempts to sign events without a `kind`. - -```typescript -// โŒ WRONG -const event = new NDKEvent(ndk); -event.content = "Hello"; -await event.sign(); // Error! - -// โœ… CORRECT -const event = new NDKEvent(ndk); -event.kind = 1; // Set kind first -event.content = "Hello"; -await event.sign(); -``` - -#### `event-content-is-object` - -**Level:** Error - -Catches attempts to set event content to an object instead of a string. - -```typescript -// โŒ WRONG -const event = new NDKEvent(ndk); -event.kind = 30023; -event.content = { title: "My Article" }; // Object! -await event.sign(); // Error! - -// โœ… CORRECT -const event = new NDKEvent(ndk); -event.kind = 30023; -event.content = JSON.stringify({ title: "My Article" }); -await event.sign(); -``` - -#### `event-param-replaceable-no-dtag` - -**Level:** Warning - -Warns about parameterized replaceable events (kinds 30000-39999) without a d-tag. - -```typescript -// โš ๏ธ WARNING - will use empty string as d-tag -const event = new NDKEvent(ndk); -event.kind = 30023; -event.content = "My article"; -await event.sign(); // Warning! - -// โœ… BETTER -const event = new NDKEvent(ndk); -event.kind = 30023; -event.dTag = "my-unique-article-id"; -event.content = "My article"; -await event.sign(); -``` - -#### `event-created-at-milliseconds` - -**Level:** Error - -Catches using milliseconds instead of seconds for `created_at`. - -```typescript -// โŒ WRONG -const event = new NDKEvent(ndk); -event.kind = 1; -event.content = "Hello"; -event.created_at = Date.now(); // Milliseconds! -await event.sign(); // Error! - -// โœ… CORRECT -const event = new NDKEvent(ndk); -event.kind = 1; -event.content = "Hello"; -event.created_at = Math.floor(Date.now() / 1000); // Seconds -await event.sign(); -``` - -### Tag Validation Checks - -#### `tag-invalid-p-tag` - -**Level:** Error - -Catches invalid p-tags (must be 64-character hex pubkeys). - -```typescript -// โŒ WRONG -const event = new NDKEvent(ndk); -event.kind = 1; -event.tags.push(['p', 'npub1...']); // bech32! -await event.sign(); // Error! - -// โœ… CORRECT -const event = new NDKEvent(ndk); -event.kind = 1; -event.tags.push(['p', ndkUser.pubkey]); // hex pubkey -await event.sign(); -``` - -#### `tag-invalid-e-tag` - -**Level:** Error - -Catches invalid e-tags (must be 64-character hex event IDs). - -```typescript -// โŒ WRONG -const event = new NDKEvent(ndk); -event.kind = 1; -event.tags.push(['e', 'note1...']); // bech32! -await event.sign(); // Error! - -// โœ… CORRECT -const event = new NDKEvent(ndk); -event.kind = 1; -event.tags.push(['e', referencedEvent.id]); // hex event ID -await event.sign(); -``` - -#### `event-manual-reply-markers` - -**Level:** Warning - -Warns about manually adding e-tags with reply/root markers instead of using `.reply()`. - -```typescript -// โš ๏ธ SUBOPTIMAL -const reply = new NDKEvent(ndk); -reply.kind = 1; -reply.content = "Great post!"; -reply.tags.push(['e', parentEvent.id, '', 'reply']); // Manual marker -await reply.sign(); // Warning! - -// โœ… BETTER - Use reply() method -const reply = new NDKEvent(ndk); -reply.kind = 1; -reply.content = "Great post!"; -await reply.reply(parentEvent); // Handles threading automatically -``` - -## Programmatic Control - -### Temporarily Disable a Check - -```typescript -// Disable for one-time use -ndk.aiGuardrails.skip('fetch-events-usage'); -const events = await ndk.fetchEvents(filter); - -// Re-enable it -ndk.aiGuardrails.enable('fetch-events-usage'); -``` - -### Check What's Skipped - -```typescript -const skipped = ndk.aiGuardrails.getSkipped(); -console.log('Skipped checks:', skipped); -``` - -### Runtime Enable/Disable - -```typescript -// Start with guardrails disabled -const ndk = new NDK(); - -// Enable later -ndk.aiGuardrails.setMode(true); - -// Or enable with specific skips -ndk.aiGuardrails.setMode({ - skip: new Set(['filter-large-limit']) -}); - -// Disable entirely -ndk.aiGuardrails.setMode(false); -``` - -## Error Messages - -When a guardrail is triggered, it will: - -1. **Log to console.error** (visible even if the exception is caught) -2. **Throw an Error** (stops execution) - -Both "ERROR" and "WARNING" level checks throw exceptions - warnings are not just console warnings. - -### Fatal vs Non-Fatal Errors - -Some errors are **fatal** - they represent fundamental mistakes that cannot be bypassed: - -- Missing event kind -- Content as object instead of string -- Timestamps in milliseconds instead of seconds -- Invalid p-tag/e-tag formats -- Bech32 in filter arrays -- Empty filters or invalid time ranges - -**Fatal errors do NOT show the "To disable this check" message.** - -Example fatal error (no disable option): - -``` -๐Ÿค– AI_GUARDRAILS ERROR: Cannot sign event without 'kind'. Set event.kind before signing. - -๐Ÿ’ก Example: event.kind = 1; // for text note -``` - -Example non-fatal error (can be disabled): - -``` -๐Ÿค– AI_GUARDRAILS ERROR: Filter[0] contains only 'limit' without any filtering criteria. - -๐Ÿ’ก Add filtering criteria like 'kinds', 'authors', or '#e' tags. - -๐Ÿ”‡ To disable this check: - ndk.aiGuardrails.skip('filter-only-limit') - or set: ndk.aiGuardrails = { skip: new Set(['filter-only-limit']) } -``` - -## Best Practices - -### For Development - -Enable all guardrails during development: - -```typescript -const ndk = new NDK({ - aiGuardrails: true, - // ... other options -}); -``` - -### For Production - -Keep guardrails disabled in production for zero performance impact: - -```typescript -const ndk = new NDK({ - aiGuardrails: process.env.NODE_ENV === 'development', - // ... other options -}); -``` - -### For AI-Generated Code - -If you're using AI to generate code, enable guardrails and let the AI learn from the errors: - -```typescript -// In your prompt/system message: -"When NDK throws an AI_GUARDRAILS error, read the error message carefully. -It explains what's wrong and how to fix it. Update your code accordingly." -``` - -The AI can also programmatically skip checks it knows are safe: - -```typescript -// LLM can disable specific checks when it knows what it's doing -ndk.aiGuardrails.skip('filter-large-limit'); // I know I need 5000 events -ndk.subscribe({ kinds: [1], limit: 5000 }); -``` - -## Complete Check ID Reference - -Import these for type-safe check IDs: - -```typescript -import { GuardrailCheckId } from '@nostr-dev-kit/ndk'; - -// All available check IDs: -GuardrailCheckId.FILTER_BECH32_IN_ARRAY -GuardrailCheckId.FILTER_ONLY_LIMIT -GuardrailCheckId.FILTER_LARGE_LIMIT -GuardrailCheckId.FILTER_EMPTY -GuardrailCheckId.FILTER_SINCE_AFTER_UNTIL -GuardrailCheckId.FILTER_INVALID_A_TAG -GuardrailCheckId.FETCH_EVENTS_USAGE -GuardrailCheckId.EVENT_MISSING_KIND -GuardrailCheckId.EVENT_PARAM_REPLACEABLE_NO_DTAG -GuardrailCheckId.EVENT_CREATED_AT_MILLISECONDS -GuardrailCheckId.EVENT_NO_NDK_INSTANCE -GuardrailCheckId.EVENT_CONTENT_IS_OBJECT -GuardrailCheckId.EVENT_MODIFIED_AFTER_SIGNING -GuardrailCheckId.EVENT_MANUAL_REPLY_MARKERS -GuardrailCheckId.TAG_E_FOR_PARAM_REPLACEABLE -GuardrailCheckId.TAG_BECH32_VALUE -GuardrailCheckId.TAG_DUPLICATE -GuardrailCheckId.TAG_INVALID_P_TAG -GuardrailCheckId.TAG_INVALID_E_TAG -GuardrailCheckId.SUBSCRIBE_NOT_STARTED -GuardrailCheckId.SUBSCRIBE_CLOSE_ON_EOSE_NO_HANDLER -GuardrailCheckId.SUBSCRIBE_PASSED_EVENT_NOT_FILTER -GuardrailCheckId.SUBSCRIBE_AWAITED -GuardrailCheckId.RELAY_INVALID_URL -GuardrailCheckId.RELAY_HTTP_INSTEAD_OF_WS -GuardrailCheckId.RELAY_NO_ERROR_HANDLERS -GuardrailCheckId.VALIDATION_PUBKEY_IS_NPUB -GuardrailCheckId.VALIDATION_PUBKEY_WRONG_LENGTH -GuardrailCheckId.VALIDATION_EVENT_ID_IS_BECH32 -GuardrailCheckId.VALIDATION_EVENT_ID_WRONG_LENGTH -``` - -## Philosophy - -AI Guardrails is designed with these principles: - -1. **Educational, not punitive** - Error messages teach, don't just reject -2. **Opt-in, not opt-out** - Zero impact when disabled (default) -3. **Flexible** - Granular control over what's checked -4. **LLM-friendly** - Help AI code self-correct and learn patterns - -## Contributing - -To add a new guardrail: - -1. Add the check ID to `GuardrailCheckId` in `ai-guardrails.ts` -2. Implement the check where appropriate (filter validation, event signing, etc.) -3. Use `ndk.aiGuardrails.error()` or `ndk.aiGuardrails.warn()` -4. Add clear, actionable error messages with hints -5. Document it in this file -6. Add tests - -Example: - -```typescript -if (ndk?.aiGuardrails.isEnabled()) { - ndk.aiGuardrails.error( - GuardrailCheckId.YOUR_NEW_CHECK, - "Clear explanation of what's wrong", - "Helpful hint on how to fix it" - ); -} -``` diff --git a/core/docs/event-class-registration.md b/core/docs/event-class-registration.md deleted file mode 100644 index fad8690a4..000000000 --- a/core/docs/event-class-registration.md +++ /dev/null @@ -1,183 +0,0 @@ -# Custom Event Class Registration - -NDK provides a registration system that allows you to register custom event classes to work with the `wrapEvent()` -function. This enables you to create your own event types that integrate seamlessly with NDK's event wrapping system. - -## Overview - -The `wrapEvent()` function automatically wraps raw `NDKEvent` objects into more specific event types based on their -`kind` property. By default, NDK includes many built-in event classes (like `NDKArticle`, `NDKImage`, etc.), but you can -also register your own custom event classes. - -## Registering Custom Event Classes - -### Requirements - -Your custom event class must meet these requirements: - -1. **Extend NDKEvent**: Your class should extend the `NDKEvent` base class -2. **Static `kinds` property**: An array of event kind numbers this class handles -3. **Static `from` method**: A factory method that creates an instance from an `NDKEvent` - -### Example - -```typescript -import { NDKEvent, registerEventClass } from "@nostr-dev-kit/ndk"; - -class MyCustomEvent extends NDKEvent { - static kinds = [12345]; // Custom event kind number - - static from(event: NDKEvent) { - return new MyCustomEvent(event.ndk, event); - } - - constructor(ndk: NDK | undefined, rawEvent?: NostrEvent | NDKEvent) { - super(ndk, rawEvent); - this.kind ??= 12345; - } - - // Add your custom methods and properties - get customProperty(): string | undefined { - return this.tagValue("custom"); - } - - set customProperty(value: string | undefined) { - this.removeTag("custom"); - if (value) this.tags.push(["custom", value]); - } -} - -// Register the class -registerEventClass(MyCustomEvent); -``` - -### Multiple Kinds - -Your custom event class can handle multiple event kinds: - -```typescript -class MyMultiKindEvent extends NDKEvent { - static kinds = [12345, 12346, 12347]; - - static from(event: NDKEvent) { - return new MyMultiKindEvent(event.ndk, event); - } - - // Implementation... -} - -registerEventClass(MyMultiKindEvent); -``` - -## API Reference - -### `registerEventClass(eventClass)` - -Registers a custom event class to be used with `wrapEvent()`. - -**Parameters:** - -- `eventClass`: An object that implements the `NDKEventClass` interface - -**Example:** - -```typescript -registerEventClass(MyCustomEvent); -``` - -### `unregisterEventClass(eventClass)` - -Removes a previously registered event class. - -**Parameters:** - -- `eventClass`: The event class to unregister - -**Example:** - -```typescript -unregisterEventClass(MyCustomEvent); -``` - -### `getRegisteredEventClasses()` - -Returns a Set of all currently registered custom event classes. - -**Returns:** - -- `Set`: Set of registered event classes - -**Example:** - -```typescript -const registeredClasses = getRegisteredEventClasses(); -console.log(`${registeredClasses.size} custom classes registered`); -``` - -## How It Works - -When you call `wrapEvent()` on an `NDKEvent`, the function: - -1. Creates a mapping of event kinds to their corresponding classes -2. Includes both built-in NDK classes and your registered custom classes -3. Looks up the event's `kind` in this mapping -4. If found, calls the class's `from()` method to create a wrapped instance -5. If not found, returns the original `NDKEvent` - -```typescript -// This will now return a MyCustomEvent instance for kind 12345 -const wrappedEvent = wrapEvent(rawEvent); // rawEvent.kind === 12345 -``` - -## Best Practices - -1. **Choose unique kind numbers**: Make sure your custom event kinds don't conflict with existing Nostr event kinds -2. **Follow NIP specifications**: If you're implementing a NIP, follow its specifications for event structure -3. **Extend existing functionality**: Build upon NDK's existing event capabilities rather than replacing them -4. **Handle errors gracefully**: Your `from()` method should handle invalid or malformed events -5. **Test thoroughly**: Test your custom event classes with various input scenarios - -## Example: Custom Chat Message Event - -```typescript -import { NDKEvent, registerEventClass, NDKKind } from "@nostr-dev-kit/ndk"; - -class NDKChatMessage extends NDKEvent { - static kinds = [42]; // Using kind 42 for chat messages - - static from(event: NDKEvent) { - return new NDKChatMessage(event.ndk, event); - } - - constructor(ndk: NDK | undefined, rawEvent?: NostrEvent | NDKEvent) { - super(ndk, rawEvent); - this.kind ??= 42; - } - - get roomId(): string | undefined { - return this.tagValue("room"); - } - - set roomId(roomId: string | undefined) { - this.removeTag("room"); - if (roomId) this.tags.push(["room", roomId]); - } - - get replyTo(): string | undefined { - return this.tagValue("reply"); - } - - set replyTo(eventId: string | undefined) { - this.removeTag("reply"); - if (eventId) this.tags.push(["reply", eventId]); - } -} - -// Register the custom chat message class -registerEventClass(NDKChatMessage); - -// Now wrapEvent will automatically create NDKChatMessage instances for kind 42 events -``` - -This registration system provides a flexible way to extend NDK with your own event types while maintaining compatibility -with the existing event wrapping infrastructure. \ No newline at end of file From 746b095409e3427711cf8ad0bf9a75f5d4684e11 Mon Sep 17 00:00:00 2001 From: digitalbase Date: Sun, 23 Nov 2025 13:42:14 +0100 Subject: [PATCH 108/139] Move every doc related file that's related to vitepress to /docs folder --- core/docs/fundamentals/signers_old.md | 150 -------------------------- core/docs/index.md | 19 ---- {core/docs => docs}/about-nostr.md | 0 docs/{index.md => home.md} | 0 {core/docs => docs}/introduction.md | 0 5 files changed, 169 deletions(-) delete mode 100644 core/docs/fundamentals/signers_old.md delete mode 100644 core/docs/index.md rename {core/docs => docs}/about-nostr.md (100%) rename docs/{index.md => home.md} (100%) rename {core/docs => docs}/introduction.md (100%) diff --git a/core/docs/fundamentals/signers_old.md b/core/docs/fundamentals/signers_old.md deleted file mode 100644 index c16f5687e..000000000 --- a/core/docs/fundamentals/signers_old.md +++ /dev/null @@ -1,150 +0,0 @@ -### Remote Signer - -A Nostr remote signer is an application or device that securely stores your private key and signs Nostr events on -your behalf, preventing you from having to expose the key clients. - -It works by establishing a secure connection, as described -in [NIP-46](https://github.com/nostr-protocol/nips/blob/master/46.md), with a Nostr -client and then receiving signing requests via push notifications to approve or deny. - -#### bunker:// - -* Create a `NDKNip46Signer`, optionally providing the local signer if you are restoring a connection that was already - generated in your app: - -```ts -const signerConnectionString = 'bunker://....'; // ask the user for the bunker connection string -const localNsec = `nsec1....`; // Restore this from whatever storage your app is using, if you have it -const signer = NDKNip46Signer.bunker(ndk, signerConnectionString, localNsec); -const user = await signer.blockUntilReady(); - -console.log("Welcome", user.npub); - -// if you didn't have a localNsec you should store it for future sessions of your app -save(signer.localSigner.nsec) -``` - -#### nostrconnect:// - -The `nostrconnect://` flow is the reverse of the `bunker://` flow; the app generates a connection string and is sent to -the signer out of band (such as scanning a QR code). - -```ts -const relay = 'wss://relay.primal.net'; // choose a relay to be used to communicate with the signer -const localNsec = `nsec1....`; // Restore this from whatever storage your app is using, if you have it - -// instantiate the signer -const signer = NDKNip46Signer.nostrconnect(ndk, relay, localNsec, { - name: "", - icon: "", - perms: ['sign_event:1,sign_event:30023'] // permissions to request (e.g. sign event kind:1 and kind:30023) -}); - -// Open the signer or show the signer.nostrConnectUri URI as a QR code -open(signer.nostrConnectUri); - -// Wait for the user to connect -const user = await signer.blockUntilReady(); - -console.log("Welcome", user.npub); - -// if you didn't have a localNsec you should store it for future sessions of your app -save(signer.localSigner.nsec) -``` - -#### Password-Protected Keys (NIP-49) - -NDK supports NIP-49 encrypted private keys (ncryptsec format) for secure storage: - -```ts -import {NDKPrivateKeySigner} from "@nostr-dev-kit/ndk"; - -// Encrypt a private key with a password -const signer = NDKPrivateKeySigner.generate(); -const password = "user-chosen-password"; -const ncryptsec = signer.encryptToNcryptsec(password); - -// Store ncryptsec securely (e.g., in localStorage) -localStorage.setItem("encrypted_key", ncryptsec); - -// Later, restore the signer from encrypted key -const storedKey = localStorage.getItem("encrypted_key"); -const restoredSigner = NDKPrivateKeySigner.fromNcryptsec(storedKey, password); -console.log("Restored pubkey:", restoredSigner.pubkey); -``` - -**Security Parameters:** - -The `encryptToNcryptsec` method accepts optional parameters for security tuning: - -```ts -// Higher security (slower, more resistant to brute force) -const ncryptsec = signer.encryptToNcryptsec(password, 20); // log_n = 20 (~2 seconds, 1GB memory) - -// Default security (faster) -const ncryptsec = signer.encryptToNcryptsec(password, 16); // log_n = 16 (~100ms, 64MB memory) - -// With key security byte (0x00, 0x01, or 0x02) -const ncryptsec = signer.encryptToNcryptsec(password, 16, 0x02); -``` - -**Direct NIP-49 utilities:** - -NDK also re-exports NIP-49 utilities for advanced use cases: - -```ts -import {nip49} from "@nostr-dev-kit/ndk"; -import {hexToBytes, bytesToHex} from "@noble/hashes/utils"; - -// Encrypt raw bytes -const privateKeyBytes = hexToBytes("14c226dbdd865d5e..."); -const ncryptsec = nip49.encrypt(privateKeyBytes, password); - -// Decrypt to raw bytes -const decryptedBytes = nip49.decrypt(ncryptsec, password); -``` - -## Sign Events - -Once the signer is initialized, you can use it to sign and [publish](/core/docs/fundamentals/publishing.html) events: - -```ts -const ndkEvent = new NDKEvent(ndk); -ndkEvent.kind = 1; -ndkEvent.content = "Hello, world!"; -await ndkEvent.sign(); // [!code focus] -``` - -## Combining signers - -You can specify the use of a different signer to sign with different pubkeys. - -<<< @/core/docs/snippets/sign_event_with_other_signers.ts - -## Read Public key - -**Read the user's public key** - -```ts -nip07signer.user().then(async (user) => { - if (!!user.npub) { - console.log("Permission granted to read their public key:", user.npub); - } -}); -``` - -## Generate Keys - -Perhaps the only time we really want you to use the `NDKPrivateKeySigner` is to help you generate new keys as the signer -provides helper methods to do just that: - -```ts -import {NDKPrivateKeySigner} from "@nostr-dev-kit/ndk"; - -// Generate a new private key -const signer = NDKPrivateKeySigner.generate(); -console.log("nsec:", signer.nsec); -console.log("npub:", signer.npub); -``` - - diff --git a/core/docs/index.md b/core/docs/index.md deleted file mode 100644 index 1cd63ae78..000000000 --- a/core/docs/index.md +++ /dev/null @@ -1,19 +0,0 @@ ---- -# https://vitepress.dev/reference/default-theme-home-page -layout: home - -hero: - name: "NDK Documentation" - tagline: "Nostr Development Kit Docs" - actions: - - theme: brand - text: Getting Started - link: /getting-started/introduction.html - - theme: secondary - text: References - link: https://github.com/nostr-dev-kit/ndk/blob/master/REFERENCES.md - ---- - -NDK is a nostr development kit that makes the experience of building Nostr-related applications, whether they are -relays, clients, or anything in between, better, more reliable and overall nicer to work with than existing solutions. \ No newline at end of file diff --git a/core/docs/about-nostr.md b/docs/about-nostr.md similarity index 100% rename from core/docs/about-nostr.md rename to docs/about-nostr.md diff --git a/docs/index.md b/docs/home.md similarity index 100% rename from docs/index.md rename to docs/home.md diff --git a/core/docs/introduction.md b/docs/introduction.md similarity index 100% rename from core/docs/introduction.md rename to docs/introduction.md From ef63ebbbc63d09f18546fb79e13cd1eaa6fe096c Mon Sep 17 00:00:00 2001 From: digitalbase Date: Sun, 23 Nov 2025 13:46:54 +0100 Subject: [PATCH 109/139] Remove prettierignore (using biome) --- core/.prettierignore | 7 ------- 1 file changed, 7 deletions(-) delete mode 100644 core/.prettierignore diff --git a/core/.prettierignore b/core/.prettierignore deleted file mode 100644 index 1d6407e51..000000000 --- a/core/.prettierignore +++ /dev/null @@ -1,7 +0,0 @@ -dist -docs -lib -coverage -**/.changeset -**/.svelte-kit -docs-styles.css \ No newline at end of file From f763cca9496cfcb3f8d2fa1427ecf651198c480b Mon Sep 17 00:00:00 2001 From: digitalbase Date: Sun, 23 Nov 2025 13:47:03 +0100 Subject: [PATCH 110/139] Link specific migration guides from changelogs --- docs/changelogs.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/changelogs.md b/docs/changelogs.md index d229fc85d..014f248e7 100644 --- a/docs/changelogs.md +++ b/docs/changelogs.md @@ -5,6 +5,8 @@ We maintain changelogs for each package in the multi-repo. Consult the individua ## Core * [@nostr-dev-kit/core](/core/README.md) -> [CHANGELOG.md](/core/CHANGELOG.md) + * [Migration guide from `2.12` to `2.13`](/core/docs/migration/2.12-to-2.13.md) + * [Migration guide from `2.13` to `2.16`](/core/docs/migration/2.13-to-2.16.md) ## Extras From 60b5d3cea739977a98fcce401534e0e7d7823439 Mon Sep 17 00:00:00 2001 From: digitalbase Date: Sun, 23 Nov 2025 13:47:16 +0100 Subject: [PATCH 111/139] Fix paths in vitepress docs after moving stuff outside of core --- docs/.vitepress/config.mts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/.vitepress/config.mts b/docs/.vitepress/config.mts index aa2268a13..6ae1773ea 100644 --- a/docs/.vitepress/config.mts +++ b/docs/.vitepress/config.mts @@ -30,10 +30,10 @@ export default defineConfig({ sidebar: [ { items: [ - {text: "Introduction โœ…", link: "/core/docs/introduction"}, - {text: "About Nostr โœ…", link: "/core/docs/about-nostr"}, - {text: "Contributing โŒ", link: "/docs/contributing.md"}, - {text: "Changelog โœ…", link: "/docs/changelogs.md"}, + {text: "Introduction โœ…", link: "/docs/introduction"}, + {text: "About Nostr โœ…", link: "/docs/about-nostr"}, + {text: "Contributing โŒ", link: "/docs/contributing"}, + {text: "Changelog โœ…", link: "/docs/changelogs"}, ], }, { From 71e077ee325a63bf35111b545953e8c21788df2d Mon Sep 17 00:00:00 2001 From: digitalbase Date: Sun, 23 Nov 2025 13:47:49 +0100 Subject: [PATCH 112/139] Delete dupe zap docs --- core/docs/tutorial/zaps/index.md | 17 ----------------- 1 file changed, 17 deletions(-) delete mode 100644 core/docs/tutorial/zaps/index.md diff --git a/core/docs/tutorial/zaps/index.md b/core/docs/tutorial/zaps/index.md deleted file mode 100644 index b8cb74117..000000000 --- a/core/docs/tutorial/zaps/index.md +++ /dev/null @@ -1,17 +0,0 @@ -# Zaps - -NDK comes with an interface to make zapping as simple as possible. - -```ts -const user = await ndk.fetchUser("pablo@f7z.io"); -const lnPay = ({ pr: string }) => { - console.log("please pay to complete the zap", pr); -}; -const zapper = new NDKZapper(user, 1000, { lnPay }); -zapper.zap(); -``` - -## NDK-Wallet - -Refer to the Wallet section of the tutorial to learn more about zapping. NDK-wallet provides many conveniences to -integrate with zaps. From ee4af1541430de13be38a9a325b70fe6a3e7084e Mon Sep 17 00:00:00 2001 From: digitalbase Date: Sun, 23 Nov 2025 13:53:33 +0100 Subject: [PATCH 113/139] Merge tutorial/auth into connecting.md (+fix import paths) --- core/docs/fundamentals/connecting.md | 25 ++++++++++++++++++++++ core/docs/snippets/connect_auth.ts | 4 ++++ core/docs/tutorial/auth.md | 31 ---------------------------- 3 files changed, 29 insertions(+), 31 deletions(-) create mode 100644 core/docs/snippets/connect_auth.ts delete mode 100644 core/docs/tutorial/auth.md diff --git a/core/docs/fundamentals/connecting.md b/core/docs/fundamentals/connecting.md index 3bf40afd3..aeb7d1b60 100644 --- a/core/docs/fundamentals/connecting.md +++ b/core/docs/fundamentals/connecting.md @@ -92,6 +92,31 @@ using the `NDKPool` class. Note that if you have outbox enabled you will have an extra pool in the `ndk.pools` array reserved for user provided relays. +## Authentication + +([NIP-42](https://github.com/nostr-protocol/nips/blob/master/42.md)) defines that relays can request authentication from +clients. NDK uses an `NDKAuthPolicy` callback to provide a way to handle authentication requests. + +* Relays can have specific `NDKAuthPolicy` functions. +* NDK can be configured with a default `relayAuthDefaultPolicy` function. +* NDK provides some generic policies: + * `NDKAuthPolicies.signIn`: Authenticate to the relay (using the `ndk.signer` signer). + * `NDKAuthPolicies.disconnect`: Immediately disconnect from the relay if asked to authenticate. + +<<< @/core/docs/snippets/connect_auth.ts + +Clients should typically allow their users to choose where to authenticate. This can be accomplished by returning the +decision the user made from the `NDKAuthPolicy` function. + +```ts +import NDK, {NDKRelayAuthPolicies} from "@nostr-dev-kit/ndk"; + +const ndk = new NDK(); +ndk.relayAuthDefaultPolicy = (relay: NDKRelay) => { + return confirm(`Authenticate to ${relay.url}?`); +}; +``` + ## Connection Events There are a number of events you can hook into to get information about relay connection diff --git a/core/docs/snippets/connect_auth.ts b/core/docs/snippets/connect_auth.ts new file mode 100644 index 000000000..8b4547c46 --- /dev/null +++ b/core/docs/snippets/connect_auth.ts @@ -0,0 +1,4 @@ +import NDK, { NDKRelayAuthPolicies } from "@nostr-dev-kit/ndk"; + +const ndk = new NDK(); +ndk.addExplicitRelay("wss://relay.f7z.io", NDKRelayAuthPolicies.signIn({ ndk })); diff --git a/core/docs/tutorial/auth.md b/core/docs/tutorial/auth.md deleted file mode 100644 index 38a32fea0..000000000 --- a/core/docs/tutorial/auth.md +++ /dev/null @@ -1,31 +0,0 @@ -# Relay Authentication - -NIP-42 defines that relays can request authentication from clients. - -NDK makes working with NIP-42 very simple. NDK uses an `NDKAuthPolicy` callback to provide a way to handle -authentication requests. - -* Relays can have specific `NDKAuthPolicy` functions. -* NDK can be configured with a default `relayAuthDefaultPolicy` function. -* NDK provides some generic policies: - * `NDKAuthPolicies.signIn`: Authenticate to the relay (using the `ndk.signer` signer). - * `NDKAuthPolicies.disconnect`: Immediately disconnect from the relay if asked to authenticate. - -```ts -import { NDK, NDKRelayAuthPolicies } from "@nostr-dev-kit/ndk"; - -const ndk = new NDK(); -ndk.addExplicitRelay("wss://relay.f7z.io", NDKRelayAuthPolicies.signIn({ndk})); -``` - -Clients should typically allow their users to choose where to authenticate. This can be accomplished by returning the -decision the user made from the `NDKAuthPolicy` function. - -```ts -import { NDK, NDKRelayAuthPolicies } from "@nostr-dev-kit/ndk"; - -const ndk = new NDK(); -ndk.relayAuthDefaultPolicy = (relay: NDKRelay) => { - return confirm(`Authenticate to ${relay.url}?`); -}; -``` From 30b79945f9c36315f0897feeef6ae8ebf071bd55 Mon Sep 17 00:00:00 2001 From: digitalbase Date: Sun, 23 Nov 2025 13:54:21 +0100 Subject: [PATCH 114/139] Move subscription management to advanced docs --- core/docs/{tutorial => advanced}/subscription-management.md | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename core/docs/{tutorial => advanced}/subscription-management.md (100%) diff --git a/core/docs/tutorial/subscription-management.md b/core/docs/advanced/subscription-management.md similarity index 100% rename from core/docs/tutorial/subscription-management.md rename to core/docs/advanced/subscription-management.md From cecebe8d18ec420ffa112efd9548ef93014e5a11 Mon Sep 17 00:00:00 2001 From: digitalbase Date: Sun, 23 Nov 2025 14:01:26 +0100 Subject: [PATCH 115/139] Move more docs to advanced so /tutorial can be removed --- core/docs/{tutorial => advanced}/filter-validation.md | 0 core/docs/{tutorial => advanced}/local-first.md | 0 core/docs/{tutorial => advanced}/mute-filtering.md | 0 core/docs/{tutorial => advanced}/speed.md | 0 4 files changed, 0 insertions(+), 0 deletions(-) rename core/docs/{tutorial => advanced}/filter-validation.md (100%) rename core/docs/{tutorial => advanced}/local-first.md (100%) rename core/docs/{tutorial => advanced}/mute-filtering.md (100%) rename core/docs/{tutorial => advanced}/speed.md (100%) diff --git a/core/docs/tutorial/filter-validation.md b/core/docs/advanced/filter-validation.md similarity index 100% rename from core/docs/tutorial/filter-validation.md rename to core/docs/advanced/filter-validation.md diff --git a/core/docs/tutorial/local-first.md b/core/docs/advanced/local-first.md similarity index 100% rename from core/docs/tutorial/local-first.md rename to core/docs/advanced/local-first.md diff --git a/core/docs/tutorial/mute-filtering.md b/core/docs/advanced/mute-filtering.md similarity index 100% rename from core/docs/tutorial/mute-filtering.md rename to core/docs/advanced/mute-filtering.md diff --git a/core/docs/tutorial/speed.md b/core/docs/advanced/speed.md similarity index 100% rename from core/docs/tutorial/speed.md rename to core/docs/advanced/speed.md From 0d9e0f8cd700ed3a77b7c04fa57abfae3f8d4293 Mon Sep 17 00:00:00 2001 From: digitalbase Date: Sun, 23 Nov 2025 14:01:38 +0100 Subject: [PATCH 116/139] Move signer-persistence + refer to it from signer doc --- core/docs/{tutorial => advanced}/signer-persistence.md | 3 --- core/docs/fundamentals/signers.md | 9 ++++++++- 2 files changed, 8 insertions(+), 4 deletions(-) rename core/docs/{tutorial => advanced}/signer-persistence.md (96%) diff --git a/core/docs/tutorial/signer-persistence.md b/core/docs/advanced/signer-persistence.md similarity index 96% rename from core/docs/tutorial/signer-persistence.md rename to core/docs/advanced/signer-persistence.md index 078adefa3..b8cd4f268 100644 --- a/core/docs/tutorial/signer-persistence.md +++ b/core/docs/advanced/signer-persistence.md @@ -12,9 +12,6 @@ function. Every NDK signer (`NDKPrivateKeySigner`, `NDKNip07Signer`, `NDKNip46Signer`, etc.) implements a `toPayload()` method. This method returns a JSON string containing the minimal information needed to reconstruct a functionally equivalent signer instance later. -Every NDK signer (`NDKPrivateKeySigner`, `NDKNip07Signer`, `NDKNip46Signer`, etc.) implements a `toPayload()` method. -This method returns a string containing the information needed to reconstruct a functionally equivalent signer instance -later. **Example Usage:** diff --git a/core/docs/fundamentals/signers.md b/core/docs/fundamentals/signers.md index a61c166d4..dd42d312c 100644 --- a/core/docs/fundamentals/signers.md +++ b/core/docs/fundamentals/signers.md @@ -110,7 +110,7 @@ You can use these different formats for different purposes: - `nsec`: Encoded private key format (bech32) - used for secure sharing when needed - `npub`: Encoded public key format (bech32) - used for user identification -### Storing Keys +### Encrypting Keys For storing keys securely with password protection, use [NIP-49](https://github.com/nostr-protocol/nips/blob/master/49.md) (ncryptsec format): @@ -119,6 +119,13 @@ use [NIP-49](https://github.com/nostr-protocol/nips/blob/master/49.md) (ncryptse See [Encrypted Keys (NIP-49)](/core/docs/advanced/encrypted-keys-nip49.md) for more examples and best practices. +## Signer Persistence + +In a lot of applications you would want to persist the signer preferences so the state +can be maintained without requiring re-authentication. + +Please consult [the dedicated section about signer persistence](/core/docs/advanced/signer-persistence.md). + ## Code Snippets More snippets and examples can be found in the [snippets directory](/docs/snippets.md#signers) \ No newline at end of file From ab467c4ae9766fcb062dc640cf8dcc48f28adf0d Mon Sep 17 00:00:00 2001 From: digitalbase Date: Tue, 25 Nov 2025 12:07:44 +0100 Subject: [PATCH 117/139] Remove some fundamentals --- docs/.vitepress/config.mts | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/docs/.vitepress/config.mts b/docs/.vitepress/config.mts index 6ae1773ea..ce314ca18 100644 --- a/docs/.vitepress/config.mts +++ b/docs/.vitepress/config.mts @@ -53,13 +53,7 @@ export default defineConfig({ {text: "Publishing โœ…", link: "/core/docs/fundamentals/publishing"}, {text: "Subscribing โœ…", link: "/core/docs/fundamentals/subscribing"}, {text: "Signers โœ…", link: "/core/docs/fundamentals/signers"}, - {text: "Zaps โŒ", link: "/core/docs/tutorial/zaps"}, - {text: "Local-first โŒ", link: "/core/docs/tutorial/local-first"}, - { - text: "Subscription Management โŒ", - link: "/core/docs/tutorial/subscription-management", - }, - {text: "Mute Filtering โŒ", link: "/core/docs/tutorial/mute-filtering"}, + {text: "Zaps โœ…", link: "/core/docs/fundamentals/zapping"}, {text: "Helpers โŒ", link: "/core/docs/fundamentals/helpers"}, ], }, From c14b6682dcc8b86c91f9ef116f78e53ca279b86e Mon Sep 17 00:00:00 2001 From: digitalbase Date: Tue, 25 Nov 2025 12:07:50 +0100 Subject: [PATCH 118/139] Fix build --- core/docs/fundamentals/zapping.md | 10 ++++++++++ core/docs/getting-started/usage.md | 2 +- core/docs/snippets/zap.ts | 16 ++++++++++++++++ core/docs/tutorial/zaps.md | 16 ---------------- 4 files changed, 27 insertions(+), 17 deletions(-) create mode 100644 core/docs/fundamentals/zapping.md create mode 100644 core/docs/snippets/zap.ts delete mode 100644 core/docs/tutorial/zaps.md diff --git a/core/docs/fundamentals/zapping.md b/core/docs/fundamentals/zapping.md new file mode 100644 index 000000000..13b98e122 --- /dev/null +++ b/core/docs/fundamentals/zapping.md @@ -0,0 +1,10 @@ +# Zaps + +NDK comes with an interface to make zapping as simple as possible. + +<<< @/core/docs/snippets/zap.ts + +## NDK-Wallet + +Refer to [the Wallet section of the documentation](/wallet/README) to learn more about zapping. +NDK-wallet provides many conveniences to integrate with zaps. diff --git a/core/docs/getting-started/usage.md b/core/docs/getting-started/usage.md index e1804cc63..022d09b4a 100644 --- a/core/docs/getting-started/usage.md +++ b/core/docs/getting-started/usage.md @@ -4,7 +4,7 @@ A simple example of how to use NDK in a Node.js application: -<<< @/core/docs/snippets/quick-start-with-guardrails.ts +<<< @/core/docs/snippets/examples/quick-start-with-guardrails.ts ## NDK Usage diff --git a/core/docs/snippets/zap.ts b/core/docs/snippets/zap.ts new file mode 100644 index 000000000..6715f00aa --- /dev/null +++ b/core/docs/snippets/zap.ts @@ -0,0 +1,16 @@ +// Import the package +import NDK, {NDKZapper} from "@nostr-dev-kit/ndk"; + +// Create a new NDK instance with explicit relays +const ndk = new NDK(); + +const user = await ndk.fetchUser("pablo@f7z.io"); + +if (user) { + + const lnPay = ({pr: 'lightning_url'}) => { + console.log("please pay to complete the zap"); + }; + const zapper = new NDKZapper(user, 1000, {lnPay}); + zapper.zap(); +} diff --git a/core/docs/tutorial/zaps.md b/core/docs/tutorial/zaps.md deleted file mode 100644 index f9ab1e62a..000000000 --- a/core/docs/tutorial/zaps.md +++ /dev/null @@ -1,16 +0,0 @@ -# Zaps - -NDK comes with an interface to make zapping as simple as possible. - -```ts -const user = await ndk.fetchUser("pablo@f7z.io"); -const lnPay = ({ pr: string }) => { - console.log("please pay to complete the zap", pr); -}; -const zapper = new NDKZapper(user, 1000, { lnPay }); -zapper.zap(); -``` - -## NDK-Wallet - -Refer to the Wallet section of the tutorial to learn more about zapping. NDK-wallet provides many conveniences to integrate with zaps. From a4268c566fbd512ca878401418d7d180e7ebcf47 Mon Sep 17 00:00:00 2001 From: digitalbase Date: Tue, 25 Nov 2025 16:44:21 +0100 Subject: [PATCH 119/139] Build to ./docs/dist folder --- docs/.vitepress/config.mts | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/.vitepress/config.mts b/docs/.vitepress/config.mts index ce314ca18..cc292c1f7 100644 --- a/docs/.vitepress/config.mts +++ b/docs/.vitepress/config.mts @@ -6,6 +6,7 @@ export default defineConfig({ srcDir: "../", description: "NDK Docs", cleanUrls: true, + outDir: "./dist", // base: "/ndk/", ignoreDeadLinks: true, markdown: { From e5fbe96bf504b839e03ced8b8657460aabfd0ef1 Mon Sep 17 00:00:00 2001 From: digitalbase Date: Tue, 25 Nov 2025 17:14:03 +0100 Subject: [PATCH 120/139] Rename config --- docs/.vitepress/{config.mts => config.ts} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename docs/.vitepress/{config.mts => config.ts} (100%) diff --git a/docs/.vitepress/config.mts b/docs/.vitepress/config.ts similarity index 100% rename from docs/.vitepress/config.mts rename to docs/.vitepress/config.ts From ca33521a3c23d9942ff180d82f706dcff08bc2f8 Mon Sep 17 00:00:00 2001 From: digitalbase Date: Tue, 25 Nov 2025 17:15:08 +0100 Subject: [PATCH 121/139] Update gitignore --- .gitignore | 19 ------------------- docs/home.md | 27 --------------------------- 2 files changed, 46 deletions(-) delete mode 100644 docs/home.md diff --git a/.gitignore b/.gitignore index 78af60a39..ed5e32921 100644 --- a/.gitignore +++ b/.gitignore @@ -20,33 +20,15 @@ package-lock.json *.tgz .DS_Store .turbo -.idea bun.lockb _local_ .svelte-kit/ .pnpm-store -docs/.vitepress/cache -docs/.vitepress/dist .ngit -.idea **/.repomix-output.txt cursor-tools.config.json coverage -# Generated docs directories -/docs/getting-started/ -/docs/internals/ -/docs/migration/ -/docs/tutorial/ -/docs/api-examples.md -/docs/index.md -/docs/snippets/ -/docs/cache/ -/docs/mobile/ -/docs/wallet/ -/docs/wrappers/ -/docs/hooks/ -**/docs/api/ cache-sqlite/example/cache.db # TENEX project files @@ -60,4 +42,3 @@ svelte/examples/basic-feed/public/ .test-sessions.json **/baseline-timing.ts **/current-timing.ts -.playwright-mcp diff --git a/docs/home.md b/docs/home.md deleted file mode 100644 index bd4ee11f2..000000000 --- a/docs/home.md +++ /dev/null @@ -1,27 +0,0 @@ ---- -# https://vitepress.dev/reference/default-theme-home-page -layout: home - -hero: - name: "NDK Documentation" - tagline: "Nostr Development Kit Docs" - actions: - - theme: brand - text: Documentation - link: /core/docs/introduction.html - - theme: secondary - text: Github Repository - link: https://github.com/nostr-dev-kit/ndk/ - ---- - -NDK (**Nostr Development Kit**) is a comprehensive type-safe Typescript toolkit for building Nostr applications. - -The library is a monorepo containing different packages and tools you need to create modern, performant, and feature-rich -Nostr clients and applications. The package contains relay interaction code, reactive UI bindings, wallet abstractions, -caching libraries, Web of Trust functionality, and more. - -> [!WARNING] -> The documentation of the NDK project is under heavy construction -> [More information available in open pull request](https://github.com/nostr-dev-kit/ndk/pull/344). -> \ No newline at end of file From 6d695215de5c6f21621e236f52ecbbbca293704d Mon Sep 17 00:00:00 2001 From: digitalbase Date: Tue, 25 Nov 2025 17:15:47 +0100 Subject: [PATCH 122/139] Add gitignore for docs dir --- docs/.gitignore | 2 ++ docs/api-examples.md | 53 ++++++++++++++++++++++++++++++++++++++++++++ docs/index.md | 28 +++++++++++++++++++++++ 3 files changed, 83 insertions(+) create mode 100644 docs/.gitignore create mode 100644 docs/api-examples.md create mode 100644 docs/index.md diff --git a/docs/.gitignore b/docs/.gitignore new file mode 100644 index 000000000..7b64d98e8 --- /dev/null +++ b/docs/.gitignore @@ -0,0 +1,2 @@ +.vitepress/cache +.vitepress/dist \ No newline at end of file diff --git a/docs/api-examples.md b/docs/api-examples.md new file mode 100644 index 000000000..77a5f5dd8 --- /dev/null +++ b/docs/api-examples.md @@ -0,0 +1,53 @@ +--- +outline: deep +--- + +# Runtime API Examples + +This page demonstrates usage of some of the runtime APIs provided by VitePress. + +The main `useData()` API can be used to access site, theme, and page data for the current page. It works in both `.md` +and `.vue` files: + +```md + + +## Results + +### Theme Data +
{{ theme }}
+ +### Page Data +
{{ page }}
+ +### Page Frontmatter +
{{ frontmatter }}
+``` + + + +## Results + +### Theme Data + +
{{ theme }}
+ +### Page Data + +
{{ page }}
+ +### Page Frontmatter + +
{{ frontmatter }}
+ +## More + +Check out the documentation for the [full list of runtime APIs](https://vitepress.dev/reference/runtime-api#usedata). diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 000000000..f0080cb46 --- /dev/null +++ b/docs/index.md @@ -0,0 +1,28 @@ +--- +# https://vitepress.dev/reference/default-theme-home-page +layout: home + +hero: + name: "NDK Documentation" + tagline: "Nostr Development Kit Docs" + actions: + - theme: brand + text: Documentation + link: /docs/introduction + - theme: secondary + text: Github Repository + link: https://github.com/nostr-dev-kit/ndk/ + +--- + +NDK (**Nostr Development Kit**) is a comprehensive type-safe Typescript toolkit for building Nostr applications. + +The library is a monorepo containing different packages and tools you need to create modern, performant, and +feature-rich +Nostr clients and applications. The package contains relay interaction code, reactive UI bindings, wallet abstractions, +caching libraries, Web of Trust functionality, and more. + +> [!WARNING] +> The documentation of the NDK project is under heavy construction +> [More information available in open pull request](https://github.com/nostr-dev-kit/ndk/pull/344). +> \ No newline at end of file From d5513b6245fd04eaba2f559b7a28854bdca03df9 Mon Sep 17 00:00:00 2001 From: digitalbase Date: Tue, 25 Nov 2025 17:16:57 +0100 Subject: [PATCH 123/139] Add public directory --- docs/.vitepress/config.ts | 158 +++++++++++++++++++------------------- 1 file changed, 79 insertions(+), 79 deletions(-) diff --git a/docs/.vitepress/config.ts b/docs/.vitepress/config.ts index cc292c1f7..fc6b982ac 100644 --- a/docs/.vitepress/config.ts +++ b/docs/.vitepress/config.ts @@ -1,4 +1,4 @@ -import {defineConfig} from "vitepress"; +import { defineConfig } from "vitepress"; // https://vitepress.dev/reference/site-config export default defineConfig({ @@ -6,89 +6,89 @@ export default defineConfig({ srcDir: "../", description: "NDK Docs", cleanUrls: true, - outDir: "./dist", + outDir: "./public", // base: "/ndk/", ignoreDeadLinks: true, markdown: { theme: { - light: 'github-light', - dark: 'github-dark' - } + light: "github-light", + dark: "github-dark", + }, }, rewrites: { - 'docs/index.md': 'index.md', + "docs/index.md": "index.md", }, themeConfig: { // https://vitepress.dev/reference/default-theme-config nav: [ - {text: "Home โœ…", link: "/docs/index.md"}, + { text: "Home โœ…", link: "/docs/index.md" }, // { text: "API Reference", link: "/api/", target: "_blank" }, // { text: "Cookbook", link: "/cookbook/" }, - {text: "Snippets โœ…", link: "/docs/snippets.md"}, - {text: "Wiki โœ…", link: "https://wikifreedia.xyz/?c=NDK", target: "_blank"}, + { text: "Snippets โœ…", link: "/docs/snippets.md" }, + { text: "Wiki โœ…", link: "https://wikifreedia.xyz/?c=NDK", target: "_blank" }, ], sidebar: [ { items: [ - {text: "Introduction โœ…", link: "/docs/introduction"}, - {text: "About Nostr โœ…", link: "/docs/about-nostr"}, - {text: "Contributing โŒ", link: "/docs/contributing"}, - {text: "Changelog โœ…", link: "/docs/changelogs"}, + { text: "Introduction โœ…", link: "/docs/introduction" }, + { text: "About Nostr โœ…", link: "/docs/about-nostr" }, + { text: "Contributing โŒ", link: "/docs/contributing" }, + { text: "Changelog โœ…", link: "/docs/changelogs" }, ], }, { text: "Getting Started", items: [ - {text: "Installing โœ…", link: "/core/docs/getting-started/installing"}, - {text: "Usage โœ…", link: "/core/docs/getting-started/usage"}, - {text: "Debugging โœ…", link: "/core/docs/getting-started/debugging"}, + { text: "Installing โœ…", link: "/core/docs/getting-started/installing" }, + { text: "Usage โœ…", link: "/core/docs/getting-started/usage" }, + { text: "Debugging โœ…", link: "/core/docs/getting-started/debugging" }, ], }, { text: "Fundamentals", collapsed: false, items: [ - {text: "Events โœ…", link: "/core/docs/fundamentals/events"}, - {text: "Connecting โœ…", link: "/core/docs/fundamentals/connecting"}, - {text: "Publishing โœ…", link: "/core/docs/fundamentals/publishing"}, - {text: "Subscribing โœ…", link: "/core/docs/fundamentals/subscribing"}, - {text: "Signers โœ…", link: "/core/docs/fundamentals/signers"}, - {text: "Zaps โœ…", link: "/core/docs/fundamentals/zapping"}, - {text: "Helpers โŒ", link: "/core/docs/fundamentals/helpers"}, + { text: "Events โœ…", link: "/core/docs/fundamentals/events" }, + { text: "Connecting โœ…", link: "/core/docs/fundamentals/connecting" }, + { text: "Publishing โœ…", link: "/core/docs/fundamentals/publishing" }, + { text: "Subscribing โœ…", link: "/core/docs/fundamentals/subscribing" }, + { text: "Signers โœ…", link: "/core/docs/fundamentals/signers" }, + { text: "Zaps โœ…", link: "/core/docs/fundamentals/zapping" }, + { text: "Helpers โŒ", link: "/core/docs/fundamentals/helpers" }, ], }, { text: "Sessions", collapsed: true, items: [ - {text: "Introduction โœ…", link: "/sessions/docs/introduction"}, - {text: "Quick Start โœ…", link: "/sessions/docs/quick-start"}, - {text: "Storage Options โœ…", link: "/sessions/docs/storage-options"}, - {text: "Multi-Account โœ…", link: "/sessions/docs/multi-account"}, - {text: "Migration Guide โœ…", link: "/sessions/docs/migration"}, - {text: "Advanced โŒ", link: "/sessions/docs/advanced"}, - {text: "Best Practices โŒ", link: "/sessions/docs/best-practices"}, - {text: "API Reference โœ…", link: "/sessions/docs/api"}, - {text: "README โ›”", link: "/sessions/README"} + { text: "Introduction โœ…", link: "/sessions/docs/introduction" }, + { text: "Quick Start โœ…", link: "/sessions/docs/quick-start" }, + { text: "Storage Options โœ…", link: "/sessions/docs/storage-options" }, + { text: "Multi-Account โœ…", link: "/sessions/docs/multi-account" }, + { text: "Migration Guide โœ…", link: "/sessions/docs/migration" }, + { text: "Advanced โŒ", link: "/sessions/docs/advanced" }, + { text: "Best Practices โŒ", link: "/sessions/docs/best-practices" }, + { text: "API Reference โœ…", link: "/sessions/docs/api" }, + { text: "README โ›”", link: "/sessions/README" }, ], }, { text: "Wallet", collapsed: true, items: [ - {text: "Introduction โŒ", link: "/wallet/README"}, - {text: "WebLN Wallet โŒ", link: '/wallet/docs/NDKWebLNWallet'}, - {text: "NWC Wallet โŒ", link: '/wallet/docs/NDKNWCWallet'}, + { text: "Introduction โŒ", link: "/wallet/README" }, + { text: "WebLN Wallet โŒ", link: "/wallet/docs/NDKWebLNWallet" }, + { text: "NWC Wallet โŒ", link: "/wallet/docs/NDKNWCWallet" }, { text: "CashuWallet (NIP-60) โŒ", link: "/wallet/docs/NDKCashuWallet", items: [ - {text: "Nutsack โŒ", link: "/wallet/docs/nutsack"}, - {text: "Nutzaps โŒ", link: "/wallet/docs/nutzaps"}, - {text: "Nutzap Monitor โŒ", link: "/wallet/docs/nutzap-monitor"}, - {text: "Monitor State Store โŒ", link: "/wallet/docs/nutzap-monitor-state-store"}, - ] + { text: "Nutsack โŒ", link: "/wallet/docs/nutsack" }, + { text: "Nutzaps โŒ", link: "/wallet/docs/nutzaps" }, + { text: "Nutzap Monitor โŒ", link: "/wallet/docs/nutzap-monitor" }, + { text: "Monitor State Store โŒ", link: "/wallet/docs/nutzap-monitor-state-store" }, + ], }, ], }, @@ -100,43 +100,43 @@ export default defineConfig({ text: "Svelte โ›”", link: "/svelte/README โ›”", items: [ - {text: "Blossom โ›”", link: "/svelte/blossom-upload"}, - {text: "Changelog โ›”", link: "/svelte/CHANGELOG.md"}, - ] + { text: "Blossom โ›”", link: "/svelte/blossom-upload" }, + { text: "Changelog โ›”", link: "/svelte/CHANGELOG.md" }, + ], }, { text: "React โŒ", link: "/react/README", items: [ - {text: "Getting Started โŒ", link: "/react/docs/getting-started"}, - {text: "Muting โŒ", link: "/react/muting"}, - {text: "Session Management โŒ", link: "/react/session-management"}, - ] - } + { text: "Getting Started โŒ", link: "/react/docs/getting-started" }, + { text: "Muting โŒ", link: "/react/muting" }, + { text: "Session Management โŒ", link: "/react/session-management" }, + ], + }, ], }, { text: "Mobile", collapsed: true, items: [ - {text: "Introduction โŒ", link: "/mobile/README"}, - {text: "Session โ›”", link: "/mobile/session"}, - {text: "Wallet โ›”", link: "/mobile/wallet"}, - {text: "Subscriptions โ›”", link: "/mobile/subscriptions"}, - {text: "Nutzaps โ›”", link: "/mobile/nutzaps"}, - {text: "Mint โ›”", link: "/mobile/mint"}, + { text: "Introduction โŒ", link: "/mobile/README" }, + { text: "Session โ›”", link: "/mobile/session" }, + { text: "Wallet โ›”", link: "/mobile/wallet" }, + { text: "Subscriptions โ›”", link: "/mobile/subscriptions" }, + { text: "Nutzaps โ›”", link: "/mobile/nutzaps" }, + { text: "Mint โ›”", link: "/mobile/mint" }, ], }, { text: "Blossom (Media)", collapsed: true, items: [ - {text: "Introduction โŒ", link: "/blossom/README"}, - {text: "Getting Started โ›”", link: "/blossom/getting-started"}, - {text: "Error Handling โ›”", link: "/blossom/error-handling"}, - {text: "Mirroring โ›”", link: "/blossom/mirroring"}, - {text: "Optimization โ›”", link: "/blossom/optimization"}, - ] + { text: "Introduction โŒ", link: "/blossom/README" }, + { text: "Getting Started โ›”", link: "/blossom/getting-started" }, + { text: "Error Handling โ›”", link: "/blossom/error-handling" }, + { text: "Mirroring โ›”", link: "/blossom/mirroring" }, + { text: "Optimization โ›”", link: "/blossom/optimization" }, + ], }, { text: "Advanced Topics", @@ -144,60 +144,60 @@ export default defineConfig({ items: [ { text: "Signer Persistence โŒ", - link: "/core/docs/tutorial/signer-persistence" + link: "/core/docs/tutorial/signer-persistence", }, { text: "Speed / Performance โŒ", - link: "/core/docs/tutorial/speed" + link: "/core/docs/tutorial/speed", }, { text: "AI Guardrails โœ…", - link: "/core/docs/advanced/ai-guardrails" + link: "/core/docs/advanced/ai-guardrails", }, { text: "Exclusive Relays โœ…", - link: "/core/docs/advanced/exclusive-relay" + link: "/core/docs/advanced/exclusive-relay", }, { text: "Encrypting Keys (NIP-49) โœ…", - link: "/core/docs/advanced/encrypted-keys-nip49" + link: "/core/docs/advanced/encrypted-keys-nip49", }, { text: "Subscription Lifecycle โŒ", - link: "/core/docs/advanced/subscription-internals" + link: "/core/docs/advanced/subscription-internals", }, { text: "Event Class Registration โŒ", - link: "/core/docs/advanced/event-class-registration" + link: "/core/docs/advanced/event-class-registration", }, { text: "Relay Metadata Caching โ›”", - link: "/core/docs/advanced/relay-metadata-caching" + link: "/core/docs/advanced/relay-metadata-caching", }, { text: "Sync & Negentropy โ›”", - link: "/sync" + link: "/sync", }, { text: "Web of Trust (WOT) โ›”", - link: "/wot/README" + link: "/wot/README", }, { text: "Cache Adapters โŒ", collapsed: true, items: [ - {text: "Memory / LRU โŒ", link: "/cache/memory/README"}, - {text: "Dexie / IndexedDB โŒ", link: "cache/dexie/README"}, - {text: "Local Nostr Relay โŒ", link: "/cache/nostr/README"}, - {text: "Redis โŒ", link: "/cache/redis/README"}, - {text: "SQLite โŒ", link: "/cache/sqlite/README"}, + { text: "Memory / LRU โŒ", link: "/cache/memory/README" }, + { text: "Dexie / IndexedDB โŒ", link: "cache/dexie/README" }, + { text: "Local Nostr Relay โŒ", link: "/cache/nostr/README" }, + { text: "Redis โŒ", link: "/cache/redis/README" }, + { text: "SQLite โŒ", link: "/cache/sqlite/README" }, { text: "SQLite WASM โŒ", link: "/cache/sqlite-wasm/README", items: [ - {text: "Bundling โŒ", link: "/cache/sqlite-wasm/bundling"}, - {text: "Web Worker Setup โŒ", link: "/cache/sqlite-wasm/web-worker-setup"} - ] + { text: "Bundling โŒ", link: "/cache/sqlite-wasm/bundling" }, + { text: "Web Worker Setup โŒ", link: "/cache/sqlite-wasm/web-worker-setup" }, + ], }, ], }, @@ -208,6 +208,6 @@ export default defineConfig({ level: [2, 3], }, - socialLinks: [{icon: "github", link: "https://github.com/nostr-dev-kit/ndk"}], + socialLinks: [{ icon: "github", link: "https://github.com/nostr-dev-kit/ndk" }], }, }); From 6abef9ff4ee318b1d04544161875b342c2d0d14d Mon Sep 17 00:00:00 2001 From: digitalbase Date: Tue, 25 Nov 2025 17:33:08 +0100 Subject: [PATCH 124/139] Fix build errors by moving file path to code snippet --- docs/.vitepress/config.ts | 2 +- svelte/examples/sessions-demo/CLAUDE.md | 12 ++++++++---- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/docs/.vitepress/config.ts b/docs/.vitepress/config.ts index fc6b982ac..d368fd5ad 100644 --- a/docs/.vitepress/config.ts +++ b/docs/.vitepress/config.ts @@ -6,7 +6,7 @@ export default defineConfig({ srcDir: "../", description: "NDK Docs", cleanUrls: true, - outDir: "./public", + outDir: "../public", // base: "/ndk/", ignoreDeadLinks: true, markdown: { diff --git a/svelte/examples/sessions-demo/CLAUDE.md b/svelte/examples/sessions-demo/CLAUDE.md index b8100b77e..d7b7d217c 100644 --- a/svelte/examples/sessions-demo/CLAUDE.md +++ b/svelte/examples/sessions-demo/CLAUDE.md @@ -27,7 +27,8 @@ Default to using Bun instead of Node.js. Use `bun test` to run tests. -```ts#index.test.ts +```ts +// index.test.ts import { test, expect } from "bun:test"; test("hello world", () => { @@ -41,7 +42,8 @@ Use HTML imports with `Bun.serve()`. Don't use `vite`. HTML imports fully suppor Server: -```ts#index.ts +```ts +// index.ts import index from "./index.html" Bun.serve({ @@ -74,7 +76,8 @@ Bun.serve({ HTML files can import .tsx, .jsx or .js files directly and Bun's bundler will transpile & bundle automatically. `` tags can point to stylesheets and Bun's CSS bundler will bundle. -```html#index.html +```html +

Hello, world!

@@ -85,7 +88,8 @@ HTML files can import .tsx, .jsx or .js files directly and Bun's bundler will tr With the following `frontend.tsx`: -```tsx#frontend.tsx +```tsx +//frontend.tsx import React from "react"; // import .css files directly and it works From 0eb624e62add866598a6459267c92f8fd43c8396 Mon Sep 17 00:00:00 2001 From: digitalbase Date: Tue, 25 Nov 2025 19:42:51 +0100 Subject: [PATCH 125/139] Add build directory to gitignore --- docs/.gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/.gitignore b/docs/.gitignore index 7b64d98e8..20197e813 100644 --- a/docs/.gitignore +++ b/docs/.gitignore @@ -1,2 +1,3 @@ .vitepress/cache -.vitepress/dist \ No newline at end of file +.vitepress/dist +public/* \ No newline at end of file From 9bbfd963d3372ce023dcdd647c1629db81e65bb5 Mon Sep 17 00:00:00 2001 From: digitalbase Date: Tue, 9 Dec 2025 16:59:58 +0100 Subject: [PATCH 126/139] Add package.json file to snippets so scripts can be verified with latest NDK --- core/docs/snippets/.gitignore | 1 + core/docs/snippets/package.json | 10 ++++++++++ core/tsconfig.json | 4 +--- sessions/docs/index.md | 0 4 files changed, 12 insertions(+), 3 deletions(-) create mode 100644 core/docs/snippets/.gitignore create mode 100644 core/docs/snippets/package.json delete mode 100644 sessions/docs/index.md diff --git a/core/docs/snippets/.gitignore b/core/docs/snippets/.gitignore new file mode 100644 index 000000000..b512c09d4 --- /dev/null +++ b/core/docs/snippets/.gitignore @@ -0,0 +1 @@ +node_modules \ No newline at end of file diff --git a/core/docs/snippets/package.json b/core/docs/snippets/package.json new file mode 100644 index 000000000..0da4cf6df --- /dev/null +++ b/core/docs/snippets/package.json @@ -0,0 +1,10 @@ +{ + "name": "snippets", + "version": "0.0.1", + "private": true, + "type": "module", + "scripts": {}, + "dependencies": { + "@nostr-dev-kit/ndk": "3.0.0-beta.62" + } +} diff --git a/core/tsconfig.json b/core/tsconfig.json index d385cf731..2a7483781 100644 --- a/core/tsconfig.json +++ b/core/tsconfig.json @@ -22,8 +22,6 @@ "lib": ["dom", "dom.iterable", "esnext"], "emitDeclarationOnly": true, "outDir": "lib", - "paths": { - "@nostr-dev-kit/ndk": ["src/index.ts"] - } + "paths": {} } } diff --git a/sessions/docs/index.md b/sessions/docs/index.md deleted file mode 100644 index e69de29bb..000000000 From 1ea89523861a0da4adb18b8607f98d4d3640afa5 Mon Sep 17 00:00:00 2001 From: digitalbase Date: Tue, 9 Dec 2025 17:00:12 +0100 Subject: [PATCH 127/139] Update doc to check on Pablo's revert --- docs/TODOS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/TODOS.md b/docs/TODOS.md index 011de52e4..69956972b 100644 --- a/docs/TODOS.md +++ b/docs/TODOS.md @@ -30,6 +30,7 @@ Some notes: - [ ] Try to find (or remove) API reference link - [ ] Remove warnings (homepage and main intro) about Docs WIP - [ ] Remove โœ… โŒ โ›” icons +- [ ] Check revert commit https://github.com/nostr-dev-kit/ndk/commit/4a7b086bd1f67f22a6c29adcf4d6c51b4dd7077c ### Changes made that need double check From c8ce873b3b7cb712bca4bfc3f86b5f7981d279ad Mon Sep 17 00:00:00 2001 From: digitalbase Date: Tue, 9 Dec 2025 17:01:23 +0100 Subject: [PATCH 128/139] Update gitignore --- core/docs/snippets/.gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core/docs/snippets/.gitignore b/core/docs/snippets/.gitignore index b512c09d4..f52e6f577 100644 --- a/core/docs/snippets/.gitignore +++ b/core/docs/snippets/.gitignore @@ -1 +1,2 @@ -node_modules \ No newline at end of file +node_modules +pnpm-lock.yaml \ No newline at end of file From 20e14382a726ab01806068fecb2d032bd0f0fb6a Mon Sep 17 00:00:00 2001 From: digitalbase Date: Tue, 9 Dec 2025 18:53:09 +0100 Subject: [PATCH 129/139] Fix snippets --- core/docs/snippets/connect_auth.ts | 1 + core/docs/snippets/connect_explicit_alt.ts | 5 +++-- core/docs/snippets/connect_nip07.ts | 3 ++- core/docs/snippets/connect_pools.ts | 6 +++++- core/docs/snippets/connecting.ts | 1 + core/docs/snippets/interest_event.ts | 4 ---- core/docs/snippets/key_create.ts | 1 + core/docs/snippets/managing-users.ts | 2 +- core/docs/snippets/nip-49-encrypting.ts | 4 ++-- core/docs/snippets/publish_event.ts | 1 + core/docs/snippets/publish_failure.ts | 6 ++++-- core/docs/snippets/replace_event.ts | 6 ++++-- core/docs/snippets/sign_event_with_other_signers.ts | 4 ++-- core/docs/snippets/subscribe_event_handlers.ts | 2 +- 14 files changed, 28 insertions(+), 18 deletions(-) diff --git a/core/docs/snippets/connect_auth.ts b/core/docs/snippets/connect_auth.ts index 8b4547c46..52a85cb50 100644 --- a/core/docs/snippets/connect_auth.ts +++ b/core/docs/snippets/connect_auth.ts @@ -1,4 +1,5 @@ import NDK, { NDKRelayAuthPolicies } from "@nostr-dev-kit/ndk"; const ndk = new NDK(); + ndk.addExplicitRelay("wss://relay.f7z.io", NDKRelayAuthPolicies.signIn({ ndk })); diff --git a/core/docs/snippets/connect_explicit_alt.ts b/core/docs/snippets/connect_explicit_alt.ts index 6f67f1d9a..d556f3eaf 100644 --- a/core/docs/snippets/connect_explicit_alt.ts +++ b/core/docs/snippets/connect_explicit_alt.ts @@ -4,8 +4,9 @@ import NDK from "@nostr-dev-kit/ndk"; // Create a new NDK instance with explicit relays const ndk = new NDK(); -ndk.addExplicitRelay("wss://a.relay"); -ndk.addExplicitRelay("wss://another.relay"); +ndk.addExplicitRelay("wss://relay.damus.io"); +ndk.addExplicitRelay("wss://nos.lol"); +ndk.addExplicitRelay("wss://relay.nostr.band"); // Now connect to specified relays await ndk.connect(); diff --git a/core/docs/snippets/connect_nip07.ts b/core/docs/snippets/connect_nip07.ts index ead101567..2c86c66c5 100644 --- a/core/docs/snippets/connect_nip07.ts +++ b/core/docs/snippets/connect_nip07.ts @@ -2,6 +2,7 @@ import NDK, { NDKNip07Signer } from "@nostr-dev-kit/ndk"; // Create a new NDK instance with signer -// (provided the signer implements the getRelays() method) +// provided the signer implements the getRelays() method const nip07signer = new NDKNip07Signer(); + const ndk = new NDK({ signer: nip07signer }); diff --git a/core/docs/snippets/connect_pools.ts b/core/docs/snippets/connect_pools.ts index a0768ae03..23baf1bdf 100644 --- a/core/docs/snippets/connect_pools.ts +++ b/core/docs/snippets/connect_pools.ts @@ -5,7 +5,11 @@ const ndk = new NDK(); const largeRelays = new NDKPool([`wss://relay.damus.io`, "wss://premium.primal.net"], ndk); largeRelays.addRelay(new NDKRelay("wss://nos.lol", undefined, ndk)); -const nicheRelays = new NDKPool([`wss://asad`, "wss://premisadasdum.primal.net"], ndk); +largeRelays.connect(); + +ndk.pools.length; // 1 + +const nicheRelays = new NDKPool([`wss://relay.vertexlab.io`, "wss://purplepag.es/"], ndk); nicheRelays.connect(); diff --git a/core/docs/snippets/connecting.ts b/core/docs/snippets/connecting.ts index b3f0fdbe1..efab0ffbf 100644 --- a/core/docs/snippets/connecting.ts +++ b/core/docs/snippets/connecting.ts @@ -5,5 +5,6 @@ import NDK from "@nostr-dev-kit/ndk"; const ndk = new NDK({ explicitRelayUrls: ["wss://a.relay", "wss://another.relay"], }); + // Now connect to specified relays await ndk.connect(); diff --git a/core/docs/snippets/interest_event.ts b/core/docs/snippets/interest_event.ts index 207eb2f5a..49bfe904c 100644 --- a/core/docs/snippets/interest_event.ts +++ b/core/docs/snippets/interest_event.ts @@ -19,10 +19,6 @@ interestList.addInterest("bitcoin"); interestList.addInterest("technology"); interestList.addInterest("privacy"); -// Set a title for the list (optional) -interestList.title = "My Interests"; -interestList.description = "Topics I'm passionate about"; - console.log("Has 'nostr'?", interestList.hasInterest("nostr")); console.log("Has 'ethereum'?", interestList.hasInterest("ethereum")); diff --git a/core/docs/snippets/key_create.ts b/core/docs/snippets/key_create.ts index 5d1234f57..6b266dd4e 100644 --- a/core/docs/snippets/key_create.ts +++ b/core/docs/snippets/key_create.ts @@ -2,6 +2,7 @@ import { NDKPrivateKeySigner } from "@nostr-dev-kit/ndk"; // Generate a new private key const signer = NDKPrivateKeySigner.generate(); + const privateKey = signer.privateKey; // Get the hex private key const publicKey = signer.pubkey; // Get the hex public key const nsec = signer.nsec; // Get the private key in nsec format diff --git a/core/docs/snippets/managing-users.ts b/core/docs/snippets/managing-users.ts index 52e12dd3d..b1abfd17e 100644 --- a/core/docs/snippets/managing-users.ts +++ b/core/docs/snippets/managing-users.ts @@ -1,6 +1,6 @@ import NDK from "@nostr-dev-kit/ndk"; -const ndk = new NDK({}); +const ndk = new NDK(); // From hex pubkey const user1 = await ndk.fetchUser("3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d"); diff --git a/core/docs/snippets/nip-49-encrypting.ts b/core/docs/snippets/nip-49-encrypting.ts index b8c7115ea..3fdad18b1 100644 --- a/core/docs/snippets/nip-49-encrypting.ts +++ b/core/docs/snippets/nip-49-encrypting.ts @@ -1,5 +1,5 @@ -import {bytesToHex, hexToBytes} from "@noble/hashes/utils"; -import {nip49} from "@nostr-dev-kit/ndk"; +import { bytesToHex, hexToBytes } from "@noble/hashes/utils"; +import { nip49 } from "@nostr-dev-kit/ndk"; // Encrypt raw private key bytes const privateKeyHex = "14c226dbdd865d5e1645e72c7470fd0a17feb42cc87b750bab6538171b3a3f8a"; diff --git a/core/docs/snippets/publish_event.ts b/core/docs/snippets/publish_event.ts index bee9ad119..b65a3fc27 100644 --- a/core/docs/snippets/publish_event.ts +++ b/core/docs/snippets/publish_event.ts @@ -6,4 +6,5 @@ const ndk = new NDK({ signer: nip07signer }); const event = new NDKEvent(ndk); event.kind = 1; event.content = "Hello world"; + await event.publish(); // [!code focus] diff --git a/core/docs/snippets/publish_failure.ts b/core/docs/snippets/publish_failure.ts index 9703ef5fe..0fab81a9a 100644 --- a/core/docs/snippets/publish_failure.ts +++ b/core/docs/snippets/publish_failure.ts @@ -1,7 +1,7 @@ import NDK, { NDKEvent, type NDKPublishError } from "@nostr-dev-kit/ndk"; const ndk = new NDK({ - explicitRelayUrls: ["wss://relay.damus.io", "wss://relay.nostr.band", "wss://nos.lol"], + explicitRelayUrls: ["wss://relay.damus.io", "wss://non.existing.relay", "wss://nos.lol"], }); await ndk.connect(); @@ -12,5 +12,7 @@ const event = new NDKEvent(ndk, { }); ndk.on(`event:publish-failed`, (event: NDKEvent, error: NDKPublishError, relays: WebSocket["url"][]) => { - console.log("Event failed to publish on", relays, error); + console.log("Event failed to publish on", event, relays, error); }); + +await event.publish(); diff --git a/core/docs/snippets/replace_event.ts b/core/docs/snippets/replace_event.ts index 78ff76d68..7dd09ae92 100644 --- a/core/docs/snippets/replace_event.ts +++ b/core/docs/snippets/replace_event.ts @@ -7,6 +7,8 @@ if (existingEvent) { existingEvent.tags.push( ["p", "fa984bd7dbb282f07e16e7ae87b26a2a7b9b90b7246a44771f0cf5ae58018f52"], // follow a new user ); - existingEvent.publish(); // this will NOT work - existingEvent.publishReplaceable(); // this WILL work + + await existingEvent.publish(); // this will NOT work + + await existingEvent.publishReplaceable(); // this WILL work } diff --git a/core/docs/snippets/sign_event_with_other_signers.ts b/core/docs/snippets/sign_event_with_other_signers.ts index b2eafca7f..cd9f5dd08 100644 --- a/core/docs/snippets/sign_event_with_other_signers.ts +++ b/core/docs/snippets/sign_event_with_other_signers.ts @@ -8,7 +8,7 @@ event1.kind = 1; event1.content = "Hello world"; await event1.sign(signer1); -event1.pubkey === pubkey1; // true +console.log(event1.pubkey === pubkey1); // true const signer2 = NDKPrivateKeySigner.generate(); const pubkey2 = signer2.pubkey; @@ -18,4 +18,4 @@ event2.kind = 1; event2.content = "Hello world"; await event2.sign(signer2); -event2.pubkey === pubkey2; // true +console.log(event2.pubkey === pubkey2); // true diff --git a/core/docs/snippets/subscribe_event_handlers.ts b/core/docs/snippets/subscribe_event_handlers.ts index 4638ea637..c523637a7 100644 --- a/core/docs/snippets/subscribe_event_handlers.ts +++ b/core/docs/snippets/subscribe_event_handlers.ts @@ -9,7 +9,7 @@ ndk.subscribe( // Direct handlers via autoStart parameter (now the 3rd argument) onEvent: (event: NDKEvent, relay?: NDKRelay) => { // Called for events received from relays after the initial cache load (if onEvents is used) - console.log("Received event from relay (id):", event.id); + console.log("Received event from relay (id):", event.id, relay); }, onEvents: (events: NDKEvent[]) => { // Parameter renamed to 'events' From a1507f8825efa4cdcbc2c16ffb981adce1c2911a Mon Sep 17 00:00:00 2001 From: digitalbase Date: Tue, 9 Dec 2025 18:53:22 +0100 Subject: [PATCH 130/139] Add wallet to package.json and fix zap snippet --- core/docs/snippets/package.json | 3 ++- core/docs/snippets/zap.ts | 15 ++++++++------- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/core/docs/snippets/package.json b/core/docs/snippets/package.json index 0da4cf6df..79c2ff31c 100644 --- a/core/docs/snippets/package.json +++ b/core/docs/snippets/package.json @@ -5,6 +5,7 @@ "type": "module", "scripts": {}, "dependencies": { - "@nostr-dev-kit/ndk": "3.0.0-beta.62" + "@nostr-dev-kit/ndk": "3.0.0-beta.62", + "@nostr-dev-kit/wallet": "1.0.0-beta.62" } } diff --git a/core/docs/snippets/zap.ts b/core/docs/snippets/zap.ts index 6715f00aa..a32a5d2dd 100644 --- a/core/docs/snippets/zap.ts +++ b/core/docs/snippets/zap.ts @@ -1,16 +1,17 @@ // Import the package -import NDK, {NDKZapper} from "@nostr-dev-kit/ndk"; +import NDK, { NDKZapper } from "@nostr-dev-kit/ndk"; +import { NDKWebLNWallet } from "@nostr-dev-kit/wallet"; // Create a new NDK instance with explicit relays const ndk = new NDK(); -const user = await ndk.fetchUser("pablo@f7z.io"); +const wallet = new NDKWebLNWallet(ndk); + +ndk.wallet = wallet; +const user = await ndk.fetchUser("pablo@f7z.io"); if (user) { + const zapper = new NDKZapper(user, 1000, "msat", { ndk }); - const lnPay = ({pr: 'lightning_url'}) => { - console.log("please pay to complete the zap"); - }; - const zapper = new NDKZapper(user, 1000, {lnPay}); - zapper.zap(); + await zapper.zap(); } From 266f6b61b44e47d60365b1777df4d1937bf1a22a Mon Sep 17 00:00:00 2001 From: digitalbase Date: Tue, 9 Dec 2025 19:00:12 +0100 Subject: [PATCH 131/139] Update docs from upstream changes (4a7b086bd1f67f22a6c29adcf4d6c51b4dd7077c) --- core/docs/advanced/exclusive-relay.md | 37 +++++++++---------- .../snippets/subscribe_relay_targetting.ts | 13 +++---- docs/TODOS.md | 2 +- 3 files changed, 24 insertions(+), 28 deletions(-) diff --git a/core/docs/advanced/exclusive-relay.md b/core/docs/advanced/exclusive-relay.md index a6b573a39..93a27879e 100644 --- a/core/docs/advanced/exclusive-relay.md +++ b/core/docs/advanced/exclusive-relay.md @@ -21,14 +21,13 @@ const normalSub = ndk.subscribe( {kinds: [1], authors: ['pubkey...']}, { relayUrls: ['wss://relay-a.com'], - exclusiveRelay: false // or omit (default) + exclusiveRelay: false, // or omit (default) + onEvent: (event) => { + // This fires for events from relay-a.com, relay-b.com, relay-c.com + // or any other relay, as long as the filter matches + } } ); - -normalSub.on('event', (event) => { - // This fires for events from relay-a.com, relay-b.com, relay-c.com - // or any other relay, as long as the filter matches -}); ``` ## Use Cases @@ -59,13 +58,13 @@ const testSub = ndk.subscribe( { relayUrls: ['wss://test-relay.com'], exclusiveRelay: true, - closeOnEose: true + closeOnEose: true, + onEose: () => { + console.log('Finished fetching from test-relay.com'); + } } ); -testSub.on('eose', () => { - console.log('Finished fetching from test-relay.com'); -}); ``` ### 3. Relay-Based Routing @@ -77,7 +76,10 @@ const publicRelaySub = ndk.subscribe( {kinds: [1]}, { relayUrls: ['wss://public-relay.com'], - exclusiveRelay: true + exclusiveRelay: true, + onEvent: (event) => { + console.log('Public event:', event.content); + } } ); @@ -85,17 +87,12 @@ const privateRelaySub = ndk.subscribe( {kinds: [1]}, { relayUrls: ['wss://private-relay.com'], - exclusiveRelay: true + exclusiveRelay: true, + onEvent: (event) => { + console.log('Private event:', event.content); + } } ); - -publicRelaySub.on('event', (event) => { - console.log('Public event:', event.content); -}); - -privateRelaySub.on('event', (event) => { - console.log('Private event:', event.content); -}); ``` ## Using NDKRelaySet diff --git a/core/docs/snippets/subscribe_relay_targetting.ts b/core/docs/snippets/subscribe_relay_targetting.ts index 67e67b9c0..c5b734662 100644 --- a/core/docs/snippets/subscribe_relay_targetting.ts +++ b/core/docs/snippets/subscribe_relay_targetting.ts @@ -4,15 +4,14 @@ const ndk = new NDK(); // Subscription that ONLY accepts events from relay-a.com const exclusiveSub = ndk.subscribe( - {kinds: [7]}, + { kinds: [7] }, { relayUrls: ["wss://relay-a.com"], exclusiveRelay: true, // ๐Ÿ”‘ Key option + onEvent: (event) => { + console.log("Event from relay-a.com:", event.content); + // This will ONLY fire for events from relay-a.com + // Events from relay-b.com or relay-c.com are rejected + }, }, ); - -exclusiveSub.on("event", (event) => { - console.log("Event from relay-a.com:", event.content); - // This will ONLY fire for events from relay-a.com - // Events from relay-b.com or relay-c.com are rejected -}); diff --git a/docs/TODOS.md b/docs/TODOS.md index 69956972b..7e48e06e8 100644 --- a/docs/TODOS.md +++ b/docs/TODOS.md @@ -30,7 +30,7 @@ Some notes: - [ ] Try to find (or remove) API reference link - [ ] Remove warnings (homepage and main intro) about Docs WIP - [ ] Remove โœ… โŒ โ›” icons -- [ ] Check revert commit https://github.com/nostr-dev-kit/ndk/commit/4a7b086bd1f67f22a6c29adcf4d6c51b4dd7077c +- [x] Check revert commit https://github.com/nostr-dev-kit/ndk/commit/4a7b086bd1f67f22a6c29adcf4d6c51b4dd7077c ### Changes made that need double check From 6ec9a52100d9555a84341ffb2d2826997834d1fd Mon Sep 17 00:00:00 2001 From: digitalbase Date: Tue, 9 Dec 2025 19:03:06 +0100 Subject: [PATCH 132/139] Fix doc build commands (revert) --- docs/.gitignore | 3 ++- package.json | 6 +++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/docs/.gitignore b/docs/.gitignore index 20197e813..764bc3756 100644 --- a/docs/.gitignore +++ b/docs/.gitignore @@ -1,3 +1,4 @@ .vitepress/cache .vitepress/dist -public/* \ No newline at end of file +public/* +/snippets/pnpm-lock.yaml diff --git a/package.json b/package.json index fe9519960..d898ce6da 100755 --- a/package.json +++ b/package.json @@ -33,9 +33,9 @@ "cs:pub": "bun cs:check && changeset publish", "cs:ver": "changeset version && bunx syncpack fix-mismatches", "dev": "turbo dev --no-cache --continue", - "docs:build": "bash ./prepare-docs.sh && vitepress build docs", - "docs:dev": "bash ./prepare-docs.sh && npx vitepress dev docs", - "docs:preview": "vitepress preview", + "docs:build": "cd docs && vitepress build", + "docs:dev": "cd docs && npx vitepress dev", + "docs:preview": "cd docs && vitepress preview", "format": "biome format --write .", "lint": "biome check .", "release": "bun cs:check && turbo build --filter=docs^... && changeset publish", From 097a46fb0952cd780dc1e156690fee4d2578fdc5 Mon Sep 17 00:00:00 2001 From: digitalbase Date: Tue, 9 Dec 2025 20:46:50 +0100 Subject: [PATCH 133/139] Add tsconfig to allow top level awaits --- core/docs/snippets/tsconfig.json | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 core/docs/snippets/tsconfig.json diff --git a/core/docs/snippets/tsconfig.json b/core/docs/snippets/tsconfig.json new file mode 100644 index 000000000..d052ac246 --- /dev/null +++ b/core/docs/snippets/tsconfig.json @@ -0,0 +1,27 @@ +{ + "compilerOptions": { + "esModuleInterop": true, + "skipLibCheck": true, + "target": "es2022", + "allowJs": true, + "resolveJsonModule": true, + "moduleDetection": "force", + "isolatedModules": true, + "verbatimModuleSyntax": true, + "strict": true, + "noUncheckedIndexedAccess": true, + "noImplicitOverride": true, + "module": "preserve", + "noEmit": true, + "lib": [ + "es2022" + ] + }, + "include": [ + "**/*.ts", + "**/*.tsx" + ], + "exclude": [ + "node_modules" + ] +} \ No newline at end of file From f903be24e2d552f3e02af5d1ddcfdb69a668255f Mon Sep 17 00:00:00 2001 From: digitalbase Date: Tue, 9 Dec 2025 20:48:40 +0100 Subject: [PATCH 134/139] Improve sessions docs --- sessions/docs/multi-account.md | 3 ++- sessions/docs/quick-start.md | 9 +++------ sessions/docs/storage-options.md | 2 +- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/sessions/docs/multi-account.md b/sessions/docs/multi-account.md index 90f21a9c5..a22f6afdc 100644 --- a/sessions/docs/multi-account.md +++ b/sessions/docs/multi-account.md @@ -4,7 +4,8 @@ The library supports multiple accounts (called sessions) through [different sign ## Adding a Session -Each time you use `sessions.login` NDK will create a new session if that signer isn't already an active session. +Each time you use `sessions.login` NDK will create a new session if [that signer](/core/docs/fundamentals/signers) isn't +already an active session. <<< @/sessions/docs/snippets/adding_sessions.ts diff --git a/sessions/docs/quick-start.md b/sessions/docs/quick-start.md index 6e75bce42..4d9e5466e 100644 --- a/sessions/docs/quick-start.md +++ b/sessions/docs/quick-start.md @@ -28,11 +28,7 @@ bun add @nostr-dev-kit/sessions ### 1. Initialize NDK -First, create and [initialise your NDK instance](/core/docs/getting-started/usage.html): - -<<< @/core/docs/snippets/connect_explicit.ts - -Additional information about relay connections in the [connection section](/core/docs/fundamentals/connecting.html). +First, create and [initialise your NDK instance](/core/docs/getting-started/usage#instantiate-ndk). ### 2. Create Session Manager @@ -50,7 +46,8 @@ Restore any previously saved sessions: ### 4. Login (and auto-fetch) -Login with a signer. To automatically fetch user data, configure `fetches` in the constructor: +Login with [a signer](/core/docs/fundamentals/signers). To automatically fetch user data, configure `fetches` in the +constructor: <<< @/sessions/docs/snippets/session_fetch_user_data.ts diff --git a/sessions/docs/storage-options.md b/sessions/docs/storage-options.md index 2ae9d1c50..84688ab00 100644 --- a/sessions/docs/storage-options.md +++ b/sessions/docs/storage-options.md @@ -28,7 +28,7 @@ const sessions = new NDKSessionManager(ndk, { ## Memory Storage -Temporary, mostly used for testing or short lived application logic. +Temporary, mostly used for testing or short-lived application logic. ```typescript import { MemoryStorage } from '@nostr-dev-kit/sessions'; From 64ea5a347f71ee5c88a73b083801999f58c046ac Mon Sep 17 00:00:00 2001 From: digitalbase Date: Tue, 9 Dec 2025 21:09:42 +0100 Subject: [PATCH 135/139] Move guardrail examples --- .../{advanced => snippets/examples}/ai-guardrails-examples.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename core/docs/{advanced => snippets/examples}/ai-guardrails-examples.ts (97%) diff --git a/core/docs/advanced/ai-guardrails-examples.ts b/core/docs/snippets/examples/ai-guardrails-examples.ts similarity index 97% rename from core/docs/advanced/ai-guardrails-examples.ts rename to core/docs/snippets/examples/ai-guardrails-examples.ts index 77d161a19..7c9b6a6da 100644 --- a/core/docs/advanced/ai-guardrails-examples.ts +++ b/core/docs/snippets/examples/ai-guardrails-examples.ts @@ -5,8 +5,8 @@ * when using NDK, especially useful for LLM-generated code. */ -import NDK, { GuardrailCheckId, NDKEvent } from "@nostr-dev-kit/ndk"; -import { nip19 } from "nostr-tools"; +import NDK, {NDKEvent} from "@nostr-dev-kit/ndk"; +import {nip19} from "nostr-tools"; // Example 1: Enable all guardrails (recommended for development) const ndk = new NDK({ From faedbb7dfc16a422ed8f79e18acfd6bd3520596f Mon Sep 17 00:00:00 2001 From: digitalbase Date: Tue, 9 Dec 2025 21:09:59 +0100 Subject: [PATCH 136/139] Move nip19 examples to snippets directory --- core/docs/fundamentals/events.md | 2 +- .../{create_event.ts => event_create.ts} | 0 core/docs/snippets/event_encode.ts | 17 ++ core/docs/snippets/nip-19-conversion.ts | 24 +++ core/docs/snippets/nip-19-decoding.ts | 12 ++ core/docs/snippets/nip-19-encoding.ts | 29 +++ core/docs/snippets/nip-19-profile-event.ts | 18 ++ core/docs/snippets/nip-19-validate.ts | 19 ++ core/docs/snippets/private-signer.ts | 7 + core/docs/snippets/user-fetching.ts | 21 ++ core/docs/tutorial/nip19.md | 179 +++--------------- docs/snippets/events.md | 2 +- 12 files changed, 172 insertions(+), 158 deletions(-) rename core/docs/snippets/{create_event.ts => event_create.ts} (100%) create mode 100644 core/docs/snippets/event_encode.ts create mode 100644 core/docs/snippets/nip-19-conversion.ts create mode 100644 core/docs/snippets/nip-19-decoding.ts create mode 100644 core/docs/snippets/nip-19-encoding.ts create mode 100644 core/docs/snippets/nip-19-profile-event.ts create mode 100644 core/docs/snippets/nip-19-validate.ts create mode 100644 core/docs/snippets/private-signer.ts create mode 100644 core/docs/snippets/user-fetching.ts diff --git a/core/docs/fundamentals/events.md b/core/docs/fundamentals/events.md index 358eadc2e..780a7c537 100644 --- a/core/docs/fundamentals/events.md +++ b/core/docs/fundamentals/events.md @@ -11,7 +11,7 @@ can also create your own. This is the simplest example of creating a text note [ `kind:1`](https://github.com/nostr-protocol/nips/tree/master?tab=readme-ov-file#event-kinds) event. -<<< @/core/docs/snippets/create_event.ts +<<< @/core/docs/snippets/event_create.ts No need to fill in event's `id`, `tags`, `pubkey`, `created_at`, NDK will do that (if not set). diff --git a/core/docs/snippets/create_event.ts b/core/docs/snippets/event_create.ts similarity index 100% rename from core/docs/snippets/create_event.ts rename to core/docs/snippets/event_create.ts diff --git a/core/docs/snippets/event_encode.ts b/core/docs/snippets/event_encode.ts new file mode 100644 index 000000000..4965d6285 --- /dev/null +++ b/core/docs/snippets/event_encode.ts @@ -0,0 +1,17 @@ +import NDK, { NDKEvent } from "@nostr-dev-kit/ndk"; + +const ndk = new NDK(); + +const event = new NDKEvent(ndk); +event.kind = 1; +event.content = "Hello Nostr!"; +await event.sign(); + +// Automatically chooses the right format: +// - naddr for parameterized replaceable events +// - nevent for events with relay information +// - note for simple note references +const encoded = event.encode(); + +// Control relay hints +const encodedWith5Relays = event.encode(5); // Include up to 5 relay hints diff --git a/core/docs/snippets/nip-19-conversion.ts b/core/docs/snippets/nip-19-conversion.ts new file mode 100644 index 000000000..be2168e69 --- /dev/null +++ b/core/docs/snippets/nip-19-conversion.ts @@ -0,0 +1,24 @@ +import { nip19 } from "@nostr-dev-kit/ndk"; + +// Convert hex pubkey to npub +function hexToNpub(hexPubkey: string): string { + return nip19.npubEncode(hexPubkey); +} + +// Extract pubkey from any NIP-19 identifier +function extractPubkey(nip19String: string): string | undefined { + const decoded = nip19.decode(nip19String); + + switch (decoded.type) { + case "npub": + return decoded.data; + case "nprofile": + return decoded.data.pubkey; + case "naddr": + return decoded.data.pubkey; + case "nevent": + return decoded.data.author; + default: + return undefined; + } +} diff --git a/core/docs/snippets/nip-19-decoding.ts b/core/docs/snippets/nip-19-decoding.ts new file mode 100644 index 000000000..794eef0f8 --- /dev/null +++ b/core/docs/snippets/nip-19-decoding.ts @@ -0,0 +1,12 @@ +import { nip19 } from "@nostr-dev-kit/ndk"; + +// Decoding +const decoded = nip19.decode("npub1..."); +console.log(decoded.type); // "npub" +console.log(decoded.data); // hex pubkey + +// Type-specific decoding +if (decoded.type === "nprofile") { + console.log(decoded.data.pubkey); + console.log(decoded.data.relays); +} diff --git a/core/docs/snippets/nip-19-encoding.ts b/core/docs/snippets/nip-19-encoding.ts new file mode 100644 index 000000000..0247e8321 --- /dev/null +++ b/core/docs/snippets/nip-19-encoding.ts @@ -0,0 +1,29 @@ +import { nip19 } from "@nostr-dev-kit/ndk"; + +const pubkey = "a6f6b6a535ba73f593579e919690b7c29df3ba0d764790326c1d3b68d0bfde2e"; +const privateKey = "df63bea84840e5fd07961af1c76286aa628264ad5f151546007530551fb8068f"; +const eventId = "aef62d07b65b80974f0bd8d6b8fc85e4ffcdf34c86de40e5c92aa5654b7a91d4"; + +// Encoding +const npub = nip19.npubEncode(pubkey); +const nsec = nip19.nsecEncode(privateKey); +const note = nip19.noteEncode(eventId); + +// Encoding with metadata +const nprofile = nip19.nprofileEncode({ + pubkey: "hexPubkey", + relays: ["wss://relay1.example.com", "wss://relay2.example.com"], +}); + +const nevent = nip19.neventEncode({ + id: eventId, + relays: ["wss://relay.example.com"], + author: pubkey, +}); + +const naddr = nip19.naddrEncode({ + kind: 30023, + pubkey: pubkey, + identifier: "article-slug", + relays: ["wss://relay.example.com"], +}); diff --git a/core/docs/snippets/nip-19-profile-event.ts b/core/docs/snippets/nip-19-profile-event.ts new file mode 100644 index 000000000..f3af93390 --- /dev/null +++ b/core/docs/snippets/nip-19-profile-event.ts @@ -0,0 +1,18 @@ +import NDK, { nip19 } from "@nostr-dev-kit/ndk"; + +const ndk = new NDK(); + +const eventId = "aef62d07b65b80974f0bd8d6b8fc85e4ffcdf34c86de40e5c92aa5654b7a91d4"; +const pubkey = "a6f6b6a535ba73f593579e919690b7c29df3ba0d764790326c1d3b68d0bfde2e"; + +// Create shareable event reference with relay hints +const event = await ndk.fetchEvent(eventId); + +if (event) { + const shareableLink = event.encode(3); // Include up to 3 relay hints + + const nprofile = nip19.nprofileEncode({ + pubkey: pubkey, + relays: ["wss://relay.damus.io", "wss://nos.lol"], + }); +} diff --git a/core/docs/snippets/nip-19-validate.ts b/core/docs/snippets/nip-19-validate.ts new file mode 100644 index 000000000..e68cc5dcc --- /dev/null +++ b/core/docs/snippets/nip-19-validate.ts @@ -0,0 +1,19 @@ +import { nip19 } from "@nostr-dev-kit/ndk"; + +function isValidNip19(str: string): boolean { + try { + nip19.decode(str); + return true; + } catch { + return false; + } +} + +function isNpub(str: string): boolean { + try { + const decoded = nip19.decode(str); + return decoded.type === "npub"; + } catch { + return false; + } +} diff --git a/core/docs/snippets/private-signer.ts b/core/docs/snippets/private-signer.ts new file mode 100644 index 000000000..1d68d1e2c --- /dev/null +++ b/core/docs/snippets/private-signer.ts @@ -0,0 +1,7 @@ +import { NDKPrivateKeySigner } from "@nostr-dev-kit/ndk"; + +// From nsec +const signer = new NDKPrivateKeySigner("nsec1..."); + +// From hex private key +const signer2 = new NDKPrivateKeySigner("hexPrivateKey"); diff --git a/core/docs/snippets/user-fetching.ts b/core/docs/snippets/user-fetching.ts new file mode 100644 index 000000000..ab3722e7c --- /dev/null +++ b/core/docs/snippets/user-fetching.ts @@ -0,0 +1,21 @@ +import NDK from "@nostr-dev-kit/ndk"; + +const ndk = new NDK(); + +// From npub +const user1 = await ndk.fetchUser("npub1n0sturny6w9zn2wwexju3m6asu7zh7jnv2jt2kx6tlmfhs7thq0qnflahe"); + +// From nprofile (includes relay hints) +const user2 = await ndk.fetchUser( + "nprofile1qqsrhuxx8l9ex335q7he0f09aej04zpazpl0ne2cgukyawd24mayt8gpp4mhxue69uhhytnc9e3k7mgpz4mhxue69uhkg6nzv9ejuumpv34kytnrdaksjlyr9p", +); + +// From hex pubkey +const user3 = await ndk.fetchUser("3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d"); + +// From NIP-05 identifier +const user4 = await ndk.fetchUser("pablo@test.com"); +const user5 = await ndk.fetchUser("test.com"); // Uses _@test.com + +// Note: fetchUser is async and returns a Promise +// For NIP-05 lookups, it may return undefined if the address cannot be resolved diff --git a/core/docs/tutorial/nip19.md b/core/docs/tutorial/nip19.md index c6b19092b..b3869fc60 100644 --- a/core/docs/tutorial/nip19.md +++ b/core/docs/tutorial/nip19.md @@ -3,179 +3,46 @@ NDK provides comprehensive support for NIP-19 identifiers (npub, nprofile, nevent, etc.), both for encoding/decoding data and for working with NDK entities. -## Direct NIP-19 Utilities - -NDK re-exports all NIP-19 utilities from nostr-tools for lightweight data conversion without needing to instantiate NDK -objects: - -```typescript -import { nip19 } from '@nostr-dev-kit/ndk'; - -// Encoding -const npub = nip19.npubEncode(pubkey); -const nsec = nip19.nsecEncode(privateKey); -const note = nip19.noteEncode(eventId); - -// Encoding with metadata -const nprofile = nip19.nprofileEncode({ - pubkey: "hexPubkey", - relays: ["wss://relay1.example.com", "wss://relay2.example.com"] -}); - -const nevent = nip19.neventEncode({ - id: eventId, - relays: ["wss://relay.example.com"], - author: authorPubkey -}); - -const naddr = nip19.naddrEncode({ - kind: 30023, - pubkey: authorPubkey, - identifier: "article-slug", - relays: ["wss://relay.example.com"] -}); - -// Decoding -const decoded = nip19.decode("npub1..."); -console.log(decoded.type); // "npub" -console.log(decoded.data); // hex pubkey - -// Type-specific decoding -if (decoded.type === 'nprofile') { - console.log(decoded.data.pubkey); - console.log(decoded.data.relays); -} -``` - -## Creating NDK Users from NIP-19 +## NIP-19 Encode/Decode -The `ndk.fetchUser()` method accepts NIP-19 encoded strings directly, automatically detecting and decoding the format: - -```typescript -import NDK from '@nostr-dev-kit/ndk'; +NDK re-exports all NIP-19 utilities from nostr-tools for lightweight data conversion without needing to encode Nostr +entities: -const ndk = new NDK({ /* ... */ }); +<<< @/core/docs/snippets/nip-19-encoding.ts -// From npub -const user1 = await ndk.fetchUser("npub1n0sturny6w9zn2wwexju3m6asu7zh7jnv2jt2kx6tlmfhs7thq0qnflahe"); +Or for decoding -// From nprofile (includes relay hints) -const user2 = await ndk.fetchUser("nprofile1qqsrhuxx8l9ex335q7he0f09aej04zpazpl0ne2cgukyawd24mayt8gpp4mhxue69uhhytnc9e3k7mgpz4mhxue69uhkg6nzv9ejuumpv34kytnrdaksjlyr9p"); +<<< @/core/docs/snippets/nip-19-decoding.ts -// From hex pubkey -const user3 = await ndk.fetchUser("3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d"); +## User Fetching -// From NIP-05 identifier -const user4 = await ndk.fetchUser("pablo@test.com"); -const user5 = await ndk.fetchUser("test.com"); // Uses _@test.com +The `ndk.fetchUser()` method accepts NIP-19 encoded strings directly, automatically detecting and decoding the format: -// Note: fetchUser is async and returns a Promise -// For NIP-05 lookups, it may return undefined if the address cannot be resolved -``` +<<< @/core/docs/snippets/user-fetching.ts ## Encoding NDK Events NDK events have a built-in `encode()` method that automatically determines the appropriate NIP-19 format: -```typescript -const event = new NDKEvent(ndk); -event.kind = 1; -event.content = "Hello Nostr!"; -await event.sign(); - -// Automatically chooses the right format: -// - naddr for parameterized replaceable events -// - nevent for events with relay information -// - note for simple note references -const encoded = event.encode(); - -// Control relay hints -const encodedWith5Relays = event.encode(5); // Include up to 5 relay hints -``` +<<< @/core/docs/snippets/event_encode.ts ## Working with Private Keys The `NDKPrivateKeySigner` can be instantiated with an nsec: -```typescript -import { NDKPrivateKeySigner } from '@nostr-dev-kit/ndk'; - -// From nsec -const signer = new NDKPrivateKeySigner("nsec1..."); - -// From hex private key -const signer2 = new NDKPrivateKeySigner("hexPrivateKey"); -``` - -## Common Use Cases - -### Converting between formats - -```typescript -import { nip19 } from '@nostr-dev-kit/ndk'; - -// Convert hex pubkey to npub -function hexToNpub(hexPubkey: string): string { - return nip19.npubEncode(hexPubkey); -} - -// Extract pubkey from any NIP-19 identifier -function extractPubkey(nip19String: string): string | undefined { - const decoded = nip19.decode(nip19String); - - switch (decoded.type) { - case 'npub': - return decoded.data; - case 'nprofile': - return decoded.data.pubkey; - case 'naddr': - return decoded.data.pubkey; - case 'nevent': - return decoded.data.author; - default: - return undefined; - } -} -``` - -### Sharing content with relay hints - -```typescript -// Create shareable event reference with relay hints -const event = await ndk.fetchEvent({ id: eventId }); -const shareableLink = event.encode(3); // Include up to 3 relay hints - -// Create user profile reference with relays -const user = ndk.getUser({ pubkey: userPubkey }); -const nprofile = nip19.nprofileEncode({ - pubkey: user.pubkey, - relays: ["wss://relay.damus.io", "wss://nos.lol"] -}); -``` - -### Validating NIP-19 strings - -```typescript -import { nip19 } from '@nostr-dev-kit/ndk'; - -function isValidNip19(str: string): boolean { - try { - nip19.decode(str); - return true; - } catch { - return false; - } -} - -function isNpub(str: string): boolean { - try { - const decoded = nip19.decode(str); - return decoded.type === 'npub'; - } catch { - return false; - } -} -``` +<<< @/core/docs/snippets/private-signer.ts + +## Converting between formats + +<<< @/core/docs/snippets/nip-19-conversion.ts + +## Sharing content with relay hints + +<<< @/core/docs/snippets/nip-19-profile-event.ts + +## Validating NIP-19 strings + +<<< @/core/docs/snippets/nip-19-validate.ts ## Best Practices diff --git a/docs/snippets/events.md b/docs/snippets/events.md index 151f336bf..99d2a8266 100644 --- a/docs/snippets/events.md +++ b/docs/snippets/events.md @@ -1,6 +1,6 @@ ### Creating an event -<<< @/core/docs/snippets/create_event.ts +<<< @/core/docs/snippets/event_create.ts ### Tagging users From e7079f97b45d9fba39e90adc9c0648c7e2a9f495 Mon Sep 17 00:00:00 2001 From: digitalbase Date: Tue, 9 Dec 2025 21:28:02 +0100 Subject: [PATCH 137/139] Replace nip19 by moving essential to helpers and snippets --- core/docs/fundamentals/helpers.md | 39 +++++++++++++++++--- core/docs/fundamentals/signers.md | 6 ++- core/docs/getting-started/usage.md | 2 +- core/docs/snippets/managing-users.ts | 21 ----------- core/docs/tutorial/nip19.md | 55 ---------------------------- docs/snippets.md | 4 ++ docs/snippets/helpers.md | 11 ++++++ 7 files changed, 55 insertions(+), 83 deletions(-) delete mode 100644 core/docs/snippets/managing-users.ts delete mode 100644 core/docs/tutorial/nip19.md create mode 100644 docs/snippets/helpers.md diff --git a/core/docs/fundamentals/helpers.md b/core/docs/fundamentals/helpers.md index c442338a5..c49ddeb17 100644 --- a/core/docs/fundamentals/helpers.md +++ b/core/docs/fundamentals/helpers.md @@ -6,23 +6,52 @@ Quite a number of those utilities come from the [nostr-tools library](https://github.com/nbd-wtf/nostr-tools) which is included as part of NDK -### NIP-19 Identifiers +## NIP-19 Identifiers NDK re-exports [NIP-19](https://github.com/nostr-protocol/nips/blob/master/19.md) from the [nostr-tools library](https://github.com/nbd-wtf/nostr-tools): -<<< @/core/docs/snippets/nip-19-identifiers.ts +### Encoding -### NIP-49 Encryption +<<< @/core/docs/snippets/nip-19-encoding.ts + +### Decoding + +<<< @/core/docs/snippets/nip-19-decoding.ts + +## NIP-49 Encryption NDK re-exports [NIP-49](https://github.com/nostr-protocol/nips/blob/master/49.md) from the [nostr-tools library](https://github.com/nbd-wtf/nostr-tools): <<< @/core/docs/snippets/nip-49-encrypting.ts -### Managing Users +## Fetching Users + +The `ndk.fetchUser()` method accepts [NIP-19](https://github.com/nostr-protocol/nips/blob/master/19.md) encoded strings +directly, automatically detecting and decoding the format: + +<<< @/core/docs/snippets/user-fetching.ts + +## Encoding Events + +NDK events have a built-in `encode()` method that automatically determines the appropriate NIP-19 format: + +<<< @/core/docs/snippets/event_encode.ts + +## Generate Keys + +By using `NDKPrivateKeySigner` you can create keysets for signing and verifying messages. + +This snippet demonstrates how to generate a new key pair and obtain all its various formats (private key, public key, +nsec, npub). + +<<< @/core/docs/snippets/key_create.ts + +More about key generaton in [the signer documentation](/core/docs/fundamentals/signers). + + -<<< @/core/docs/snippets/managing-users.ts diff --git a/core/docs/fundamentals/signers.md b/core/docs/fundamentals/signers.md index dd42d312c..3f8852b34 100644 --- a/core/docs/fundamentals/signers.md +++ b/core/docs/fundamentals/signers.md @@ -37,7 +37,11 @@ applications that manage keys locally. > using [a browser extension](/core/docs/fundamentals/signers.html#browser-extensions) > or [a remote signer](/core/docs/fundamentals/signers.html#remote-signer). -The private key signer takes the private key in the `nsec` format. +The `NDKPrivateKeySigner` can be instantiated with an nsec or hex private key: + +<<< @/core/docs/snippets/private-signer.ts + +After that the signer can be used to sign events: <<< @/core/docs/snippets/sign_event_nsec.ts diff --git a/core/docs/getting-started/usage.md b/core/docs/getting-started/usage.md index 022d09b4a..8fd8a7226 100644 --- a/core/docs/getting-started/usage.md +++ b/core/docs/getting-started/usage.md @@ -42,7 +42,7 @@ NDK provides flexible ways to fetch user objects, including support for [NIP-19](https://github.com/nostr-protocol/nips/blob/master/19.md) encoded identifiers and [NIP-05](https://github.com/nostr-protocol/nips/blob/master/05.md) addresses: -<<< @/core/docs/snippets/managing-users.ts +<<< @/core/docs/snippets/user-fetching.ts Note: `fetchUser` is async and returns a Promise. For [NIP-05](https://github.com/nostr-protocol/nips/blob/master/05.md) lookups, it may return `undefined` if the address cannot be resolved. diff --git a/core/docs/snippets/managing-users.ts b/core/docs/snippets/managing-users.ts deleted file mode 100644 index b1abfd17e..000000000 --- a/core/docs/snippets/managing-users.ts +++ /dev/null @@ -1,21 +0,0 @@ -import NDK from "@nostr-dev-kit/ndk"; - -const ndk = new NDK(); - -// From hex pubkey -const user1 = await ndk.fetchUser("3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d"); - -// From npub (NIP-19 encoded) -const user2 = await ndk.fetchUser("npub1n0sturny6w9zn2wwexju3m6asu7zh7jnv2jt2kx6tlmfhs7thq0qnflahe"); - -// From nprofile (includes relay hints) -const user3 = await ndk.fetchUser( - "nprofile1qqsrhuxx8l9ex335q7he0f09aej04zpazpl0ne2cgukyawd24mayt8gpp4mhxue69uhhytnc9e3k7mgpz4mhxue69uhkg6nzv9ejuumpv34kytnrdaksjlyr9p", -); - -// From NIP-05 identifier -const user4 = await ndk.fetchUser("pablo@test.com"); -const user5 = await ndk.fetchUser("test.com"); // Uses _@test.com - -// The method automatically detects the format -const user6 = await ndk.fetchUser("deadbeef..."); // Assumes hex pubkey diff --git a/core/docs/tutorial/nip19.md b/core/docs/tutorial/nip19.md deleted file mode 100644 index b3869fc60..000000000 --- a/core/docs/tutorial/nip19.md +++ /dev/null @@ -1,55 +0,0 @@ -# Working with NIP-19 Identifiers - -NDK provides comprehensive support for NIP-19 identifiers (npub, nprofile, nevent, etc.), both for encoding/decoding -data and for working with NDK entities. - -## NIP-19 Encode/Decode - -NDK re-exports all NIP-19 utilities from nostr-tools for lightweight data conversion without needing to encode Nostr -entities: - -<<< @/core/docs/snippets/nip-19-encoding.ts - -Or for decoding - -<<< @/core/docs/snippets/nip-19-decoding.ts - -## User Fetching - -The `ndk.fetchUser()` method accepts NIP-19 encoded strings directly, automatically detecting and decoding the format: - -<<< @/core/docs/snippets/user-fetching.ts - -## Encoding NDK Events - -NDK events have a built-in `encode()` method that automatically determines the appropriate NIP-19 format: - -<<< @/core/docs/snippets/event_encode.ts - -## Working with Private Keys - -The `NDKPrivateKeySigner` can be instantiated with an nsec: - -<<< @/core/docs/snippets/private-signer.ts - -## Converting between formats - -<<< @/core/docs/snippets/nip-19-conversion.ts - -## Sharing content with relay hints - -<<< @/core/docs/snippets/nip-19-profile-event.ts - -## Validating NIP-19 strings - -<<< @/core/docs/snippets/nip-19-validate.ts - -## Best Practices - -1. **Use NIP-19 for user-facing displays**: Always show npub/nprofile to users instead of hex pubkeys -2. **Include relay hints for better discovery**: When sharing events or profiles, include 2-3 relay hints -3. **Handle decoding errors**: Always wrap `nip19.decode()` in try-catch blocks -4. **Use the right tool**: - - Use `nip19` utilities for pure data conversion - - Use `ndk.getUser()` when you need an NDK User object - - Use `event.encode()` for encoding existing NDK events \ No newline at end of file diff --git a/docs/snippets.md b/docs/snippets.md index f24d07271..76413ea09 100644 --- a/docs/snippets.md +++ b/docs/snippets.md @@ -25,6 +25,10 @@ Snippets are grouped by category. Some of them are listed in more than one categ +## Helpers + + + ## Not migrated yet diff --git a/docs/snippets/helpers.md b/docs/snippets/helpers.md new file mode 100644 index 000000000..17a06e099 --- /dev/null +++ b/docs/snippets/helpers.md @@ -0,0 +1,11 @@ +### Converting between formats + +<<< @/core/docs/snippets/nip-19-conversion.ts + +### Sharing content with relay hints + +<<< @/core/docs/snippets/nip-19-profile-event.ts + +### Validating NIP-19 strings + +<<< @/core/docs/snippets/nip-19-validate.ts \ No newline at end of file From cf554e4fcf60f9231b0654f54e03a310a015e0dd Mon Sep 17 00:00:00 2001 From: digitalbase Date: Tue, 9 Dec 2025 21:29:15 +0100 Subject: [PATCH 138/139] Add code snippets to helpers --- core/docs/fundamentals/helpers.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/core/docs/fundamentals/helpers.md b/core/docs/fundamentals/helpers.md index c49ddeb17..afe0114b1 100644 --- a/core/docs/fundamentals/helpers.md +++ b/core/docs/fundamentals/helpers.md @@ -50,6 +50,9 @@ nsec, npub). More about key generaton in [the signer documentation](/core/docs/fundamentals/signers). +## Code Snippets + +More snippets and examples can be found in the [snippets directory](/docs/snippets.md#helpers) From d75ece090abef26a143b8debffa2e1cbbd5f48e1 Mon Sep 17 00:00:00 2001 From: digitalbase Date: Tue, 9 Dec 2025 21:33:57 +0100 Subject: [PATCH 139/139] Remove duplicate helper documentation --- core/docs/fundamentals/events.md | 6 ++++++ core/docs/fundamentals/helpers.md | 13 ++----------- docs/.vitepress/config.ts | 2 +- 3 files changed, 9 insertions(+), 12 deletions(-) diff --git a/core/docs/fundamentals/events.md b/core/docs/fundamentals/events.md index 780a7c537..e9e41e2eb 100644 --- a/core/docs/fundamentals/events.md +++ b/core/docs/fundamentals/events.md @@ -40,6 +40,12 @@ NDK uses the default signer `ndk.signer` to sign events. Read more about signers in [the signer documentation](/core/docs/fundamentals/signers.md) +## Encoding Events + +NDK events have a built-in `encode()` method that automatically determines the appropriate NIP-19 format: + +<<< @/core/docs/snippets/event_encode.ts + ## Code Snippets More snippets and examples can be found in the [snippets directory](/docs/snippets.md#events). diff --git a/core/docs/fundamentals/helpers.md b/core/docs/fundamentals/helpers.md index afe0114b1..3118a0be1 100644 --- a/core/docs/fundamentals/helpers.md +++ b/core/docs/fundamentals/helpers.md @@ -1,10 +1,7 @@ # Helpers -NDK comes with a ton of useful helpers and utilities to do things faster and easier. - -Quite a number of those utilities come from the -[nostr-tools library](https://github.com/nbd-wtf/nostr-tools) which is included as part of -NDK +NDK comes with a ton of additional helpers and utilities to do things faster. A number of those utilities come from the +[nostr-tools library](https://github.com/nbd-wtf/nostr-tools) which is included as part of NDK ## NIP-19 Identifiers @@ -33,12 +30,6 @@ directly, automatically detecting and decoding the format: <<< @/core/docs/snippets/user-fetching.ts -## Encoding Events - -NDK events have a built-in `encode()` method that automatically determines the appropriate NIP-19 format: - -<<< @/core/docs/snippets/event_encode.ts - ## Generate Keys By using `NDKPrivateKeySigner` you can create keysets for signing and verifying messages. diff --git a/docs/.vitepress/config.ts b/docs/.vitepress/config.ts index d368fd5ad..abf32c144 100644 --- a/docs/.vitepress/config.ts +++ b/docs/.vitepress/config.ts @@ -55,7 +55,7 @@ export default defineConfig({ { text: "Subscribing โœ…", link: "/core/docs/fundamentals/subscribing" }, { text: "Signers โœ…", link: "/core/docs/fundamentals/signers" }, { text: "Zaps โœ…", link: "/core/docs/fundamentals/zapping" }, - { text: "Helpers โŒ", link: "/core/docs/fundamentals/helpers" }, + { text: "Helpers โœ…", link: "/core/docs/fundamentals/helpers" }, ], }, {