AbsoluteJS

@absolutejs/sync-pack-counters

@absolutejs/sync-pack-countersv0.1.5betaData & Sync

Read-set-tracked live counters for @absolutejs/sync — define a compute function, get a reactive count derived from host tables

#Installation

BASH
bun add @absolutejs/sync-pack-counters

#Capabilities

Overview

Read-set-tracked live counters for @absolutejs/sync. Define a compute function that reads through db; the engine re-runs and pushes the new value whenever any table the compute read changes. No manual invalidation, no polling, no operator graph — just a function.

Usage

Each counter becomes its own reactive query collection named counter: returning a single row. Subscribe from the client:

Why defineReactiveQuery?

The pack is one big use case for sync's read-set tracking: a counter is literally "compute a number from one or more tables, and re-emit it when those tables change." The engine instruments every db.all / db.get / db.where your compute makes, records the resulting dependency set, and parks the query on it. Any subsequent change to a touched table triggers a re-run; rows that didn't change don't.

Prefer db.where(table, predicate) over db.all(table).filter(...) when possible — where records a range dependency that re-runs only when a change matches the predicate now or matched it before, instead of on every change to the table.

Permissions

The default authorize requires the caller's ctx to expose an actor id (via getActorId, defaulting to (ctx) => ctx.userId). Per-counter authorize overrides this — return () => true for a public counter, or implement role-based gating.

What the pack ships

Surface — Name — What it does

Reactive query (×N) — counter: — One per counter; emits a single CounterRow

The pack owns no tables and reads no tables of its own — every counter's read-set comes from the host's registered readers. engine.inspect().packs[0] reports empty ownsTables and readsTables.

Multiple instances

Pass prefix to coexist with another counters pack instance:

Outcomes

What you can build

Build on the supported package contract

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

Hardening checklist

Production guidance

Make every external boundary explicitPin the deployed @absolutejs/sync-pack-counters 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-counters 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-counters quick start

Partial snippet

# @absolutejs/sync-pack-counters

BASH
bun add @absolutejs/sync-pack-counters

#Usage

Partial snippet

Working example for Usage.

TS
import { createSyncEngine } from '@absolutejs/sync/engine';
import { createCountersPack } from '@absolutejs/sync-pack-counters';

const engine = createSyncEngine();
engine.registerReader('tasks',         { all: () => db.tasks.list() });
engine.registerReader('notifications', { all: () => db.notifications.list() });

engine.registerPack(
	createCountersPack({
		counters: {
			// Bare function form.
			openTasks: async ({ db }) =>
				(await db.all<Task>('tasks')).filter((t) => !t.done).length,

			// Per-actor counter using ctx.
			unreadNotifications: async ({ db, ctx }) =>
				(await db.all<Notification>('notifications'))
					.filter((n) => n.actorId === ctx.userId && n.readAt === null)
					.length,

			// Object form when you need a per-counter authorize override —
			// e.g. a public site-wide stat anyone can subscribe to.
			totalUsers: {
				authorize: () => true,
				compute: async ({ db }) => (await db.all('users')).length,
			},
		},
	}),
);

#Usage 2

Partial snippet

Each counter becomes its own reactive query collection named counter: returning a single row. Subscribe from the client:

TS
useSyncCollection<CounterRow>({ collection: 'counter:openTasks' });
// Emits { id: 'openTasks', key: 'openTasks', value: 3, computedAt: ... }

#Multiple instances

Partial snippet

Pass prefix to coexist with another counters pack instance:

TS
engine.registerPack(createCountersPack({ prefix: 'team_', counters: { ... } }));
engine.registerPack(createCountersPack({ prefix: 'org_',  counters: { ... } }));
// Collections: team_counter:<key> and org_counter:<key>

#Public entry points

Supported entry points declared by this package manifest.

Package entry point declared in package.json.

@absolutejs/sync-pack-counters@absolutejs/sync-pack-counters/manifest@absolutejs/sync-pack-counters/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

#API reference

Search the declarations exported by the current package type files. Expand a symbol to inspect its source-backed signature.

7 symbols
CounterRowtypePermalinkSource

One reactive count emission.

TS
type CounterRow = {
    /** Row key — matches the counter's name. */
    id: string;
    key: string;
    value: number;
    /** Epoch ms the compute completed. Useful for "as of" labelling. */
    computedAt: number;
};
Exported from @absolutejs/sync-pack-counters