placementt-core 1.400.1126 → 1.400.1128

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,330 @@
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
+ /** Lead to link, e.g. when confirming a domain match. */
78
+ leadId: z.ZodOptional<z.ZodString>;
79
+ /** Also apply to every URN in `outreach.sharedEmailUrns`. */
80
+ applyToSharedEmail: z.ZodOptional<z.ZodBoolean>;
81
+ }, "strip", z.ZodTypeAny, {
82
+ status: "pool" | "lead" | "deferred" | "customer" | "doNotContact";
83
+ urn: number;
84
+ reason?: string | undefined;
85
+ oId?: string | undefined;
86
+ deferUntil?: string | undefined;
87
+ leadId?: string | undefined;
88
+ applyToSharedEmail?: boolean | undefined;
89
+ }, {
90
+ status: "pool" | "lead" | "deferred" | "customer" | "doNotContact";
91
+ urn: number;
92
+ reason?: string | undefined;
93
+ oId?: string | undefined;
94
+ deferUntil?: string | undefined;
95
+ leadId?: string | undefined;
96
+ applyToSharedEmail?: boolean | undefined;
97
+ }>;
98
+ response: z.ZodObject<{
99
+ urn: z.ZodNumber;
100
+ status: z.ZodEnum<["pool", "lead", "deferred", "customer", "doNotContact"]>;
101
+ brevoSynced: z.ZodBoolean;
102
+ affectedUrns: z.ZodArray<z.ZodNumber, "many">;
103
+ }, "strip", z.ZodTypeAny, {
104
+ status: "pool" | "lead" | "deferred" | "customer" | "doNotContact";
105
+ urn: number;
106
+ brevoSynced: boolean;
107
+ affectedUrns: number[];
108
+ }, {
109
+ status: "pool" | "lead" | "deferred" | "customer" | "doNotContact";
110
+ urn: number;
111
+ brevoSynced: boolean;
112
+ affectedUrns: number[];
113
+ }>;
114
+ };
115
+ "outreach-convertToLead": {
116
+ request: z.ZodObject<{
117
+ urn: z.ZodNumber;
118
+ lead: z.ZodObject<{
119
+ forename: z.ZodOptional<z.ZodString>;
120
+ surname: z.ZodOptional<z.ZodString>;
121
+ email: z.ZodString;
122
+ organisation: z.ZodString;
123
+ wex: z.ZodOptional<z.ZodBoolean>;
124
+ events: z.ZodOptional<z.ZodBoolean>;
125
+ docCheck: z.ZodOptional<z.ZodBoolean>;
126
+ assignee: z.ZodString;
127
+ personalNotes: z.ZodOptional<z.ZodArray<z.ZodAny, "many">>;
128
+ generalNotes: z.ZodOptional<z.ZodArray<z.ZodAny, "many">>;
129
+ }, "strip", z.ZodTypeAny, {
130
+ email: string;
131
+ organisation: string;
132
+ assignee: string;
133
+ wex?: boolean | undefined;
134
+ events?: boolean | undefined;
135
+ docCheck?: boolean | undefined;
136
+ forename?: string | undefined;
137
+ surname?: string | undefined;
138
+ personalNotes?: any[] | undefined;
139
+ generalNotes?: any[] | undefined;
140
+ }, {
141
+ email: string;
142
+ organisation: string;
143
+ assignee: string;
144
+ wex?: boolean | undefined;
145
+ events?: boolean | undefined;
146
+ docCheck?: boolean | undefined;
147
+ forename?: string | undefined;
148
+ surname?: string | undefined;
149
+ personalNotes?: any[] | undefined;
150
+ generalNotes?: any[] | undefined;
151
+ }>;
152
+ }, "strip", z.ZodTypeAny, {
153
+ lead: {
154
+ email: string;
155
+ organisation: string;
156
+ assignee: string;
157
+ wex?: boolean | undefined;
158
+ events?: boolean | undefined;
159
+ docCheck?: boolean | undefined;
160
+ forename?: string | undefined;
161
+ surname?: string | undefined;
162
+ personalNotes?: any[] | undefined;
163
+ generalNotes?: any[] | undefined;
164
+ };
165
+ urn: number;
166
+ }, {
167
+ lead: {
168
+ email: string;
169
+ organisation: string;
170
+ assignee: string;
171
+ wex?: boolean | undefined;
172
+ events?: boolean | undefined;
173
+ docCheck?: boolean | undefined;
174
+ forename?: string | undefined;
175
+ surname?: string | undefined;
176
+ personalNotes?: any[] | undefined;
177
+ generalNotes?: any[] | undefined;
178
+ };
179
+ urn: number;
180
+ }>;
181
+ response: z.ZodObject<{
182
+ leadId: z.ZodString;
183
+ }, "strip", z.ZodTypeAny, {
184
+ leadId: string;
185
+ }, {
186
+ leadId: string;
187
+ }>;
188
+ };
189
+ "outreach-getBrevoContact": {
190
+ request: z.ZodObject<{
191
+ urn: z.ZodNumber;
192
+ }, "strip", z.ZodTypeAny, {
193
+ urn: number;
194
+ }, {
195
+ urn: number;
196
+ }>;
197
+ response: z.ZodObject<{
198
+ found: z.ZodBoolean;
199
+ contactId: z.ZodOptional<z.ZodNumber>;
200
+ listIds: z.ZodArray<z.ZodNumber, "many">;
201
+ emailBlacklisted: z.ZodOptional<z.ZodBoolean>;
202
+ attributes: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
203
+ brevoUrl: z.ZodOptional<z.ZodString>;
204
+ }, "strip", z.ZodTypeAny, {
205
+ found: boolean;
206
+ listIds: number[];
207
+ attributes?: Record<string, unknown> | undefined;
208
+ contactId?: number | undefined;
209
+ emailBlacklisted?: boolean | undefined;
210
+ brevoUrl?: string | undefined;
211
+ }, {
212
+ found: boolean;
213
+ listIds: number[];
214
+ attributes?: Record<string, unknown> | undefined;
215
+ contactId?: number | undefined;
216
+ emailBlacklisted?: boolean | undefined;
217
+ brevoUrl?: string | undefined;
218
+ }>;
219
+ };
220
+ "outreach-setupBrevo": {
221
+ request: z.ZodObject<{}, "strip", z.ZodTypeAny, {}, {}>;
222
+ response: z.ZodObject<{
223
+ marketingListId: z.ZodNumber;
224
+ customersListId: z.ZodNumber;
225
+ webhookId: z.ZodNumber;
226
+ attributesCreated: z.ZodArray<z.ZodString, "many">;
227
+ }, "strip", z.ZodTypeAny, {
228
+ marketingListId: number;
229
+ customersListId: number;
230
+ webhookId: number;
231
+ attributesCreated: string[];
232
+ }, {
233
+ marketingListId: number;
234
+ customersListId: number;
235
+ webhookId: number;
236
+ attributesCreated: string[];
237
+ }>;
238
+ };
239
+ "outreach-migrateExisting": {
240
+ request: z.ZodObject<{}, "strip", z.ZodTypeAny, {}, {}>;
241
+ response: z.ZodObject<{
242
+ initialised: z.ZodNumber;
243
+ linkedToLeads: z.ZodNumber;
244
+ linkedToCustomers: z.ZodNumber;
245
+ importedToBrevo: z.ZodNumber;
246
+ }, "strip", z.ZodTypeAny, {
247
+ initialised: number;
248
+ linkedToLeads: number;
249
+ linkedToCustomers: number;
250
+ importedToBrevo: number;
251
+ }, {
252
+ initialised: number;
253
+ linkedToLeads: number;
254
+ linkedToCustomers: number;
255
+ importedToBrevo: number;
256
+ }>;
257
+ };
258
+ "outreach-backfillFromBrevo": {
259
+ request: z.ZodObject<{
260
+ batchSize: z.ZodOptional<z.ZodNumber>;
261
+ /** Restart from the first URN. */
262
+ reset: z.ZodOptional<z.ZodBoolean>;
263
+ }, "strip", z.ZodTypeAny, {
264
+ reset?: boolean | undefined;
265
+ batchSize?: number | undefined;
266
+ }, {
267
+ reset?: boolean | undefined;
268
+ batchSize?: number | undefined;
269
+ }>;
270
+ response: z.ZodObject<{
271
+ processed: z.ZodNumber;
272
+ total: z.ZodNumber;
273
+ done: z.ZodBoolean;
274
+ cursorUrn: z.ZodOptional<z.ZodNumber>;
275
+ }, "strip", z.ZodTypeAny, {
276
+ total: number;
277
+ done: boolean;
278
+ processed: number;
279
+ cursorUrn?: number | undefined;
280
+ }, {
281
+ total: number;
282
+ done: boolean;
283
+ processed: number;
284
+ cursorUrn?: number | undefined;
285
+ }>;
286
+ };
287
+ "outreach-runSweep": {
288
+ request: z.ZodObject<{
289
+ step: z.ZodOptional<z.ZodEnum<["all", "deferrals", "campaigns", "domainMatch", "brevoAttributes", "reconcile"]>>;
290
+ }, "strip", z.ZodTypeAny, {
291
+ step?: "all" | "reconcile" | "deferrals" | "campaigns" | "domainMatch" | "brevoAttributes" | undefined;
292
+ }, {
293
+ step?: "all" | "reconcile" | "deferrals" | "campaigns" | "domainMatch" | "brevoAttributes" | undefined;
294
+ }>;
295
+ response: z.ZodObject<{
296
+ undeferred: z.ZodNumber;
297
+ campaignsSynced: z.ZodNumber;
298
+ domainMatches: z.ZodNumber;
299
+ attributesPushed: z.ZodNumber;
300
+ reconcile: z.ZodOptional<z.ZodObject<{
301
+ marketingOnlyInBrevo: z.ZodNumber;
302
+ missingFromBrevo: z.ZodNumber;
303
+ }, "strip", z.ZodTypeAny, {
304
+ marketingOnlyInBrevo: number;
305
+ missingFromBrevo: number;
306
+ }, {
307
+ marketingOnlyInBrevo: number;
308
+ missingFromBrevo: number;
309
+ }>>;
310
+ }, "strip", z.ZodTypeAny, {
311
+ undeferred: number;
312
+ campaignsSynced: number;
313
+ domainMatches: number;
314
+ attributesPushed: number;
315
+ reconcile?: {
316
+ marketingOnlyInBrevo: number;
317
+ missingFromBrevo: number;
318
+ } | undefined;
319
+ }, {
320
+ undeferred: number;
321
+ campaignsSynced: number;
322
+ domainMatches: number;
323
+ attributesPushed: number;
324
+ reconcile?: {
325
+ marketingOnlyInBrevo: number;
326
+ missingFromBrevo: number;
327
+ } | undefined;
328
+ }>;
329
+ };
330
+ };
@@ -0,0 +1,133 @@
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
+ /** Lead to link, e.g. when confirming a domain match. */
46
+ leadId: zod_1.z.string().optional(),
47
+ /** Also apply to every URN in `outreach.sharedEmailUrns`. */
48
+ applyToSharedEmail: zod_1.z.boolean().optional(),
49
+ }),
50
+ response: zod_1.z.object({
51
+ urn: zod_1.z.number(),
52
+ status: outreachStatus,
53
+ brevoSynced: zod_1.z.boolean(),
54
+ affectedUrns: zod_1.z.array(zod_1.z.number()),
55
+ }),
56
+ },
57
+ "outreach-convertToLead": {
58
+ request: zod_1.z.object({
59
+ urn: zod_1.z.number(),
60
+ lead: zod_1.z.object({
61
+ forename: zod_1.z.string().optional(),
62
+ surname: zod_1.z.string().optional(),
63
+ email: zod_1.z.string().email(),
64
+ organisation: zod_1.z.string(),
65
+ wex: zod_1.z.boolean().optional(),
66
+ events: zod_1.z.boolean().optional(),
67
+ docCheck: zod_1.z.boolean().optional(),
68
+ assignee: zod_1.z.string(),
69
+ personalNotes: zod_1.z.array(zod_1.z.any()).optional(),
70
+ generalNotes: zod_1.z.array(zod_1.z.any()).optional(),
71
+ }),
72
+ }),
73
+ response: zod_1.z.object({ leadId: zod_1.z.string() }),
74
+ },
75
+ "outreach-getBrevoContact": {
76
+ request: zod_1.z.object({ urn: zod_1.z.number() }),
77
+ response: zod_1.z.object({
78
+ found: zod_1.z.boolean(),
79
+ contactId: zod_1.z.number().optional(),
80
+ listIds: zod_1.z.array(zod_1.z.number()),
81
+ emailBlacklisted: zod_1.z.boolean().optional(),
82
+ attributes: zod_1.z.record(zod_1.z.string(), zod_1.z.unknown()).optional(),
83
+ brevoUrl: zod_1.z.string().optional(),
84
+ }),
85
+ },
86
+ "outreach-setupBrevo": {
87
+ request: zod_1.z.object({}),
88
+ response: zod_1.z.object({
89
+ marketingListId: zod_1.z.number(),
90
+ customersListId: zod_1.z.number(),
91
+ webhookId: zod_1.z.number(),
92
+ attributesCreated: zod_1.z.array(zod_1.z.string()),
93
+ }),
94
+ },
95
+ "outreach-migrateExisting": {
96
+ request: zod_1.z.object({}),
97
+ response: zod_1.z.object({
98
+ initialised: zod_1.z.number(),
99
+ linkedToLeads: zod_1.z.number(),
100
+ linkedToCustomers: zod_1.z.number(),
101
+ importedToBrevo: zod_1.z.number(),
102
+ }),
103
+ },
104
+ "outreach-backfillFromBrevo": {
105
+ request: zod_1.z.object({
106
+ batchSize: zod_1.z.number().int().min(1).max(400).optional(),
107
+ /** Restart from the first URN. */
108
+ reset: zod_1.z.boolean().optional(),
109
+ }),
110
+ response: zod_1.z.object({
111
+ processed: zod_1.z.number(),
112
+ total: zod_1.z.number(),
113
+ done: zod_1.z.boolean(),
114
+ cursorUrn: zod_1.z.number().optional(),
115
+ }),
116
+ },
117
+ "outreach-runSweep": {
118
+ request: zod_1.z.object({
119
+ step: zod_1.z.enum(["all", "deferrals", "campaigns", "domainMatch", "brevoAttributes", "reconcile"]).optional(),
120
+ }),
121
+ response: zod_1.z.object({
122
+ undeferred: zod_1.z.number(),
123
+ campaignsSynced: zod_1.z.number(),
124
+ domainMatches: zod_1.z.number(),
125
+ attributesPushed: zod_1.z.number(),
126
+ reconcile: zod_1.z.object({
127
+ marketingOnlyInBrevo: zod_1.z.number(),
128
+ missingFromBrevo: zod_1.z.number(),
129
+ }).optional(),
130
+ }),
131
+ },
132
+ };
133
+ //# 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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"}