Skip to content

Migrating from applesauce v4 to v5

This guide explains how to migrate an existing applesauce v4 project to v5, with a focus on import changes and the applesauce-common package. It is written for both humans and AI agents (LLMs, code‑mod tools) and favors mechanical “if you see X, replace with Y” style rules.

1. Goals and scope

  • What this covers:
    • Moving helpers, models, blueprints, and operations that were previously imported from applesauce-core (and applesauce-factory) to their new homes.
    • Installing and wiring the applesauce-common package.
    • A step‑by‑step algorithm that an AI agent can safely follow to refactor imports.
  • What this does not cover:
    • Non-import behavior changes (runtime semantics, new features).
    • Breaking changes in external deps (nostr-tools, React, etc.).

If you only remember one thing: most NIP‑specific helpers and models moved out of applesauce-core into applesauce-common.


2. Conceptual changes: applesauce-core vs applesauce-common

In v4, applesauce-core exposed both protocol‑level primitives and a large set of NIP‑specific helpers and models. In v5, these responsibilities are split:

  • applesauce-core (protocol‑level primitives):
    • EventStore, AsyncEventStore
    • Observables (Observable, Subject, BehaviorSubject, ReplaySubject), promise helpers (firstValueFrom, lastValueFrom, simpleTimeout, TimeoutError)
    • Low‑level event helpers (NostrEvent, UnsignedEvent, VerifiedEvent, kinds, encoding/decoding, pointer utilities, filters, cache)
    • Base models for generic patterns (EventModel, TimelineModel, ProfileModel, etc. in the “core” sense)
    • EventFactory shell in applesauce-core/event-factory
  • applesauce-common (NIP‑specific and app‑level functionality):
    • NIP‑specific helpers for:
      • Threads/comments (NIP‑10, NIP‑22)
      • Groups
      • Streams (NIP‑53)
      • Articles (NIP‑23)
      • Calendar events (NIP‑52)
      • Polls (NIP‑88)
      • Zaps, bookmarks, mutes, highlights, handlers, blossom servers, etc.
    • NIP‑specific models:
      • CommentsModel, ThreadModel, WrappedMessagesModel, LegacyMessages*, CalendarEventsModel, EventZapsModel, ReceivedZapsModel, SentZapsModel, various “group” and “stream” models, etc.
    • EventFactory blueprints and domain‑level operations.

You can see this reflected in the package docs:

  • applesauce-core: packages/core/README.md
  • applesauce-common: packages/common/README.md
  • Packages overview: apps/docs/introduction/packages.md

Rationale: applesauce-core stays small and focused on the Nostr protocol itself, while applesauce-common hosts higher‑level, NIP‑specific building blocks you may or may not want to depend on.


3. Package changes and installation

At a high level, v5 projects will usually use:

AreaPackage(s)
Core protocolapplesauce-core
NIP helpersapplesauce-common
Content parsingapplesauce-content
Reactive UIapplesauce-react
Actionsapplesauce-actions
Wallet (NIP‑60)applesauce-wallet
Relaysapplesauce-relay, applesauce-loaders, etc.

3.1. Add applesauce-common

If your v4 project did not already use applesauce-common, install it alongside your existing packages:

sh
npm install applesauce-common applesauce-core
# or
yarn add applesauce-common applesauce-core
# or
pnpm add applesauce-common applesauce-core

3.2. Keep applesauce packages on the same major version

To avoid subtle breakage:

  • Ensure all applesauce-* packages you use are on a consistent major version (e.g. all ^5.x once v5 is published).
  • Do not mix applesauce-core@4 with applesauce-common@5, etc.

4. Import migration rules (applesauce-coreapplesauce-common)

This section is designed for AI agents as well as humans. It focuses on patterns, not a complete symbol list.

4.1. Helpers: move NIP‑specific helpers to applesauce-common/helpers

Rule:

  • Before (v4): Many NIP‑specific helpers were imported from:
    • applesauce-core/helpers
    • applesauce-core/helpers/<module>
  • After (v5): Those helpers now live under:
    • applesauce-common/helpers
    • applesauce-common/helpers/<module>

