/** * Opt-in policy controls for background job creation and dispatch. * * @remarks * These guards harden the background-jobs surface flagged by the S5 audit * (#1402): * * - {@link MAX_JOB_RETRIES} caps the retry count a caller can request so a * misconfigured `.retries(n)` cannot pin a worker on a poison job forever. * - {@link assertWithinTenantCreationCap} bounds how many jobs a single tenant * may hold in the queue at once, so one tenant cannot exhaust the shared * worker pool (a cross-tenant denial of service). * - {@link isBackgroundEligibleMethod} / {@link backgroundEligible} provide an * opt-in allowlist of methods that may be invoked by the runner. The runner's * dispatch is already bounded to existing prototype methods (no eval / dynamic * import), but a class can further restrict which of its methods are reachable * from a persisted job row. * * All three live in and are enforced by this package (`@happyvertical/smrt-jobs`). * Other packages apply the marker (`smrt-reports`, `smrt-support`, * `smrt-fields`), but the only thing that reads it is `TaskRunner`'s dispatch in * `runner.ts`. `@happyvertical/smrt-agents` neither imports nor honours it, and * does not depend on this package. Marking a method does **not** make it * background-eligible anywhere outside the jobs runner. */ /** * Hard ceiling on retry attempts a caller may request via `.retries(n)` / * `bg(..., { retries })`. Requests above this are clamped (not rejected) so * existing callers keep working while the worst case stays bounded. */ export declare const MAX_JOB_RETRIES = 25; /** * Default maximum number of non-terminal (pending/running) jobs a single * tenant may hold in the queue at once. Configurable per call; `0` / negative * disables the cap. */ export declare const DEFAULT_TENANT_JOB_CAP = 10000; /** * Clamp a requested retry count to {@link MAX_JOB_RETRIES}. * * @param requested - The retry count supplied by the caller. * @returns A non-negative integer no greater than {@link MAX_JOB_RETRIES}. */ export declare function clampRetries(requested: number): number; /** * Error thrown when a tenant exceeds its allowed in-flight job count. */ export declare class TenantJobCapExceededError extends Error { readonly tenantId: string; readonly cap: number; readonly current: number; constructor(tenantId: string, cap: number, current: number); } /** * Throw {@link TenantJobCapExceededError} when a tenant is at or above its cap. * * @param tenantId - Tenant the new job would belong to (`null` = global; not * subject to the per-tenant cap). * @param current - Current count of non-terminal jobs for the tenant. * @param cap - Maximum allowed; `<= 0` disables the check. */ export declare function assertWithinTenantCreationCap(tenantId: string | null | undefined, current: number, cap: number): void; /** * Class shape that opts into a background-method allowlist by declaring a * static set/array of method names. */ export interface BackgroundEligibleClass { /** * Method names that may be invoked by `TaskRunner`. When present (even if * empty), it is treated as an exhaustive allowlist. When absent, the runner * falls back to its default behaviour (any existing method). */ backgroundEligibleMethods?: ReadonlyArray | ReadonlySet; } /** * Add method names to a class's background-eligible allowlist. * * Installs/extends a static `backgroundEligibleMethods` set on the constructor. * Once any method is marked, the runner refuses to dispatch a job whose * `method` is not in the set — turning the dispatch surface from "any prototype * method" into an explicit contract. Use this when applying the * {@link backgroundEligible} decorator is inconvenient. Non-decorator code can * skip the helper entirely and declare the static array directly, as * `smrt-fields` does in `usage-learning.ts`. * * @param ctor - The class constructor to annotate. * @param methods - Method names to allow. */ export declare function markBackgroundEligible(ctor: object, ...methods: string[]): void; /** * Decorator: mark a method as background-eligible. * * This is **restrictive, not enabling**. Without it, `TaskRunner` will dispatch * any existing prototype method; the first `@backgroundEligible()` on a class * closes that surface and turns the set into an exhaustive allowlist, so every * *other* method on that class stops being reachable from a persisted job row. * Adding it to one method of an existing class is therefore a behaviour change * for its siblings — mark all the methods you dispatch, or none of them. * * This is a legacy (`experimentalDecorators`) method decorator — the mode the * SMRT monorepo compiles with. Applying it (one or more times) builds up the * static `backgroundEligibleMethods` allowlist on the owning class. * * @example * ```ts * class Report extends SmrtObject { * \@backgroundEligible() * async regenerate() {} // reachable from a job * * async deleteEverything() {} // NOT reachable from a job * } * ``` */ export declare function backgroundEligible(): (target: object, propertyKey: string | symbol, descriptor?: PropertyDescriptor) => PropertyDescriptor | undefined; /** * Resolve the declared allowlist for a class, if any. * * @param ctor - The target object's constructor. * @returns A `Set` of allowed method names, or `null` when the class did not * opt in (runner should fall back to default behaviour). */ export declare function getBackgroundEligibleMethods(ctor: unknown): ReadonlySet | null; /** * Whether a method may be invoked by the runner for a given target class. * * @param ctor - Constructor of the resolved target class. * @param method - Method name from the persisted job row. * @returns `true` when the class declared no allowlist (default) or when the * method is on the allowlist; `false` when an allowlist exists and excludes * the method. */ export declare function isBackgroundEligibleMethod(ctor: unknown, method: string): boolean; //# sourceMappingURL=background-policy.d.ts.map