@adeli/sdk 0.0.0-stage → 0.1.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.
- package/LICENSE +21 -0
- package/README.md +30 -2
- package/dist/comments-C_vDnwix.d.cts +8268 -0
- package/dist/comments-Dx3195Ty.d.ts +8268 -0
- package/dist/helpers/index.cjs +482 -0
- package/dist/helpers/index.cjs.map +1 -0
- package/dist/helpers/index.d.cts +207 -0
- package/dist/helpers/index.d.ts +207 -0
- package/dist/helpers/index.js +471 -0
- package/dist/helpers/index.js.map +1 -0
- package/dist/index.cjs +878 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +360 -0
- package/dist/index.d.ts +360 -0
- package/dist/index.js +860 -0
- package/dist/index.js.map +1 -0
- package/dist/uploads-MGVSU99R.d.cts +100 -0
- package/dist/uploads-MGVSU99R.d.ts +100 -0
- package/dist/uploads.cjs +45 -0
- package/dist/uploads.cjs.map +1 -0
- package/dist/uploads.d.cts +1 -0
- package/dist/uploads.d.ts +1 -0
- package/dist/uploads.js +41 -0
- package/dist/uploads.js.map +1 -0
- package/docs/api-reference.md +72 -0
- package/docs/availability.md +26 -0
- package/docs/client.md +72 -0
- package/docs/maintenance.md +30 -0
- package/docs/publishing.md +39 -0
- package/openapi/adeli.openapi.json +20505 -0
- package/openapi/docs-links.json +71 -0
- package/openapi/provenance.json +8 -0
- package/package.json +85 -4
|
@@ -0,0 +1,482 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var sdk = require('@adeli/sdk');
|
|
4
|
+
var crypto = require('crypto');
|
|
5
|
+
|
|
6
|
+
// src/helpers/polling.ts
|
|
7
|
+
|
|
8
|
+
// src/core/duration.ts
|
|
9
|
+
var MAX_TIMER_DURATION_MS = 2147483647;
|
|
10
|
+
function isRepresentableTimerDuration(value) {
|
|
11
|
+
return Number.isFinite(value) && value > 0 && value <= MAX_TIMER_DURATION_MS;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// src/helpers/polling.ts
|
|
15
|
+
function validatePollingOptions(options) {
|
|
16
|
+
if (!isRepresentableTimerDuration(options.timeoutMs)) {
|
|
17
|
+
throw new RangeError(`timeoutMs must be a positive finite number no greater than ${MAX_TIMER_DURATION_MS}ms`);
|
|
18
|
+
}
|
|
19
|
+
if (!isRepresentableTimerDuration(options.intervalMs)) {
|
|
20
|
+
throw new RangeError(`intervalMs must be a positive finite number no greater than ${MAX_TIMER_DURATION_MS}ms`);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
function cancellation() {
|
|
24
|
+
return new sdk.CancellationError("The helper operation was cancelled");
|
|
25
|
+
}
|
|
26
|
+
function timeout() {
|
|
27
|
+
return new sdk.TimeoutError("The helper operation exceeded its total timeout");
|
|
28
|
+
}
|
|
29
|
+
function protocolError(message) {
|
|
30
|
+
return new sdk.MalformedResponseError(message);
|
|
31
|
+
}
|
|
32
|
+
function assertNotAborted(signal) {
|
|
33
|
+
if (signal?.aborted) throw cancellation();
|
|
34
|
+
}
|
|
35
|
+
async function requestWithDeadline(request, deadline, callerSignal) {
|
|
36
|
+
assertNotAborted(callerSignal);
|
|
37
|
+
const remaining = deadline.at - Date.now();
|
|
38
|
+
if (remaining <= 0) throw timeout();
|
|
39
|
+
const controller = new AbortController();
|
|
40
|
+
let timer;
|
|
41
|
+
let settled = false;
|
|
42
|
+
let onAbort;
|
|
43
|
+
const outcome = new Promise((resolve, reject) => {
|
|
44
|
+
const finish = (action) => {
|
|
45
|
+
if (settled) return;
|
|
46
|
+
settled = true;
|
|
47
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
48
|
+
if (onAbort !== void 0) callerSignal?.removeEventListener("abort", onAbort);
|
|
49
|
+
action();
|
|
50
|
+
};
|
|
51
|
+
onAbort = () => {
|
|
52
|
+
controller.abort();
|
|
53
|
+
finish(() => reject(cancellation()));
|
|
54
|
+
};
|
|
55
|
+
callerSignal?.addEventListener("abort", onAbort, { once: true });
|
|
56
|
+
timer = setTimeout(() => {
|
|
57
|
+
controller.abort();
|
|
58
|
+
finish(() => reject(timeout()));
|
|
59
|
+
}, remaining);
|
|
60
|
+
let result;
|
|
61
|
+
try {
|
|
62
|
+
result = Promise.resolve(request(controller.signal));
|
|
63
|
+
} catch (error) {
|
|
64
|
+
finish(() => reject(error));
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
result.then(
|
|
68
|
+
(value) => finish(() => {
|
|
69
|
+
if (callerSignal?.aborted) reject(cancellation());
|
|
70
|
+
else if (Date.now() >= deadline.at) reject(timeout());
|
|
71
|
+
else resolve(value);
|
|
72
|
+
}),
|
|
73
|
+
(error) => finish(() => {
|
|
74
|
+
if (callerSignal?.aborted) reject(cancellation());
|
|
75
|
+
else if (Date.now() >= deadline.at) reject(timeout());
|
|
76
|
+
else reject(error);
|
|
77
|
+
})
|
|
78
|
+
);
|
|
79
|
+
});
|
|
80
|
+
return outcome;
|
|
81
|
+
}
|
|
82
|
+
async function sleepUntil(deadline, intervalMs, signal) {
|
|
83
|
+
assertNotAborted(signal);
|
|
84
|
+
const remaining = deadline.at - Date.now();
|
|
85
|
+
if (remaining <= 0) throw timeout();
|
|
86
|
+
await new Promise((resolve, reject) => {
|
|
87
|
+
let settled = false;
|
|
88
|
+
const delay = Math.min(intervalMs, remaining);
|
|
89
|
+
let timer = setTimeout(() => {
|
|
90
|
+
finish(() => {
|
|
91
|
+
if (deadline.at - Date.now() <= 0) reject(timeout());
|
|
92
|
+
else resolve();
|
|
93
|
+
});
|
|
94
|
+
}, delay);
|
|
95
|
+
const onAbort = () => finish(() => reject(cancellation()));
|
|
96
|
+
const finish = (action) => {
|
|
97
|
+
if (settled) return;
|
|
98
|
+
settled = true;
|
|
99
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
100
|
+
signal?.removeEventListener("abort", onAbort);
|
|
101
|
+
action();
|
|
102
|
+
};
|
|
103
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
async function poll(fetchStatus, status, options) {
|
|
107
|
+
validatePollingOptions(options);
|
|
108
|
+
assertNotAborted(options.signal);
|
|
109
|
+
const deadline = { at: Date.now() + options.timeoutMs };
|
|
110
|
+
while (true) {
|
|
111
|
+
const response = await requestWithDeadline(fetchStatus, deadline, options.signal);
|
|
112
|
+
assertNotAborted(options.signal);
|
|
113
|
+
if (Date.now() >= deadline.at) throw timeout();
|
|
114
|
+
const state = status(response);
|
|
115
|
+
if (state === "terminal") return response;
|
|
116
|
+
if (state === "invalid") throw protocolError("The provider returned an unknown polling status");
|
|
117
|
+
await sleepUntil(deadline, options.intervalMs, options.signal);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
function connectionStatus(response) {
|
|
121
|
+
const value = response.data?.status;
|
|
122
|
+
switch (value) {
|
|
123
|
+
case "pending_authorization":
|
|
124
|
+
case "processing":
|
|
125
|
+
case "pending_page_selection":
|
|
126
|
+
return "pending";
|
|
127
|
+
case "connected":
|
|
128
|
+
case "failed":
|
|
129
|
+
case "expired":
|
|
130
|
+
return "terminal";
|
|
131
|
+
default:
|
|
132
|
+
return "invalid";
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
function instagramStatus(response) {
|
|
136
|
+
switch (response.data?.status) {
|
|
137
|
+
case "queued":
|
|
138
|
+
case "processing":
|
|
139
|
+
return "pending";
|
|
140
|
+
case "published":
|
|
141
|
+
case "failed":
|
|
142
|
+
case "outcome_unknown":
|
|
143
|
+
return "terminal";
|
|
144
|
+
default:
|
|
145
|
+
return "invalid";
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
function facebookStatus(response) {
|
|
149
|
+
switch (response.data?.status) {
|
|
150
|
+
case "PROCESSING":
|
|
151
|
+
return "pending";
|
|
152
|
+
case "READY":
|
|
153
|
+
case "ERROR":
|
|
154
|
+
return "terminal";
|
|
155
|
+
default:
|
|
156
|
+
return "invalid";
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
function youtubeStatus(response) {
|
|
160
|
+
switch (response.data?.status) {
|
|
161
|
+
case "processing":
|
|
162
|
+
return "pending";
|
|
163
|
+
case "published":
|
|
164
|
+
case "failed":
|
|
165
|
+
return "terminal";
|
|
166
|
+
default:
|
|
167
|
+
return "invalid";
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
function tiktokStatus(response) {
|
|
171
|
+
switch (response.data?.status) {
|
|
172
|
+
// TikTok may report upload/download processing before delivery to the inbox.
|
|
173
|
+
case "PROCESSING_UPLOAD":
|
|
174
|
+
case "PROCESSING_DOWNLOAD":
|
|
175
|
+
return "pending";
|
|
176
|
+
// Inbox delivery is terminal, but it is not public publication.
|
|
177
|
+
case "SEND_TO_USER_INBOX":
|
|
178
|
+
case "PUBLISH_COMPLETE":
|
|
179
|
+
case "FAILED":
|
|
180
|
+
return "terminal";
|
|
181
|
+
default:
|
|
182
|
+
return "invalid";
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
function connectionMethod(client) {
|
|
186
|
+
return "profiles" in client ? (args, options) => client.profiles.getConnection(args, options) : (args, options) => client.getConnection(args, options);
|
|
187
|
+
}
|
|
188
|
+
function instagramMethod(client) {
|
|
189
|
+
return "posts" in client ? (args, options) => client.posts.getInstagramPostStatus(args, options) : (args, options) => client.getInstagramPostStatus(args, options);
|
|
190
|
+
}
|
|
191
|
+
function tikTokMethod(client) {
|
|
192
|
+
return "posts" in client ? (args, options) => client.posts.getTikTokPostStatus(args, options) : (args, options) => client.getTikTokPostStatus(args, options);
|
|
193
|
+
}
|
|
194
|
+
function facebookMethod(client) {
|
|
195
|
+
return "posts" in client ? (args, options) => client.posts.getFacebookPostStatus(args, options) : (args, options) => client.getFacebookPostStatus(args, options);
|
|
196
|
+
}
|
|
197
|
+
function youtubeMethod(client) {
|
|
198
|
+
return "posts" in client ? (args, options) => client.posts.getYouTubeVideoStatus(args, options) : (args, options) => client.getYouTubeVideoStatus(args, options);
|
|
199
|
+
}
|
|
200
|
+
function waitForConnection(client, args, options) {
|
|
201
|
+
const getConnection = connectionMethod(client);
|
|
202
|
+
const snapshot = { path: { ...args.path } };
|
|
203
|
+
return poll((signal) => getConnection(snapshot, { signal }), connectionStatus, options);
|
|
204
|
+
}
|
|
205
|
+
function waitForInstagramPost(client, args, options) {
|
|
206
|
+
const getStatus = instagramMethod(client);
|
|
207
|
+
const snapshot = { query: { ...args.query } };
|
|
208
|
+
return poll((signal) => getStatus(snapshot, { signal }), instagramStatus, options);
|
|
209
|
+
}
|
|
210
|
+
function waitForTikTokPost(client, args, options) {
|
|
211
|
+
const getStatus = tikTokMethod(client);
|
|
212
|
+
const snapshot = { query: { ...args.query } };
|
|
213
|
+
return poll((signal) => getStatus(snapshot, { signal }), tiktokStatus, options);
|
|
214
|
+
}
|
|
215
|
+
function waitForFacebookPost(client, args, options) {
|
|
216
|
+
const getStatus = facebookMethod(client);
|
|
217
|
+
const snapshot = { query: { ...args.query } };
|
|
218
|
+
return poll((signal) => getStatus(snapshot, { signal }), facebookStatus, options);
|
|
219
|
+
}
|
|
220
|
+
function waitForYouTubeVideo(client, args, options) {
|
|
221
|
+
const getStatus = youtubeMethod(client);
|
|
222
|
+
const snapshot = { query: { ...args.query } };
|
|
223
|
+
return poll((signal) => getStatus(snapshot, { signal }), youtubeStatus, options);
|
|
224
|
+
}
|
|
225
|
+
var DEFAULT_MAX_PAGES = 100;
|
|
226
|
+
function isRecord(value) {
|
|
227
|
+
return typeof value === "object" && value !== null;
|
|
228
|
+
}
|
|
229
|
+
function validateOptions(options) {
|
|
230
|
+
const maxPages = options.maxPages ?? DEFAULT_MAX_PAGES;
|
|
231
|
+
if (!Number.isFinite(maxPages) || maxPages <= 0 || !Number.isInteger(maxPages)) {
|
|
232
|
+
throw new sdk.ConfigurationError("maxPages must be a finite positive integer");
|
|
233
|
+
}
|
|
234
|
+
if (options.timeoutMs !== void 0 && !isRepresentableTimerDuration(options.timeoutMs)) {
|
|
235
|
+
throw new sdk.ConfigurationError(`timeoutMs must be a positive finite number no greater than ${MAX_TIMER_DURATION_MS}ms`);
|
|
236
|
+
}
|
|
237
|
+
return { ...options, maxPages };
|
|
238
|
+
}
|
|
239
|
+
function abortError() {
|
|
240
|
+
return new sdk.CancellationError("The helper operation was cancelled");
|
|
241
|
+
}
|
|
242
|
+
function timeoutError() {
|
|
243
|
+
return new sdk.TimeoutError("The helper operation exceeded its total timeout");
|
|
244
|
+
}
|
|
245
|
+
function malformed(message) {
|
|
246
|
+
return new sdk.MalformedResponseError(message);
|
|
247
|
+
}
|
|
248
|
+
function assertPage(value) {
|
|
249
|
+
if (!isRecord(value) || !Array.isArray(value.comments)) {
|
|
250
|
+
throw malformed("The comments endpoint returned a malformed page");
|
|
251
|
+
}
|
|
252
|
+
if ("postId" in value && value.postId !== null && value.postId !== void 0 && typeof value.postId !== "string") {
|
|
253
|
+
throw malformed("The comments endpoint returned an invalid post identity");
|
|
254
|
+
}
|
|
255
|
+
if (value.nextCursor !== void 0 && value.nextCursor !== null && typeof value.nextCursor !== "string") {
|
|
256
|
+
throw malformed("The comments endpoint returned an invalid cursor");
|
|
257
|
+
}
|
|
258
|
+
for (const comment of value.comments) {
|
|
259
|
+
if (!isRecord(comment) || typeof comment.id !== "string" || comment.id.length === 0) {
|
|
260
|
+
throw malformed("The comments endpoint returned a malformed comment");
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
function assertScope(page, query) {
|
|
265
|
+
if (page.platform !== query.platform || page.accountId !== query.accountId) {
|
|
266
|
+
throw malformed("The comments endpoint changed the requested account scope");
|
|
267
|
+
}
|
|
268
|
+
if (query.profileId !== void 0 && page.profileId !== query.profileId) {
|
|
269
|
+
throw malformed("The comments endpoint changed the requested profile scope");
|
|
270
|
+
}
|
|
271
|
+
if (query.postId !== void 0 && page.postId !== query.postId) {
|
|
272
|
+
throw malformed("The comments endpoint changed the requested post scope");
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
function makeDeadline(timeoutMs) {
|
|
276
|
+
return timeoutMs === void 0 ? void 0 : Date.now() + timeoutMs;
|
|
277
|
+
}
|
|
278
|
+
async function requestPage(client, query, deadline, signal) {
|
|
279
|
+
if (signal?.aborted) throw abortError();
|
|
280
|
+
const remaining = deadline === void 0 ? void 0 : deadline - Date.now();
|
|
281
|
+
if (remaining !== void 0 && remaining <= 0) throw timeoutError();
|
|
282
|
+
const controller = new AbortController();
|
|
283
|
+
let timer;
|
|
284
|
+
let settled = false;
|
|
285
|
+
let onAbort;
|
|
286
|
+
return new Promise((resolve, reject) => {
|
|
287
|
+
const finish = (action) => {
|
|
288
|
+
if (settled) return;
|
|
289
|
+
settled = true;
|
|
290
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
291
|
+
if (onAbort !== void 0) signal?.removeEventListener("abort", onAbort);
|
|
292
|
+
action();
|
|
293
|
+
};
|
|
294
|
+
onAbort = () => {
|
|
295
|
+
controller.abort();
|
|
296
|
+
finish(() => reject(abortError()));
|
|
297
|
+
};
|
|
298
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
299
|
+
if (remaining !== void 0) {
|
|
300
|
+
timer = setTimeout(() => {
|
|
301
|
+
controller.abort();
|
|
302
|
+
finish(() => reject(timeoutError()));
|
|
303
|
+
}, remaining);
|
|
304
|
+
}
|
|
305
|
+
let result;
|
|
306
|
+
try {
|
|
307
|
+
result = Promise.resolve(client.comments.listComments({ query }, { signal: controller.signal }));
|
|
308
|
+
} catch (error) {
|
|
309
|
+
finish(() => reject(error));
|
|
310
|
+
return;
|
|
311
|
+
}
|
|
312
|
+
result.then(
|
|
313
|
+
(value) => finish(() => {
|
|
314
|
+
if (signal?.aborted) reject(abortError());
|
|
315
|
+
else if (deadline !== void 0 && Date.now() >= deadline) reject(timeoutError());
|
|
316
|
+
else resolve(value);
|
|
317
|
+
}),
|
|
318
|
+
(error) => finish(() => {
|
|
319
|
+
if (signal?.aborted) reject(abortError());
|
|
320
|
+
else if (deadline !== void 0 && Date.now() >= deadline) reject(timeoutError());
|
|
321
|
+
else reject(error);
|
|
322
|
+
})
|
|
323
|
+
);
|
|
324
|
+
});
|
|
325
|
+
}
|
|
326
|
+
function validatePlatform(query) {
|
|
327
|
+
if (query.platform !== "tiktok" && query.platform !== "youtube") {
|
|
328
|
+
throw new sdk.ConfigurationError("Comment iteration is supported only for TikTok and YouTube");
|
|
329
|
+
}
|
|
330
|
+
if (query.cursor !== void 0 && query.postId === void 0) {
|
|
331
|
+
throw malformed("A comment cursor requires a selected postId");
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
async function* pages(client, args, options) {
|
|
335
|
+
const checked = validateOptions(options);
|
|
336
|
+
validatePlatform(args.query);
|
|
337
|
+
const initialQuery = { ...args.query };
|
|
338
|
+
let query = { ...initialQuery };
|
|
339
|
+
const deadline = makeDeadline(checked.timeoutMs);
|
|
340
|
+
const seenCursors = /* @__PURE__ */ new Set();
|
|
341
|
+
if (query.cursor !== void 0) seenCursors.add(query.cursor);
|
|
342
|
+
let selectedPostId = query.postId;
|
|
343
|
+
try {
|
|
344
|
+
for (let pageNumber = 0; pageNumber < checked.maxPages; pageNumber += 1) {
|
|
345
|
+
if (checked.signal?.aborted) throw abortError();
|
|
346
|
+
const response = await requestPage(client, query, deadline, checked.signal);
|
|
347
|
+
assertPage(response.data);
|
|
348
|
+
assertScope(response.data, query);
|
|
349
|
+
if (selectedPostId === void 0) {
|
|
350
|
+
selectedPostId = response.data.postId ?? void 0;
|
|
351
|
+
if (selectedPostId !== void 0) query = { ...query, postId: selectedPostId };
|
|
352
|
+
} else if (response.data.postId !== selectedPostId) {
|
|
353
|
+
throw malformed("The comments endpoint changed the selected post");
|
|
354
|
+
}
|
|
355
|
+
const nextCursor = response.data.nextCursor;
|
|
356
|
+
if (selectedPostId === void 0 && nextCursor !== null && nextCursor !== void 0 && nextCursor !== "") {
|
|
357
|
+
throw malformed("A comment cursor was returned without an authoritative selected post");
|
|
358
|
+
}
|
|
359
|
+
yield response;
|
|
360
|
+
if (nextCursor === null || nextCursor === void 0 || nextCursor === "") return;
|
|
361
|
+
if (selectedPostId === void 0) {
|
|
362
|
+
throw malformed("A comment cursor was returned without an authoritative selected post");
|
|
363
|
+
}
|
|
364
|
+
if (seenCursors.has(nextCursor)) return;
|
|
365
|
+
seenCursors.add(nextCursor);
|
|
366
|
+
query = { ...query, cursor: nextCursor };
|
|
367
|
+
}
|
|
368
|
+
} finally {
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
function iterateCommentPages(client, args, options = {}) {
|
|
372
|
+
return pages(client, args, options);
|
|
373
|
+
}
|
|
374
|
+
async function* iterateComments(client, args, options = {}) {
|
|
375
|
+
for await (const page of pages(client, args, options)) {
|
|
376
|
+
for (const comment of page.data.comments) yield comment;
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
var DEFAULT_WEBHOOK_TOLERANCE_SECONDS = 300;
|
|
380
|
+
function verifyWebhookSignature(input) {
|
|
381
|
+
if (typeof input.secret !== "string" || input.secret.length === 0) throw new sdk.ConfigurationError("verifyWebhookSignature requires the webhook signing secret");
|
|
382
|
+
const tolerance = input.toleranceSeconds ?? DEFAULT_WEBHOOK_TOLERANCE_SECONDS;
|
|
383
|
+
if (!Number.isFinite(tolerance) || tolerance < 0) throw new sdk.ConfigurationError("toleranceSeconds must be a finite, non-negative number");
|
|
384
|
+
const parsed = parseSignatureHeader(input.header);
|
|
385
|
+
if (!parsed) return false;
|
|
386
|
+
const now = input.now ?? Math.floor(Date.now() / 1e3);
|
|
387
|
+
if (Math.abs(now - parsed.timestamp) > tolerance) return false;
|
|
388
|
+
const expected = crypto.createHmac("sha256", input.secret).update(`${parsed.timestamp}.`).update(input.payload).digest();
|
|
389
|
+
return crypto.timingSafeEqual(expected, Buffer.from(parsed.signature, "hex"));
|
|
390
|
+
}
|
|
391
|
+
function parseSignatureHeader(header) {
|
|
392
|
+
if (typeof header !== "string") return void 0;
|
|
393
|
+
const fields = /* @__PURE__ */ new Map();
|
|
394
|
+
for (const part of header.split(",")) {
|
|
395
|
+
const separator = part.indexOf("=");
|
|
396
|
+
if (separator <= 0) return void 0;
|
|
397
|
+
const key = part.slice(0, separator).trim();
|
|
398
|
+
if (fields.has(key)) return void 0;
|
|
399
|
+
fields.set(key, part.slice(separator + 1).trim());
|
|
400
|
+
}
|
|
401
|
+
const timestamp = fields.get("t");
|
|
402
|
+
const signature = fields.get("v1");
|
|
403
|
+
if (timestamp === void 0 || !/^\d{1,12}$/.test(timestamp)) return void 0;
|
|
404
|
+
if (signature === void 0 || !/^[0-9a-f]{64}$/.test(signature)) return void 0;
|
|
405
|
+
return { timestamp: Number(timestamp), signature };
|
|
406
|
+
}
|
|
407
|
+
var isRecord2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
408
|
+
var isNullableString = (value) => value === null || typeof value === "string";
|
|
409
|
+
function malformed2(detail) {
|
|
410
|
+
throw new sdk.MalformedResponseError(`Malformed webhook event: ${detail}`);
|
|
411
|
+
}
|
|
412
|
+
function requireString(record, key, where) {
|
|
413
|
+
const value = record[key];
|
|
414
|
+
if (typeof value !== "string") malformed2(`${where}.${key} must be a string`);
|
|
415
|
+
return value;
|
|
416
|
+
}
|
|
417
|
+
function requireNullableString(record, key, where) {
|
|
418
|
+
const value = record[key];
|
|
419
|
+
if (!isNullableString(value)) malformed2(`${where}.${key} must be a string or null`);
|
|
420
|
+
return value;
|
|
421
|
+
}
|
|
422
|
+
function parseWebhookEvent(payload) {
|
|
423
|
+
let text;
|
|
424
|
+
try {
|
|
425
|
+
text = typeof payload === "string" ? payload : new TextDecoder("utf-8", { fatal: true }).decode(payload);
|
|
426
|
+
} catch {
|
|
427
|
+
malformed2("body is not valid UTF-8");
|
|
428
|
+
}
|
|
429
|
+
let value;
|
|
430
|
+
try {
|
|
431
|
+
value = JSON.parse(text);
|
|
432
|
+
} catch {
|
|
433
|
+
malformed2("body is not valid JSON");
|
|
434
|
+
}
|
|
435
|
+
if (!isRecord2(value)) malformed2("body must be a JSON object");
|
|
436
|
+
const envelope = {
|
|
437
|
+
id: requireString(value, "id", "event"),
|
|
438
|
+
createdAt: requireString(value, "createdAt", "event"),
|
|
439
|
+
profileId: requireString(value, "profileId", "event"),
|
|
440
|
+
accountId: requireString(value, "accountId", "event")
|
|
441
|
+
};
|
|
442
|
+
const type = requireString(value, "type", "event");
|
|
443
|
+
const platform = requireString(value, "platform", "event");
|
|
444
|
+
const data = value.data;
|
|
445
|
+
if (platform === "instagram" && type === "comment.created") {
|
|
446
|
+
if (!isRecord2(data)) malformed2("data must be an object");
|
|
447
|
+
const from = data.from;
|
|
448
|
+
if (!isRecord2(from)) malformed2("data.from must be an object");
|
|
449
|
+
if (typeof data.isSelf !== "boolean") malformed2("data.isSelf must be a boolean");
|
|
450
|
+
return { ...envelope, type, platform, data: {
|
|
451
|
+
commentId: requireString(data, "commentId", "data"),
|
|
452
|
+
mediaId: requireString(data, "mediaId", "data"),
|
|
453
|
+
parentId: requireNullableString(data, "parentId", "data"),
|
|
454
|
+
text: requireString(data, "text", "data"),
|
|
455
|
+
from: { id: requireNullableString(from, "id", "data.from"), username: requireNullableString(from, "username", "data.from") },
|
|
456
|
+
isSelf: data.isSelf
|
|
457
|
+
} };
|
|
458
|
+
}
|
|
459
|
+
if (platform === "instagram" && type === "message.postback") {
|
|
460
|
+
if (!isRecord2(data)) malformed2("data must be an object");
|
|
461
|
+
return { ...envelope, type, platform, data: {
|
|
462
|
+
senderId: requireString(data, "senderId", "data"),
|
|
463
|
+
payload: requireString(data, "payload", "data"),
|
|
464
|
+
title: requireNullableString(data, "title", "data"),
|
|
465
|
+
messageId: requireNullableString(data, "messageId", "data")
|
|
466
|
+
} };
|
|
467
|
+
}
|
|
468
|
+
return { ...envelope, type: "unknown", eventType: type, platform, data };
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
exports.DEFAULT_WEBHOOK_TOLERANCE_SECONDS = DEFAULT_WEBHOOK_TOLERANCE_SECONDS;
|
|
472
|
+
exports.iterateCommentPages = iterateCommentPages;
|
|
473
|
+
exports.iterateComments = iterateComments;
|
|
474
|
+
exports.parseWebhookEvent = parseWebhookEvent;
|
|
475
|
+
exports.verifyWebhookSignature = verifyWebhookSignature;
|
|
476
|
+
exports.waitForConnection = waitForConnection;
|
|
477
|
+
exports.waitForFacebookPost = waitForFacebookPost;
|
|
478
|
+
exports.waitForInstagramPost = waitForInstagramPost;
|
|
479
|
+
exports.waitForTikTokPost = waitForTikTokPost;
|
|
480
|
+
exports.waitForYouTubeVideo = waitForYouTubeVideo;
|
|
481
|
+
//# sourceMappingURL=index.cjs.map
|
|
482
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/core/duration.ts","../../src/helpers/polling.ts","../../src/helpers/comments.ts","../../src/helpers/webhooks.ts"],"names":["CancellationError","TimeoutError","MalformedResponseError","ConfigurationError","createHmac","timingSafeEqual","isRecord","malformed"],"mappings":";;;;;;;;AACO,IAAM,qBAAA,GAAwB,UAAA;AAE9B,SAAS,6BAA6B,KAAA,EAAwB;AACnE,EAAA,OAAO,OAAO,QAAA,CAAS,KAAK,CAAA,IAAK,KAAA,GAAQ,KAAK,KAAA,IAAS,qBAAA;AACzD;;;ACuCA,SAAS,uBAAuB,OAAA,EAA+B;AAC7D,EAAA,IAAI,CAAC,4BAAA,CAA6B,OAAA,CAAQ,SAAS,CAAA,EAAG;AACpD,IAAA,MAAM,IAAI,UAAA,CAAW,CAAA,2DAAA,EAA8D,qBAAqB,CAAA,EAAA,CAAI,CAAA;AAAA,EAC9G;AACA,EAAA,IAAI,CAAC,4BAAA,CAA6B,OAAA,CAAQ,UAAU,CAAA,EAAG;AACrD,IAAA,MAAM,IAAI,UAAA,CAAW,CAAA,4DAAA,EAA+D,qBAAqB,CAAA,EAAA,CAAI,CAAA;AAAA,EAC/G;AACF;AAEA,SAAS,YAAA,GAAkC;AACzC,EAAA,OAAO,IAAIA,sBAAkB,oCAAoC,CAAA;AACnE;AAEA,SAAS,OAAA,GAAwB;AAC/B,EAAA,OAAO,IAAIC,iBAAa,iDAAiD,CAAA;AAC3E;AAEA,SAAS,cAAc,OAAA,EAAyC;AAC9D,EAAA,OAAO,IAAIC,2BAAuB,OAAO,CAAA;AAC3C;AAEA,SAAS,iBAAiB,MAAA,EAAuC;AAC/D,EAAA,IAAI,MAAA,EAAQ,OAAA,EAAS,MAAM,YAAA,EAAa;AAC1C;AAOA,eAAe,mBAAA,CACb,OAAA,EACA,QAAA,EACA,YAAA,EACyB;AACzB,EAAA,gBAAA,CAAiB,YAAY,CAAA;AAC7B,EAAA,MAAM,SAAA,GAAY,QAAA,CAAS,EAAA,GAAK,IAAA,CAAK,GAAA,EAAI;AACzC,EAAA,IAAI,SAAA,IAAa,CAAA,EAAG,MAAM,OAAA,EAAQ;AAElC,EAAA,MAAM,UAAA,GAAa,IAAI,eAAA,EAAgB;AACvC,EAAA,IAAI,KAAA;AACJ,EAAA,IAAI,OAAA,GAAU,KAAA;AACd,EAAA,IAAI,OAAA;AACJ,EAAA,MAAM,OAAA,GAAU,IAAI,OAAA,CAAwB,CAAC,SAAS,MAAA,KAAW;AAC/D,IAAA,MAAM,MAAA,GAAS,CAAC,MAAA,KAA6B;AAC3C,MAAA,IAAI,OAAA,EAAS;AACb,MAAA,OAAA,GAAU,IAAA;AACV,MAAA,IAAI,KAAA,KAAU,MAAA,EAAW,YAAA,CAAa,KAAK,CAAA;AAC3C,MAAA,IAAI,OAAA,KAAY,MAAA,EAAW,YAAA,EAAc,mBAAA,CAAoB,SAAS,OAAO,CAAA;AAC7E,MAAA,MAAA,EAAO;AAAA,IACT,CAAA;AACA,IAAA,OAAA,GAAU,MAAM;AACd,MAAA,UAAA,CAAW,KAAA,EAAM;AACjB,MAAA,MAAA,CAAO,MAAM,MAAA,CAAO,YAAA,EAAc,CAAC,CAAA;AAAA,IACrC,CAAA;AACA,IAAA,YAAA,EAAc,iBAAiB,OAAA,EAAS,OAAA,EAAS,EAAE,IAAA,EAAM,MAAM,CAAA;AAC/D,IAAA,KAAA,GAAQ,WAAW,MAAM;AACvB,MAAA,UAAA,CAAW,KAAA,EAAM;AACjB,MAAA,MAAA,CAAO,MAAM,MAAA,CAAO,OAAA,EAAS,CAAC,CAAA;AAAA,IAChC,GAAG,SAAS,CAAA;AAEZ,IAAA,IAAI,MAAA;AACJ,IAAA,IAAI;AACF,MAAA,MAAA,GAAS,OAAA,CAAQ,OAAA,CAAQ,OAAA,CAAQ,UAAA,CAAW,MAAM,CAAC,CAAA;AAAA,IACrD,SAAS,KAAA,EAAO;AACd,MAAA,MAAA,CAAO,MAAM,MAAA,CAAO,KAAK,CAAC,CAAA;AAC1B,MAAA;AAAA,IACF;AACA,IAAA,MAAA,CAAO,IAAA;AAAA,MACL,CAAA,KAAA,KAAS,OAAO,MAAM;AACpB,QAAA,IAAI,YAAA,EAAc,OAAA,EAAS,MAAA,CAAO,YAAA,EAAc,CAAA;AAAA,aAAA,IACvC,KAAK,GAAA,EAAI,IAAK,SAAS,EAAA,EAAI,MAAA,CAAO,SAAS,CAAA;AAAA,qBACvC,KAAK,CAAA;AAAA,MACpB,CAAC,CAAA;AAAA,MACD,CAAA,KAAA,KAAS,OAAO,MAAM;AACpB,QAAA,IAAI,YAAA,EAAc,OAAA,EAAS,MAAA,CAAO,YAAA,EAAc,CAAA;AAAA,aAAA,IACvC,KAAK,GAAA,EAAI,IAAK,SAAS,EAAA,EAAI,MAAA,CAAO,SAAS,CAAA;AAAA,oBACxC,KAAK,CAAA;AAAA,MACnB,CAAC;AAAA,KACH;AAAA,EACF,CAAC,CAAA;AACD,EAAA,OAAO,OAAA;AACT;AAEA,eAAe,UAAA,CAAW,QAAA,EAAoB,UAAA,EAAoB,MAAA,EAAgD;AAChH,EAAA,gBAAA,CAAiB,MAAM,CAAA;AACvB,EAAA,MAAM,SAAA,GAAY,QAAA,CAAS,EAAA,GAAK,IAAA,CAAK,GAAA,EAAI;AACzC,EAAA,IAAI,SAAA,IAAa,CAAA,EAAG,MAAM,OAAA,EAAQ;AAClC,EAAA,MAAM,IAAI,OAAA,CAAc,CAAC,OAAA,EAAS,MAAA,KAAW;AAC3C,IAAA,IAAI,OAAA,GAAU,KAAA;AACd,IAAA,MAAM,KAAA,GAAQ,IAAA,CAAK,GAAA,CAAI,UAAA,EAAY,SAAS,CAAA;AAC5C,IAAA,IAAI,KAAA,GAAmD,WAAW,MAAM;AACtE,MAAA,MAAA,CAAO,MAAM;AACX,QAAA,IAAI,QAAA,CAAS,KAAK,IAAA,CAAK,GAAA,MAAS,CAAA,EAAG,MAAA,CAAO,SAAS,CAAA;AAAA,aAC9C,OAAA,EAAQ;AAAA,MACf,CAAC,CAAA;AAAA,IACH,GAAG,KAAK,CAAA;AACR,IAAA,MAAM,UAAU,MAAY,MAAA,CAAO,MAAM,MAAA,CAAO,YAAA,EAAc,CAAC,CAAA;AAC/D,IAAA,MAAM,MAAA,GAAS,CAAC,MAAA,KAA6B;AAC3C,MAAA,IAAI,OAAA,EAAS;AACb,MAAA,OAAA,GAAU,IAAA;AACV,MAAA,IAAI,KAAA,KAAU,MAAA,EAAW,YAAA,CAAa,KAAK,CAAA;AAC3C,MAAA,MAAA,EAAQ,mBAAA,CAAoB,SAAS,OAAO,CAAA;AAC5C,MAAA,MAAA,EAAO;AAAA,IACT,CAAA;AACA,IAAA,MAAA,EAAQ,iBAAiB,OAAA,EAAS,OAAA,EAAS,EAAE,IAAA,EAAM,MAAM,CAAA;AAAA,EAC3D,CAAC,CAAA;AACH;AAEA,eAAe,IAAA,CACb,WAAA,EACA,MAAA,EACA,OAAA,EACyB;AACzB,EAAA,sBAAA,CAAuB,OAAO,CAAA;AAC9B,EAAA,gBAAA,CAAiB,QAAQ,MAAM,CAAA;AAC/B,EAAA,MAAM,WAAW,EAAE,EAAA,EAAI,KAAK,GAAA,EAAI,GAAI,QAAQ,SAAA,EAAU;AACtD,EAAA,OAAO,IAAA,EAAM;AACX,IAAA,MAAM,WAAW,MAAM,mBAAA,CAAoB,WAAA,EAAa,QAAA,EAAU,QAAQ,MAAM,CAAA;AAChF,IAAA,gBAAA,CAAiB,QAAQ,MAAM,CAAA;AAC/B,IAAA,IAAI,KAAK,GAAA,EAAI,IAAK,QAAA,CAAS,EAAA,QAAU,OAAA,EAAQ;AAC7C,IAAA,MAAM,KAAA,GAAQ,OAAO,QAAQ,CAAA;AAC7B,IAAA,IAAI,KAAA,KAAU,YAAY,OAAO,QAAA;AACjC,IAAA,IAAI,KAAA,KAAU,SAAA,EAAW,MAAM,aAAA,CAAc,iDAAiD,CAAA;AAC9F,IAAA,MAAM,UAAA,CAAW,QAAA,EAAU,OAAA,CAAQ,UAAA,EAAY,QAAQ,MAAM,CAAA;AAAA,EAC/D;AACF;AAEA,SAAS,iBAAiB,QAAA,EAA8E;AACtG,EAAA,MAAM,KAAA,GAAQ,SAAS,IAAA,EAAM,MAAA;AAC7B,EAAA,QAAQ,KAAA;AAAO,IACb,KAAK,uBAAA;AAAA,IACL,KAAK,YAAA;AAAA,IACL,KAAK,wBAAA;AACH,MAAA,OAAO,SAAA;AAAA,IACT,KAAK,WAAA;AAAA,IACL,KAAK,QAAA;AAAA,IACL,KAAK,SAAA;AACH,MAAA,OAAO,UAAA;AAAA,IACT;AACE,MAAA,OAAO,SAAA;AAAA;AAEb;AAEA,SAAS,gBAAgB,QAAA,EAAwF;AAC/G,EAAA,QAAQ,QAAA,CAAS,MAAM,MAAA;AAAQ,IAC7B,KAAK,QAAA;AAAA,IACL,KAAK,YAAA;AAAc,MAAA,OAAO,SAAA;AAAA,IAC1B,KAAK,WAAA;AAAA,IACL,KAAK,QAAA;AAAA,IACL,KAAK,iBAAA;AAAmB,MAAA,OAAO,UAAA;AAAA,IAC/B;AAAS,MAAA,OAAO,SAAA;AAAA;AAEpB;AAEA,SAAS,eAAe,QAAA,EAAuF;AAC7G,EAAA,QAAQ,QAAA,CAAS,MAAM,MAAA;AAAQ,IAC7B,KAAK,YAAA;AAAc,MAAA,OAAO,SAAA;AAAA,IAC1B,KAAK,OAAA;AAAA,IACL,KAAK,OAAA;AAAS,MAAA,OAAO,UAAA;AAAA,IACrB;AAAS,MAAA,OAAO,SAAA;AAAA;AAEpB;AAEA,SAAS,cAAc,QAAA,EAAuF;AAC5G,EAAA,QAAQ,QAAA,CAAS,MAAM,MAAA;AAAQ,IAC7B,KAAK,YAAA;AAAc,MAAA,OAAO,SAAA;AAAA,IAC1B,KAAK,WAAA;AAAA,IACL,KAAK,QAAA;AAAU,MAAA,OAAO,UAAA;AAAA,IACtB;AAAS,MAAA,OAAO,SAAA;AAAA;AAEpB;AAEA,SAAS,aAAa,QAAA,EAAqF;AACzG,EAAA,QAAQ,QAAA,CAAS,MAAM,MAAA;AAAQ;AAAA,IAE7B,KAAK,mBAAA;AAAA,IACL,KAAK,qBAAA;AAAuB,MAAA,OAAO,SAAA;AAAA;AAAA,IAEnC,KAAK,oBAAA;AAAA,IACL,KAAK,kBAAA;AAAA,IACL,KAAK,QAAA;AAAU,MAAA,OAAO,UAAA;AAAA,IACtB;AAAS,MAAA,OAAO,SAAA;AAAA;AAEpB;AAEA,SAAS,iBAAiB,MAAA,EAAmD;AAC3E,EAAA,OAAO,cAAc,MAAA,GACjB,CAAC,IAAA,EAAM,OAAA,KAAY,OAAO,QAAA,CAAS,aAAA,CAAc,IAAA,EAAM,OAAO,IAC9D,CAAC,IAAA,EAAM,YAAY,MAAA,CAAO,aAAA,CAAc,MAAM,OAAO,CAAA;AAC3D;AACA,SAAS,gBAAgB,MAAA,EAAiD;AACxE,EAAA,OAAO,WAAW,MAAA,GACd,CAAC,IAAA,EAAM,OAAA,KAAY,OAAO,KAAA,CAAM,sBAAA,CAAuB,IAAA,EAAM,OAAO,IACpE,CAAC,IAAA,EAAM,YAAY,MAAA,CAAO,sBAAA,CAAuB,MAAM,OAAO,CAAA;AACpE;AACA,SAAS,aAAa,MAAA,EAA2C;AAC/D,EAAA,OAAO,WAAW,MAAA,GACd,CAAC,IAAA,EAAM,OAAA,KAAY,OAAO,KAAA,CAAM,mBAAA,CAAoB,IAAA,EAAM,OAAO,IACjE,CAAC,IAAA,EAAM,YAAY,MAAA,CAAO,mBAAA,CAAoB,MAAM,OAAO,CAAA;AACjE;AACA,SAAS,eAAe,MAAA,EAA+C;AACrE,EAAA,OAAO,WAAW,MAAA,GACd,CAAC,IAAA,EAAM,OAAA,KAAY,OAAO,KAAA,CAAM,qBAAA,CAAsB,IAAA,EAAM,OAAO,IACnE,CAAC,IAAA,EAAM,YAAY,MAAA,CAAO,qBAAA,CAAsB,MAAM,OAAO,CAAA;AACnE;AACA,SAAS,cAAc,MAAA,EAA6C;AAClE,EAAA,OAAO,WAAW,MAAA,GACd,CAAC,IAAA,EAAM,OAAA,KAAY,OAAO,KAAA,CAAM,qBAAA,CAAsB,IAAA,EAAM,OAAO,IACnE,CAAC,IAAA,EAAM,YAAY,MAAA,CAAO,qBAAA,CAAsB,MAAM,OAAO,CAAA;AACnE;AAGO,SAAS,iBAAA,CACd,MAAA,EACA,IAAA,EACA,OAAA,EACyC;AACzC,EAAA,MAAM,aAAA,GAAgB,iBAAiB,MAAM,CAAA;AAC7C,EAAA,MAAM,WAAW,EAAE,IAAA,EAAM,EAAE,GAAG,IAAA,CAAK,MAAK,EAAE;AAC1C,EAAA,OAAO,IAAA,CAAK,YAAU,aAAA,CAAc,QAAA,EAAU,EAAE,MAAA,EAAQ,CAAA,EAAG,gBAAA,EAAkB,OAAO,CAAA;AACtF;AAGO,SAAS,oBAAA,CACd,MAAA,EACA,IAAA,EACA,OAAA,EACmD;AACnD,EAAA,MAAM,SAAA,GAAY,gBAAgB,MAAM,CAAA;AACxC,EAAA,MAAM,WAAW,EAAE,KAAA,EAAO,EAAE,GAAG,IAAA,CAAK,OAAM,EAAE;AAC5C,EAAA,OAAO,IAAA,CAAK,YAAU,SAAA,CAAU,QAAA,EAAU,EAAE,MAAA,EAAQ,CAAA,EAAG,eAAA,EAAiB,OAAO,CAAA;AACjF;AAGO,SAAS,iBAAA,CACd,MAAA,EACA,IAAA,EACA,OAAA,EACgD;AAChD,EAAA,MAAM,SAAA,GAAY,aAAa,MAAM,CAAA;AACrC,EAAA,MAAM,WAAW,EAAE,KAAA,EAAO,EAAE,GAAG,IAAA,CAAK,OAAM,EAAE;AAC5C,EAAA,OAAO,IAAA,CAAK,YAAU,SAAA,CAAU,QAAA,EAAU,EAAE,MAAA,EAAQ,CAAA,EAAG,YAAA,EAAc,OAAO,CAAA;AAC9E;AAGO,SAAS,mBAAA,CACd,MAAA,EACA,IAAA,EACA,OAAA,EACkD;AAClD,EAAA,MAAM,SAAA,GAAY,eAAe,MAAM,CAAA;AACvC,EAAA,MAAM,WAAW,EAAE,KAAA,EAAO,EAAE,GAAG,IAAA,CAAK,OAAM,EAAE;AAC5C,EAAA,OAAO,IAAA,CAAK,YAAU,SAAA,CAAU,QAAA,EAAU,EAAE,MAAA,EAAQ,CAAA,EAAG,cAAA,EAAgB,OAAO,CAAA;AAChF;AAGO,SAAS,mBAAA,CACd,MAAA,EACA,IAAA,EACA,OAAA,EACkD;AAClD,EAAA,MAAM,SAAA,GAAY,cAAc,MAAM,CAAA;AACtC,EAAA,MAAM,WAAW,EAAE,KAAA,EAAO,EAAE,GAAG,IAAA,CAAK,OAAM,EAAE;AAC5C,EAAA,OAAO,IAAA,CAAK,YAAU,SAAA,CAAU,QAAA,EAAU,EAAE,MAAA,EAAQ,CAAA,EAAG,aAAA,EAAe,OAAO,CAAA;AAC/E;AChSA,IAAM,iBAAA,GAAoB,GAAA;AAE1B,SAAS,SAAS,KAAA,EAAkD;AAClE,EAAA,OAAO,OAAO,KAAA,KAAU,QAAA,IAAY,KAAA,KAAU,IAAA;AAChD;AAEA,SAAS,gBAAgB,OAAA,EAA8G;AACrI,EAAA,MAAM,QAAA,GAAW,QAAQ,QAAA,IAAY,iBAAA;AACrC,EAAA,IAAI,CAAC,MAAA,CAAO,QAAA,CAAS,QAAQ,CAAA,IAAK,QAAA,IAAY,CAAA,IAAK,CAAC,MAAA,CAAO,SAAA,CAAU,QAAQ,CAAA,EAAG;AAC9E,IAAA,MAAM,IAAIC,uBAAmB,4CAA4C,CAAA;AAAA,EAC3E;AACA,EAAA,IAAI,QAAQ,SAAA,KAAc,MAAA,IAAa,CAAC,4BAAA,CAA6B,OAAA,CAAQ,SAAS,CAAA,EAAG;AACvF,IAAA,MAAM,IAAIA,sBAAA,CAAmB,CAAA,2DAAA,EAA8D,qBAAqB,CAAA,EAAA,CAAI,CAAA;AAAA,EACtH;AACA,EAAA,OAAO,EAAE,GAAG,OAAA,EAAS,QAAA,EAAS;AAChC;AAEA,SAAS,UAAA,GAAgC;AACvC,EAAA,OAAO,IAAIH,sBAAkB,oCAAoC,CAAA;AACnE;AAEA,SAAS,YAAA,GAA6B;AACpC,EAAA,OAAO,IAAIC,iBAAa,iDAAiD,CAAA;AAC3E;AAEA,SAAS,UAAU,OAAA,EAAyC;AAC1D,EAAA,OAAO,IAAIC,2BAAuB,OAAO,CAAA;AAC3C;AAEA,SAAS,WAAW,KAAA,EAAmD;AACrE,EAAA,IAAI,CAAC,SAAS,KAAK,CAAA,IAAK,CAAC,KAAA,CAAM,OAAA,CAAQ,KAAA,CAAM,QAAQ,CAAA,EAAG;AACtD,IAAA,MAAM,UAAU,iDAAiD,CAAA;AAAA,EACnE;AACA,EAAA,IAAI,QAAA,IAAY,KAAA,IAAS,KAAA,CAAM,MAAA,KAAW,IAAA,IAAQ,KAAA,CAAM,MAAA,KAAW,MAAA,IAAa,OAAO,KAAA,CAAM,MAAA,KAAW,QAAA,EAAU;AAChH,IAAA,MAAM,UAAU,yDAAyD,CAAA;AAAA,EAC3E;AACA,EAAA,IAAI,KAAA,CAAM,eAAe,MAAA,IAAa,KAAA,CAAM,eAAe,IAAA,IAAQ,OAAO,KAAA,CAAM,UAAA,KAAe,QAAA,EAAU;AACvG,IAAA,MAAM,UAAU,kDAAkD,CAAA;AAAA,EACpE;AACA,EAAA,KAAA,MAAW,OAAA,IAAW,MAAM,QAAA,EAAU;AACpC,IAAA,IAAI,CAAC,QAAA,CAAS,OAAO,CAAA,IAAK,OAAO,OAAA,CAAQ,EAAA,KAAO,QAAA,IAAY,OAAA,CAAQ,EAAA,CAAG,MAAA,KAAW,CAAA,EAAG;AACnF,MAAA,MAAM,UAAU,oDAAoD,CAAA;AAAA,IACtE;AAAA,EACF;AACF;AAEA,SAAS,WAAA,CAAY,MAAwB,KAAA,EAA4B;AACvE,EAAA,IAAI,KAAK,QAAA,KAAa,KAAA,CAAM,YAAY,IAAA,CAAK,SAAA,KAAc,MAAM,SAAA,EAAW;AAC1E,IAAA,MAAM,UAAU,2DAA2D,CAAA;AAAA,EAC7E;AACA,EAAA,IAAI,MAAM,SAAA,KAAc,MAAA,IAAa,IAAA,CAAK,SAAA,KAAc,MAAM,SAAA,EAAW;AACvE,IAAA,MAAM,UAAU,2DAA2D,CAAA;AAAA,EAC7E;AACA,EAAA,IAAI,MAAM,MAAA,KAAW,MAAA,IAAa,IAAA,CAAK,MAAA,KAAW,MAAM,MAAA,EAAQ;AAC9D,IAAA,MAAM,UAAU,wDAAwD,CAAA;AAAA,EAC1E;AACF;AAEA,SAAS,aAAa,SAAA,EAAmD;AACvE,EAAA,OAAO,SAAA,KAAc,MAAA,GAAY,MAAA,GAAY,IAAA,CAAK,KAAI,GAAI,SAAA;AAC5D;AAEA,eAAe,WAAA,CACb,MAAA,EACA,KAAA,EACA,QAAA,EACA,MAAA,EACwC;AACxC,EAAA,IAAI,MAAA,EAAQ,OAAA,EAAS,MAAM,UAAA,EAAW;AACtC,EAAA,MAAM,YAAY,QAAA,KAAa,MAAA,GAAY,MAAA,GAAY,QAAA,GAAW,KAAK,GAAA,EAAI;AAC3E,EAAA,IAAI,SAAA,KAAc,MAAA,IAAa,SAAA,IAAa,CAAA,QAAS,YAAA,EAAa;AAElE,EAAA,MAAM,UAAA,GAAa,IAAI,eAAA,EAAgB;AACvC,EAAA,IAAI,KAAA;AACJ,EAAA,IAAI,OAAA,GAAU,KAAA;AACd,EAAA,IAAI,OAAA;AACJ,EAAA,OAAO,IAAI,OAAA,CAAuC,CAAC,OAAA,EAAS,MAAA,KAAW;AACrE,IAAA,MAAM,MAAA,GAAS,CAAC,MAAA,KAA6B;AAC3C,MAAA,IAAI,OAAA,EAAS;AACb,MAAA,OAAA,GAAU,IAAA;AACV,MAAA,IAAI,KAAA,KAAU,MAAA,EAAW,YAAA,CAAa,KAAK,CAAA;AAC3C,MAAA,IAAI,OAAA,KAAY,MAAA,EAAW,MAAA,EAAQ,mBAAA,CAAoB,SAAS,OAAO,CAAA;AACvE,MAAA,MAAA,EAAO;AAAA,IACT,CAAA;AACA,IAAA,OAAA,GAAU,MAAM;AACd,MAAA,UAAA,CAAW,KAAA,EAAM;AACjB,MAAA,MAAA,CAAO,MAAM,MAAA,CAAO,UAAA,EAAY,CAAC,CAAA;AAAA,IACnC,CAAA;AACA,IAAA,MAAA,EAAQ,iBAAiB,OAAA,EAAS,OAAA,EAAS,EAAE,IAAA,EAAM,MAAM,CAAA;AACzD,IAAA,IAAI,cAAc,MAAA,EAAW;AAC3B,MAAA,KAAA,GAAQ,WAAW,MAAM;AACvB,QAAA,UAAA,CAAW,KAAA,EAAM;AACjB,QAAA,MAAA,CAAO,MAAM,MAAA,CAAO,YAAA,EAAc,CAAC,CAAA;AAAA,MACrC,GAAG,SAAS,CAAA;AAAA,IACd;AACA,IAAA,IAAI,MAAA;AACJ,IAAA,IAAI;AACF,MAAA,MAAA,GAAS,OAAA,CAAQ,OAAA,CAAQ,MAAA,CAAO,QAAA,CAAS,YAAA,CAAa,EAAE,KAAA,EAAM,EAAG,EAAE,MAAA,EAAQ,UAAA,CAAW,MAAA,EAAQ,CAAC,CAAA;AAAA,IACjG,SAAS,KAAA,EAAO;AACd,MAAA,MAAA,CAAO,MAAM,MAAA,CAAO,KAAK,CAAC,CAAA;AAC1B,MAAA;AAAA,IACF;AACA,IAAA,MAAA,CAAO,IAAA;AAAA,MACL,CAAA,KAAA,KAAS,OAAO,MAAM;AACpB,QAAA,IAAI,MAAA,EAAQ,OAAA,EAAS,MAAA,CAAO,UAAA,EAAY,CAAA;AAAA,aAAA,IAC/B,QAAA,KAAa,UAAa,IAAA,CAAK,GAAA,MAAS,QAAA,EAAU,MAAA,CAAO,cAAc,CAAA;AAAA,qBACnE,KAAK,CAAA;AAAA,MACpB,CAAC,CAAA;AAAA,MACD,CAAA,KAAA,KAAS,OAAO,MAAM;AACpB,QAAA,IAAI,MAAA,EAAQ,OAAA,EAAS,MAAA,CAAO,UAAA,EAAY,CAAA;AAAA,aAAA,IAC/B,QAAA,KAAa,UAAa,IAAA,CAAK,GAAA,MAAS,QAAA,EAAU,MAAA,CAAO,cAAc,CAAA;AAAA,oBACpE,KAAK,CAAA;AAAA,MACnB,CAAC;AAAA,KACH;AAAA,EACF,CAAC,CAAA;AACH;AAEA,SAAS,iBAAiB,KAAA,EAAmC;AAC3D,EAAA,IAAI,KAAA,CAAM,QAAA,KAAa,QAAA,IAAY,KAAA,CAAM,aAAa,SAAA,EAAW;AAC/D,IAAA,MAAM,IAAIC,uBAAmB,4DAA4D,CAAA;AAAA,EAC3F;AACA,EAAA,IAAI,KAAA,CAAM,MAAA,KAAW,MAAA,IAAa,KAAA,CAAM,WAAW,MAAA,EAAW;AAC5D,IAAA,MAAM,UAAU,6CAA6C,CAAA;AAAA,EAC/D;AACF;AAEA,gBAAgB,KAAA,CACd,MAAA,EACA,IAAA,EACA,OAAA,EAC+C;AAC/C,EAAA,MAAM,OAAA,GAAU,gBAAgB,OAAO,CAAA;AACvC,EAAA,gBAAA,CAAiB,KAAK,KAAK,CAAA;AAC3B,EAAA,MAAM,YAAA,GAAe,EAAE,GAAG,IAAA,CAAK,KAAA,EAAM;AACrC,EAAA,IAAI,KAAA,GAAuB,EAAE,GAAG,YAAA,EAAa;AAC7C,EAAA,MAAM,QAAA,GAAW,YAAA,CAAa,OAAA,CAAQ,SAAS,CAAA;AAC/C,EAAA,MAAM,WAAA,uBAAkB,GAAA,EAAY;AACpC,EAAA,IAAI,MAAM,MAAA,KAAW,MAAA,EAAW,WAAA,CAAY,GAAA,CAAI,MAAM,MAAM,CAAA;AAC5D,EAAA,IAAI,iBAAiB,KAAA,CAAM,MAAA;AAE3B,EAAA,IAAI;AACF,IAAA,KAAA,IAAS,aAAa,CAAA,EAAG,UAAA,GAAa,OAAA,CAAQ,QAAA,EAAU,cAAc,CAAA,EAAG;AACvE,MAAA,IAAI,OAAA,CAAQ,MAAA,EAAQ,OAAA,EAAS,MAAM,UAAA,EAAW;AAC9C,MAAA,MAAM,WAAW,MAAM,WAAA,CAAY,QAAQ,KAAA,EAAO,QAAA,EAAU,QAAQ,MAAM,CAAA;AAC1E,MAAA,UAAA,CAAW,SAAS,IAAI,CAAA;AACxB,MAAA,WAAA,CAAY,QAAA,CAAS,MAAM,KAAK,CAAA;AAEhC,MAAA,IAAI,mBAAmB,KAAA,CAAA,EAAW;AAEhC,QAAA,cAAA,GAAiB,QAAA,CAAS,KAAK,MAAA,IAAU,KAAA,CAAA;AACzC,QAAA,IAAI,mBAAmB,KAAA,CAAA,EAAW,KAAA,GAAQ,EAAE,GAAG,KAAA,EAAO,QAAQ,cAAA,EAAe;AAAA,MAC/E,CAAA,MAAA,IAAW,QAAA,CAAS,IAAA,CAAK,MAAA,KAAW,cAAA,EAAgB;AAClD,QAAA,MAAM,UAAU,iDAAiD,CAAA;AAAA,MACnE;AAEA,MAAA,MAAM,UAAA,GAAa,SAAS,IAAA,CAAK,UAAA;AACjC,MAAA,IAAI,mBAAmB,KAAA,CAAA,IAAa,UAAA,KAAe,QAAQ,UAAA,KAAe,KAAA,CAAA,IAAa,eAAe,EAAA,EAAI;AACxG,QAAA,MAAM,UAAU,sEAAsE,CAAA;AAAA,MACxF;AACA,MAAA,MAAM,QAAA;AACN,MAAA,IAAI,UAAA,KAAe,IAAA,IAAQ,UAAA,KAAe,KAAA,CAAA,IAAa,eAAe,EAAA,EAAI;AAC1E,MAAA,IAAI,mBAAmB,KAAA,CAAA,EAAW;AAChC,QAAA,MAAM,UAAU,sEAAsE,CAAA;AAAA,MACxF;AACA,MAAA,IAAI,WAAA,CAAY,GAAA,CAAI,UAAU,CAAA,EAAG;AACjC,MAAA,WAAA,CAAY,IAAI,UAAU,CAAA;AAC1B,MAAA,KAAA,GAAQ,EAAE,GAAG,KAAA,EAAO,MAAA,EAAQ,UAAA,EAAW;AAAA,IACzC;AAAA,EACF,CAAA,SAAE;AAAA,EAGF;AACF;AAGO,SAAS,mBAAA,CACd,MAAA,EACA,IAAA,EACA,OAAA,GAAkC,EAAC,EACY;AAC/C,EAAA,OAAO,KAAA,CAAM,MAAA,EAAQ,IAAA,EAAM,OAAO,CAAA;AACpC;AAGA,gBAAuB,eAAA,CACrB,MAAA,EACA,IAAA,EACA,OAAA,GAAkC,EAAC,EACmB;AACtD,EAAA,WAAA,MAAiB,IAAA,IAAQ,KAAA,CAAM,MAAA,EAAQ,IAAA,EAAM,OAAO,CAAA,EAAG;AACrD,IAAA,KAAA,MAAW,OAAA,IAAW,IAAA,CAAK,IAAA,CAAK,QAAA,EAAU,MAAM,OAAA;AAAA,EAClD;AACF;AClMO,IAAM,iCAAA,GAAoC;AAQ1C,SAAS,uBAAuB,KAAA,EAA6C;AAClF,EAAA,IAAI,OAAO,KAAA,CAAM,MAAA,KAAW,QAAA,IAAY,KAAA,CAAM,MAAA,CAAO,MAAA,KAAW,CAAA,EAAG,MAAM,IAAIA,sBAAAA,CAAmB,4DAA4D,CAAA;AAC5J,EAAA,MAAM,SAAA,GAAY,MAAM,gBAAA,IAAoB,iCAAA;AAC5C,EAAA,IAAI,CAAC,MAAA,CAAO,QAAA,CAAS,SAAS,CAAA,IAAK,YAAY,CAAA,EAAG,MAAM,IAAIA,sBAAAA,CAAmB,wDAAwD,CAAA;AACvI,EAAA,MAAM,MAAA,GAAS,oBAAA,CAAqB,KAAA,CAAM,MAAM,CAAA;AAChD,EAAA,IAAI,CAAC,QAAQ,OAAO,KAAA;AACpB,EAAA,MAAM,GAAA,GAAM,MAAM,GAAA,IAAO,IAAA,CAAK,MAAM,IAAA,CAAK,GAAA,KAAQ,GAAI,CAAA;AACrD,EAAA,IAAI,KAAK,GAAA,CAAI,GAAA,GAAM,OAAO,SAAS,CAAA,GAAI,WAAW,OAAO,KAAA;AACzD,EAAA,MAAM,WAAWC,iBAAA,CAAW,QAAA,EAAU,KAAA,CAAM,MAAM,EAAE,MAAA,CAAO,CAAA,EAAG,MAAA,CAAO,SAAS,GAAG,CAAA,CAAE,MAAA,CAAO,KAAA,CAAM,OAAO,EAAE,MAAA,EAAO;AAChH,EAAA,OAAOC,uBAAgB,QAAA,EAAU,MAAA,CAAO,KAAK,MAAA,CAAO,SAAA,EAAW,KAAK,CAAC,CAAA;AACvE;AAEA,SAAS,qBAAqB,MAAA,EAAyF;AACrH,EAAA,IAAI,OAAO,MAAA,KAAW,QAAA,EAAU,OAAO,MAAA;AACvC,EAAA,MAAM,MAAA,uBAAa,GAAA,EAAoB;AACvC,EAAA,KAAA,MAAW,IAAA,IAAQ,MAAA,CAAO,KAAA,CAAM,GAAG,CAAA,EAAG;AACpC,IAAA,MAAM,SAAA,GAAY,IAAA,CAAK,OAAA,CAAQ,GAAG,CAAA;AAClC,IAAA,IAAI,SAAA,IAAa,GAAG,OAAO,MAAA;AAC3B,IAAA,MAAM,MAAM,IAAA,CAAK,KAAA,CAAM,CAAA,EAAG,SAAS,EAAE,IAAA,EAAK;AAC1C,IAAA,IAAI,MAAA,CAAO,GAAA,CAAI,GAAG,CAAA,EAAG,OAAO,MAAA;AAC5B,IAAA,MAAA,CAAO,GAAA,CAAI,KAAK,IAAA,CAAK,KAAA,CAAM,YAAY,CAAC,CAAA,CAAE,MAAM,CAAA;AAAA,EAClD;AACA,EAAA,MAAM,SAAA,GAAY,MAAA,CAAO,GAAA,CAAI,GAAG,CAAA;AAChC,EAAA,MAAM,SAAA,GAAY,MAAA,CAAO,GAAA,CAAI,IAAI,CAAA;AACjC,EAAA,IAAI,cAAc,MAAA,IAAa,CAAC,aAAa,IAAA,CAAK,SAAS,GAAG,OAAO,MAAA;AACrE,EAAA,IAAI,cAAc,MAAA,IAAa,CAAC,iBAAiB,IAAA,CAAK,SAAS,GAAG,OAAO,MAAA;AACzE,EAAA,OAAO,EAAE,SAAA,EAAW,MAAA,CAAO,SAAS,GAAG,SAAA,EAAU;AACnD;AAgDA,IAAMC,SAAAA,GAAW,CAAC,KAAA,KAAwC,OAAO,KAAA,KAAU,QAAA,IAAY,KAAA,KAAU,IAAA,IAAQ,CAAC,KAAA,CAAM,OAAA,CAAQ,KAAK,CAAA;AAC7H,IAAM,mBAAmB,CAAC,KAAA,KAA2C,KAAA,KAAU,IAAA,IAAQ,OAAO,KAAA,KAAU,QAAA;AAExG,SAASC,WAAU,MAAA,EAAuB;AACxC,EAAA,MAAM,IAAIL,0BAAAA,CAAuB,CAAA,yBAAA,EAA4B,MAAM,CAAA,CAAE,CAAA;AACvE;AAEA,SAAS,aAAA,CAAc,MAAA,EAAoB,GAAA,EAAa,KAAA,EAAuB;AAC7E,EAAA,MAAM,KAAA,GAAQ,OAAO,GAAG,CAAA;AACxB,EAAA,IAAI,OAAO,UAAU,QAAA,EAAUK,WAAU,CAAA,EAAG,KAAK,CAAA,CAAA,EAAI,GAAG,CAAA,iBAAA,CAAmB,CAAA;AAC3E,EAAA,OAAO,KAAA;AACT;AAEA,SAAS,qBAAA,CAAsB,MAAA,EAAoB,GAAA,EAAa,KAAA,EAA8B;AAC5F,EAAA,MAAM,KAAA,GAAQ,OAAO,GAAG,CAAA;AACxB,EAAA,IAAI,CAAC,gBAAA,CAAiB,KAAK,CAAA,EAAGA,WAAU,CAAA,EAAG,KAAK,CAAA,CAAA,EAAI,GAAG,CAAA,yBAAA,CAA2B,CAAA;AAClF,EAAA,OAAO,KAAA;AACT;AAQO,SAAS,kBAAkB,OAAA,EAAuC;AACvE,EAAA,IAAI,IAAA;AACJ,EAAA,IAAI;AAAE,IAAA,IAAA,GAAO,OAAO,OAAA,KAAY,QAAA,GAAW,OAAA,GAAU,IAAI,WAAA,CAAY,OAAA,EAAS,EAAE,KAAA,EAAO,IAAA,EAAM,CAAA,CAAE,OAAO,OAAO,CAAA;AAAA,EAAG,CAAA,CAAA,MAC1G;AAAE,IAAAA,WAAU,yBAAyB,CAAA;AAAA,EAAG;AAC9C,EAAA,IAAI,KAAA;AACJ,EAAA,IAAI;AAAE,IAAA,KAAA,GAAQ,IAAA,CAAK,MAAM,IAAI,CAAA;AAAA,EAAG,CAAA,CAAA,MAC1B;AAAE,IAAAA,WAAU,wBAAwB,CAAA;AAAA,EAAG;AAC7C,EAAA,IAAI,CAACD,SAAAA,CAAS,KAAK,CAAA,EAAGC,WAAU,4BAA4B,CAAA;AAC5D,EAAA,MAAM,QAAA,GAAW;AAAA,IACf,EAAA,EAAI,aAAA,CAAc,KAAA,EAAO,IAAA,EAAM,OAAO,CAAA;AAAA,IACtC,SAAA,EAAW,aAAA,CAAc,KAAA,EAAO,WAAA,EAAa,OAAO,CAAA;AAAA,IACpD,SAAA,EAAW,aAAA,CAAc,KAAA,EAAO,WAAA,EAAa,OAAO,CAAA;AAAA,IACpD,SAAA,EAAW,aAAA,CAAc,KAAA,EAAO,WAAA,EAAa,OAAO;AAAA,GACtD;AACA,EAAA,MAAM,IAAA,GAAO,aAAA,CAAc,KAAA,EAAO,MAAA,EAAQ,OAAO,CAAA;AACjD,EAAA,MAAM,QAAA,GAAW,aAAA,CAAc,KAAA,EAAO,UAAA,EAAY,OAAO,CAAA;AACzD,EAAA,MAAM,OAAO,KAAA,CAAM,IAAA;AACnB,EAAA,IAAI,QAAA,KAAa,WAAA,IAAe,IAAA,KAAS,iBAAA,EAAmB;AAC1D,IAAA,IAAI,CAACD,SAAAA,CAAS,IAAI,CAAA,EAAGC,WAAU,wBAAwB,CAAA;AACvD,IAAA,MAAM,OAAO,IAAA,CAAK,IAAA;AAClB,IAAA,IAAI,CAACD,SAAAA,CAAS,IAAI,CAAA,EAAGC,WAAU,6BAA6B,CAAA;AAC5D,IAAA,IAAI,OAAO,IAAA,CAAK,MAAA,KAAW,SAAA,EAAWA,WAAU,+BAA+B,CAAA;AAC/E,IAAA,OAAO,EAAE,GAAG,QAAA,EAAU,IAAA,EAAM,UAAU,IAAA,EAAM;AAAA,MAC1C,SAAA,EAAW,aAAA,CAAc,IAAA,EAAM,WAAA,EAAa,MAAM,CAAA;AAAA,MAClD,OAAA,EAAS,aAAA,CAAc,IAAA,EAAM,SAAA,EAAW,MAAM,CAAA;AAAA,MAC9C,QAAA,EAAU,qBAAA,CAAsB,IAAA,EAAM,UAAA,EAAY,MAAM,CAAA;AAAA,MACxD,IAAA,EAAM,aAAA,CAAc,IAAA,EAAM,MAAA,EAAQ,MAAM,CAAA;AAAA,MACxC,IAAA,EAAM,EAAE,EAAA,EAAI,qBAAA,CAAsB,IAAA,EAAM,IAAA,EAAM,WAAW,CAAA,EAAG,QAAA,EAAU,qBAAA,CAAsB,IAAA,EAAM,UAAA,EAAY,WAAW,CAAA,EAAE;AAAA,MAC3H,QAAQ,IAAA,CAAK;AAAA,KACf,EAAE;AAAA,EACJ;AACA,EAAA,IAAI,QAAA,KAAa,WAAA,IAAe,IAAA,KAAS,kBAAA,EAAoB;AAC3D,IAAA,IAAI,CAACD,SAAAA,CAAS,IAAI,CAAA,EAAGC,WAAU,wBAAwB,CAAA;AACvD,IAAA,OAAO,EAAE,GAAG,QAAA,EAAU,IAAA,EAAM,UAAU,IAAA,EAAM;AAAA,MAC1C,QAAA,EAAU,aAAA,CAAc,IAAA,EAAM,UAAA,EAAY,MAAM,CAAA;AAAA,MAChD,OAAA,EAAS,aAAA,CAAc,IAAA,EAAM,SAAA,EAAW,MAAM,CAAA;AAAA,MAC9C,KAAA,EAAO,qBAAA,CAAsB,IAAA,EAAM,OAAA,EAAS,MAAM,CAAA;AAAA,MAClD,SAAA,EAAW,qBAAA,CAAsB,IAAA,EAAM,WAAA,EAAa,MAAM;AAAA,KAC5D,EAAE;AAAA,EACJ;AACA,EAAA,OAAO,EAAE,GAAG,QAAA,EAAU,IAAA,EAAM,WAAW,SAAA,EAAW,IAAA,EAAM,UAAU,IAAA,EAAK;AACzE","file":"index.cjs","sourcesContent":["/** Node timers clamp values above this signed 32-bit millisecond limit to 1ms. */\nexport const MAX_TIMER_DURATION_MS = 2_147_483_647;\n\nexport function isRepresentableTimerDuration(value: number): boolean {\n return Number.isFinite(value) && value > 0 && value <= MAX_TIMER_DURATION_MS;\n}\n","import { CancellationError, MalformedResponseError, TimeoutError } from '@adeli/sdk';\nimport { isRepresentableTimerDuration, MAX_TIMER_DURATION_MS } from '../core/duration.js';\nimport type { ApiResponse, RequestOptions } from '../core/types.js';\nimport type { ConnectionSession, ProfilePath } from '../resources/profiles.js';\nimport type {\n FacebookPostStatusResponse,\n InstagramPostStatusResponse,\n TikTokPostStatusResponse,\n YouTubeVideoStatusResponse,\n} from '../resources/posts.js';\n\ntype PublishStatusQuery = { readonly accountId: string; readonly profileId?: string; readonly publishId: string };\ntype InstagramStatusQuery = { readonly jobId: string; readonly profileId?: string };\ntype VideoStatusQuery = { readonly accountId: string; readonly profileId?: string; readonly videoId: string };\n\n/** A bounded polling deadline and delay. Both values are required to make the side effect explicit. */\nexport interface PollingOptions {\n readonly timeoutMs: number;\n readonly intervalMs: number;\n readonly signal?: AbortSignal;\n}\n\ntype ConnectionMethod = (args: { readonly path: ProfilePath & { readonly connectionSessionId: string } }, options?: RequestOptions) => Promise<ApiResponse<ConnectionSession>>;\nexport type ConnectionPollingClient =\n | { readonly profiles: { readonly getConnection: ConnectionMethod } }\n | { readonly getConnection: ConnectionMethod };\n\ntype InstagramMethod = (args: { readonly query: InstagramStatusQuery }, options?: RequestOptions) => Promise<ApiResponse<InstagramPostStatusResponse>>;\ntype TikTokMethod = (args: { readonly query: PublishStatusQuery }, options?: RequestOptions) => Promise<ApiResponse<TikTokPostStatusResponse>>;\ntype FacebookMethod = (args: { readonly query: PublishStatusQuery }, options?: RequestOptions) => Promise<ApiResponse<FacebookPostStatusResponse>>;\ntype YouTubeMethod = (args: { readonly query: VideoStatusQuery }, options?: RequestOptions) => Promise<ApiResponse<YouTubeVideoStatusResponse>>;\nexport type InstagramPollingClient = { readonly posts: { readonly getInstagramPostStatus: InstagramMethod } } | { readonly getInstagramPostStatus: InstagramMethod };\nexport type TikTokPollingClient = { readonly posts: { readonly getTikTokPostStatus: TikTokMethod } } | { readonly getTikTokPostStatus: TikTokMethod };\nexport type FacebookPollingClient = { readonly posts: { readonly getFacebookPostStatus: FacebookMethod } } | { readonly getFacebookPostStatus: FacebookMethod };\nexport type YouTubePollingClient = { readonly posts: { readonly getYouTubeVideoStatus: YouTubeMethod } } | { readonly getYouTubeVideoStatus: YouTubeMethod };\n/** A convenience structural type for clients that expose all post status methods under resources. */\nexport type PostPollingClient = { readonly posts: { readonly getInstagramPostStatus: InstagramMethod; readonly getTikTokPostStatus: TikTokMethod; readonly getFacebookPostStatus: FacebookMethod; readonly getYouTubeVideoStatus: YouTubeMethod } };\n\ninterface Deadline {\n readonly at: number;\n}\n\ntype PendingStatus<T> = (response: ApiResponse<T>) => 'pending' | 'terminal' | 'invalid';\n\nfunction validatePollingOptions(options: PollingOptions): void {\n if (!isRepresentableTimerDuration(options.timeoutMs)) {\n throw new RangeError(`timeoutMs must be a positive finite number no greater than ${MAX_TIMER_DURATION_MS}ms`);\n }\n if (!isRepresentableTimerDuration(options.intervalMs)) {\n throw new RangeError(`intervalMs must be a positive finite number no greater than ${MAX_TIMER_DURATION_MS}ms`);\n }\n}\n\nfunction cancellation(): CancellationError {\n return new CancellationError('The helper operation was cancelled');\n}\n\nfunction timeout(): TimeoutError {\n return new TimeoutError('The helper operation exceeded its total timeout');\n}\n\nfunction protocolError(message: string): MalformedResponseError {\n return new MalformedResponseError(message);\n}\n\nfunction assertNotAborted(signal: AbortSignal | undefined): void {\n if (signal?.aborted) throw cancellation();\n}\n\n/**\n * Races a request even when a structural fake client ignores AbortSignal. The\n * request's eventual result is observed so a late rejection cannot become\n * unhandled after the helper has timed out or been cancelled.\n */\nasync function requestWithDeadline<T>(\n request: (signal: AbortSignal) => Promise<ApiResponse<T>>,\n deadline: Deadline,\n callerSignal: AbortSignal | undefined,\n): Promise<ApiResponse<T>> {\n assertNotAborted(callerSignal);\n const remaining = deadline.at - Date.now();\n if (remaining <= 0) throw timeout();\n\n const controller = new AbortController();\n let timer: ReturnType<typeof setTimeout> | undefined;\n let settled = false;\n let onAbort: (() => void) | undefined;\n const outcome = new Promise<ApiResponse<T>>((resolve, reject) => {\n const finish = (action: () => void): void => {\n if (settled) return;\n settled = true;\n if (timer !== undefined) clearTimeout(timer);\n if (onAbort !== undefined) callerSignal?.removeEventListener('abort', onAbort);\n action();\n };\n onAbort = () => {\n controller.abort();\n finish(() => reject(cancellation()));\n };\n callerSignal?.addEventListener('abort', onAbort, { once: true });\n timer = setTimeout(() => {\n controller.abort();\n finish(() => reject(timeout()));\n }, remaining);\n\n let result: Promise<ApiResponse<T>>;\n try {\n result = Promise.resolve(request(controller.signal));\n } catch (error) {\n finish(() => reject(error));\n return;\n }\n result.then(\n value => finish(() => {\n if (callerSignal?.aborted) reject(cancellation());\n else if (Date.now() >= deadline.at) reject(timeout());\n else resolve(value);\n }),\n error => finish(() => {\n if (callerSignal?.aborted) reject(cancellation());\n else if (Date.now() >= deadline.at) reject(timeout());\n else reject(error);\n }),\n );\n });\n return outcome;\n}\n\nasync function sleepUntil(deadline: Deadline, intervalMs: number, signal: AbortSignal | undefined): Promise<void> {\n assertNotAborted(signal);\n const remaining = deadline.at - Date.now();\n if (remaining <= 0) throw timeout();\n await new Promise<void>((resolve, reject) => {\n let settled = false;\n const delay = Math.min(intervalMs, remaining);\n let timer: ReturnType<typeof setTimeout> | undefined = setTimeout(() => {\n finish(() => {\n if (deadline.at - Date.now() <= 0) reject(timeout());\n else resolve();\n });\n }, delay);\n const onAbort = (): void => finish(() => reject(cancellation()));\n const finish = (action: () => void): void => {\n if (settled) return;\n settled = true;\n if (timer !== undefined) clearTimeout(timer);\n signal?.removeEventListener('abort', onAbort);\n action();\n };\n signal?.addEventListener('abort', onAbort, { once: true });\n });\n}\n\nasync function poll<T>(\n fetchStatus: (signal: AbortSignal) => Promise<ApiResponse<T>>,\n status: PendingStatus<T>,\n options: PollingOptions,\n): Promise<ApiResponse<T>> {\n validatePollingOptions(options);\n assertNotAborted(options.signal);\n const deadline = { at: Date.now() + options.timeoutMs };\n while (true) {\n const response = await requestWithDeadline(fetchStatus, deadline, options.signal);\n assertNotAborted(options.signal);\n if (Date.now() >= deadline.at) throw timeout();\n const state = status(response);\n if (state === 'terminal') return response;\n if (state === 'invalid') throw protocolError('The provider returned an unknown polling status');\n await sleepUntil(deadline, options.intervalMs, options.signal);\n }\n}\n\nfunction connectionStatus(response: ApiResponse<ConnectionSession>): 'pending' | 'terminal' | 'invalid' {\n const value = response.data?.status as string | undefined;\n switch (value) {\n case 'pending_authorization':\n case 'processing':\n case 'pending_page_selection':\n return 'pending';\n case 'connected':\n case 'failed':\n case 'expired':\n return 'terminal';\n default:\n return 'invalid';\n }\n}\n\nfunction instagramStatus(response: ApiResponse<InstagramPostStatusResponse>): 'pending' | 'terminal' | 'invalid' {\n switch (response.data?.status) {\n case 'queued':\n case 'processing': return 'pending';\n case 'published':\n case 'failed':\n case 'outcome_unknown': return 'terminal';\n default: return 'invalid';\n }\n}\n\nfunction facebookStatus(response: ApiResponse<FacebookPostStatusResponse>): 'pending' | 'terminal' | 'invalid' {\n switch (response.data?.status) {\n case 'PROCESSING': return 'pending';\n case 'READY':\n case 'ERROR': return 'terminal';\n default: return 'invalid';\n }\n}\n\nfunction youtubeStatus(response: ApiResponse<YouTubeVideoStatusResponse>): 'pending' | 'terminal' | 'invalid' {\n switch (response.data?.status) {\n case 'processing': return 'pending';\n case 'published':\n case 'failed': return 'terminal';\n default: return 'invalid';\n }\n}\n\nfunction tiktokStatus(response: ApiResponse<TikTokPostStatusResponse>): 'pending' | 'terminal' | 'invalid' {\n switch (response.data?.status) {\n // TikTok may report upload/download processing before delivery to the inbox.\n case 'PROCESSING_UPLOAD':\n case 'PROCESSING_DOWNLOAD': return 'pending';\n // Inbox delivery is terminal, but it is not public publication.\n case 'SEND_TO_USER_INBOX':\n case 'PUBLISH_COMPLETE':\n case 'FAILED': return 'terminal';\n default: return 'invalid';\n }\n}\n\nfunction connectionMethod(client: ConnectionPollingClient): ConnectionMethod {\n return 'profiles' in client\n ? (args, options) => client.profiles.getConnection(args, options)\n : (args, options) => client.getConnection(args, options);\n}\nfunction instagramMethod(client: InstagramPollingClient): InstagramMethod {\n return 'posts' in client\n ? (args, options) => client.posts.getInstagramPostStatus(args, options)\n : (args, options) => client.getInstagramPostStatus(args, options);\n}\nfunction tikTokMethod(client: TikTokPollingClient): TikTokMethod {\n return 'posts' in client\n ? (args, options) => client.posts.getTikTokPostStatus(args, options)\n : (args, options) => client.getTikTokPostStatus(args, options);\n}\nfunction facebookMethod(client: FacebookPollingClient): FacebookMethod {\n return 'posts' in client\n ? (args, options) => client.posts.getFacebookPostStatus(args, options)\n : (args, options) => client.getFacebookPostStatus(args, options);\n}\nfunction youtubeMethod(client: YouTubePollingClient): YouTubeMethod {\n return 'posts' in client\n ? (args, options) => client.posts.getYouTubeVideoStatus(args, options)\n : (args, options) => client.getYouTubeVideoStatus(args, options);\n}\n\n/** Poll a typed connection session; Facebook Page selection can require a dashboard handoff. */\nexport function waitForConnection(\n client: ConnectionPollingClient,\n args: { readonly path: ProfilePath & { readonly connectionSessionId: string } },\n options: PollingOptions,\n): Promise<ApiResponse<ConnectionSession>> {\n const getConnection = connectionMethod(client);\n const snapshot = { path: { ...args.path } };\n return poll(signal => getConnection(snapshot, { signal }), connectionStatus, options);\n}\n\n/** Poll durable Instagram publication through published, failed, or uncertain outcome. */\nexport function waitForInstagramPost(\n client: InstagramPollingClient,\n args: { readonly query: InstagramStatusQuery },\n options: PollingOptions,\n): Promise<ApiResponse<InstagramPostStatusResponse>> {\n const getStatus = instagramMethod(client);\n const snapshot = { query: { ...args.query } };\n return poll(signal => getStatus(snapshot, { signal }), instagramStatus, options);\n}\n\n/** Poll TikTok processing. SEND_TO_USER_INBOX is not a public publication. */\nexport function waitForTikTokPost(\n client: TikTokPollingClient,\n args: { readonly query: PublishStatusQuery },\n options: PollingOptions,\n): Promise<ApiResponse<TikTokPostStatusResponse>> {\n const getStatus = tikTokMethod(client);\n const snapshot = { query: { ...args.query } };\n return poll(signal => getStatus(snapshot, { signal }), tiktokStatus, options);\n}\n\n/** Poll Facebook video processing; Page selection is outside this status endpoint. */\nexport function waitForFacebookPost(\n client: FacebookPollingClient,\n args: { readonly query: PublishStatusQuery },\n options: PollingOptions,\n): Promise<ApiResponse<FacebookPostStatusResponse>> {\n const getStatus = facebookMethod(client);\n const snapshot = { query: { ...args.query } };\n return poll(signal => getStatus(snapshot, { signal }), facebookStatus, options);\n}\n\n/** Poll YouTube processing. Published means processing completed, not public visibility. */\nexport function waitForYouTubeVideo(\n client: YouTubePollingClient,\n args: { readonly query: VideoStatusQuery },\n options: PollingOptions,\n): Promise<ApiResponse<YouTubeVideoStatusResponse>> {\n const getStatus = youtubeMethod(client);\n const snapshot = { query: { ...args.query } };\n return poll(signal => getStatus(snapshot, { signal }), youtubeStatus, options);\n}\n","import { CancellationError, ConfigurationError, MalformedResponseError, TimeoutError } from '@adeli/sdk';\nimport { isRepresentableTimerDuration, MAX_TIMER_DURATION_MS } from '../core/duration.js';\nimport type { ApiResponse, RequestOptions } from '../core/types.js';\nimport type { CommentsQuery, CommentsResponse } from '../resources/comments.js';\n\nexport type CommentIteratorQuery = Omit<CommentsQuery, 'platform'> & { readonly platform: 'tiktok' | 'youtube' };\n\nexport interface CommentsClient {\n readonly comments: {\n listComments(args: { readonly query: CommentsQuery }, options?: RequestOptions): Promise<ApiResponse<CommentsResponse>>;\n };\n}\n\nexport interface CommentIteratorOptions {\n /** Maximum number of requests. The default is deliberately finite. */\n readonly maxPages?: number;\n /** Total wall-clock budget for all pages, including request time. */\n readonly timeoutMs?: number;\n readonly signal?: AbortSignal;\n}\n\nconst DEFAULT_MAX_PAGES = 100;\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null;\n}\n\nfunction validateOptions(options: CommentIteratorOptions): Required<Pick<CommentIteratorOptions, 'maxPages'>> & CommentIteratorOptions {\n const maxPages = options.maxPages ?? DEFAULT_MAX_PAGES;\n if (!Number.isFinite(maxPages) || maxPages <= 0 || !Number.isInteger(maxPages)) {\n throw new ConfigurationError('maxPages must be a finite positive integer');\n }\n if (options.timeoutMs !== undefined && !isRepresentableTimerDuration(options.timeoutMs)) {\n throw new ConfigurationError(`timeoutMs must be a positive finite number no greater than ${MAX_TIMER_DURATION_MS}ms`);\n }\n return { ...options, maxPages };\n}\n\nfunction abortError(): CancellationError {\n return new CancellationError('The helper operation was cancelled');\n}\n\nfunction timeoutError(): TimeoutError {\n return new TimeoutError('The helper operation exceeded its total timeout');\n}\n\nfunction malformed(message: string): MalformedResponseError {\n return new MalformedResponseError(message);\n}\n\nfunction assertPage(value: unknown): asserts value is CommentsResponse {\n if (!isRecord(value) || !Array.isArray(value.comments)) {\n throw malformed('The comments endpoint returned a malformed page');\n }\n if ('postId' in value && value.postId !== null && value.postId !== undefined && typeof value.postId !== 'string') {\n throw malformed('The comments endpoint returned an invalid post identity');\n }\n if (value.nextCursor !== undefined && value.nextCursor !== null && typeof value.nextCursor !== 'string') {\n throw malformed('The comments endpoint returned an invalid cursor');\n }\n for (const comment of value.comments) {\n if (!isRecord(comment) || typeof comment.id !== 'string' || comment.id.length === 0) {\n throw malformed('The comments endpoint returned a malformed comment');\n }\n }\n}\n\nfunction assertScope(page: CommentsResponse, query: CommentsQuery): void {\n if (page.platform !== query.platform || page.accountId !== query.accountId) {\n throw malformed('The comments endpoint changed the requested account scope');\n }\n if (query.profileId !== undefined && page.profileId !== query.profileId) {\n throw malformed('The comments endpoint changed the requested profile scope');\n }\n if (query.postId !== undefined && page.postId !== query.postId) {\n throw malformed('The comments endpoint changed the requested post scope');\n }\n}\n\nfunction makeDeadline(timeoutMs: number | undefined): number | undefined {\n return timeoutMs === undefined ? undefined : Date.now() + timeoutMs;\n}\n\nasync function requestPage(\n client: CommentsClient,\n query: CommentsQuery,\n deadline: number | undefined,\n signal: AbortSignal | undefined,\n): Promise<ApiResponse<CommentsResponse>> {\n if (signal?.aborted) throw abortError();\n const remaining = deadline === undefined ? undefined : deadline - Date.now();\n if (remaining !== undefined && remaining <= 0) throw timeoutError();\n\n const controller = new AbortController();\n let timer: ReturnType<typeof setTimeout> | undefined;\n let settled = false;\n let onAbort: (() => void) | undefined;\n return new Promise<ApiResponse<CommentsResponse>>((resolve, reject) => {\n const finish = (action: () => void): void => {\n if (settled) return;\n settled = true;\n if (timer !== undefined) clearTimeout(timer);\n if (onAbort !== undefined) signal?.removeEventListener('abort', onAbort);\n action();\n };\n onAbort = () => {\n controller.abort();\n finish(() => reject(abortError()));\n };\n signal?.addEventListener('abort', onAbort, { once: true });\n if (remaining !== undefined) {\n timer = setTimeout(() => {\n controller.abort();\n finish(() => reject(timeoutError()));\n }, remaining);\n }\n let result: Promise<ApiResponse<CommentsResponse>>;\n try {\n result = Promise.resolve(client.comments.listComments({ query }, { signal: controller.signal }));\n } catch (error) {\n finish(() => reject(error));\n return;\n }\n result.then(\n value => finish(() => {\n if (signal?.aborted) reject(abortError());\n else if (deadline !== undefined && Date.now() >= deadline) reject(timeoutError());\n else resolve(value);\n }),\n error => finish(() => {\n if (signal?.aborted) reject(abortError());\n else if (deadline !== undefined && Date.now() >= deadline) reject(timeoutError());\n else reject(error);\n }),\n );\n });\n}\n\nfunction validatePlatform(query: CommentIteratorQuery): void {\n if (query.platform !== 'tiktok' && query.platform !== 'youtube') {\n throw new ConfigurationError('Comment iteration is supported only for TikTok and YouTube');\n }\n if (query.cursor !== undefined && query.postId === undefined) {\n throw malformed('A comment cursor requires a selected postId');\n }\n}\n\nasync function* pages(\n client: CommentsClient,\n args: { readonly query: CommentIteratorQuery },\n options: CommentIteratorOptions,\n): AsyncGenerator<ApiResponse<CommentsResponse>> {\n const checked = validateOptions(options);\n validatePlatform(args.query);\n const initialQuery = { ...args.query };\n let query: CommentsQuery = { ...initialQuery };\n const deadline = makeDeadline(checked.timeoutMs);\n const seenCursors = new Set<string>();\n if (query.cursor !== undefined) seenCursors.add(query.cursor);\n let selectedPostId = query.postId;\n\n try {\n for (let pageNumber = 0; pageNumber < checked.maxPages; pageNumber += 1) {\n if (checked.signal?.aborted) throw abortError();\n const response = await requestPage(client, query, deadline, checked.signal);\n assertPage(response.data);\n assertScope(response.data, query);\n\n if (selectedPostId === undefined) {\n // Only the endpoint's authoritative postId may pin later requests.\n selectedPostId = response.data.postId ?? undefined;\n if (selectedPostId !== undefined) query = { ...query, postId: selectedPostId };\n } else if (response.data.postId !== selectedPostId) {\n throw malformed('The comments endpoint changed the selected post');\n }\n\n const nextCursor = response.data.nextCursor;\n if (selectedPostId === undefined && nextCursor !== null && nextCursor !== undefined && nextCursor !== '') {\n throw malformed('A comment cursor was returned without an authoritative selected post');\n }\n yield response;\n if (nextCursor === null || nextCursor === undefined || nextCursor === '') return;\n if (selectedPostId === undefined) {\n throw malformed('A comment cursor was returned without an authoritative selected post');\n }\n if (seenCursors.has(nextCursor)) return;\n seenCursors.add(nextCursor);\n query = { ...query, cursor: nextCursor };\n }\n } finally {\n // No eager page is requested: this also makes early consumer breaks cheap\n // and leaves no helper-owned timers or listeners behind.\n }\n}\n\n/** Yield complete page responses; `complete` describes cache freshness, not pagination. */\nexport function iterateCommentPages(\n client: CommentsClient,\n args: { readonly query: CommentIteratorQuery },\n options: CommentIteratorOptions = {},\n): AsyncGenerator<ApiResponse<CommentsResponse>> {\n return pages(client, args, options);\n}\n\n/** Yield the original comment records without normalizing provider fields. */\nexport async function* iterateComments(\n client: CommentsClient,\n args: { readonly query: CommentIteratorQuery },\n options: CommentIteratorOptions = {},\n): AsyncGenerator<CommentsResponse['comments'][number]> {\n for await (const page of pages(client, args, options)) {\n for (const comment of page.data.comments) yield comment;\n }\n}\n","import { createHmac, timingSafeEqual } from 'node:crypto';\nimport { ConfigurationError, MalformedResponseError } from '@adeli/sdk';\n\n/** The raw request body exactly as received. Verify before parsing; re-serialized JSON will not match. */\nexport type WebhookPayload = string | Uint8Array;\n\nexport interface VerifyWebhookSignatureInput {\n /** The raw request body. */\n readonly payload: WebhookPayload;\n /** The `Adeli-Signature` header: `t=<unix seconds>,v1=<hex HMAC-SHA256>`. */\n readonly header: string | null | undefined;\n /** The webhook's signing secret, returned once by `createWebhook`. */\n readonly secret: string;\n /** Maximum age (or clock skew) of the signed timestamp. Defaults to 300 seconds. */\n readonly toleranceSeconds?: number;\n /** Current time in unix seconds, for tests. Defaults to the system clock. */\n readonly now?: number;\n}\n\nexport const DEFAULT_WEBHOOK_TOLERANCE_SECONDS = 300;\n\n/**\n * Check an `Adeli-Signature` header against the raw body. Returns false for a\n * malformed header, a timestamp outside the tolerance, or a signature mismatch;\n * the comparison is constant-time. Throws only for a missing secret or an\n * invalid tolerance, which are configuration mistakes rather than bad deliveries.\n */\nexport function verifyWebhookSignature(input: VerifyWebhookSignatureInput): boolean {\n if (typeof input.secret !== 'string' || input.secret.length === 0) throw new ConfigurationError('verifyWebhookSignature requires the webhook signing secret');\n const tolerance = input.toleranceSeconds ?? DEFAULT_WEBHOOK_TOLERANCE_SECONDS;\n if (!Number.isFinite(tolerance) || tolerance < 0) throw new ConfigurationError('toleranceSeconds must be a finite, non-negative number');\n const parsed = parseSignatureHeader(input.header);\n if (!parsed) return false;\n const now = input.now ?? Math.floor(Date.now() / 1000);\n if (Math.abs(now - parsed.timestamp) > tolerance) return false;\n const expected = createHmac('sha256', input.secret).update(`${parsed.timestamp}.`).update(input.payload).digest();\n return timingSafeEqual(expected, Buffer.from(parsed.signature, 'hex'));\n}\n\nfunction parseSignatureHeader(header: string | null | undefined): { timestamp: number; signature: string } | undefined {\n if (typeof header !== 'string') return undefined;\n const fields = new Map<string, string>();\n for (const part of header.split(',')) {\n const separator = part.indexOf('=');\n if (separator <= 0) return undefined;\n const key = part.slice(0, separator).trim();\n if (fields.has(key)) return undefined;\n fields.set(key, part.slice(separator + 1).trim());\n }\n const timestamp = fields.get('t');\n const signature = fields.get('v1');\n if (timestamp === undefined || !/^\\d{1,12}$/.test(timestamp)) return undefined;\n if (signature === undefined || !/^[0-9a-f]{64}$/.test(signature)) return undefined;\n return { timestamp: Number(timestamp), signature };\n}\n\ninterface WebhookEventEnvelope {\n /** Stable per provider event; deduplicate redeliveries on it. */\n readonly id: string;\n readonly createdAt: string;\n readonly profileId: string;\n readonly accountId: string;\n}\n\nexport interface CommentCreatedEvent extends WebhookEventEnvelope {\n readonly type: 'comment.created';\n readonly platform: 'instagram';\n readonly data: {\n readonly commentId: string;\n readonly mediaId: string;\n readonly parentId: string | null;\n readonly text: string;\n readonly from: { readonly id: string | null; readonly username: string | null };\n /** True when the connected account wrote the comment itself. */\n readonly isSelf: boolean;\n };\n}\n\nexport interface MessagePostbackEvent extends WebhookEventEnvelope {\n readonly type: 'message.postback';\n readonly platform: 'instagram';\n readonly data: {\n /** The person's Instagram-scoped id; reply with `sendInstagramMessage({ recipient: { id: senderId } })`. */\n readonly senderId: string;\n readonly payload: string;\n readonly title: string | null;\n readonly messageId: string | null;\n };\n}\n\n/** An event type (or platform) this SDK version does not know yet. `eventType` keeps the wire `type`. */\nexport interface UnknownWebhookEvent extends WebhookEventEnvelope {\n readonly type: 'unknown';\n readonly eventType: string;\n readonly platform: string;\n readonly data: unknown;\n}\n\nexport type WebhookEvent = CommentCreatedEvent | MessagePostbackEvent | UnknownWebhookEvent;\nexport type WebhookEventType = Exclude<WebhookEvent['type'], 'unknown'>;\n\ntype JsonRecord = Record<string, unknown>;\nconst isRecord = (value: unknown): value is JsonRecord => typeof value === 'object' && value !== null && !Array.isArray(value);\nconst isNullableString = (value: unknown): value is string | null => value === null || typeof value === 'string';\n\nfunction malformed(detail: string): never {\n throw new MalformedResponseError(`Malformed webhook event: ${detail}`);\n}\n\nfunction requireString(record: JsonRecord, key: string, where: string): string {\n const value = record[key];\n if (typeof value !== 'string') malformed(`${where}.${key} must be a string`);\n return value;\n}\n\nfunction requireNullableString(record: JsonRecord, key: string, where: string): string | null {\n const value = record[key];\n if (!isNullableString(value)) malformed(`${where}.${key} must be a string or null`);\n return value;\n}\n\n/**\n * Parse a verified webhook body into a typed event. Known types are validated\n * field by field; an unrecognised type parses as `type: 'unknown'` so new event\n * types stay additive. Throws `MalformedResponseError` for invalid JSON, a\n * missing envelope field, or a known event whose data does not match.\n */\nexport function parseWebhookEvent(payload: WebhookPayload): WebhookEvent {\n let text: string;\n try { text = typeof payload === 'string' ? payload : new TextDecoder('utf-8', { fatal: true }).decode(payload); }\n catch { malformed('body is not valid UTF-8'); }\n let value: unknown;\n try { value = JSON.parse(text); }\n catch { malformed('body is not valid JSON'); }\n if (!isRecord(value)) malformed('body must be a JSON object');\n const envelope = {\n id: requireString(value, 'id', 'event'),\n createdAt: requireString(value, 'createdAt', 'event'),\n profileId: requireString(value, 'profileId', 'event'),\n accountId: requireString(value, 'accountId', 'event'),\n };\n const type = requireString(value, 'type', 'event');\n const platform = requireString(value, 'platform', 'event');\n const data = value.data;\n if (platform === 'instagram' && type === 'comment.created') {\n if (!isRecord(data)) malformed('data must be an object');\n const from = data.from;\n if (!isRecord(from)) malformed('data.from must be an object');\n if (typeof data.isSelf !== 'boolean') malformed('data.isSelf must be a boolean');\n return { ...envelope, type, platform, data: {\n commentId: requireString(data, 'commentId', 'data'),\n mediaId: requireString(data, 'mediaId', 'data'),\n parentId: requireNullableString(data, 'parentId', 'data'),\n text: requireString(data, 'text', 'data'),\n from: { id: requireNullableString(from, 'id', 'data.from'), username: requireNullableString(from, 'username', 'data.from') },\n isSelf: data.isSelf,\n } };\n }\n if (platform === 'instagram' && type === 'message.postback') {\n if (!isRecord(data)) malformed('data must be an object');\n return { ...envelope, type, platform, data: {\n senderId: requireString(data, 'senderId', 'data'),\n payload: requireString(data, 'payload', 'data'),\n title: requireNullableString(data, 'title', 'data'),\n messageId: requireNullableString(data, 'messageId', 'data'),\n } };\n }\n return { ...envelope, type: 'unknown', eventType: type, platform, data };\n}\n"]}
|