Representative mappings (derived from current exports and examples):

  • Identity / profile helpers:
    • getDisplayName, getProfilePicture, getProfileContent, getProfilePointersFromList
    • v4: from applesauce-core/helpers
    • v5: from applesauce-common/helpers
  • Social graph / relay helpers:
    • groupPubkeysByRelay, mergeRelaySets, getSeenRelays, selectOptimalRelays
    • v4: applesauce-core/helpers
    • v5: applesauce-common/helpers
  • Threading and comments:
    • getNip10References, interpretThreadTags, getCommentEventPointer, getCommentReplyPointer, getCommentRootPointer
    • v4: applesauce-core/helpers (threading‑related modules)
    • v5: applesauce-common/helpers
  • Groups:
    • encodeGroupPointer, decodeGroupPointer, groupMessageEvents, group list helpers
    • NIP-29 Group Management helpers (new in v5): CREATE_GROUP_KIND, CREATE_INVITE_KIND, DELETE_GROUP_KIND, EDIT_METADATA_KIND, JOIN_REQUEST_KIND, LEAVE_REQUEST_KIND, PUT_USER_KIND, REMOVE_USER_KIND, and related group management functions
    • v4: applesauce-core/helpers
    • v5: applesauce-common/helpers (or applesauce-common/helpers/groups for group-specific helpers)
  • Articles / highlights / media:
    • getArticleImage, getArticlePublished, getArticleSummary, getArticleTitle
    • Highlight helpers (getHighlightText, getHighlightContext, getHighlightSourceEventPointer, getHighlightSourceAddressPointer, etc.)
    • v4: applesauce-core/helpers
    • v5: applesauce-common/helpers
  • Calendar:
    • Calendar kinds and symbols (CALENDAR_EVENT_RSVP_KIND, DATE_BASED_CALENDAR_EVENT_KIND, TIME_BASED_CALENDAR_EVENT_KIND, etc.)
    • Helpers like getCalendarEventStart/End, getCalendarEventTitle, getCalendarAddressPointers
    • v4: applesauce-core/helpers
    • v5: applesauce-common/helpers
  • Polls:
    • POLL_KIND, POLL_RESPONSE_KIND, poll options / votes / type helpers
    • v4: applesauce-core/helpers
    • v5: applesauce-common/helpers
  • Zaps:
    • getZapAmount, getZapEventPointer, getZapSender, getZapRecipient, getZapRequest, isValidZap, etc.
    • v4: applesauce-core/helpers
    • v5: applesauce-common/helpers
  • Gift wrap, bookmarks, mutes, streams, handlers, blossom servers, etc.:
    • Numerous helpers with names like getGiftWrapRumor, getBookmarks, getMutedThings, getStream*, getHandler*, mergeBookmarks, matchMutes, mergeBlossomServers.
    • v4: applesauce-core/helpers
    • v5: applesauce-common/helpers
  • Lists and relay lists:
    • FAVORITE_RELAYS_KIND, getListTags, getRelaysFromList, getAddressPointersFromList, isEventPointerInList, ReadListTags, and other list-related helpers
    • v4: applesauce-core/helpers/lists
    • v5: applesauce-common/helpers/lists
  • External identifiers (NIP-73):
    • parseExternalPointer, isValidExternalPointer, getExternalPointerFromTag, ExternalIdentifiers, ExternalPointer
    • v4: applesauce-core/helpers/external-id
    • v5: applesauce-common/helpers/external-id

Keep these in applesauce-core/helpers (protocol primitives):

  • Encoding/decoding and core types:
    • NostrEvent, UnsignedEvent, VerifiedEvent, kinds, noteEncode, neventEncode, naddrEncode, npubEncode, nsecEncode
  • Pointers and tags:
    • AddressPointer, EventPointer, ProfilePointer, isEventPointer, low‑level decodeAddressPointer, decodeEventPointer, decodeProfilePointer
    • Note: In v5, pointer decoding helpers (decodeAddressPointer, decodeEventPointer, decodeProfilePointer, parseReplaceableAddress) no longer throw errors on invalid input; they return null instead. Update any code that relied on try/catch around these functions to check for null return values.
  • Generic tag / filter / cache helpers:
    • Filter, matchFilter, matchFilters, mergeFilters, processTags
    • getOrComputeCachedValue, getCachedValue, setCachedValue, LRU
  • URL / bytes helpers:
    • bytesToHex, hexToBytes, ensureHttpURL, ensureWebSocketURL, normalizeURL, etc.

