import { DatabasePayload, Id } from "../types.mjs"; import { NotificationContract } from "../contracts/notification.contract.mjs"; import { NotificationColumnMapHost } from "./column-map.mjs"; import { DatabaseNotification } from "./database-notification.mjs"; import { FilterRules, RepositoryManager } from "@warlock.js/core"; //#region ../notifications/src/in-app/base-notifications-repository.d.ts /** * Filter shape — logical keys the repo understands. `filterBy` maps each to a * physical column (or a predicate) derived from the model's `columnMap`. */ type NotificationsFilter = { id?: Id; type?: string; /** Mode-agnostic unread filter — resolves to the model's read-state column. */ unread?: boolean; recipientId?: Id; idempotencyKey?: string; }; /** Class form of a `DatabaseNotification` subclass — what `configure()` accepts. */ type NotificationModelClass = (new (...args: any[]) => DatabaseNotification) & NotificationColumnMapHost; declare class BaseNotificationsRepository extends RepositoryManager { /** Resolved physical columns for the bound model — the single source. */ private readonly columns; filterBy: FilterRules; constructor(model?: NotificationModelClass); /** The tenant column for this model, or `undefined` for single-tenant. */ get tenantColumn(): string | undefined; /** * Build `filterBy` from the resolved column map. `unread` is the * mode-agnostic read filter; `isRead` / `readAt` / `tenant` are exposed for * direct filtering only when the model declares those columns. */ private buildFilterBy; /** * Translate an object keyed by LOGICAL names into one keyed by PHYSICAL * columns, using `filterBy` as the lookup. Keys with no `[op, column]` tuple * (e.g. `title`, `body`, `payload`, `type`, `id`) pass through unchanged — * they're already physical. Used for every write / update / delete payload + * filter (the paths `RepositoryManager` does NOT map). */ protected toRow(obj: Record): Record; /** * Runtime backstop for the compile-time `DatabasePayload` `Omit`: untyped * callers (`notify.channel(name).send`, payloads assembled from request * JSON) can smuggle server-owned keys past the type system, and spreading * them into the row would let a payload redirect the notification into * another recipient's inbox (recipient spoofing), cross tenants, pre-set * read-state, or clobber the primary key. Drops BOTH the logical names and * the model's resolved physical columns — `toRow` passes unmapped keys * through verbatim, so a payload carrying `user_id` would otherwise reach * the recipient column directly. */ private stripServerOwnedKeys; /** * Create one row for the recipient. New rows start unread; the tenant column * (when the model declares one) is written from `tenantId`, which the * database channel reads off the recipient. `recipientId` and the other * server-owned keys always come from the trusted arguments — matching keys * in `input` are stripped, never merged. * * Idempotency: when `input.idempotencyKey` is set, find-or-create — return * the existing row instead of inserting a duplicate. A unique index on the * key is the backstop for the rare insert race (we re-fetch on conflict). */ createFor(recipientId: Id, input: DatabasePayload, tenantId?: Id): Promise; /** * Bulk-create one row per recipient sharing the same input + tenant. Reserved * for the Phase-2 `Channel.sendMany` fan-out hook; not on the per-recipient * send path yet. */ createManyFor(recipientIds: Id[], input: DatabasePayload, tenantId?: Id): Promise; /** * Mark rows read — recipient-scoped. `id` omitted → all unread rows for the * recipient; `id` given → that one row, still scoped to the recipient. Scoped * to unread so a re-mark never overwrites an earlier `read_at` timestamp. * Sets whichever read-state column(s) the model declares. */ markRead(recipientId: Id, id?: Id): Promise; /** * Inverse of `markRead`, same recipient-scoping. No unread scope needed — * clearing the read-state of an already-unread row is a no-op. */ markUnread(recipientId: Id, id?: Id): Promise; /** Find one row for a recipient (read path — `filterBy` maps the keys). */ findFor(recipientId: Id, id: Id): Promise; /** * Delete rows — recipient-scoped. `id` omitted → clear all for the * recipient; `id` given → that one row. */ deleteFor(recipientId: Id, id?: Id): Promise; /** Read path — `filterBy` maps `recipientId`/`idempotencyKey` to columns. */ private firstByIdempotencyKey; } //#endregion export { BaseNotificationsRepository, NotificationModelClass, NotificationsFilter }; //# sourceMappingURL=base-notifications-repository.d.mts.map