import type { PushSubscriptionData, PushSubscriptionRepository } from '../adapters/types.js'; import { type RateLimitStore } from '../rate-limit.js'; export type { VapidKeys } from './web-push-crypto.js'; export { generateVapidKeys } from './web-push-crypto.js'; export interface VapidConfig { publicKey: string; privateKey: string; subject: string; } export interface PushPayload { title: string; body?: string; icon?: string; url?: string; tag?: string; } export interface PushResult { endpoint: string; success: boolean; statusCode?: number; expired?: boolean; /** Set to true when the push was skipped because the per-endpoint rate limit was hit. */ rateLimited?: boolean; /** Error message when the push failed before a response was received (crypto, fetch, VAPID). */ error?: string; } export interface PushRateLimitConfig { /** Rolling window in milliseconds within which `max` pushes per endpoint are allowed. */ windowMs: number; /** Maximum number of pushes to the same endpoint per window. */ max: number; /** Optional persistent adapter (Redis/Prisma/etc.). Defaults to in-memory. */ store?: RateLimitStore; } export interface PushServiceOptions { /** * Enable per-endpoint rate limiting. Pushes that exceed the window/max * budget are returned with `rateLimited: true` and `statusCode: 429` in * their `PushResult` — the HTTP request to the push endpoint is skipped. */ rateLimit?: PushRateLimitConfig; } /** * A push subscription paired with its owning user, so cleanup can scope the * delete by `(userId, endpoint)` and avoid removing another user's row that * happens to share an endpoint URL. */ export interface OwnedPushSubscription extends PushSubscriptionData { userId: string; } export interface PushService { sendPush(subscriptions: PushSubscriptionData[], payload: PushPayload): Promise; cleanupExpired(repo: PushSubscriptionRepository, subscriptions: OwnedPushSubscription[]): Promise; } export declare function createPushService(vapidConfig: VapidConfig, options?: PushServiceOptions): PushService;