If a helper name is obviously NIP‑specific or app‑level, move it to applesauce-common/helpers. If it is a core protocol helper (encoding, pointers, filters, cache, low‑level URL handling), keep it in applesauce-core/helpers.

4.2. Models: move NIP‑specific models to applesauce-common/models

Rule:

  • Before (v4): Most high‑level models (threads, comments, zaps, streams, calendar, bookmarks, mutes, etc.) were exported from:
    • applesauce-core/models
  • After (v5): These models live in:
    • applesauce-common/models

Representative mappings:

  • Threading / comments:
    • ThreadModel, CommentsModel, RepliesModel, WrappedMessagesModel, LegacyMessagesThreads, LegacyMessageReplies, etc.
    • v4: applesauce-core/models
    • v5: applesauce-common/models
  • Zaps:
    • EventZapsModel, ReceivedZapsModel, SentZapsModel
    • v4: applesauce-core/models
    • v5: applesauce-common/models
  • Calendar:
    • CalendarEventsModel, CalendarEventRSVPsModel
    • v4: applesauce-core/models
    • v5: applesauce-common/models
  • Groups and channels:
    • CommentsModel, channel‑related models (e.g. ChannelMessagesModel, ChannelMetadataModel, ChannelHiddenModel, ChannelMutedModel), streams/chat models
    • v4: applesauce-core/models
    • v5: applesauce-common/models
  • Bookmarks, pins, hidden/public variants:
    • UserBookmarkModel, UserHiddenBookmarkModel, UserPublicBookmarkModel, UserPinnedModel, UserBlossomServersModel
    • v4: applesauce-core/models
    • v5: applesauce-common/models

Models that remain “core”:

  • Base models such as EventModel, TimelineModel, ProfileModel, FiltersModel, MailboxesModel, OutboxModel, ReplaceableModel, ContactsModel, HiddenContactsModel, PublicContactsModel are still conceptually part of applesauce-core.
  • However, in v5 these may be re‑exported and/or extended in applesauce-common/models for NIP‑specific use cases.

Relay models moved to applesauce-common:

  • Relay-related models (FavoriteRelaysModel, FavoriteRelaySetsModel, SearchRelaysModel, BlockedRelaysModel) moved from applesauce-core/models to applesauce-common/models:
    • v4: applesauce-core/models (or applesauce-core/models/relays)
    • v5: applesauce-common/models (or applesauce-common/models/relays)

As a migration heuristic:

  • If a *Model name refers to generic protocol concepts (events, timelines, contacts): keep it in applesauce-core/models.
  • If a *Model name refers to relay lists or other NIP-specific concepts: move it to applesauce-common/models.
  • If it refers to a NIP or app concept (comments, polls, streams, bookmarks, mutes, zaps, calendar, wrapped messages): move it to applesauce-common/models.

4.3. Operations and blueprints: move to applesauce-common

In v4, many EventFactory operations and blueprints lived either in applesauce-factory or under applesauce-core exports. In v5:

  • EventFactory itself lives in applesauce-core/event-factory.
  • Domain‑specific operations and blueprints live in applesauce-common:
    • applesauce-common/operations
    • applesauce-common/blueprints

