ab-ecosystem-cpass 1.0.6 → 1.0.8
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/README.md +5 -2
- package/dist/client.d.ts +31 -6
- package/dist/client.js +36 -8
- package/dist/client.js.map +1 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.js +9 -5
- package/dist/index.js.map +1 -1
- package/dist/resources/audience/canned-replies.d.ts +95 -0
- package/dist/resources/audience/canned-replies.js +112 -0
- package/dist/resources/audience/canned-replies.js.map +1 -0
- package/dist/resources/audience/notifications.d.ts +14 -0
- package/dist/resources/audience/notifications.js +14 -0
- package/dist/resources/audience/notifications.js.map +1 -1
- package/dist/resources/campaign-messages.d.ts +153 -0
- package/dist/resources/campaign-messages.js +179 -0
- package/dist/resources/campaign-messages.js.map +1 -0
- package/dist/resources/data-stores.d.ts +469 -0
- package/dist/resources/data-stores.js +530 -0
- package/dist/resources/data-stores.js.map +1 -0
- package/dist/resources/paths.d.ts +6 -0
- package/dist/resources/paths.js +7 -1
- package/dist/resources/paths.js.map +1 -1
- package/dist/types/campaign.d.ts +137 -41
- package/dist/types/canned-reply.d.ts +124 -0
- package/dist/types/canned-reply.js +6 -0
- package/dist/types/canned-reply.js.map +1 -0
- package/dist/types/datastore.d.ts +260 -0
- package/dist/types/datastore.js +21 -0
- package/dist/types/datastore.js.map +1 -0
- package/dist/types/index.d.ts +2 -0
- package/dist/types/index.js +3 -0
- package/dist/types/index.js.map +1 -1
- package/docs/audience/canned-replies.md +302 -0
- package/docs/audience/notifications.md +99 -0
- package/docs/campaign/campaign-messages.md +282 -0
- package/docs/campaign/campaigns.md +8 -48
- package/docs/data-store.md +495 -0
- package/package.json +1 -1
- package/dist/resources/campaign-report-exports.d.ts +0 -14
- package/dist/resources/campaign-report-exports.js +0 -28
- package/dist/resources/campaign-report-exports.js.map +0 -1
- package/dist/resources/campaign-reports.d.ts +0 -15
- package/dist/resources/campaign-reports.js +0 -25
- package/dist/resources/campaign-reports.js.map +0 -1
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import type { CampaignMessageRecord, CampaignMessageStats, CampaignMessageStatsQuery, CampaignRepliesQuery, CampaignReplyBreakdown, ListCampaignMessagesQuery } from '../types/campaign';
|
|
2
|
+
import type { Paginated } from '../types/common';
|
|
3
|
+
import type { HttpClient } from '../http';
|
|
4
|
+
/**
|
|
5
|
+
* Campaign reporting: what actually happened to each message.
|
|
6
|
+
*
|
|
7
|
+
* ```ts
|
|
8
|
+
* const stats = await crm.campaignMessages.stats(campaign);
|
|
9
|
+
* // { total: 12480, sent: 12410, delivered: 11980, read: 7420, replied: 310, failed: 70 }
|
|
10
|
+
*
|
|
11
|
+
* const { data } = await crm.campaignMessages.list(campaign, { status: 'failed' });
|
|
12
|
+
* data[0].error; // why that one did not go
|
|
13
|
+
* ```
|
|
14
|
+
*
|
|
15
|
+
* Every method takes the campaign id first. A report has no meaning without one,
|
|
16
|
+
* so it is addressed as the campaign's own sub-resource --
|
|
17
|
+
* `/v1/campaigns/:id/report` -- rather than as a collection of rows that a
|
|
18
|
+
* campaign filters. The options object holds only what *narrows* a report that
|
|
19
|
+
* already exists: a message index, a status, a page.
|
|
20
|
+
*
|
|
21
|
+
* ## Where these numbers come from
|
|
22
|
+
*
|
|
23
|
+
* One row per attempted send, written by the campaign manager as it hands each
|
|
24
|
+
* message to a channel, with its `status` moved forward by the channel's
|
|
25
|
+
* delivery webhooks -- which are processed outside the CRM entirely. The report
|
|
26
|
+
* is **counted off those rows when you ask for it**, not rolled up on a
|
|
27
|
+
* schedule, so there is no "generated at" to reason about and no staleness
|
|
28
|
+
* window: the numbers are as current as the webhooks that have arrived.
|
|
29
|
+
*
|
|
30
|
+
* The flip side is that a campaign still going out has moving numbers. Reading
|
|
31
|
+
* `stats` seconds after launching under-counts everything, and that is correct
|
|
32
|
+
* rather than a lag.
|
|
33
|
+
*
|
|
34
|
+
* ## The funnel
|
|
35
|
+
*
|
|
36
|
+
* `status` is a single field holding the furthest stage reached, so the counts
|
|
37
|
+
* are cumulative and nest: `replied ⊆ read ⊆ delivered ⊆ sent`. For a stage's
|
|
38
|
+
* exclusive count, subtract the next one -- `delivered - read` is "delivered but
|
|
39
|
+
* unread". `failed` is outside the funnel and never inside `sent`.
|
|
40
|
+
*
|
|
41
|
+
* ## There is no export endpoint
|
|
42
|
+
*
|
|
43
|
+
* An export is {@link list} written to a file, so {@link iterate} does the
|
|
44
|
+
* paging and the caller formats the rows. Nothing is queued, nothing has to be
|
|
45
|
+
* polled, and no generated file expires before someone downloads it.
|
|
46
|
+
*/
|
|
47
|
+
export declare class CampaignMessages {
|
|
48
|
+
private readonly http;
|
|
49
|
+
constructor(http: HttpClient);
|
|
50
|
+
/**
|
|
51
|
+
* The campaign's funnel counts.
|
|
52
|
+
*
|
|
53
|
+
* `GET /v1/campaigns/:id/report` · `campaigns:read` · **200**
|
|
54
|
+
*
|
|
55
|
+
* ### Query
|
|
56
|
+
*
|
|
57
|
+
* | Field | Type | Notes |
|
|
58
|
+
* | --- | --- | --- |
|
|
59
|
+
* | `message` | number | One message of a multi-message campaign, zero-based. Omit for the whole campaign. |
|
|
60
|
+
*
|
|
61
|
+
* ```ts
|
|
62
|
+
* const overall = await crm.campaignMessages.stats(campaign);
|
|
63
|
+
* const second = await crm.campaignMessages.stats(campaign, { message: 1 });
|
|
64
|
+
* ```
|
|
65
|
+
*
|
|
66
|
+
* No date window: a campaign is one send, so its whole life is the only
|
|
67
|
+
* meaningful window. A repeating campaign's runs are separate campaigns with
|
|
68
|
+
* their own ids.
|
|
69
|
+
*
|
|
70
|
+
* `404 not_found` if the campaign is not the caller's -- the same answer as a
|
|
71
|
+
* campaign that does not exist, so the error cannot be used to probe ids.
|
|
72
|
+
*/
|
|
73
|
+
stats(campaign: string, query?: CampaignMessageStatsQuery): Promise<CampaignMessageStats>;
|
|
74
|
+
/**
|
|
75
|
+
* The rows behind the funnel. This is the report table.
|
|
76
|
+
*
|
|
77
|
+
* `GET /v1/campaigns/:id/report/messages` · `campaigns:read` · **200**
|
|
78
|
+
*
|
|
79
|
+
* ### Query
|
|
80
|
+
*
|
|
81
|
+
* | Field | Type | Default | Notes |
|
|
82
|
+
* | --- | --- | --- | --- |
|
|
83
|
+
* | `message` | number | — | Zero-based index of one campaign message. |
|
|
84
|
+
* | `status` | `sent \| delivered \| read \| replied \| failed` | — | Cumulative; see below. Omit for every row. |
|
|
85
|
+
* | `exact` | boolean | `false` | Only rows sitting at `status` right now. |
|
|
86
|
+
* | `identifier` | string | — | Exact match on one recipient. Not a search. |
|
|
87
|
+
* | `reply_type` | string | — | Replied rows only; refused otherwise. |
|
|
88
|
+
* | `replied_within_hours` | number | — | Replied rows only; refused otherwise. |
|
|
89
|
+
* | `page` | number | `1` | 1-based. |
|
|
90
|
+
* | `rows` | number | `25` | 1–100. |
|
|
91
|
+
*
|
|
92
|
+
* Newest attempt first, ordered by when the row was written -- not by a stage
|
|
93
|
+
* timestamp, which would reorder the list as webhooks arrive and make paging
|
|
94
|
+
* skip and repeat rows.
|
|
95
|
+
*
|
|
96
|
+
* ```ts
|
|
97
|
+
* // the two queries a report screen actually makes
|
|
98
|
+
* const failures = await crm.campaignMessages.list(campaign, { status: 'failed', rows: 100 });
|
|
99
|
+
* const unread = await crm.campaignMessages.list(campaign, { status: 'delivered', exact: true });
|
|
100
|
+
* ```
|
|
101
|
+
*
|
|
102
|
+
* `reply_type` and `replied_within_hours` are **refused** with
|
|
103
|
+
* `422 validation_failed` unless `status` is `replied`, rather than quietly
|
|
104
|
+
* matching nothing.
|
|
105
|
+
*/
|
|
106
|
+
list(campaign: string, query?: ListCampaignMessagesQuery): Promise<Paginated<CampaignMessageRecord>>;
|
|
107
|
+
/**
|
|
108
|
+
* Every matching row, paged through for you. What an export is.
|
|
109
|
+
*
|
|
110
|
+
* ```ts
|
|
111
|
+
* for await (const row of crm.campaignMessages.iterate(campaign)) {
|
|
112
|
+
* csv.write([row.identifier, row.name, row.status, row.error ?? '']);
|
|
113
|
+
* }
|
|
114
|
+
* ```
|
|
115
|
+
*
|
|
116
|
+
* Pages at 100 rows unless told otherwise -- the server's maximum, and the
|
|
117
|
+
* fewest round trips. A campaign of a million recipients is ten thousand
|
|
118
|
+
* requests, so for exports that size prefer running it where the latency is
|
|
119
|
+
* low rather than from a browser.
|
|
120
|
+
*
|
|
121
|
+
* The collection is live: rows are written while a campaign is going out, and
|
|
122
|
+
* statuses change under an export that takes minutes. Offset paging over
|
|
123
|
+
* moving data can repeat or skip a row -- for a stable snapshot, export after
|
|
124
|
+
* the campaign completes.
|
|
125
|
+
*/
|
|
126
|
+
iterate(campaign: string, query?: ListCampaignMessagesQuery): AsyncGenerator<CampaignMessageRecord>;
|
|
127
|
+
/**
|
|
128
|
+
* What people replied, grouped by the reply itself.
|
|
129
|
+
*
|
|
130
|
+
* `GET /v1/campaigns/:id/report/replies` · `campaigns:read` · **200**
|
|
131
|
+
*
|
|
132
|
+
* ### Query
|
|
133
|
+
*
|
|
134
|
+
* | Field | Type | Default | Notes |
|
|
135
|
+
* | --- | --- | --- | --- |
|
|
136
|
+
* | `message` | number | — | Zero-based index of one campaign message. |
|
|
137
|
+
* | `limit` | number | `50` | Distinct replies to return, 1–200. |
|
|
138
|
+
*
|
|
139
|
+
* ```ts
|
|
140
|
+
* const { groups, replied, distinct } = await crm.campaignMessages.replies(campaign);
|
|
141
|
+
* groups; // [{ reply: 'Yes', replyType: 'button', count: 182 }, …]
|
|
142
|
+
* ```
|
|
143
|
+
*
|
|
144
|
+
* This is what a poll or a button message is sent to find out. Grouped on the
|
|
145
|
+
* reply **and** its type, so a tapped `Yes` and a typed `Yes` stay apart --
|
|
146
|
+
* they say different things about how engaged the recipient was.
|
|
147
|
+
*
|
|
148
|
+
* `distinct` against `groups.length` tells you whether the breakdown is
|
|
149
|
+
* complete. A campaign that asked an open question has as many groups as it
|
|
150
|
+
* has repliers, so a truncated answer there is normal rather than a problem.
|
|
151
|
+
*/
|
|
152
|
+
replies(campaign: string, query?: CampaignRepliesQuery): Promise<CampaignReplyBreakdown>;
|
|
153
|
+
}
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.CampaignMessages = void 0;
|
|
4
|
+
const paths_1 = require("./paths");
|
|
5
|
+
/** `/v1/campaigns/:id/report` -- the report is the campaign's own sub-resource. */
|
|
6
|
+
const report = (campaign) => `/v1/campaigns/${(0, paths_1.seg)(campaign)}/report`;
|
|
7
|
+
/**
|
|
8
|
+
* Campaign reporting: what actually happened to each message.
|
|
9
|
+
*
|
|
10
|
+
* ```ts
|
|
11
|
+
* const stats = await crm.campaignMessages.stats(campaign);
|
|
12
|
+
* // { total: 12480, sent: 12410, delivered: 11980, read: 7420, replied: 310, failed: 70 }
|
|
13
|
+
*
|
|
14
|
+
* const { data } = await crm.campaignMessages.list(campaign, { status: 'failed' });
|
|
15
|
+
* data[0].error; // why that one did not go
|
|
16
|
+
* ```
|
|
17
|
+
*
|
|
18
|
+
* Every method takes the campaign id first. A report has no meaning without one,
|
|
19
|
+
* so it is addressed as the campaign's own sub-resource --
|
|
20
|
+
* `/v1/campaigns/:id/report` -- rather than as a collection of rows that a
|
|
21
|
+
* campaign filters. The options object holds only what *narrows* a report that
|
|
22
|
+
* already exists: a message index, a status, a page.
|
|
23
|
+
*
|
|
24
|
+
* ## Where these numbers come from
|
|
25
|
+
*
|
|
26
|
+
* One row per attempted send, written by the campaign manager as it hands each
|
|
27
|
+
* message to a channel, with its `status` moved forward by the channel's
|
|
28
|
+
* delivery webhooks -- which are processed outside the CRM entirely. The report
|
|
29
|
+
* is **counted off those rows when you ask for it**, not rolled up on a
|
|
30
|
+
* schedule, so there is no "generated at" to reason about and no staleness
|
|
31
|
+
* window: the numbers are as current as the webhooks that have arrived.
|
|
32
|
+
*
|
|
33
|
+
* The flip side is that a campaign still going out has moving numbers. Reading
|
|
34
|
+
* `stats` seconds after launching under-counts everything, and that is correct
|
|
35
|
+
* rather than a lag.
|
|
36
|
+
*
|
|
37
|
+
* ## The funnel
|
|
38
|
+
*
|
|
39
|
+
* `status` is a single field holding the furthest stage reached, so the counts
|
|
40
|
+
* are cumulative and nest: `replied ⊆ read ⊆ delivered ⊆ sent`. For a stage's
|
|
41
|
+
* exclusive count, subtract the next one -- `delivered - read` is "delivered but
|
|
42
|
+
* unread". `failed` is outside the funnel and never inside `sent`.
|
|
43
|
+
*
|
|
44
|
+
* ## There is no export endpoint
|
|
45
|
+
*
|
|
46
|
+
* An export is {@link list} written to a file, so {@link iterate} does the
|
|
47
|
+
* paging and the caller formats the rows. Nothing is queued, nothing has to be
|
|
48
|
+
* polled, and no generated file expires before someone downloads it.
|
|
49
|
+
*/
|
|
50
|
+
class CampaignMessages {
|
|
51
|
+
http;
|
|
52
|
+
constructor(http) {
|
|
53
|
+
this.http = http;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* The campaign's funnel counts.
|
|
57
|
+
*
|
|
58
|
+
* `GET /v1/campaigns/:id/report` · `campaigns:read` · **200**
|
|
59
|
+
*
|
|
60
|
+
* ### Query
|
|
61
|
+
*
|
|
62
|
+
* | Field | Type | Notes |
|
|
63
|
+
* | --- | --- | --- |
|
|
64
|
+
* | `message` | number | One message of a multi-message campaign, zero-based. Omit for the whole campaign. |
|
|
65
|
+
*
|
|
66
|
+
* ```ts
|
|
67
|
+
* const overall = await crm.campaignMessages.stats(campaign);
|
|
68
|
+
* const second = await crm.campaignMessages.stats(campaign, { message: 1 });
|
|
69
|
+
* ```
|
|
70
|
+
*
|
|
71
|
+
* No date window: a campaign is one send, so its whole life is the only
|
|
72
|
+
* meaningful window. A repeating campaign's runs are separate campaigns with
|
|
73
|
+
* their own ids.
|
|
74
|
+
*
|
|
75
|
+
* `404 not_found` if the campaign is not the caller's -- the same answer as a
|
|
76
|
+
* campaign that does not exist, so the error cannot be used to probe ids.
|
|
77
|
+
*/
|
|
78
|
+
stats(campaign, query = {}) {
|
|
79
|
+
return this.http.request('GET', report(campaign), { query });
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* The rows behind the funnel. This is the report table.
|
|
83
|
+
*
|
|
84
|
+
* `GET /v1/campaigns/:id/report/messages` · `campaigns:read` · **200**
|
|
85
|
+
*
|
|
86
|
+
* ### Query
|
|
87
|
+
*
|
|
88
|
+
* | Field | Type | Default | Notes |
|
|
89
|
+
* | --- | --- | --- | --- |
|
|
90
|
+
* | `message` | number | — | Zero-based index of one campaign message. |
|
|
91
|
+
* | `status` | `sent \| delivered \| read \| replied \| failed` | — | Cumulative; see below. Omit for every row. |
|
|
92
|
+
* | `exact` | boolean | `false` | Only rows sitting at `status` right now. |
|
|
93
|
+
* | `identifier` | string | — | Exact match on one recipient. Not a search. |
|
|
94
|
+
* | `reply_type` | string | — | Replied rows only; refused otherwise. |
|
|
95
|
+
* | `replied_within_hours` | number | — | Replied rows only; refused otherwise. |
|
|
96
|
+
* | `page` | number | `1` | 1-based. |
|
|
97
|
+
* | `rows` | number | `25` | 1–100. |
|
|
98
|
+
*
|
|
99
|
+
* Newest attempt first, ordered by when the row was written -- not by a stage
|
|
100
|
+
* timestamp, which would reorder the list as webhooks arrive and make paging
|
|
101
|
+
* skip and repeat rows.
|
|
102
|
+
*
|
|
103
|
+
* ```ts
|
|
104
|
+
* // the two queries a report screen actually makes
|
|
105
|
+
* const failures = await crm.campaignMessages.list(campaign, { status: 'failed', rows: 100 });
|
|
106
|
+
* const unread = await crm.campaignMessages.list(campaign, { status: 'delivered', exact: true });
|
|
107
|
+
* ```
|
|
108
|
+
*
|
|
109
|
+
* `reply_type` and `replied_within_hours` are **refused** with
|
|
110
|
+
* `422 validation_failed` unless `status` is `replied`, rather than quietly
|
|
111
|
+
* matching nothing.
|
|
112
|
+
*/
|
|
113
|
+
list(campaign, query = {}) {
|
|
114
|
+
return this.http.request('GET', `${report(campaign)}/messages`, { query });
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Every matching row, paged through for you. What an export is.
|
|
118
|
+
*
|
|
119
|
+
* ```ts
|
|
120
|
+
* for await (const row of crm.campaignMessages.iterate(campaign)) {
|
|
121
|
+
* csv.write([row.identifier, row.name, row.status, row.error ?? '']);
|
|
122
|
+
* }
|
|
123
|
+
* ```
|
|
124
|
+
*
|
|
125
|
+
* Pages at 100 rows unless told otherwise -- the server's maximum, and the
|
|
126
|
+
* fewest round trips. A campaign of a million recipients is ten thousand
|
|
127
|
+
* requests, so for exports that size prefer running it where the latency is
|
|
128
|
+
* low rather than from a browser.
|
|
129
|
+
*
|
|
130
|
+
* The collection is live: rows are written while a campaign is going out, and
|
|
131
|
+
* statuses change under an export that takes minutes. Offset paging over
|
|
132
|
+
* moving data can repeat or skip a row -- for a stable snapshot, export after
|
|
133
|
+
* the campaign completes.
|
|
134
|
+
*/
|
|
135
|
+
async *iterate(campaign, query = {}) {
|
|
136
|
+
const rows = query.rows ?? 100;
|
|
137
|
+
let page = query.page ?? 1;
|
|
138
|
+
for (;;) {
|
|
139
|
+
const result = await this.list(campaign, { ...query, page, rows });
|
|
140
|
+
for (const record of result.data)
|
|
141
|
+
yield record;
|
|
142
|
+
if (result.data.length === 0 || page >= result.pagination.pages)
|
|
143
|
+
return;
|
|
144
|
+
page += 1;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* What people replied, grouped by the reply itself.
|
|
149
|
+
*
|
|
150
|
+
* `GET /v1/campaigns/:id/report/replies` · `campaigns:read` · **200**
|
|
151
|
+
*
|
|
152
|
+
* ### Query
|
|
153
|
+
*
|
|
154
|
+
* | Field | Type | Default | Notes |
|
|
155
|
+
* | --- | --- | --- | --- |
|
|
156
|
+
* | `message` | number | — | Zero-based index of one campaign message. |
|
|
157
|
+
* | `limit` | number | `50` | Distinct replies to return, 1–200. |
|
|
158
|
+
*
|
|
159
|
+
* ```ts
|
|
160
|
+
* const { groups, replied, distinct } = await crm.campaignMessages.replies(campaign);
|
|
161
|
+
* groups; // [{ reply: 'Yes', replyType: 'button', count: 182 }, …]
|
|
162
|
+
* ```
|
|
163
|
+
*
|
|
164
|
+
* This is what a poll or a button message is sent to find out. Grouped on the
|
|
165
|
+
* reply **and** its type, so a tapped `Yes` and a typed `Yes` stay apart --
|
|
166
|
+
* they say different things about how engaged the recipient was.
|
|
167
|
+
*
|
|
168
|
+
* `distinct` against `groups.length` tells you whether the breakdown is
|
|
169
|
+
* complete. A campaign that asked an open question has as many groups as it
|
|
170
|
+
* has repliers, so a truncated answer there is normal rather than a problem.
|
|
171
|
+
*/
|
|
172
|
+
replies(campaign, query = {}) {
|
|
173
|
+
return this.http.request('GET', `${report(campaign)}/replies`, {
|
|
174
|
+
query,
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
exports.CampaignMessages = CampaignMessages;
|
|
179
|
+
//# sourceMappingURL=campaign-messages.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"campaign-messages.js","sourceRoot":"","sources":["../../src/resources/campaign-messages.ts"],"names":[],"mappings":";;;AAUA,mCAA8B;AAE9B,mFAAmF;AACnF,MAAM,MAAM,GAAG,CAAC,QAAgB,EAAU,EAAE,CAAC,iBAAiB,IAAA,WAAG,EAAC,QAAQ,CAAC,SAAS,CAAC;AAErF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AACH,MAAa,gBAAgB;IACE;IAA7B,YAA6B,IAAgB;QAAhB,SAAI,GAAJ,IAAI,CAAY;IAAG,CAAC;IAEjD;;;;;;;;;;;;;;;;;;;;;;OAsBG;IACH,KAAK,CAAC,QAAgB,EAAE,QAAmC,EAAE;QAC3D,OAAO,IAAI,CAAC,IAAI,CAAC,OAAO,CAAuB,KAAK,EAAE,MAAM,CAAC,QAAQ,CAAC,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC;IACrF,CAAC;IAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA+BG;IACH,IAAI,CACF,QAAgB,EAChB,QAAmC,EAAE;QAErC,OAAO,IAAI,CAAC,IAAI,CAAC,OAAO,CACtB,KAAK,EACL,GAAG,MAAM,CAAC,QAAQ,CAAC,WAAW,EAC9B,EAAE,KAAK,EAAE,CACV,CAAC;IACJ,CAAC;IAED;;;;;;;;;;;;;;;;;;OAkBG;IACH,KAAK,CAAC,CAAC,OAAO,CACZ,QAAgB,EAChB,QAAmC,EAAE;QAErC,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,IAAI,GAAG,CAAC;QAC/B,IAAI,IAAI,GAAG,KAAK,CAAC,IAAI,IAAI,CAAC,CAAC;QAE3B,SAAS,CAAC;YACR,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,EAAE,GAAG,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;YACnE,KAAK,MAAM,MAAM,IAAI,MAAM,CAAC,IAAI;gBAAE,MAAM,MAAM,CAAC;YAE/C,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,KAAK,CAAC,IAAI,IAAI,IAAI,MAAM,CAAC,UAAU,CAAC,KAAK;gBAAE,OAAO;YACxE,IAAI,IAAI,CAAC,CAAC;QACZ,CAAC;IACH,CAAC;IAED;;;;;;;;;;;;;;;;;;;;;;;;OAwBG;IACH,OAAO,CAAC,QAAgB,EAAE,QAA8B,EAAE;QACxD,OAAO,IAAI,CAAC,IAAI,CAAC,OAAO,CAAyB,KAAK,EAAE,GAAG,MAAM,CAAC,QAAQ,CAAC,UAAU,EAAE;YACrF,KAAK;SACN,CAAC,CAAC;IACL,CAAC;CACF;AA1ID,4CA0IC"}
|