zaileys 4.14.0 → 4.15.0

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.
Files changed (42) hide show
  1. package/README.md +22 -23
  2. package/dist/auth/adapters/file.d.ts +11 -0
  3. package/dist/auth/adapters/memory.d.ts +1 -0
  4. package/dist/auth/adapters/postgres.d.ts +3 -0
  5. package/dist/auth/adapters/redis.d.ts +2 -0
  6. package/dist/auth/adapters/sqlite.d.ts +3 -0
  7. package/dist/auth/types.d.ts +12 -0
  8. package/dist/automation/errors.d.ts +1 -1
  9. package/dist/automation/presence.d.ts +8 -0
  10. package/dist/automation/queue.d.ts +5 -0
  11. package/dist/automation/rate-limiter.d.ts +1 -0
  12. package/dist/automation/schedule.d.ts +6 -0
  13. package/dist/automation/types.d.ts +4 -0
  14. package/dist/builder/builder.d.ts +9 -0
  15. package/dist/builder/content/airich.d.ts +7 -0
  16. package/dist/builder/content/buttons.d.ts +2 -0
  17. package/dist/builder/content/html-app.d.ts +26 -0
  18. package/dist/builder/index.d.ts +2 -0
  19. package/dist/builder/media-loader.d.ts +15 -1
  20. package/dist/client/client.d.ts +9 -0
  21. package/dist/client/types.d.ts +32 -0
  22. package/dist/cloud/transport.d.ts +1 -0
  23. package/dist/cloud/types.d.ts +5 -0
  24. package/dist/cloud/webhook.d.ts +5 -0
  25. package/dist/connection/auth-adapter.d.ts +6 -0
  26. package/dist/connection/disconnect-reason.d.ts +8 -1
  27. package/dist/events/context.d.ts +8 -0
  28. package/dist/events/guards.d.ts +5 -1
  29. package/dist/events/pipeline.d.ts +7 -0
  30. package/dist/index.cjs +17 -17
  31. package/dist/index.mjs +17 -17
  32. package/dist/media/ffmpeg/core.d.ts +23 -0
  33. package/dist/store/adapters/convex.d.ts +1 -0
  34. package/dist/store/adapters/memory.d.ts +1 -0
  35. package/dist/store/adapters/postgres.d.ts +5 -0
  36. package/dist/store/adapters/redis.d.ts +2 -0
  37. package/dist/store/adapters/sqlite.d.ts +5 -0
  38. package/dist/store/types.d.ts +2 -0
  39. package/dist/types/convex.d.ts +2 -0
  40. package/dist/types/table-prefix.d.ts +15 -0
  41. package/dist/utils/jid.d.ts +11 -0
  42. package/package.json +16 -5
package/README.md CHANGED
@@ -15,7 +15,7 @@
15
15
 
16
16
  <div align="center">
17
17
  <a href="https://github.com/zeative/zaileys/blob/main/LICENSE"><img src="https://img.shields.io/badge/license-MIT-blue" alt="License: MIT"></a>
18
- <a href="https://discord.gg/KBHhTTVUc5"><img alt="Discord" src="https://img.shields.io/discord/1105833273415962654?logo=discord&label=discord&link=https%3A%2F%2Fgithub.com%2Fzeative%2Fzaileys"></a>
18
+ <a href="https://discord.gg/HUcQe4xGr3"><img alt="Discord" src="https://img.shields.io/discord/1105833273415962654?logo=discord&label=discord&link=https%3A%2F%2Fgithub.com%2Fzeative%2Fzaileys"></a>
19
19
  <a href="https://chat.whatsapp.com/GlQfvc83mSH3F6ov06vuCt"><img alt="WhatsApp" src="https://img.shields.io/badge/WhatsApp-Group-25D366?logo=whatsapp&logoColor=white"></a>
20
20
  <a href="https://github.com/zeative/zaileys"><img src="https://img.shields.io/github/stars/zeative/zaileys" alt="GitHub Stars"></a>
21
21
  <a href="https://github.com/zeative/zaileys"><img src="https://img.shields.io/github/forks/zeative/zaileys" alt="GitHub Forks"></a>
