@whanext/core 0.19.16 → 0.19.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,58 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.19.19
4
+
5
+ ### Provider
6
+ - Updated the official provider from `zapo-js@1.7.1` to `zapo-js@1.8.0`.
7
+ - Kept full received LID/PN addressing metadata when replying, reacting, editing, revoking, and pinning messages.
8
+ - Inherits Zapo 1.8.0 mailbox `fromMe` persistence fixes, group-status stanza attribute unwrapping, username support, expired-CDN media resend support, and mobile-primary WhatsApp Business support.
9
+
10
+ ### Incoming message observability
11
+ - Added `message_unavailable` tracking with primary-device resend correlation and a bounded 30-second recovery window.
12
+ - Added `debug_decrypted_payload` correlation with `debug_unhandled_stanza` so decrypted-but-undecodable payloads are visible without logging plaintext bytes.
13
+ - Expanded `health().messaging` with `decryptedPayloads`, `unavailable`, `resendRequested`, `recovered`, `recoveryFailed`, `unavailableUnrecoverable`, `decodeFailures`, `unhandledStanzas`, `ignoredOffline`, `duplicates`, and `normalizationFailures`.
14
+ - Added typed `messageUnavailable`, `messageRecovered`, `messageRecoveryFailed`, `messageDecodeFailure`, and `messageDiscarded` application events.
15
+ - Recovery failures and decrypted-payload decode failures temporarily mark provider stability as `degraded`; expected unrecoverable placeholders such as consumed view-once messages are counted without automatically degrading the session.
16
+
17
+ ### Safety
18
+ - Raw decrypted payload bytes from Zapo are never emitted through the WhaNext public events or written to logs.
19
+ - Existing `received`, `sent`, and `failed` counters keep their previous semantics; the new counters explain why an inbound stanza may not have become a public WhaNext `Message`.
20
+
21
+ ## 0.19.18
22
+
23
+ ### Stability
24
+ - Added bounded command queues for `strategy: 'queue'`: `maxQueue` defaults to 10 waiting executions and `queueTimeoutMs` defaults to 60 seconds. Set either option to `0` to disable that limit.
25
+ - Added `COMMAND_QUEUE_FULL` and `COMMAND_QUEUE_TIMEOUT` with typed `commandQueueFull` and `commandQueueTimeout` application events.
26
+ - Added explicit Zapo transport/query timeouts through `providerTimeouts.connectTimeoutMs` and `providerTimeouts.nodeQueryTimeoutMs`, defaulting to 15 seconds and 30 seconds.
27
+ - Stability event listeners are isolated from provider recovery work so consumer handler failures cannot turn a successful metadata recovery into a provider failure.
28
+
29
+ ### Health
30
+ - Expanded `app.health()` without removing the existing fields. It now exposes `stability`, connection/reconnect state, messaging counters, crypto degradation counters, group metadata recovery counters, command queue metrics, and effective provider timeouts.
31
+ - Added `healthy`, `degraded`, `reconnecting`, and `offline` operational stability states. Transient crypto/group warnings move the provider to `degraded` for a bounded window and automatically return it to `healthy`.
32
+ - Added typed `healthChanged`, `connectionRecovered`, `groupMetadataRecovered`, and `cryptoDegraded` events.
33
+
34
+ ### Performance
35
+ - Added `@zapo-js/native@0.1.0` as an optional dependency. Zapo automatically uses its X25519/XEdDSA accelerator when available and falls back to JavaScript when unavailable.
36
+ - Health reports the detected crypto backend as `napi`, `wasm`, `js`, or `unknown`; the published optional package is detected as WASM on supported Node runtimes while locally built N-API addons are reported only when they can actually be loaded.
37
+
38
+ ### Compatibility
39
+ - Existing command concurrency declarations remain valid; the queue limits only affect commands that already use `strategy: 'queue'`.
40
+ - Custom providers are not required to implement `health()`; WhaNext supplies neutral health fallbacks when a provider does not expose runtime metrics.
41
+
42
+ ## 0.19.17
43
+
44
+ ### Reliability
45
+ - Group sends now self-heal when Zapo reports an acknowledged participant-hash mismatch: WhaNext invalidates only the affected group's metadata cache, fetches fresh server metadata, invalidates the public group snapshot, and rate-limits recovery to once per group per minute.
46
+ - Volatile Zapo `groupMetadata` and `deviceList` memory caches now expire after 3 minutes instead of the 5-minute default, reducing stale LID/device fan-out windows without disabling caching or forcing an IQ/usync on every send.
47
+ - Transient WhatsApp disconnects now retry indefinitely by default with the existing exponential backoff; applications that set `reconnect.maxAttempts` keep their explicit limit. Fatal/logout reasons still stop reconnection immediately.
48
+
49
+ ### Performance
50
+ - The shared Zapo SQLite store now uses `WAL`, `synchronous=NORMAL`, and a 5-second `busy_timeout`, improving concurrent reads/writes for persisted Signal, sender-key, mailbox, and app-state data.
51
+ - The WhaNext mutation-snapshot SQLite database now uses the same WAL/NORMAL settings, reducing fsync contention on active bots.
52
+
53
+ ### Safety
54
+ - Sender keys and Signal sessions are never globally reset during mismatch recovery. Zapo's normal retry-receipt/decryption recovery remains authoritative; WhaNext only refreshes stale group metadata.
55
+
3
56
  ## 0.19.16
