@iann29/rastro 0.10.9 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (178) hide show
  1. package/README.md +8 -0
  2. package/agent/integration.md +45 -0
  3. package/agent/manifest.json +4 -4
  4. package/dist/client/federation.d.ts +82 -9
  5. package/dist/client/federation.d.ts.map +1 -1
  6. package/dist/client/federation.js +40 -2
  7. package/dist/client/federation.js.map +1 -1
  8. package/dist/client/index.d.ts +464 -8
  9. package/dist/client/index.d.ts.map +1 -1
  10. package/dist/client/index.js +259 -7
  11. package/dist/client/index.js.map +1 -1
  12. package/dist/component/_generated/api.d.ts +10 -0
  13. package/dist/component/_generated/api.d.ts.map +1 -1
  14. package/dist/component/_generated/api.js.map +1 -1
  15. package/dist/component/_generated/component.d.ts +273 -21
  16. package/dist/component/_generated/component.d.ts.map +1 -1
  17. package/dist/component/_generated/server.d.ts +2 -0
  18. package/dist/component/_generated/server.d.ts.map +1 -1
  19. package/dist/component/_generated/server.js.map +1 -1
  20. package/dist/component/constants.d.ts +31 -0
  21. package/dist/component/constants.d.ts.map +1 -1
  22. package/dist/component/constants.js +31 -0
  23. package/dist/component/constants.js.map +1 -1
  24. package/dist/component/convex.config.d.ts +2 -0
  25. package/dist/component/convex.config.js +7 -0
  26. package/dist/component/convex.config.js.map +1 -1
  27. package/dist/component/errors.d.ts +1 -1
  28. package/dist/component/errors.d.ts.map +1 -1
  29. package/dist/component/errors.js.map +1 -1
  30. package/dist/component/http.d.ts.map +1 -1
  31. package/dist/component/http.js +64 -0
  32. package/dist/component/http.js.map +1 -1
  33. package/dist/component/leadAdsValidators.d.ts +1092 -0
  34. package/dist/component/leadAdsValidators.d.ts.map +1 -0
  35. package/dist/component/leadAdsValidators.js +304 -0
  36. package/dist/component/leadAdsValidators.js.map +1 -0
  37. package/dist/component/leadFormRules.d.ts +60 -0
  38. package/dist/component/leadFormRules.d.ts.map +1 -0
  39. package/dist/component/leadFormRules.js +140 -0
  40. package/dist/component/leadFormRules.js.map +1 -0
  41. package/dist/component/leadFormStore.d.ts +16 -0
  42. package/dist/component/leadFormStore.d.ts.map +1 -0
  43. package/dist/component/leadFormStore.js +92 -0
  44. package/dist/component/leadFormStore.js.map +1 -0
  45. package/dist/component/leadgenToken.d.ts +56 -0
  46. package/dist/component/leadgenToken.d.ts.map +1 -0
  47. package/dist/component/leadgenToken.js +262 -0
  48. package/dist/component/leadgenToken.js.map +1 -0
  49. package/dist/component/leads.d.ts +76 -7
  50. package/dist/component/leads.d.ts.map +1 -1
  51. package/dist/component/leads.js +112 -78
  52. package/dist/component/leads.js.map +1 -1
  53. package/dist/component/meta.d.ts.map +1 -1
  54. package/dist/component/meta.js +21 -1
  55. package/dist/component/meta.js.map +1 -1
  56. package/dist/component/metaFake.d.ts +44 -0
  57. package/dist/component/metaFake.d.ts.map +1 -1
  58. package/dist/component/metaFake.js +400 -1
  59. package/dist/component/metaFake.js.map +1 -1
  60. package/dist/component/metaGraph.d.ts +91 -0
  61. package/dist/component/metaGraph.d.ts.map +1 -1
  62. package/dist/component/metaGraph.js +217 -0
  63. package/dist/component/metaGraph.js.map +1 -1
  64. package/dist/component/metaLeads.d.ts +681 -0
  65. package/dist/component/metaLeads.d.ts.map +1 -0
  66. package/dist/component/metaLeads.js +2634 -0
  67. package/dist/component/metaLeads.js.map +1 -0
  68. package/dist/component/schema.d.ts +414 -5
  69. package/dist/component/schema.d.ts.map +1 -1
  70. package/dist/component/schema.js +169 -1
  71. package/dist/component/schema.js.map +1 -1
  72. package/dist/component/validators.d.ts +235 -11
  73. package/dist/component/validators.d.ts.map +1 -1
  74. package/dist/component/validators.js +14 -1
  75. package/dist/component/validators.js.map +1 -1
  76. package/dist/tracker/generated.d.ts +1 -1
  77. package/dist/tracker/generated.js +1 -1
  78. package/dist/ui/campaigns/CampaignsView.d.ts +5 -1
  79. package/dist/ui/campaigns/CampaignsView.d.ts.map +1 -1
  80. package/dist/ui/campaigns/CampaignsView.js.map +1 -1
  81. package/dist/ui/campaigns/Drawer.d.ts.map +1 -1
  82. package/dist/ui/campaigns/Drawer.js +8 -26
  83. package/dist/ui/campaigns/Drawer.js.map +1 -1
  84. package/dist/ui/campaigns/labels.d.ts +29 -1
  85. package/dist/ui/campaigns/labels.d.ts.map +1 -1
  86. package/dist/ui/campaigns/labels.js +77 -2
  87. package/dist/ui/campaigns/labels.js.map +1 -1
  88. package/dist/ui/campaigns/model.js +2 -2
  89. package/dist/ui/campaigns/model.js.map +1 -1
  90. package/dist/ui/forms/FirstContact.d.ts +19 -0
  91. package/dist/ui/forms/FirstContact.d.ts.map +1 -0
  92. package/dist/ui/forms/FirstContact.js +81 -0
  93. package/dist/ui/forms/FirstContact.js.map +1 -0
  94. package/dist/ui/forms/FormLead.d.ts +28 -0
  95. package/dist/ui/forms/FormLead.d.ts.map +1 -0
  96. package/dist/ui/forms/FormLead.js +199 -0
  97. package/dist/ui/forms/FormLead.js.map +1 -0
  98. package/dist/ui/forms/FormRulesDrawer.d.ts +49 -0
  99. package/dist/ui/forms/FormRulesDrawer.d.ts.map +1 -0
  100. package/dist/ui/forms/FormRulesDrawer.js +149 -0
  101. package/dist/ui/forms/FormRulesDrawer.js.map +1 -0
  102. package/dist/ui/forms/FormsView.d.ts +10 -0
  103. package/dist/ui/forms/FormsView.d.ts.map +1 -0
  104. package/dist/ui/forms/FormsView.js +215 -0
  105. package/dist/ui/forms/FormsView.js.map +1 -0
  106. package/dist/ui/forms/Icons.d.ts +46 -0
  107. package/dist/ui/forms/Icons.d.ts.map +1 -0
  108. package/dist/ui/forms/Icons.js +45 -0
  109. package/dist/ui/forms/Icons.js.map +1 -0
  110. package/dist/ui/forms/RuleRow.d.ts +37 -0
  111. package/dist/ui/forms/RuleRow.d.ts.map +1 -0
  112. package/dist/ui/forms/RuleRow.js +80 -0
  113. package/dist/ui/forms/RuleRow.js.map +1 -0
  114. package/dist/ui/forms/WhatsAppButtonGuide.d.ts +33 -0
  115. package/dist/ui/forms/WhatsAppButtonGuide.d.ts.map +1 -0
  116. package/dist/ui/forms/WhatsAppButtonGuide.js +90 -0
  117. package/dist/ui/forms/WhatsAppButtonGuide.js.map +1 -0
  118. package/dist/ui/forms/index.d.ts +11 -0
  119. package/dist/ui/forms/index.d.ts.map +1 -0
  120. package/dist/ui/forms/index.js +7 -0
  121. package/dist/ui/forms/index.js.map +1 -0
  122. package/dist/ui/forms/journey.d.ts +19 -0
  123. package/dist/ui/forms/journey.d.ts.map +1 -0
  124. package/dist/ui/forms/journey.js +54 -0
  125. package/dist/ui/forms/journey.js.map +1 -0
  126. package/dist/ui/forms/model.d.ts +239 -0
  127. package/dist/ui/forms/model.d.ts.map +1 -0
  128. package/dist/ui/forms/model.js +556 -0
  129. package/dist/ui/forms/model.js.map +1 -0
  130. package/dist/ui/forms/sample.d.ts +36 -0
  131. package/dist/ui/forms/sample.d.ts.map +1 -0
  132. package/dist/ui/forms/sample.js +444 -0
  133. package/dist/ui/forms/sample.js.map +1 -0
  134. package/dist/ui/forms/types.d.ts +454 -0
  135. package/dist/ui/forms/types.d.ts.map +1 -0
  136. package/dist/ui/forms/types.js +2 -0
  137. package/dist/ui/forms/types.js.map +1 -0
  138. package/dist/ui/index.d.ts +2 -0
  139. package/dist/ui/index.d.ts.map +1 -1
  140. package/dist/ui/index.js +4 -0
  141. package/dist/ui/index.js.map +1 -1
  142. package/dist/ui/integrations/Overlay.d.ts +3 -1
  143. package/dist/ui/integrations/Overlay.d.ts.map +1 -1
  144. package/dist/ui/integrations/Overlay.js +2 -2
  145. package/dist/ui/integrations/Overlay.js.map +1 -1
  146. package/dist/ui/portfolio/PortfolioView.d.ts +4 -1
  147. package/dist/ui/portfolio/PortfolioView.d.ts.map +1 -1
  148. package/dist/ui/portfolio/PortfolioView.js.map +1 -1
  149. package/dist/ui/sample.d.ts +3 -1
  150. package/dist/ui/sample.d.ts.map +1 -1
  151. package/dist/ui/sample.js +2 -0
  152. package/dist/ui/sample.js.map +1 -1
  153. package/dist/ui/seed.d.ts +1 -1
  154. package/dist/ui/ui.css +2301 -0
  155. package/docs/federation.md +19 -0
  156. package/docs/meta-ads.md +713 -7
  157. package/docs/upgrading.md +70 -0
  158. package/llms.txt +1 -0
  159. package/package.json +1 -1
  160. package/src/component/_generated/api.ts +10 -0
  161. package/src/component/_generated/component.ts +289 -20
  162. package/src/component/_generated/server.ts +2 -0
  163. package/src/component/constants.ts +33 -0
  164. package/src/component/convex.config.ts +7 -0
  165. package/src/component/errors.ts +3 -1
  166. package/src/component/http.ts +75 -0
  167. package/src/component/leadAdsValidators.ts +365 -0
  168. package/src/component/leadFormRules.ts +202 -0
  169. package/src/component/leadFormStore.ts +129 -0
  170. package/src/component/leadgenToken.ts +348 -0
  171. package/src/component/leads.ts +136 -84
  172. package/src/component/meta.ts +21 -1
  173. package/src/component/metaFake.ts +485 -1
  174. package/src/component/metaGraph.ts +327 -0
  175. package/src/component/metaLeads.ts +3101 -0
  176. package/src/component/schema.ts +187 -1
  177. package/src/component/validators.ts +17 -1
  178. package/src/tracker/generated.ts +1 -1