The common operations/blueprints include (non‑exhaustive):

  • Operations:
    • AppData, Calendar, CalendarEvent, CalendarRsvp, Channel, Comment, FileMetadata, Geohash, GiftWrap, Groups, Hashtags, Highlight, LegacyMessage, List, LiveStream, MediaAttachment, Note, PicturePost, Poll, PollResponse, Reaction, Stream, StreamChat, TagOperations, Torrent, WrappedMessage, Zap
  • Blueprints:
    • AppDataBlueprint, CalendarBlueprint, ChannelMessageBlueprint, ChannelMessageReplyBlueprint, CommentBlueprint, DeleteBlueprint, FileMetadataBlueprint, FollowSetBlueprint, GiftWrapBlueprint, GroupMessageBlueprint, HighlightBlueprint, LegacyMessageBlueprint, LegacyMessageReplyBlueprint, LiveChatMessageBlueprint, NoteBlueprint, NoteReplyBlueprint, PicturePostBlueprint, PollBlueprint, PollResponseBlueprint, ProfileBlueprint, ReactionBlueprint, ShareBlueprint, SingleChoicePollResponseBlueprint, StreamChatMessage, TorrentBlueprint, WrappedMessageBlueprint, WrappedMessageReplyBlueprint
    • NIP-29 Group Management blueprints (new in v5): GroupJoinRequestBlueprint, GroupLeaveRequestBlueprint, GroupCreateBlueprint, GroupDeleteBlueprint, GroupEditMetadataBlueprint, GroupPutUserBlueprint, GroupRemoveUserBlueprint

Migration rules:

  • If an operation or blueprint name clearly corresponds to a NIP or domain:
    • v4 imports from applesauce-core or applesauce-factory
    • v5 imports from:
      • applesauce-common/operations (for operations)
      • applesauce-common/blueprints (for blueprints)
  • EventFactory usage:
    • v4:
      • Often import { EventFactory } from "applesauce-factory";
    • v5:
      • import { EventFactory } from "applesauce-core/event-factory";
      • And blueprints/operations from applesauce-common.

5. AI‑friendly refactor algorithm

This section is a mechanical algorithm that an AI agent can follow to refactor imports.

5.1. Search scope

Scan all TypeScript/JavaScript files:

  • Look for imports whose module specifier starts with:
    • "applesauce-core"
    • "applesauce-factory" (v4 only)

5.2. Classify imported symbols

Define two curated lists (these can be hard‑coded by the agent or configured by the user):

  • Always‑core (keep in applesauce-core):
    • EventStore, AsyncEventStore
    • Observable, Subject, BehaviorSubject, ReplaySubject, TimeoutError, firstValueFrom, lastValueFrom, simpleTimeout
    • EventFactory (but move to applesauce-core/event-factory if previously from applesauce-factory)
    • Protocol types and utilities: NostrEvent, UnsignedEvent, VerifiedEvent, kinds, encoding/decoding helpers, pointer types and basic pointer helpers, Filter, cache helpers, pure URL/byte helpers.
  • Always‑common (move to applesauce-common):
    • Any symbol that is:
      • A NIP or app concept: CommentsModel, ThreadModel, WrappedMessagesModel, LegacyMessagesThreads, LegacyMessageReplies, EventZapsModel, CalendarEventsModel, FavoriteRelaysModel, FavoriteRelaySetsModel, SearchRelaysModel, BlockedRelaysModel, HighlightBlueprint, CommentBlueprint, GroupMessageBlueprint, PollBlueprint, Zap, AppData, Calendar, GiftWrap, Stream, etc.
      • A NIP‑oriented helper, e.g. getNip10References, groupPubkeysByRelay (social graph), getArticle*, getHighlight*, getCalendarEvent*, getZap*, list helpers like getListTags, getRelaysFromList, FAVORITE_RELAYS_KIND.

5.3. Rewrite rules (pseudo‑code)

For each import statement:

ts
import { A, B, C } from "applesauce-core/helpers";
// or:
import { X, Y } from "applesauce-core/models";
// or:
import { EventFactory, CommentBlueprint } from "applesauce-factory";

