import { BaseConnector } from '../../base/base.connector'; import type { IPushOptions, IPushProvider, ISendMessageSuccessResponse, WithPassthrough, PushSendResult, IPushConnector } from '../../types'; import { ChannelTypeEnum } from '../../types'; import type { FcmConfig } from './fcm.config'; import type { FcmPushSendInput } from './fcm.types'; export declare class FcmPushConnector extends BaseConnector implements IPushProvider, IPushConnector { private config; readonly id = "fcm"; readonly channelType: ChannelTypeEnum.PUSH; constructor(config: FcmConfig, fetchImpl?: typeof fetch); /** * Thinwrap-native send. Implements `IPushConnector`. * * Auth flow: * 1. Resolve access token via `getAccessTokenViaHookOrFresh()` — either * from the consumer's `tokenCache` hook (if supplied and unexpired) * or by signing a fresh RS256 JWT and exchanging it at Google's * OAuth 2.0 endpoint. * 2. POST `{ message: }` to * https://fcm.googleapis.com/v1/projects//messages:send * with `Authorization: Bearer `. * * Single-recipient enforcement: `input.to` is a single device * token. Topic-based delivery flows through `_passthrough.body.message.topic`. * * Error handling: vendor errors map to canonical `ConnectorError` via * `mapVendorError`. On vendor 401 the wrapper does NOT evict the hook * cache — eviction is the consumer's responsibility. */ send(input: FcmPushSendInput): Promise; /** * Resolve the FCM OAuth access token, going through the consumer's * `tokenCache` hook when configured: * * - No `tokenCache`: mint fresh on every call (the stateless default path). * - `tokenCache.get()` returns `{ token, expiresAt }` with `Date.now() < expiresAt`: * reuse the cached token; do NOT call `mintFreshToken`; do NOT call `set`. * - `tokenCache.get()` returns null or a stale entry: mint fresh, then call * `set(key, accessToken, Date.now() + expiresIn * 1000)`. * * The cache key is `'fcm:' + projectId + ':' + clientEmail` — the * service-account identity is part of the key so two configs for the same * project with different service accounts (sharing one `tokenCache`) never * serve each other's tokens. Vendor-rejection-of-cached-token eviction is the * consumer's responsibility (the wrapper holds no state). */ private getAccessTokenViaHookOrFresh; /** * Build an FCM HTTP v1 `Message` object from the narrowed `FcmPushSendInput`. * Field-mapping: * input.to → message.token (single recipient) * input.title → message.notification.title * input.body → message.notification.body * input.data → message.data (already constrained to Record) * input.ttl → message.android.ttl = "s" (FCM HTTP v1 wire shape) * Platform blocks (android, apns, webpush, fcm_options) forwarded verbatim. */ private buildFcmMessage; /** * Map FCM HTTP v1 error responses to canonical `ConnectorError` with the * 6-value `ProviderCode` union. Parses `Retry-After`: * parsed seconds embedded in `providerMessage` text; raw header * value on `cause.retryAfter`. No structured `retryAfterSeconds` field per * Retry is consumer policy (no retryAfterSeconds field). */ private mapVendorError; sendMessage(options: IPushOptions, bridgeProviderData?: WithPassthrough>): Promise; private buildLegacyMessage; private cleanPayload; }