diff --git a/docs/app/commands.md b/docs/app/commands.md index 446beef..69f28b7 100644 --- a/docs/app/commands.md +++ b/docs/app/commands.md @@ -52,8 +52,8 @@ pin(tag: string[]): Promise ```typescript type SendWrappedOptions = Omit & { - template: EventTemplate - pubkeys: string[] + event: EventTemplate + recipients: string[] } sendWrapped(options: SendWrappedOptions): Promise diff --git a/docs/app/index.md b/docs/app/index.md index 6fb99b6..a5878f8 100644 --- a/docs/app/index.md +++ b/docs/app/index.md @@ -52,7 +52,7 @@ const thunk = publishThunk({ thunk.controller.abort() // Some commands are included -const thunk = follow('97c70a44366a6535c145b333f973ea86dfdc2d7a99da618c40c64705ad98e322') +const thunk = follow(['p', '97c70a44366a6535c145b333f973ea86dfdc2d7a99da618c40c64705ad98e322']) // Load events as a promise const events = await load({ diff --git a/docs/app/making-requests.md b/docs/app/making-requests.md index b7a4be2..647d9ce 100644 --- a/docs/app/making-requests.md +++ b/docs/app/making-requests.md @@ -61,14 +61,14 @@ Several common collections are built-in and ready for use: profiles → profilesByPubkey → deriveProfile → loadProfile // Lists -follows → followsByPubkey → deriveFollows → loadFollows -mutes → mutesByPubkey → deriveMutes → loadMutes -pins → pinsByPubkey → derivePins → loadPins +followLists → followListsByPubkey → deriveFollowList → loadFollowList +muteLists → muteListsByPubkey → deriveMuteList → loadMuteList +pinLists → pinListsByPubkey → derivePinList → loadPinList // Relays relays → relaysByUrl → deriveRelay → loadRelay -relayLists → relayListsByPubkey → deriveRelayLists → loadRelayLists -messagingRelayLists → messagingRelayListsByPubkey → deriveMessagingRelayLists → loadMessagingRelayLists +relayLists → relayListsByPubkey → deriveRelayList → loadRelayList +messagingRelayLists → messagingRelayListsByPubkey → deriveMessagingRelayList → loadMessagingRelayList // Identity handles → handlesByNip05 → deriveHandle → loadHandle @@ -97,24 +97,20 @@ const name = deriveProfileDisplay(pubkey) Several modules provide user-specific derived stores that automatically load data for the currently signed-in user: ```typescript -import { userProfile, userFollows, userMutes, userPins } from '@welshman/app' +import { userProfile, userFollowList, userMuteList, userPinList } from '@welshman/app' userProfile.subscribe(profile => { // Current user's profile data }) -userFollows.subscribe(follows => { +userFollowList.subscribe(follows => { // Current user's follow list }) ``` ### Repository Integration -All events from subscriptions are automatically: - -- Saved to the repository -- Tracked to their source relay -- Checked against deletion status +Events from subscriptions are automatically tracked to their source relay and saved to the repository, unless they are DVM-kind or ephemeral events (which are discarded). WRAP (kind 1059) events are handled separately and only processed when `shouldUnwrap` is set to `true`. The repository serves as an intelligent cache layer, making subsequent queries for the same data faster. diff --git a/docs/app/publishing-events.md b/docs/app/publishing-events.md index 7f443b7..7304459 100644 --- a/docs/app/publishing-events.md +++ b/docs/app/publishing-events.md @@ -63,10 +63,11 @@ Several thunk factories are provided for common or more complicated scenarios li - `unfollow(value: string)` - `follow(tag: string[])` - `unmute(value: string)` -- `mute(tag: string[])` +- `mutePublicly(tag: string[])` +- `mutePrivately(tag: string[])` - `unpin(value: string)` - `pin(tag: string[])` -- `sendWrapped({template, pubkeys, ...options}: SendWrappedOptions)` +- `sendWrapped({event, recipients, ...options}: SendWrappedOptions)` - `manageRelay(url: string, request: ManagementRequest)` - `createRoom(url: string, room: RoomMeta)` - `deleteRoom(url: string, room: RoomMeta)` diff --git a/docs/app/session.md b/docs/app/session.md index 6b481cb..c57cfef 100644 --- a/docs/app/session.md +++ b/docs/app/session.md @@ -174,7 +174,7 @@ getNip55().then(signerApps => { A fun feature of nostr is that you can log in as other people, and see what nostr is like from their perspective (minus encrypted data or course). ```typescript -import {loginWithPubkey} from "@welshman/signer" +import {loginWithPubkey} from "@welshman/app" // Log in as hodlbod loginWithPubkey("97c70a44366a6535c145b333f973ea86dfdc2d7a99da618c40c64705ad98e322") diff --git a/docs/app/user.md b/docs/app/user.md index 6c3b372..66a9e04 100644 --- a/docs/app/user.md +++ b/docs/app/user.md @@ -2,25 +2,6 @@ The User Data module provides utilities for loading and managing user-specific data like profiles, follows, mutes, pins, and relay selections. It includes both reactive stores and manual loading functions. -## Types - -### UserDataLoader -```typescript -type UserDataLoader = (pubkey: string, relays?: string[], force?: boolean) => unknown -``` - -Function type for loading user data with optional relay specification and force refresh. - -### MakeUserDataOptions -```typescript -type MakeUserDataOptions = { - mapStore: Readable> - loadItem: UserDataLoader -} -``` - -Configuration for creating user data stores. - ## User Data Stores These reactive stores automatically load and cache user data: @@ -30,56 +11,68 @@ These reactive stores automatically load and cache user data: export const userProfile: Store // User follows list -export const userFollows: Store +export const userFollowList: Store // User mutes list -export const userMutes: Store +export const userMuteList: Store // User pins list -export const userPins: Store +export const userPinList: Store // User relay selections -export const userRelayLists: Store +export const userRelayList: Store // User messaging relay selections -export const userMessagingRelayLists: Store +export const userMessagingRelayList: Store // User blossom servers -export const userBlossomServers: Store +export const userBlossomServerList: Store ``` ## Manual Loading Functions -These functions allow manual loading of user data with optional relay specification and force refresh: +These functions load user data for the currently signed-in user with optional relay hints: ```typescript // Load user profile -function loadUserProfile(relays?: string[], force?: boolean): Promise +function loadUserProfile(relays?: string[]): Promise // Load user follows -function loadUserFollows(relays?: string[], force?: boolean): Promise +function loadUserFollowList(relays?: string[]): Promise // Load user mutes -function loadUserMutes(relays?: string[], force?: boolean): Promise +function loadUserMuteList(relays?: string[]): Promise // Load user pins -function loadUserPins(relays?: string[], force?: boolean): Promise +function loadUserPinList(relays?: string[]): Promise // Load user relay selections -function loadUserRelayLists(relays?: string[], force?: boolean): Promise +function loadUserRelayList(relays?: string[]): Promise // Load user messaging relay selections -function loadUserMessagingRelayLists(relays?: string[], force?: boolean): Promise +function loadUserMessagingRelayList(relays?: string[]): Promise // Load user blossom servers -function loadUserBlossomServers(relays?: string[], force?: boolean): Promise +function loadUserBlossomServerList(relays?: string[]): Promise +``` + +Force-reload variants bypass the cache and always fetch fresh data: + +```typescript +function forceLoadUserProfile(relays?: string[]): Promise +function forceLoadUserFollowList(relays?: string[]): Promise +function forceLoadUserMuteList(relays?: string[]): Promise +function forceLoadUserPinList(relays?: string[]): Promise +function forceLoadUserRelayList(relays?: string[]): Promise +function forceLoadUserMessagingRelayList(relays?: string[]): Promise +function forceLoadUserBlossomServerList(relays?: string[]): Promise ``` ## Usage Examples ### Using Reactive Stores ```typescript -import { userProfile, userFollows } from '@welshman/app' +import { userProfile, userFollowList } from '@welshman/app' // Subscribe to user profile changes userProfile.subscribe(profile => { @@ -89,18 +82,18 @@ userProfile.subscribe(profile => { }) // Get current follows list -const follows = userFollows.get() +const follows = userFollowList.get() ``` ### Manual Loading ```typescript -import { loadUserMutes, loadUserRelayLists } from '@welshman/app' +import { loadUserMuteList, forceLoadUserRelayList } from '@welshman/app' // Load user mutes from specific relays -await loadUserMutes(['wss://relay1.com', 'wss://relay2.com']) +await loadUserMuteList(['wss://relay1.com', 'wss://relay2.com']) // Force refresh user relay selections -await loadUserRelayLists([], true) +await forceLoadUserRelayList([]) // Load from default relays await loadUserProfile() diff --git a/docs/app/wot.md b/docs/app/wot.md index 28a7bb3..06d6269 100644 --- a/docs/app/wot.md +++ b/docs/app/wot.md @@ -53,7 +53,7 @@ followersByPubkey: Readable>> mutersByPubkey: Readable>> // The full WOT graph with scores (pubkey → score) -wotGraph: Readable> +wotGraph: Writable> // The maximum WOT score in the graph maxWot: Readable diff --git a/docs/content/parser.md b/docs/content/parser.md index 27d282d..d3ef2a2 100644 --- a/docs/content/parser.md +++ b/docs/content/parser.md @@ -11,6 +11,7 @@ Defines all supported content types: - `Cashu` - Cashu token strings - `Code` - Code blocks and inline code - `Ellipsis` - Truncation indicators +- `Email` - Email addresses - `Emoji` - Custom emoji references - `Event` - Event references (note/nevent) - `Invoice` - Lightning invoices @@ -57,13 +58,12 @@ reduceLinks(content: Parsed[]) => Parsed[] ## Type Guards Utility functions to check parsed element types: -- `isAddress(parsed)`, `isCashu(parsed)`, `isCode(parsed)`, etc. +- `isAddress(parsed)`, `isCashu(parsed)`, `isCode(parsed)`, `isEmail(parsed)`, etc. - `isImage(parsed)` - special check for image links ## Utilities - `urlIsMedia(url)` - Checks if URL points to media file -- `fromNostrURI(s)` - Removes nostr: protocol prefix ## Example Usage diff --git a/docs/content/render.md b/docs/content/render.md index 0892fe1..ba9450b 100644 --- a/docs/content/render.md +++ b/docs/content/render.md @@ -25,8 +25,8 @@ type RenderOptions = { ## Built-in Renderers -- `makeTextRenderer` - renders an array of `Parsed` elements as text -- `makeHtmlRenderer` - renders an array of `Parsed` elements as HTML +- `makeTextRenderer` - creates a `Renderer` configured for plain-text output +- `makeHtmlRenderer` - creates a `Renderer` configured for HTML output ## Main Functions @@ -54,7 +54,7 @@ const html = renderAsHtml(parsed, { }).toString() // Result: -// Check out this cool #nostr client!
-// Visit npub1jlrs53p... for more info
+// Check out this cool #nostr client! +// Visit npub1jlrs53p... for more info // github.com/coracle-social/welshman ``` diff --git a/docs/feeds/controller.md b/docs/feeds/controller.md index f80cbb3..68e1856 100644 --- a/docs/feeds/controller.md +++ b/docs/feeds/controller.md @@ -32,10 +32,10 @@ export class FeedController { load(limit: number): Promise // Get listener function (memoized) - getListener(): Promise<() => Promise> + getListener(): Promise<() => () => void> - // Listen for new events in the feed - listen(): Promise + // Listen for new events in the feed; returns an unsubscribe function + listen(): () => Promise } ``` diff --git a/docs/feeds/index.md b/docs/feeds/index.md index d266327..24cbeb2 100644 --- a/docs/feeds/index.md +++ b/docs/feeds/index.md @@ -18,31 +18,29 @@ Read the spec on [the NIPs repository](https://github.com/nostr-protocol/nips/bl ```javascript // Define a feed using set operations -const feed = intersectionFeed( - unionFeed( - dvmFeed({ +const feed = makeIntersectionFeed( + makeUnionFeed( + makeDVMFeed({ kind: 5300, pubkey: '19b78ccfa7c5e31e6bacbb3f2a1703f64b62017702e584440bf29a7e16263e8c', }), - listFeed("10003:19ba654f26afd4930fd3d51baf4e26f1413b7aeec7190cd6c0cdf4d2f14cec6b:"), - ) - wotFeed({min: 0.1}), - scopeFeed("global"), + makeListFeed("10003:19ba654f26afd4930fd3d51baf4e26f1413b7aeec7190cd6c0cdf4d2f14cec6b:"), + ), + makeWOTFeed({min: 0.1}), + makeGlobalFeed(), ) // Create a controller, providing required context via FeedOptions const controller = new FeedController({ feed, - request, - requestDVM, getPubkeysForScope, getPubkeysForWOTRange, onEvent: event => console.log("Event", event), onExhausted: () => console.log("Exhausted"), }) -// Load notes using the feed -const events = await controller.load(10) +// Load notes using the feed — events are delivered via the onEvent callback above +await controller.load(10) ``` ## Installation diff --git a/docs/lib/emitter.md b/docs/lib/emitter.md index 8709b74..a44411e 100644 --- a/docs/lib/emitter.md +++ b/docs/lib/emitter.md @@ -32,6 +32,6 @@ emitter.on('*', (eventType, ...args) => { // Emit an event - triggers both listeners emitter.emit('message', 'Hello world'); // Output: -// Event: message ['Hello world'] // Message: Hello world +// Event: message ['Hello world'] ``` \ No newline at end of file diff --git a/docs/lib/lru.md b/docs/lib/lru.md index 75ee2f3..11941dc 100644 --- a/docs/lib/lru.md +++ b/docs/lib/lru.md @@ -40,9 +40,9 @@ cache.set('c', 3); console.log(cache.get('a')); // 1 -// Adding 'd' will evict 'b' (least recently used) +// Adding 'd' will evict 'a' (its stale key entry is at the front of the tracking queue) cache.set('d', 4); -console.log(cache.has('b')); // false -console.log(cache.has('a')); // true (recently accessed) +console.log(cache.has('b')); // true +console.log(cache.has('a')); // false ``` diff --git a/docs/lib/task-queue.md b/docs/lib/task-queue.md index 958fb69..61a4742 100644 --- a/docs/lib/task-queue.md +++ b/docs/lib/task-queue.md @@ -8,6 +8,7 @@ The `TaskQueue` class provides a simple queue processing system with batched ope // Task queue options export type TaskQueueOptions = { batchSize: number; + batchDelay: number; processItem: (item: Item) => unknown; }; @@ -17,7 +18,7 @@ export declare class TaskQueue { push(item: Item): void; remove(item: Item): void; subscribe(subscriber: (item: Item) => void): () => void; - process(): Promise; + process(): void; stop(): void; start(): void; clear(): void; @@ -32,6 +33,7 @@ import { TaskQueue } from '@welshman/lib'; // Create a task queue that processes 3 items at a time const queue = new TaskQueue({ batchSize: 3, + batchDelay: 0, processItem: async (message: string) => { console.log('Processing:', message); // Simulate async work diff --git a/docs/lib/tools.md b/docs/lib/tools.md index f4c746b..780404c 100644 --- a/docs/lib/tools.md +++ b/docs/lib/tools.md @@ -35,17 +35,17 @@ type PartialUser = MakeOptional; ## Basic functional programming utilities ```typescript -// Null or undefined -export type Nil = null | undefined; +// Undefined or T +export type Maybe = T | undefined; -// Check whether something is null or undefined -export declare const isNil: (x: T, ...args: unknown[]) => boolean; +// Check whether something is not undefined +export declare const isDefined: (x: T, ...args: unknown[]) => boolean; -// Check whether something is not null or undefined -export declare const isNotNil: (x: T, ...args: unknown[]) => x is (T & null) | (T & {}) | (T & undefined); +// Check whether something is undefined +export declare const isUndefined: (x: T, ...args: unknown[]) => boolean; -// Assert that a nullable type is not null or undefined -export declare const assertNotNil: (x: T, ...args: unknown[]) => NonNullable; +// Assert that a value is not undefined +export declare const assertDefined: (x: T, ...args: unknown[]) => NonNullable; // Function that does nothing and returns undefined export declare const noop: (...args: unknown[]) => undefined; @@ -215,7 +215,7 @@ export declare const drop: (n: number, xs: Iterable) => T[]; // Returns first n elements of array export declare const take: (n: number, xs: Iterable) => T[]; -// Concatenates multiple arrays, filtering out null/undefined +// Concatenates multiple arrays, skipping undefined array arguments export declare const concat: (...xs: T[][]) => T[]; // Prepends element to array @@ -417,7 +417,7 @@ export declare const yieldThread: () => any; // Poll options for condition checking type PollOptions = { signal: AbortSignal; - condition: () => boolean; + condition: () => unknown; interval?: number; }; @@ -468,6 +468,7 @@ export declare const setJson: (k: string, v: any) => void; // Options for fetch requests type FetchOpts = { method?: string; + signal?: AbortSignal; headers?: Record; body?: string | FormData; }; @@ -497,8 +498,8 @@ export declare const ellipsize: (s: string, l: number, suffix?: string) => strin // Displays a list of items with oxford commas and a chosen conjunction export declare const displayList: (xs: T[], conj?: string, n?: number) => string; -// Generates a hash string from input string -export declare const hash: (s: string) => string; +// Generates a hash number from input string +export declare const hash: (s: string) => number; ``` ## Curried utilities for working with collections @@ -564,8 +565,8 @@ export declare const hexToBech32: (prefix: string, hex: string) => `${Lowercase< // Converts bech32 string to hex format export declare const bech32ToHex: (b32: string) => string; -// Converts an array buffer to hex format -export declare const bytesToHex: (buffer: ArrayBuffer) => string; +// Converts an array buffer or Uint8Array to hex format +export declare const bytesToHex: (buffer: ArrayBuffer | Uint8Array) => string; // Converts a hex string to a Uint8Array buffer export declare const hexToBytes: (hex: string) => Uint8Array; diff --git a/docs/net/context.md b/docs/net/context.md index e2f408b..de53142 100644 --- a/docs/net/context.md +++ b/docs/net/context.md @@ -27,7 +27,7 @@ The `netContext` global provides sensible defaults: import {netContext} from '@welshman/net' // Override event validation -netContext.isEventValid: (event, url) => { +netContext.isEventValid = (event, url) => { return event.kind < 30000 && verifyEvent(event) } diff --git a/docs/net/message.md b/docs/net/message.md index c8fbca4..0ce0f78 100644 --- a/docs/net/message.md +++ b/docs/net/message.md @@ -5,6 +5,7 @@ Type definitions and utilities for Nostr protocol messages. ## Relay Message Types **Enums:** +- `RelayMessageType.Notice` - Human-readable notice from relay - `RelayMessageType.Auth` - Authentication challenge - `RelayMessageType.Closed` - Subscription closed - `RelayMessageType.Eose` - End of stored events @@ -15,11 +16,11 @@ Type definitions and utilities for Nostr protocol messages. **Type Definitions:** - `RelayMessage` - Base relay message type -- `RelayAuth`, `RelayClosed`, `RelayEose`, `RelayEvent`, `RelayNegErr`, `RelayNegMsg`, `RelayOk` - Specific message types -- `RelayAuthPayload`, `RelayClosedPayload`, etc. - Payload types for each message +- `RelayNotice`, `RelayAuth`, `RelayClosed`, `RelayEose`, `RelayEvent`, `RelayNegErr`, `RelayNegMsg`, `RelayOk` - Specific message types +- `RelayNoticePayload`, `RelayAuthPayload`, `RelayClosedPayload`, etc. - Payload types for each message **Type Guards:** -- `isRelayAuth()`, `isRelayClosed()`, `isRelayEose()`, `isRelayEvent()`, `isRelayNegErr()`, `isRelayNegMsg()`, `isRelayOk()` +- `isRelayNotice()`, `isRelayAuth()`, `isRelayClosed()`, `isRelayEose()`, `isRelayEvent()`, `isRelayNegErr()`, `isRelayNegMsg()`, `isRelayOk()` ## Client Message Types diff --git a/docs/net/policy.md b/docs/net/policy.md index e68ea85..3732e0f 100644 --- a/docs/net/policy.md +++ b/docs/net/policy.md @@ -13,6 +13,10 @@ The contract for socket policies. Takes a Socket object and returns a cleanup fu ## Built-in Policies +### `socketPolicyPing` + +Sends a PING message every 30 seconds when the socket is open and has been idle (no send or receive activity in the last 30 seconds). Keeps connections alive through NAT/firewall idle timeouts. + ### `socketPolicyAuthBuffer` Buffers messages during authentication flow and replays them after successful auth. diff --git a/docs/net/pool.md b/docs/net/pool.md index a9b7abf..52165c6 100644 --- a/docs/net/pool.md +++ b/docs/net/pool.md @@ -12,16 +12,11 @@ A connection pool that creates and manages Socket instances for different relay - `static get()` - Returns the singleton pool instance - `has(url)` - Checks if a socket exists for the given URL - `get(url)` - Gets or creates a socket for the given URL +- `makeSocket(url)` - Creates a Socket for the given URL, using `PoolOptions.makeSocket` if provided, otherwise applies `defaultSocketPolicies` - `subscribe(callback)` - Subscribes to new socket creation events - `remove(url)` - Removes and cleans up a socket - `clear()` - Removes all sockets from the pool -## Functions - -### makeSocket(url, policies) - -Creates a new Socket instance with the given URL and applies default policies. - ## Example ```typescript diff --git a/docs/net/request.md b/docs/net/request.md index 69d9cdb..75baf71 100644 --- a/docs/net/request.md +++ b/docs/net/request.md @@ -16,7 +16,9 @@ Requests events from a single relay using the given filters. Returns a promise t - `context?` - Adapter context - `autoClose?` - Auto-close subscription after EOSE - Validation functions: `isEventValid`, `isEventDeleted` -- Callback functions: `onEvent`, `onDeleted`, `onInvalid`, `onFiltered`, `onDuplicate`, `onDisconnect`, `onEose`, `onClose` +- Callback functions: `onEvent`, `onDeleted`, `onInvalid`, `onFiltered`, `onDuplicate`, `onDisconnect`, `onEose`, `onClosed`, `onClose` + - `onClosed?: (message: string, url: string) => void` - Called when the relay sends a CLOSED message, receiving the close reason and relay URL + - `onClose?: () => void` - Called when the subscription is fully closed ### request(options) @@ -38,9 +40,9 @@ Creates a batched loader function that delays and combines requests for efficien - `threshold?` - Relay completion threshold - Validation functions and context options -### load(filters, relays, options) +### load(options) -Pre-configured loader with 200ms delay, 3s timeout, and 0.5 threshold. +Pre-configured loader with 200ms delay, 3s timeout, and 0.5 threshold. Takes a `LoadOptions` object containing `relays`, `filters`, and optional callback fields (`onEvent`, `onDisconnect`, `onEose`, `onClose`) and an optional `signal`. ## Example diff --git a/docs/router/index.md b/docs/router/index.md index cb884e2..b0ed8fd 100644 --- a/docs/router/index.md +++ b/docs/router/index.md @@ -31,7 +31,7 @@ const router = Router.get() // Get relays for reading events from specific pubkeys const readRelays = router.FromPubkeys(['pubkey1', 'pubkey2']).getUrls() -// Get relays for publishing an event (author's outbox + mentions' messaginges) +// Get relays for publishing (author's outbox + mentions' inbox/read relays) const publishRelays = router.PublishEvent(event).getUrls() // Try hard to find a quoted note with maximal fallbacks @@ -74,8 +74,8 @@ The main class for relay selection. Configure it once with your relay discovery **Scenario Methods:** - `FromRelays(relays)` - Use specific relays -- `ForUser()` / `FromUser()` / `UserMessaging()` - User's read/write/messaging relays -- `ForPubkey(pubkey)` / `FromPubkey(pubkey)` / `PubkeyMessaging(pubkey)` - Pubkey's relays +- `ForUser()` / `FromUser()` / `MessagesForUser()` - User's read/write/messaging relays +- `ForPubkey(pubkey)` / `FromPubkey(pubkey)` / `MessagesForPubkey(pubkey)` - Pubkey's relays - `ForPubkeys(pubkeys)` / `FromPubkeys(pubkeys)` - Multiple pubkeys' relays - `Event(event)` - Relays for an event's author - `PublishEvent(event)` - Relays for publishing (author + mentions) @@ -106,8 +106,8 @@ Functions that determine how many fallback relays to add: `getFilterSelections(filters)` automatically chooses appropriate relays based on filter content: - Search filters → search relays - Wrap events → user's messaging -- Profile/relay kinds → indexer relays +- Profile/relay/follow/messaging-relay kinds → indexer relays - Author filters → authors' relays -- Everything else → user's relays (low weight) +- All filters → user's read relays (low-weight baseline, always fires) Returns `RelaysAndFilters[]` with optimized relay-filter combinations. diff --git a/docs/signer/isigner.md b/docs/signer/isigner.md index 74899a3..885d814 100644 --- a/docs/signer/isigner.md +++ b/docs/signer/isigner.md @@ -15,6 +15,7 @@ interface ISigner { // Core signing functionality sign: SignWithOptions getPubkey: () => Promise + cleanup?: () => Promise // Encryption capabilities nip04: { diff --git a/docs/signer/nip-01.md b/docs/signer/nip-01.md index 00238ee..5d7a051 100644 --- a/docs/signer/nip-01.md +++ b/docs/signer/nip-01.md @@ -29,8 +29,7 @@ The NIP-01 implementation extends the base interface with two static utility met ### Usage Example ```typescript -import { ISigner } from './interfaces' -import { Nip01Signer } from './signers/nip01' +import { ISigner, Nip01Signer } from '@welshman/signer' // Using the standard interface const signer: ISigner = new Nip01Signer(mySecret) diff --git a/docs/signer/nip-07.md b/docs/signer/nip-07.md index b0b5f22..677afbf 100644 --- a/docs/signer/nip-07.md +++ b/docs/signer/nip-07.md @@ -30,7 +30,7 @@ const signer = new Nip07Signer() ```typescript import { Nip07Signer, getNip07 } from '@welshman/signer' -import { createEvent, NOTE } from '@welshman/util' +import { makeEvent, NOTE } from '@welshman/util' async function example() { // Check for NIP-07 provider @@ -47,7 +47,7 @@ async function example() { console.log('Public key:', pubkey) // Create and sign an event (will prompt user) - const event = createEvent(NOTE, { + const event = makeEvent(NOTE, { content: "Hello via browser extension!", tags: [["t", "test"]] }) diff --git a/docs/signer/nip-46.md b/docs/signer/nip-46.md index bcccbcb..32c05bb 100644 --- a/docs/signer/nip-46.md +++ b/docs/signer/nip-46.md @@ -16,13 +16,13 @@ import { Nip46Broker, Nip46Signer } from '@welshman/signer' -import { createEvent, NOTE } from '@welshman/util' +import { makeEvent, NOTE } from '@welshman/util' async function connectToRemoteSigner() { // Initial setup const clientSecret = makeSecret() const relays = ['wss://relay.example.com'] - const broker = Nip46Broker.get({ relays, clientSecret }) + const broker = new Nip46Broker({ relays, clientSecret }) const signer = new Nip46Signer(broker) // Generate connection URL @@ -36,9 +36,10 @@ async function connectToRemoteSigner() { try { // Wait for connection + const abortController = new AbortController() const response = await broker.waitForNostrconnect( ncUrl, - new AbortController() + abortController.signal ) // Store signer info for later @@ -46,7 +47,7 @@ async function connectToRemoteSigner() { localStorage.setItem('bunkerUrl', bunkerUrl) // Use the signer - const event = createEvent(NOTE, { + const event = makeEvent(NOTE, { content: "Signed with remote signer!", tags: [["t", "test"]] }) @@ -72,7 +73,7 @@ async function reconnect() { relays } = Nip46Broker.parseBunkerUrl(bunkerUrl) - const broker = Nip46Broker.get({ + const broker = new Nip46Broker({ relays, clientSecret: makeSecret(), signerPubkey, @@ -88,8 +89,8 @@ async function reconnect() { ### Constructor and Factory ```typescript -// Recommended: use the singleton factory -const broker = Nip46Broker.get({ +// Direct instantiation +new Nip46Broker({ relays: string[], clientSecret: string, connectSecret?: string, @@ -97,8 +98,8 @@ const broker = Nip46Broker.get({ algorithm?: "nip04" | "nip44" }) -// Direct instantiation (not recommended) -new Nip46Broker(params) +// Static factory: create a broker from a bunker:// URL +Nip46Broker.fromBunkerUrl(url: string): Nip46Broker ``` ### Connection Methods @@ -110,7 +111,7 @@ broker.makeNostrconnectUrl(metadata: Record): Promise // Wait for connection approval broker.waitForNostrconnect( url: string, - abort?: AbortController + signal: AbortSignal ): Promise // Get bunker URL for later reconnection diff --git a/docs/signer/nip-55.md b/docs/signer/nip-55.md index 7b05062..cdf1377 100644 --- a/docs/signer/nip-55.md +++ b/docs/signer/nip-55.md @@ -51,7 +51,7 @@ Creates a new signer instance that will communicate with the specified native ap ```typescript import { Nip55Signer, getNip55 } from '@welshman/signer' -import { createEvent, NOTE } from '@welshman/util' +import { makeEvent, NOTE } from '@welshman/util' async function example() { try { @@ -69,7 +69,7 @@ async function example() { console.log('Public key:', pubkey) // Sign an event - const event = createEvent(NOTE, { + const event = makeEvent(NOTE, { content: "Hello from native app!", tags: [["t", "test"]] }) diff --git a/docs/signer/nip-59.md b/docs/signer/nip-59.md index d157454..2057068 100644 --- a/docs/signer/nip-59.md +++ b/docs/signer/nip-59.md @@ -14,23 +14,20 @@ The `Nip59` class provides utilities for implementing the Gift Wrap protocol (NI ```typescript import { Nip59 } from '@welshman/signer' -import { createEvent, DIRECT_MESSAGE } from '@welshman/util' +import { makeEvent, DIRECT_MESSAGE } from '@welshman/util' // Create a NIP-59 instance from any signer const nip59 = Nip59.fromSigner(mySigner) -// Wrap an event -const rumor = await nip59.wrap( +// Wrap an event — returns the gift-wrap SignedEvent to publish +const wrappedEvent = await nip59.wrap( recipientPubkey, - createEvent(DIRECT_MESSAGE, { + makeEvent(DIRECT_MESSAGE, { content: "Secret message", tags: [["p", recipientPubkey]] }) ) -// The wrapped event to publish -const wrappedEvent = rumor.wrap - // Unwrap a received event const unwrapped = await nip59.unwrap(receivedWrappedEvent) ``` @@ -51,11 +48,12 @@ export const wrap = async ( template: StampedEvent, tags: string[][] = [] ) => { - const rumor = await getRumor(signer, template) + const author = await signer.getPubkey() + const rumor = await prep(template, author) const seal = await getSeal(signer, pubkey, rumor) const wrap = await getWrap(wrapper, pubkey, seal, tags) - return Object.assign(rumor, {wrap}) + return wrap } ``` @@ -79,20 +77,20 @@ class Nip59 { * @param pubkey Recipient's public key * @param template The event to wrap * @param tags Additional tags for the wrap event (optional) - * @returns Promise Original event and its wrapped version + * @returns Promise The gift-wrap event to publish */ wrap( pubkey: string, template: StampedEvent, tags?: string[][] - ): Promise + ): Promise /** * Unwraps a received wrapped event * @param event The wrapped event to decrypt - * @returns Promise The original unwrapped event + * @returns Promise The original unwrapped event */ - unwrap(event: SignedEvent): Promise + unwrap(event: SignedEvent): Promise /** * Creates a new instance with a specific wrapper signer @@ -109,24 +107,20 @@ class Nip59 { ```typescript import { Nip59, Nip01Signer } from '@welshman/signer' -import { createEvent, DIRECT_MESSAGE } from '@welshman/util' +import { makeEvent, DIRECT_MESSAGE } from '@welshman/util' async function example() { // Create NIP-59 instance const signer = new Nip01Signer(mySecret) const nip59 = Nip59.fromSigner(signer) - // Create and wrap an event - const event = createEvent(DIRECT_MESSAGE, { + // Create and wrap an event — returns the gift-wrap SignedEvent to publish + const event = makeEvent(DIRECT_MESSAGE, { content: "Secret message", tags: [["p", recipientPubkey]] }) - const rumor = await nip59.wrap(recipientPubkey, event) - - // rumor contains: - // - The original event (rumor) - // - The wrapped version to publish (rumor.wrap) + const wrappedEvent = await nip59.wrap(recipientPubkey, event) // Later, unwrap a received event const unwrapped = await nip59.unwrap(receivedEvent) diff --git a/docs/store/index.md b/docs/store/index.md index 58cb534..002349b 100644 --- a/docs/store/index.md +++ b/docs/store/index.md @@ -15,7 +15,8 @@ A utility package providing welshman-specific svelte store functionality and uti ## Quick Example ```typescript -import {Repository, NAMED_PEOPLE, TrustedEvent, PublishedList, readList} from '@welshman/util' +import {Repository} from '@welshman/net' +import {NAMED_PEOPLE, TrustedEvent, PublishedList, readList} from '@welshman/util' import {deriveItemsByKey} from '@welshman/store' const repository = new Repository() diff --git a/docs/store/repository.md b/docs/store/repository.md index 8c573cf..1fe03c8 100644 --- a/docs/store/repository.md +++ b/docs/store/repository.md @@ -13,16 +13,16 @@ deriveEventsById(options: { }): Readable> // Convert events by ID to array -deriveEvents(eventsByIdStore: Readable>): Readable +deriveEvents(options: { repository: Repository, filters: Filter[], includeDeleted?: boolean }): Readable // Sort events ascending by created_at -deriveEventsAsc(eventsStore: Readable): Readable +deriveEventsAsc(eventsByIdStore: Readable>): Readable // Sort events descending by created_at -deriveEventsDesc(eventsStore: Readable): Readable +deriveEventsDesc(eventsByIdStore: Readable>): Readable // Derive single event by ID or address -deriveEvent(repository: Repository, idOrAddress: string): Readable +makeDeriveEvent(options: { repository: Repository, includeDeleted?: boolean, onDerive?: (filters: Filter[], ...args: any[]) => void }): (idOrAddress: string, ...args: any[]) => Readable // Track if event is deleted deriveIsDeleted(repository: Repository, event: TrustedEvent): Readable @@ -52,14 +52,14 @@ makeDeriveItem( // Create cached loader with staleness checking and exponential backoff makeLoadItem( loadItem: (key: string, ...args: any[]) => Promise, - getItem: (key: string) => T | undefined, + getItem: (key: string, ...args: any[]) => T | undefined, options?: {getFetched?, setFetched?, timeout?} ): (key: string, ...args: any[]) => Promise // Create loader that always fetches fresh data makeForceLoadItem( loadItem: (key: string, ...args: any[]) => Promise, - getItem: (key: string) => T | undefined + getItem: (key: string, ...args: any[]) => T | undefined ): (key: string, ...args: any[]) => Promise // Optimized getter that switches to subscription when hot @@ -70,17 +70,13 @@ getter(store: Readable, options?: {threshold?: number}): () => T ```typescript import {Repository} from "@welshman/net" -import {deriveEventsById, deriveEvents, deriveItemsByKey, deriveItems} from "@welshman/store" +import {deriveEvents, deriveItemsByKey, deriveItems} from "@welshman/store" import {readProfile, PROFILE} from "@welshman/util" const repository = new Repository() // Reactive store of text notes -const noteEventsById = deriveEventsById({ - repository, - filters: [{kinds: [1], limit: 100}] -}) -const notes = deriveEvents(noteEventsById) +const notes = deriveEvents({ repository, filters: [{kinds: [1], limit: 100}] }) // Reactive store of profiles indexed by pubkey const profilesByPubkey = deriveItemsByKey({ diff --git a/docs/util/events.md b/docs/util/events.md index a4ffa9e..7c952b8 100644 --- a/docs/util/events.md +++ b/docs/util/events.md @@ -38,15 +38,9 @@ export type SignedEvent = HashedEvent & { sig: string; }; -// Wrapped event (NIP-59) -export type UnwrappedEvent = HashedEvent & { - wrap: SignedEvent; -}; - -// Event that can be either signed or wrapped +// Event that may or may not be signed export type TrustedEvent = HashedEvent & { sig?: string; - wrap?: SignedEvent; }; ``` @@ -72,8 +66,6 @@ export declare const isStampedEvent: (e: StampedEvent) => e is StampedEvent; export declare const isOwnedEvent: (e: OwnedEvent) => e is OwnedEvent; export declare const isHashedEvent: (e: HashedEvent) => e is HashedEvent; export declare const isSignedEvent: (e: TrustedEvent) => e is SignedEvent; -export declare const isUnwrappedEvent: (e: TrustedEvent) => e is UnwrappedEvent; -export declare const isTrustedEvent: (e: TrustedEvent) => e is TrustedEvent; ``` ### Event Utilities @@ -88,7 +80,7 @@ export declare const getIdOrAddress: (e: HashedEvent) => string; export declare const getIdAndAddress: (e: HashedEvent) => string[]; // Event deduplication by id or address -export declare const deduplicateEvents: (e: TrustedEvent) => TrustedEvent[]; +export declare const deduplicateEvents: (events: TrustedEvent[]) => TrustedEvent[]; // Event type checking export declare const isEphemeral: (e: EventTemplate) => boolean; diff --git a/docs/util/handlers.md b/docs/util/handlers.md index 2da4f07..ec0bdfb 100644 --- a/docs/util/handlers.md +++ b/docs/util/handlers.md @@ -79,7 +79,8 @@ const handlers = readHandlers(event) ```typescript // Get unique handler identifier const key = getHandlerKey(handler) -// => "1:30023:note-viewer" (kind:pubkey:identifier) +// => "1:31990:pubkey:identifier" (handler-kind:address) +// where address is the "kind:pubkey:identifier" of the handler event // Display handler name const name = displayHandler(handler, "Unknown Handler") diff --git a/docs/util/nip86.md b/docs/util/nip86.md index eaf26ff..f9ff773 100644 --- a/docs/util/nip86.md +++ b/docs/util/nip86.md @@ -32,13 +32,18 @@ export type ManagementRequest = { method: ManagementMethod params: string[] } + +export type ManagementResponse = { + result?: any + error?: string +} ``` ## Functions ```typescript // Sends a management request to a relay -export declare const sendManagementRequest: (url: string, request: ManagementRequest, authEvent: SignedEvent) => Promise +export declare const sendManagementRequest: (url: string, request: ManagementRequest, authEvent: SignedEvent) => Promise ``` ## Example diff --git a/docs/util/nip98.md b/docs/util/nip98.md index 6aa8947..0d99728 100644 --- a/docs/util/nip98.md +++ b/docs/util/nip98.md @@ -6,7 +6,7 @@ Implementation of NIP-98 HTTP Authentication for authenticating HTTP requests wi ```typescript // Creates an HTTP auth event for authenticating requests -export declare const makeHttpAuth: (url: string, method?: string, body?: string) => Promise +export declare const makeHttpAuth: (url: string, method?: string, body?: string) => Promise // Creates Authorization header from signed HTTP auth event export declare const makeHttpAuthHeader: (event: SignedEvent) => string diff --git a/docs/util/relay.md b/docs/util/relay.md index e119101..fd9476c 100644 --- a/docs/util/relay.md +++ b/docs/util/relay.md @@ -11,6 +11,8 @@ The `Relay` module provides utilities for working with Nostr relays, including U export enum RelayMode { Read = "read", Write = "write", + Search = "search", + Blocked = "blocked", Messaging = "messaging" } @@ -26,7 +28,7 @@ export type RelayProfile = { version?: string; negentropy?: number; description?: string; - supported_nips?: number[]; + supported_nips?: string[]; limitation?: { min_pow_difficulty?: number; payment_required?: boolean; diff --git a/docs/util/zaps.md b/docs/util/zaps.md index 365912e..1b3da00 100644 --- a/docs/util/zaps.md +++ b/docs/util/zaps.md @@ -44,7 +44,7 @@ export declare const hrpToMillisat: (hrpString: string) => bigint; export declare const getInvoiceAmount: (bolt11: string) => number; // Convert lightning address or URL to LNURL -export declare const getLnUrl: (address: string) => string | null; +export declare const getLnUrl: (address: string) => string | undefined; ``` ### Zap Validation