Apply this algorithm:

  1. For each imported symbol S:
    1. If S is in the always‑core list:
      • Keep module as applesauce-core (or update path to applesauce-core/event-factory for EventFactory).
    2. Else if S is in the always‑common list or matches any of these patterns:
      • Name ends with Model and is clearly domain‑specific (CommentsModel, ThreadModel, WrappedMessagesModel, etc.).
      • Name ends with Blueprint.
      • Name is in the known operations list (AppData, Calendar, Comment, Poll, Zap, Stream, etc.).
      • Name is a helper that appears in both v4 applesauce-core/helpers and v5 applesauce-common/helpers.
      • Then:
        • If it is a helper → new module: applesauce-common/helpers (or applesauce-common/helpers/<module>).
        • If it is a model → new module: applesauce-common/models.
        • If it is an operation → new module: applesauce-common/operations.
        • If it is a blueprint → new module: applesauce-common/blueprints.
    3. Else if ambiguous:
      • Prefer applesauce-core if it deals only with low‑level protocol concerns.
      • Prefer applesauce-common if it refers to a NIP, UI feature, or application domain.
  2. Once each symbol is classified, group them by their new target module and emit one import per module.

5.4. Handling applesauce-factory

If you see imports from "applesauce-factory":

  • EventFactoryimport { EventFactory } from "applesauce-core/event-factory";
  • Any blueprints or operations → map to applesauce-common/blueprints or applesauce-common/operations as described above.

5.5. Other packages

As you refactor, do not change imports from these packages unless you have a specific reason:

  • applesauce-actions
  • applesauce-wallet
  • applesauce-react
  • applesauce-content
  • applesauce-relay
  • applesauce-loaders

They are already v5‑compatible at their public surface; the main change is that they internally depend on applesauce-common. Your application code usually does not need to adjust imports from these packages.


6. Before/after examples

These examples demonstrate common migration patterns.

6.1. Simple helper move (profiles)

Before (v4):

ts
import { getDisplayName, getProfilePicture } from "applesauce-core/helpers";

After (v5):

ts
import { getDisplayName, getProfilePicture } from "applesauce-common/helpers";

6.2. Models move (threads and comments)

Before (v4):

ts
import { EventStore } from "applesauce-core";
import { ThreadModel, CommentsModel } from "applesauce-core/models";

After (v5):

ts
import { EventStore } from "applesauce-core";
import { ThreadModel, CommentsModel } from "applesauce-common/models";

6.3. Factory + blueprints

Before (v4):

ts
import { EventFactory } from "applesauce-factory";
import { CommentBlueprint } from "applesauce-core/operations"; // or applesauce-factory in older code

const factory = new EventFactory({ signer });
const blueprint = CommentBlueprint({ content: "Hello" });

After (v5):

ts
import { EventFactory } from "applesauce-core/event-factory";
import { CommentBlueprint } from "applesauce-common/blueprints";

const factory = new EventFactory({ signer });
const blueprint = CommentBlueprint({ content: "Hello" });

6.4. End‑to‑end example: zap timeline

Before (v4) – everything from core:

ts
import { EventStore, mapEventsToStore, mapEventsToTimeline } from "applesauce-core";
import { getZapAmount, getZapEventPointer, getZapSender, isValidZap } from "applesauce-core/helpers";
import { useObservableMemo } from "applesauce-react/hooks";
import { onlyEvents, RelayPool } from "applesauce-relay";

After (v5) – split between core and common:

ts
import { EventStore, mapEventsToStore, mapEventsToTimeline } from "applesauce-core";
import { getZapAmount, getZapEventPointer, getZapSender, isValidZap } from "applesauce-common/helpers";
import { useObservableMemo } from "applesauce-react/hooks";
import { onlyEvents, RelayPool } from "applesauce-relay";

6.5. End‑to‑end example: group threads

Before (v4):

ts
import { EventStore, mapEventsToStore, mapEventsToTimeline } from "applesauce-core";
import { getDisplayName, getProfilePicture, getTagValue } from "applesauce-core/helpers";
import { EventFactory } from "applesauce-factory";
import { CommentsModel } from "applesauce-core/models";

After (v5):

ts
import { EventStore, mapEventsToStore, mapEventsToTimeline } from "applesauce-core";
import { getDisplayName, getProfilePicture, getTagValue } from "applesauce-common/helpers";
import { EventFactory } from "applesauce-core/event-factory";
import { CommentsModel } from "applesauce-common/models";

6.6. Lists helpers migration

Before (v4):

ts
import { FAVORITE_RELAYS_KIND, getListTags, getRelaysFromList } from "applesauce-core/helpers/lists";
import { FavoriteRelaysModel } from "applesauce-core/models";