4
57
 
5
58
  ### Fixed
package/README.md CHANGED
@@ -311,20 +311,41 @@ const app = await create({
311
311
 
312
312
  ## Health checks
313
313
 
314
- `app.health()` entrega um snapshot sem consultar o WhatsApp novamente:
314
+ `app.health()` entrega um snapshot local e barato, sem consultar o WhatsApp novamente. Os campos antigos permanecem disponíveis e a camada de estabilidade acrescenta conexão, mensagens, crypto, grupos, filas e timeouts:
315
315
 
316
316
  ```ts
317
317
  const health = app.health();
318
318
 
319
319
  console.log(health.status);
320
+ console.log(health.stability);
320
321
  console.log(health.ready);
321
322
  console.log(health.state);
322
323
  console.log(health.uptimeMs);
323
- console.log(health.muteEnabled);
324
- console.log(health.logLevel);
324
+ console.log(health.connection);
325
+ console.log(health.messaging);
326
+ console.log(health.crypto);
327
+ console.log(health.groups);
328
+ console.log(health.commands);
329
+ console.log(health.timeouts);
325
330
  ```
326
331
 
327
- Estados possíveis: `idle`, `starting`, `ready` e `stopped`.
332
+ `status` continua representando o ciclo da aplicação: `idle`, `starting`, `ready` ou `stopped`. `stability` representa a saúde operacional do provider: `healthy`, `degraded`, `reconnecting` ou `offline`.
333
+
334
+ O provider Zapo contabiliza reconexões, mensagens enviadas/recebidas, falhas de envio, falhas de descriptografia, `sender key id mismatch`, falhas de addons, divergências de `phash` e recuperações de metadata. Com Zapo 1.8.0, `health.messaging` também mostra mensagens indisponíveis, pedidos de resend, recuperações, payloads descriptografados, falhas de decode, stanzas não tratadas, duplicatas, mensagens offline ignoradas e falhas de normalização. O router contabiliza execuções ativas, fila atual, expirações e rejeições por fila cheia.
335
+
336
+ ```ts
337
+ const { messaging } = app.health();
338
+
339
+ console.log(messaging.unavailable);
340
+ console.log(messaging.resendRequested);
341
+ console.log(messaging.recovered);
342
+ console.log(messaging.recoveryFailed);
343
+ console.log(messaging.decodeFailures);
344
+ console.log(messaging.ignoredOffline);
345
+ console.log(messaging.duplicates);
346
+ ```
347
+
348
+ `decryptedPayloads` conta payloads `<enc>` individuais, portanto pode ser maior que `received` em fanout para vários devices. O WhaNext usa esse sinal apenas para correlação e nunca expõe os bytes descriptografados no health, eventos públicos ou logs.
328
349
 
329
350
  ```ts
330
351
  server.get('/health', async () => app.health());
@@ -333,11 +354,87 @@ server.get('/health', async () => app.health());
333
354
  Para uma verificação simples:
334
355
 
335
356
  ```ts
336
- if (app.isReady) {
337
- console.log('Aplicação pronta.');
357
+ if (app.isReady && app.health().stability === 'healthy') {
358
+ console.log('Aplicação pronta e saudável.');
338
359
  }
339
360
  ```
340
361
 
362
+ ## Estabilidade do provider
363
+
364
+ O provider oficial usa timeouts explícitos para conexão e queries de protocolo. Os padrões são 15 segundos para conexão e 30 segundos para queries; ambos podem ser ajustados na criação da aplicação:
365
+
366
+ ```ts
367
+ const app = await create({
368
+ providerTimeouts: {
369
+ connectTimeoutMs: 15_000,
370
+ nodeQueryTimeoutMs: 30_000,
371
+ },
372
+ });
373
+ ```
374
+
375
+ Valores menores que 1 segundo são normalizados para 1 segundo. Os valores efetivos ficam disponíveis em `app.health().timeouts`.
376
+
377
+ A aceleração crypto do Zapo também é integrada de forma opcional. `@zapo-js/native` acelera X25519 e XEdDSA e o provider continua funcionando com o backend JavaScript caso o acelerador não esteja disponível. O pacote publicado usa WASM; um build N-API local também é detectado quando realmente carregável.
378
+
379
+ ```ts
380
+ const health = app.health();
381
+
382
+ console.log(health.crypto.backend);
383
+ console.log(health.crypto.acceleration);
384
+ ```
385
+
386
+ Backends reportados: `napi`, `wasm`, `js` ou `unknown`. Para forçar o fallback JavaScript, inicie o processo com `ZAPO_NATIVE_BACKEND=js`.
387
+
388
+ A aplicação expõe eventos tipados para observabilidade sem parsing de logs:
389
+
390
+ ```ts
391
+ app.on('healthChanged', ({ previous, current }) => {
392
+ console.log(previous, current);
393
+ });
394
+
395
+ app.on('connectionRecovered', ({ recoveredAt, reconnects }) => {
396
+ console.log(recoveredAt, reconnects);
397
+ });
398
+
399
+ app.on('groupMetadataRecovered', ({ groupId }) => {
400
+ console.log(groupId);
401
+ });
402
+
403
+ app.on('cryptoDegraded', ({ kind, chatId }) => {
404
+ console.log(kind, chatId);
405
+ });
406
+
407
+ app.on('messageUnavailable', ({ messageId, resendRequested }) => {
408
+ console.log(messageId, resendRequested);
409
+ });
410
+
411
+ app.on('messageRecovered', ({ messageId, recoveryMs }) => {
412
+ console.log(messageId, recoveryMs);
413
+ });
414
+
415
+ app.on('messageRecoveryFailed', ({ messageId, waitedMs }) => {
416
+ console.log(messageId, waitedMs);
417
+ });
418
+
419
+ app.on('messageDecodeFailure', ({ stanzaId, reason }) => {
420
+ console.log(stanzaId, reason);
421
+ });
422
+
423
+ app.on('messageDiscarded', ({ messageId, reason }) => {
424
+ console.log(messageId, reason);
425
+ });
426
+
427
+ app.on('commandQueueTimeout', ({ command, queuedForMs }) => {
428
+ console.log(command, queuedForMs);
429
+ });
430
+
431
+ app.on('commandQueueFull', ({ command, queued }) => {
432
+ console.log(command, queued);
433
+ });
434
+ ```
435
+
436
+ Falhas criptográficas transitórias deixam o provider em `degraded` por uma janela curta; a volta para `healthy` também dispara `healthChanged`. A recuperação de metadata por `phash` continua específica ao grupo afetado e não apaga Sender Keys nem sessões Signal.
437
+
341
438
  ## Usuários
342
439
 
343
440
  Toda mensagem possui `message.sender: User`. Menções ficam em `message.mentionedUsers`, e o remetente de um reply em `message.quoted?.sender`.
@@ -697,6 +794,8 @@ app.commands.command(
697
794
  scope: 'chat',
698
795
  max: 1,
699
796
  strategy: 'queue',
797
+ maxQueue: 10,
798
+ queueTimeoutMs: 60_000,
700
799
  },
701
800
 
702
801
  async execute(ctx) {
@@ -715,6 +814,21 @@ app.commands.command(
715
814
  );
716
815
  ```
717
816
 
817
+ Para `strategy: 'queue'`, o WhaNext limita por padrão a fila a 10 execuções aguardando e cada entrada pode esperar até 60 segundos. `maxQueue: 0` remove o limite de quantidade e `queueTimeoutMs: 0` remove o timeout. Quando os limites são atingidos, o router usa `COMMAND_QUEUE_FULL` ou `COMMAND_QUEUE_TIMEOUT` e emite os eventos tipados correspondentes.
818
+
819
+ ```ts
820
+ app.commands.onError(async (ctx, error) => {
821
+ if (error.code === 'COMMAND_QUEUE_FULL') {
822
+ await ctx.reply('Muitos comandos estão aguardando. Tente novamente em instantes.');
823
+ return;
824
+ }
825
+
826
+ if (error.code === 'COMMAND_QUEUE_TIMEOUT') {
827
+ await ctx.reply('A fila demorou demais. Envie o comando novamente.');
828
+ }
829
+ });
830
+ ```
831
+
718
832
  ### Comandos exclusivos do dono
719
833
 
720
834
  O dono é a própria conta conectada naquela aplicação. Não é necessário salvar número, JID ou LID manualmente. O provider usa a marca de mensagem enviada pela própria conta e, quando necessário, compara as identidades normalizadas da sessão.
package/dist/index.d.ts CHANGED
@@ -289,6 +289,8 @@ interface CallEvent {
289
289
  }
290
290
 
291
291
  type ConnectionState = 'idle' | 'connecting' | 'connected' | 'reconnecting' | 'closed';
292
+ type StabilityHealthStatus = 'healthy' | 'degraded' | 'reconnecting' | 'offline';
293
+ type CryptoAccelerationBackend = 'napi' | 'wasm' | 'js' | 'unknown';
292
294
  type PresenceState = 'typing' | 'recording' | 'paused';
293
295
  type ParticipantAction = 'remove' | 'promote' | 'demote';
294
296
  interface ParticipantUpdateResult {
@@ -301,6 +303,131 @@ interface ConnectionUpdate {
301
303
  attempt?: number;
302
304
  error?: Error;
303
305
  }
306
+ interface ProviderConnectionHealth {
307
+ state: ConnectionState;
308
+ uptimeMs: number;
309
+ reconnects: number;
310
+ reconnectAttempt: number;
311
+ lastConnectedAt?: Date;
312
+ lastDisconnectedAt?: Date;
313
+ }
314
+ interface ProviderMessagingHealth {
315
+ sent: number;
316
+ received: number;
317
+ failed: number;
318
+ decryptedPayloads: number;
319
+ unavailable: number;
320
+ resendRequested: number;
321
+ recovered: number;
322
+ recoveryFailed: number;
323
+ unavailableUnrecoverable: number;
324
+ decodeFailures: number;
325
+ unhandledStanzas: number;
326
+ ignoredOffline: number;
327
+ duplicates: number;
328
+ normalizationFailures: number;
329
+ lastIncomingAt?: Date;
330
+ lastOutgoingAt?: Date;
331
+ }
332
+ interface ProviderCryptoHealth {
333
+ backend: CryptoAccelerationBackend;
334
+ acceleration: boolean;
335
+ decryptFailures: number;
336
+ addonDecryptFailures: number;
337
+ senderKeyMismatches: number;
338
+ }
339
+ interface ProviderGroupHealth {
340
+ phashMismatches: number;
341
+ metadataRecoveries: number;
342
+ metadataRecoveryFailures: number;
343
+ }
344
+ interface ProviderTimeoutHealth {
345
+ connectTimeoutMs: number;
346
+ nodeQueryTimeoutMs: number;
347
+ }
348
+ interface ProviderHealth {
349
+ stability: StabilityHealthStatus;
350
+ connection: ProviderConnectionHealth;
351
+ messaging: ProviderMessagingHealth;
352
+ crypto: ProviderCryptoHealth;
353
+ groups: ProviderGroupHealth;
354
+ timeouts: ProviderTimeoutHealth;
355
+ }
356
+ interface GroupMetadataRecoveredEvent {
357
+ groupId: string;
358
+ recoveredAt: Date;
359
+ }
360
+ type CryptoDegradationKind = 'decrypt_failure' | 'addon_decrypt_failure' | 'sender_key_mismatch';
361
+ interface CryptoDegradedEvent {
362
+ kind: CryptoDegradationKind;
363
+ occurredAt: Date;
364
+ messageId?: string;
365
+ chatId?: string;
366
+ participantId?: string;
367
+ }
368
+ interface MessageUnavailableEvent {
369
+ kind: 'view_once' | 'hosted' | 'bot' | 'other';
370
+ resendRequested: boolean;
371
+ occurredAt: Date;
372
+ messageId?: string;
373
+ chatId?: string;
374
+ participantId?: string;
375
+ }
376
+ interface MessageRecoveredEvent {
377
+ recoveredAt: Date;
378
+ recoveryMs: number;
379
+ messageId?: string;
380
+ chatId?: string;
381
+ participantId?: string;
382
+ }
383
+ interface MessageRecoveryFailedEvent {
384
+ failedAt: Date;
385
+ waitedMs: number;
386
+ messageId?: string;
387
+ chatId?: string;
388
+ participantId?: string;
389
+ }
390
+ interface MessageDecodeFailureEvent {
391
+ occurredAt: Date;
392
+ reason: string;
393
+ stanzaId?: string;
394
+ chatId?: string;
395
+ encType?: string;
396
+ }
397
+ type MessageDiscardReason = 'offline' | 'duplicate' | 'normalization_failed';
398
+ interface MessageDiscardedEvent {
399
+ reason: MessageDiscardReason;
400
+ occurredAt: Date;
401
+ messageId?: string;
402
+ chatId?: string;
403
+ }
404
+ type ProviderStabilityEvent = {
405
+ type: 'groupMetadataRecovered';
406
+ payload: GroupMetadataRecoveredEvent;
407
+ } | {
408
+ type: 'cryptoDegraded';
409
+ payload: CryptoDegradedEvent;
410
+ } | {
411
+ type: 'messageUnavailable';
412
+ payload: MessageUnavailableEvent;
413
+ } | {
414
+ type: 'messageRecovered';
415
+ payload: MessageRecoveredEvent;
416
+ } | {
417
+ type: 'messageRecoveryFailed';
418
+ payload: MessageRecoveryFailedEvent;
419
+ } | {
420
+ type: 'messageDecodeFailure';
421
+ payload: MessageDecodeFailureEvent;
422
+ } | {
423
+ type: 'messageDiscarded';
424
+ payload: MessageDiscardedEvent;
425
+ } | {
426
+ type: 'healthRefresh';
427
+ payload: {
428
+ occurredAt: Date;
429
+ };
430
+ };
304
431
  interface ProviderEvents {
305
432
  message: Message;
306
433
  messageDeleted: MessageDeleted;
@@ -311,6 +438,7 @@ interface ProviderEvents {
311
438
  };
312
439
  groupParticipantsChanged: GroupParticipantsChanged;
313
440
  call: CallEvent;
441
+ stability: ProviderStabilityEvent;
314
442
  }
315
443
  type Unsubscribe = () => void;
316
444
  interface WhatsAppProvider {
@@ -319,6 +447,7 @@ interface WhatsAppProvider {
319
447
  getCurrentUserIds(): string[];
320
448
  requestPairingCode(phone: string): Promise<string>;
321
449
  on<Event extends keyof ProviderEvents>(event: Event, listener: (payload: ProviderEvents[Event]) => void | Promise<void>): Unsubscribe;
450
+ health?(): ProviderHealth;
322
451
  sendMessage(chatId: string, content: MessageContent, replyTo?: MessageKey): Promise<SentMessage>;
323
452
  repostMessage(source: MessageKey, chatId: string, options?: RepostMessageOptions): Promise<SentMessage>;
324
453
  reactToMessage(key: MessageKey, emoji?: string): Promise<SentMessage>;
@@ -616,7 +745,7 @@ declare class DeferredReply {
616
745
  delete(): Promise<void>;
617
746
  }
618
747
 
619
- type WhaNextErrorCode = 'AUTH_INVALID_PHONE' | 'AUTH_EXPIRED' | 'AUTH_PASSKEY_REQUIRED' | 'CONNECTION_CLOSED' | 'CONNECTION_FAILED' | 'GROUP_NOT_FOUND' | 'MEMBER_NOT_FOUND' | 'BOT_NOT_ADMIN' | 'MESSAGE_NOT_FOUND' | 'MEDIA_NOT_AVAILABLE' | 'MESSAGE_REACHOUT_LOCKED' | 'MUTE_DISABLED' | 'STORAGE_ERROR' | 'ARGUMENT_MISSING' | 'ARGUMENT_INVALID' | 'COMMAND_NOT_ALLOWED' | 'COMMAND_COOLDOWN' | 'COMMAND_BUSY' | 'COMMAND_LOAD_FAILED' | 'PROVIDER_ERROR' | 'UNKNOWN_ERROR';
748
+ type WhaNextErrorCode = 'AUTH_INVALID_PHONE' | 'AUTH_EXPIRED' | 'AUTH_PASSKEY_REQUIRED' | 'CONNECTION_CLOSED' | 'CONNECTION_FAILED' | 'GROUP_NOT_FOUND' | 'MEMBER_NOT_FOUND' | 'BOT_NOT_ADMIN' | 'MESSAGE_NOT_FOUND' | 'MEDIA_NOT_AVAILABLE' | 'MESSAGE_REACHOUT_LOCKED' | 'MUTE_DISABLED' | 'STORAGE_ERROR' | 'ARGUMENT_MISSING' | 'ARGUMENT_INVALID' | 'COMMAND_NOT_ALLOWED' | 'COMMAND_COOLDOWN' | 'COMMAND_BUSY' | 'COMMAND_QUEUE_FULL' | 'COMMAND_QUEUE_TIMEOUT' | 'COMMAND_LOAD_FAILED' | 'PROVIDER_ERROR' | 'UNKNOWN_ERROR';
620
749
  interface WhaNextErrorOptions {
621
750
  cause?: unknown;
622
751
  context?: Readonly<Record<string, unknown>>;
@@ -656,6 +785,8 @@ interface CommandConcurrency {
656
785
  max?: number;
657
786
  scope?: CommandScope;
658
787
  strategy?: ConcurrencyStrategy;
788
+ maxQueue?: number;
789
+ queueTimeoutMs?: number;
659
790
  }
660
791
  interface CommandLocalization {
661
792
  name?: string;
@@ -710,6 +841,37 @@ declare function defineCommandGroup<const Group extends CommandGroupDefinition>(
710
841
  declare function defineCommands<const Commands extends readonly CommandDefinition[]>(...commands: Commands): Commands;
711
842
  declare function isCommandGroup(definition: CommandDefinition): definition is CommandGroupDefinition;
712
843
 
844
+ interface CommandConcurrencyContext {
845
+ command: string;
846
+ messageId: string;
847
+ chatId: string;
848
+ userId: string;
849
+ }
850
+ interface CommandQueueTimeoutEvent extends CommandConcurrencyContext {
851
+ key: string;
852
+ queuedForMs: number;
853
+ queueTimeoutMs: number;
854
+ queued: number;
855
+ }
856
+ interface CommandQueueFullEvent extends CommandConcurrencyContext {
857
+ key: string;
858
+ maxQueue: number;
859
+ queued: number;
860
+ }
861
+ interface CommandConcurrencyHealth {
862
+ running: number;
863
+ queued: number;
864
+ queueTimeouts: number;
865
+ queueRejected: number;
866
+ }
867
+ type CommandConcurrencyEvent = {
868
+ type: 'commandQueueTimeout';
869
+ payload: CommandQueueTimeoutEvent;
870
+ } | {
871
+ type: 'commandQueueFull';
872
+ payload: CommandQueueFullEvent;
873
+ };
874
+
713
875
  interface CommandRegistrar {
714
876
  command(definition: CommandDefinition): unknown;
715
877
  }
@@ -743,6 +905,10 @@ interface CommandHelpOptions extends CommandCatalogOptions {
743
905
  title?: string;
744
906
  }
745
907
  type CommandErrorHandler = (context: CommandContext, error: WhaNextError) => void | Promise<void>;
908
+ interface CommandRouterEvents {
909
+ commandQueueTimeout: CommandQueueTimeoutEvent;
910
+ commandQueueFull: CommandQueueFullEvent;
911
+ }
746
912
  declare class CommandRouter {
747
913
  #private;
748
914
  constructor(services: CommandRuntimeServices, options?: RouterOptions);
@@ -751,6 +917,8 @@ declare class CommandRouter {
751
917
  get prefixes(): readonly string[];
752
918
  setPrefixes(prefixes: string | readonly string[]): this;
753
919
  get size(): number;
920
+ health(): CommandConcurrencyHealth;
921
+ on<Event extends keyof CommandRouterEvents>(event: Event, listener: (payload: CommandRouterEvents[Event]) => void | Promise<void>): () => void;
754
922
  command(definition: CommandDefinition): this;
755
923
  load(dirPath: string | URL, options?: LoadCommandsOptions): Promise<LoadCommandsResult>;
756
924
  use(middleware: CommandMiddleware): this;
@@ -808,6 +976,15 @@ declare class AccountService {
808
976
  isOwner(message: Pick<Message, 'keys' | 'senderIds'>): boolean;
809
977
  }
810
978
 
979
+ interface HealthChangedEvent {
980
+ previous: StabilityHealthStatus;
981
+ current: StabilityHealthStatus;
982
+ health: AppHealth;
983
+ }
984
+ interface ConnectionRecoveredEvent {
985
+ recoveredAt: Date;
986
+ reconnects: number;
987
+ }
811
988
  interface AppEvents {
812
989
  message: Message;
813
990
  messageDeleted: MessageDeleted;
@@ -817,6 +994,17 @@ interface AppEvents {
817
994
  mute: MuteEnforcement;
818
995
  call: CallEvent;
819
996
  groupParticipantsChanged: GroupParticipantsChanged;
997
+ healthChanged: HealthChangedEvent;
998
+ connectionRecovered: ConnectionRecoveredEvent;
999
+ groupMetadataRecovered: GroupMetadataRecoveredEvent;
1000
+ cryptoDegraded: CryptoDegradedEvent;
1001
+ messageUnavailable: MessageUnavailableEvent;
1002
+ messageRecovered: MessageRecoveredEvent;
1003
+ messageRecoveryFailed: MessageRecoveryFailedEvent;
1004
+ messageDecodeFailure: MessageDecodeFailureEvent;
1005
+ messageDiscarded: MessageDiscardedEvent;
1006
+ commandQueueTimeout: CommandQueueTimeoutEvent;
1007
+ commandQueueFull: CommandQueueFullEvent;
820
1008
  }
821
1009
  interface LoginOptions {
822
1010
  onCode?: (code: string) => void | Promise<void>;
@@ -825,12 +1013,19 @@ interface LoginOptions {
825
1013
  type AppHealthStatus = 'idle' | 'starting' | 'ready' | 'stopped';
826
1014
  interface AppHealth {
827
1015
  status: AppHealthStatus;
1016
+ stability: StabilityHealthStatus;
828
1017
  state: ConnectionUpdate['state'];
829
1018
  ready: boolean;
830
1019
  uptimeMs: number;
831
1020
  timestamp: Date;
832
1021
  muteEnabled: boolean;
833
1022
  logLevel: LogLevel;
1023
+ connection: ProviderConnectionHealth;
1024
+ messaging: ProviderMessagingHealth;
1025
+ crypto: ProviderCryptoHealth;
1026
+ groups: ProviderGroupHealth;
1027
+ commands: CommandConcurrencyHealth;
1028
+ timeouts: ProviderTimeoutHealth;
834
1029
  }
835
1030
  interface WhaNextAppOptions {
836
1031
  phone?: string;
@@ -869,6 +1064,10 @@ declare enum Browser {
869
1064
  Ubuntu = "ubuntu"
870
1065
  }
871
1066
 
1067
+ interface ProviderTimeoutOptions {
1068
+ connectTimeoutMs?: number;
1069
+ nodeQueryTimeoutMs?: number;
1070
+ }
872
1071
  interface ReconnectOptions {
873
1072
  enabled?: boolean;
874
1073
  maxAttempts?: number;
@@ -885,6 +1084,7 @@ interface CreateOptions {
885
1084
  mute?: MuteOptions;
886
1085
  router?: Omit<RouterOptions, 'prefix'>;
887
1086
  reconnect?: ReconnectOptions;
1087
+ providerTimeouts?: ProviderTimeoutOptions;
888
1088
  messageCacheSize?: number;
889
1089
  processOfflineMessages?: boolean;
890
1090
  provider?: WhatsAppProvider;
@@ -972,4 +1172,4 @@ declare class SqliteMuteStore implements MuteStore {
972
1172
  close(): void;
973
1173
  }
974
1174
 
975
- export { AccountService, type AddMuteOptions, type AddMuteResult, type AppEvents, type AppHealth, type AppHealthStatus, ArgsParser, type AudioContent, type BooleanOption, Browser, type ButtonsContent, type CacheOptions, type CacheStore, type CallEvent, type CallStatus, type ChangeResult, type CommandAccountContext, type CommandCatalogOptions, type CommandCatalogView, type CommandChatContext, type CommandConcurrency, type CommandContext, type CommandCooldown, type CommandDefinition, type CommandErrorHandler, type CommandGroupContext, type CommandGroupDefinition, type CommandGuard, type CommandHelpOptions, type CommandHooks, type CommandLocalization, type CommandMetadata, type CommandMiddleware, type CommandOptionDefinition, type CommandOptionSchema, type CommandOptionValue, type CommandOptionValues, type CommandRegistrar, CommandRouter, type CommandRuntimeServices, type CommandScope, type ConcurrencyStrategy, type ConnectionState, type ConnectionUpdate, type CopyCodeButton, type CreateMultiOptions, type CreateOptions, DeferredReply, type DownloadedMedia, type DurationOption, type EnumOption, type ExecutableCommandDefinition, type GroupAccess, type GroupAddressingMode, type GroupParticipant, type GroupParticipantAction, type GroupParticipantsChanged, type GroupRole, type GroupSnapshot, type GuardResult, type ImageContent, type InteractiveResponse, type InteractiveResponseKind, type InviteResult, type LinkButton, type ListContent, type ListRow, type ListSection, type LoadCommandsOptions, type LoadCommandsResult, type LoadedCommand, type LogContext, type LogEntry, type LogFormat, type LogLevel, type LogWriter, Logger, type LoggerConfig, type LoggerOptions, type LoginOptions, type MediaKind, type MediaSource, type MemberActionState, MemoryCache, type MemoryCacheStats, type MentionTarget, type Message, type MessageButton, type MessageContent, type MessageContentKind, type MessageDeleted, type MessageEdited, type MessageKey, type MessageMedia, type MessagePayloadKind, type MessageProtocolKind, type MultiAccountOptions, type MultiAppEvent, type MultiAppHealth, MultiCommandRouter, type MultiLoadCommandsResult, type MultiLoginOptions, type MuteChangeResult, type MuteEnforcement, type MuteOptions, type MuteRecord, MuteService, type MuteStore, type NumberOption, ParsedCommandOptions, type ParticipantUpdateResult, type PollContent, type PresenceState, type QuickReplyButton, type QuotedMessage, type ReconnectOptions, type RegisteredCommand, type RemoveMuteResult, type ReplyOptions, type RepostMessageOptions, type RouterOptions, type SentMessage, SqliteMuteStore, type StickerContent, type StoredMute, type StringOption, type TextContent, User, type UserData, type UserOption, type VideoContent, WhaNextApp, WhaNextError, type WhaNextErrorCode, WhaNextMultiApp, type WhatsAppProvider, create, createMulti, defineCommand, defineCommandGroup, defineCommands, defineSubcommand, guards, isCommandGroup, loadCommands, option, toWhaNextError };
1175
+ export { AccountService, type AddMuteOptions, type AddMuteResult, type AppEvents, type AppHealth, type AppHealthStatus, ArgsParser, type AudioContent, type BooleanOption, Browser, type ButtonsContent, type CacheOptions, type CacheStore, type CallEvent, type CallStatus, type ChangeResult, type CommandAccountContext, type CommandCatalogOptions, type CommandCatalogView, type CommandChatContext, type CommandConcurrency, type CommandConcurrencyContext, type CommandConcurrencyEvent, type CommandConcurrencyHealth, type CommandContext, type CommandCooldown, type CommandDefinition, type CommandErrorHandler, type CommandGroupContext, type CommandGroupDefinition, type CommandGuard, type CommandHelpOptions, type CommandHooks, type CommandLocalization, type CommandMetadata, type CommandMiddleware, type CommandOptionDefinition, type CommandOptionSchema, type CommandOptionValue, type CommandOptionValues, type CommandQueueFullEvent, type CommandQueueTimeoutEvent, type CommandRegistrar, CommandRouter, type CommandRouterEvents, type CommandRuntimeServices, type CommandScope, type ConcurrencyStrategy, type ConnectionRecoveredEvent, type ConnectionState, type ConnectionUpdate, type CopyCodeButton, type CreateMultiOptions, type CreateOptions, type CryptoAccelerationBackend, type CryptoDegradationKind, type CryptoDegradedEvent, DeferredReply, type DownloadedMedia, type DurationOption, type EnumOption, type ExecutableCommandDefinition, type GroupAccess, type GroupAddressingMode, type GroupMetadataRecoveredEvent, type GroupParticipant, type GroupParticipantAction, type GroupParticipantsChanged, type GroupRole, type GroupSnapshot, type GuardResult, type HealthChangedEvent, type ImageContent, type InteractiveResponse, type InteractiveResponseKind, type InviteResult, type LinkButton, type ListContent, type ListRow, type ListSection, type LoadCommandsOptions, type LoadCommandsResult, type LoadedCommand, type LogContext, type LogEntry, type LogFormat, type LogLevel, type LogWriter, Logger, type LoggerConfig, type LoggerOptions, type LoginOptions, type MediaKind, type MediaSource, type MemberActionState, MemoryCache, type MemoryCacheStats, type MentionTarget, type Message, type MessageButton, type MessageContent, type MessageContentKind, type MessageDecodeFailureEvent, type MessageDeleted, type MessageDiscardReason, type MessageDiscardedEvent, type MessageEdited, type MessageKey, type MessageMedia, type MessagePayloadKind, type MessageProtocolKind, type MessageRecoveredEvent, type MessageRecoveryFailedEvent, type MessageUnavailableEvent, type MultiAccountOptions, type MultiAppEvent, type MultiAppHealth, MultiCommandRouter, type MultiLoadCommandsResult, type MultiLoginOptions, type MuteChangeResult, type MuteEnforcement, type MuteOptions, type MuteRecord, MuteService, type MuteStore, type NumberOption, ParsedCommandOptions, type ParticipantUpdateResult, type PollContent, type PresenceState, type ProviderConnectionHealth, type ProviderCryptoHealth, type ProviderGroupHealth, type ProviderHealth, type ProviderMessagingHealth, type ProviderStabilityEvent, type ProviderTimeoutHealth, type ProviderTimeoutOptions, type QuickReplyButton, type QuotedMessage, type ReconnectOptions, type RegisteredCommand, type RemoveMuteResult, type ReplyOptions, type RepostMessageOptions, type RouterOptions, type SentMessage, SqliteMuteStore, type StabilityHealthStatus, type StickerContent, type StoredMute, type StringOption, type TextContent, User, type UserData, type UserOption, type VideoContent, WhaNextApp, WhaNextError, type WhaNextErrorCode, WhaNextMultiApp, type WhatsAppProvider, create, createMulti, defineCommand, defineCommandGroup, defineCommands, defineSubcommand, guards, isCommandGroup, loadCommands, option, toWhaNextError };