placementt-core 1.400.1125 → 1.400.1127

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.
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,56 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const outreach_1 = require("../outreach");
4
+ /** One valid request and response per outreach contract, so a schema change that
5
+ * breaks the web or backend payload fails here first. */
6
+ const samples = {
7
+ "outreach-upsertContacts": {
8
+ request: { rows: [{ urn: "100000", affiliation: "Test School", email: "head@test.sch.uk" }], dryRun: true },
9
+ response: { added: 1, updated: 0, keptStatus: 0, noEmail: 0, invalidEmail: 0, duplicateEmailInUpload: 0, skipped: [{ row: 2, reason: "No URN" }] },
10
+ },
11
+ "outreach-setStatus": {
12
+ request: { urn: 100000, status: "deferred", deferUntil: "2027-09-28", reason: "Next year" },
13
+ response: { urn: 100000, status: "deferred", brevoSynced: true, affectedUrns: [100000] },
14
+ },
15
+ "outreach-convertToLead": {
16
+ request: { urn: 100000, lead: { forename: "Jo", surname: "Bloggs", email: "jo@test.sch.uk", organisation: "Test School", assignee: "uid1" } },
17
+ response: { leadId: "lead1" },
18
+ },
19
+ "outreach-getBrevoContact": {
20
+ request: { urn: 100000 },
21
+ response: { found: true, contactId: 12, listIds: [3], emailBlacklisted: false, attributes: { URN: 100000 }, brevoUrl: "https://app.brevo.com/contact/index/12" },
22
+ },
23
+ "outreach-setupBrevo": {
24
+ request: {},
25
+ response: { marketingListId: 3, customersListId: 80, webhookId: 5, attributesCreated: ["URN"] },
26
+ },
27
+ "outreach-migrateExisting": {
28
+ request: {},
29
+ response: { initialised: 10, linkedToLeads: 1, linkedToCustomers: 1, importedToBrevo: 8 },
30
+ },
31
+ "outreach-backfillFromBrevo": {
32
+ request: { batchSize: 400 },
33
+ response: { processed: 400, total: 1000, done: false, cursorUrn: 100400 },
34
+ },
35
+ "outreach-runSweep": {
36
+ request: { step: "reconcile" },
37
+ response: { undeferred: 0, campaignsSynced: 0, domainMatches: 0, attributesPushed: 0, reconcile: { marketingOnlyInBrevo: 0, missingFromBrevo: 0 } },
38
+ },
39
+ };
40
+ describe("outreach contracts", () => {
41
+ for (const [name, sample] of Object.entries(samples)) {
42
+ it(`parses a sample ${name}`, () => {
43
+ const contract = outreach_1.outreachCallables[name];
44
+ expect(contract.request.safeParse(sample.request).success).toBe(true);
45
+ expect(contract.response.safeParse(sample.response).success).toBe(true);
46
+ });
47
+ }
48
+ it("rejects a deferral date that is not YYYY-MM-DD", () => {
49
+ expect(outreach_1.outreachCallables["outreach-setStatus"].request
50
+ .safeParse({ urn: 1, status: "deferred", deferUntil: "28/09/2027" }).success).toBe(false);
51
+ });
52
+ it("rejects an empty upload", () => {
53
+ expect(outreach_1.outreachCallables["outreach-upsertContacts"].request.safeParse({ rows: [] }).success).toBe(false);
54
+ });
55
+ });
56
+ //# sourceMappingURL=outreach.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"outreach.test.js","sourceRoot":"","sources":["../../../src/callables/__tests__/outreach.test.ts"],"names":[],"mappings":"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"}
@@ -30,8 +30,8 @@ export declare const eventsCallables: {
30
30
  "address-line1"?: string | undefined;
31
31
  "address-line2"?: string | undefined;
32
32
  country?: string | undefined;
33
- locality?: string | undefined;
34
33
  postal_code?: string | undefined;
34
+ locality?: string | undefined;
35
35
  geohash?: string | undefined;
36
36
  }, {
37
37
  id: string;
@@ -39,8 +39,8 @@ export declare const eventsCallables: {
39
39
  "address-line1"?: string | undefined;
40
40
  "address-line2"?: string | undefined;
41
41
  country?: string | undefined;
42
- locality?: string | undefined;
43
42
  postal_code?: string | undefined;
43
+ locality?: string | undefined;
44
44
  geohash?: string | undefined;
45
45
  }>, "many">;
46
46
  };