@@ -82,6 +82,52 @@ export interface GraphSpendRow {
82
82
  campaignName: string | null;
83
83
  }
84
84
 
85
+ /** A Facebook Page the token can act for, with the Page's own token. */
86
+ export interface GraphPage {
87
+ pageId: string;
88
+ name: string;
89
+ /** The Page token Meta hands out with `me/accounts`; null when it did not. */
90
+ accessToken: string | null;
91
+ tasks: string[];
92
+ }
93
+
94
+ /** One question of an instant form. */
95
+ export interface GraphFormQuestion {
96
+ key: string;
97
+ label: string;
98
+ type: string;
99
+ options?: Array<{ key: string; value: string }>;
100
+ }
101
+
102
+ export interface GraphForm {
103
+ formId: string;
104
+ name: string;
105
+ status: string;
106
+ questions: GraphFormQuestion[];
107
+ /** The end screen's WhatsApp button; null when Meta does not say. */
108
+ whatsappButton: boolean | null;
109
+ createdTime: number | null;
110
+ }
111
+
112
+ /** A form lead as Meta returns it. `fields` carries personal data. */
113
+ export interface GraphLead {
114
+ leadgenId: string;
115
+ createdTime: number;
116
+ formId: string | null;
117
+ adId: string | null;
118
+ adsetId: string | null;
119
+ campaignId: string | null;
120
+ platform: string | null;
121
+ isOrganic: boolean;
122
+ fields: Array<{ name: string; values: string[] }>;
123
+ }
124
+
125
+ /**
126
+ * Which Pages a login may read leads from (`leads_retrieval`): every Page it
127
+ * reaches (`all`), or only `pageIds`. Null when Meta did not say.
128
+ */
129
+ export type GraphLeadAccess = { all: boolean; pageIds: string[] } | null;
130
+
85
131
  /** How close the app is to Meta's rate limits, from the response headers. */
