@diegoaltoworks/chatter 0.27.0 → 0.29.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/dist/bin/wa-pair.js +320 -0
  2. package/dist/channels/index.d.ts +47 -0
  3. package/dist/channels/index.d.ts.map +1 -0
  4. package/dist/channels/index.js +137 -0
  5. package/dist/channels/index.mjs +105 -0
  6. package/dist/channels/senders.d.ts +30 -0
  7. package/dist/channels/senders.d.ts.map +1 -0
  8. package/dist/channels/senders.test.d.ts +2 -0
  9. package/dist/channels/senders.test.d.ts.map +1 -0
  10. package/dist/channels/whatsapp/authState.d.ts +39 -0
  11. package/dist/channels/whatsapp/authState.d.ts.map +1 -0
  12. package/dist/channels/whatsapp/authState.test.d.ts +2 -0
  13. package/dist/channels/whatsapp/authState.test.d.ts.map +1 -0
  14. package/dist/channels/whatsapp/baileys.d.ts +21 -0
  15. package/dist/channels/whatsapp/baileys.d.ts.map +1 -0
  16. package/dist/channels/whatsapp/baileys.test.d.ts +2 -0
  17. package/dist/channels/whatsapp/baileys.test.d.ts.map +1 -0
  18. package/dist/channels/whatsapp/channel.d.ts +95 -0
  19. package/dist/channels/whatsapp/channel.d.ts.map +1 -0
  20. package/dist/channels/whatsapp/channel.test.d.ts +2 -0
  21. package/dist/channels/whatsapp/channel.test.d.ts.map +1 -0
  22. package/dist/channels/whatsapp/crypto.d.ts +10 -0
  23. package/dist/channels/whatsapp/crypto.d.ts.map +1 -0
  24. package/dist/channels/whatsapp/crypto.test.d.ts +2 -0
  25. package/dist/channels/whatsapp/crypto.test.d.ts.map +1 -0
  26. package/dist/channels/whatsapp/index.d.ts +33 -0
  27. package/dist/channels/whatsapp/index.d.ts.map +1 -0
  28. package/dist/channels/whatsapp/index.js +478 -0
  29. package/dist/channels/whatsapp/index.mjs +428 -0
  30. package/dist/channels/whatsapp/lease.d.ts +42 -0
  31. package/dist/channels/whatsapp/lease.d.ts.map +1 -0
  32. package/dist/channels/whatsapp/lease.test.d.ts +2 -0
  33. package/dist/channels/whatsapp/lease.test.d.ts.map +1 -0
  34. package/dist/index.d.ts +2 -0
  35. package/dist/index.d.ts.map +1 -1
  36. package/dist/index.js +83 -2
  37. package/dist/index.mjs +83 -2
  38. package/dist/server.d.ts +21 -2
  39. package/dist/server.d.ts.map +1 -1
  40. package/dist/types.d.ts +17 -0
  41. package/dist/types.d.ts.map +1 -1
  42. package/package.json +29 -4
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Turso/libsql-backed Baileys auth state, encrypted at rest.
3
+ *
4
+ * Same contract as Baileys' own `useMultiFileAuthState`, but rows in a
5
+ * `wa_auth` table instead of files, AES-GCM encrypted (see `crypto.ts`) so
6
+ * session material — which grants full account access — is never stored
7
+ * readable.
8
+ *
9
+ * Only Baileys *types* are imported here (erased at compile time, so they add
10
+ * no runtime dependency); the few Baileys runtime values this module needs
11
+ * (`BufferJSON`, `initAuthCreds`, the app-state-sync-key constructor) are
12
+ * passed in by the caller, which already holds them from the dynamic import
13
+ * in `baileys.ts` — see that module for why the import is dynamic.
14
+ */
15
+ import type { Client } from "@libsql/client";
16
+ import type { AuthenticationCreds, AuthenticationState, SignalDataTypeMap } from "@whiskeysockets/baileys";
17
+ export interface TursoAuthStateResult {
18
+ state: AuthenticationState;
19
+ saveCreds: () => Promise<void>;
20
+ /** Wipe the stored session (unlink / re-pair). */
21
+ clear: () => Promise<void>;
22
+ }
23
+ /** The slice of the dynamically-loaded Baileys module this file needs at runtime. */
24
+ export interface AuthStateRuntime {
25
+ bufferJSON: {
26
+ replacer: (key: string, value: unknown) => unknown;
27
+ reviver: (key: string, value: unknown) => unknown;
28
+ };
29
+ initAuthCreds: () => AuthenticationCreds;
30
+ appStateSyncKeyFromObject: (value: Record<string, unknown>) => SignalDataTypeMap["app-state-sync-key"];
31
+ }
32
+ /**
33
+ * Sessions share the `wa_auth` table, namespaced by row-id prefix. The
34
+ * "default" session uses UNPREFIXED ids — exactly a single-session layout —
35
+ * so an existing default session needs no migration when multi-session
36
+ * support is introduced.
37
+ */
38
+ export declare function useTursoAuthState(db: Client, secret: string, sessionId: string, runtime: AuthStateRuntime): Promise<TursoAuthStateResult>;
39
+ //# sourceMappingURL=authState.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"authState.d.ts","sourceRoot":"","sources":["../../../src/channels/whatsapp/authState.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AAC7C,OAAO,KAAK,EACV,mBAAmB,EACnB,mBAAmB,EACnB,iBAAiB,EAClB,MAAM,yBAAyB,CAAC;AAGjC,MAAM,WAAW,oBAAoB;IACnC,KAAK,EAAE,mBAAmB,CAAC;IAC3B,SAAS,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/B,kDAAkD;IAClD,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC5B;AAED,qFAAqF;AACrF,MAAM,WAAW,gBAAgB;IAC/B,UAAU,EAAE;QACV,QAAQ,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,KAAK,OAAO,CAAC;QACnD,OAAO,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,KAAK,OAAO,CAAC;KACnD,CAAC;IACF,aAAa,EAAE,MAAM,mBAAmB,CAAC;IACzC,yBAAyB,EAAE,CACzB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAC3B,iBAAiB,CAAC,oBAAoB,CAAC,CAAC;CAC9C;AAiBD;;;;;GAKG;AACH,wBAAsB,iBAAiB,CACrC,EAAE,EAAE,MAAM,EACV,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,gBAAgB,GACxB,OAAO,CAAC,oBAAoB,CAAC,CA6E/B"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=authState.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"authState.test.d.ts","sourceRoot":"","sources":["../../../src/channels/whatsapp/authState.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * The one place `./whatsapp` touches the `@whiskeysockets/baileys` package at
3
+ * runtime. It's an OPTIONAL peer dependency — `bun install` and every core
4
+ * import must work without it — so nothing in this subpath may `import` it
5
+ * statically; every other module here takes what it needs as a value (see
6
+ * `AuthStateRuntime` in `authState.ts`) or a type-only import (erased at
7
+ * compile time, so it costs nothing at runtime).
8
+ *
9
+ * `loadBaileys()` is the single dynamic import, called lazily when a channel
10
+ * actually starts, so a missing package fails with an actionable message
11
+ * instead of a raw "Cannot find module" — or, worse, at unrelated core
12
+ * imports that never touch WhatsApp at all.
13
+ */
14
+ import type * as BaileysModule from "@whiskeysockets/baileys";
15
+ export type Baileys = typeof BaileysModule;
16
+ /** Wraps a failed `import("@whiskeysockets/baileys")` in an actionable message. Exported separately so the message content is unit-testable without simulating a real missing module. */
17
+ export declare function wrapMissingBaileysError(cause: unknown): Error;
18
+ export declare function loadBaileys(): Promise<Baileys>;
19
+ /** A silent, structurally pino-compatible logger — Baileys logs very verbosely by default. */
20
+ export declare function silentLogger(): unknown;
21
+ //# sourceMappingURL=baileys.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"baileys.d.ts","sourceRoot":"","sources":["../../../src/channels/whatsapp/baileys.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,KAAK,aAAa,MAAM,yBAAyB,CAAC;AAE9D,MAAM,MAAM,OAAO,GAAG,OAAO,aAAa,CAAC;AAE3C,yLAAyL;AACzL,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,CAO7D;AAID,wBAAgB,WAAW,IAAI,OAAO,CAAC,OAAO,CAAC,CAQ9C;AAED,8FAA8F;AAC9F,wBAAgB,YAAY,IAAI,OAAO,CAatC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=baileys.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"baileys.test.d.ts","sourceRoot":"","sources":["../../../src/channels/whatsapp/baileys.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,95 @@
1
+ /**
2
+ * WhatsApp web-client transport: a {@link Channel} that runs one Baileys
3
+ * socket per configured session id (one WhatsApp number each), auth state
4
+ * encrypted in the host's own database (`deps.db` — no second connection
5
+ * opened), and a deploy lease so two overlapping instances can never hold the
6
+ * same session's socket at once (WhatsApp treats a second connection as a
7
+ * takeover and logs the first one out).
8
+ *
9
+ * Message INTERPRETATION — mention/reply detection, allowlists, loop guards —
10
+ * is deliberately not this module's job; it hands every raw inbound message
11
+ * to `config.onMessage` and lets the caller apply `./channels`' reply gates.
12
+ * This module owns only the connection lifecycle and outbound sending.
13
+ *
14
+ * ToS note: Baileys is an unofficial WhatsApp client. Linking a number
15
+ * carries a real ban risk — use a number you can afford to lose, and treat
16
+ * this channel as opt-in, never a default.
17
+ */
18
+ import type { WAMessage, WASocket } from "@whiskeysockets/baileys";
19
+ import type { Channel } from "../index";
20
+ import { type Baileys } from "./baileys";
21
+ import { type WaLeaseStore } from "./lease";
22
+ /** Exponential backoff: 5s, 10s, 20s ... capped at 10 minutes. Hammering
23
+ * WhatsApp every few seconds during an outage or ban aggravates ban scoring. */
24
+ export declare function reconnectDelayMs(failures: number): number;
25
+ export interface WhatsAppMessageEvent {
26
+ sessionId: string;
27
+ sock: WASocket;
28
+ message: WAMessage;
29
+ }
30
+ export interface WhatsAppChannelConfig {
31
+ /** Passphrase that encrypts auth state at rest (AES-256-GCM key material). Keep it stable across restarts and deploys — losing it means re-pairing. */
32
+ sessionSecret: string;
33
+ /** One Baileys connection per id, each a separate WhatsApp number. Defaults to `["default"]`, whose auth rows are unprefixed (a pre-multi-session deployment needs no migration). */
34
+ sessionIds?: string[];
35
+ /**
36
+ * Called for every raw inbound message on every session. This module does
37
+ * no interpretation of its own — no mention detection, no allowlists, no
38
+ * loop guard — so replies belong entirely to the handler; pair it with
39
+ * `./channels`' `decideChannelAction` and `isEffectivelyFromSelf`. A
40
+ * throwing/rejecting handler is caught and logged, never left to crash the
41
+ * socket's event loop.
42
+ */
43
+ onMessage?: (event: WhatsAppMessageEvent) => void | Promise<void>;
44
+ now?: () => number;
45
+ /** Overridable for tests; defaults to `setTimeout`. */
46
+ schedule?: (fn: () => void, ms: number) => void;
47
+ /** Overridable for tests, which fake the Baileys module instead of installing it; defaults to `loadBaileys` from `./baileys`. */
48
+ loadBaileys?: () => Promise<Baileys>;
49
+ staleMs?: number;
50
+ heartbeatMs?: number;
51
+ waitMs?: number;
52
+ }
53
+ /** A live, leased session: what `stop()` (or losing the lease) must tear down. */
54
+ export interface WaSessionHandle {
55
+ sock: {
56
+ end: (error?: Error) => void;
57
+ };
58
+ stopHeartbeat: () => void;
59
+ release: () => Promise<void>;
60
+ }
61
+ /**
62
+ * Tears down every session in `sessions`: stop renewing, close the socket,
63
+ * THEN release the lease — releasing before the socket closes could let
64
+ * another instance connect while this one is still on the wire. Sessions run
65
+ * independently (`Promise.all`); one release rejecting must not block or skip
66
+ * the rest.
67
+ */
68
+ export declare function shutdownWaSessions(sessions: Map<string, WaSessionHandle>): Promise<void>;
69
+ export interface LeaseGatedConnectDeps {
70
+ leaseStore: WaLeaseStore;
71
+ instanceId: string;
72
+ sessions: Map<string, WaSessionHandle>;
73
+ isStopped: () => boolean;
74
+ /** The actual Baileys connect — registers its own reconnect-on-close loop; this only gates its FIRST call. */
75
+ connect: (sessionId: string) => Promise<void>;
76
+ now?: () => number;
77
+ staleMs?: number;
78
+ heartbeatMs?: number;
79
+ waitMs?: number;
80
+ schedule?: (fn: () => void, ms: number) => void;
81
+ }
82
+ /**
83
+ * Acquire `sessionId`'s lease before connecting — two instances must never
84
+ * run concurrent Baileys clients for the same session. Not holding it (or a
85
+ * database blip while trying) means wait and re-check instead of connecting.
86
+ * Once held, a heartbeat renews it for as long as the session runs; if a
87
+ * heartbeat ever finds the lease gone (this instance went stale and another
88
+ * took over), the session is torn down and this re-enters the same
89
+ * wait-and-retry loop rather than staying connected alongside the new holder.
90
+ */
91
+ export declare function acquireSessionLease(sessionId: string, deps: LeaseGatedConnectDeps): Promise<void>;
92
+ /** Sender-registry name for a session — the default session stays unprefixed so single-number deployments see a plain `"whatsapp"` name. */
93
+ export declare function senderNameFor(channelName: string, sessionId: string): string;
94
+ export declare function createWhatsAppChannel(config: WhatsAppChannelConfig): Channel;
95
+ //# sourceMappingURL=channel.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"channel.d.ts","sourceRoot":"","sources":["../../../src/channels/whatsapp/channel.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAGH,OAAO,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAC;AACnE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AAIxC,OAAO,EAAE,KAAK,OAAO,EAAmD,MAAM,WAAW,CAAC;AAC1F,OAAO,EAKL,KAAK,YAAY,EAClB,MAAM,SAAS,CAAC;AAKjB;gFACgF;AAChF,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAEzD;AAED,MAAM,WAAW,oBAAoB;IACnC,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,QAAQ,CAAC;IACf,OAAO,EAAE,SAAS,CAAC;CACpB;AAED,MAAM,WAAW,qBAAqB;IACpC,uJAAuJ;IACvJ,aAAa,EAAE,MAAM,CAAC;IACtB,qLAAqL;IACrL,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB;;;;;;;OAOG;IACH,SAAS,CAAC,EAAE,CAAC,KAAK,EAAE,oBAAoB,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAClE,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IACnB,uDAAuD;IACvD,QAAQ,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,IAAI,EAAE,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IAChD,iIAAiI;IACjI,WAAW,CAAC,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC;IACrC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,kFAAkF;AAClF,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE;QAAE,GAAG,EAAE,CAAC,KAAK,CAAC,EAAE,KAAK,KAAK,IAAI,CAAA;KAAE,CAAC;IACvC,aAAa,EAAE,MAAM,IAAI,CAAC;IAC1B,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC9B;AAED;;;;;;GAMG;AACH,wBAAsB,kBAAkB,CAAC,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,eAAe,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAQ9F;AAYD,MAAM,WAAW,qBAAqB;IACpC,UAAU,EAAE,YAAY,CAAC;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;IACvC,SAAS,EAAE,MAAM,OAAO,CAAC;IACzB,8GAA8G;IAC9G,OAAO,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9C,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,IAAI,EAAE,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;CACjD;AAED;;;;;;;;GAQG;AACH,wBAAsB,mBAAmB,CACvC,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,qBAAqB,GAC1B,OAAO,CAAC,IAAI,CAAC,CA2Df;AAED,4IAA4I;AAC5I,wBAAgB,aAAa,CAAC,WAAW,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAE5E;AAED,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,qBAAqB,GAAG,OAAO,CA8L5E"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=channel.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"channel.test.d.ts","sourceRoot":"","sources":["../../../src/channels/whatsapp/channel.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Encryption for persisted WhatsApp session material (AES-256-GCM).
3
+ *
4
+ * The stored auth state grants full account access, so it never touches the
5
+ * database in plaintext. The key derives from a caller-supplied secret (any
6
+ * strong passphrase, kept stable across restarts and deploys).
7
+ */
8
+ export declare function encrypt(plaintext: string, secret: string): string;
9
+ export declare function decrypt(payload: string, secret: string): string;
10
+ //# sourceMappingURL=crypto.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"crypto.d.ts","sourceRoot":"","sources":["../../../src/channels/whatsapp/crypto.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAWH,wBAAgB,OAAO,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAKjE;AAED,wBAAgB,OAAO,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAQ/D"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=crypto.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"crypto.test.d.ts","sourceRoot":"","sources":["../../../src/channels/whatsapp/crypto.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,33 @@
1
+ /**
2
+ * WhatsApp web-client transport — a {@link Channel} built on Baileys
3
+ * (`@whiskeysockets/baileys`, an OPTIONAL peer dependency; nothing in core or
4
+ * `./channels` imports it, so `bun install` and every core import work
5
+ * without it).
6
+ *
7
+ * ```ts
8
+ * import { createWhatsAppChannel } from "@diegoaltoworks/chatter/whatsapp";
9
+ *
10
+ * const channel = createWhatsAppChannel({
11
+ * sessionSecret: process.env.WA_SESSION_SECRET as string,
12
+ * sessionIds: (process.env.WA_SESSION_IDS ?? "default").split(","),
13
+ * onMessage: async ({ sessionId, sock, message }) => {
14
+ * // apply ./channels' decideChannelAction, resolve the reply, sock.sendMessage(...)
15
+ * },
16
+ * });
17
+ *
18
+ * await createServer({ ..., channels: [channel] });
19
+ * ```
20
+ *
21
+ * ToS note: Baileys is an unofficial WhatsApp client. A linked number can be
22
+ * banned — use one you can afford to lose, and keep this channel opt-in.
23
+ *
24
+ * @packageDocumentation
25
+ */
26
+ export type { AuthStateRuntime, TursoAuthStateResult } from "./authState";
27
+ export { useTursoAuthState } from "./authState";
28
+ export type { Baileys } from "./baileys";
29
+ export { loadBaileys } from "./baileys";
30
+ export { acquireSessionLease, createWhatsAppChannel, type LeaseGatedConnectDeps, reconnectDelayMs, senderNameFor, shutdownWaSessions, type WaSessionHandle, type WhatsAppChannelConfig, type WhatsAppMessageEvent, } from "./channel";
31
+ export { decrypt, encrypt } from "./crypto";
32
+ export { canAcquireLease, createTursoWaLeaseStore, LEASE_HEARTBEAT_MS, LEASE_STALE_MS, LEASE_WAIT_MS, type WaLeaseRow, type WaLeaseStore, } from "./lease";
33
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/channels/whatsapp/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,YAAY,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAC1E,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAChD,YAAY,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AACxC,OAAO,EACL,mBAAmB,EACnB,qBAAqB,EACrB,KAAK,qBAAqB,EAC1B,gBAAgB,EAChB,aAAa,EACb,kBAAkB,EAClB,KAAK,eAAe,EACpB,KAAK,qBAAqB,EAC1B,KAAK,oBAAoB,GAC1B,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AAC5C,OAAO,EACL,eAAe,EACf,uBAAuB,EACvB,kBAAkB,EAClB,cAAc,EACd,aAAa,EACb,KAAK,UAAU,EACf,KAAK,YAAY,GAClB,MAAM,SAAS,CAAC"}