{"version":3,"sources":["../src/write-chunker.ts","../src/notification-manager.ts","../src/device.ts","../src/types.ts","../src/beacio.ts","../src/units.ts"],"names":["DEFAULT_CHUNK_SIZE","chunkSize","n","BeacioError","clampChunkSize","fallback","WriteChunker","deps","service","characteristic","value","options","timeoutMs","validateTimeoutMs","char","writeToken","e","bytes","totalBytes","step","deriveChunkSizeFromMtu","deriveChunkSize","maxRetries","retryDelayMs","bytesWritten","chunkCount","retryCount","offset","nextOffset","chunk","attempt","error","derivedChunkSize","safeChunk","transportInfo","limits","mtu","payload","limit","abortInFlightWrites","entry","explicitChunkSize","mode","preferred","ms","resolve","_NotificationManager","notificationStates","handleNotification","event","key","notificationState","charCache","cb","emitError","callback","unsubscribe","ready","registerNotificationConsumer","normalizedError","listenerError","autoRecover","addToRecoveryRegistry","originalUnsubscribe","removeFromRecoveryRegistry","release","maxQueueSize","validateMaxQueueSize","overflowStrategy","queue","droppedCount","state","r","overflowEvent","emitQueueOverflow","overflowError","reject","_unsubscribe","result","gate","getReconnectGate","pending","teardownSubscriptions","operation","detachNotificationListener","stopNotificationsSafely","entries","recoveryRegistry","recoveredError","emitSubscriptionLost","charKey","currentState","syncNotificationState","serviceKey","settled","deactivateNotificationState","deleteNotificationStateIfIdle","context","resolveUUID","NotificationManager","isUndeclaredServiceSecurityError","msg","undeclaredServiceError","uuid","BeacioDevice","device","hooks","primaryServicesCache","serviceCache","disconnectListeners","reconnectedListeners","queueOverflowListeners","subscriptionLostListeners","errorListeners","reconnectGate","lastDisconnectReason","autoReconnectConfig","autoReconnectAbort","writeChunker","message","notificationManager","handleDisconnect","gatt","fn","DEFAULT_RETRY_OPTIONS","withRetry","parseOrOptions","maybeOptions","parse","listener","detach","cancelled","rawDevice","normalizedServices","cached","getLastDisconnectReason","recoveryEntry","reason","gateResolve","gatePromise","config","controller","maxAttempts","initialDelay","maxDelay","multiplier","delay","timer","timeoutId","timeoutPromise","_","listeners","svc","charUUID","discoveredService","DEFAULT_BEACIO_OPTIONS","UnsupportedBackgroundSync","errorFactory","_options","_registrationId","_template","UnsupportedPeripheral","_service","Beacio","detectPlatform","normalizeMaxConnections","getBluetoothAPI","unsupportedFeatureErrorFactory","normalizeRequestDeviceOptions","wrapDevice","uuids","bluetoothWithGetDevices","bluetoothWithScan","effectiveOptionalServices","mergeOptionalServices","normalizeServices","services","normalized","filter","callerServices","merged","maxConnections","existing","wrapped","nextDevice","assertConnectionCapacity","connectedCount","percent","clampPercent"],"mappings":"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","file":"chunk-DNT37NCJ.mjs","sourcesContent":["import { BeacioError } from './errors';\nimport type {\n  DeviceErrorContext,\n  WriteAutoOptions,\n  WriteAutoResult,\n  WriteFragmentedOptions,\n  WriteFragmentedResult,\n  WriteLargeOptions,\n  WriteLargeResult,\n  WriteLimits,\n  WriteOptions,\n} from './types';\n\n// AIDEV-NOTE: Transport metadata is exposed by the Safari Beacio extension via\n// the GATT server object; standard browsers leave these methods undefined.\ntype DeviceTransportInfo = {\n  getMtu?: () => Promise<number | null>;\n  getWriteLimits?: () => Promise<Partial<WriteLimits> | null | undefined>;\n};\n\ntype Transport = (BluetoothRemoteGATTServer & DeviceTransportInfo) | null;\n\ndeclare const chunkSizeBrand: unique symbol;\n\n/**\n * A validated, strictly-positive integer chunk size in bytes.\n *\n * Nominal/branded: the brand can only be attached by {@link chunkSize} or\n * {@link clampChunkSize}, both of which guarantee `value >= 1`. Typing a chunk\n * loop's stride as `ChunkSize` makes a `0` (or negative) increment\n * unrepresentable at `offset += step`, eliminating the zero-stride infinite\n * loop at the type level — not just by runtime check.\n */\nexport type ChunkSize = number & { readonly [chunkSizeBrand]: true };\n\n/** Conservative default chunk payload: the classic 23-byte ATT MTU minus the 3-byte ATT header. */\nconst DEFAULT_CHUNK_SIZE = 20;\n\n/**\n * Strict smart-constructor for {@link ChunkSize}. Throws `INVALID_PARAMETER`\n * on a non-integer or non-positive value. Use when the caller supplied an\n * explicit size that must be rejected (not silently corrected) if invalid.\n */\nexport function chunkSize(n: number): ChunkSize {\n  if (!Number.isInteger(n) || n <= 0) {\n    throw new BeacioError('INVALID_PARAMETER', `Invalid chunkSize: ${n}. Must be a positive integer.`);\n  }\n  return n as ChunkSize;\n}\n\n/**\n * Lenient smart-constructor for {@link ChunkSize}. Coerces any\n * `null`/`undefined`/`0`/negative/`NaN`/non-integer input to a positive\n * `fallback` (default {@link DEFAULT_CHUNK_SIZE}). Never returns `<= 0`.\n *\n * This is the single clamp for platform-reported limits, where a literal `0`\n * (a valid `number | null` per {@link WriteLimits}) must be treated as \"no\n * usable limit\" rather than a zero-length stride.\n */\nexport function clampChunkSize(n: number | null | undefined, fallback: number = DEFAULT_CHUNK_SIZE): ChunkSize {\n  return Number.isInteger(n) && (n as number) > 0 ? (n as ChunkSize) : chunkSize(fallback);\n}\n\n/**\n * Dependencies injected by {@link BeacioDevice} so the chunker stays free of any\n * direct BLE state. The device owns connection/characteristic resolution and the\n * timeout primitives; the chunker owns write fragmentation and in-flight tracking.\n */\nexport type WriteChunkerDeps = {\n  getCharacteristic: (service: string, characteristic: string) => Promise<BluetoothRemoteGATTCharacteristic>;\n  emitError: (error: Error, context: DeviceErrorContext) => void;\n  validateTimeoutMs: (timeoutMs: number | undefined) => number | undefined;\n  withOptionalTimeout: <T>(operation: Promise<T>, timeoutMs: number | undefined, message: string) => Promise<T>;\n  isConnected: () => boolean;\n  getTransport: () => Transport;\n};\n\n/**\n * Owns single-packet and fragmented writes, platform write-limit discovery, and\n * the in-flight write registry used to fail outstanding writes with\n * `WRITE_INCOMPLETE` when the device disconnects mid-transfer.\n *\n * AIDEV-NOTE: Extracted from device.ts (cleanup item 144). Behavior is\n * byte-identical to the former BeacioDevice methods; BeacioDevice delegates to\n * this class and wires {@link abortInFlightWrites} into its disconnect handler.\n */\nexport class WriteChunker {\n  private inFlightWrites = new Map<symbol, { service: string; characteristic: string; aborted: boolean }>();\n\n  constructor(private readonly deps: WriteChunkerDeps) {}\n\n  async write(service: string, characteristic: string, value: BufferSource, options?: WriteOptions): Promise<void> {\n    const timeoutMs = this.deps.validateTimeoutMs(options?.timeoutMs);\n    const char = await this.deps.getCharacteristic(service, characteristic);\n    const writeToken = Symbol(`write:${service}:${characteristic}`);\n    this.inFlightWrites.set(writeToken, { service, characteristic, aborted: false });\n\n    try {\n      if (options?.mode === 'without-response') {\n        await this.deps.withOptionalTimeout(\n          char.writeValueWithoutResponse(value),\n          timeoutMs,\n          'Write without response timed out',\n        );\n        return;\n      }\n\n      await this.deps.withOptionalTimeout(\n        char.writeValueWithResponse(value),\n        timeoutMs,\n        'Write with response timed out',\n      );\n    } catch (e) {\n      const tracked = this.inFlightWrites.get(writeToken);\n      if (tracked?.aborted) {\n        throw new BeacioError(\n          'WRITE_INCOMPLETE',\n          `Write incomplete for ${service}/${characteristic}: disconnected before completion`,\n          { retryAfterMs: 1000 },\n        );\n      }\n      throw BeacioError.from(e);\n    } finally {\n      this.inFlightWrites.delete(writeToken);\n    }\n  }\n\n  async writeFragmented(\n    service: string,\n    characteristic: string,\n    value: BufferSource,\n    options?: WriteFragmentedOptions,\n  ): Promise<WriteFragmentedResult> {\n    const bytes = this.toUint8Array(value);\n    const totalBytes = bytes.byteLength;\n    if (totalBytes === 0) {\n      return { bytesWritten: 0, totalBytes: 0, chunkSize: 0, chunkCount: 0, retryCount: 0 };\n    }\n\n    const step: ChunkSize = options?.chunkSize !== undefined\n      ? chunkSize(options.chunkSize)\n      : this.deriveChunkSizeFromMtu(options?.mtu)\n        ?? await this.deriveChunkSize(undefined, options?.mode);\n    const maxRetries = options?.maxRetries ?? 0;\n    const retryDelayMs = options?.retryDelayMs ?? 0;\n\n    let bytesWritten = 0;\n    let chunkCount = 0;\n    let retryCount = 0;\n\n    for (let offset = 0; offset < totalBytes; offset += step) {\n      const nextOffset = Math.min(offset + step, totalBytes);\n      const chunk = new Uint8Array(bytes.subarray(offset, nextOffset));\n      let attempt = 0;\n\n      while (true) {\n        try {\n          await this.write(service, characteristic, chunk, options);\n          bytesWritten += chunk.byteLength;\n          chunkCount += 1;\n          break;\n        } catch (error) {\n          if (attempt >= maxRetries) {\n            if (bytesWritten > 0 && bytesWritten < totalBytes) {\n              throw new BeacioError(\n                'WRITE_INCOMPLETE',\n                `Write fragmented incomplete (${bytesWritten}/${totalBytes} bytes written): ${this.errorMessage(error instanceof Error ? error : String(error))}`,\n                { retryAfterMs: 1000 },\n              );\n            }\n            throw BeacioError.from(error);\n          }\n          attempt += 1;\n          retryCount += 1;\n          if (retryDelayMs > 0) {\n            await this.delay(retryDelayMs);\n          }\n        }\n      }\n    }\n\n    return { bytesWritten, totalBytes, chunkSize: step, chunkCount, retryCount };\n  }\n\n  async writeLarge(\n    service: string,\n    characteristic: string,\n    value: BufferSource,\n    options?: WriteLargeOptions,\n  ): Promise<WriteLargeResult> {\n    const bytes = this.toUint8Array(value);\n    const totalBytes = bytes.byteLength;\n\n    if (totalBytes === 0) {\n      return { bytesWritten: 0, totalBytes: 0, chunkSize: 0, chunkCount: 0 };\n    }\n\n    const derivedChunkSize = await this.deriveChunkSize(options?.chunkSize, options?.mode);\n    let bytesWritten = 0;\n    let chunkCount = 0;\n\n    for (let offset = 0; offset < totalBytes; offset += derivedChunkSize) {\n      const nextOffset = Math.min(offset + derivedChunkSize, totalBytes);\n      const chunk = bytes.subarray(offset, nextOffset);\n      const safeChunk = new Uint8Array(chunk);\n\n      try {\n        await this.write(service, characteristic, safeChunk, options);\n        bytesWritten += chunk.byteLength;\n        chunkCount += 1;\n      } catch (error) {\n        if (bytesWritten > 0 && bytesWritten < totalBytes) {\n          throw new BeacioError(\n            'WRITE_INCOMPLETE',\n            `Write incomplete (${bytesWritten}/${totalBytes} bytes written): ${this.errorMessage(error instanceof Error ? error : String(error))}`,\n          );\n        }\n        throw BeacioError.from(error);\n      }\n    }\n\n    if (bytesWritten !== totalBytes) {\n      throw new BeacioError('WRITE_INCOMPLETE', `Write incomplete (${bytesWritten}/${totalBytes} bytes written)`);\n    }\n\n    return {\n      bytesWritten,\n      totalBytes,\n      chunkSize: derivedChunkSize,\n      chunkCount,\n    };\n  }\n\n  async writeWithoutResponse(service: string, characteristic: string, value: BufferSource, options?: Omit<WriteOptions, 'mode'>): Promise<void> {\n    return this.write(service, characteristic, value, { ...options, mode: 'without-response' });\n  }\n\n  async getWriteLimits(): Promise<WriteLimits> {\n    if (!this.deps.isConnected()) throw new BeacioError('DEVICE_DISCONNECTED');\n\n    const transportInfo = this.deps.getTransport();\n    const limits = await transportInfo?.getWriteLimits?.();\n    const mtu = limits?.mtu ?? await transportInfo?.getMtu?.() ?? null;\n\n    return {\n      withResponse: limits?.withResponse ?? null,\n      withoutResponse: limits?.withoutResponse ?? null,\n      mtu,\n    };\n  }\n\n  async writeAuto(\n    service: string,\n    characteristic: string,\n    value: BufferSource,\n    options?: WriteAutoOptions,\n  ): Promise<WriteAutoResult> {\n    const bytes = this.toUint8Array(value);\n    const totalBytes = bytes.byteLength;\n    const payload = new Uint8Array(bytes);\n\n    if (totalBytes === 0) {\n      await this.write(service, characteristic, payload, options);\n      return {\n        bytesWritten: 0,\n        totalBytes: 0,\n        chunkSize: 0,\n        chunkCount: 0,\n        retryCount: 0,\n        fragmented: false,\n      };\n    }\n\n    const limit = await this.deriveChunkSize(options?.chunkSize, options?.mode);\n    if (totalBytes <= limit) {\n      await this.write(service, characteristic, payload, options);\n      return {\n        bytesWritten: totalBytes,\n        totalBytes,\n        chunkSize: totalBytes,\n        chunkCount: 1,\n        retryCount: 0,\n        fragmented: false,\n      };\n    }\n\n    const result = await this.writeFragmented(service, characteristic, payload, options);\n    return {\n      ...result,\n      fragmented: true,\n    };\n  }\n\n  // AIDEV-NOTE: Called from BeacioDevice.handleDisconnect()/disconnect() so any\n  // outstanding write rejects with WRITE_INCOMPLETE instead of a raw GATT error.\n  abortInFlightWrites(): void {\n    for (const entry of this.inFlightWrites.values()) {\n      entry.aborted = true;\n    }\n  }\n\n  private async deriveChunkSize(explicitChunkSize: number | undefined, mode: WriteOptions['mode']): Promise<ChunkSize> {\n    if (explicitChunkSize !== undefined) {\n      return chunkSize(explicitChunkSize);\n    }\n\n    const limits = await this.getWriteLimits().catch(() => ({ withResponse: null, withoutResponse: null, mtu: null }));\n    const preferred = mode === 'without-response' ? limits.withoutResponse : limits.withResponse;\n    if (typeof preferred === 'number' && preferred > 0) return chunkSize(preferred);\n    if (typeof limits.mtu === 'number' && limits.mtu > 3) return chunkSize(limits.mtu - 3);\n\n    // Conservative fallback for platforms that do not expose limits.\n    return chunkSize(DEFAULT_CHUNK_SIZE);\n  }\n\n  private deriveChunkSizeFromMtu(mtu: number | undefined): ChunkSize | null {\n    if (mtu === undefined) return null;\n    if (!Number.isInteger(mtu) || mtu <= 3) {\n      throw new BeacioError('INVALID_PARAMETER', `Invalid mtu: ${mtu}. Must be an integer greater than 3.`);\n    }\n    return chunkSize(mtu - 3);\n  }\n\n  private delay(ms: number): Promise<void> {\n    return new Promise((resolve) => { setTimeout(resolve, ms); });\n  }\n\n  private toUint8Array(value: BufferSource): Uint8Array {\n    if (value instanceof ArrayBuffer) return new Uint8Array(value);\n    return new Uint8Array(value.buffer, value.byteOffset, value.byteLength);\n  }\n\n  private errorMessage(error: Error | string): string {\n    if (error instanceof Error) return error.message;\n    return String(error);\n  }\n}\n","import { BeacioError } from './errors';\nimport { resolveUUID } from './uuid';\nimport type {\n  DeviceErrorContext,\n  NotificationCallback,\n  NotificationOptions,\n  QueueOverflowEvent,\n  SubscribeOptions,\n  SubscriptionLostEvent,\n} from './types';\n\n// AIDEV-NOTE: Runtime, per-characteristic notification state. `reconcilePromise`\n// is the serialization chain that keeps subscribe()/notifications() lifecycles\n// from diverging during async start/stop races.\nexport type NotificationState = {\n  callbacks: Set<NotificationCallback>;\n  characteristic: BluetoothRemoteGATTCharacteristic | null;\n  listenerAttached: boolean;\n  nativeActive: boolean;\n  reconcilePromise: Promise<void> | null;\n};\n\n// AIDEV-NOTE: Auto-recovery registry entry. Owned by BeacioDevice and passed to\n// the manager BY REFERENCE — the device must keep reading/clearing it across\n// connect()/disconnect(), so both objects share the same Map instance.\nexport type RecoveryEntry = { service: string; characteristic: string; callbacks: Set<NotificationCallback> };\n\ntype ReconnectGate = { promise: Promise<void>; resolve: () => void };\n\n/**\n * Dependencies injected by {@link BeacioDevice}. The device retains ownership of\n * the GATT connection, the recovery registry, the characteristic cache, and the\n * reconnect-gate lifecycle; the manager reaches into those via these bindings so\n * the gate's create-before-fire / resolve-in-finally timing is unchanged.\n */\nexport type NotificationManagerDeps = {\n  getCharacteristic: (service: string, characteristic: string) => Promise<BluetoothRemoteGATTCharacteristic>;\n  emitError: (error: Error, context: DeviceErrorContext) => void;\n  emitSubscriptionLost: (event: SubscriptionLostEvent) => void;\n  emitQueueOverflow: (event: QueueOverflowEvent) => void;\n  // Shared by reference with BeacioDevice.\n  recoveryRegistry: Map<string, RecoveryEntry>;\n  charCache: Map<string, BluetoothRemoteGATTCharacteristic>;\n  // Live reads of the device's reconnect-gate / disconnect intent.\n  getReconnectGate: () => ReconnectGate | null;\n  isIntentionalDisconnect: () => boolean;\n};\n\n/**\n * Owns the runtime notification subscription lifecycle: native start/stop\n * reconciliation, listener fan-out, the callback / async-iterator surfaces, and\n * auto-recovery after reconnect.\n *\n * AIDEV-NOTE: Extracted from device.ts (cleanup item 144). Behavior is\n * byte-identical to the former BeacioDevice methods. The reconcilePromise\n * serialization chain and reconnect-gate await semantics are preserved exactly.\n */\nexport class NotificationManager {\n  // AIDEV-NOTE: PERF — Keep this low enough to avoid hidden memory growth while\n  // still absorbing short consumer stalls without spurious overflow failures.\n  static readonly DEFAULT_NOTIFICATION_QUEUE_SIZE = 256;\n\n  private notificationStates = new Map<string, NotificationState>();\n\n  constructor(private readonly deps: NotificationManagerDeps) {}\n\n  /** Read-only access for BeacioDevice.getActiveSubscriptions(). */\n  getNotificationStates(): ReadonlyMap<string, NotificationState> {\n    return this.notificationStates;\n  }\n\n  subscribe(service: string, characteristic: string, callback: NotificationCallback, options?: SubscribeOptions): () => void {\n    const { unsubscribe, ready } = this.registerNotificationConsumer(service, characteristic, callback);\n    void ready.catch((error) => {\n      const normalizedError = BeacioError.from(error);\n      try {\n        options?.onError?.(normalizedError);\n      } catch (listenerError) {\n        this.deps.emitError(BeacioError.from(listenerError), {\n          operation: 'device.subscribe.onError',\n          service,\n          characteristic,\n        });\n      }\n    });\n\n    const autoRecover = options?.autoRecover ?? true;\n\n    if (autoRecover) {\n      this.addToRecoveryRegistry(this.charKey(service, characteristic), service, characteristic, callback);\n    }\n\n    const originalUnsubscribe = unsubscribe;\n    return () => {\n      originalUnsubscribe();\n      if (autoRecover) {\n        this.removeFromRecoveryRegistry(this.charKey(service, characteristic), callback);\n      }\n    };\n  }\n\n  async subscribeAsync(\n    service: string,\n    characteristic: string,\n    callback: NotificationCallback,\n    options?: SubscribeOptions,\n  ): Promise<() => void> {\n    const { unsubscribe, release, ready } = this.registerNotificationConsumer(service, characteristic, callback);\n    const autoRecover = options?.autoRecover ?? true;\n\n    if (autoRecover) {\n      this.addToRecoveryRegistry(this.charKey(service, characteristic), service, characteristic, callback);\n    }\n\n    try {\n      await ready;\n    } catch (error) {\n      const normalizedError = BeacioError.from(error);\n      // AIDEV-NOTE: Unlike sync subscribe(), subscribeAsync surfaces errors via throw only.\n      // Calling onError here would double-report since the caller already gets the rejection.\n\n      await release();\n      if (autoRecover) {\n        this.removeFromRecoveryRegistry(this.charKey(service, characteristic), callback);\n      }\n      throw normalizedError;\n    }\n\n    return () => {\n      unsubscribe();\n      if (autoRecover) {\n        this.removeFromRecoveryRegistry(this.charKey(service, characteristic), callback);\n      }\n    };\n  }\n\n  async *notifications(service: string, characteristic: string, options: NotificationOptions = { maxQueueSize: NotificationManager.DEFAULT_NOTIFICATION_QUEUE_SIZE }): AsyncIterable<DataView> {\n    const maxQueueSize = this.validateMaxQueueSize(\n      options.maxQueueSize ?? NotificationManager.DEFAULT_NOTIFICATION_QUEUE_SIZE,\n    );\n    const overflowStrategy = options?.overflowStrategy ?? 'error';\n    const queue: DataView[] = [];\n    let droppedCount = 0;\n    type Resolver = (v: IteratorResult<DataView>) => void;\n    type Rejecter = (reason?: Error) => void;\n    const state: { resolve: Resolver | null; reject: Rejecter | null; done: boolean; failure: Error | null } = {\n      resolve: null,\n      reject: null,\n      done: false,\n      failure: null,\n    };\n\n    const callback: NotificationCallback = (value) => {\n      if (state.failure) return;\n\n      if (state.resolve) {\n        const r = state.resolve;\n        state.resolve = null;\n        state.reject = null;\n        r({ value, done: false });\n      } else {\n        if (queue.length >= maxQueueSize) {\n          droppedCount += 1;\n          const overflowEvent: QueueOverflowEvent = {\n            service,\n            characteristic,\n            strategy: overflowStrategy,\n            queueSize: maxQueueSize,\n            droppedCount,\n          };\n          this.deps.emitQueueOverflow(overflowEvent);\n\n          try {\n            options?.onOverflow?.(overflowEvent);\n          } catch (error) {\n            this.deps.emitError(BeacioError.from(error), {\n              operation: 'device.notifications.onOverflow',\n              service,\n              characteristic,\n            });\n          }\n\n          if (overflowStrategy === 'error') {\n            const overflowError = new BeacioError(\n              'GATT_OPERATION_FAILED',\n              `Notification queue overflowed (maxQueueSize=${maxQueueSize}). Increase queue size or consume faster.`,\n            );\n            state.failure = overflowError;\n            const reject = state.reject;\n            state.resolve = null;\n            state.reject = null;\n            reject?.(overflowError);\n            return;\n          }\n\n          if (overflowStrategy === 'drop-oldest') {\n            queue.shift();\n          }\n\n          if (overflowStrategy === 'drop-newest') {\n            return;\n          }\n        }\n        queue.push(value);\n      }\n    };\n\n    // AIDEV-NOTE: notifications() always sets autoRecover internally so the\n    // iterator can pause on unexpected disconnect and resume on reconnect.\n    const key = this.charKey(service, characteristic);\n    this.addToRecoveryRegistry(key, service, characteristic, callback);\n\n    const { unsubscribe: _unsubscribe, release, ready } = this.registerNotificationConsumer(service, characteristic, callback);\n    try {\n      await ready;\n    } catch (error) {\n      await release();\n      this.removeFromRecoveryRegistry(key, callback);\n      throw error;\n    }\n\n    try {\n      while (!state.done) {\n        if (state.failure) throw state.failure;\n\n        if (queue.length > 0) {\n          yield queue.shift()!;\n        } else {\n          const result = await new Promise<IteratorResult<DataView>>((resolve, reject) => {\n            state.resolve = resolve;\n            state.reject = reject;\n          });\n          if (result.done) {\n            // AIDEV-NOTE: On unexpected disconnect, the reconciliation loop will\n            // clear notificationStates but recoveryRegistry survives. If a\n            // reconnectGate exists and disconnect was not intentional, we pause\n            // the iterator and wait for reconnect rather than terminating.\n            const gate = this.deps.getReconnectGate();\n            if (gate && !this.deps.isIntentionalDisconnect()) {\n              await gate.promise;\n              if (this.deps.isIntentionalDisconnect()) return;\n              // After reconnect, recoverSubscriptions() already re-registered\n              // our callback. Continue the loop to yield new values.\n              continue;\n            }\n            return;\n          }\n          yield result.value;\n        }\n      }\n    } finally {\n      const pending = state.resolve;\n      state.resolve = null;\n      state.reject = null;\n      state.done = true;\n      await release();\n      this.removeFromRecoveryRegistry(key, callback);\n      if (pending) pending({ value: undefined as DataView | undefined, done: true } as IteratorResult<DataView>);\n    }\n  }\n\n  private handleNotification = (event: Event): void => {\n    const char = event.target as BluetoothRemoteGATTCharacteristic;\n    const value = char.value;\n    if (!value) return;\n\n    // Dispatch to all subscribers for this characteristic\n    for (const [key, notificationState] of this.notificationStates) {\n      const cached = notificationState.characteristic ?? this.deps.charCache.get(key);\n      if (cached === char) {\n        const [service, characteristic] = key.split(':');\n        for (const cb of notificationState.callbacks) {\n          try {\n            cb(value);\n          } catch (error) {\n            this.deps.emitError(BeacioError.from(error), {\n              operation: 'device.notification-callback',\n              service,\n              characteristic,\n            });\n          }\n        }\n        break;\n      }\n    }\n  };\n\n  // Detach every native listener, stop active notifications, and clear runtime\n  // notificationStates. `operation` distinguishes the caller in error context.\n  private teardownSubscriptions(operation: string): void {\n    for (const notificationState of this.notificationStates.values()) {\n      this.detachNotificationListener(notificationState);\n      if (notificationState.nativeActive) {\n        void this.stopNotificationsSafely(notificationState.characteristic, {\n          operation,\n        });\n      }\n    }\n    this.notificationStates.clear();\n  }\n\n  cleanupSubscriptions(): void {\n    this.teardownSubscriptions('notification.cleanup');\n  }\n\n  // AIDEV-NOTE: suspendSubscriptions detaches listeners and clears runtime\n  // notificationStates but preserves recoveryRegistry so subscriptions can be\n  // rebuilt on reconnect.\n  suspendSubscriptions(): void {\n    this.teardownSubscriptions('notification.suspend');\n  }\n\n  async recoverSubscriptions(): Promise<void> {\n    const entries = [...this.deps.recoveryRegistry.entries()];\n    for (const [key, entry] of entries) {\n      try {\n        for (const callback of entry.callbacks) {\n          const { ready } = this.registerNotificationConsumer(entry.service, entry.characteristic, callback);\n          await ready;\n        }\n      } catch (error) {\n        // Characteristic may no longer exist after firmware update or\n        // service change — remove the stale entry from the registry.\n        this.deps.recoveryRegistry.delete(key);\n        const recoveredError = BeacioError.from(error);\n        this.deps.emitSubscriptionLost({\n          service: entry.service,\n          characteristic: entry.characteristic,\n          error: recoveredError,\n        });\n        this.deps.emitError(recoveredError, {\n          operation: 'notification.recover',\n          service: entry.service,\n          characteristic: entry.characteristic,\n        });\n      }\n    }\n  }\n\n  // AIDEV-NOTE: Keep one serialized notification lifecycle per characteristic so\n  // subscribe() and notifications() cannot diverge during async start/stop races.\n  private registerNotificationConsumer(\n    service: string,\n    characteristic: string,\n    callback: NotificationCallback,\n  ): { unsubscribe: () => void; release: () => Promise<void>; ready: Promise<void> } {\n    const charKey = this.charKey(service, characteristic);\n    let notificationState = this.notificationStates.get(charKey);\n    if (!notificationState) {\n      notificationState = {\n        callbacks: new Set(),\n        characteristic: null,\n        listenerAttached: false,\n        nativeActive: false,\n        reconcilePromise: null,\n      };\n      this.notificationStates.set(charKey, notificationState);\n    }\n\n    notificationState.callbacks.add(callback);\n\n    const release = (): Promise<void> => {\n      const currentState = this.notificationStates.get(charKey);\n      if (!currentState?.callbacks.has(callback)) return Promise.resolve();\n\n      currentState.callbacks.delete(callback);\n      return this.syncNotificationState(charKey, service, characteristic);\n    };\n\n    return {\n      unsubscribe: () => { void release(); },\n      release,\n      ready: this.syncNotificationState(charKey, service, characteristic),\n    };\n  }\n\n  private syncNotificationState(serviceKey: string, service: string, characteristic: string): Promise<void> {\n    const notificationState = this.notificationStates.get(serviceKey);\n    if (!notificationState) return Promise.resolve();\n\n    const previous = notificationState.reconcilePromise ?? Promise.resolve();\n    const next = previous.catch((error) => {\n      this.deps.emitError(BeacioError.from(error), {\n        operation: 'notification.reconcile',\n        service,\n        characteristic,\n      });\n    }).then(async () => {\n      while (true) {\n        const currentState = this.notificationStates.get(serviceKey);\n        if (currentState !== notificationState) {\n          await this.deactivateNotificationState(notificationState);\n          return;\n        }\n\n        if (notificationState.callbacks.size === 0) {\n          this.detachNotificationListener(notificationState);\n\n          if (notificationState.nativeActive) {\n            notificationState.nativeActive = false;\n            await this.stopNotificationsSafely(notificationState.characteristic, {\n              operation: 'notification.stop',\n              service,\n              characteristic,\n            });\n            continue;\n          }\n\n          this.deleteNotificationStateIfIdle(serviceKey, notificationState);\n          return;\n        }\n\n        const char = notificationState.characteristic ?? await this.deps.getCharacteristic(service, characteristic);\n        notificationState.characteristic = char;\n\n        // AIDEV-NOTE: Attach event listener BEFORE startNotifications() to avoid\n        // losing notifications that arrive between the native subscribe completing\n        // and the JS listener being attached. Safe because listener is a no-op if\n        // notifications haven't started yet.\n        if (!notificationState.listenerAttached) {\n          char.addEventListener('characteristicvaluechanged', this.handleNotification);\n          notificationState.listenerAttached = true;\n        }\n\n        if (!notificationState.nativeActive) {\n          await char.startNotifications();\n          notificationState.nativeActive = true;\n\n          if (this.notificationStates.get(serviceKey) !== notificationState) {\n            await this.deactivateNotificationState(notificationState);\n            return;\n          }\n\n          if (notificationState.callbacks.size === 0) continue;\n        }\n\n        if (notificationState.callbacks.size === 0) continue;\n        return;\n      }\n    });\n\n    const settled = next.finally(() => {\n      if (notificationState.reconcilePromise === settled) {\n        notificationState.reconcilePromise = null;\n        if (this.notificationStates.get(serviceKey) === notificationState) {\n          this.deleteNotificationStateIfIdle(serviceKey, notificationState);\n        }\n      }\n    });\n\n    notificationState.reconcilePromise = settled;\n    return settled;\n  }\n\n  private async deactivateNotificationState(notificationState: NotificationState): Promise<void> {\n    this.detachNotificationListener(notificationState);\n    if (!notificationState.nativeActive) return;\n\n    notificationState.nativeActive = false;\n    await this.stopNotificationsSafely(notificationState.characteristic, {\n      operation: 'notification.deactivate',\n    });\n  }\n\n  private detachNotificationListener(notificationState: NotificationState): void {\n    if (!notificationState.listenerAttached || !notificationState.characteristic) return;\n\n    notificationState.characteristic.removeEventListener('characteristicvaluechanged', this.handleNotification);\n    notificationState.listenerAttached = false;\n  }\n\n  private deleteNotificationStateIfIdle(serviceKey: string, notificationState: NotificationState): void {\n    if (this.notificationStates.get(serviceKey) !== notificationState) return;\n    if (notificationState.callbacks.size > 0 || notificationState.nativeActive || notificationState.reconcilePromise) return;\n    this.notificationStates.delete(serviceKey);\n  }\n\n  private validateMaxQueueSize(maxQueueSize: number): number {\n    if (!Number.isInteger(maxQueueSize) || maxQueueSize <= 0) {\n      throw new BeacioError('INVALID_PARAMETER', `Invalid maxQueueSize: ${maxQueueSize}. Must be a positive integer.`);\n    }\n    return maxQueueSize;\n  }\n\n  private async stopNotificationsSafely(\n    characteristic: BluetoothRemoteGATTCharacteristic | null,\n    context: DeviceErrorContext,\n  ): Promise<void> {\n    if (!characteristic) return;\n    try {\n      await characteristic.stopNotifications();\n    } catch (error) {\n      this.deps.emitError(BeacioError.from(error), context);\n    }\n  }\n\n  private charKey(service: string, characteristic: string): string {\n    return `${resolveUUID(service)}:${resolveUUID(characteristic)}`;\n  }\n\n  /**\n   * Register a callback in the auto-recovery registry under `key`, creating the\n   * entry if absent. Used so subscriptions can be re-established after reconnect.\n   */\n  private addToRecoveryRegistry(\n    key: string,\n    service: string,\n    characteristic: string,\n    callback: NotificationCallback,\n  ): void {\n    let entry = this.deps.recoveryRegistry.get(key);\n    if (!entry) {\n      entry = { service, characteristic, callbacks: new Set() };\n      this.deps.recoveryRegistry.set(key, entry);\n    }\n    entry.callbacks.add(callback);\n  }\n\n  /**\n   * Remove a callback from the auto-recovery registry entry at `key`, deleting\n   * the entry entirely once its last callback is removed.\n   */\n  private removeFromRecoveryRegistry(key: string, callback: NotificationCallback): void {\n    const entry = this.deps.recoveryRegistry.get(key);\n    if (entry) {\n      entry.callbacks.delete(callback);\n      if (entry.callbacks.size === 0) this.deps.recoveryRegistry.delete(key);\n    }\n  }\n}\n","import type { RetryOptions } from './errors';\nimport { BeacioError, DEFAULT_RETRY_OPTIONS, withRetry } from './errors';\nimport { resolveUUID } from './uuid';\nimport { WriteChunker } from './write-chunker';\nimport { NotificationManager } from './notification-manager';\nimport type { RecoveryEntry } from './notification-manager';\nimport type {\n  ActiveSubscription,\n  AutoReconnectOptions,\n  ConnectOptions,\n  DeviceErrorContext,\n  DisconnectReason,\n  NotificationCallback,\n  NotificationOptions,\n  QueueOverflowEvent,\n  ReadOptions,\n  SubscribeOptions,\n  SubscriptionLostEvent,\n  WriteAutoOptions,\n  WriteAutoResult,\n  WriteFragmentedOptions,\n  WriteFragmentedResult,\n  WriteLargeOptions,\n  WriteLargeResult,\n  WriteLimits,\n  WriteOptions,\n} from './types';\n\ntype DeviceHooks = {\n  beforeConnect?: (device: BeacioDevice) => void;\n  onConnectionChange?: (device: BeacioDevice) => void;\n};\n\ntype DisconnectListener = (reason: DisconnectReason) => void;\ntype Listener = () => void;\ntype QueueOverflowListener = (event: QueueOverflowEvent) => void;\ntype SubscriptionLostListener = (event: SubscriptionLostEvent) => void;\ntype ErrorListener = (error: Error, context: DeviceErrorContext) => void;\n\ntype DeviceTransportInfo = {\n  getMtu?: () => Promise<number | null>;\n  getWriteLimits?: () => Promise<Partial<WriteLimits> | null | undefined>;\n};\n\n/**\n * SB-SDK-05 AC3: is this native rejection the \"service not declared in\n * optionalServices\" failure? WebKit raises it as a SecurityError DOMException\n * (\"Origin is not allowed to access the service…\"); other engines word it as an\n * access/allowed/blocklist failure. We match the SecurityError name OR that\n * access-denied phrasing so the operator gets the actionable remedy regardless of\n * engine wording, while genuine non-permission GATT failures still fall through to\n * BeacioError.from.\n */\nfunction isUndeclaredServiceSecurityError<T>(error: T): boolean {\n  const name =\n    typeof error === 'object' && error !== null && 'name' in error && typeof (error as { name: string }).name === 'string'\n      ? (error as { name: string }).name\n      : '';\n  if (name === 'SecurityError') return true;\n  const msg = (error instanceof Error ? error.message : String(error)).toLowerCase();\n  return (\n    (msg.includes('not allowed to access') || msg.includes('blocklist') || msg.includes('blocked')) &&\n    msg.includes('service')\n  );\n}\n\n/**\n * SB-SDK-05 AC3: build the human, stack-free SecurityError for an undeclared\n * service. The .message is a complete plain-English sentence that NAMES the\n * offending service UUID (canonical resolved form) and states the optionalServices\n * remedy (mirroring SUGGESTIONS.SERVICE_NOT_FOUND), with no native URL/jargon, no\n * stack, and no competitor name. A SecurityError DOMException is the W3C-correct\n * class a raw `catch` already special-cases.\n */\nfunction undeclaredServiceError(uuid: string): DOMException {\n  return new DOMException(\n    `This site is not allowed to access the Bluetooth service ${uuid}. ` +\n      `Add \"${uuid}\" to the optionalServices array in your requestDevice() options, ` +\n      `then reconnect.`,\n    'SecurityError'\n  );\n}\n\n/**\n * Represents a connected BLE device. Provides methods to read, write,\n * and subscribe to GATT characteristics using human-readable UUID names.\n *\n * @example\n * ```typescript\n * const device = await ble.requestDevice({ filters: [{ services: ['heart_rate'] }] })\n * await device.connect()\n *\n * // Read a value\n * const battery = await device.read('battery_service', 'battery_level')\n *\n * // Subscribe to notifications\n * const unsub = device.subscribe('heart_rate', 'heart_rate_measurement', (data) => {\n *   console.log('Heart rate:', data.getUint8(1))\n * })\n *\n * // Cleanup\n * unsub()\n * device.disconnect()\n * ```\n */\nexport class BeacioDevice {\n  readonly id: string;\n  /** Device name, mirroring the WebIDL `DOMString?` shape: null when the\n   *  device has no name (never undefined). */\n  readonly name: string | null;\n  readonly raw: BluetoothDevice;\n\n  private server: BluetoothRemoteGATTServer | null = null;\n  private primaryServicesCache: BluetoothRemoteGATTService[] | null = null;\n  private serviceCache = new Map<string, BluetoothRemoteGATTService>();\n  private charCache = new Map<string, BluetoothRemoteGATTCharacteristic>();\n  private recoveryRegistry = new Map<string, RecoveryEntry>();\n  private disconnectListeners = new Set<DisconnectListener>();\n  private reconnectedListeners = new Set<Listener>();\n  private queueOverflowListeners = new Set<QueueOverflowListener>();\n  private subscriptionLostListeners = new Set<SubscriptionLostListener>();\n  private errorListeners = new Set<ErrorListener>();\n  private reconnectGate: { promise: Promise<void>; resolve: () => void } | null = null;\n  private intentionalDisconnect = false;\n  private lastDisconnectReason: DisconnectReason | null = null;\n  private autoReconnectConfig: AutoReconnectOptions | null = null;\n  private autoReconnectAbort: AbortController | null = null;\n  private readonly writeChunker: WriteChunker;\n  private readonly notificationManager: NotificationManager;\n  private readonly hooks: DeviceHooks;\n\n  constructor(device: BluetoothDevice, hooks: DeviceHooks = {}) {\n    this.id = device.id;\n    this.name = device.name ?? null;\n    this.raw = device;\n    this.hooks = hooks;\n\n    // AIDEV-NOTE: Write fragmentation + in-flight tracking lives in WriteChunker\n    // (cleanup item 144). Deps are bound so the chunker holds no BLE state itself.\n    this.writeChunker = new WriteChunker({\n      getCharacteristic: (service, characteristic) => this.getCharacteristic(service, characteristic),\n      emitError: (error, context) => this.emitError(error, context),\n      validateTimeoutMs: (timeoutMs) => this.validateTimeoutMs(timeoutMs),\n      withOptionalTimeout: (operation, timeoutMs, message) => this.withOptionalTimeout(operation, timeoutMs, message),\n      isConnected: () => this.connected,\n      getTransport: () => this.raw.gatt as (BluetoothRemoteGATTServer & DeviceTransportInfo) | null,\n    });\n\n    // AIDEV-NOTE: Notification subscription lifecycle lives in NotificationManager\n    // (cleanup item 144). recoveryRegistry + charCache are shared BY REFERENCE so\n    // the device keeps reading/clearing them across connect()/disconnect(); the\n    // reconnect gate is read live via getReconnectGate() so its create-before-fire\n    // / resolve-in-finally lifecycle is unchanged.\n    this.notificationManager = new NotificationManager({\n      getCharacteristic: (service, characteristic) => this.getCharacteristic(service, characteristic),\n      emitError: (error, context) => this.emitError(error, context),\n      emitSubscriptionLost: (event) => this.emitSubscriptionLost(event),\n      emitQueueOverflow: (event) => this.emitQueueOverflow(event),\n      recoveryRegistry: this.recoveryRegistry,\n      charCache: this.charCache,\n      getReconnectGate: () => this.reconnectGate,\n      isIntentionalDisconnect: () => this.intentionalDisconnect,\n    });\n\n    // AIDEV-NOTE: Guard for environments where BluetoothDevice objects may lack\n    // EventTarget methods (e.g. advertisement-discovered devices in scan results).\n    if (typeof device.addEventListener === 'function') {\n      device.addEventListener('gattserverdisconnected', () => {\n        this.handleDisconnect();\n      });\n    }\n  }\n\n  get connected(): boolean {\n    return this.server?.connected ?? false;\n  }\n\n  /**\n   * Connect to the device's GATT server. Must be called before any read/write/subscribe\n   * operation. No-op if already connected.\n   *\n   * **Auto-reconnect lifecycle:**\n   * Pass `autoReconnect: true` for default exponential backoff (1s initial, 30s max,\n   * 2x multiplier, infinite attempts), or pass an {@link AutoReconnectOptions} object\n   * to customize. On unexpected disconnect, the SDK automatically retries connection\n   * and recovers all subscriptions registered with `autoRecover: true`.\n   *\n   * Auto-reconnect stops when:\n   * - `disconnect()` is called (intentional disconnect)\n   * - `maxAttempts` is exhausted (emits error via `addErrorListener`)\n   * - `connect({ autoReconnect: ... })` is called again while disconnected and replaces the reconnect config\n   *\n   * @param options - Connection options including auto-reconnect configuration.\n   *\n   * @throws {BeacioError} `GATT_OPERATION_FAILED` -- device has no GATT server\n   * @throws {BeacioError} `CONNECTION_LIMIT_REACHED` -- `Beacio.maxConnections` exceeded\n   *\n   * @example\n   * ```typescript\n   * // Simple connection\n   * await device.connect()\n   *\n   * // With auto-reconnect (default backoff)\n   * await device.connect({ autoReconnect: true })\n   *\n   * // Custom backoff: 500ms initial, 10s max, 3x multiplier, 5 attempts\n   * await device.connect({\n   *   autoReconnect: {\n   *     initialDelayMs: 500,\n   *     maxDelayMs: 10000,\n   *     backoffMultiplier: 3,\n   *     maxAttempts: 5,\n   *   },\n   * })\n   * ```\n   *\n   * @see {@link ConnectOptions}\n   * @see {@link AutoReconnectOptions}\n   * @see {@link connectWithRetry}\n   */\n  async connect(options?: ConnectOptions): Promise<void> {\n    if (this.connected) return;\n\n  // Store auto-reconnect config when explicitly provided for a new connection attempt.\n    if (options?.autoReconnect) {\n      this.autoReconnectConfig = options.autoReconnect === true\n        ? {}\n        : options.autoReconnect;\n    }\n\n    this.hooks.beforeConnect?.(this);\n    const gatt = this.raw.gatt;\n    if (!gatt) throw new BeacioError('GATT_OPERATION_FAILED', 'Device has no GATT server');\n    const reconnectGate = this.reconnectGate;\n\n    try {\n      this.server = await gatt.connect();\n      this.lastDisconnectReason = null;\n\n      await this.notificationManager.recoverSubscriptions();\n\n      for (const fn of this.reconnectedListeners) {\n        try {\n          fn();\n        } catch (error) {\n          this.emitError(BeacioError.from(error), { operation: 'device.reconnected-listener' });\n        }\n      }\n      this.intentionalDisconnect = false;\n      this.hooks.onConnectionChange?.(this);\n    } catch (error) {\n      if (!this.server?.connected) {\n        this.server = null;\n        this.hooks.onConnectionChange?.(this);\n      }\n      throw BeacioError.from(error);\n    } finally {\n      if (this.reconnectGate === reconnectGate) {\n        this.reconnectGate = null;\n      }\n      reconnectGate?.resolve();\n    }\n  }\n\n  /**\n   * Disconnect from the device, stop auto-reconnect, and clean up all subscriptions.\n   * Clears all notification recovery registrations, aborts in-flight writes, and\n   * releases the GATT server. Fires `'disconnected'` event with reason `'intentional'`.\n   *\n   * Safe to call multiple times or when already disconnected.\n   */\n  disconnect(): void {\n    this.intentionalDisconnect = true;\n    this.lastDisconnectReason = 'intentional';\n    this.autoReconnectConfig = null;\n    this.autoReconnectAbort?.abort();\n    this.autoReconnectAbort = null;\n    this.writeChunker.abortInFlightWrites();\n    this.notificationManager.cleanupSubscriptions();\n    this.recoveryRegistry.clear();\n    if (this.reconnectGate) {\n      this.reconnectGate.resolve();\n      this.reconnectGate = null;\n    }\n    this.server?.disconnect();\n    this.server = null;\n    this.primaryServicesCache = null;\n    this.hooks.onConnectionChange?.(this);\n  }\n\n  /**\n   * Connect with automatic retry using {@link withRetry}.\n   * Convenience wrapper that combines `connect()` with the SDK's retry utility.\n   *\n   * Use this for one-shot retry on initial connection. For persistent auto-reconnect\n   * after unexpected disconnects, use `connect({ autoReconnect: true })` instead.\n   *\n   * @param options - Retry options (defaults: 3 attempts, 250ms delay, 1.5x backoff).\n   *\n   * @throws {BeacioError} Last error from the final failed attempt\n   *\n   * @see {@link connect}\n   * @see {@link withRetry}\n   */\n  async connectWithRetry(options: RetryOptions = DEFAULT_RETRY_OPTIONS): Promise<void> {\n    await withRetry(async () => {\n      await this.connect();\n    }, options);\n  }\n\n  /**\n   * Read a characteristic value. Return raw `DataView` bytes, or apply a parse\n   * function to get a typed result.\n   *\n   * Service and characteristic names (e.g. `'battery_service'`, `'battery_level'`)\n   * are resolved to full 128-bit UUIDs via {@link resolveUUID}.\n   *\n   * For typed reads, use the overload with a parse function (e.g. from `@beacio/core/profiles`):\n   * ```typescript\n   * const hr = await device.read('heart_rate', 'heart_rate_measurement', parseHeartRate)\n   * console.log(hr.bpm) // typed HeartRateData\n   * ```\n   *\n   * @param service - Service UUID or name (e.g. `'heart_rate'`, `'180d'`).\n   * @param characteristic - Characteristic UUID or name (e.g. `'battery_level'`).\n   * @param options - Read options including timeout.\n   * @returns Raw characteristic value as a `DataView`.\n   *\n   * @throws {BeacioError} `DEVICE_DISCONNECTED` -- not connected\n   * @throws {BeacioError} `SERVICE_NOT_FOUND` -- service UUID not found on device\n   * @throws {BeacioError} `CHARACTERISTIC_NOT_FOUND` -- characteristic UUID not found\n   * @throws {BeacioError} `CHARACTERISTIC_NOT_READABLE` -- characteristic does not support read\n   * @throws {BeacioError} `TIMEOUT` -- read did not complete within `timeoutMs`\n   *\n   * @example\n   * ```typescript\n   * // Raw DataView read\n   * const data = await device.read('battery_service', 'battery_level')\n   * const level = data.getUint8(0) // 0-100\n   *\n   * // Typed read with parse function\n   * const hr = await device.read('heart_rate', 'heart_rate_measurement', parseHeartRate)\n   * console.log(hr.bpm)\n   *\n   * // With timeout\n   * const data = await device.read('my_service', 'my_char', { timeoutMs: 5000 })\n   * ```\n   *\n   * @see {@link ReadOptions}\n   * @see {@link subscribe} for continuous notifications\n   */\n  async read(service: string, characteristic: string, options?: ReadOptions): Promise<DataView>;\n  /**\n   * Read a characteristic value and apply a parse function to get a typed result.\n   *\n   * @param service - Service UUID or name.\n   * @param characteristic - Characteristic UUID or name.\n   * @param parse - Function to transform the raw `DataView` into a typed value.\n   * @param options - Read options including timeout.\n   * @returns Parsed value of type `T`.\n   */\n  async read<T>(\n    service: string,\n    characteristic: string,\n    parse: (dv: DataView) => T | Promise<T>,\n    options?: ReadOptions,\n  ): Promise<T>;\n  async read<T = DataView>(\n    service: string,\n    characteristic: string,\n    parseOrOptions?: ((dv: DataView) => T | Promise<T>) | ReadOptions,\n    maybeOptions?: ReadOptions,\n  ): Promise<DataView | T> {\n    const parse = typeof parseOrOptions === 'function' ? parseOrOptions : undefined;\n    const options = typeof parseOrOptions === 'function' ? maybeOptions : parseOrOptions;\n    const timeoutMs = this.validateTimeoutMs(options?.timeoutMs);\n    const char = await this.getCharacteristic(service, characteristic);\n    try {\n      const value = await this.withOptionalTimeout(\n        char.readValue(),\n        timeoutMs,\n        'Read timed out',\n      );\n      return parse ? await parse(value) : value;\n    } catch (e) {\n      throw BeacioError.from(e);\n    }\n  }\n\n  /**\n   * Write a single packet to a characteristic. Default mode is write-with-response\n   * (acknowledged). For payloads larger than one ATT packet, use {@link writeLarge},\n   * {@link writeFragmented}, or {@link writeAuto}.\n   *\n   * @param service - Service UUID or name.\n   * @param characteristic - Characteristic UUID or name.\n   * @param value - Data to write (ArrayBuffer, TypedArray, or DataView).\n   * @param options - Write mode and timeout options.\n   *\n   * @throws {BeacioError} `DEVICE_DISCONNECTED` -- not connected\n   * @throws {BeacioError} `CHARACTERISTIC_NOT_WRITABLE` -- characteristic does not support write\n   * @throws {BeacioError} `WRITE_INCOMPLETE` -- disconnected before write completed\n   * @throws {BeacioError} `TIMEOUT` -- write did not complete within `timeoutMs`\n   *\n   * @example\n   * ```typescript\n   * await device.write('my_service', 'my_char', new Uint8Array([0x01]))\n   * await device.write('my_service', 'my_char', data, { mode: 'without-response' })\n   * ```\n   *\n   * @see {@link writeLarge} for chunked writes without retry\n   * @see {@link writeFragmented} for chunked writes with per-chunk retry\n   * @see {@link writeAuto} for automatic fragmentation decisions\n   * @see {@link writeWithoutResponse} for convenience fire-and-forget writes\n   */\n  async write(service: string, characteristic: string, value: BufferSource, options?: WriteOptions): Promise<void> {\n    return this.writeChunker.write(service, characteristic, value, options);\n  }\n\n  /**\n   * Write a large payload by chunking it across multiple write operations,\n   * with optional per-chunk retry on failure.\n   *\n   * **Chunk size determination order:**\n   * 1. Explicit `chunkSize` if provided\n   * 2. `mtu - 3` if `mtu` option is provided (BLE ATT header = 3 bytes)\n   * 3. Platform-reported write limits via `getWriteLimits()`\n   * 4. 20-byte conservative fallback\n   *\n   * Unlike {@link writeLarge}, failed chunks are retried up to `maxRetries` times\n   * before the entire operation fails with `WRITE_INCOMPLETE`.\n   *\n   * @param service - Service UUID or name.\n   * @param characteristic - Characteristic UUID or name.\n   * @param value - Full payload to write (will be chunked automatically).\n   * @param options - Fragmentation, retry, and write mode options.\n   * @returns Result with byte counts, chunk info, and total retry count.\n   *\n   * @throws {BeacioError} `WRITE_INCOMPLETE` -- partial write after chunk retries exhausted\n   * @throws {BeacioError} `DEVICE_DISCONNECTED` -- disconnected during write\n   *\n   * @see {@link writeLarge} for chunked writes without retry\n   * @see {@link writeAuto} for automatic fragmentation decisions\n   * @see {@link WriteFragmentedOptions}\n   */\n  async writeFragmented(\n    service: string,\n    characteristic: string,\n    value: BufferSource,\n    options?: WriteFragmentedOptions,\n  ): Promise<WriteFragmentedResult> {\n    return this.writeChunker.writeFragmented(service, characteristic, value, options);\n  }\n\n  /**\n   * Write a large payload by chunking it across multiple write operations.\n   * No per-chunk retry -- any chunk failure aborts the entire write.\n   *\n   * **Chunk size determination order:**\n   * 1. Explicit `chunkSize` if provided\n   * 2. Platform-reported write limits via `getWriteLimits()`\n   * 3. `MTU - 3` (ATT header overhead)\n   * 4. 20-byte conservative fallback\n   *\n   * @param service - Service UUID or name.\n   * @param characteristic - Characteristic UUID or name.\n   * @param value - Full payload to write (will be chunked automatically).\n   * @param options - Chunk size and write mode options.\n   * @returns Result with byte counts and chunk info.\n   *\n   * @throws {BeacioError} `WRITE_INCOMPLETE` -- not all bytes were transferred\n   * @throws {BeacioError} `DEVICE_DISCONNECTED` -- disconnected during write\n   *\n   * @see {@link writeFragmented} for chunked writes with per-chunk retry\n   * @see {@link writeAuto} for automatic fragmentation decisions\n   * @see {@link WriteLargeOptions}\n   */\n  async writeLarge(\n    service: string,\n    characteristic: string,\n    value: BufferSource,\n    options?: WriteLargeOptions,\n  ): Promise<WriteLargeResult> {\n    return this.writeChunker.writeLarge(service, characteristic, value, options);\n  }\n\n  /**\n   * Write a value to a characteristic without waiting for acknowledgment (fire-and-forget).\n   * Convenience wrapper for `write(service, char, value, { mode: 'without-response' })`.\n   *\n   * @param service - Service UUID or name.\n   * @param characteristic - Characteristic UUID or name.\n   * @param value - Data to write.\n   * @param options - Write options (mode is forced to `'without-response'`).\n   *\n   * @see {@link write}\n   */\n  async writeWithoutResponse(service: string, characteristic: string, value: BufferSource, options?: Omit<WriteOptions, 'mode'>): Promise<void> {\n    return this.writeChunker.writeWithoutResponse(service, characteristic, value, options);\n  }\n\n  /**\n   * Return the platform-reported writable payload limits and negotiated ATT MTU.\n   *\n   * On standard browser Web Bluetooth stacks (Chrome, Edge), these values are typically\n   * unavailable and return `null`. The Safari Beacio extension reports actual negotiated values.\n   *\n   * @returns Object with `withResponse`, `withoutResponse`, and `mtu` fields (each `null` when unavailable).\n   *\n   * @throws {BeacioError} `DEVICE_DISCONNECTED` -- not connected\n   *\n   * @see {@link WriteLimits}\n   * @see {@link getMtu} for just the MTU value\n   * @see {@link getEffectiveMtu} for a guaranteed non-null MTU (falls back to 23)\n   */\n  async getWriteLimits(): Promise<WriteLimits> {\n    return this.writeChunker.getWriteLimits();\n  }\n\n  // AIDEV-NOTE: getEffectiveMtu defined below (line ~632) with explicit type guards.\n  // First copy removed to fix TS2393 duplicate function implementation error.\n\n  /**\n   * Return the negotiated ATT MTU when the underlying platform exposes it.\n   * Returns `null` when unavailable. Max write payload = MTU - 3 (ATT header).\n   *\n   * @see {@link getEffectiveMtu} for a guaranteed non-null value (falls back to 23)\n   * @see {@link getWriteLimits} for full write limit details\n   */\n  async getMtu(): Promise<number | null> {\n    return (await this.getWriteLimits()).mtu;\n  }\n\n  /**\n   * Smart write that automatically decides between single-packet and fragmented writes.\n   *\n   * If the payload fits within the platform write limit, send it as a single `write()`.\n   * Otherwise, delegate to `writeFragmented()` with per-chunk retry support.\n   *\n   * **Decision logic:**\n   * 1. Determine max single-write size (same as chunk size derivation)\n   * 2. If `payload.byteLength <= limit`, use single `write()`\n   * 3. Otherwise, use `writeFragmented()` and set `result.fragmented = true`\n   *\n   * @param service - Service UUID or name.\n   * @param characteristic - Characteristic UUID or name.\n   * @param value - Data to write (any size).\n   * @param options - All fragmentation, retry, and write mode options.\n   * @returns Result indicating whether fragmentation was used.\n   *\n   * @throws {BeacioError} `WRITE_INCOMPLETE` -- partial write after chunk retries exhausted\n   * @throws {BeacioError} `DEVICE_DISCONNECTED` -- disconnected during write\n   *\n   * @see {@link write} for single-packet writes\n   * @see {@link writeFragmented} for explicit fragmentation\n   * @see {@link WriteAutoResult}\n   */\n  async writeAuto(\n    service: string,\n    characteristic: string,\n    value: BufferSource,\n    options?: WriteAutoOptions,\n  ): Promise<WriteAutoResult> {\n    return this.writeChunker.writeAuto(service, characteristic, value, options);\n  }\n\n  /**\n   * Subscribe to characteristic notifications. Return an unsubscribe function.\n   *\n   * **Notification deduplication:** Multiple callbacks on the same characteristic share\n   * a single native BLE listener. The first `subscribe()` call starts notifications;\n   * subsequent calls just add callbacks. The native listener stops only when all\n   * callbacks are unsubscribed.\n   *\n   * **Auto-recovery:** By default (`autoRecover: true`), subscriptions are automatically\n   * re-established after reconnection. The callback continues receiving values\n   * transparently after the device reconnects.\n   *\n   * **Error handling:** Setup errors are delivered via `options.onError` (not thrown),\n   * since `subscribe()` returns synchronously. Use {@link subscribeAsync} if you need\n   * to `await` setup completion and catch errors directly.\n   *\n   * @param service - Service UUID or name.\n   * @param characteristic - Characteristic UUID or name.\n   * @param callback - Function called with each notification `DataView` value.\n   * @param options - Auto-recovery and error handling options.\n   * @returns Unsubscribe function -- call it to stop receiving notifications.\n   *\n   * @example\n   * ```typescript\n   * const unsub = device.subscribe('heart_rate', 'heart_rate_measurement', (data) => {\n   *   console.log(`Heart rate: ${data.getUint8(1)} BPM`)\n   * })\n   *\n   * // Later: stop notifications\n   * unsub()\n   * ```\n   *\n   * @see {@link subscribeAsync} for awaitable setup with error throwing\n   * @see {@link notifications} for async iterator interface\n   * @see {@link SubscribeOptions}\n   */\n  subscribe(service: string, characteristic: string, callback: NotificationCallback, options?: SubscribeOptions): () => void {\n    return this.notificationManager.subscribe(service, characteristic, callback, options);\n  }\n\n  /**\n   * Subscribe to characteristic notifications with awaitable setup.\n   *\n   * Unlike {@link subscribe}, this method awaits the native notification setup and\n   * throws on failure instead of routing errors to `onError`. The returned promise\n   * resolves with an unsubscribe function once notifications are active.\n   *\n   * Shares the same underlying notification state as `subscribe()` -- multiple\n   * callbacks on the same characteristic share one native listener.\n   *\n   * @param service - Service UUID or name.\n   * @param characteristic - Characteristic UUID or name.\n   * @param callback - Function called with each notification value.\n   * @param options - Auto-recovery options. `onError` is NOT called (errors are thrown).\n   * @returns Unsubscribe function (resolves only after setup completes).\n   *\n   * @throws {BeacioError} `CHARACTERISTIC_NOT_NOTIFIABLE` -- characteristic does not support notify\n   * @throws {BeacioError} `DEVICE_DISCONNECTED` -- not connected\n   *\n   * @see {@link subscribe} for fire-and-forget subscription\n   * @see {@link notifications} for async iterator interface\n   */\n  async subscribeAsync(\n    service: string,\n    characteristic: string,\n    callback: NotificationCallback,\n    options?: SubscribeOptions,\n  ): Promise<() => void> {\n    return this.notificationManager.subscribeAsync(service, characteristic, callback, options);\n  }\n\n  /**\n   * Observe NATIVE notification-queue overflows for a characteristic. Returns an\n   * unsubscribe function synchronously (like {@link subscribe}); the underlying\n   * characteristic lookup + listener attach happen asynchronously.\n   *\n   * Safari's bounded Swift `EventQueue` evicts notifications under sustained\n   * high-frequency load rather than letting the page silently miss samples; the\n   * polyfill re-surfaces each eviction as a `beacio:overflow` `CustomEvent`\n   * dispatched on the underlying `BluetoothRemoteGATTCharacteristic`. This method\n   * resolves that characteristic (via the same cached lookup `read`/`subscribe`\n   * use) and forwards each `beacio:overflow` event to `listener`. The\n   * `CustomEvent.detail` carries the eviction metadata (`evictedCount`,\n   * `queueCapacity`, `seq`, `timestamp`); `@beacio/core/profiles` decodes it into a\n   * typed {@link NativeOverflowEvent} for you. The recommended response is a\n   * fresh `read()` to resynchronise any UI that was tracking the last notified\n   * value.\n   *\n   * This is distinct from `notifications().onOverflow` /\n   * `on('queue-overflow')`, which report the *JS* async-iterator queue (see\n   * {@link QueueOverflowEvent}). On browser stacks that never emit\n   * `beacio:overflow` (Chrome/Edge), the listener simply never fires.\n   *\n   * Setup errors (e.g. not connected) are routed to any registered\n   * {@link addErrorListener} — never thrown — since this returns synchronously.\n   *\n   * @param service - Service UUID or name.\n   * @param characteristic - Characteristic UUID or name.\n   * @param listener - Called with the raw `beacio:overflow` event on each overflow.\n   * @returns Unsubscribe function -- detaches the underlying event listener\n   *   (also cancels a still-pending attach).\n   *\n   * @see {@link NativeOverflowEvent}\n   * @see {@link decodeNativeOverflow}\n   * @see {@link subscribe}\n   */\n  onCharacteristicOverflow(\n    service: string,\n    characteristic: string,\n    listener: (event: Event) => void,\n  ): () => void {\n    let detach: (() => void) | null = null;\n    let cancelled = false;\n\n    this.getCharacteristic(service, characteristic)\n      .then((char) => {\n        if (cancelled) return;\n        char.addEventListener('beacio:overflow', listener);\n        detach = () => char.removeEventListener('beacio:overflow', listener);\n      })\n      .catch((error) => {\n        this.emitError(BeacioError.from(error), {\n          operation: 'device.onCharacteristicOverflow',\n          service,\n          characteristic,\n        });\n      });\n\n    return () => {\n      cancelled = true;\n      detach?.();\n      detach = null;\n    };\n  }\n\n  /**\n   * Async iterator for characteristic notifications. Use with `for await...of`.\n   *\n   * **Overflow strategies:** When the consumer cannot keep up with incoming notifications,\n   * the internal queue fills up. Choose a strategy via `options.overflowStrategy`:\n   * - `'error'` (default) -- Throw `GATT_OPERATION_FAILED` and terminate the iterator\n   * - `'drop-oldest'` -- Discard the oldest buffered value (lossy FIFO)\n   * - `'drop-newest'` -- Discard the incoming value (backpressure)\n   *\n   * **Reconnect behavior:** On unexpected disconnect, the iterator pauses (does not\n   * terminate) and waits for auto-reconnect to complete. Subscriptions are auto-recovered\n   * and the iterator resumes yielding values transparently. On intentional disconnect,\n   * the iterator terminates.\n   *\n   * @param service - Service UUID or name.\n   * @param characteristic - Characteristic UUID or name.\n   * @param options - Queue size, overflow strategy, and overflow callback.\n   * @returns Async iterable of `DataView` notification values.\n   *\n   * @example\n   * ```typescript\n   * for await (const data of device.notifications('heart_rate', 'heart_rate_measurement')) {\n   *   console.log('BPM:', data.getUint8(1))\n   * }\n   *\n   * // With overflow handling\n   * const stream = device.notifications('sensor', 'data', {\n   *   maxQueueSize: 64,\n   *   overflowStrategy: 'drop-oldest',\n   *   onOverflow: (e) => console.warn(`Dropped ${e.droppedCount} values`),\n   * })\n   * ```\n   *\n   * @see {@link subscribe} for callback-based notifications\n   * @see {@link NotificationOptions}\n   * @see {@link NotificationOverflowStrategy}\n   */\n  notifications(service: string, characteristic: string, options?: NotificationOptions): AsyncIterable<DataView> {\n    return this.notificationManager.notifications(service, characteristic, options);\n  }\n\n  /**\n   * Start watching for BLE advertisements from this device.\n   * Not supported on all platforms.\n   *\n   * @throws {BeacioError} `GATT_OPERATION_FAILED` -- platform does not support watchAdvertisements\n   */\n  async watchAdvertisements(): Promise<void> {\n    if (typeof this.raw.watchAdvertisements !== 'function') {\n      throw new BeacioError('GATT_OPERATION_FAILED', 'watchAdvertisements is not supported on this device');\n    }\n    await this.raw.watchAdvertisements();\n  }\n\n  /**\n   * Stop watching for BLE advertisements from this device.\n   *\n   * @throws {BeacioError} `GATT_OPERATION_FAILED` -- platform does not support unwatchAdvertisements\n   */\n  async unwatchAdvertisements(): Promise<void> {\n    const rawDevice = this.raw as BluetoothDevice & { unwatchAdvertisements?: () => Promise<void> };\n    if (typeof rawDevice.unwatchAdvertisements !== 'function') {\n      throw new BeacioError('GATT_OPERATION_FAILED', 'unwatchAdvertisements is not supported on this device');\n    }\n    await rawDevice.unwatchAdvertisements();\n  }\n\n  /**\n   * Request the browser to forget this device and revoke its permissions.\n   * After calling, the device must be re-selected via `requestDevice()`.\n   *\n   * @throws {BeacioError} `GATT_OPERATION_FAILED` -- platform does not support forget\n   */\n  async forget(): Promise<void> {\n    if (typeof this.raw.forget !== 'function') {\n      throw new BeacioError('GATT_OPERATION_FAILED', 'forget is not supported on this device');\n    }\n    await this.raw.forget();\n  }\n\n  /**\n   * Discover all primary GATT services on the connected device.\n   * Results are cached until disconnect.\n   *\n   * @returns Array of `BluetoothRemoteGATTService` objects.\n   * @throws {BeacioError} `DEVICE_DISCONNECTED` -- not connected\n   */\n  async getPrimaryServices(): Promise<BluetoothRemoteGATTService[]> {\n    if (!this.connected) throw new BeacioError('DEVICE_DISCONNECTED');\n    if (this.primaryServicesCache) return this.primaryServicesCache;\n\n    try {\n      const services = await this.server!.getPrimaryServices();\n      const normalizedServices = services.map((service) => {\n        const cached = this.serviceCache.get(service.uuid) ?? service;\n        this.serviceCache.set(service.uuid, cached);\n        return cached;\n      });\n      this.primaryServicesCache = normalizedServices;\n      return normalizedServices;\n    } catch (e) {\n      throw BeacioError.from(e);\n    }\n  }\n\n  /**\n   * Return the effective ATT MTU, guaranteed non-null.\n   * Falls back through: reported MTU > withResponse + 3 > withoutResponse + 3 > 23 (BLE default).\n   *\n   * @returns MTU value in bytes (minimum 23).\n   *\n   * @see {@link getMtu} for nullable version\n   * @see {@link getWriteLimits} for full limit details\n   */\n  async getEffectiveMtu(): Promise<number> {\n    const limits = await this.getWriteLimits();\n    if (typeof limits.mtu === 'number' && limits.mtu > 0) return limits.mtu;\n    if (typeof limits.withResponse === 'number' && limits.withResponse > 0) return limits.withResponse + 3;\n    if (typeof limits.withoutResponse === 'number' && limits.withoutResponse > 0) return limits.withoutResponse + 3;\n    return 23;\n  }\n\n  /**\n   * Return the reason for the most recent disconnection, or `null` if the device\n   * has never disconnected during this session.\n   *\n   * @see {@link DisconnectReason}\n   */\n  getLastDisconnectReason(): DisconnectReason | null {\n    return this.lastDisconnectReason;\n  }\n\n  /**\n   * Return a snapshot of all active notification subscriptions on this device.\n   * Includes both currently active native subscriptions and those registered\n   * for auto-recovery after reconnection.\n   *\n   * @returns Array of {@link ActiveSubscription} snapshots.\n   */\n  getActiveSubscriptions(): ActiveSubscription[] {\n    const notificationStates = this.notificationManager.getNotificationStates();\n    const keys = new Set<string>([\n      ...notificationStates.keys(),\n      ...this.recoveryRegistry.keys(),\n    ]);\n\n    return [...keys].map((key) => {\n      const state = notificationStates.get(key);\n      const recoveryEntry = this.recoveryRegistry.get(key);\n      const [service, characteristic] = key.split(':');\n      return {\n        service,\n        characteristic,\n        callbackCount: state?.callbacks.size ?? recoveryEntry?.callbacks.size ?? 0,\n        autoRecovering: recoveryEntry !== undefined,\n        nativeActive: state?.nativeActive ?? false,\n      };\n    });\n  }\n\n  /**\n   * Register a listener for device lifecycle events. Return an unsubscribe function.\n   *\n   * **Events:**\n   * - `'disconnected'` -- Fired on any disconnect (intentional or unexpected), with reason.\n   * - `'reconnected'` -- Fired after a successful connection while reconnect recovery listeners are registered.\n   * - `'queue-overflow'` -- Fired when a notification queue exceeds its max size.\n   * - `'subscription-lost'` -- Fired when a subscription cannot be recovered after reconnect.\n   *\n   * @param event - Event name.\n   * @param fn - Listener function.\n   * @returns Unsubscribe function.\n   */\n  on(event: 'reconnected', fn: Listener): () => void;\n  on(event: 'disconnected', fn: DisconnectListener): () => void;\n  on(event: 'queue-overflow', fn: QueueOverflowListener): () => void;\n  on(event: 'subscription-lost', fn: SubscriptionLostListener): () => void;\n  on(\n    event: 'disconnected' | 'reconnected' | 'queue-overflow' | 'subscription-lost',\n    fn: DisconnectListener | Listener | QueueOverflowListener | SubscriptionLostListener,\n  ): () => void {\n    if (event === 'disconnected') this.disconnectListeners.add(fn as DisconnectListener);\n    if (event === 'reconnected') this.reconnectedListeners.add(fn as Listener);\n    if (event === 'queue-overflow') this.queueOverflowListeners.add(fn as QueueOverflowListener);\n    if (event === 'subscription-lost') this.subscriptionLostListeners.add(fn as SubscriptionLostListener);\n    return () => { this.off(event as never, fn as never); };\n  }\n\n  /** Remove a previously registered event listener. No-op if the listener is not found. */\n  off(event: 'reconnected', fn: Listener): void;\n  off(event: 'disconnected', fn: DisconnectListener): void;\n  off(event: 'queue-overflow', fn: QueueOverflowListener): void;\n  off(event: 'subscription-lost', fn: SubscriptionLostListener): void;\n  off(\n    event: 'disconnected' | 'reconnected' | 'queue-overflow' | 'subscription-lost',\n    fn: DisconnectListener | Listener | QueueOverflowListener | SubscriptionLostListener,\n  ): void {\n    if (event === 'disconnected') this.disconnectListeners.delete(fn as DisconnectListener);\n    if (event === 'reconnected') this.reconnectedListeners.delete(fn as Listener);\n    if (event === 'queue-overflow') this.queueOverflowListeners.delete(fn as QueueOverflowListener);\n    if (event === 'subscription-lost') this.subscriptionLostListeners.delete(fn as SubscriptionLostListener);\n  }\n\n  /**\n   * Register a global error listener for this device. Receives errors from internal\n   * operations (notification recovery, listener failures, auto-reconnect exhaustion).\n   *\n   * @param listener - Error callback with error and context.\n   * @returns Unsubscribe function.\n   */\n  addErrorListener(listener: ErrorListener): () => void {\n    this.errorListeners.add(listener);\n    return () => { this.removeErrorListener(listener); };\n  }\n\n  /** Remove a previously registered error listener. */\n  removeErrorListener(listener: ErrorListener): void {\n    this.errorListeners.delete(listener);\n  }\n\n  // --- Private ---\n\n  private handleDisconnect(): void {\n    const reason: DisconnectReason = this.intentionalDisconnect ? 'intentional' : 'unexpected';\n    this.lastDisconnectReason = reason;\n    this.writeChunker.abortInFlightWrites();\n    this.notificationManager.suspendSubscriptions();\n    this.serviceCache.clear();\n    this.primaryServicesCache = null;\n    this.charCache.clear();\n    this.server = null;\n    this.hooks.onConnectionChange?.(this);\n\n    // AIDEV-NOTE: Create the reconnect gate BEFORE firing disconnect listeners\n    // so that notifications() iterators can await it immediately.\n    if (this.recoveryRegistry.size > 0) {\n      let gateResolve: () => void;\n      const gatePromise = new Promise<void>((r) => { gateResolve = r; });\n      this.reconnectGate = { promise: gatePromise, resolve: gateResolve! };\n    }\n\n    for (const fn of this.disconnectListeners) {\n      try {\n        fn(reason);\n      } catch (error) {\n        this.emitError(BeacioError.from(error), { operation: 'device.disconnected-listener' });\n      }\n    }\n\n    // Auto-reconnect on unexpected disconnect\n    if (reason === 'unexpected' && this.autoReconnectConfig) {\n      this.startAutoReconnect(this.autoReconnectConfig);\n    }\n  }\n\n  private startAutoReconnect(config: AutoReconnectOptions): void {\n    this.autoReconnectAbort?.abort();\n    const controller = new AbortController();\n    this.autoReconnectAbort = controller;\n\n    const maxAttempts = config.maxAttempts ?? Infinity;\n    const initialDelay = config.initialDelayMs ?? 1000;\n    const maxDelay = config.maxDelayMs ?? 30000;\n    const multiplier = config.backoffMultiplier ?? 2;\n\n    const loop = async () => {\n      let delay = initialDelay;\n      for (let attempt = 1; attempt <= maxAttempts; attempt++) {\n        if (controller.signal.aborted) return;\n\n        await new Promise<void>((r) => {\n          const timer = setTimeout(r, delay);\n          controller.signal.addEventListener('abort', () => { clearTimeout(timer); r(); }, { once: true });\n        });\n        if (controller.signal.aborted) return;\n\n        try {\n          await this.connect();\n          return; // success — connect() fires reconnected listeners and resolves the gate\n        } catch {\n          delay = Math.min(delay * multiplier, maxDelay);\n        }\n      }\n      // Exhausted attempts — emit error so consumers know\n      this.emitError(\n        new BeacioError('CONNECTION_TIMEOUT', `Auto-reconnect failed after ${maxAttempts} attempts`),\n        { operation: 'device.auto-reconnect' },\n      );\n    };\n\n    void loop();\n  }\n\n  private async withOptionalTimeout<T>(operation: Promise<T>, timeoutMs: number | undefined, message: string): Promise<T> {\n    if (timeoutMs === undefined) return operation;\n\n    let timeoutId: ReturnType<typeof setTimeout> | null = null;\n    const timeoutPromise = new Promise<T>((_, reject) => {\n      timeoutId = setTimeout(() => {\n        reject(new BeacioError('TIMEOUT', message));\n      }, timeoutMs);\n    });\n\n    try {\n      return await Promise.race([operation, timeoutPromise]);\n    } finally {\n      if (timeoutId !== null) clearTimeout(timeoutId);\n    }\n  }\n\n  private validateTimeoutMs(timeoutMs: number | undefined): number | undefined {\n    if (timeoutMs === undefined) return undefined;\n    if (!Number.isFinite(timeoutMs) || timeoutMs <= 0) {\n      throw new BeacioError('INVALID_PARAMETER', `Invalid timeoutMs: ${timeoutMs}. Must be a positive number.`);\n    }\n    return timeoutMs;\n  }\n\n  /**\n   * Invoke each listener with `event`, routing any listener exception to\n   * {@link emitError} tagged with `operation`. Failures are isolated so one\n   * throwing listener does not prevent the rest from running.\n   */\n  private fanout<T>(listeners: Iterable<(event: T) => void>, event: T, operation: string): void {\n    for (const listener of listeners) {\n      try {\n        listener(event);\n      } catch (error) {\n        this.emitError(BeacioError.from(error), { operation });\n      }\n    }\n  }\n\n  private emitQueueOverflow(event: QueueOverflowEvent): void {\n    this.fanout(this.queueOverflowListeners, event, 'device.queue-overflow-listener');\n  }\n\n  private emitSubscriptionLost(event: SubscriptionLostEvent): void {\n    this.fanout(this.subscriptionLostListeners, event, 'device.subscription-lost-listener');\n  }\n\n  private emitError(error: Error, context: DeviceErrorContext): void {\n    for (const listener of this.errorListeners) {\n      try {\n        listener(error, context);\n      } catch {\n        // Avoid recursive error propagation from error listeners.\n      }\n    }\n  }\n\n  private async getCharacteristic(service: string, characteristic: string): Promise<BluetoothRemoteGATTCharacteristic> {\n    if (!this.connected) throw new BeacioError('DEVICE_DISCONNECTED');\n\n    const key = this.charKey(service, characteristic);\n    const cached = this.charCache.get(key);\n    if (cached) return cached;\n\n    const svc = await this.getService(service);\n    const charUUID = resolveUUID(characteristic);\n    try {\n      const char = await svc.getCharacteristic(charUUID);\n      this.charCache.set(key, char);\n      return char;\n    } catch (e) {\n      throw BeacioError.from(e);\n    }\n  }\n\n  private async getService(service: string): Promise<BluetoothRemoteGATTService> {\n    const uuid = resolveUUID(service);\n    if (this.primaryServicesCache) {\n      const discoveredService = this.primaryServicesCache.find((entry) => entry.uuid === uuid);\n      if (discoveredService) return discoveredService;\n    }\n\n    const cached = this.serviceCache.get(uuid);\n    if (cached) return cached;\n\n    try {\n      const svc = await this.server!.getPrimaryService(uuid);\n      this.serviceCache.set(uuid, svc);\n      return svc;\n    } catch (e) {\n      // SB-SDK-05 AC3: the dominant real failure for a vanilla site (e.g. S&B) is\n      // forgetting to declare a service in requestDevice({ optionalServices }).\n      // WebKit then rejects getPrimaryService with a SecurityError carrying terse,\n      // jargon-laden, UUID-LESS text — the one detail (which service) the operator\n      // needs to fix is dropped, and BeacioError.from would flatten it to a generic\n      // PERMISSION_DENIED. Re-raise it as a SecurityError DOMException whose message\n      // is a complete plain-English sentence naming THIS service's canonical UUID and\n      // the optionalServices remedy, with no leaked stack / native URL / jargon.\n      if (isUndeclaredServiceSecurityError(e)) {\n        throw undeclaredServiceError(uuid);\n      }\n      throw BeacioError.from(e);\n    }\n  }\n\n  private charKey(service: string, characteristic: string): string {\n    return `${resolveUUID(service)}:${resolveUUID(characteristic)}`;\n  }\n}\n","/**\n * Runtime platform where Beacio is executing.\n *\n * - `'auto'` -- Sentinel: resolve the real platform at runtime via {@link detectPlatform}.\n *   This is the default value of {@link BeacioOptions.platform}; it never appears as a\n *   resolved `Beacio.platform` value (that is always one of the three concrete states below).\n * - `'safari-extension'` -- iOS Safari with the Beacio extension installed\n * - `'native'` -- Browser with built-in Web Bluetooth (Chrome, Edge, etc.)\n * - `'unsupported'` -- No Web Bluetooth capability detected\n */\nexport type Platform = 'auto' | 'safari-extension' | 'native' | 'unsupported';\n\n/**\n * Configuration options for the {@link Beacio} constructor.\n *\n * All fields are required with documented sentinel defaults so the public surface has\n * no optional arguments. {@link DEFAULT_BEACIO_OPTIONS} is the canonical sentinel bag —\n * pass it (optionally spread with overrides) rather than constructing a partial object.\n */\nexport interface BeacioOptions {\n  /**\n   * Force a specific platform, or `'auto'` to auto-detect via {@link detectPlatform}.\n   * Sentinel: `'auto'` (auto-detect).\n   */\n  platform: Platform;\n  /**\n   * Maximum concurrently connected SDK-managed devices for this Beacio instance.\n   * Throws `CONNECTION_LIMIT_REACHED` when exceeded. Sentinel: `0` (unlimited).\n   */\n  maxConnections: number;\n  /**\n   * Service UUIDs to seed the instance-wide optionalServices registry once, so\n   * they are merged into every {@link Beacio.requestDevice} call's effective\n   * `optionalServices` without per-call boilerplate. Accepts names (`'battery_service'`),\n   * 4/8-hex, or full 128-bit UUIDs — each is resolved via {@link resolveUUID} and\n   * de-duped. Equivalent to calling {@link Beacio.registerServices} in the constructor.\n   * This NEVER widens the device picker (it does not touch `filters` or synthesize\n   * `acceptAllDevices`) — it only declares post-connection GATT access intent.\n   * Sentinel: `[]` (none).\n   */\n  defaultOptionalServices: string[];\n}\n\n/**\n * Canonical sentinel {@link BeacioOptions} bag. Pass this (optionally spread with\n * overrides) to the {@link Beacio} constructor instead of building a partial object:\n * `new Beacio({ ...DEFAULT_BEACIO_OPTIONS, maxConnections: 4 })`. Each field carries\n * the documented default: `'auto'` platform detection, `0` (unlimited) connections,\n * and an empty default-optional-services registry.\n */\nexport const DEFAULT_BEACIO_OPTIONS: BeacioOptions = {\n  platform: 'auto',\n  maxConnections: 0,\n  defaultOptionalServices: [],\n};\n\n/**\n * Declarative native periodic-write keep-warm request (SB-NAT-04).\n *\n * Poll-driven devices (e.g. S&B Venty/Veazy) emit no unsolicited notifications —\n * their telemetry only advances because the page writes a status-request frame\n * every ~500 ms. A backgrounded Safari tab issues no writes, so the session goes\n * stale. Declaring this lets the native iOS side re-issue the frame on a\n * **best-effort, battery-safe CLAMPED cadence** while it holds the connection in\n * the background, then route the reply through the same condition pipeline as a\n * pushed notification. The page only DECLARES intent — the cadence floor and\n * frame interpretation live in the native layer (a BLE invariant).\n */\nexport interface PeriodicWriteOptions {\n  /** GATT service UUID owning the writable control characteristic. Accepts names, 4/8-hex, or full 128-bit UUIDs. */\n  serviceUUID: BluetoothServiceUUID;\n  /** Writable characteristic the keep-warm frame is written to. Accepts names, 4/8-hex, or full 128-bit UUIDs. */\n  characteristicUUID: BluetoothCharacteristicUUID;\n  /** Exact bytes to write each tick (e.g. the device's status-request command). An empty array is a no-op. */\n  payload: number[];\n  /**\n   * Requested interval in milliseconds. NOTE: the native side clamps any\n   * sub-floor value UP to a battery-safe minimum and only polls within the\n   * granted iOS background window, so the page's foreground cadence (e.g. 500 ms)\n   * is **never guaranteed** in the background — this is a best-effort poll, not a\n   * real-time loop.\n   */\n  intervalMs: number;\n}\n\n/** Options for registering a background keep-alive connection. */\nexport interface BackgroundConnectionOptions {\n  /** Unique identifier of the device to maintain a background connection to (from `BeacioDevice.id`). */\n  deviceId: string;\n  /**\n   * Optional native periodic-write keep-warm poll (SB-NAT-04). Omit to hold the\n   * connection passively (the native side relays only values the device pushes).\n   * Supply it for poll-driven devices so telemetry keeps advancing while Safari\n   * is backgrounded, on a clamped best-effort cadence (see {@link PeriodicWriteOptions}).\n   */\n  periodicWrite?: PeriodicWriteOptions;\n}\n\n/**\n * Notification permission state, mirroring the Notification API.\n *\n * - `'granted'` -- User has allowed notifications\n * - `'denied'` -- User has blocked notifications\n * - `'prompt'` -- User has not yet been asked\n */\nexport type NotificationPermissionState = 'granted' | 'denied' | 'prompt';\n\n/**\n * Template for iOS notifications delivered by background sync.\n * Supports placeholder interpolation: `{{value.utf8}}`, `{{value.hex}}`, `{{deviceName}}`, `{{timestamp}}`.\n */\nexport interface NotificationTemplate {\n  /** Notification title. Supports `{{placeholder}}` interpolation. */\n  title: string;\n  /** Notification body text. Supports `{{placeholder}}` interpolation. */\n  body: string;\n  /** Deep-link URL opened when the user taps the notification. */\n  url?: string;\n  /** Play the default notification sound. Defaults to true when omitted. */\n  sound?: boolean;\n}\n\n/** Configuration for an interactive reply action on a notification. */\nexport interface ReplyActionConfig {\n  /** Button label shown in the notification (e.g. \"Reply\", \"Send Command\"). */\n  actionTitle: string;\n  /** Placeholder text in the reply text field. */\n  placeholder?: string;\n}\n\n/**\n * Decoder format applied to raw characteristic bytes before evaluating a {@link NotificationCondition}.\n * Determines how the raw `DataView` bytes are interpreted as a numeric value.\n */\nexport type ConditionDecoder = 'uint8' | 'int16be' | 'int16le' | 'int32be' | 'float32le' | 'float32be';\n\n/**\n * Comparison operator for evaluating whether a characteristic value should trigger a notification.\n *\n * - `'changed'` -- Fire when the decoded value differs from the previous reading\n * - `'always'` -- Fire on every characteristic update regardless of value\n * - Numeric operators compare the decoded value against {@link NotificationCondition.threshold}\n */\nexport type ConditionOperator = 'gt' | 'lt' | 'gte' | 'lte' | 'eq' | 'neq' | 'changed' | 'always';\n\n/** Condition that must be met for a background characteristic notification to fire. */\nexport type NotificationCondition = {\n  /** How to decode the raw characteristic bytes into a number. */\n  decode: ConditionDecoder;\n  /**\n   * Byte offset into the characteristic value where the scalar is decoded.\n   * Defaults to `0`. Lets a condition read past the start of a vendor frame —\n   * e.g. the S&B Venty/Veazy CMD `0x01` reply puts the settings bitmask at\n   * byte 14 and the auto-shutoff countdown at byte 9.\n   */\n  byteOffset?: number;\n  /**\n   * Optional bitwise-AND mask applied to the decoded **integer** before the\n   * comparison, to isolate a single status bit in a packed flags byte. For\n   * example, `{ decode: 'uint8', byteOffset: 14, mask: 0x02, operator: 'eq',\n   * threshold: 2 }` fires on Venty/Veazy \"setpoint reached\" (byte 14, bit 1).\n   * Ignored for the `float32*` decoders.\n   */\n  mask?: number;\n  /**\n   * Optional multiplier applied to the decoded value before the comparison, so\n   * a raw integer can be compared in engineering units (e.g. a uint16 that is\n   * `÷10 = °C` uses `scale: 0.1`, letting `threshold` be a real temperature).\n   */\n  scale?: number;\n} & (\n  {\n    /** Comparison operator for edge-triggered notifications that do not compare against a fixed threshold. */\n    operator: 'changed' | 'always';\n  } | {\n    /** Comparison operator to evaluate against `threshold`. */\n    operator: Exclude<ConditionOperator, 'changed' | 'always'>;\n    /** Reference value for numeric comparison operators. */\n    threshold: number;\n  }\n);\n\n/**\n * Options for registering background characteristic notifications.\n * When the condition is met, an iOS notification is delivered using the template.\n */\nexport interface CharacteristicNotificationOptions {\n  /** Device identifier (from `BeacioDevice.id`). */\n  deviceId: string;\n  /** GATT service UUID containing the characteristic. Accepts names, 4/8-hex, or full 128-bit UUIDs. */\n  serviceUUID: BluetoothServiceUUID;\n  /** GATT characteristic UUID to monitor. Accepts names, 4/8-hex, or full 128-bit UUIDs. */\n  characteristicUUID: BluetoothCharacteristicUUID;\n  /** Notification content template with placeholder support. */\n  template: NotificationTemplate;\n  /** Condition that must be satisfied for the notification to fire. */\n  condition: NotificationCondition;\n  /** Optional interactive reply action attached to the notification. */\n  replyAction?: ReplyActionConfig;\n  /** Minimum seconds between consecutive notifications for this registration. Prevents notification spam. */\n  cooldownSeconds?: number;\n}\n\n/** Filter criteria for beacon scanning. Multiple filters are OR-combined. */\nexport interface BeaconScanFilter {\n  /** Service UUIDs to match in advertisement data. */\n  services?: BluetoothServiceUUID[];\n  /** Match devices whose name starts with this prefix (case-sensitive). */\n  namePrefix?: string;\n}\n\n/** Options for registering a background beacon scan that delivers iOS notifications on discovery. */\nexport interface BeaconScanningOptions {\n  /** One or more filters to match against BLE advertisements. Filters are OR-combined. */\n  filters: BeaconScanFilter[];\n  /** Minimum seconds between consecutive beacon notifications. Prevents notification spam. */\n  cooldownSeconds?: number;\n  /** Notification content template delivered when a matching beacon is found. */\n  template: NotificationTemplate;\n}\n\n/**\n * Discriminator for background sync registration types.\n *\n * - `'connection'` -- Keep-alive device connection\n * - `'characteristic-notification'` -- Characteristic value monitoring with iOS notifications\n * - `'beacon-scan'` -- BLE advertisement scanning with iOS notifications\n */\nexport type BackgroundRegistrationType =\n  | 'connection'\n  | 'characteristic-notification'\n  | 'beacon-scan';\n\n/** Handle for an active background sync registration. Use to update or cancel the registration. */\nexport interface BackgroundRegistration {\n  /** Unique identifier for this registration. */\n  readonly id: string;\n  /** The kind of background operation this registration represents. */\n  readonly type: BackgroundRegistrationType;\n  /** Unix timestamp (ms) when the registration was created. */\n  readonly createdAt: number;\n  /** Unix timestamp (ms) of the last time this registration triggered a notification. */\n  readonly lastTriggeredAt?: number;\n  /** Cancel this registration and stop the background operation. */\n  unregister(): Promise<void>;\n  /** Update the notification template for this registration. */\n  update(template: Partial<NotificationTemplate>): Promise<void>;\n}\n\n/**\n * Background sync API for maintaining BLE connections and delivering iOS notifications\n * when Safari is not in the foreground.\n *\n * Access via `ble.backgroundSync`. Requires the companion app to be running in IPC relay mode.\n * Falls back to a stub that throws `BLUETOOTH_UNAVAILABLE` when Bluetooth is unavailable\n * or `GATT_OPERATION_FAILED` when the extension runtime is missing.\n */\nexport interface BeacioBackgroundSync {\n  /** Request iOS notification permission. Must be granted before registering notification-based syncs. */\n  requestPermission(): Promise<NotificationPermissionState>;\n  /** Register a keep-alive background connection to a device. */\n  requestBackgroundConnection(options: BackgroundConnectionOptions): Promise<BackgroundRegistration>;\n  /** Register characteristic monitoring with iOS notification delivery. */\n  registerCharacteristicNotifications(options: CharacteristicNotificationOptions): Promise<BackgroundRegistration>;\n  /** Register beacon scanning with iOS notification delivery on discovery. */\n  registerBeaconScanning(options: BeaconScanningOptions): Promise<BackgroundRegistration>;\n  /** List all active background sync registrations for the current origin. */\n  getRegistrations(): Promise<BackgroundRegistration[]>;\n  /** Cancel a registration by ID. */\n  unregister(registrationId: string): Promise<void>;\n  /** Update the notification template of an existing registration. */\n  update(registrationId: string, template: Partial<NotificationTemplate>): Promise<void>;\n  /** Release all resources held by the background sync manager. */\n  destroy(): void;\n  /** Alias for {@link requestBackgroundConnection}. */\n  connect(options: BackgroundConnectionOptions): Promise<BackgroundRegistration>;\n  /** Alias for {@link registerCharacteristicNotifications}. */\n  subscribe(options: CharacteristicNotificationOptions): Promise<BackgroundRegistration>;\n  /** Alias for {@link registerBeaconScanning}. */\n  scan(options: BeaconScanningOptions): Promise<BackgroundRegistration>;\n  /** Alias for {@link getRegistrations}. */\n  list(): Promise<BackgroundRegistration[]>;\n}\n\n/** Options for starting BLE peripheral advertising. */\nexport interface BeacioPeripheralAdvertisingOptions {\n  /** Local name included in advertisement data. */\n  localName?: string;\n  /** Services to register before advertising begins. */\n  services?: BeacioPeripheralServiceDefinition[];\n  /** Service UUIDs to include in advertisement packets. */\n  serviceUUIDs?: BluetoothServiceUUID[];\n  /** Manufacturer-specific data included in advertisements. */\n  manufacturerData?: Array<{\n    /** Bluetooth SIG company identifier (e.g. 0x004C for Apple). */\n    companyIdentifier: number;\n    /** Raw manufacturer data payload. */\n    data: BufferSource;\n  }>;\n  /** Service-specific data included in advertisements. */\n  serviceData?: Array<{\n    /** Service UUID this data is associated with. */\n    service: BluetoothServiceUUID;\n    /** Raw service data payload. */\n    data: BufferSource;\n  }>;\n  /** Whether the peripheral accepts incoming connections. Defaults to true. */\n  connectable?: boolean;\n  /** Transmit power level in dBm included in advertisements. */\n  txPower?: number;\n}\n\n/** Incoming write request from a connected central device. */\nexport interface BeacioPeripheralWriteRequest {\n  /** Platform device identifier of the central. */\n  deviceId?: string;\n  /** CoreBluetooth central UUID. */\n  centralUUID?: string;\n  /** Target service UUID. */\n  serviceUuid?: string;\n  /** Target characteristic UUID. */\n  characteristicUuid?: string;\n  /** Written value (type depends on platform encoding). */\n  value?: ArrayBuffer | DataView | number[];\n  /** Byte offset for prepared writes. */\n  offset?: number;\n  /** True if the central used write-without-response. */\n  withoutResponse?: boolean;\n}\n\n/** Connection state change event from a central device. */\nexport interface BeacioPeripheralConnectionStateChange {\n  /** Platform device identifier of the central. */\n  deviceId?: string;\n  /** CoreBluetooth central UUID. */\n  centralUUID?: string;\n  /** Whether the central is now connected. */\n  connected?: boolean;\n  /** Total number of currently subscribed centrals across all characteristics. */\n  subscriberCount?: number;\n}\n\n/** Subscription (notify/indicate) state change from a central device. */\nexport interface BeacioPeripheralSubscriptionChange {\n  /** Platform device identifier of the central. */\n  deviceId?: string;\n  /** CoreBluetooth central UUID. */\n  centralUUID?: string;\n  /** Service UUID of the subscribed characteristic. */\n  serviceUuid?: string;\n  /** Characteristic UUID the central subscribed to or unsubscribed from. */\n  characteristicUuid?: string;\n  /** Whether the central is now subscribed (true) or unsubscribed (false). */\n  subscribed?: boolean;\n  /** Total subscriber count for this characteristic after the change. */\n  subscriberCount?: number;\n}\n\n/** Options for sending a notification/indication to subscribed centrals. */\nexport interface BeacioPeripheralSendOptions {\n  /** Service UUID containing the characteristic. */\n  serviceUuid: string;\n  /** Characteristic UUID to send the value update on. */\n  characteristicUuid: string;\n  /** Raw value to send as a notification/indication. */\n  value: BufferSource;\n}\n\n/**\n * GATT characteristic property for peripheral-mode services.\n * Determines which operations centrals can perform on the characteristic.\n */\nexport type BeacioPeripheralCharacteristicProperty =\n  | 'read'\n  | 'write'\n  | 'writeWithoutResponse'\n  | 'notify'\n  | 'indicate';\n\n/** Definition of a characteristic within a peripheral-mode GATT service. */\nexport interface BeacioPeripheralCharacteristicDefinition {\n  /** Characteristic UUID. Mutually exclusive with `uuid` (provide one). */\n  characteristicUuid?: BluetoothCharacteristicUUID;\n  /** Characteristic UUID (alias). Mutually exclusive with `characteristicUuid`. */\n  uuid?: BluetoothCharacteristicUUID;\n  /** Supported operations. Array form or object form (`{ read: true, notify: true }`). */\n  properties?: BeacioPeripheralCharacteristicProperty[] | Partial<Record<BeacioPeripheralCharacteristicProperty, boolean>>;\n  /** ATT permission set. Accepts synonyms: `'writeable'`/`'writable'` are equivalent to `'write'`. */\n  permissions?: Array<'read' | 'readable' | 'write' | 'writeable' | 'writable'>;\n  /** Initial static value for read requests before any writes occur. */\n  value?: BufferSource;\n}\n\n/** Definition of a GATT service to register in peripheral mode. */\nexport interface BeacioPeripheralServiceDefinition {\n  /** Service UUID. Mutually exclusive with `uuid` (provide one). */\n  serviceUuid?: BluetoothServiceUUID;\n  /** Service UUID (alias). Mutually exclusive with `serviceUuid`. */\n  uuid?: BluetoothServiceUUID;\n  /** Whether this is a primary service. Defaults to true. */\n  isPrimary?: boolean;\n  /** Characteristics to include in this service. */\n  characteristics?: BeacioPeripheralCharacteristicDefinition[];\n}\n\n/** Snapshot of a registered peripheral characteristic at a point in time. */\nexport interface BeacioPeripheralCharacteristicRecord {\n  /** Canonical service UUID this characteristic belongs to. */\n  serviceUuid: string;\n  /** Canonical characteristic UUID. */\n  characteristicUuid: string;\n  /** List of property names this characteristic supports. */\n  properties: string[];\n  /** Current characteristic value as raw bytes. */\n  value: Uint8Array;\n  /** Number of centrals currently subscribed to this characteristic. */\n  subscriberCount: number;\n  /** Number of notification updates queued but not yet delivered. */\n  pendingNotifications: number;\n}\n\n/** Snapshot of a registered peripheral service at a point in time. */\nexport interface BeacioPeripheralServiceRecord {\n  /** Canonical service UUID. */\n  serviceUuid: string;\n  /** Whether this is a primary service. */\n  isPrimary: boolean;\n  /** Characteristics registered within this service. */\n  characteristics: BeacioPeripheralCharacteristicRecord[];\n}\n\n/** Result of sending a notification/indication to subscribed centrals. */\nexport interface BeacioPeripheralSendResult {\n  /** Whether the update was accepted by the platform (passed initial validation). */\n  accepted: boolean;\n  /** Whether the value was immediately transmitted to at least one central. */\n  sent: boolean;\n  /** Whether the update was queued for later delivery (e.g. central's transmit window full). */\n  queued: boolean;\n  /** Number of pending (queued but undelivered) updates for this characteristic. */\n  pendingCount: number;\n  /** Number of centrals currently subscribed to this characteristic. */\n  subscriberCount: number;\n  /** Service UUID of the updated characteristic. */\n  serviceUuid: string;\n  /** Characteristic UUID that was updated. */\n  characteristicUuid: string;\n}\n\n/** Event type map for the peripheral `addEventListener` interface. */\nexport interface BeacioPeripheralEventMap {\n  /** Fired when a connected central writes to a characteristic. */\n  writerequest: CustomEvent<BeacioPeripheralWriteRequest>;\n  /** Fired when a central subscribes to or unsubscribes from notifications. */\n  subscriptionchange: CustomEvent<BeacioPeripheralSubscriptionChange>;\n  /** Fired when a central connects or disconnects. */\n  connectionstatechange: CustomEvent<BeacioPeripheralConnectionStateChange>;\n  /** Fired when advertising state changes (started/stopped). */\n  advertisingstatechange: CustomEvent<{ advertising?: boolean; localName?: string | null; serviceUUIDs?: string[] }>;\n  /** Fired when a queued notification has been delivered and the characteristic is ready for more. */\n  notificationready: CustomEvent<BeacioPeripheralNotificationReady>;\n}\n\n/** Detail payload for the `notificationready` event. */\nexport interface BeacioPeripheralNotificationReady {\n  /** Service UUID of the characteristic that became ready. */\n  serviceUuid?: string;\n  /** Characteristic UUID that is ready to accept more notifications. */\n  characteristicUuid?: string;\n  /** Number of remaining queued updates after this delivery. */\n  pendingCount?: number;\n}\n\n/**\n * Peripheral-mode API for acting as a BLE GATT server.\n * Access via `ble.peripheral`. Supports service registration, advertising, and notification delivery.\n * Falls back to a stub that throws `GATT_OPERATION_FAILED` on unsupported platforms.\n */\nexport interface BeacioPeripheral {\n  /** Whether the peripheral is currently advertising. */\n  readonly advertising: boolean;\n  /** Start advertising with the given options. Registers any included services first. */\n  advertise(options: BeacioPeripheralAdvertisingOptions): Promise<void>;\n  /** Register a GATT service. Must be called before advertising if not included in advertise options. */\n  addService(service: BeacioPeripheralServiceDefinition): Promise<BeacioPeripheralServiceRecord>;\n  /** Stop advertising. Does not unregister services. */\n  stopAdvertising(): Promise<void>;\n  /** Send a notification/indication value update to all subscribed centrals. */\n  send(options: BeacioPeripheralSendOptions): Promise<BeacioPeripheralSendResult>;\n  /** Release all resources, stop advertising, and unregister services. */\n  destroy(): void;\n  /** Register an event listener. See {@link BeacioPeripheralEventMap} for event types. */\n  addEventListener(type: string, listener: EventListenerOrEventListenerObject | null, options?: boolean | AddEventListenerOptions): void;\n  /** Remove a previously registered event listener. */\n  removeEventListener(type: string, listener: EventListenerOrEventListenerObject | null, options?: boolean | EventListenerOptions): void;\n  /** Called when a central writes to a characteristic. */\n  onwriterequest: ((this: BeacioPeripheral, ev: Event) => void) | null;\n  /** Called when a central subscribes to or unsubscribes from notifications. */\n  onsubscriptionchange: ((this: BeacioPeripheral, ev: Event) => void) | null;\n  /** Called when a central connects or disconnects. */\n  onconnectionstatechange: ((this: BeacioPeripheral, ev: Event) => void) | null;\n  /** Called when advertising state changes. */\n  onadvertisingstatechange: ((this: BeacioPeripheral, ev: Event) => void) | null;\n  /** Called when a queued notification has been delivered and the characteristic is ready for more. */\n  onnotificationready: ((this: BeacioPeripheral, ev: Event) => void) | null;\n}\n\n/**\n * Filter criteria for BLE device discovery. Within a single filter, all specified\n * fields must match (AND logic). Multiple filters in `RequestDeviceOptions.filters`\n * are OR-combined.\n */\nexport interface BluetoothLEScanFilter {\n  /** Service UUIDs the device must advertise. Accepts names, 4/8-hex, or full 128-bit UUIDs. */\n  services?: string[];\n  /** Exact device name to match (case-sensitive). */\n  name?: string;\n  /** Match devices whose name starts with this prefix (case-sensitive). */\n  namePrefix?: string;\n  /** Filter by manufacturer-specific data. */\n  manufacturerData?: { companyIdentifier: number; dataPrefix?: BufferSource }[];\n  /** Filter by service data. */\n  serviceData?: { service: string; dataPrefix?: BufferSource }[];\n}\n\n/**\n * Options for `requestDevice()`. Define which BLE devices appear in the picker.\n *\n * **Filter semantics:**\n * - `filters` array entries are OR-combined (device matches if ANY filter matches)\n * - Within a single filter, all specified fields are AND-combined (device must match ALL)\n * - `exclusionFilters` are applied after `filters` to remove unwanted devices\n * - `acceptAllDevices: true` cannot be combined with `filters`\n *\n * **Service access:** Only services listed in `filters[].services` or `optionalServices`\n * can be accessed after connection. `optionalServices` does NOT affect the device picker --\n * it only declares post-connection GATT access intent.\n *\n * @see {@link BluetoothLEScanFilter}\n */\nexport interface RequestDeviceOptions {\n  /** Device filters. OR-combined; within each filter, fields are AND-combined. */\n  filters?: BluetoothLEScanFilter[];\n  /** Filters applied after `filters` to exclude specific devices from results. */\n  exclusionFilters?: BluetoothLEScanFilter[];\n  /** Additional service UUIDs the app needs access to post-connection (does NOT affect picker). */\n  optionalServices?: string[];\n  /** Manufacturer data IDs to request access to post-connection. */\n  optionalManufacturerData?: number[];\n  /** Accept any device without filtering. Cannot be combined with `filters`. */\n  acceptAllDevices?: boolean;\n}\n\n/** Emitted when the notification queue exceeds `maxQueueSize`. */\nexport interface QueueOverflowEvent {\n  /** Service UUID of the overflowing characteristic. */\n  service: string;\n  /** Characteristic UUID of the overflowing notification stream. */\n  characteristic: string;\n  /** The overflow strategy in effect when the overflow occurred. */\n  strategy: NotificationOverflowStrategy;\n  /** Maximum queue size that was exceeded. */\n  queueSize: number;\n  /** Cumulative count of dropped notifications since the subscription started. */\n  droppedCount: number;\n}\n\n/**\n * Eviction metadata for a NATIVE notification-queue overflow, decoded from the\n * `beacio:overflow` CustomEvent the polyfill dispatches on a\n * `BluetoothRemoteGATTCharacteristic` when Safari's bounded Swift `EventQueue`\n * evicts notifications under sustained high-frequency load.\n *\n * **Distinct from {@link QueueOverflowEvent}.** That event describes the *JS*\n * `device.notifications()` async-iterator queue overflowing (a client-side\n * backpressure mechanism the page configures). This event describes the\n * *native* bridge's own bounded queue dropping samples before they ever reach\n * JS — so the page learns it has a silent gap in its\n * `characteristicvaluechanged` stream and can re-read to resynchronise. The\n * fields differ accordingly; do not conflate the two.\n *\n * Each field is `undefined` only if the native bridge omitted it from the\n * signal (a forward-compat guard); a conforming bridge always supplies all four.\n *\n * @see {@link BeacioDevice.onCharacteristicOverflow}\n */\nexport interface NativeOverflowEvent {\n  /** Number of notifications the native queue evicted in this overflow. */\n  evictedCount?: number;\n  /** Capacity of the native bounded queue that was exceeded. */\n  queueCapacity?: number;\n  /** Next expected notification sequence number, so the page can quantify the gap. */\n  seq?: number;\n  /** Epoch-millis timestamp the native bridge stamped on the overflow. */\n  timestamp?: number;\n}\n\n/** Context information attached to device-level error events. */\nexport interface DeviceErrorContext {\n  /** The operation that triggered the error (e.g. `'device.subscribe.onError'`, `'notification.recover'`). */\n  operation: string;\n  /** Service UUID involved in the failed operation, when applicable. */\n  service?: string;\n  /** Characteristic UUID involved in the failed operation, when applicable. */\n  characteristic?: string;\n  /** Additional diagnostic details. */\n  details?: { [key: string]: string | number | boolean | null };\n}\n\n/**\n * Reason for a device disconnection.\n *\n * - `'intentional'` -- `disconnect()` was called by the application\n * - `'unexpected'` -- Connection dropped (out of range, device powered off, etc.)\n * - `'service-change'` -- Device's GATT database changed (firmware update, etc.)\n */\nexport type DisconnectReason = 'intentional' | 'unexpected' | 'service-change';\n\n/**\n * Backoff parameters for automatic reconnection after unexpected disconnects.\n *\n * **Backoff formula:** `delay = min(initialDelayMs * backoffMultiplier^(attempt-1), maxDelayMs)`\n *\n * @see {@link ConnectOptions.autoReconnect}\n */\nexport interface AutoReconnectOptions {\n  /** Maximum reconnection attempts before giving up. Defaults to Infinity. */\n  maxAttempts?: number;\n  /** Initial delay in ms before first reconnection attempt. Defaults to 1000. */\n  initialDelayMs?: number;\n  /** Maximum delay in ms between attempts (exponential backoff cap). Defaults to 30000. */\n  maxDelayMs?: number;\n  /** Backoff multiplier applied after each failed attempt. Defaults to 2. */\n  backoffMultiplier?: number;\n}\n\n/**\n * Configuration for the transparent foreground auto-reconnect supervisor that\n * `@beacio/core/auto` layers over the polyfilled `navigator.bluetooth` (SB-SDK-13).\n *\n * Unlike {@link AutoReconnectOptions}, this is read by the auto-install shim — NOT\n * by {@link ConnectOptions} — so raw-`navigator.bluetooth` consumers get foreground\n * reconnection + subscription recovery WITHOUT instantiating `Beacio` or rewriting\n * `requestDevice`. The supervisor is **default-on for the beacio runtime** and a\n * no-op everywhere else (native Chrome/Edge, the unsupported stub).\n *\n * Set it before the first `requestDevice()` by assigning `window.beacioAutoReconnect`:\n * ```js\n * // tune the backoff\n * window.beacioAutoReconnect = { initialDelayMs: 500, maxDelayMs: 10000 };\n * // or opt out entirely\n * window.beacioAutoReconnect = { enabled: false };\n * ```\n *\n * @see {@link AutoReconnectOptions} for the backoff-field semantics\n */\nexport interface RawAutoReconnectConfig extends AutoReconnectOptions {\n  /**\n   * Master switch for the auto-install supervisor. Defaults to `true` (on for the\n   * beacio runtime). Set `false` to keep the legacy raw behavior (the app's own\n   * `gattserverdisconnected` handler runs and nothing reconnects for it).\n   */\n  enabled?: boolean;\n}\n\n/**\n * Options for `device.connect()`.\n *\n * @see {@link AutoReconnectOptions}\n */\nexport interface ConnectOptions {\n  /**\n   * Automatically reconnect on unexpected disconnects using exponential backoff.\n   * Pass `true` for defaults (1s initial, 30s max, 2x multiplier, infinite attempts)\n   * or an {@link AutoReconnectOptions} object to customize.\n   * Auto-reconnect is stopped by calling `disconnect()`, reaching `maxAttempts`,\n   * or calling `connect()` again with new options.\n   */\n  autoReconnect?: boolean | AutoReconnectOptions;\n}\n\n/** Emitted when a subscription could not be recovered after reconnection (e.g. characteristic no longer exists). */\nexport interface SubscriptionLostEvent {\n  /** Service UUID of the lost subscription. */\n  service: string;\n  /** Characteristic UUID of the lost subscription. */\n  characteristic: string;\n  /** The error that prevented recovery. */\n  error: Error;\n}\n\n/** Snapshot of an active notification subscription's state. */\nexport interface ActiveSubscription {\n  /** Service UUID this subscription is registered on. */\n  service: string;\n  /** Characteristic UUID this subscription is receiving notifications from. */\n  characteristic: string;\n  /** Number of callback functions currently receiving notifications for this characteristic. */\n  callbackCount: number;\n  /** Whether this subscription will be automatically restored after reconnection. */\n  autoRecovering: boolean;\n  /** Whether the native BLE notification is currently active on the platform. */\n  nativeActive: boolean;\n}\n\n/** Options for `device.read()`. */\nexport interface ReadOptions {\n  /** Timeout in ms for the read operation. No timeout if omitted. */\n  timeoutMs?: number;\n}\n\n/**\n * Write mode for GATT characteristic writes.\n *\n * - `'with-response'` -- Write with acknowledgment (ATT Write Request). Slower but reliable.\n * - `'without-response'` -- Fire-and-forget write (ATT Write Command). Faster but no error feedback.\n */\nexport type WriteMode = 'with-response' | 'without-response';\n\n/** Options for single-packet `device.write()` operations. */\nexport interface WriteOptions {\n  /** Write mode. Defaults to `'with-response'`. */\n  mode?: WriteMode;\n  /** Timeout in ms for the write operation. No timeout if omitted. */\n  timeoutMs?: number;\n}\n\n/**\n * Options for `device.writeLarge()` -- chunked writes without per-chunk retry.\n *\n * **Chunk size determination order:**\n * 1. Explicit `chunkSize` if provided\n * 2. Platform-reported write limits (`getWriteLimits()`)\n * 3. `MTU - 3` (ATT header overhead)\n * 4. 20-byte conservative fallback\n *\n * @see {@link WriteFragmentedOptions} for chunked writes with per-chunk retry\n */\nexport interface WriteLargeOptions extends WriteOptions {\n  /**\n   * Explicit chunk size in bytes for segmented writes.\n   * When omitted, SDK uses platform write limits when available.\n   */\n  chunkSize?: number;\n}\n\n/** Result of a `device.writeLarge()` operation. */\nexport interface WriteLargeResult {\n  /** Total bytes successfully written. */\n  bytesWritten: number;\n  /** Total bytes in the original payload. */\n  totalBytes: number;\n  /** Chunk size used for segmentation. */\n  chunkSize: number;\n  /** Number of write operations performed. */\n  chunkCount: number;\n}\n\n/**\n * Options for `device.writeFragmented()` -- chunked writes with per-chunk retry.\n *\n * Extends {@link WriteLargeOptions} with retry semantics and an explicit MTU override.\n * The MTU-3 formula applies: BLE ATT header = 3 bytes, so max payload = MTU - 3.\n *\n * @see {@link WriteLargeOptions} for chunk size determination order\n * @see {@link WriteAutoOptions} for automatic fragmentation decisions\n */\nexport interface WriteFragmentedOptions extends WriteLargeOptions {\n  /**\n   * Optional MTU override. When provided, chunk size becomes `mtu - 3`\n   * unless `chunkSize` is set explicitly.\n   */\n  mtu?: number;\n  /** Retries per chunk before failing the whole write. Defaults to 0 (no retries). */\n  maxRetries?: number;\n  /** Delay in ms between chunk retries. Defaults to 0. */\n  retryDelayMs?: number;\n}\n\n/** Result of a `device.writeFragmented()` operation. Extends {@link WriteLargeResult} with retry count. */\nexport interface WriteFragmentedResult extends WriteLargeResult {\n  /** Total number of retries across all chunks. */\n  retryCount: number;\n}\n\n/** Options for `device.writeAuto()`. Inherits all fragmentation and retry options. */\nexport interface WriteAutoOptions extends WriteFragmentedOptions {}\n\n/** Result of a `device.writeAuto()` operation. Indicates whether fragmentation was used. */\nexport interface WriteAutoResult extends WriteFragmentedResult {\n  /** Whether the payload was split into multiple chunks (true) or sent as a single write (false). */\n  fragmented: boolean;\n}\n\n/**\n * Platform-reported write payload limits and negotiated ATT MTU.\n * Fields are `null` when the platform does not expose that information.\n *\n * @see {@link BeacioDevice.getWriteLimits}\n */\nexport interface WriteLimits {\n  /** Maximum payload bytes for write-with-response, or `null` if unknown. */\n  withResponse: number | null;\n  /** Maximum payload bytes for write-without-response, or `null` if unknown. */\n  withoutResponse: number | null;\n  /** Negotiated ATT MTU in bytes, or `null` if unknown. Max payload = `mtu - 3`. */\n  mtu: number | null;\n}\n\n/** Callback function invoked with each characteristic notification value. */\nexport type NotificationCallback = (value: DataView) => void;\n\n/**\n * Strategy for handling notification queue overflow.\n *\n * - `'error'` -- Throw an error and stop the notification stream (fail-fast)\n * - `'drop-oldest'` -- Discard the oldest buffered value to make room (lossy FIFO)\n * - `'drop-newest'` -- Discard the incoming value (backpressure)\n */\nexport type NotificationOverflowStrategy = 'error' | 'drop-oldest' | 'drop-newest';\n\n/**\n * Options for `device.notifications()` async iterator.\n *\n * @see {@link NotificationOverflowStrategy}\n */\nexport interface NotificationOptions {\n  /**\n   * Maximum buffered notification events before overflow handling applies.\n   * Defaults to 256. Increase for high-throughput characteristics.\n   */\n  maxQueueSize?: number;\n  /** Strategy for handling overflow. Defaults to `'error'`. */\n  overflowStrategy?: NotificationOverflowStrategy;\n  /** Callback invoked on every overflow event, regardless of strategy. */\n  onOverflow?: (event: QueueOverflowEvent) => void;\n}\n\n/**\n * Options for `device.subscribe()` and `device.subscribeAsync()`.\n *\n * @see {@link BeacioDevice.subscribe}\n * @see {@link BeacioDevice.subscribeAsync}\n */\nexport interface SubscribeOptions {\n  /** Automatically re-subscribe after reconnection. Defaults to `true`. */\n  autoRecover?: boolean;\n  /** Error callback for asynchronous setup failures. Only used by `subscribe()` (not `subscribeAsync()`). */\n  onError?: (error: Error) => void;\n}\n","import { BeacioDevice } from './device';\nimport { BeacioError } from './errors';\nimport { detectPlatform, getBluetoothAPI } from './platform';\nimport { resolveUUID } from './uuid';\nimport type {\n  BackgroundConnectionOptions,\n  BackgroundRegistration,\n  BeaconScanningOptions,\n  CharacteristicNotificationOptions,\n  NotificationTemplate,\n  Platform,\n  RequestDeviceOptions,\n  BeacioPeripheralServiceDefinition,\n  BeacioPeripheralServiceRecord,\n  BeacioBackgroundSync,\n  BeacioOptions,\n  BeacioPeripheral,\n  BeacioPeripheralAdvertisingOptions,\n  BeacioPeripheralSendOptions,\n  BeacioPeripheralSendResult,\n} from './types';\nimport { DEFAULT_BEACIO_OPTIONS } from './types';\n\ntype RuntimeBluetooth = Bluetooth & {\n  backgroundSync?: BeacioBackgroundSync;\n  peripheral?: BeacioPeripheral;\n};\n\nclass UnsupportedBackgroundSync implements BeacioBackgroundSync {\n  private readonly errorFactory: () => BeacioError;\n\n  constructor(errorFactory: () => BeacioError) {\n    this.errorFactory = errorFactory;\n  }\n\n  private unsupported(): never {\n    throw this.errorFactory();\n  }\n\n  requestPermission(): Promise<'granted' | 'denied' | 'prompt'> {\n    this.unsupported();\n  }\n\n  requestBackgroundConnection(_options: BackgroundConnectionOptions): Promise<BackgroundRegistration> {\n    this.unsupported();\n  }\n\n  registerCharacteristicNotifications(_options: CharacteristicNotificationOptions): Promise<BackgroundRegistration> {\n    this.unsupported();\n  }\n\n  registerBeaconScanning(_options: BeaconScanningOptions): Promise<BackgroundRegistration> {\n    this.unsupported();\n  }\n\n  getRegistrations(): Promise<BackgroundRegistration[]> {\n    this.unsupported();\n  }\n\n  unregister(_registrationId: string): Promise<void> {\n    this.unsupported();\n  }\n\n  update(_registrationId: string, _template: Partial<NotificationTemplate>): Promise<void> {\n    this.unsupported();\n  }\n\n  connect(options: BackgroundConnectionOptions): Promise<BackgroundRegistration> {\n    return this.requestBackgroundConnection(options);\n  }\n\n  subscribe(options: CharacteristicNotificationOptions): Promise<BackgroundRegistration> {\n    return this.registerCharacteristicNotifications(options);\n  }\n\n  scan(options: BeaconScanningOptions): Promise<BackgroundRegistration> {\n    return this.registerBeaconScanning(options);\n  }\n\n  list(): Promise<BackgroundRegistration[]> {\n    return this.getRegistrations();\n  }\n\n  destroy(): void {}\n}\n\nclass UnsupportedPeripheral extends EventTarget implements BeacioPeripheral {\n  private readonly errorFactory: () => BeacioError;\n\n  onwriterequest: ((this: BeacioPeripheral, ev: Event) => void) | null = null;\n  onsubscriptionchange: ((this: BeacioPeripheral, ev: Event) => void) | null = null;\n  onconnectionstatechange: ((this: BeacioPeripheral, ev: Event) => void) | null = null;\n  onadvertisingstatechange: ((this: BeacioPeripheral, ev: Event) => void) | null = null;\n  onnotificationready: ((this: BeacioPeripheral, ev: Event) => void) | null = null;\n\n  constructor(errorFactory: () => BeacioError) {\n    super();\n    this.errorFactory = errorFactory;\n  }\n\n  get advertising(): boolean {\n    return false;\n  }\n\n  private unsupported(): never {\n    throw this.errorFactory();\n  }\n\n  advertise(_options: BeacioPeripheralAdvertisingOptions): Promise<void> {\n    this.unsupported();\n  }\n\n  addService(_service: BeacioPeripheralServiceDefinition): Promise<BeacioPeripheralServiceRecord> {\n    this.unsupported();\n  }\n\n  stopAdvertising(): Promise<void> {\n    this.unsupported();\n  }\n\n  send(_options: BeacioPeripheralSendOptions): Promise<BeacioPeripheralSendResult> {\n    this.unsupported();\n  }\n\n  destroy(): void {}\n}\n\n/**\n * Core Beacio SDK entry point. Handles platform detection and device discovery.\n *\n * @example\n * ```typescript\n * import { Beacio } from '@beacio/core'\n *\n * const ble = new Beacio()\n * const device = await ble.requestDevice({\n *   filters: [{ services: ['heart_rate'] }]\n * })\n * await device.connect()\n * ```\n */\nexport class Beacio {\n  readonly platform: Platform;\n  readonly isSupported: boolean;\n  readonly maxConnections: number | null;\n\n  private bluetooth: Bluetooth | null;\n  private readonly runtimeBluetooth: RuntimeBluetooth | null;\n  private readonly unsupportedFeatureErrorFactory: () => BeacioError;\n  private readonly unsupportedBackgroundSync: BeacioBackgroundSync;\n  private readonly unsupportedPeripheral: BeacioPeripheral;\n  private readonly devices = new Map<string, BeacioDevice>();\n  /**\n   * Instance-wide optionalServices registry. Holds canonical 128-bit UUIDs\n   * (already {@link resolveUUID}-normalized); a Set both de-dups and preserves\n   * registration order. {@link requestDevice} unions this into the effective\n   * `optionalServices` of every call. Seeded from {@link BeacioOptions.defaultOptionalServices}.\n   */\n  private readonly registeredOptionalServices = new Set<string>();\n\n  constructor(options: BeacioOptions = DEFAULT_BEACIO_OPTIONS) {\n    // Sentinel resolution (see BeacioOptions / DEFAULT_BEACIO_OPTIONS):\n    //  - platform 'auto' → detect the real platform at runtime.\n    //  - maxConnections 0 → unlimited (null internally).\n    //  - defaultOptionalServices [] → seed nothing.\n    this.platform = options.platform === 'auto' ? detectPlatform() : options.platform;\n    this.maxConnections = this.normalizeMaxConnections(options.maxConnections);\n    if (options.defaultOptionalServices.length > 0) {\n      this.registerServices(options.defaultOptionalServices);\n    }\n    this.bluetooth = this.platform !== 'unsupported' ? getBluetoothAPI() : null;\n    this.runtimeBluetooth = this.bluetooth as RuntimeBluetooth | null;\n    this.isSupported = this.bluetooth !== null;\n    this.unsupportedFeatureErrorFactory = () => {\n      if (this.platform === 'unsupported') {\n        return new BeacioError('BLUETOOTH_UNAVAILABLE');\n      }\n      return new BeacioError(\n        'GATT_OPERATION_FAILED',\n        'This Beacio feature requires the iOS Safari Beacio extension runtime.',\n      );\n    };\n    this.unsupportedBackgroundSync = new UnsupportedBackgroundSync(this.unsupportedFeatureErrorFactory);\n    this.unsupportedPeripheral = new UnsupportedPeripheral(this.unsupportedFeatureErrorFactory);\n  }\n\n  /**\n   * Access the background sync API for maintaining BLE connections and delivering\n   * iOS notifications when Safari is not in the foreground.\n   *\n   * Requires the companion app running in IPC relay mode. Returns a stub that\n   * throws `BLUETOOTH_UNAVAILABLE` when Bluetooth is unavailable, or\n   * `GATT_OPERATION_FAILED` when the extension runtime is missing.\n   *\n   * @see {@link BeacioBackgroundSync}\n   */\n  get backgroundSync(): BeacioBackgroundSync {\n    return this.runtimeBluetooth?.backgroundSync ?? this.unsupportedBackgroundSync;\n  }\n\n  /**\n   * Access the peripheral-mode API for acting as a BLE GATT server.\n   *\n   * Allows registering services, advertising, and sending notifications to\n   * connected centrals. Returns a stub that throws `GATT_OPERATION_FAILED`\n   * on unsupported platforms.\n   *\n   * @see {@link BeacioPeripheral}\n   */\n  get peripheral(): BeacioPeripheral {\n    return this.runtimeBluetooth?.peripheral ?? this.unsupportedPeripheral;\n  }\n\n  /**\n   * Prompt the user to select a BLE device. Open the browser's device picker\n   * filtered by the given options.\n   *\n   * **Filter semantics:**\n   * - `filters` array entries are OR-combined -- a device matches if ANY filter matches\n   * - Within a single filter, all specified fields are AND-combined -- device must match ALL\n   * - `exclusionFilters` are applied after `filters` to remove unwanted matches\n   * - `acceptAllDevices: true` cannot be combined with `filters`\n   *\n   * **Service access:** Only services declared in `filters[].services` or `optionalServices`\n   * can be accessed after connection. `optionalServices` does NOT affect the picker -- it\n   * only declares post-connection GATT access intent.\n   *\n   * Service names (e.g. `'heart_rate'`) are resolved to full 128-bit UUIDs via {@link resolveUUID}.\n   *\n   * @param options - Device filter and service access options. Defaults to `{ acceptAllDevices: true }`.\n   * @returns A {@link BeacioDevice} wrapping the user-selected device.\n   *\n   * @throws {BeacioError} `BLUETOOTH_UNAVAILABLE` -- browser or platform does not support Web Bluetooth\n   * @throws {BeacioError} `USER_CANCELLED` -- user dismissed the device picker without selecting\n   * @throws {BeacioError} `DEVICE_NOT_FOUND` -- no devices matched the given filters\n   * @throws {BeacioError} `PERMISSION_DENIED` -- request was not triggered by a user gesture\n   *\n   * @example\n   * ```typescript\n   * // OR filter: match devices with heart_rate OR battery_service\n   * const device = await ble.requestDevice({\n   *   filters: [\n   *     { services: ['heart_rate'] },\n   *     { services: ['battery_service'] },\n   *   ],\n   * })\n   *\n   * // AND within filter: must have heart_rate AND name starting with \"Polar\"\n   * const device = await ble.requestDevice({\n   *   filters: [{ services: ['heart_rate'], namePrefix: 'Polar' }],\n   *   optionalServices: ['battery_service'],\n   * })\n   *\n   * // Accept all devices (no filtering)\n   * const device = await ble.requestDevice({ acceptAllDevices: true })\n   * ```\n   *\n   * @see {@link RequestDeviceOptions}\n   * @see {@link resolveUUID}\n   */\n  async requestDevice(options?: RequestDeviceOptions): Promise<BeacioDevice> {\n    if (!this.bluetooth) throw new BeacioError('BLUETOOTH_UNAVAILABLE');\n\n    try {\n      const device = await this.bluetooth.requestDevice(\n        (this.normalizeRequestDeviceOptions(options) ?? { acceptAllDevices: true }) as Parameters<Bluetooth['requestDevice']>[0],\n      );\n      return this.wrapDevice(device);\n    } catch (e) {\n      throw BeacioError.from(e, 'DEVICE_NOT_FOUND');\n    }\n  }\n\n  /**\n   * Register service UUIDs once for this instance so they are merged into the\n   * effective `optionalServices` of every subsequent {@link requestDevice} call —\n   * eliminating the per-call `optionalServices` boilerplate when a site or agent\n   * always needs the same allowlist (e.g. a vendor's full multi-family service\n   * bundle). Pairs with {@link BeacioOptions.defaultOptionalServices}.\n   *\n   * Accumulating and idempotent: each UUID is resolved via {@link resolveUUID}\n   * (names, 4/8-hex, or full 128-bit all accepted) to its canonical lowercase\n   * 128-bit form and stored in a de-duped, insertion-ordered set, so registering\n   * the same service twice — by alias or canonical form — is a no-op.\n   *\n   * **Picker-safe:** this declares post-connection GATT access intent ONLY. It\n   * never widens the device picker — it does not add to `filters`, does not\n   * synthesize `acceptAllDevices`, and the registered set is unioned into\n   * `optionalServices` (caller entries first), never a replacement.\n   *\n   * @param uuids - Service names, 4/8-hex, or full 128-bit UUID strings to register.\n   * @throws {TypeError} If a value is not a resolvable UUID or known SIG name (via {@link resolveUUID}).\n   *\n   * @example\n   * ```typescript\n   * import { Beacio } from '@beacio/core'\n   * import { StorzBickel } from '@beacio/core/experimental/profiles/storz-bickel'\n   *\n   * const ble = new Beacio()\n   * ble.registerServices(StorzBickel.allServices()) // declare every S&B family once\n   * // ...every requestDevice() now carries the bundle as optionalServices.\n   * ```\n   *\n   * @see {@link requestDevice}\n   * @see {@link BeacioOptions.defaultOptionalServices}\n   */\n  registerServices(uuids: string[]): void {\n    for (const uuid of uuids) {\n      this.registeredOptionalServices.add(resolveUUID(uuid));\n    }\n  }\n\n  /**\n   * Return previously granted devices without prompting the user.\n   * Only available on platforms that implement `Bluetooth.getDevices()` (e.g. Chrome).\n   * Returns an empty array when unsupported.\n   *\n   * @returns Array of previously paired {@link BeacioDevice} instances, or empty if unsupported.\n   * @throws {BeacioError} `BLUETOOTH_UNAVAILABLE` -- no Bluetooth API available\n   */\n  async getDevices(): Promise<BeacioDevice[]> {\n    if (!this.bluetooth) throw new BeacioError('BLUETOOTH_UNAVAILABLE');\n\n    const bluetoothWithGetDevices = this.bluetooth as Bluetooth & {\n      getDevices?: () => Promise<BluetoothDevice[]>;\n    };\n\n    if (typeof bluetoothWithGetDevices.getDevices !== 'function') {\n      return [];\n    }\n\n    try {\n      const devices = await bluetoothWithGetDevices.getDevices();\n      return devices.map((device) => this.wrapDevice(device));\n    } catch (error) {\n      throw BeacioError.from(error);\n    }\n  }\n\n  /**\n   * Check if Bluetooth is available on this device/browser.\n   * Returns `false` gracefully when the API is missing or throws.\n   *\n   * @returns `true` if Bluetooth is available and can be used for device discovery.\n   */\n  async getAvailability(): Promise<boolean> {\n    if (!this.bluetooth) return false;\n    try {\n      return await this.bluetooth.getAvailability();\n    } catch {\n      return false;\n    }\n  }\n\n  /**\n   * Start a BLE advertisement scan. Returns `null` when the platform does not support\n   * `Bluetooth.requestLEScan()`.\n   *\n   * @param options - Scan filter options. Defaults to accepting all advertisements.\n   * @returns A `BluetoothLEScan` handle to stop the scan, or `null` if unsupported.\n   * @throws {BeacioError} `BLUETOOTH_UNAVAILABLE` -- no Bluetooth API available\n   */\n  async requestLEScan(options: BluetoothLEScanOptions = { acceptAllAdvertisements: true }): Promise<BluetoothLEScan | null> {\n    if (!this.bluetooth) {\n      throw new BeacioError('BLUETOOTH_UNAVAILABLE');\n    }\n\n    const bluetoothWithScan = this.bluetooth as Bluetooth & {\n      requestLEScan?: (options?: BluetoothLEScanOptions) => Promise<BluetoothLEScan>;\n    };\n\n    if (typeof bluetoothWithScan.requestLEScan !== 'function') {\n      return null;\n    }\n\n    try {\n      return await bluetoothWithScan.requestLEScan(options);\n    } catch (error) {\n      throw BeacioError.from(error);\n    }\n  }\n\n  private normalizeRequestDeviceOptions(options?: RequestDeviceOptions): RequestDeviceOptions | undefined {\n    // The instance registry can contribute optionalServices even when the caller\n    // passes no options at all, so compute the effective list first and bail to\n    // `undefined` (→ the default acceptAllDevices) only when there is genuinely\n    // nothing to forward.\n    const effectiveOptionalServices = this.mergeOptionalServices(options?.optionalServices);\n\n    if (!options) {\n      return effectiveOptionalServices ? { optionalServices: effectiveOptionalServices } : undefined;\n    }\n\n    const normalizeServices = (services?: string[]): string[] | undefined => {\n      if (!services) return undefined;\n      return services.map((service) => resolveUUID(service));\n    };\n\n    const normalized: RequestDeviceOptions = {};\n\n    if (options.acceptAllDevices !== undefined) {\n      normalized.acceptAllDevices = options.acceptAllDevices;\n    }\n\n    if (options.optionalManufacturerData !== undefined) {\n      normalized.optionalManufacturerData = options.optionalManufacturerData;\n    }\n\n    if (options.filters) {\n      normalized.filters = options.filters.map((filter) => ({\n        ...filter,\n        services: normalizeServices(filter.services),\n      }));\n    }\n\n    if (options.exclusionFilters) {\n      normalized.exclusionFilters = options.exclusionFilters.map((filter) => ({\n        ...filter,\n        services: normalizeServices(filter.services),\n      }));\n    }\n\n    // Effective optionalServices = caller's list UNIONed with the instance\n    // registry, never a replacement. Omitted entirely (not `[]`) when both are\n    // empty so an empty registry stays a true no-op at the native boundary.\n    if (effectiveOptionalServices) {\n      normalized.optionalServices = effectiveOptionalServices;\n    }\n\n    return normalized;\n  }\n\n  /**\n   * Compute the effective `optionalServices` for a request: the de-duped UNION\n   * of the caller-supplied list (resolved + first, preserving caller order) and\n   * the instance registry (already canonical). Returns `undefined` when both are\n   * empty, so the caller can omit the key and keep an empty registry a no-op.\n   * This NEVER touches filters or the picker — it only assembles the access list.\n   */\n  private mergeOptionalServices(callerServices?: string[]): string[] | undefined {\n    if (!callerServices && this.registeredOptionalServices.size === 0) {\n      return undefined;\n    }\n    const merged = new Set<string>();\n    for (const service of callerServices ?? []) {\n      merged.add(resolveUUID(service));\n    }\n    for (const service of this.registeredOptionalServices) {\n      merged.add(service);\n    }\n    return [...merged];\n  }\n\n  private normalizeMaxConnections(maxConnections: number): number | null {\n    // Sentinel: 0 = unlimited (represented as null internally). Any other value\n    // must be a positive integer; negatives / fractions / NaN are rejected.\n    if (maxConnections === 0) return null;\n    if (!Number.isInteger(maxConnections) || maxConnections < 0) {\n      throw new BeacioError(\n        'INVALID_PARAMETER',\n        `Invalid maxConnections: ${maxConnections}. Must be 0 (unlimited) or a positive integer.`,\n      );\n    }\n    return maxConnections;\n  }\n\n  private wrapDevice(device: BluetoothDevice): BeacioDevice {\n    const existing = this.devices.get(device.id);\n    if (existing) {\n      return existing;\n    }\n\n    const wrapped = new BeacioDevice(device, {\n      beforeConnect: (nextDevice: BeacioDevice) => { this.assertConnectionCapacity(nextDevice); },\n      onConnectionChange: (nextDevice: BeacioDevice) => { this.devices.set(nextDevice.id, nextDevice); },\n    });\n    this.devices.set(device.id, wrapped);\n    return wrapped;\n  }\n\n  private assertConnectionCapacity(nextDevice: BeacioDevice): void {\n    if (this.maxConnections === null) return;\n\n    this.devices.set(nextDevice.id, nextDevice);\n    if (nextDevice.connected) return;\n\n    const connectedCount = [...this.devices.values()].filter((device) => device.connected).length;\n    if (connectedCount >= this.maxConnections) {\n      throw new BeacioError(\n        'CONNECTION_LIMIT_REACHED',\n        `Connection limit reached (${connectedCount}/${this.maxConnections}). Disconnect another device or increase maxConnections before connecting ${nextDevice.name ?? nextDevice.id}.`,\n        { retryAfterMs: 1000 },\n      );\n    }\n  }\n}\n","import { BeacioError } from './errors';\n\ndeclare const percentageBrand: unique symbol;\n\n/**\n * A validated integer percentage in [0, 100].\n *\n * Nominal/branded: a raw `number` can never be passed where a percentage is\n * required — the brand is attachable only by {@link percent} (strict) or\n * {@link clampPercent} (lenient), both of which guarantee `0 <= value <= 100`\n * and that the value is an integer. Typing a decoded battery level as\n * `Percentage` makes an out-of-range value (e.g. a stray 356% from an\n * unmasked uint16) unrepresentable at the type level — not just by a runtime\n * check at the call site.\n *\n * `units.ts` is the home for unit-of-measure invariants (percentage today;\n * future Celsius/decibel brands can co-locate here), keeping them distinct\n * from write/chunk concerns in {@link ./write-chunker}.\n */\nexport type Percentage = number & { readonly [percentageBrand]: true };\n\n/**\n * Strict smart-constructor for {@link Percentage}. Throws `INVALID_PARAMETER`\n * unless `n` is an integer in `0..100`. Use when the caller supplied an\n * explicit value that must be rejected (not silently corrected) if invalid.\n */\nexport function percent(n: number): Percentage {\n  if (!Number.isInteger(n) || n < 0 || n > 100) {\n    throw new BeacioError('INVALID_PARAMETER', `Invalid percent: ${n}. Must be an integer in 0..100.`);\n  }\n  return n as Percentage;\n}\n\n/**\n * Lenient smart-constructor for {@link Percentage}. Saturates any\n * number/`NaN`/non-integer input into `0..100` (`NaN` -> `0`). Never throws.\n *\n * Takes a plain `number` (not `number | null | undefined` like\n * {@link clampChunkSize}) because its inputs are concrete decoded bytes, never\n * a nullable platform-reported limit — honoring the repo's \"required fields,\n * no optional arguments\" convention. `Math.trunc` (not a saturating-only\n * clamp) guards against a fractional sneaking through from any non-byte caller;\n * for an integer byte it is a no-op.\n */\nexport function clampPercent(n: number): Percentage {\n  if (!Number.isFinite(n)) return 0 as Percentage;\n  return Math.min(100, Math.max(0, Math.trunc(n))) as Percentage;\n}\n"]}