Why Nostr? What is Njump?
2025-04-29 17:43:10

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

I’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 the QueryStore.

The Query interface has been converted to a method instead of an object with key and run 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 or close 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 the EventFactory 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 the run or exec 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 just rx-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 });
Author Public Key
npub1ye5ptcxfyyxl5vjvdjar2ua3f0hynkjzpx552mu5snj3qmx5pzjscpknpr