Build on the supported package contract
Use @absolutejs/sync-pack-utils through its supported public entry points.
@absolutejs/sync-pack-utilsv0.1.5betaData & SyncShared helpers for @absolutejs/sync packs — resolveActor, requireRowOwner/Moderator, createInMemoryStore, and other patterns extracted from the official packs
bun add @absolutejs/sync-pack-utilsShared helpers for @absolutejs/sync packs. Each helper closes one repeated pattern that showed up across the six official packs (presence, comments, digest, notifications, favorites, counters). The goal: make new packs — first- or third-party — cheaper to write and consistent with what's already shipped.
Actor resolution
Permission builders
requireRowOwner and requireOwnerOrModerator are higher-order — call them once at pack-build time to receive the per-row predicate the engine expects. Both reject when getActorId(ctx) is undefined (anonymous callers can't own a row).
In-memory store
createInMemoryStore<Row>() returns:
reader: TableReader<CollectionContext> — all() returns [...rows.values()]
writer: TableWriter<Row> — upsert-by-id; delete removes by row.id
getById(id) — point lookup
rows: Map — direct access (treat as read-only)
Every official pack uses this as its default backing store and exposes a store?: ... config field so consumers can swap in a Postgres / Drizzle / Redis implementation when they need persistence.
prefixed(prefix, ...): just use template literals.
${prefix} is fine.
defineActorPack(...): a Convex-style abstraction that wraps
defineSyncPack with permission defaults. Decided against — it'd hide the engine surface, which is the thing pack authors learn first.
Validators / Zod adapters: out of scope; sync's field helper +
schema is the validation layer.
Pack tests typically pair with @absolutejs/sync/testing (added in sync 1.9.2):
This package stays 0.x indefinitely while the ecosystem settles. When the six official packs are stable, the helpers will follow.
Outcomes
Use @absolutejs/sync-pack-utils through its supported public entry points.
Hardening checklist
Follow in order
# @absolutejs/sync-pack-utils
bun add @absolutejs/sync-pack-utilsWorking example for Actor resolution.
import {
defaultGetActorId,
resolveActor,
} from '@absolutejs/sync-pack-utils';
// (ctx) => ctx.userId — the conventional default.
const getActorId = config.getActorId ?? defaultGetActorId<MyCtx>();
// Inside a mutation handler: throws UnauthorizedError if no actor id.
// The context string ends up in the error message so logs name the op.
const actorId = resolveActor(getActorId, ctx, 'mypack:create');Working example for Permission builders.
import {
requireRowOwner,
requireOwnerOrModerator,
} from '@absolutejs/sync-pack-utils';
permissions: {
[table]: {
read: ...,
// Owner-only writes — uses row.actorId by default; override the
// field name for packs that use authorId, userId, etc.
insert: requireRowOwner(getActorId, 'actorId'),
update: requireRowOwner(getActorId, 'actorId'),
// Owner OR moderator delete. Falls back to store.getById when
// actions.delete supplied only the row key (the common case).
delete: requireOwnerOrModerator({
actorIdField: 'actorId',
canModerate, // optional
getActorId,
store, // { getById }
}),
},
},Supported entry points declared by this package manifest.
Package entry point declared in package.json.
Scripts declared by this package manifest.