import type { AdvertisementObservation } from '../backend-contract/advertisement.js'; import type { ConnectionLifecycleCause, ConnectionLifecycleEvent } from '../backend-contract/connection-lifecycle.js'; import type { BackendIdentity } from '../backend-contract/identity.js'; import type { PeerId } from '../backend-contract/primitives.js'; import type { BleManager as InternalBleManager } from '../manager/ble-manager.js'; import type { BleManagerOptions } from '../manager/ble-manager.js'; import type { BoundedAsyncStream, StreamTerminalNotice } from '../backend-contract/streams.js'; import type { OperationOptions } from './operation-options.js'; import type { StreamPolicy } from './stream-presets.js'; import type { IpcAdvertisement } from '../ipc/manager.js'; import type { BleCapabilities } from './capabilities.js'; import type { BleAdapter } from './ble-adapter.js'; import type { BleDiagnostics } from './diagnostics.js'; import type { GattDatabase, GattValueEvent } from './gatt.js'; import { normalizeScanQuery, type NormalizedScanObservation, type ScanQuery } from './scan-query.js'; import type { BlePeerDirectory, BlePeerState, PeerSource } from './peer-directory.js'; import type { PeerReference } from './peer-reference.js'; import type { ScanPlan } from '../backend-contract/scan-planning.js'; export type { ScanPlan } from '../backend-contract/scan-planning.js'; import type { BleSecurity } from './security.js'; import type { Limitation } from '../backend-contract/capabilities.js'; import { type ConnectionPriority } from '../backend-contract/connection-controls.js'; import type { CleanupRecord as PublicCleanupRecord } from './cleanup.js'; import { type PublicBoundedAsyncStream } from './streams.js'; export type { ConnectionPriority } from '../backend-contract/connection-controls.js'; type PublicInternalManager> = InternalBleManager; export type GattSubscriptionValue = GattValueEvent; export type ConnectionIntent = 'direct' | 'when-available'; /** * Out-of-band address entry form for `connect()` (NFC, QR codes, persisted state) minted * without a prior scan. Address targeting only works for peers using public/static * addresses; devices using resolvable private addresses need the durable `PeerReference` * form instead. Requires the `peer:address-targeting` capability and fails closed with * `capability.unsupported` on backends that do not implement it. */ export interface PeerAddress { readonly address: string; /** Defaults to 'public'. */ readonly addressType?: 'public' | 'random'; } export interface ConnectOptions extends OperationOptions { readonly intent?: ConnectionIntent; readonly transport?: 'le' | 'auto'; readonly preferredPhy?: readonly BlePhy[]; } export interface BleConnectionEvent { readonly kind: 'connection-lifecycle'; readonly previous: ConnectionLifecycleEvent['previous']; readonly current: ConnectionLifecycleEvent['current']; readonly cause: ConnectionLifecycleCause; readonly connectionGeneration: string; readonly sequence: number; } export type BleControlObservationState = 'measured' | 'unavailable' | 'unsupported'; export type BleObservationSource = 'backend' | 'platform' | 'core' | 'unknown'; export interface BleControlObservationMetadata { readonly connectionGeneration: string; readonly observedAtMonotonicMs: number; readonly source: BleObservationSource; readonly authority: string; readonly limitations: readonly Limitation[]; } export interface RssiObservation extends BleControlObservationMetadata { readonly state: BleControlObservationState; readonly rssi: number | null; } export interface MtuObservation extends BleControlObservationMetadata { readonly state: BleControlObservationState; readonly attMtu: number | null; readonly payloadBytes: number | null; readonly platformPduBytes: number | null; } export type MtuNegotiationState = 'accepted' | 'rejected' | 'unavailable' | 'unsupported'; export interface MtuNegotiation extends BleControlObservationMetadata { readonly state: MtuNegotiationState; readonly requestedMtu: number; readonly observation: MtuObservation | null; } export type BlePhy = 'le-1m' | 'le-2m' | 'le-coded'; export type PhyPreference = Readonly<{ readonly tx?: BlePhy; readonly rx?: BlePhy; }>; export type SubrateMode = 'default' | 'low-latency' | 'low-power'; export type WriteMode = 'with-response' | 'without-response'; export interface MaximumWriteLengthObservation extends BleControlObservationMetadata { readonly state: BleControlObservationState; readonly mode: WriteMode; readonly maximumWriteLength: number | null; } export interface ConnectionPriorityResult extends BleControlObservationMetadata { readonly state: 'accepted' | 'rejected' | 'unavailable' | 'unsupported'; readonly requested: ConnectionPriority; } export interface PhyObservation extends BleControlObservationMetadata { readonly state: BleControlObservationState; readonly tx: BlePhy | null; readonly rx: BlePhy | null; } export interface PhyUpdateResult extends BleControlObservationMetadata { readonly state: 'accepted' | 'rejected' | 'unavailable' | 'unsupported'; readonly requested: PhyPreference; readonly observation: PhyObservation | null; } export interface ConnectionParametersObservation extends BleControlObservationMetadata { readonly state: BleControlObservationState; readonly intervalMs: number | null; readonly peripheralLatency: number | null; readonly supervisionTimeoutMs: number | null; readonly subrateFactor: number | null; readonly connectionEventLengthMs: number | null; } export interface SubrateResult extends BleControlObservationMetadata { readonly state: 'accepted' | 'rejected' | 'unavailable' | 'unsupported'; readonly requested: SubrateMode; readonly observation: ConnectionParametersObservation | null; } export interface WriteReadinessEvent extends BleControlObservationMetadata { readonly state: BleControlObservationState; readonly mode: 'without-response'; readonly ready: boolean | null; } export interface BleConnectionControls { readRssi(options?: OperationOptions): Promise; effectiveMtu(): Promise; requestMtu(mtu: number, options?: OperationOptions): Promise; maximumWriteLength(mode: WriteMode): Promise; requestPriority(priority: ConnectionPriority, options?: OperationOptions): Promise; readPhy(options?: OperationOptions): Promise; requestPhy(preference: PhyPreference, options?: OperationOptions): Promise; parameters(): Promise; parameterEvents(): AsyncIterable; requestSubrate(mode: SubrateMode, options?: OperationOptions): Promise; writeReadiness(mode: 'without-response'): AsyncIterable; } export interface RediscoverGattOptions extends OperationOptions { readonly reason: 'service-changed' | 'manual'; } export type { GattDatabase, GattDatabaseSnapshot, GattService, GattCharacteristic, GattDescriptor, GattSubscription, GattValueEvent, GattValueStream, GattDatabaseChangedEvent, GattWriteReceipt, GattLongWriteReceipt, GattCharacteristicProperties, GattAccessRequirements, GattServiceReference, GattWriteOptions, LongWriteOptions, DescriptorWriteOptions, GattSubscribeOptions, OccurrenceSelector, GattPathSelector, UuidInput } from './gatt.js'; export type { ManufacturerDataPattern, NormalizedManufacturerDataPattern, NormalizedScanClause, NormalizedScanObservation, NormalizedScanQuery, NormalizedServiceDataPattern, ScanClause, ScanQuery, ServiceDataPattern } from './scan-query.js'; export type { BlePeerDirectory, BlePeerState, KnownPeerQuery, PeerSource } from './peer-directory.js'; export type { PeerReference, PeerReferenceScope } from './peer-reference.js'; export type { BleSecurity, PairCancelResult, PairingAgent, PairingChallenge, PairingResponse, PairOptions, RequiredSecurityOptions, PairResult, PeerSecurityEvent, PeerSecurityState, SecurityAuthenticationState, SecurityBondState, SecurityEncryptionState, SecureConnectionsState, SecurityPeer, UnpairResult, SecurityRequirement } from './security.js'; export interface BlePeer { readonly id: string; readonly name: string | null; readonly rssi: number | null; readonly reference: PeerReference | null; readonly sources: readonly PeerSource[]; readonly lastAdvertisement: NormalizedScanObservation | null; readonly state?: BlePeerState; } type BlePeerInput = Pick & Partial>; export declare function snapshotBlePeer(peer: BlePeerInput): BlePeer; export interface BleConnection { readonly peer: BlePeer; readonly connectionGeneration: string; readonly lifecycleEvents: AsyncIterable; readonly controls: BleConnectionControls; readonly discover: (options?: OperationOptions) => Promise; readonly rediscoverGatt: (options: RediscoverGattOptions) => Promise; readonly disconnect: () => Promise; readonly release: () => Promise; } export interface PublicScanObservation extends NormalizedScanObservation { readonly peer: BlePeer; readonly observedAtMonotonicMs: number | null; } export type DiscoveryEvent = { readonly kind: 'observed'; readonly peer: BlePeer; } | { readonly kind: 'lost'; readonly peer: BlePeer; readonly lastObservedAt: number; readonly derivedAt: number; readonly reason: 'observation-timeout'; } | { readonly kind: 'presence-tracking-overflow'; readonly guarantee: 'reportLostAfterMs-completeness'; readonly droppedEntries: number; readonly droppedBytes: number; }; export type AndroidScanMode = 'low-power' | 'balanced' | 'low-latency' | 'opportunistic'; export type AndroidScanCallbackType = 'all-matches' | 'first-match' | 'match-lost'; export type AndroidScanPhy = 'all-supported' | '1m' | 'coded'; export interface AndroidScanPlatformOptions { readonly kind: 'android'; readonly mode?: AndroidScanMode; readonly callbackType?: AndroidScanCallbackType; readonly reportDelayMs?: number; readonly legacy?: boolean; readonly phy?: AndroidScanPhy; } export type ScanPlatformOptions = AndroidScanPlatformOptions | { readonly kind: 'corebluetooth'; } | { readonly kind: 'winrt'; } | { readonly kind: 'web'; } | { readonly kind: 'electron'; } | { readonly kind: 'tauri'; }; export interface ScanSession { readonly plan: ScanPlan | null; readonly stop: () => Promise; readonly observations: PublicBoundedAsyncStream; readonly events?: AsyncIterable; readonly state: AsyncIterable; } export interface PublicScanFingerprintAccounting { readonly fingerprintCount: number; readonly fingerprintBytes: number; readonly summedEntryBytes: number; } export declare function inspectPublicScanFingerprintAccountingForTests(session: ScanSession): PublicScanFingerprintAccounting; /** * Public scan session lifecycle. * * `active` means the session is still accepting source advertisements. * Host/source terminals project out of `active` even with no iterator: * `source-failed`/`connection-lost`/`overflow` become `failed`, ordinary close * becomes `stopped`. An already-terminal source publishes that projected * terminal as the initial state and never `active`. Drop-policy overflow * notices keep the session `active` and the radio up. Subscriber overflow that * fail-closes a consumed view (`overflowPolicy: 'error'`) is `failed`/`overflow`; * physical `stop()` remains cleanup and reports through its `CleanupRecord`. */ export type ScanStateEvent = { readonly state: 'starting' | 'active' | 'stopping' | 'stopped' | 'failed'; readonly reason?: string; }; export interface BleManager { readonly capabilities: BleCapabilities; readonly adapter: BleAdapter; readonly diagnostics: BleDiagnostics; readonly peers: BlePeerDirectory; readonly security: BleSecurity; readonly discovery: BleDiscoveryInfo; readonly destroy: () => Promise; scan(options?: ScanOptions): Promise; find(options?: FindOptions): Promise; choose(options?: ChooseOptions): Promise; connect(peer: BlePeer | string | PeerReference | PeerAddress, options?: ConnectOptions): Promise; withConnection(peer: BlePeer | string | PeerReference | PeerAddress, options: ConnectOptions, action: (connection: BleConnection) => Promise): Promise; withScan(options: ScanOptions, action: (scan: ScanSession) => Promise): Promise; withDiscoveredConnection(peer: BlePeer | string | PeerReference | PeerAddress, options: ConnectOptions, action: (scope: { readonly connection: BleConnection; readonly gatt: GattDatabase; }) => Promise): Promise; } export { PublicBleManager as BleManagerImpl }; export interface ScanOptions extends OperationOptions { readonly query?: ScanQuery; readonly duplicates?: 'coalesced' | 'all'; readonly delivery?: StreamPolicy; readonly observation?: { readonly reportLostAfterMs?: number; readonly includeRawAdvertisement?: boolean; }; readonly platform?: ScanPlatformOptions; } /** * Fallback deadline for `find()` when the caller supplies no `timeoutMs`. * * Host policy, not an invariant: it exists only so a convenience call cannot * scan indefinitely. Shared with the IPC/renderer adapter so the same logical * operation is not governed by two independently drifting numbers. */ export declare const DEFAULT_FIND_TIMEOUT_MS = 10000; /** * Fallback deadline for `adapter.waitUntilReady()` when the caller supplies no * `timeoutMs`. * * Host policy, not an invariant: a caller-supplied deadline always wins. The * fallback exists only so a readiness wait cannot hang forever on a host whose * adapter never reports a usable state. Shared with the IPC/renderer adapter so * the same logical wait does not expire at two different times either side of * the IPC boundary. */ export declare const DEFAULT_ADAPTER_READINESS_TIMEOUT_MS = 10000; /** * Options for the one-shot `find()` convenience over `scan()`. * * `find()` owns the scan session it opens, so the observation-stream policy is * host policy rather than a package invariant: a peripheral that advertises in * dense bursts overflows a small observation budget on one host and never comes * close on another. Every field below is optional and keeps the historical * default, so `find()` behaves exactly as before when nothing is supplied. */ export interface FindOptions extends OperationOptions { /** `OperationOptions.timeoutMs` defaults to 10 seconds when omitted. */ readonly query?: ScanQuery; readonly select?: 'first' | ((peer: BlePeer) => boolean); /** Defaults to `'coalesced'`; `'all'` keeps every report for selectors that inspect advertisement churn. */ readonly duplicates?: 'coalesced' | 'all'; /** * Observation-stream budget for the scan `find()` opens. Defaults to `'latest'` * (a one-item drop-oldest window), which is the smallest useful budget and the * one most easily overflowed by a chatty peripheral; raise it with `'balanced'` * or a custom budget when advertisement bursts are expected. */ readonly delivery?: StreamPolicy; readonly platform?: ScanPlatformOptions; } export interface ChooseOptions extends OperationOptions { readonly filters?: readonly ChooseFilter[]; readonly optionalServices?: readonly (string | number)[]; readonly acceptAllDevices?: boolean; } export interface ChooseFilter { readonly serviceUuids?: readonly (string | number)[]; readonly manufacturerData?: readonly { readonly companyIdentifier: number; readonly dataPrefix?: Readonly; }[]; readonly localNamePrefix?: string; } export interface BleDiscoveryInfo { readonly kind: 'continuous-scan' | 'system-chooser' | 'hybrid'; } export interface PublicBleManagerHostOptions { readonly discoveryKind?: BleDiscoveryInfo['kind']; readonly choose?: (options: ChooseOptions) => Promise; readonly peers?: BlePeerDirectory; /** Explicit test seam for lightweight manager doubles without backend identity. */ readonly peerId?: (value: string) => PeerId; } export declare function createPublicBleManager>(internal: PublicInternalManager, now: () => number, hostOptions?: PublicBleManagerHostOptions): Promise; declare class PublicBleManager> implements BleManager { private readonly internal; private readonly now; readonly capabilities: BleCapabilities; readonly adapter: BleAdapter; readonly diagnostics: BleDiagnostics; readonly peers: BlePeerDirectory; readonly security: BleSecurity; private readonly peerIdAuthority; private readonly activeScanSessions; private destroyPromise; constructor(internal: PublicInternalManager, now: () => number, hostOptions: PublicBleManagerHostOptions); readonly discovery: BleDiscoveryInfo; private readonly chooseImpl; scan(options?: ScanOptions): Promise; find(options?: FindOptions): Promise; choose(options?: ChooseOptions): Promise; connect(peer: BlePeer | string | PeerReference | PeerAddress, options?: ConnectOptions): Promise; withConnection(peer: BlePeer | string | PeerReference | PeerAddress, options: ConnectOptions, action: (connection: BleConnection) => Promise): Promise; withScan(options: ScanOptions, action: (scan: ScanSession) => Promise): Promise; withDiscoveredConnection(peer: BlePeer | string | PeerReference | PeerAddress, options: ConnectOptions, action: (scope: { readonly connection: BleConnection; readonly gatt: GattDatabase; }) => Promise): Promise; destroy(): Promise; private destroyInternal; } export type { BleManagerOptions }; export declare function filterScanObservations(source: BoundedAsyncStream | IpcAdvertisement>, query: ReturnType, duplicates?: 'coalesced' | 'all'): BoundedAsyncStream; export declare function publicConnectionEvents(source: BoundedAsyncStream>): AsyncIterable; export declare function broadcastConnectionEvents(source: AsyncIterable): AsyncIterable; export declare function connectionEventsEndedExpectedly(iterable: AsyncIterable): boolean; export declare function publicConnectionTerminalError(reason: StreamTerminalNotice['reason']): Error; export declare function peerFromPublicObservation(observation: PublicScanObservation | AdvertisementObservation | IpcAdvertisement): BlePeer; export declare function isPeerAddressTarget(value: unknown): value is PeerAddress; export declare function assertPublicScanOptions(options: ScanOptions): void; export declare function assertPublicConnectOptions(options: ConnectOptions): void; export declare function assertPublicChooseOptions(options: ChooseOptions): void; export declare function findPeerInScan(scan: ScanSession, select: FindOptions['select'], operation?: { readonly signal: AbortSignal | null; readonly deadline: number | null; readonly now: () => number; } | null): Promise; //# sourceMappingURL=ble-manager.d.ts.map