@@ -40,7 +40,7 @@
40
40
  [What you can build](#what-you-can-build) &nbsp;•&nbsp;
41
41
  [Storage](#storage) &nbsp;•&nbsp;
42
42
  [Runtimes](#runtime-support) &nbsp;•&nbsp;
43
- [Docs](https://zeative.github.io/zaileys/)
43
+ [Docs](https://zaileys.kejaa.id)
44
44
 
45
45
  </div>
46
46
 
@@ -49,7 +49,7 @@
49
49
  <br>
50
50
 
51
51
  > [!NOTE]
52
- > This README is a **high-level overview**. The complete API reference, guides, and recipes live in the documentation site at **<https://zeative.github.io/zaileys/>**. Runnable code lives in [`examples/`](./examples).
52
+ > This README is a **high-level overview**. The complete API reference, guides, and recipes live in the documentation site at **<https://zaileys.kejaa.id>**. Runnable code lives in [`examples/`](./examples).
53
53
 
54
54
  ---
55
55
 
@@ -110,10 +110,11 @@ export const POST = client.webhook()
110
110
  | Login | QR / pairing code, no approval | Permanent token |
111
111
  | Ban risk | Exists | None (sanctioned) |
112
112
  | Groups / channels / polls | ✅ | ❌ |
113
+ | Commands, plugins, broadcast, scheduled sends | ✅ | ❌ (need the WhatsApp Web connection) |
113
114
  | Templates / OTP / marketing | ❌ | ✅ |
114
115
  | Message users who never texted you | ✅ any number | ✅ via approved templates |
115
116
 
116
- Pick your provider → **[Choose Your Provider](https://zeative.github.io/zaileys/providers)** · **[Official Cloud API guide](https://zeative.github.io/zaileys/official)**.
117
+ Pick your provider → **[Choose Your Provider](https://zaileys.kejaa.id/providers)** · **[Official Cloud API guide](https://zaileys.kejaa.id/cloud/overview)**.
117
118
 
118
119
  ## Build with AI
119
120
 
@@ -130,7 +131,7 @@ to fix errors. Install it straight from this repo:
130
131
  npx skills add zeative/zaileys # add -g for a global install
131
132
  ```
132
133
 
133
- The suite has an orchestrator that auto-routes plus focused scaffold, debug, and review skills. See the full guide → **[zeative.github.io/zaileys/skill](https://zeative.github.io/zaileys/skill/)**.
134
+ The suite has an orchestrator that auto-routes plus focused scaffold, debug, and review skills. See the full guide → **[zaileys.kejaa.id/ai](https://zaileys.kejaa.id/ai)**.
134
135
 
135
136
  ## Why Zaileys
136
137
 
@@ -140,9 +141,9 @@ The suite has an orchestrator that auto-routes plus focused scaffold, debug, and
140
141
  - **Rich & interactive out of the box** — native buttons, lists, carousels, and Meta-AI-style rich responses written as plain markdown.
141
142
  - **Auto lifecycle** — QR or pairing-code login, auto-reconnect with backoff, clean logout, optional `ignoreMe`.
142
143
  - **Pluggable storage** — independent `AuthStore` and `MessageStore` interfaces with `file`, `memory`, `sqlite`, `redis`, `postgres`, and `convex` adapters.
143
- - **Batteries included** — command framework, broadcast with rate limiting, scheduled sends, and lazy media processing (image/video/audio/sticker).
144
+ - **Batteries included** — command framework, broadcast with rate limiting, and scheduled sends (WhatsApp Web only), plus lazy media processing (image/video/audio/sticker) on both providers.
144
145
  - **Runs everywhere** — dual ESM/CJS with `.d.ts` + `.d.cts` types; verified on Node, Bun, Deno, and Termux.
145
- - **Modern foundation** — Baileys `7.0.0-rc13` (includes the CVE-2026-48063 spoofing patch), built and type-checked with the native (Go) TypeScript 7 compiler.
146
+ - **Modern foundation** — Baileys `7.0.0-rc14` (includes the CVE-2026-48063 spoofing patch), built and type-checked with the native (Go) TypeScript 7 compiler.
146
147
 
147
148
  ## Install
148
149
 
@@ -201,24 +202,16 @@ client.on('button-click', (ctx) => console.log('tapped:', ctx.buttonId))
201
202
  Toggle `{ rich: true }` and write ordinary markdown — fenced code (syntax-highlighted), tables, images, and `:::` directives for products, suggestions, and more.
202
203
 
203
204
  ```typescript
204
- await client.send(jid).text(
205
- [
206
- '*Daily brief* ☕',
207
- '',
208
- '```ts',
209
- "const client = new Client()",
210
- '```',
211
- '',
212
- ':::suggest',
213
- 'See changelog | Upgrade guide',
214
- ':::',
215
- ].join('\n'),
216
- { rich: true, title: '📰 zaileys' },
217
- )
205
+ // The message contains backticks, so write it as a plain string with \n line breaks.
206
+ const brief = "*Daily brief* ☕\n\n```ts\nconst client = new Client()\n```\n\n:::suggest\nSee changelog | Upgrade guide\n:::"
207
+
208
+ await client.send(jid).text(brief, { rich: true, title: '📰 zaileys' })
218
209
  ```
219
210
 
220
211
  ### Commands, broadcast & schedule
221
212
 
213
+ These run on the WhatsApp Web provider; on the Cloud API, handle `text` events and send in a loop instead.
214
+
222
215
  ```typescript
223
216
  const client = new Client({ commandPrefix: ['/', '!'] })
224
217
  client.command('ping', (ctx) => ctx.reply('pong 🏓'))
@@ -281,8 +274,14 @@ Package managers: **npm**, **pnpm**, **yarn**, and **bun** are all supported.
281
274
 
282
275
  ## Documentation
283
276
 
284
- - 🌐 [**zeative.github.io/zaileys**](https://zeative.github.io/zaileys/) — full documentation site: guides, API reference, recipes
285
- - 🤖 [**AI Skill**](https://zeative.github.io/zaileys/skill/) — official Claude Code / `npx skills` skill
277
+ - 🌐 [**zaileys.kejaa.id**](https://zaileys.kejaa.id) — the documentation site
278
+ - [Quickstart](https://zaileys.kejaa.id/quickstart) — a working bot in about five minutes
279
+ - [Guides](https://zaileys.kejaa.id/messaging/text) — messaging, media, buttons, commands, groups, storage
280
+ - [Cloud API](https://zaileys.kejaa.id/cloud/overview) — webhook, templates, limits
281
+ - [Recipes](https://zaileys.kejaa.id/recipes/auto-reply) — complete bots you can copy
282
+ - [Reference](https://zaileys.kejaa.id/reference/client) — every method, option, and event
283
+ - [Feature matrix](https://zaileys.kejaa.id/feature-matrix) — what works on which provider
284
+ - 🤖 [**Build with AI**](https://zaileys.kejaa.id/ai) — official Claude Code / `npx skills` skill
286
285
  - 📦 [**examples/**](./examples) — runnable bots: quickstart, interactive buttons, AIRich, storage adapters, broadcast
287
286
  - 🔀 [**MIGRATION.md**](./MIGRATION.md) — upgrading from v3.x to v4.0.0 (breaking changes, side-by-side snippets)
288
287
  - 🤝 [**CONTRIBUTING.md**](./CONTRIBUTING.md) — dev setup, tests, commit convention, release flow
@@ -4,13 +4,24 @@ export interface FileAuthStoreOptions {
4
4
  }
5
5
  export declare class FileAuthStore implements AuthStoreBundle {
6
6
  private readonly basePath;
7
+ /** Where credentials live, so callers can keep that directory out of reach of media loading. */
8
+ get directory(): string;
9
+ private readonly resolvedBase;
7
10
  private closed;
8
11
  constructor(options?: FileAuthStoreOptions);
12
+ /** Independent of the caller's validation: nothing this store touches may escape its base. */
13
+ private assertContained;
9
14
  readonly signal: AuthStore;
10
15
  readonly creds: AuthCredsStore;
11
16
  private credsPath;
17
+ /** Newest `creds.revoked-*.json`, or undefined when nothing was ever quarantined. */
18
+ private newestBackup;
12
19
  private signalDir;
13
20
  private signalPath;
21
+ /**
22
+ * Durable replace: the bytes are flushed before the rename, so a crash mid-write leaves either the
23
+ * old file or the new one — never a truncated `creds.json`, which reads as a lost session.
24
+ */
14
25
  private atomicWrite;
15
26
  private assertOpen;
16
27
  }
@@ -1,6 +1,7 @@
1
1
  import type { AuthCredsStore, AuthStore, AuthStoreBundle } from '../types.js';
2
2
  export declare class MemoryAuthStore implements AuthStoreBundle {
3
3
  private credsBlob;
4
+ private credsBackup;
4
5
  private readonly signalMap;
5
6
  private closed;
6
7
  readonly signal: AuthStore;
@@ -4,6 +4,8 @@ export interface PostgresAuthStoreOptions {
4
4
  pool?: PgPoolLike;
5
5
  connectionString?: string;
6
6
  max?: number;
7
+ /** Prefix for this store's tables, so several sessions can share one database. Default none. */
8
+ tablePrefix?: string;
7
9
  }
8
10
  export declare class PostgresAuthStore implements AuthStoreBundle {
9
11
  private readonly externalPool;
@@ -13,6 +15,7 @@ export declare class PostgresAuthStore implements AuthStoreBundle {
13
15
  private resolvedPool;
14
16
  private readyPromise;
15
17
  private closed;
18
+ private readonly rewrite;
16
19
  constructor(options: PostgresAuthStoreOptions);
17
20
  private ensureReady;
18
21
  readonly signal: AuthStore;
@@ -16,6 +16,8 @@ export declare class RedisAuthStore implements AuthStoreBundle {
16
16
  readonly signal: AuthStore;
17
17
  readonly creds: AuthCredsStore;
18
18
  private credsKey;
19
+ /** Quarantine key: keeps the last credentials recoverable after an erase. */
20
+ private credsBackupKey;
19
21
  private signalKey;
20
22
  private indexKey;
21
23
  private ensureReady;
@@ -2,6 +2,8 @@ import type { AuthCredsStore, AuthStore, AuthStoreBundle } from '../types.js';
2
2
  export interface SqliteAuthStoreOptions {
3
3
  database: string | Buffer;
4
4
  readonly?: boolean;
5
+ /** Prefix for this store's tables, so several sessions can share one database file. Default none. */
6
+ tablePrefix?: string;
5
7
  }
6
8
  export declare class SqliteAuthStore implements AuthStoreBundle {
7
9
  private readonly options;
@@ -9,6 +11,7 @@ export declare class SqliteAuthStore implements AuthStoreBundle {
9
11
  private prepared;
10
12
  private readyPromise;
11
13
  private closed;
14
+ private readonly rewrite;
12
15
  constructor(options: SqliteAuthStoreOptions);
13
16
  readonly creds: AuthCredsStore;
14
17
  readonly signal: AuthStore;
@@ -9,11 +9,23 @@ export interface AuthStore {
9
9
  delete<K extends AuthStoreKey>(type: K, ids: readonly string[]): Promise<void>;
10
10
  clear(): Promise<void>;
11
11
  close(): Promise<void>;
12
+ /**
13
+ * Undo `close()` so a disconnected client can connect again. Optional: without it the client
14
+ * reports that the adapter cannot be reused and a new Client must be built.
15
+ */
16
+ reopen?(): Promise<void>;
12
17
  }
13
18
  export interface AuthCredsStore {
14
19
  readCreds(): Promise<AuthenticationCreds | undefined>;
15
20
  writeCreds(creds: AuthenticationCreds): Promise<void>;
16
21
  deleteCreds(): Promise<void>;
22
+ /**
23
+ * Move the current credentials aside so an erase stays recoverable. Optional: a third-party
24
+ * adapter without it simply skips the snapshot. Called before every destructive path.
25
+ */
26
+ backupCreds?(): Promise<void>;
27
+ /** Read back the newest snapshot written by {@link backupCreds}. */
28
+ readBackupCreds?(): Promise<AuthenticationCreds | undefined>;
17
29
  }
18
30
  export interface AuthStoreBundle {
19
31
  readonly creds: AuthCredsStore;
@@ -1,4 +1,4 @@
1
- export type AutomationErrorCode = 'NOT_CONNECTED' | 'RATE_LIMIT_INVALID' | 'TASK_FAILED' | 'SCHEDULE_INVALID' | 'STORE_UNAVAILABLE' | 'PRESENCE_FAILED';
1
+ export type AutomationErrorCode = 'NOT_CONNECTED' | 'RATE_LIMIT_INVALID' | 'TASK_FAILED' | 'SCHEDULE_INVALID' | 'STORE_UNAVAILABLE' | 'PRESENCE_FAILED' | 'QUEUE_FULL' | 'QUEUE_TIMEOUT';
2
2
  export declare class ZaileysAutomationError extends Error {
3
3
  readonly code: AutomationErrorCode;
4
4
  readonly cause?: unknown;
@@ -15,12 +15,20 @@ export declare class PresenceModule {
15
15
  private readonly throttleEnabled;
16
16
  private readonly minIntervalMs;
17
17
  private readonly now;
18
+ /** Bounded: one entry per chat ever messaged would otherwise live for the process's lifetime. */
18
19
  private readonly lastSent;
20
+ private readonly pendingClears;
19
21
  constructor(getSocket: () => AutomationSocketLike | undefined, throttle?: PresenceThrottleOptions, clock?: PresenceClock);
20
22
  protected requireSocket(): AutomationSocketLike;
21
23
  private throttled;
22
24
  private update;
25
+ /**
26
+ * Resolves the socket when the timer fires rather than capturing it, so a reconnect does not get
27
+ * a burst of presence updates aimed at a dead socket. Handles are tracked so dispose() can cancel.
28
+ */
23
29
  private scheduleClear;
30
+ /** Cancels every outstanding auto-clear. Called on disconnect. */
31
+ dispose(): void;
24
32
  online(): Promise<void>;
25
33
  offline(): Promise<void>;
26
34
  typing(jid: string, ms?: number): Promise<void>;
@@ -6,7 +6,10 @@ export declare class TaskQueue {
6
6
  private readonly concurrency;
7
7
  private readonly retry;
8
8
  private readonly sleep;
9
+ /** Bounded backlog: an unbounded queue plus a hung task retains every caller's closure forever. */
9
10
  private readonly pending;
11
+ private readonly maxPending;
12
+ private readonly taskTimeoutMs;
10
13
  private active;
11
14
  private idleWaiters;
12
15
  constructor(options?: TaskQueueOptions, clock?: TaskQueueClock);
@@ -14,5 +17,7 @@ export declare class TaskQueue {
14
17
  onIdle(): Promise<void>;
15
18
  private pump;
16
19
  private settleIdle;
20
+ /** A task with no deadline parks its concurrency slot forever on a half-open socket. */
21
+ private withDeadline;
17
22
  private execute;
18
23
  }
@@ -9,6 +9,7 @@ export declare class RateLimiter {
9
9
  private readonly perJidRatePerMs?;
10
10
  private readonly perJidCapacity?;
11
11
  private readonly global;
12
+ /** Bounded: a refilled bucket is indistinguishable from a fresh one, so retaining it is waste. */
12
13
  private readonly perJid;
13
14
  constructor(options: RateLimiterOptions, clock?: RateLimiterClock);
14
15
  acquire(jid?: string): Promise<void>;
@@ -32,11 +32,17 @@ export declare class Scheduler {
32
32
  private readonly acquire;
33
33
  private readonly memory;
34
34
  private readonly timers;
35
+ private readonly failures;
35
36
  constructor(deps: SchedulerDeps);
36
37
  scheduleAt(date: Date, build: (b: MessageBuilder<'init'>) => MessageBuilder<'content-set'>): Promise<ScheduleHandle>;
37
38
  loadPending(): Promise<void>;
38
39
  dispose(): void;
39
40
  private cancel;
41
+ /**
42
+ * Node clamps a setTimeout delay above 2^31-1 ms (~24.8 days) to 1, so a job scheduled months out
43
+ * would fire almost immediately — for a broadcast, an instant mass-send. Long waits are re-armed
44
+ * in chunks instead.
45
+ */
40
46
  private arm;
41
47
  private fire;
42
48
  private persist;
@@ -8,6 +8,10 @@ export type RetryPolicy = {
8
8
  backoffMs: (attempt: number) => number;
9
9
  };
10
10
  export type TaskQueueOptions = {
11
+ /** Reject new work past this backlog instead of growing without bound. Default 10000. */
12
+ maxPending?: number;
13
+ /** Per-task deadline; a hung task otherwise parks its concurrency slot forever. Default 120000. */
14
+ taskTimeoutMs?: number;
11
15
  concurrency?: number;
12
16
  retry?: RetryPolicy;
13
17
  };
@@ -2,6 +2,7 @@ import { type AnyMessageContent, type MiscMessageGenerationOptions, type WAMessa
2
2
  import { type ButtonsContentOptions } from './content/buttons.js';
3
3
  import { type CarouselCard } from './content/carousel.js';
4
4
  import { type AIRichOptions } from './content/airich.js';
5
+ import { type HtmlAppOptions } from './content/html-app.js';
5
6
  import { type BuilderInternalState } from './state.js';
6
7
  import type { AlbumItem, AudioOptions, BuilderState, ButtonDef, DocumentOptions, EventOptions, GroupInviteOptions, GroupStatusOptions, GroupStatusRepostOptions, GroupStatusSource, ImageOptions, InteractiveButton, ListOptions, LocationOptions, MediaSource, PollOptions, ProductOptions, StickerOptions, TemplateOptions, VideoNoteOptions, VideoOptions } from './types.js';
7
8
  export interface BuilderSocketLike {
@@ -29,6 +30,7 @@ export declare class MessageBuilder<State extends BuilderState> {
29
30
  static create(socket: BuilderSocketLike, recipient: string, resolveRecipient?: (raw: string) => Promise<string>, recordSent?: (message: WAMessage) => void, inheritDisappearing?: (jid: string) => number | undefined): MessageBuilder<'init'>;
30
31
  to(this: MessageBuilder<'init'>, recipient: string): MessageBuilder<'init'>;
31
32
  text(this: MessageBuilder<'init'>, content: string, opts?: TextOptions): MessageBuilder<'content-set'>;
33
+ htmlApp(this: MessageBuilder<'init'>, html: string, opts?: HtmlAppOptions): MessageBuilder<'content-set'>;
32
34
  image(this: MessageBuilder<'init'>, src: MediaSource, opts?: ImageOptions): MessageBuilder<'content-set'>;
33
35
  videoNote(this: MessageBuilder<'init'>, src: MediaSource, opts?: VideoNoteOptions): MessageBuilder<'content-set'>;
34
36
  video(this: MessageBuilder<'init'>, src: MediaSource, opts?: VideoOptions): MessageBuilder<'content-set'>;
@@ -74,4 +76,11 @@ export declare class MessageBuilder<State extends BuilderState> {
74
76
  /** Relay skips baileys' contextInfo pass, so mentions and mentionAll are applied to the unwrapped node here. */
75
77
  private applyRelayMentions;
76
78
  private sendRelay;
79
+ /**
80
+ * Re-sends the card as an edit of itself. Without it the recipient gets WhatsApp's
81
+ * "can't verify the security of this media" prompt and has to tap Download before the page renders.
82
+ * The edit must sit inside `botForwardedMessage` like the original, otherwise it lands as its own
83
+ * broken message instead of replacing the card.
84
+ */
85
+ private relayIdenticalEdit;
77
86
  }
@@ -28,6 +28,11 @@ export type AIRichPart = {
28
28
  } | {
29
29
  type: 'tip';
30
30
  text: string;
31
+ } | {
32
+ type: 'html';
33
+ html: string;
34
+ trustedSources?: string[];
35
+ height?: number;
31
36
  } | {
32
37
  type: 'suggest';
33
38
  prompts: string | string[];
@@ -77,4 +82,6 @@ export type AIRichOptions = {
77
82
  footer?: string;
78
83
  sources?: Array<[profileUrl: string, url: string, text: string]>;
79
84
  };
85
+ /** Android renders no other primitive; Web, Desktop and iOS map it to an empty section. */
86
+ export declare const AI_RICH_HTML_PRIMITIVE = "GenAIaeacdsnwHtmlPrimitive";
80
87
  export declare const buildAIRichContent: (parts: AIRichPart[], opts?: AIRichOptions) => AnyMessageContent;
@@ -4,6 +4,8 @@ export declare const RELAY_CONTENT_KEY = "__zaileysRelayMessage";
4
4
  export declare const RELAY_MEDIA_KEY = "__zaileysHeaderMedia";
5
5
  /** Marks relay content that may only target a group jid; the value is the method label used in the error. */
6
6
  export declare const RELAY_REQUIRE_GROUP_KEY = "__zaileysRequireGroupJid";
7
+ /** Asks `sendRelay` to follow the message with an identical edit, which renders it without the download prompt. */
8
+ export declare const RELAY_BYPASS_DOWNLOAD_KEY = "__zaileysBypassDownload";
7
9
  /** Carries a downloaded media buffer that `sendRelay` re-uploads and injects into the status envelope. */
8
10
  export declare const RELAY_STATUS_MEDIA_KEY = "__zaileysStatusMedia";
9
11
  export type StatusMedia = {
@@ -0,0 +1,26 @@
1
+ import type { AnyMessageContent } from 'baileys';
2
+ export type HtmlAppDevice = 'android' | 'ios' | 'web' | 'desktop' | 'unknown';
3
+ export type HtmlAppOptions = {
4
+ /** From baileys' `getDevice(messageId)`. Anything but `android` takes the fallback. */
5
+ device?: HtmlAppDevice;
6
+ /** Where non-Android clients open the page instead. Required unless the device is android. */
7
+ fallbackUrl?: string;
8
+ fallbackButtonText?: string;
9
+ /** Rendered above the card, and as the body of the fallback message. */
10
+ text?: string;
11
+ footer?: string;
12
+ trustedSources?: string[];
13
+ /** Pins the page height so the host stops re-measuring a bubble whose height follows its width. */
14
+ height?: number;
15
+ /**
16
+ * Follows the card with an identical edit so it renders immediately instead of behind WhatsApp's
17
+ * "can't verify the security of this media" prompt. Costs one extra relay and one re-render, so a
18
+ * page running an animation restarts once. Defaults to on.
19
+ */
20
+ bypassDownload?: boolean;
21
+ };
22
+ /**
23
+ * Only WhatsApp Android renders an inline HTML primitive. Every other client drops the section and
24
+ * leaves an empty bubble, so they get a webview button to the same page instead.
25
+ */
26
+ export declare const buildHtmlAppContent: (html: string, opts?: HtmlAppOptions) => AnyMessageContent;
@@ -2,5 +2,7 @@ export * from './types.js';
2
2
  export * from './errors.js';
3
3
  export { MessageBuilder, type BuilderSocketLike, type TextOptions } from './builder.js';
4
4
  export { EditBuilder } from './edit-builder.js';
5
+ export { buildHtmlAppContent, type HtmlAppDevice, type HtmlAppOptions, } from './content/html-app.js';
6
+ export { AI_RICH_HTML_PRIMITIVE, type AIRichPart } from './content/airich.js';
5
7
  export { deleteMessage, reactToMessage, forwardMessage, pinMessage, type DeleteOptions, type PinOptions, } from './mutations.js';
6
8
  export { isJid, resolveUsername, type UsernameResolveSocketLike, } from './username-resolve.js';
@@ -6,6 +6,20 @@ export type LoadedMedia = {
6
6
  };
7
7
  export type LoadMediaOptions = {
8
8
  timeoutMs?: number;
9
+ /** Hard ceiling on bytes read from a URL or file. Default 64 MB. */
10
+ maxBytes?: number;
11
+ /** Treat a plain string as a filesystem path. Default `true` — set false for URL-only input. */
12
+ allowLocalPaths?: boolean;
13
+ /** Allow fetching loopback/RFC1918/link-local addresses. Default `false`. */
14
+ allowPrivateNetwork?: boolean;
15
+ /** Extra directories a local path may not resolve into, on top of the auth directory. */
16
+ deniedDirs?: readonly string[];
9
17
  };
10
18
  export declare const detectMimeFromBuffer: (buffer: Buffer) => Promise<string>;
11
- export declare const loadMedia: (src: MediaSource, options?: LoadMediaOptions) => Promise<LoadedMedia>;
19
+ /**
20
+ * Process-wide defaults under every `loadMedia` call. The builders load media deep inside content
21
+ * helpers that never see a Client, so security policy is set once here; a per-call option wins.
22
+ */
23
+ export declare const configureMediaLoading: (next: LoadMediaOptions) => void;
24
+ export declare const getMediaLoadingDefaults: () => Readonly<LoadMediaOptions>;
25
+ export declare const loadMedia: (src: MediaSource, callOptions?: LoadMediaOptions) => Promise<LoadedMedia>;
@@ -33,10 +33,12 @@ export declare class Client extends TypedEventEmitter<ClientEventMap> {
33
33
  private readonly machine;
34
34
  private reconnectStrategy;
35
35
  private readonly authGuard;
36
+ private readonly clearAuthReasons;
36
37
  private readonly operationGuard;
37
38
  private readonly presenceThrottle;
38
39
  private readonly scheduleLimiter;
39
40
  private authExhausted;
41
+ private storesClosedByDisconnect;
40
42
  /** Each chat's disappearing timer, learned from inbound messages so outbound sends can inherit it. */
41
43
  private readonly chatExpiration;
42
44
  private _socket;
@@ -197,5 +199,12 @@ export declare class Client extends TypedEventEmitter<ClientEventMap> {
197
199
  downloadMedia(key: WAMessageKey): Promise<MediaDownloadResult | null>;
198
200
  private lookupQuoted;
199
201
  private handleClose;
202
+ /**
203
+ * disconnect() still releases connections, so a script that disconnects can exit. Reconnecting
204
+ * re-opens what it closed; before this, connect() after disconnect() failed with STORE_CLOSED.
205
+ */
206
+ private reopenStores;
207
+ /** Snapshot the credentials before any erase, so a wrong wipe stays recoverable. */
208
+ private quarantineCreds;
200
209
  private rejectPendingConnect;
201
210
  }
@@ -33,6 +33,31 @@ export interface ReconnectOptions {
33
33
  /** Fixed backoff applied when the disconnect reason is `rate-limited` (429). Default `300000`. */
34
34
  rateLimitedDelayMs?: number;
35
35
  }
36
+ export interface SessionSafetyOptions {
37
+ /**
38
+ * Disconnect reasons permitted to erase stored credentials. Defaults to `['logged-out']` only —
39
+ * see {@link DEFAULT_CLEAR_AUTH_REASONS}. Pass the wider set to restore pre-4.15 behaviour.
40
+ */
41
+ clearAuthOn?: readonly DisconnectReasonDomain[];
42
+ }
43
+ export interface MediaOptions {
44
+ /** Byte ceiling for media read from a URL or file. Default 64 MB. */
45
+ maxBytes?: number;
46
+ /** Treat plain strings as filesystem paths. Default `true`; `false` = URLs, Buffers and `file:` URLs only. */
47
+ allowLocalPaths?: boolean;
48
+ /** Allow fetching loopback, RFC1918 and link-local addresses. Default `false`. */
49
+ allowPrivateNetwork?: boolean;
50
+ /** Extra directories media may never be read from. The auth directory is always protected. */
51
+ deniedDirs?: readonly string[];
52
+ /** Largest image the decoders accept, in pixels. Default 50 MP; lower it on small servers. */
53
+ maxImagePixels?: number;
54
+ /** ffmpeg processes at once. Each 1080p re-encode holds ~235 MB. Default 4. */
55
+ maxConcurrentFfmpeg?: number;
56
+ /** ffmpeg jobs allowed to wait for a slot before new ones are rejected. Default 64. */
57
+ maxQueuedFfmpeg?: number;
58
+ /** Longest an ffmpeg job may wait for a slot. Default 120000. */
59
+ ffmpegQueueTimeoutMs?: number;
60
+ }
36
61
  export interface ClientOptions {
37
62
  /** Message transport: baileys (WhatsApp Web, default) or the official Meta Cloud API. */
38
63
  provider?: ProviderKind;
@@ -55,6 +80,13 @@ export interface ClientOptions {
55
80
  ignoreMe?: boolean;
56
81
  /** Bounds QR/pairing regeneration to avoid spam restriction. ON by default; `{ enabled: false }` to opt out. */
57
82
  authGuard?: AuthGuardOptions;
83
+ /** Guards the stored session. By default only an explicit logout may erase credentials. */
84
+ session?: SessionSafetyOptions;
85
+ /**
86
+ * Media loading and processing limits. Process-wide: they govern shared CPU, RAM and ffmpeg
87
+ * children, so with several clients in one process the last one constructed wins.
88
+ */
89
+ media?: MediaOptions;
58
90
  /** Spaces out sensitive group/community/newsletter operations. ON by default; `{ enabled: false }` to opt out. */
59
91
  operationGuard?: OperationGuardOptions;
60
92
  /** Drops duplicate presence (typing/recording/online) updates within a window. ON by default. */
@@ -8,6 +8,7 @@ export interface CloudMe {
8
8
  id: string;
9
9
  name?: string;
10
10
  }
11
+ export declare const assertMetaMediaHost: (raw: string) => void;
11
12
  export declare class CloudTransport implements Transport {
12
13
  readonly ev: EventEmitter<[never]>;
13
14
  readonly user: {
@@ -9,6 +9,11 @@ export interface CloudOptions {
9
9
  verifyToken?: string;
10
10
  /** Meta app secret; enables X-Hub-Signature-256 verification of webhook POSTs. */
11
11
  appSecret?: string;
12
+ /**
13
+ * Accept webhook POSTs that carry no verified signature. Off by default — without `appSecret`
14
+ * anyone who learns the endpoint URL can inject inbound events. Local development only.
15
+ */
16
+ allowUnsigned?: boolean;
12
17
  /** Graph API version, e.g. 'v23.0'. Defaults to the pinned stable version. */
13
18
  apiVersion?: string;
14
19
  /** Override the Graph API origin (tests / proxies). */
@@ -2,6 +2,11 @@ export type WebhookHandler = (req: Request) => Promise<Response>;
2
2
  export interface WebhookHandlerOptions {
3
3
  verifyToken?: string;
4
4
  appSecret?: string;
5
+ /**
6
+ * Accept POSTs without an `appSecret` to verify them. Off by default: an unverified endpoint lets
7
+ * anyone who learns the URL inject inbound messages. Only for local development.
8
+ */
9
+ allowUnsigned?: boolean;
5
10
  onPayload: (payload: unknown) => void;
6
11
  }
7
12
  /**
@@ -1,4 +1,10 @@
1
1
  import type { SignalKeyStore } from 'baileys';
2
2
  import type { AuthStore } from '../auth/types.js';
3
3
  import type { Logger } from '../client/types.js';
4
+ /**
5
+ * `clear` is deliberately omitted. Every adapter's `signal.clear()` also erases credentials
6
+ * (AUTH-07), and baileys types `SignalKeyStore.clear` as optional — handing it over would let a
7
+ * future baileys release wipe a live session while merely resetting signal state. zaileys' own
8
+ * logout path calls `auth.signal.clear()` directly instead.
9
+ */
4
10
  export declare function signalKeyStoreFromAuthStore(store: AuthStore, _logger?: Logger): SignalKeyStore;
@@ -2,5 +2,12 @@ export type DisconnectReasonDomain = 'logged-out' | 'connection-replaced' | 'for
2
2
  export declare function mapDisconnectReason(code: number | undefined): DisconnectReasonDomain;
3
3
  export declare function isFatalDisconnect(reason: DisconnectReasonDomain): boolean;
4
4
  export declare function isRateLimited(reason: DisconnectReasonDomain): boolean;
5
- export declare function shouldClearAuth(reason: DisconnectReasonDomain): boolean;
5
+ /**
6
+ * Reasons allowed to erase stored credentials. Deliberately just `logged-out`: baileys defaults an
7
+ * unknown stream error and an unrecognised WS error to 500 (`bad-session`), and 440
8
+ * (`connection-replaced`) means the creds are valid and in use elsewhere — erasing on either
9
+ * destroys a working session over ordinary network noise.
10
+ */
11
+ export declare const DEFAULT_CLEAR_AUTH_REASONS: readonly DisconnectReasonDomain[];
12
+ export declare function shouldClearAuth(reason: DisconnectReasonDomain, allowed?: readonly DisconnectReasonDomain[]): boolean;
6
13
  export declare function shouldReconnect(reason: DisconnectReasonDomain): boolean;
@@ -190,6 +190,12 @@ export interface MessageContext {
190
190
  mentions: string[];
191
191
  links: string[];
192
192
  isFromMe: boolean;
193
+ /**
194
+ * Whether this context came from a message we actually stored. `false` means it was rebuilt from
195
+ * the sender's own `contextInfo` — the text, the author and `isFromMe` are the sender's claim, not
196
+ * proof. Require `verified` before trusting a quote for an authorization decision.
197
+ */
198
+ verified: boolean;
193
199
  isGroup: boolean;
194
200
  isNewsletter: boolean;
195
201
  isBroadcast: boolean;
@@ -236,6 +242,8 @@ export interface MentionAllContext extends MessageContext {
236
242
  members?: string[];
237
243
  }
238
244
  export interface BuildContextInput {
245
+ /** Defaults to true; set false when the context was rebuilt from unauthenticated `contextInfo`. */
246
+ verified?: boolean;
239
247
  message: WAMessage;
240
248
  key: WAMessageKey;
241
249
  channelId: string;
@@ -1,4 +1,8 @@
1
- import type { WAMessage, MessageUpsertType } from 'baileys';
1
+ import { type WAMessage, type MessageUpsertType } from 'baileys';
2
+ /**
3
+ * Protocol messages that only ever originate from our own devices. WhatsApp does not authenticate
4
+ * the sender of these for us, so an ordinary contact can put one on the wire.
5
+ */
2
6
  export declare const SELF_ONLY_PROTOCOL_TYPES: readonly ["HISTORY_SYNC_NOTIFICATION", "APP_STATE_SYNC_KEY_SHARE", "LID_MIGRATION_MAPPING_SYNC", "PEER_DATA_OPERATION_REQUEST_RESPONSE_MESSAGE"];
3
7
  export type SelfOnlyProtocolType = (typeof SELF_ONLY_PROTOCOL_TYPES)[number];
4
8
  export interface UpsertPayload {
@@ -24,6 +24,13 @@ export interface InboundPipelineContext {
24
24
  sendReply?: (target: string, content: string, opts: TextOptions | undefined, quoted: WAMessage) => Promise<WAMessageKey>;
25
25
  react?: (key: WAMessageKey, emoji: string) => Promise<WAMessageKey>;
26
26
  ignoreMe?: boolean;
27
+ /** Messages allowed to wait on LID resolution at once. Default 256. */
28
+ maxPendingResolutions?: number;
29
+ /**
30
+ * Weight budget for messages queued behind those; beyond it the overflow is shed. A plain message
31
+ * weighs 1, mentions and long text add to it. Default 20000.
32
+ */
33
+ maxQueuedResolutions?: number;
27
34
  }
28
35
  export interface BaileysEventSurface {
29
36
  on: (event: string, handler: (...args: unknown[]) => void) => void;