quoting
naddr1qv…zaw4I’m excited to announce the release of Applesauce v1.0.0! There are a few breaking changes and a lot of improvements and new features across all packages. Each package has been updated to 1.0.0, marking a stable API for developers to build upon.
Applesauce core changes
There was a change in the
applesauce-core
package in theQueryStore
.The
Query
interface has been converted to a method instead of an object withkey
andrun
fields.A bunch of new helper methods and queries were added, checkout the changelog for a full list.
Applesauce Relay
There is a new
applesauce-relay
package that provides a simple RxJS based api for connecting to relays and publishing events.Documentation: applesauce-relay
Features:
- A simple API for subscribing or publishing to a single relay or a group of relays
- No
connect
orclose
methods, connections are managed automatically by rxjs- NIP-11
auth_required
support- Support for NIP-42 authentication
- Prebuilt or custom re-connection back-off
- Keep-alive timeout (default 30s)
- Client-side Negentropy sync support
Example Usage: Single relay
import { Relay } from "applesauce-relay"; // Connect to a relay const relay = new Relay("wss://relay.example.com"); // Create a REQ and subscribe to it relay .req({ kinds: [1], limit: 10, }) .subscribe((response) => { if (response === "EOSE") { console.log("End of stored events"); } else { console.log("Received event:", response); } });
Example Usage: Relay pool
import { Relay, RelayPool } from "applesauce-relay"; // Create a pool with a custom relay const pool = new RelayPool(); // Create a REQ and subscribe to it pool .req(["wss://relay.damus.io", "wss://relay.snort.social"], { kinds: [1], limit: 10, }) .subscribe((response) => { if (response === "EOSE") { console.log("End of stored events on all relays"); } else { console.log("Received event:", response); } });
Applesauce actions
Another new package is the
applesauce-actions
package. This package provides a set of async operations for common Nostr actions.Actions are run against the events in the
EventStore
and use theEventFactory
to create new events to publish.Documentation: applesauce-actions
Example Usage:
import { ActionHub } from "applesauce-actions"; // An EventStore and EventFactory are required to use the ActionHub import { eventStore } from "./stores.ts"; import { eventFactory } from "./factories.ts"; // Custom publish logic const publish = async (event: NostrEvent) => { console.log("Publishing", event); await app.relayPool.publish(event, app.defaultRelays); }; // The `publish` method is optional for the async `run` method to work const hub = new ActionHub(eventStore, eventFactory, publish);
Once an
ActionsHub
is created, you can use therun
orexec
methods to execute actions:import { FollowUser, MuteUser } from "applesauce-actions/actions"; // Follow fiatjaf await hub.run( FollowUser, "3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d", ); // Or use the `exec` method with a custom publish method await hub .exec( MuteUser, "3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d", ) .forEach((event) => { // NOTE: Don't publish this event because we never want to mute fiatjaf // pool.publish(['wss://pyramid.fiatjaf.com/'], event) });
There are a log more actions including some for working with NIP-51 lists (private and public), you can find them in the reference
Applesauce loaders
The
applesauce-loaders
package has been updated to support any relay connection libraries and not justrx-nostr
.Before:
import { ReplaceableLoader } from "applesauce-loaders"; import { createRxNostr } from "rx-nostr"; // Create a new rx-nostr instance const rxNostr = createRxNostr(); // Create a new replaceable loader const replaceableLoader = new ReplaceableLoader(rxNostr);
After:
import { Observable } from "rxjs"; import { ReplaceableLoader, NostrRequest } from "applesauce-loaders"; import { SimplePool } from "nostr-tools"; // Create a new nostr-tools pool const pool = new SimplePool(); // Create a method that subscribes using nostr-tools and returns an observable function nostrRequest: NostrRequest = (relays, filters, id) => { return new Observable((subscriber) => { const sub = pool.subscribe(relays, filters, { onevent: (event) => { subscriber.next(event); }, onclose: () => subscriber.complete(), oneose: () => subscriber.complete(), }); return () => sub.close(); }); }; // Create a new replaceable loader const replaceableLoader = new ReplaceableLoader(nostrRequest);
Of course you can still use rx-nostr if you want:
import { createRxNostr } from "rx-nostr"; // Create a new rx-nostr instance const rxNostr = createRxNostr(); // Create a method that subscribes using rx-nostr and returns an observable function nostrRequest( relays: string[], filters: Filter[], id?: string, ): Observable<NostrEvent> { // Create a new oneshot request so it will complete when EOSE is received const req = createRxOneshotReq({ filters, rxReqId: id }); return rxNostr .use(req, { on: { relays } }) .pipe(map((packet) => packet.event)); } // Create a new replaceable loader const replaceableLoader = new ReplaceableLoader(nostrRequest);
There where a few more changes, check out the changelog
Applesauce wallet
Its far from complete, but there is a new
applesauce-wallet
package that provides a actions and queries for working with NIP-60 wallets.Documentation: applesauce-wallet
Example Usage:
import { CreateWallet, UnlockWallet } from "applesauce-wallet/actions"; // Create a new NIP-60 wallet await hub.run(CreateWallet, ["wss://mint.example.com"], privateKey); // Unlock wallet and associated tokens/history await hub.run(UnlockWallet, { tokens: true, history: true });
hzrd149 on Nostr: Been working on this for the past few months. I think its stable enough for other ...
Been working on this for the past few months. I think its stable enough for other developers to start building on and next release of noStrudel is going to be using it as the core