86
132
  export interface GraphUsage {
87
133
  /** The highest percentage any limit header reported, 0–100+. */
@@ -182,6 +228,37 @@ export interface MetaGraph {
182
228
  pixelId: string,
183
229
  payload: ConversionsPayload,
184
230
  ): Promise<{ eventsReceived: number; fbtraceId: string | null }>;
231
+ /**
232
+ * The Pages the token acts for (`me/accounts`), each with its own token.
233
+ * `complete` is false when the listing stopped at the page cap.
234
+ */
235
+ pages(token: string): Promise<{ pages: GraphPage[]; complete: boolean }>;
236
+ /**
237
+ * Which Pages the login may read leads from: `debug_token`'s granular
238
+ * scopes with `app`, else `me/permissions`. Null when Meta did not say.
239
+ */
240
+ leadAccess(token: string, app?: MetaApp | null): Promise<GraphLeadAccess>;
241
+ /** Subscribes the app to the Page's `leadgen` webhook (the Page token). */
242
+ subscribePage(pageToken: string, pageId: string): Promise<void>;
243
+ /** The Page's instant forms, every status. */
244
+ leadForms(
245
+ pageToken: string,
246
+ pageId: string,
247
+ ): Promise<{ forms: GraphForm[]; usage: GraphUsage; complete: boolean }>;
248
+ /** One lead, with its answers (personal data: never store `fields`). */
249
+ lead(pageToken: string, leadgenId: string): Promise<GraphLead>;
250
+ /**
251
+ * A form's leads created after `sinceSec` and, with `untilSec`, before it
252
+ * (Meta's seconds). Meta lists the newest first, so a listing that stopped
253
+ * at the cap (`complete: false`) holds the newest of the window: read a
254
+ * narrower window instead of trusting it to cover the older ones.
255
+ */
256
+ formLeads(
257
+ pageToken: string,
258
+ formId: string,
259
+ sinceSec: number,
260
+ untilSec?: number,
261
+ ): Promise<{ leads: GraphLead[]; usage: GraphUsage; complete: boolean }>;
185
262
  }