After (v5):

ts
import { FAVORITE_RELAYS_KIND, getListTags, getRelaysFromList } from "applesauce-common/helpers/lists";
import { FavoriteRelaysModel } from "applesauce-common/models";

6.7. Pointer helpers behavior change

Before (v4) - relying on thrown errors:

ts
import { decodeEventPointer } from "applesauce-core/helpers/pointers";

try {
  const pointer = decodeEventPointer(encoded);
  // use pointer
} catch (e) {
  // handle error
}

After (v5) - checking for null:

ts
import { decodeEventPointer } from "applesauce-core/helpers/pointers";

const pointer = decodeEventPointer(encoded);
if (pointer) {
  // use pointer
} else {
  // handle invalid input
}

7. Notes on other packages

7.1. applesauce-actions

  • Public exports are stable in v5:
    • Root index exports: ActionRunner, Actions.
    • applesauce-actions/actions exposes individual actions such as BookmarkEvent, FollowUser, MuteUser, SendWrappedMessage, etc.
  • You do not generally need to change your imports:
    • import { ActionRunner } from "applesauce-actions";
    • import { FollowUser } from "applesauce-actions/actions";

Breaking changes in v5:

  • Actions are now async-only: All actions must return Promise<void>. Actions can no longer be synchronous functions or async generators.
  • Removed async generator support: The ActionRunner no longer supports actions that are async generators (async function*).
  • ActionContext methods are async: Both publish and run methods in ActionContext now return Promise<void>.

Migration example:

ts
// v4 - could be sync or async generator
export function MyAction(): Action {
  return async function* (context) {
    // generator code
    yield event1;
    yield event2;
  };
}

// v5 - must be async function returning Promise<void>
export function MyAction(): Action {
  return async (context) => {
    // async code
    await context.publish(event1);
    await context.publish(event2);
  };
}

If you have custom actions that used async generators, convert them to async functions that call context.publish() for each event.

7.2. applesauce-wallet

  • Public exports are also stable:
    • Root index exports: Actions, Blueprints, Helpers, Models, Operations.
    • applesauce-wallet/actions exports wallet‑specific actions like CreateWallet, UnlockWallet, NutzapProfile, etc.
  • Migration work here is mostly indirect:
    • Ensure applesauce-common is installed so any shared helpers/models used by wallet internals resolve correctly.

7.3. applesauce-react

  • Root exports are unchanged:
    • AccountsContext, AccountsProvider
    • ActionsContext, ActionsProvider
    • EventStoreContext, EventStoreProvider
    • FactoryContext, FactoryProvider
    • Helpers, Hooks
  • Hooks and providers still import from:
    • "applesauce-react/hooks"
    • "applesauce-react/providers"
  • New in v5: The use$ hook is available from "applesauce-react/hooks":
    • A utility hook that combines useObservableState and useMemo for easier observable usage
    • Supports BehaviorSubject, Observable, and factory functions with dependency arrays
    • Example: const value = use$(observable); or const value = use$(() => createObservable(), [deps]);
  • Your migration work is mostly in how you feed models and helpers into your React components (via the import rules above).

7.4. applesauce-content

  • The content package’s top‑level exports (Helpers, Text, Markdown, Nast) are unchanged between v4 and v5.
  • Some examples in the docs now combine applesauce-content (for parsing text/markdown) with models and helpers from applesauce-common.

7.5. Loaders, relay, signers, accounts, etc.

  • applesauce-loaders, applesauce-relay, applesauce-signers, applesauce-accounts are largely unaffected at the import level.
  • Just ensure you keep them on a consistent major version with applesauce-core/applesauce-common.
Loader attachment to EventStore
  • In v4, there were multiple experimental loader properties on EventStore (eventLoader, replaceableLoader, addressableLoader).
  • In v5, these are replaced by a single unified eventLoader. Apps should:
    • Prefer the Unified Event Loader (createUnifiedEventLoader) when setting up loaders.
    • Or, more commonly, use createEventLoaderForStore(eventStore, pool, options) from applesauce-loaders/loaders to attach the unified loader to an EventStore in one step.
