@excitedjs/feishu-transport 0.0.1

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 (50) hide show
  1. package/README.md +39 -0
  2. package/dist/contract/access-store.d.ts +23 -0
  3. package/dist/contract/access-store.d.ts.map +1 -0
  4. package/dist/contract/access-store.js +16 -0
  5. package/dist/contract/access-store.js.map +1 -0
  6. package/dist/contract/outbound.d.ts +39 -0
  7. package/dist/contract/outbound.d.ts.map +1 -0
  8. package/dist/contract/outbound.js +16 -0
  9. package/dist/contract/outbound.js.map +1 -0
  10. package/dist/contract/types.d.ts +86 -0
  11. package/dist/contract/types.d.ts.map +1 -0
  12. package/dist/contract/types.js +10 -0
  13. package/dist/contract/types.js.map +1 -0
  14. package/dist/index.d.ts +29 -0
  15. package/dist/index.d.ts.map +1 -0
  16. package/dist/index.js +31 -0
  17. package/dist/index.js.map +1 -0
  18. package/dist/json.d.ts +10 -0
  19. package/dist/json.d.ts.map +1 -0
  20. package/dist/json.js +14 -0
  21. package/dist/json.js.map +1 -0
  22. package/dist/parse/comment.d.ts +41 -0
  23. package/dist/parse/comment.d.ts.map +1 -0
  24. package/dist/parse/comment.js +51 -0
  25. package/dist/parse/comment.js.map +1 -0
  26. package/dist/parse/content.d.ts +42 -0
  27. package/dist/parse/content.d.ts.map +1 -0
  28. package/dist/parse/content.js +208 -0
  29. package/dist/parse/content.js.map +1 -0
  30. package/dist/policy/gate.d.ts +105 -0
  31. package/dist/policy/gate.d.ts.map +1 -0
  32. package/dist/policy/gate.js +276 -0
  33. package/dist/policy/gate.js.map +1 -0
  34. package/dist/policy/pairing.d.ts +12 -0
  35. package/dist/policy/pairing.d.ts.map +1 -0
  36. package/dist/policy/pairing.js +15 -0
  37. package/dist/policy/pairing.js.map +1 -0
  38. package/dist/render/render.d.ts +186 -0
  39. package/dist/render/render.d.ts.map +1 -0
  40. package/dist/render/render.js +630 -0
  41. package/dist/render/render.js.map +1 -0
  42. package/dist/transport/connection.d.ts +25 -0
  43. package/dist/transport/connection.d.ts.map +1 -0
  44. package/dist/transport/connection.js +42 -0
  45. package/dist/transport/connection.js.map +1 -0
  46. package/dist/transport/feishu.d.ts +222 -0
  47. package/dist/transport/feishu.d.ts.map +1 -0
  48. package/dist/transport/feishu.js +431 -0
  49. package/dist/transport/feishu.js.map +1 -0
  50. package/package.json +39 -0
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Pure log-line builders for the Feishu WebSocket connection lifecycle.
3
+ *
4
+ * `createFeishuTransport` wires the SDK's connection callbacks to these, so a
5
+ * dropped or failed connection is always surfaced on the host's stderr instead
6
+ * of disappearing into the SDK's reconnect loop. The wording lives here — pure
7
+ * and free of any SDK dependency — so it is unit-testable. Ported verbatim from
8
+ * claudemux.
9
+ */
10
+ /** The connection dropped and the SDK has begun a reconnect cycle. */
11
+ export declare function reconnectingLogLine(): string;
12
+ /** The reconnect cycle restored the connection. */
13
+ export declare function reconnectedLogLine(): string;
14
+ /**
15
+ * The SDK reported a connection error and stopped — a non-recoverable error,
16
+ * or an exhausted retry budget. The channel needs a restart to reconnect.
17
+ */
18
+ export declare function connectionErrorLogLine(err: unknown): string;
19
+ /**
20
+ * The initial connection never came up within the startup grace window.
21
+ * `sdkGaveUp` is true when the SDK has already stopped retrying on its own,
22
+ * false when it is still looping and the channel is the one stopping it.
23
+ */
24
+ export declare function startupTimeoutLogLine(graceMs: number, sdkGaveUp: boolean): string;
25
+ //# sourceMappingURL=connection.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connection.d.ts","sourceRoot":"","sources":["../../src/transport/connection.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAOH,sEAAsE;AACtE,wBAAgB,mBAAmB,IAAI,MAAM,CAE5C;AAED,mDAAmD;AACnD,wBAAgB,kBAAkB,IAAI,MAAM,CAE3C;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,CAE3D;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,GAAG,MAAM,CASjF"}
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Pure log-line builders for the Feishu WebSocket connection lifecycle.
3
+ *
4
+ * `createFeishuTransport` wires the SDK's connection callbacks to these, so a
5
+ * dropped or failed connection is always surfaced on the host's stderr instead
6
+ * of disappearing into the SDK's reconnect loop. The wording lives here — pure
7
+ * and free of any SDK dependency — so it is unit-testable. Ported verbatim from
8
+ * claudemux.
9
+ */
10
+ /** Extract a human-readable detail from an unknown thrown value. */
11
+ function detail(err) {
12
+ return err instanceof Error ? err.message : String(err);
13
+ }
14
+ /** The connection dropped and the SDK has begun a reconnect cycle. */
15
+ export function reconnectingLogLine() {
16
+ return 'Feishu connection lost — the SDK is reconnecting.';
17
+ }
18
+ /** The reconnect cycle restored the connection. */
19
+ export function reconnectedLogLine() {
20
+ return 'Feishu connection re-established.';
21
+ }
22
+ /**
23
+ * The SDK reported a connection error and stopped — a non-recoverable error,
24
+ * or an exhausted retry budget. The channel needs a restart to reconnect.
25
+ */
26
+ export function connectionErrorLogLine(err) {
27
+ return `Feishu connection failed and the SDK stopped retrying: ${detail(err)}`;
28
+ }
29
+ /**
30
+ * The initial connection never came up within the startup grace window.
31
+ * `sdkGaveUp` is true when the SDK has already stopped retrying on its own,
32
+ * false when it is still looping and the channel is the one stopping it.
33
+ */
34
+ export function startupTimeoutLogLine(graceMs, sdkGaveUp) {
35
+ const secs = Math.round(graceMs / 1000);
36
+ const tail = sdkGaveUp
37
+ ? 'the SDK has stopped retrying'
38
+ : 'stopping the connection attempt so it does not retry in a tight loop';
39
+ return (`Feishu connection did not come up within ${secs}s of startup; ${tail}. ` +
40
+ 'Inbound events will not arrive until the channel is restarted.');
41
+ }
42
+ //# sourceMappingURL=connection.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connection.js","sourceRoot":"","sources":["../../src/transport/connection.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,oEAAoE;AACpE,SAAS,MAAM,CAAC,GAAY;IAC1B,OAAO,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAA;AACzD,CAAC;AAED,sEAAsE;AACtE,MAAM,UAAU,mBAAmB;IACjC,OAAO,mDAAmD,CAAA;AAC5D,CAAC;AAED,mDAAmD;AACnD,MAAM,UAAU,kBAAkB;IAChC,OAAO,mCAAmC,CAAA;AAC5C,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,sBAAsB,CAAC,GAAY;IACjD,OAAO,0DAA0D,MAAM,CAAC,GAAG,CAAC,EAAE,CAAA;AAChF,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,qBAAqB,CAAC,OAAe,EAAE,SAAkB;IACvE,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,IAAI,CAAC,CAAA;IACvC,MAAM,IAAI,GAAG,SAAS;QACpB,CAAC,CAAC,8BAA8B;QAChC,CAAC,CAAC,sEAAsE,CAAA;IAC1E,OAAO,CACL,4CAA4C,IAAI,iBAAiB,IAAI,IAAI;QACzE,gEAAgE,CACjE,CAAA;AACH,CAAC"}
@@ -0,0 +1,222 @@
1
+ /**
2
+ * The Feishu platform boundary — the only module in the workspace that imports
3
+ * the Feishu SDK.
4
+ *
5
+ * Everything that talks to Feishu — the inbound long-lived WebSocket and the
6
+ * outbound message API — sits behind the `FeishuTransport` interface. A host
7
+ * depends only on that interface, so its wiring can be exercised against an
8
+ * injected fake with no live connection.
9
+ *
10
+ * The transport is event-type agnostic. `start` is handed a route table mapping
11
+ * each Feishu event_type to a callback and registers every entry with the SDK's
12
+ * event dispatcher; decoding a specific event's payload is the job of that
13
+ * event's handler (see `../parse`), not this module. Adding a new event type to
14
+ * a host therefore never touches this file.
15
+ *
16
+ * Cross-process single-instance election is intentionally **not** here. dreamux
17
+ * gives each dispatcher its own bot identity (election is moot), and claudemux
18
+ * wraps this transport with its own elected-transport layer. Core opens the
19
+ * inbound WebSocket directly; whether exactly one process may do so is the
20
+ * host's concern, layered on top. (claudemux#155 §二 / dreamux#25 §7.2.)
21
+ *
22
+ * Ported from claudemux's `feishu-channel/src/feishu.ts` (the source of truth),
23
+ * with the instance-lock removed, `sendText` upgraded to the structured
24
+ * `send(OutboundTarget, …)` contract, and `botOpenId` renamed to `selfId`.
25
+ */
26
+ import * as lark from '@larksuiteoapi/node-sdk';
27
+ import type { OutboundTarget } from '../contract/outbound.js';
28
+ /** Outcome of an outbound send. */
29
+ export interface FeishuSendResult {
30
+ /**
31
+ * message_ids of every card the send produced, in order. A Markdown body
32
+ * that fits one card produces one entry; a longer body that the renderer
33
+ * split over several cards produces several. Empty when Feishu omitted the
34
+ * message_ids.
35
+ */
36
+ messageIds: string[];
37
+ }
38
+ /**
39
+ * Build the `content` string for a Feishu plain-text message — the legacy
40
+ * `msg_type: 'text'` payload, used by `editText`'s fallback path so an edit
41
+ * on a message that was sent before this channel switched to interactive
42
+ * cards still works.
43
+ */
44
+ export declare function textMessageContent(text: string): string;
45
+ /**
46
+ * Safe ceiling for the serialised card `content` string. Stays a few hundred
47
+ * bytes below the documented 30 KB request-body limit so HTTP headers and the
48
+ * `{ params, data: { receive_id, msg_type, content } }` envelope still fit.
49
+ */
50
+ export declare const FEISHU_CARD_CONTENT_SAFE_BYTES: number;
51
+ /** One reply within a fetched document-comment thread. */
52
+ export interface FeishuDocCommentReply {
53
+ /** reply_id of this reply; `''` when Feishu omitted it. */
54
+ replyId: string;
55
+ /** open_id of the reply's author. */
56
+ authorId: string;
57
+ /** Raw Feishu rich-content elements of the reply body, rendered by the handler. */
58
+ elements: unknown[];
59
+ }
60
+ /**
61
+ * A document comment and its reply thread, fetched to enrich a comment event.
62
+ *
63
+ * The `drive.notice.comment_add_v1` payload carries only the comment's ids, so
64
+ * the comment text is fetched separately — this is the fetched result.
65
+ */
66
+ export interface FeishuDocComment {
67
+ /** False for a comment anchored to a text selection; `quote` then holds it. */
68
+ isWhole: boolean;
69
+ /** The selected text a local-selection comment is anchored to; `''` otherwise. */
70
+ quote: string;
71
+ /** The comment's replies, oldest first. */
72
+ replies: FeishuDocCommentReply[];
73
+ }
74
+ /** A document's human-readable identity, fetched to render a comment event. */
75
+ export interface FeishuDocMeta {
76
+ /** Document title. */
77
+ title: string;
78
+ /** Browser URL of the document. */
79
+ url: string;
80
+ }
81
+ /**
82
+ * One comment as `drive.v1.fileComment.batchQuery` returns it — only the
83
+ * fields the channel reads. The SDK's response type carries more; this is the
84
+ * structural subset `commentFromBatchQuery` decodes, and the shape a unit
85
+ * test builds a fixture against.
86
+ */
87
+ interface RawCommentItem {
88
+ comment_id?: string;
89
+ is_whole?: boolean;
90
+ quote?: string;
91
+ reply_list?: {
92
+ replies?: Array<{
93
+ reply_id?: string;
94
+ user_id?: string;
95
+ content?: {
96
+ elements?: unknown[];
97
+ };
98
+ }>;
99
+ };
100
+ }
101
+ /**
102
+ * Pick the comment with `commentId` out of a `fileComment.batchQuery` response
103
+ * and shape it into a `FeishuDocComment`. Returns `null` when the response
104
+ * carried no such comment. Pure: no I/O, never throws — exported so the decode
105
+ * is unit-tested without a live Feishu connection.
106
+ */
107
+ export declare function commentFromBatchQuery(items: RawCommentItem[], commentId: string): FeishuDocComment | null;
108
+ /**
109
+ * A single inbound-event route handler. Receives the raw event payload exactly
110
+ * as the Feishu SDK delivered it; decoding it is the handler's job (`../parse`).
111
+ *
112
+ * Deferred-ACK invariant (claudemux#155 §七.3 / dreamux#25 §7.3): the Feishu SDK
113
+ * sends the platform ACK frame only **after** this promise resolves. The
114
+ * returned promise MUST therefore resolve only once the message is durable in
115
+ * the host (dreamux: the SQLite `enqueueInbound` INSERT; claudemux: the inbox
116
+ * tmp+rename). Returning before durability — or returning a non-promise — would
117
+ * ACK an unpersisted message and lose it on a crash. The type is
118
+ * `=> Promise<void>`, not `=> void | Promise<void>`, precisely to keep that
119
+ * gate from being bypassed by a synchronous handler.
120
+ */
121
+ export type RouteHandler = (raw: unknown) => Promise<void>;
122
+ /**
123
+ * Inbound event routes: Feishu event_type → handler. A host builds this from
124
+ * its event registry; the transport registers every entry with the SDK's event
125
+ * dispatcher.
126
+ */
127
+ export type InboundRoutes = Record<string, RouteHandler>;
128
+ /**
129
+ * The platform boundary a host depends on. The real implementation
130
+ * (`createFeishuTransport`) wraps the Feishu SDK; tests inject a fake so the
131
+ * inbound and outbound wiring runs without a live Feishu connection.
132
+ */
133
+ export interface FeishuTransport {
134
+ /** The app id this transport was created with. */
135
+ readonly appId: string;
136
+ /**
137
+ * open_id of the bot itself, for group mention-gating. `undefined` until
138
+ * `start` has resolved it (and stays `undefined` if resolution failed).
139
+ */
140
+ readonly selfId: string | undefined;
141
+ /**
142
+ * Open the inbound WebSocket and dispatch every subscribed event_type through
143
+ * `routes`. Core opens the connection directly — single-instance election, if
144
+ * a deployment needs it, is layered on top by the host. **Rejects** if the
145
+ * connection does not come up within the startup grace window, so a host can
146
+ * fail a dispatcher loudly instead of registering a silently-dark bot.
147
+ */
148
+ start(routes: InboundRoutes): Promise<void>;
149
+ /**
150
+ * Send a text message to the target chat. Routed by `chat_id`, never by a
151
+ * message_id, so a forged reply target cannot redirect the message into an
152
+ * unrelated conversation.
153
+ *
154
+ * PR1 reads only `target.chatId` (behavior-identical to the old
155
+ * `sendText(chatId, text)`). Honoring `target.replyToMessageId` (reply-threading
156
+ * via `im.message.reply`) and `target.mentionUserIds` (auto @-back) lands in
157
+ * dreamux#25 PR2 (gap ④), with its own tests; `target.conversationKey` is a
158
+ * channel-layer routing hint the transport never reads.
159
+ */
160
+ send(target: OutboundTarget, text: string): Promise<FeishuSendResult>;
161
+ /**
162
+ * Add an emoji reaction to a message and return the reaction_id Feishu
163
+ * assigned. That id is what `removeReaction` needs to take the same reaction
164
+ * back off; Feishu can omit it, in which case an empty string is returned.
165
+ */
166
+ addReaction(messageId: string, emoji: string): Promise<string>;
167
+ /**
168
+ * Remove a reaction from a message, identified by the reaction_id that
169
+ * `addReaction` returned. Feishu only lets the app that added a reaction
170
+ * remove it, so this is always paired with a prior `addReaction` from the
171
+ * same channel.
172
+ */
173
+ removeReaction(messageId: string, reactionId: string): Promise<void>;
174
+ /** Replace the text of a message the bot previously sent. */
175
+ editText(messageId: string, text: string): Promise<void>;
176
+ /**
177
+ * Fetch one document comment and its reply thread. The comment-add event
178
+ * payload carries no comment text, so the doc-comment handler calls this to
179
+ * fill it in. Best-effort: returns `null` for a file type with no comment
180
+ * API or on any API failure, and never throws — a failure degrades the
181
+ * notification rather than dropping the event.
182
+ */
183
+ fetchDocComment(fileToken: string, fileType: string, commentId: string): Promise<FeishuDocComment | null>;
184
+ /**
185
+ * Fetch a document's title and URL, so a comment notification names the
186
+ * document a human would recognize. Best-effort: returns `null` for a file
187
+ * type with no metadata API or on any API failure, and never throws.
188
+ */
189
+ fetchDocMeta(fileToken: string, fileType: string): Promise<FeishuDocMeta | null>;
190
+ /** Close the connection and release every resource it holds. */
191
+ close(): Promise<void>;
192
+ }
193
+ /** Feishu self-built-app credentials. */
194
+ export interface FeishuCredentials {
195
+ appId: string;
196
+ appSecret: string;
197
+ }
198
+ /**
199
+ * Optional knobs for `createFeishuTransport`. The `client` seam lets unit
200
+ * tests inject a stub of just the SDK methods this module touches, so the
201
+ * outbound paths (`send`, `editText`, the doc-comment fetchers) are covered
202
+ * without a live Feishu app.
203
+ */
204
+ export interface FeishuTransportOptions {
205
+ /**
206
+ * SDK client to use for outbound API calls. Default: a fresh `lark.Client`
207
+ * built from `creds`. Tests pass a stub; production never sets this.
208
+ */
209
+ client?: lark.Client;
210
+ }
211
+ /**
212
+ * The real Feishu transport, wrapping the official SDK.
213
+ *
214
+ * Inbound: a `WSClient` opens a long-lived WebSocket and an `EventDispatcher`
215
+ * routes every subscribed event_type to its callback. Outbound: a `Client`
216
+ * calls the `im` message API; it manages the `tenant_access_token` internally.
217
+ * The outbound paths are unit-tested through the `client` seam in
218
+ * `FeishuTransportOptions`; inbound still needs a live Feishu connection.
219
+ */
220
+ export declare function createFeishuTransport(creds: FeishuCredentials, options?: FeishuTransportOptions): FeishuTransport;
221
+ export {};
222
+ //# sourceMappingURL=feishu.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"feishu.d.ts","sourceRoot":"","sources":["../../src/transport/feishu.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,OAAO,KAAK,IAAI,MAAM,yBAAyB,CAAA;AAE/C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAA;AA0C7D,mCAAmC;AACnC,MAAM,WAAW,gBAAgB;IAC/B;;;;;OAKG;IACH,UAAU,EAAE,MAAM,EAAE,CAAA;CACrB;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEvD;AAED;;;;GAIG;AACH,eAAO,MAAM,8BAA8B,QAAY,CAAA;AAyCvD,0DAA0D;AAC1D,MAAM,WAAW,qBAAqB;IACpC,2DAA2D;IAC3D,OAAO,EAAE,MAAM,CAAA;IACf,qCAAqC;IACrC,QAAQ,EAAE,MAAM,CAAA;IAChB,mFAAmF;IACnF,QAAQ,EAAE,OAAO,EAAE,CAAA;CACpB;AAED;;;;;GAKG;AACH,MAAM,WAAW,gBAAgB;IAC/B,+EAA+E;IAC/E,OAAO,EAAE,OAAO,CAAA;IAChB,kFAAkF;IAClF,KAAK,EAAE,MAAM,CAAA;IACb,2CAA2C;IAC3C,OAAO,EAAE,qBAAqB,EAAE,CAAA;CACjC;AAED,+EAA+E;AAC/E,MAAM,WAAW,aAAa;IAC5B,sBAAsB;IACtB,KAAK,EAAE,MAAM,CAAA;IACb,mCAAmC;IACnC,GAAG,EAAE,MAAM,CAAA;CACZ;AAaD;;;;;GAKG;AACH,UAAU,cAAc;IACtB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,UAAU,CAAC,EAAE;QACX,OAAO,CAAC,EAAE,KAAK,CAAC;YACd,QAAQ,CAAC,EAAE,MAAM,CAAA;YACjB,OAAO,CAAC,EAAE,MAAM,CAAA;YAChB,OAAO,CAAC,EAAE;gBAAE,QAAQ,CAAC,EAAE,OAAO,EAAE,CAAA;aAAE,CAAA;SACnC,CAAC,CAAA;KACH,CAAA;CACF;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CACnC,KAAK,EAAE,cAAc,EAAE,EACvB,SAAS,EAAE,MAAM,GAChB,gBAAgB,GAAG,IAAI,CASzB;AAwBD;;;;;;;;;;;;GAYG;AACH,MAAM,MAAM,YAAY,GAAG,CAAC,GAAG,EAAE,OAAO,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;AAE1D;;;;GAIG;AACH,MAAM,MAAM,aAAa,GAAG,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAA;AAExD;;;;GAIG;AACH,MAAM,WAAW,eAAe;IAC9B,kDAAkD;IAClD,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB;;;OAGG;IACH,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAA;IACnC;;;;;;OAMG;IACH,KAAK,CAAC,MAAM,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAC3C;;;;;;;;;;OAUG;IACH,IAAI,CAAC,MAAM,EAAE,cAAc,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAAA;IACrE;;;;OAIG;IACH,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAA;IAC9D;;;;;OAKG;IACH,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACpE,6DAA6D;IAC7D,QAAQ,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACxD;;;;;;OAMG;IACH,eAAe,CACb,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAAA;IACnC;;;;OAIG;IACH,YAAY,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CAAA;IAChF,gEAAgE;IAChE,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;CACvB;AAED,yCAAyC;AACzC,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,MAAM,CAAA;IACb,SAAS,EAAE,MAAM,CAAA;CAClB;AAED;;;;;GAKG;AACH,MAAM,WAAW,sBAAsB;IACrC;;;OAGG;IACH,MAAM,CAAC,EAAE,IAAI,CAAC,MAAM,CAAA;CACrB;AAED;;;;;;;;GAQG;AACH,wBAAgB,qBAAqB,CACnC,KAAK,EAAE,iBAAiB,EACxB,OAAO,GAAE,sBAA2B,GACnC,eAAe,CAmOjB"}