@@ -13,3 +13,4 @@ export * from "./e2e";
13
13
  export * from "./wonde";
14
14
  export * from "./misSync";
15
15
  export * from "./userManagement";
16
+ export * from "./outreach";
@@ -29,4 +29,5 @@ __exportStar(require("./e2e"), exports);
29
29
  __exportStar(require("./wonde"), exports);
30
30
  __exportStar(require("./misSync"), exports);
31
31
  __exportStar(require("./userManagement"), exports);
32
+ __exportStar(require("./outreach"), exports);
32
33
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/callables/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,0CAAwB;AACxB,6CAA2B;AAC3B,uDAAqC;AACrC,8CAA4B;AAC5B,2CAAyB;AACzB,6CAA2B;AAC3B,oDAAkC;AAClC,uCAAqB;AACrB,oDAAkC;AAClC,4CAA0B;AAC1B,oDAAkC;AAClC,wCAAsB;AACtB,0CAAwB;AACxB,4CAA0B;AAC1B,mDAAiC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/callables/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,0CAAwB;AACxB,6CAA2B;AAC3B,uDAAqC;AACrC,8CAA4B;AAC5B,2CAAyB;AACzB,6CAA2B;AAC3B,oDAAkC;AAClC,uCAAqB;AACrB,oDAAkC;AAClC,4CAA0B;AAC1B,oDAAkC;AAClC,wCAAsB;AACtB,0CAAwB;AACxB,4CAA0B;AAC1B,mDAAiC;AACjC,6CAA2B"}
@@ -0,0 +1,326 @@
1
+ import { z } from "zod";
2
+ export declare const outreachCallables: {
3
+ "outreach-upsertContacts": {
4
+ request: z.ZodObject<{
5
+ rows: z.ZodArray<z.ZodRecord<z.ZodString, z.ZodOptional<z.ZodString>>, "many">;
6
+ /** Count what would happen without writing to Firestore or Brevo. */
7
+ dryRun: z.ZodOptional<z.ZodBoolean>;
8
+ }, "strip", z.ZodTypeAny, {
9
+ rows: Record<string, string | undefined>[];
10
+ dryRun?: boolean | undefined;
11
+ }, {
12
+ rows: Record<string, string | undefined>[];
13
+ dryRun?: boolean | undefined;
14
+ }>;
15
+ response: z.ZodObject<{
16
+ added: z.ZodNumber;
17
+ updated: z.ZodNumber;
18
+ /** Existing contacts not in the pool: data updated, status left alone. */
19
+ keptStatus: z.ZodNumber;
20
+ noEmail: z.ZodNumber;
21
+ invalidEmail: z.ZodNumber;
22
+ duplicateEmailInUpload: z.ZodNumber;
23
+ skipped: z.ZodArray<z.ZodObject<{
24
+ row: z.ZodNumber;
25
+ urn: z.ZodOptional<z.ZodString>;
26
+ reason: z.ZodString;
27
+ }, "strip", z.ZodTypeAny, {
28
+ reason: string;
29
+ row: number;
30
+ urn?: string | undefined;
31
+ }, {
32
+ reason: string;
33
+ row: number;
34
+ urn?: string | undefined;
35
+ }>, "many">;
36
+ brevoProcessId: z.ZodOptional<z.ZodNumber>;
37
+ brevoError: z.ZodOptional<z.ZodString>;
38
+ }, "strip", z.ZodTypeAny, {
39
+ skipped: {
40
+ reason: string;
41
+ row: number;
42
+ urn?: string | undefined;
43
+ }[];
44
+ added: number;
45
+ noEmail: number;
46
+ updated: number;
47
+ keptStatus: number;
48
+ invalidEmail: number;
49
+ duplicateEmailInUpload: number;
50
+ brevoProcessId?: number | undefined;
51
+ brevoError?: string | undefined;
52
+ }, {
53
+ skipped: {
54
+ reason: string;
55
+ row: number;
56
+ urn?: string | undefined;
57
+ }[];
58
+ added: number;
59
+ noEmail: number;
60
+ updated: number;
61
+ keptStatus: number;
62
+ invalidEmail: number;
63
+ duplicateEmailInUpload: number;
64
+ brevoProcessId?: number | undefined;
65
+ brevoError?: string | undefined;
66
+ }>;
67
+ };
68
+ "outreach-setStatus": {
69
+ request: z.ZodObject<{
70
+ urn: z.ZodNumber;
71
+ status: z.ZodEnum<["pool", "lead", "deferred", "customer", "doNotContact"]>;
72
+ reason: z.ZodOptional<z.ZodString>;
73
+ /** Required when status is deferred. */
74
+ deferUntil: z.ZodOptional<z.ZodString>;
75
+ /** Customer institute, when status is customer. */
76
+ oId: z.ZodOptional<z.ZodString>;
77
+ /** Also apply to every URN in `outreach.sharedEmailUrns`. */
78
+ applyToSharedEmail: z.ZodOptional<z.ZodBoolean>;
79
+ }, "strip", z.ZodTypeAny, {
80
+ status: "pool" | "lead" | "deferred" | "customer" | "doNotContact";
81
+ urn: number;
82
+ reason?: string | undefined;
83
+ oId?: string | undefined;
84
+ deferUntil?: string | undefined;
85
+ applyToSharedEmail?: boolean | undefined;
86
+ }, {
87
+ status: "pool" | "lead" | "deferred" | "customer" | "doNotContact";
88
+ urn: number;
89
+ reason?: string | undefined;
90
+ oId?: string | undefined;
91
+ deferUntil?: string | undefined;
92
+ applyToSharedEmail?: boolean | undefined;
93
+ }>;
94
+ response: z.ZodObject<{
95
+ urn: z.ZodNumber;
96
+ status: z.ZodEnum<["pool", "lead", "deferred", "customer", "doNotContact"]>;
97
+ brevoSynced: z.ZodBoolean;
98
+ affectedUrns: z.ZodArray<z.ZodNumber, "many">;
99
+ }, "strip", z.ZodTypeAny, {
100
+ status: "pool" | "lead" | "deferred" | "customer" | "doNotContact";
101
+ urn: number;
102
+ brevoSynced: boolean;
103
+ affectedUrns: number[];
104
+ }, {
105
+ status: "pool" | "lead" | "deferred" | "customer" | "doNotContact";
106
+ urn: number;
107
+ brevoSynced: boolean;
108
+ affectedUrns: number[];
109
+ }>;
110
+ };
111
+ "outreach-convertToLead": {
112
+ request: z.ZodObject<{
113
+ urn: z.ZodNumber;
114
+ lead: z.ZodObject<{
115
+ forename: z.ZodOptional<z.ZodString>;
116
+ surname: z.ZodOptional<z.ZodString>;
117
+ email: z.ZodString;
118
+ organisation: z.ZodString;
119
+ wex: z.ZodOptional<z.ZodBoolean>;
120
+ events: z.ZodOptional<z.ZodBoolean>;
121
+ docCheck: z.ZodOptional<z.ZodBoolean>;
122
+ assignee: z.ZodString;
123
+ personalNotes: z.ZodOptional<z.ZodArray<z.ZodAny, "many">>;
124
+ generalNotes: z.ZodOptional<z.ZodArray<z.ZodAny, "many">>;
125
+ }, "strip", z.ZodTypeAny, {
126
+ email: string;
127
+ organisation: string;
128
+ assignee: string;
129
+ wex?: boolean | undefined;
130
+ events?: boolean | undefined;
131
+ docCheck?: boolean | undefined;
132
+ forename?: string | undefined;
133
+ surname?: string | undefined;
134
+ personalNotes?: any[] | undefined;
135
+ generalNotes?: any[] | undefined;
136
+ }, {
137
+ email: string;
138
+ organisation: string;
139
+ assignee: string;
140
+ wex?: boolean | undefined;
141
+ events?: boolean | undefined;
142
+ docCheck?: boolean | undefined;
143
+ forename?: string | undefined;
144
+ surname?: string | undefined;
145
+ personalNotes?: any[] | undefined;
146
+ generalNotes?: any[] | undefined;
147
+ }>;
148
+ }, "strip", z.ZodTypeAny, {
149
+ lead: {
150
+ email: string;
151
+ organisation: string;
152
+ assignee: string;
153
+ wex?: boolean | undefined;
154
+ events?: boolean | undefined;
155
+ docCheck?: boolean | undefined;
156
+ forename?: string | undefined;
157
+ surname?: string | undefined;
158
+ personalNotes?: any[] | undefined;
159
+ generalNotes?: any[] | undefined;
160
+ };
161
+ urn: number;
162
+ }, {
163
+ lead: {
164
+ email: string;
165
+ organisation: string;
166
+ assignee: string;
167
+ wex?: boolean | undefined;
168
+ events?: boolean | undefined;
169
+ docCheck?: boolean | undefined;
170
+ forename?: string | undefined;
171
+ surname?: string | undefined;
172
+ personalNotes?: any[] | undefined;
173
+ generalNotes?: any[] | undefined;
174
+ };
175
+ urn: number;
176
+ }>;
177
+ response: z.ZodObject<{
178
+ leadId: z.ZodString;
179
+ }, "strip", z.ZodTypeAny, {
180
+ leadId: string;
181
+ }, {
182
+ leadId: string;
183
+ }>;
184
+ };
185
+ "outreach-getBrevoContact": {
186
+ request: z.ZodObject<{
187
+ urn: z.ZodNumber;
188
+ }, "strip", z.ZodTypeAny, {
189
+ urn: number;
190
+ }, {
191
+ urn: number;
192
+ }>;
193
+ response: z.ZodObject<{
194
+ found: z.ZodBoolean;
195
+ contactId: z.ZodOptional<z.ZodNumber>;
196
+ listIds: z.ZodArray<z.ZodNumber, "many">;
197
+ emailBlacklisted: z.ZodOptional<z.ZodBoolean>;
198
+ attributes: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
199
+ brevoUrl: z.ZodOptional<z.ZodString>;
200
+ }, "strip", z.ZodTypeAny, {
201
+ found: boolean;
202
+ listIds: number[];
203
+ attributes?: Record<string, unknown> | undefined;
204
+ contactId?: number | undefined;
205
+ emailBlacklisted?: boolean | undefined;
206
+ brevoUrl?: string | undefined;
207
+ }, {
208
+ found: boolean;
209
+ listIds: number[];
210
+ attributes?: Record<string, unknown> | undefined;
211
+ contactId?: number | undefined;
212
+ emailBlacklisted?: boolean | undefined;
213
+ brevoUrl?: string | undefined;
214
+ }>;
215
+ };
216
+ "outreach-setupBrevo": {
217
+ request: z.ZodObject<{}, "strip", z.ZodTypeAny, {}, {}>;
218
+ response: z.ZodObject<{
219
+ marketingListId: z.ZodNumber;
220
+ customersListId: z.ZodNumber;
221
+ webhookId: z.ZodNumber;
222
+ attributesCreated: z.ZodArray<z.ZodString, "many">;
223
+ }, "strip", z.ZodTypeAny, {
224
+ marketingListId: number;
225
+ customersListId: number;
226
+ webhookId: number;
227
+ attributesCreated: string[];
228
+ }, {
229
+ marketingListId: number;
230
+ customersListId: number;
231
+ webhookId: number;
232
+ attributesCreated: string[];
233
+ }>;
234
+ };
235
+ "outreach-migrateExisting": {
236
+ request: z.ZodObject<{}, "strip", z.ZodTypeAny, {}, {}>;
237
+ response: z.ZodObject<{
238
+ initialised: z.ZodNumber;
239
+ linkedToLeads: z.ZodNumber;
240
+ linkedToCustomers: z.ZodNumber;
241
+ importedToBrevo: z.ZodNumber;
242
+ }, "strip", z.ZodTypeAny, {
243
+ initialised: number;
244
+ linkedToLeads: number;
245
+ linkedToCustomers: number;
246
+ importedToBrevo: number;
247
+ }, {
248
+ initialised: number;
249
+ linkedToLeads: number;
250
+ linkedToCustomers: number;
251
+ importedToBrevo: number;
252
+ }>;
253
+ };
254
+ "outreach-backfillFromBrevo": {
255
+ request: z.ZodObject<{
256
+ batchSize: z.ZodOptional<z.ZodNumber>;
257
+ /** Restart from the first URN. */
258
+ reset: z.ZodOptional<z.ZodBoolean>;
259
+ }, "strip", z.ZodTypeAny, {
260
+ reset?: boolean | undefined;
261
+ batchSize?: number | undefined;
262
+ }, {
263
+ reset?: boolean | undefined;
264
+ batchSize?: number | undefined;
265
+ }>;
266
+ response: z.ZodObject<{
267
+ processed: z.ZodNumber;
268
+ total: z.ZodNumber;
269
+ done: z.ZodBoolean;
270
+ cursorUrn: z.ZodOptional<z.ZodNumber>;
271
+ }, "strip", z.ZodTypeAny, {
272
+ total: number;
273
+ done: boolean;
274
+ processed: number;
275
+ cursorUrn?: number | undefined;
276
+ }, {
277
+ total: number;
278
+ done: boolean;
279
+ processed: number;
280
+ cursorUrn?: number | undefined;
281
+ }>;
282
+ };
283
+ "outreach-runSweep": {
284
+ request: z.ZodObject<{
285
+ step: z.ZodOptional<z.ZodEnum<["all", "deferrals", "campaigns", "domainMatch", "brevoAttributes", "reconcile"]>>;
286
+ }, "strip", z.ZodTypeAny, {
287
+ step?: "all" | "reconcile" | "deferrals" | "campaigns" | "domainMatch" | "brevoAttributes" | undefined;
288
+ }, {
289
+ step?: "all" | "reconcile" | "deferrals" | "campaigns" | "domainMatch" | "brevoAttributes" | undefined;
290
+ }>;
291
+ response: z.ZodObject<{
292
+ undeferred: z.ZodNumber;
293
+ campaignsSynced: z.ZodNumber;
294
+ domainMatches: z.ZodNumber;
295
+ attributesPushed: z.ZodNumber;
296
+ reconcile: z.ZodOptional<z.ZodObject<{
297
+ marketingOnlyInBrevo: z.ZodNumber;
298
+ missingFromBrevo: z.ZodNumber;
299
+ }, "strip", z.ZodTypeAny, {
300
+ marketingOnlyInBrevo: number;
301
+ missingFromBrevo: number;
302
+ }, {
303
+ marketingOnlyInBrevo: number;
304
+ missingFromBrevo: number;
305
+ }>>;
306
+ }, "strip", z.ZodTypeAny, {
307
+ undeferred: number;
308
+ campaignsSynced: number;
309
+ domainMatches: number;
310
+ attributesPushed: number;
311
+ reconcile?: {
312
+ marketingOnlyInBrevo: number;
313
+ missingFromBrevo: number;
314
+ } | undefined;
315
+ }, {
316
+ undeferred: number;
317
+ campaignsSynced: number;
318
+ domainMatches: number;
319
+ attributesPushed: number;
320
+ reconcile?: {
321
+ marketingOnlyInBrevo: number;
322
+ missingFromBrevo: number;
323
+ } | undefined;
324
+ }>;
325
+ };
326
+ };
@@ -0,0 +1,131 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.outreachCallables = void 0;
4
+ const zod_1 = require("zod");
5
+ /**
6
+ * Contracts for the outreach pipeline (`Placementt-Backend/functions/src/outreach`).
7
+ *
8
+ * `dfeSchoolContacts` is the single source of truth for who is in the outreach pool.
9
+ * Every write to `outreach` goes through these callables so Firestore and the Brevo
10
+ * lists never drift. All of them are admin-only.
11
+ */
12
+ const outreachStatus = zod_1.z.enum(["pool", "lead", "deferred", "customer", "doNotContact"]);
13
+ /** A pasted spreadsheet row keyed by DFE_CONTACT_UPLOAD_COLUMNS. Raw strings; the backend coerces. */
14
+ const uploadRow = zod_1.z.record(zod_1.z.string(), zod_1.z.string().optional());
15
+ const isoDate = zod_1.z.string().regex(/^\d{4}-\d{2}-\d{2}$/, "Use a YYYY-MM-DD date.");
16
+ exports.outreachCallables = {
17
+ "outreach-upsertContacts": {
18
+ request: zod_1.z.object({
19
+ rows: zod_1.z.array(uploadRow).min(1).max(2000),
20
+ /** Count what would happen without writing to Firestore or Brevo. */
21
+ dryRun: zod_1.z.boolean().optional(),
22
+ }),
23
+ response: zod_1.z.object({
24
+ added: zod_1.z.number(),
25
+ updated: zod_1.z.number(),
26
+ /** Existing contacts not in the pool: data updated, status left alone. */
27
+ keptStatus: zod_1.z.number(),
28
+ noEmail: zod_1.z.number(),
29
+ invalidEmail: zod_1.z.number(),
30
+ duplicateEmailInUpload: zod_1.z.number(),
31
+ skipped: zod_1.z.array(zod_1.z.object({ row: zod_1.z.number(), urn: zod_1.z.string().optional(), reason: zod_1.z.string() })),
32
+ brevoProcessId: zod_1.z.number().optional(),
33
+ brevoError: zod_1.z.string().optional(),
34
+ }),
35
+ },
36
+ "outreach-setStatus": {
37
+ request: zod_1.z.object({
38
+ urn: zod_1.z.number(),
39
+ status: outreachStatus,
40
+ reason: zod_1.z.string().optional(),
41
+ /** Required when status is deferred. */
42
+ deferUntil: isoDate.optional(),
43
+ /** Customer institute, when status is customer. */
44
+ oId: zod_1.z.string().optional(),
45
+ /** Also apply to every URN in `outreach.sharedEmailUrns`. */
46
+ applyToSharedEmail: zod_1.z.boolean().optional(),
47
+ }),
48
+ response: zod_1.z.object({
49
+ urn: zod_1.z.number(),
50
+ status: outreachStatus,
51
+ brevoSynced: zod_1.z.boolean(),
52
+ affectedUrns: zod_1.z.array(zod_1.z.number()),
53
+ }),
54
+ },
55
+ "outreach-convertToLead": {
56
+ request: zod_1.z.object({
57
+ urn: zod_1.z.number(),
58
+ lead: zod_1.z.object({
59
+ forename: zod_1.z.string().optional(),
60
+ surname: zod_1.z.string().optional(),
61
+ email: zod_1.z.string().email(),
62
+ organisation: zod_1.z.string(),
63
+ wex: zod_1.z.boolean().optional(),
64
+ events: zod_1.z.boolean().optional(),
65
+ docCheck: zod_1.z.boolean().optional(),
66
+ assignee: zod_1.z.string(),
67
+ personalNotes: zod_1.z.array(zod_1.z.any()).optional(),
68
+ generalNotes: zod_1.z.array(zod_1.z.any()).optional(),
69
+ }),
70
+ }),
71
+ response: zod_1.z.object({ leadId: zod_1.z.string() }),
72
+ },
73
+ "outreach-getBrevoContact": {
74
+ request: zod_1.z.object({ urn: zod_1.z.number() }),
75
+ response: zod_1.z.object({
76
+ found: zod_1.z.boolean(),
77
+ contactId: zod_1.z.number().optional(),
78
+ listIds: zod_1.z.array(zod_1.z.number()),
79
+ emailBlacklisted: zod_1.z.boolean().optional(),
80
+ attributes: zod_1.z.record(zod_1.z.string(), zod_1.z.unknown()).optional(),
81
+ brevoUrl: zod_1.z.string().optional(),
82
+ }),
83
+ },
84
+ "outreach-setupBrevo": {
85
+ request: zod_1.z.object({}),
86
+ response: zod_1.z.object({
87
+ marketingListId: zod_1.z.number(),
88
+ customersListId: zod_1.z.number(),
89
+ webhookId: zod_1.z.number(),
90
+ attributesCreated: zod_1.z.array(zod_1.z.string()),
91
+ }),
92
+ },
93
+ "outreach-migrateExisting": {
94
+ request: zod_1.z.object({}),
95
+ response: zod_1.z.object({
96
+ initialised: zod_1.z.number(),
97
+ linkedToLeads: zod_1.z.number(),
98
+ linkedToCustomers: zod_1.z.number(),
99
+ importedToBrevo: zod_1.z.number(),
100
+ }),
101
+ },
102
+ "outreach-backfillFromBrevo": {
103
+ request: zod_1.z.object({
104
+ batchSize: zod_1.z.number().int().min(1).max(400).optional(),
105
+ /** Restart from the first URN. */
106
+ reset: zod_1.z.boolean().optional(),
107
+ }),
108
+ response: zod_1.z.object({
109
+ processed: zod_1.z.number(),
110
+ total: zod_1.z.number(),
111
+ done: zod_1.z.boolean(),
112
+ cursorUrn: zod_1.z.number().optional(),
113
+ }),
114
+ },
115
+ "outreach-runSweep": {
116
+ request: zod_1.z.object({
117
+ step: zod_1.z.enum(["all", "deferrals", "campaigns", "domainMatch", "brevoAttributes", "reconcile"]).optional(),
118
+ }),
119
+ response: zod_1.z.object({
120
+ undeferred: zod_1.z.number(),
121
+ campaignsSynced: zod_1.z.number(),
122
+ domainMatches: zod_1.z.number(),
123
+ attributesPushed: zod_1.z.number(),
124
+ reconcile: zod_1.z.object({
125
+ marketingOnlyInBrevo: zod_1.z.number(),
126
+ missingFromBrevo: zod_1.z.number(),
127
+ }).optional(),
128
+ }),
129
+ },
130
+ };
131
+ //# sourceMappingURL=outreach.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"outreach.js","sourceRoot":"","sources":["../../src/callables/outreach.ts"],"names":[],"mappings":"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"}