RelayPool behavior changes
  • RelayPool ignores offline relays by default: The request(), subscription(), publish(), and sync() methods in RelayPool now ignore unreachable (ready=false) relays by default. This is controlled by the ignoreOffline property (default: true). Manual methods like req() and event() still include offline relays.
  • New retry configuration options: Relay and RelayPool constructors now accept subscriptionRetry, requestRetry, and publishRetry options for configuring default retry limits. These replace the previous approach.

Migration note: If your v4 code relied on offline relays being included in automatic methods (request(), subscription(), publish(), sync()), you may need to:

  • Set ignoreOffline: false on your RelayPool instance, or
  • Use the manual req() and event() methods which always include offline relays.

8. Verification and troubleshooting checklist

After applying the import migration, use this checklist:

  • 1. Install dependencies
    • Ensure applesauce-common is installed.
    • Align all applesauce-* packages to the same major version.
  • 2. Compile and typecheck
    • Run your project’s build/test commands (for this monorepo, pnpm build / pnpm test).
    • Fix any remaining import‑not‑found errors by moving the symbol to applesauce-common as per the rules above.
  • 3. Runtime smoke tests
    • Open key screens: threads/comments, group chat, highlight/article pages, zap timelines, calendar views.
    • Verify that models populate and helpers behave as before.
  • 4. Diff‑based validation (for AI agents)
    • Confirm that any removed imports from "applesauce-core/helpers" or "applesauce-core/models":
      • Either moved to "applesauce-common/helpers" / "applesauce-common/models" / "applesauce-common/operations" / "applesauce-common/blueprints", or
      • Were intentionally deleted because they are unused.
  • 5. Ambiguous symbols
    • When in doubt, prefer:
      • applesauce-core for protocol‑level primitives and generic utilities.
      • applesauce-common for NIP‑specific or app‑level behavior.

Once this checklist passes, your project should be functionally equivalent to the v4 version while taking full advantage of the v5 applesauce-common split.


9. New features in v5

This section documents new features and APIs introduced in v5 that are not migration concerns but may be useful to know about.

9.1. Event Casting System

v5 introduces a type-safe event casting system in applesauce-common/casts for working with Nostr events in a more structured way.

Location: applesauce-common/casts or applesauce-common/Casts

Key exports:

  • cast() function and Cast base class
  • Specific cast classes: NoteCast, ProfileCast, ZapCast, CommentCast, MailboxesCast, StreamCast

Usage example:

ts
import { cast, NoteCast } from "applesauce-common/casts";

// Cast an event to a NoteCast for type-safe access to note-specific properties
const note = cast(event, NoteCast);
// Now you can use note-specific methods and properties

This is a new API for v5 and does not require migration from v4 code.

9.2. Observable Utilities

v5 adds new observable utilities in applesauce-common/observable:

Location: applesauce-common/observable or applesauce-common/Observable

Key exports:

  • chainable - Chainable observable utilities for building reactive pipelines
  • cast-event - Event casting observables
  • filter-timeline-by-mutes - Timeline filtering utilities for muting

These are new utilities for v5 and do not require migration from v4 code.

9.3. NIP-29 Group Management

v5 adds comprehensive support for NIP-29 group management:

Helpers: applesauce-common/helpers/groups

  • Group management constants: CREATE_GROUP_KIND, CREATE_INVITE_KIND, DELETE_GROUP_KIND, EDIT_METADATA_KIND, JOIN_REQUEST_KIND, LEAVE_REQUEST_KIND, PUT_USER_KIND, REMOVE_USER_KIND
  • Group management helper functions

Blueprints: applesauce-common/blueprints

  • GroupJoinRequestBlueprint - Create a join request for a group
  • GroupLeaveRequestBlueprint - Create a leave request for a group
  • GroupCreateBlueprint - Create a new group
  • GroupDeleteBlueprint - Delete a group
  • GroupEditMetadataBlueprint - Edit group metadata
  • GroupPutUserBlueprint - Add or update a user in a group
  • GroupRemoveUserBlueprint - Remove a user from a group

This is a new feature for v5 and does not require migration from v4 code.