186
263
 
187
264
  /** One Conversions API event, user data already hashed by the host. */
@@ -427,6 +504,124 @@ function mapAccount(raw: unknown): GraphAdAccount | null {
427
504
  };
428
505
  }
429
506
 
507
+ /** Meta's times (`2026-10-09T12:34:56+0000`) in milliseconds, or null. */
508
+ export function graphTime(value: unknown): number | null {
509
+ const raw = text(value);
510
+ if (!raw) return null;
511
+ const parsed = Date.parse(raw.replace(/([+-]\d{2})(\d{2})$/u, "$1:$2"));
512
+ return Number.isFinite(parsed) ? parsed : null;
513
+ }
514
+
515
+ const GRAPH_ID = /^\d{1,32}$/u;
516
+
517
+ function mapPage(raw: unknown): GraphPage | null {
518
+ if (!isRecord(raw)) return null;
519
+ const pageId = text(raw.id);
520
+ if (!pageId || !GRAPH_ID.test(pageId)) return null;
521
+ return {
522
+ pageId,
523
+ name: text(raw.name) ?? pageId,
524
+ accessToken: text(raw.access_token),
525
+ tasks: Array.isArray(raw.tasks)
526
+ ? raw.tasks.filter((task): task is string => typeof task === "string")
527
+ : [],
528
+ };
529
+ }
530
+
531
+ function mapQuestion(raw: unknown): GraphFormQuestion | null {
532
+ if (!isRecord(raw)) return null;
533
+ const key = text(raw.key);
534
+ if (!key) return null;
535
+ const options = Array.isArray(raw.options)
536
+ ? raw.options.flatMap((option) => {
537
+ if (!isRecord(option)) return [];
538
+ const optionKey = text(option.key) ?? text(option.value);
539
+ const value = text(option.value) ?? optionKey;
540
+ return optionKey && value ? [{ key: optionKey, value }] : [];
541
+ })
542
+ : [];
543
+ return {
544
+ key,
545
+ label: text(raw.label) ?? key,
546
+ type: text(raw.type) ?? "CUSTOM",
547
+ ...(options.length > 0 ? { options } : {}),
548
+ };
549
+ }
550
+
551
+ /**
552
+ * Whether the form's end screen sends the lead to WhatsApp, from its button
553
+ * type; null when Meta does not describe the end screen's button.
554
+ */
555
+ export function whatsappButtonOf(thankYouPage: unknown): boolean | null {
556
+ if (!isRecord(thankYouPage)) return null;
557
+ const type = text(thankYouPage.button_type);
558
+ if (!type) return null;
559
+ return /WHATSAPP/iu.test(type);
560
+ }
561
+
562
+ function mapForm(raw: unknown): GraphForm | null {
563
+ if (!isRecord(raw)) return null;
564
+ const formId = text(raw.id);
565
+ if (!formId || !GRAPH_ID.test(formId)) return null;
566
+ return {
567
+ formId,
568
+ name: text(raw.name) ?? formId,
569
+ status: text(raw.status)?.toUpperCase() ?? "ACTIVE",
570
+ questions: Array.isArray(raw.questions)
571
+ ? raw.questions.flatMap((question) => {
572
+ const mapped = mapQuestion(question);
573
+ return mapped ? [mapped] : [];
574
+ })
575
+ : [],
576
+ whatsappButton: whatsappButtonOf(raw.thank_you_page),
577
+ createdTime: graphTime(raw.created_time),
578
+ };
579
+ }
580
+
581
+ function mapLead(raw: unknown): GraphLead | null {
582
+ if (!isRecord(raw)) return null;
583
+ const leadgenId = text(raw.id);
584
+ const createdTime = graphTime(raw.created_time);
585
+ if (!leadgenId || !GRAPH_ID.test(leadgenId) || createdTime === null) {
586
+ return null;
587
+ }
588
+ const id = (value: unknown) => {
589
+ const found = text(typeof value === "number" ? String(value) : value);
590
+ return found && GRAPH_ID.test(found) ? found : null;
591
+ };
592
+ return {
593
+ leadgenId,
594
+ createdTime,
595
+ formId: id(raw.form_id),
596
+ adId: id(raw.ad_id),
597
+ adsetId: id(raw.adset_id),
598
+ campaignId: id(raw.campaign_id),
599
+ platform: text(raw.platform)?.slice(0, 16) ?? null,
600
+ isOrganic: raw.is_organic === true,
601
+ fields: Array.isArray(raw.field_data)
602
+ ? raw.field_data.flatMap((field) => {
603
+ if (!isRecord(field)) return [];
604
+ const name = text(field.name);
605
+ if (!name) return [];
606
+ const values = Array.isArray(field.values)
607
+ ? field.values.filter(
608
+ (value): value is string => typeof value === "string",
609
+ )
610
+ : [];
611
+ return [{ name, values }];
612
+ })
613
+ : [],
614
+ };
615
+ }
616
+
617
+ const FORM_FIELDS =
618
+ "id,name,status,questions,thank_you_page{button_type},created_time";
619
+ const LEAD_FIELDS =
620
+ "id,created_time,field_data,ad_id,adset_id,campaign_id,form_id,platform,is_organic";
621
+ const MAX_GRAPH_PAGES = 200;
622
+ const MAX_FORMS = 500;
623
+ const MAX_FORM_LEADS = 2_000;
624
+
430
625
  export function actId(adAccountId: string): string {
431
626
  return adAccountId.startsWith("act_") ? adAccountId : `act_${adAccountId}`;
432
627
  }
