Skip to content

Overview

@lastshotlabs/slingshot-notifications is Slingshot’s shared notification package. It owns notification records, user notification preferences, dispatcher scheduling, rate limiting, preference resolution, and the builder/runtime surface that other packages use to enqueue or deliver notifications. The notification entities themselves follow the shared package-first/entity authoring model; createNotificationsPackage() is the runtime shell that wires storage, dispatch, and delivery.

Use this package when your app or plugin needs:

  • persisted notification records
  • user notification preferences and quiet-hours-aware resolution
  • queued notification dispatch with a polling loop
  • SSE delivery for the current user
  • a common notification builder that feature packages can share

This is the right dependency for chat, community, push, and other product packages that need a shared notification backbone instead of private per-feature notification tables.

The package declares slingshot-auth as a dependency. It expects the auth layer to be present before notification routes and user-scoped SSE behavior are used.

The config is intentionally easy to start with. createNotificationsPackage() accepts a partial config object and fills in defaults.

With no config, the package already gives you:

  • route mount path /notifications
  • SSE endpoint /notifications/sse
  • dispatcher enabled
  • dispatcher interval 30000
  • dispatcher max-per-tick 500
  • memory-backed rate limiting
  • default channel preferences of push, email, and in-app all enabled

The main knobs are:

  • mountPath
  • sseEnabled
  • ssePath
  • dispatcher
  • rateLimit
  • defaultPreferences
  • reliability

For the PostgreSQL production path, opt into atomic notification persistence and event publication:

createNotificationsPackage({
reliability: {
store: 'postgres',
consumerName: 'slingshot-notifications-delivery-v1',
},
});

The app must enable matching events.reliability outbox and inbox configuration. Immediate notification creation and the governed notifications:notification.created envelope then commit in one SQL transaction. Delivery runs through the stable named inbox consumer. Delivery adapters receive the envelope event ID as context.idempotencyKey and should forward it to external providers.

The package provides:

  • notification and notification-preference entities
  • adapters resolved for the active store backend
  • a notification builder surface created from package state
  • a dispatcher that can poll and deliver queued notifications
  • SSE routing for user-scoped notification streams
  • a registration point for delivery adapters

At runtime, the package publishes typed capabilities:

  • NotificationsBuilderFactoryCap({ source }) => NotificationBuilder for building source-scoped notification builders
  • NotificationsDeliveryRegistryCap.register(adapter) for registering delivery adapters
  • NotificationsHealthCap() => NotificationsHealth for the aggregated health snapshot
import type { HookServices } from '@lastshotlabs/slingshot-core';
import { NotificationsHealthCap } from '@lastshotlabs/slingshot-notifications';
function readNotificationsHealth(ctx: HookServices) {
return ctx.capabilities.require(NotificationsHealthCap)();
}

Internal state remains published under NOTIFICATIONS_PLUGIN_STATE_KEY for legacy consumers, but new code should resolve the capabilities above.

The first files to read are:

  • src/plugin.ts for package lifecycle and runtime state
  • src/public.ts for the contract and capabilities
  • src/types/config.ts for defaults and configuration
  • src/builder.ts for builder behavior
  • src/dispatcher.ts for queue draining and delivery
  • src/preferences.ts for quiet hours and effective preference resolution

The highest-value changes are usually:

  • dispatcher cadence and throughput
  • default preference behavior
  • rate-limit backend and thresholds
  • delivery adapter registration
  • mountPath must start with /; trailing slashes are trimmed before routes are mounted.
  • Disabling the dispatcher does not remove notification persistence. It only stops the automatic polling loop that drains queued notifications.
  • Delivery adapters are opt-in. Persisted notifications exist without them, but external delivery will not happen until another package registers an adapter.
  • Package state is only complete after setup has resolved the entity adapters. If those adapters are missing, the package throws during setup instead of continuing in a half-wired state.
  • SSE can be disabled independently from storage and dispatch. Treat it as a delivery surface, not the whole notification system.
  • src/index.ts
  • src/plugin.ts
  • src/types/config.ts
  • src/builder.ts
  • src/dispatcher.ts
  • src/preferences.ts
  • src/sse.ts