AbsoluteJS

@absolutejs/sync-pack-notifications

@absolutejs/sync-pack-notificationsv0.5.1betaData & Sync

Per-actor inbox pack for @absolutejs/sync — notify, mark-read, scoped reads, optional TTL auto-archive

#Installation

BASH
bun add @absolutejs/sync-pack-notifications

#Capabilities

Overview

Per-actor inbox pack for @absolutejs/sync. Each actor sees only their own rows; notify is the host-trusted insert path; markRead and markAllRead are client-callable owner-only mutations. Optional autoArchiveAfterDays deletes rows past TTL via a cron schedule.

The pack exposes

Surface — Name — What it does

Collection — notifications — Each actor sees their own rows; moderators see all

Mutation — notifications:notify — Insert one row for a target actor (host-trusted)

Show 3 more

Mutation — notifications:markRead — Stamp readAt on one row — owner only

Mutation — notifications:markAllRead — Bulk-mark every unread row in the caller's inbox

Schedule — notifications:cleanup — (Only if autoArchiveAfterDays set) deletes expired

Storage

Default: per-instance in-memory store. For a persistent backend pass a custom store:

getById is required (used by markRead to verify ownership before update). expired is required when autoArchiveAfterDays is set.

Multiple instances

Pass prefix to coexist with another notifications pack instance (e.g. a separate "system" inbox vs the regular one):

Persistent inboxes and preferences (0.5+)

createPersistentNotificationsPack<Ctx, Tx> supports asynchronous storage and ACLs. Supply table, preferencesTable, dependencies (resource and access tables), allowed kinds, getActorId, canRead(row, ctx), and a store implementing list, get, markRead, preferences, and savePreferences.

The collection named after table filters both actor ownership and current resource access. The ${table}:markRead mutation checks those gates before durable receipt replay and again before its writer runs. ${preferencesTable}:save accepts {values, expectedUpdatedAt}; preferences contain exactly the configured kinds, each with boolean email and inApp channels. The owner always comes from the authenticated context, never request data.

Use the supplied engine transaction for writes. savePreferences must atomically compare expectedUpdatedAt, persist the values and advance its timestamp. The host owns notification creation: insert rows and enqueue ABS Queue jobs in the same transaction as the business event. No client-callable notification-creation mutation is exposed. Recheck resource access, channel preferences and account status before external delivery. Email, retries and templates remain Dispatch/Queue and host concerns.

Show 1 more

The existing synchronous createNotificationsPack API is unchanged.

Outcomes

What you can build

Build on the supported package contract

Use @absolutejs/sync-pack-notifications through its supported public entry points.

Hardening checklist

Production guidance

Make every external boundary explicitPin the deployed @absolutejs/sync-pack-notifications version, replace example or memory-backed dependencies with durable implementations, bound external calls, protect credentials, and emit enough evidence to retry or recover safely.

Follow in order

Troubleshooting path

1
Trace from the first failed boundary
Reproduce the smallest canonical @absolutejs/sync-pack-notifications example, confirm the supported entry point and version in the API explorer, then inspect the first boundary that did not produce its documented result.

#@absolutejs/sync-pack-notifications quick start

Partial snippet

# @absolutejs/sync-pack-notifications

BASH
bun add @absolutejs/sync-pack-notifications

#Usage

Partial snippet

Working example for Usage.

TS
import { createSyncEngine } from '@absolutejs/sync/engine';
import { createNotificationsPack } from '@absolutejs/sync-pack-notifications';

const engine = createSyncEngine();
engine.registerPack(
	createNotificationsPack({
		getActorId: (ctx) => ctx.session.userId,
		// `notify` is the trusted insert path. Mark the ctx your host
		// uses to call notify as a moderator so the row-author permission
		// check passes. (In a real app this is a server-only "system"
		// trust flag, not isModerator.)
		canModerate: (ctx) => ctx.session.systemTrusted === true,
		// Optional: archive rows after 30 days. Cleanup schedule fires
		// hourly by default.
		autoArchiveAfterDays: 30,
	}),
);

// From any server-side path (a webhook, a schedule, another mutation):
await engine.runMutation(
	'notifications:notify',
	{
		actorId: 'alice',
		kind: 'mention',
		title: 'You were mentioned',
		body: 'in @doc-123 by bob',
		href: '/docs/123#comment-456',
	},
	{ session: { systemTrusted: true } },
);

#Row shape

Partial snippet

Working example for Row shape.

TS
type NotificationRow = {
	id: string;
	actorId: string;       // whose inbox
	kind: string;          // app-level tag: "mention", "reply", "system", ...
	title: string;
	body: string;
	href: string | null;   // optional jump-to URL
	createdAt: number;
	readAt: number | null; // null = unread
	expiresAt: number | null;
};

#Storage

Partial snippet

Default: per-instance in-memory store. For a persistent backend pass a custom store:

TS
import {
	createNotificationsPack,
	type NotificationsStore,
} from '@absolutejs/sync-pack-notifications';

const store: NotificationsStore = {
	getById: (id) => /* SELECT * FROM notifications WHERE id = $1 */,
	expired: (now) => /* SELECT * FROM notifications WHERE expires_at <= $1 */,
	reader: { all: () => /* SELECT */ },
	writer: { insert, update, delete },
};

#Public entry points

Supported entry points declared by this package manifest.

Package entry point declared in package.json.

@absolutejs/sync-pack-notifications@absolutejs/sync-pack-notifications/manifest@absolutejs/sync-pack-notifications/manifest.json

#Package commands

Scripts declared by this package manifest.

bun run buildrm -rf dist && bun build src/index.ts src/manifest.ts --outdir dist --sourcemap --target=bun --external @absolutejs/sync && tsc --project tsconfig.build.json && absolute-manifest emit
bun run formatprettier --write "./**/*.{ts,json,md}"
bun run testbun test
bun run typechecktsc --noEmit