@@ -908,6 +1103,138 @@ function realMetaGraph(fetchImpl: Fetch): MetaGraph {
908
1103
  fbtraceId: text(body.fbtrace_id),
909
1104
  };
910
1105
  },
1106
+
1107
+ async pages(token) {
1108
+ const { items, complete } = await pages(
1109
+ url("me/accounts", {
1110
+ fields: "id,name,access_token,tasks",
1111
+ limit: "100",
1112
+ }),
1113
+ token,
1114
+ mapPage,
1115
+ MAX_GRAPH_PAGES,
1116
+ );
1117
+ return { pages: items, complete };
1118
+ },
1119
+
1120
+ async leadAccess(token, app) {
1121
+ // A login's granular scopes say which Pages `leads_retrieval` covers;
1122
+ // without the app's token, the token's own permissions say whether
1123
+ // it has it at all.
1124
+ if (app) {
1125
+ try {
1126
+ const { body } = await call(
1127
+ url("debug_token", { input_token: token }),
1128
+ `${app.appId}|${app.appSecret}`,
1129
+ );
1130
+ const data = isRecord(body.data) ? body.data : {};
1131
+ const granular = Array.isArray(data.granular_scopes)
1132
+ ? data.granular_scopes.filter(isRecord)
1133
+ : [];
1134
+ const leads = granular.find(
1135
+ (scope) => scope.scope === "leads_retrieval",
1136
+ );
1137
+ if (leads) {
1138
+ const targets = Array.isArray(leads.target_ids)
1139
+ ? leads.target_ids.map(String).filter((id) => GRAPH_ID.test(id))
1140
+ : null;
1141
+ return targets === null || targets.length === 0
1142
+ ? { all: true, pageIds: [] }
1143
+ : { all: false, pageIds: targets };
1144
+ }
1145
+ const scopes = Array.isArray(data.scopes) ? data.scopes : null;
1146
+ if (scopes) {
1147
+ return { all: scopes.includes("leads_retrieval"), pageIds: [] };
1148
+ }
1149
+ } catch (error) {
1150
+ if (!(error instanceof MetaGraphError)) throw error;
1151
+ }
1152
+ }
1153
+ try {
1154
+ const { body } = await call(url("me/permissions", {}), token);
1155
+ const rows = Array.isArray(body.data) ? body.data.filter(isRecord) : [];
1156
+ if (rows.length === 0) return null;
1157
+ return {
1158
+ all: rows.some(
1159
+ (row) =>
1160
+ row.permission === "leads_retrieval" && row.status === "granted",
1161
+ ),
1162
+ pageIds: [],
1163
+ };
1164
+ } catch (error) {
1165
+ if (error instanceof MetaGraphError) return null;
1166
+ throw error;
1167
+ }
1168
+ },
1169
+
1170
+ async subscribePage(pageToken, pageId) {
1171
+ const { body } = await call(
1172
+ url(`${pageId}/subscribed_apps`, { subscribed_fields: "leadgen" }),
1173
+ pageToken,
1174
+ { subscribed_fields: "leadgen" },
1175
+ );
1176
+ // A 200 is not enough: Meta says `success: true` when it subscribed.
1177
+ if (body.success !== true) {
1178
+ throw new MetaGraphError(
1179
+ "other",
1180
+ "Meta did not confirm the Page's leadgen subscription",
1181
+ );
1182
+ }
1183
+ },
1184
+
1185
+ async leadForms(pageToken, pageId) {
1186
+ const { items, usage, complete } = await pages(
1187
+ url(`${pageId}/leadgen_forms`, {
1188
+ fields: FORM_FIELDS,
1189
+ limit: "100",
1190
+ }),
1191
+ pageToken,
1192
+ mapForm,
1193
+ MAX_FORMS,
1194
+ );
1195
+ return { forms: items, usage, complete };
1196
+ },
1197
+
1198
+ async lead(pageToken, leadgenId) {
1199
+ const { body } = await call(
1200
+ url(leadgenId, { fields: LEAD_FIELDS }),
1201
+ pageToken,
1202
+ );
1203
+ const lead = mapLead(body);
1204
+ if (!lead) {
1205
+ throw new MetaGraphError("other", "Meta returned an unreadable lead");
1206
+ }
1207
+ return lead;
1208
+ },
1209
+
1210
+ async formLeads(pageToken, formId, sinceSec, untilSec) {
1211
+ const { items, usage, complete } = await pages(
1212
+ url(`${formId}/leads`, {
1213
+ fields: LEAD_FIELDS,
1214
+ filtering: JSON.stringify([
1215
+ {
1216
+ field: "time_created",
1217
+ operator: "GREATER_THAN",
1218
+ value: Math.max(Math.floor(sinceSec), 0),
1219
+ },
1220
+ ...(untilSec === undefined
1221
+ ? []
1222
+ : [
1223
+ {
1224
+ field: "time_created",
1225
+ operator: "LESS_THAN",
1226
+ value: Math.max(Math.floor(untilSec), 0),
1227
+ },
1228
+ ]),
1229
+ ]),
1230
+ limit: String(PAGE_LIMIT),
1231
+ }),
1232
+ pageToken,
1233
+ mapLead,
1234
+ MAX_FORM_LEADS,
1235
+ );
1236
+ return { leads: items, usage, complete };
1237
+ },
911
1238
  };
912
1239
  }
913
1240