@marlinjai/mail-contract 0.9.0 → 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.
- package/dist/index.d.mts +2575 -981
- package/dist/index.d.ts +2575 -981
- package/dist/index.js +913 -641
- package/dist/index.mjs +891 -641
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -77,6 +77,12 @@ var ERROR_STATUS = {
|
|
|
77
77
|
invalid_mjml: 422,
|
|
78
78
|
missing_unsubscribe_url: 422,
|
|
79
79
|
mailing_not_ready: 422,
|
|
80
|
+
/**
|
|
81
|
+
* A value placed inside a link's address comes out empty for at least one
|
|
82
|
+
* recipient, so the mailing does not start; `details.values` lists each such
|
|
83
|
+
* value with how many recipients it is empty for.
|
|
84
|
+
*/
|
|
85
|
+
unfilled_link: 422,
|
|
80
86
|
unknown_topic: 422,
|
|
81
87
|
unknown_provider: 422,
|
|
82
88
|
/**
|
|
@@ -145,10 +151,10 @@ var API_VERSION_PREFIX = "/v1";
|
|
|
145
151
|
var HEALTH_PATH = "/healthz";
|
|
146
152
|
|
|
147
153
|
// src/workspace.ts
|
|
148
|
-
import { z as
|
|
154
|
+
import { z as z11 } from "zod";
|
|
149
155
|
|
|
150
156
|
// src/automations.ts
|
|
151
|
-
import { z as
|
|
157
|
+
import { z as z10 } from "zod";
|
|
152
158
|
|
|
153
159
|
// src/templates.ts
|
|
154
160
|
import { z as z5 } from "zod";
|
|
@@ -169,11 +175,11 @@ var RESERVED_MERGE_FIELDS = {
|
|
|
169
175
|
};
|
|
170
176
|
var MERGE_FIELD_PATTERN = /\{\{\s*([a-z][a-z0-9_]*)\s*(?:\|([^}]*))?\}\}/g;
|
|
171
177
|
function findMergeFields(html) {
|
|
172
|
-
const
|
|
178
|
+
const uses2 = [];
|
|
173
179
|
for (const match of html.matchAll(MERGE_FIELD_PATTERN)) {
|
|
174
|
-
|
|
180
|
+
uses2.push({ name: match[1], fallback: match[2] === void 0 ? null : match[2].trim() });
|
|
175
181
|
}
|
|
176
|
-
return
|
|
182
|
+
return uses2;
|
|
177
183
|
}
|
|
178
184
|
function missingRequiredMergeFields(html) {
|
|
179
185
|
const used = new Set(findMergeFields(html).map((u) => u.name));
|
|
@@ -856,10 +862,10 @@ function exportFilename(name, format) {
|
|
|
856
862
|
}
|
|
857
863
|
|
|
858
864
|
// src/platform.ts
|
|
859
|
-
import { z as
|
|
865
|
+
import { z as z9 } from "zod";
|
|
860
866
|
|
|
861
867
|
// src/mailings.ts
|
|
862
|
-
import { z as
|
|
868
|
+
import { z as z8 } from "zod";
|
|
863
869
|
|
|
864
870
|
// src/ab-test.ts
|
|
865
871
|
import { z as z6 } from "zod";
|
|
@@ -910,6 +916,167 @@ function testGroupSize(total, fraction, variants) {
|
|
|
910
916
|
return Math.min(total, Math.max(variants, Math.ceil(total * fraction)));
|
|
911
917
|
}
|
|
912
918
|
|
|
919
|
+
// src/values.ts
|
|
920
|
+
import { z as z7 } from "zod";
|
|
921
|
+
var MAX_MERGE_DEFAULTS = 50;
|
|
922
|
+
var MAX_MERGE_DEFAULT_LENGTH = 2e3;
|
|
923
|
+
var MergeFieldName = z7.string().regex(/^[a-z][a-z0-9_]*$/, "a merge-field name: lowercase letters, digits and underscores, starting with a letter").max(64);
|
|
924
|
+
var MergeDefaultValue = z7.union([z7.string().max(MAX_MERGE_DEFAULT_LENGTH), z7.number().finite(), z7.boolean()]);
|
|
925
|
+
var MailingMerge = z7.record(MergeFieldName, MergeDefaultValue).refine((m) => Object.keys(m).length <= MAX_MERGE_DEFAULTS, `at most ${MAX_MERGE_DEFAULTS} values on a mailing`).refine((m) => !Object.keys(m).some((k) => SERVICE_FILLED_MERGE_FIELDS.includes(k)), {
|
|
926
|
+
message: `the service fills ${SERVICE_FILLED_MERGE_FIELDS.join(" and ")}; a mailing cannot set them`
|
|
927
|
+
});
|
|
928
|
+
var LETTER_ONLY_MERGE_FIELDS = ["first_name", "last_name"];
|
|
929
|
+
var NAME_ON_BROADCAST_REFUSAL = "name_on_broadcast";
|
|
930
|
+
function letterOnlyValues(merge) {
|
|
931
|
+
return LETTER_ONLY_MERGE_FIELDS.filter((name) => ownEntry(merge, name) !== void 0);
|
|
932
|
+
}
|
|
933
|
+
var VALUE_LAYERS = ["recipient", "mailing", "contact", "workspace", "fallback", "empty"];
|
|
934
|
+
var ValueLayer = z7.enum(VALUE_LAYERS);
|
|
935
|
+
var ResolvedValue = z7.object({ text: z7.string(), layer: ValueLayer });
|
|
936
|
+
var MessageValues = z7.record(ResolvedValue);
|
|
937
|
+
function scalarText(value) {
|
|
938
|
+
if (typeof value === "string") return value;
|
|
939
|
+
if (typeof value === "number" && Number.isFinite(value)) return String(value);
|
|
940
|
+
if (typeof value === "boolean") return String(value);
|
|
941
|
+
return null;
|
|
942
|
+
}
|
|
943
|
+
function isNameField(name) {
|
|
944
|
+
return name === "first_name" || name === "last_name";
|
|
945
|
+
}
|
|
946
|
+
function rawMergeValue(name, sources) {
|
|
947
|
+
const own = ownEntry(sources.recipient, name);
|
|
948
|
+
if (scalarText(own) !== null) return { value: own, layer: "recipient" };
|
|
949
|
+
const held = ownEntry(sources.mailing, name);
|
|
950
|
+
if (scalarText(held) !== null) return { value: held, layer: "mailing" };
|
|
951
|
+
if (isNameField(name)) {
|
|
952
|
+
const stored = sources.contact?.[name];
|
|
953
|
+
return typeof stored === "string" ? { value: stored, layer: "contact" } : null;
|
|
954
|
+
}
|
|
955
|
+
const property = ownEntry(sources.contact?.properties, name);
|
|
956
|
+
if (scalarText(property) !== null) return { value: property, layer: "contact" };
|
|
957
|
+
const byDefault = ownEntry(sources.workspace, name);
|
|
958
|
+
return scalarText(byDefault) !== null ? { value: byDefault, layer: "workspace" } : null;
|
|
959
|
+
}
|
|
960
|
+
function resolveValue(name, fallback, sources) {
|
|
961
|
+
const found = rawMergeValue(name, sources);
|
|
962
|
+
const text = found === null ? null : scalarText(found.value);
|
|
963
|
+
if (found !== null && text !== null && text.trim() !== "") return { text, layer: found.layer };
|
|
964
|
+
const instead = fallback?.trim() ?? "";
|
|
965
|
+
return instead === "" ? { text: "", layer: "empty" } : { text: instead, layer: "fallback" };
|
|
966
|
+
}
|
|
967
|
+
function pattern2() {
|
|
968
|
+
return new RegExp(MERGE_FIELD_PATTERN.source, "g");
|
|
969
|
+
}
|
|
970
|
+
function isServiceFilled(name) {
|
|
971
|
+
return SERVICE_FILLED_MERGE_FIELDS.includes(name);
|
|
972
|
+
}
|
|
973
|
+
function uses(text) {
|
|
974
|
+
return [...text.matchAll(pattern2())].map((m) => ({ name: m[1], fallback: m[2] === void 0 ? null : m[2].trim() }));
|
|
975
|
+
}
|
|
976
|
+
function valueNames(texts, insertions) {
|
|
977
|
+
const names = [];
|
|
978
|
+
const add = (name) => {
|
|
979
|
+
if (!isServiceFilled(name) && !names.includes(name)) names.push(name);
|
|
980
|
+
};
|
|
981
|
+
for (const text of texts) {
|
|
982
|
+
for (const use of uses(text ?? "")) {
|
|
983
|
+
const def = ownEntry(insertions, use.name);
|
|
984
|
+
if (!def) {
|
|
985
|
+
add(use.name);
|
|
986
|
+
continue;
|
|
987
|
+
}
|
|
988
|
+
add(def.by);
|
|
989
|
+
for (const inner of [...Object.values(def.choices), def.filled ?? "", def.otherwise]) for (const u of uses(inner)) add(u.name);
|
|
990
|
+
}
|
|
991
|
+
}
|
|
992
|
+
return names;
|
|
993
|
+
}
|
|
994
|
+
function printedFields(texts, insertions, chosen2) {
|
|
995
|
+
const out = [];
|
|
996
|
+
const add = (use) => {
|
|
997
|
+
if (isServiceFilled(use.name)) return;
|
|
998
|
+
if (!out.some((o) => o.name === use.name && o.fallback === use.fallback)) out.push(use);
|
|
999
|
+
};
|
|
1000
|
+
for (const text of texts) {
|
|
1001
|
+
for (const use of uses(text ?? "")) {
|
|
1002
|
+
if (!ownEntry(insertions, use.name)) add(use);
|
|
1003
|
+
else for (const inner of uses(ownEntry(chosen2, use.name)?.text ?? "")) add(inner);
|
|
1004
|
+
}
|
|
1005
|
+
}
|
|
1006
|
+
return out;
|
|
1007
|
+
}
|
|
1008
|
+
function valuesRead(texts, insertions, chosen2, resolve) {
|
|
1009
|
+
const out = {};
|
|
1010
|
+
const read = (use) => {
|
|
1011
|
+
if (isServiceFilled(use.name)) return;
|
|
1012
|
+
const value = resolve(use.name, use.fallback);
|
|
1013
|
+
const seen = ownEntry(out, use.name);
|
|
1014
|
+
if (seen === void 0 || value.layer === "empty" && seen.layer !== "empty") out[use.name] = value;
|
|
1015
|
+
};
|
|
1016
|
+
for (const text of texts) {
|
|
1017
|
+
for (const use of uses(text ?? "")) {
|
|
1018
|
+
const def = ownEntry(insertions, use.name);
|
|
1019
|
+
if (!def) {
|
|
1020
|
+
read(use);
|
|
1021
|
+
continue;
|
|
1022
|
+
}
|
|
1023
|
+
read({ name: def.by, fallback: null });
|
|
1024
|
+
for (const inner of uses(ownEntry(chosen2, use.name)?.text ?? "")) read(inner);
|
|
1025
|
+
}
|
|
1026
|
+
}
|
|
1027
|
+
return out;
|
|
1028
|
+
}
|
|
1029
|
+
var HREF = /\bhref\s*=\s*(?:"([^"]*)"|'([^']*)')/gi;
|
|
1030
|
+
function linkTokens(html) {
|
|
1031
|
+
const out = [];
|
|
1032
|
+
for (const match of html.matchAll(HREF)) {
|
|
1033
|
+
for (const use of uses(match[1] ?? match[2] ?? "")) {
|
|
1034
|
+
if (isServiceFilled(use.name)) continue;
|
|
1035
|
+
if (!out.some((o) => o.name === use.name && o.fallback === use.fallback)) out.push(use);
|
|
1036
|
+
}
|
|
1037
|
+
}
|
|
1038
|
+
return out;
|
|
1039
|
+
}
|
|
1040
|
+
function emptyLinkValues(tokens, p) {
|
|
1041
|
+
const empty = [];
|
|
1042
|
+
for (const token of tokens) {
|
|
1043
|
+
const ins = ownEntry(p.insertions, token.name);
|
|
1044
|
+
const text = ins ? ins.text.replace(pattern2(), (_m, name, fallback) => p.value(name, fallback)) : p.value(token.name, token.fallback ?? void 0);
|
|
1045
|
+
if (text.trim() === "" && !empty.includes(token.name)) empty.push(token.name);
|
|
1046
|
+
}
|
|
1047
|
+
return empty;
|
|
1048
|
+
}
|
|
1049
|
+
var UnfilledLink = z7.object({
|
|
1050
|
+
/** The value, or the insertion, that comes out empty. */
|
|
1051
|
+
value: z7.string(),
|
|
1052
|
+
/** How many recipients it is empty for. */
|
|
1053
|
+
recipients: z7.number().int().min(1)
|
|
1054
|
+
});
|
|
1055
|
+
var RESOLVE_PROBLEMS = ["unfilled_link", "empty_value", "insertion_miss"];
|
|
1056
|
+
var ResolveProblem = z7.object({
|
|
1057
|
+
kind: z7.enum(RESOLVE_PROBLEMS),
|
|
1058
|
+
severity: z7.enum(["blocking", "note"]),
|
|
1059
|
+
value: z7.string(),
|
|
1060
|
+
message: z7.string()
|
|
1061
|
+
});
|
|
1062
|
+
var ResolvedRecipient = z7.object({
|
|
1063
|
+
/** The recipient's index in the request; null for the reader with nothing of their own. */
|
|
1064
|
+
index: z7.number().int().min(0).nullable(),
|
|
1065
|
+
/** The address the message would go to; null for the reader with nothing of their own. */
|
|
1066
|
+
email: Email.nullable(),
|
|
1067
|
+
/** The contact found for the recipient; null when the address has none yet (a send creates it, empty). */
|
|
1068
|
+
contact_id: Id.nullable(),
|
|
1069
|
+
/** The language version this recipient would get. */
|
|
1070
|
+
locale: LocaleTag,
|
|
1071
|
+
/** The subject line as they would read it. */
|
|
1072
|
+
subject: z7.string(),
|
|
1073
|
+
values: MessageValues,
|
|
1074
|
+
/** The choice each insertion would make; null when the content has none. */
|
|
1075
|
+
insertions: InsertionChoices.nullable(),
|
|
1076
|
+
problems: z7.array(ResolveProblem)
|
|
1077
|
+
});
|
|
1078
|
+
var RESOLVE_REJECTIONS = ["unknown_contact", "duplicate_in_batch"];
|
|
1079
|
+
|
|
913
1080
|
// src/mailings.ts
|
|
914
1081
|
var MAILING_STATUSES = [
|
|
915
1082
|
"draft",
|
|
@@ -920,9 +1087,9 @@ var MAILING_STATUSES = [
|
|
|
920
1087
|
"partially_failed",
|
|
921
1088
|
"cancelled"
|
|
922
1089
|
];
|
|
923
|
-
var MailingStatus =
|
|
1090
|
+
var MailingStatus = z8.enum(MAILING_STATUSES);
|
|
924
1091
|
var MAILING_ACTIONS = ["send", "schedule", "unschedule", "pause", "resume", "cancel", "retry-failed"];
|
|
925
|
-
var MailingAction =
|
|
1092
|
+
var MailingAction = z8.enum(MAILING_ACTIONS);
|
|
926
1093
|
var MAILING_TRANSITIONS = {
|
|
927
1094
|
draft: ["send", "schedule", "cancel"],
|
|
928
1095
|
scheduled: ["send", "schedule", "unschedule", "cancel"],
|
|
@@ -937,23 +1104,23 @@ function canTransition(status, action) {
|
|
|
937
1104
|
}
|
|
938
1105
|
var EDITABLE_MAILING_STATUSES = ["draft", "scheduled"];
|
|
939
1106
|
var TERMINAL_MAILING_STATUSES = ["sent", "cancelled"];
|
|
940
|
-
var MailingMetadata =
|
|
941
|
-
var MailingCounts =
|
|
942
|
-
total:
|
|
943
|
-
queued:
|
|
944
|
-
sending:
|
|
945
|
-
sent:
|
|
946
|
-
failed:
|
|
947
|
-
skipped:
|
|
1107
|
+
var MailingMetadata = z8.record(z8.string().max(500)).refine((v) => Object.keys(v).length <= 20, "at most 20 keys").refine((v) => Object.keys(v).every((k) => k.length >= 1 && k.length <= 64), "keys are 1 to 64 characters");
|
|
1108
|
+
var MailingCounts = z8.object({
|
|
1109
|
+
total: z8.number().int().min(0),
|
|
1110
|
+
queued: z8.number().int().min(0),
|
|
1111
|
+
sending: z8.number().int().min(0),
|
|
1112
|
+
sent: z8.number().int().min(0),
|
|
1113
|
+
failed: z8.number().int().min(0),
|
|
1114
|
+
skipped: z8.number().int().min(0)
|
|
948
1115
|
});
|
|
949
1116
|
var MAILING_KINDS = ["broadcast", "automation", "notification"];
|
|
950
|
-
var MailingKind =
|
|
951
|
-
var Mailing =
|
|
1117
|
+
var MailingKind = z8.enum(MAILING_KINDS);
|
|
1118
|
+
var Mailing = z8.object({
|
|
952
1119
|
id: Id,
|
|
953
1120
|
kind: MailingKind,
|
|
954
|
-
name:
|
|
955
|
-
subject:
|
|
956
|
-
preheader:
|
|
1121
|
+
name: z8.string().max(200).nullable(),
|
|
1122
|
+
subject: z8.string().min(1).max(998),
|
|
1123
|
+
preheader: z8.string().max(500).nullable(),
|
|
957
1124
|
/**
|
|
958
1125
|
* The template the document was taken from, if any. The snapshot is what is
|
|
959
1126
|
* sent in the main language; the template's other language versions are
|
|
@@ -977,6 +1144,14 @@ var Mailing = z7.object({
|
|
|
977
1144
|
* language: without a ready version in it, the main language goes out.
|
|
978
1145
|
*/
|
|
979
1146
|
send_locale: LocaleTag.nullable(),
|
|
1147
|
+
/**
|
|
1148
|
+
* The template's language version the mailing was written from
|
|
1149
|
+
* (`MailingCreate.template_locale`), or null. When set, it equals `locale`
|
|
1150
|
+
* and `send_locale`: the mailing is one letter in that language, and the
|
|
1151
|
+
* template's other versions never go out with it (see "Writing from one
|
|
1152
|
+
* language version" in the contract's documentation).
|
|
1153
|
+
*/
|
|
1154
|
+
template_locale: LocaleTag.nullable().default(null),
|
|
980
1155
|
/**
|
|
981
1156
|
* The topic the mailing is sent under. Null for a letter (a one-to-one
|
|
982
1157
|
* mailing with at most one recipient, which belongs to no topic and whose
|
|
@@ -987,6 +1162,12 @@ var Mailing = z7.object({
|
|
|
987
1162
|
status: MailingStatus,
|
|
988
1163
|
counts: MailingCounts,
|
|
989
1164
|
metadata: MailingMetadata,
|
|
1165
|
+
/**
|
|
1166
|
+
* The values held on this mailing (see `MailingMerge`): the same for
|
|
1167
|
+
* everyone who gets it, above the contact's stored name and properties and
|
|
1168
|
+
* the workspace's defaults, below the recipient's own. Empty by default.
|
|
1169
|
+
*/
|
|
1170
|
+
merge: MailingMerge.default({}),
|
|
990
1171
|
scheduled_at: Timestamp.nullable(),
|
|
991
1172
|
/** The A/B test on subject or content (S4), null when there is none. */
|
|
992
1173
|
ab_test: AbTestState.nullable(),
|
|
@@ -998,15 +1179,15 @@ var Mailing = z7.object({
|
|
|
998
1179
|
* recipients refused as dead addresses in one run (see the provider's
|
|
999
1180
|
* `rejections.anomaly`). Resuming clears it and starts a new run.
|
|
1000
1181
|
*/
|
|
1001
|
-
pause_reason:
|
|
1182
|
+
pause_reason: z8.string().nullable().default(null),
|
|
1002
1183
|
created_at: Timestamp,
|
|
1003
1184
|
updated_at: Timestamp
|
|
1004
1185
|
});
|
|
1005
1186
|
var MailingSummary = Mailing.omit({ document: true });
|
|
1006
1187
|
var MailingContent = {
|
|
1007
|
-
name:
|
|
1008
|
-
subject:
|
|
1009
|
-
preheader:
|
|
1188
|
+
name: z8.string().max(200).optional(),
|
|
1189
|
+
subject: z8.string().min(1).max(998),
|
|
1190
|
+
preheader: z8.string().max(500).optional(),
|
|
1010
1191
|
/** See `Mailing.send_locale`. */
|
|
1011
1192
|
send_locale: LocaleTag.nullable().optional(),
|
|
1012
1193
|
/**
|
|
@@ -1017,29 +1198,42 @@ var MailingContent = {
|
|
|
1017
1198
|
*/
|
|
1018
1199
|
topic: Slug.nullable().optional(),
|
|
1019
1200
|
provider_id: Id,
|
|
1020
|
-
metadata: MailingMetadata.optional()
|
|
1201
|
+
metadata: MailingMetadata.optional(),
|
|
1202
|
+
/** See `Mailing.merge`. `first_name` and `last_name` only on a letter (no topic). */
|
|
1203
|
+
merge: MailingMerge.optional()
|
|
1021
1204
|
};
|
|
1022
|
-
var MailingCreate =
|
|
1205
|
+
var MailingCreate = z8.object({
|
|
1023
1206
|
...MailingContent,
|
|
1024
1207
|
subject: MailingContent.subject.optional(),
|
|
1025
1208
|
document: TemplateDocument.optional(),
|
|
1026
|
-
template_id: Id.optional()
|
|
1209
|
+
template_id: Id.optional(),
|
|
1210
|
+
template_locale: LocaleTag.optional()
|
|
1027
1211
|
}).refine((v) => v.document === void 0 !== (v.template_id === void 0), {
|
|
1028
1212
|
message: "exactly one of document or template_id",
|
|
1029
1213
|
path: ["document"]
|
|
1214
|
+
}).refine((v) => v.template_locale === void 0 || v.template_id !== void 0, {
|
|
1215
|
+
message: "template_locale names a version of template_id",
|
|
1216
|
+
path: ["template_locale"]
|
|
1030
1217
|
}).refine((v) => v.template_id !== void 0 || v.subject !== void 0, {
|
|
1031
1218
|
message: "a mailing made from a document needs a subject",
|
|
1032
1219
|
path: ["subject"]
|
|
1033
1220
|
});
|
|
1034
|
-
var MailingUpdate =
|
|
1035
|
-
name:
|
|
1221
|
+
var MailingUpdate = z8.object({
|
|
1222
|
+
name: z8.string().max(200).nullable().optional(),
|
|
1036
1223
|
subject: MailingContent.subject.optional(),
|
|
1037
|
-
preheader:
|
|
1224
|
+
preheader: z8.string().max(500).nullable().optional(),
|
|
1038
1225
|
/** Null turns a draft into a letter, which it can only be with at most one recipient. */
|
|
1039
1226
|
topic: Slug.nullable().optional(),
|
|
1040
1227
|
send_locale: LocaleTag.nullable().optional(),
|
|
1041
1228
|
provider_id: Id.optional(),
|
|
1042
1229
|
metadata: MailingMetadata.optional(),
|
|
1230
|
+
/**
|
|
1231
|
+
* Replaces the mailing's values whole (see `Mailing.merge`). Giving a
|
|
1232
|
+
* mailing that holds `first_name` or `last_name` a topic is refused
|
|
1233
|
+
* (`name_on_broadcast`) unless the same call replaces its values without
|
|
1234
|
+
* them.
|
|
1235
|
+
*/
|
|
1236
|
+
merge: MailingMerge.optional(),
|
|
1043
1237
|
document: TemplateDocument.optional()
|
|
1044
1238
|
}).refine((v) => Object.keys(v).length > 0, "at least one field");
|
|
1045
1239
|
var MailingListQuery = PageQuery.extend({
|
|
@@ -1048,30 +1242,30 @@ var MailingListQuery = PageQuery.extend({
|
|
|
1048
1242
|
kind: MailingKind.optional(),
|
|
1049
1243
|
automation_id: Id.optional()
|
|
1050
1244
|
});
|
|
1051
|
-
var MailingSendRequest =
|
|
1052
|
-
var MailingRetryFailedRequest =
|
|
1245
|
+
var MailingSendRequest = z8.object({}).strict();
|
|
1246
|
+
var MailingRetryFailedRequest = z8.object({
|
|
1053
1247
|
/**
|
|
1054
1248
|
* Also requeue recipients whose outcome is unknown after a crash. They may
|
|
1055
1249
|
* already have received the message, so this is a human's decision, never a
|
|
1056
1250
|
* default.
|
|
1057
1251
|
*/
|
|
1058
|
-
include_outcome_unknown:
|
|
1252
|
+
include_outcome_unknown: z8.boolean().optional()
|
|
1059
1253
|
}).strict();
|
|
1060
|
-
var MailingActionRequest =
|
|
1061
|
-
var MailingTestRequest =
|
|
1254
|
+
var MailingActionRequest = z8.object({}).strict();
|
|
1255
|
+
var MailingTestRequest = z8.object({
|
|
1062
1256
|
to: Email,
|
|
1063
1257
|
merge: Properties.optional(),
|
|
1064
1258
|
locale: LocaleTag.optional()
|
|
1065
1259
|
});
|
|
1066
|
-
var MailingTestResult =
|
|
1260
|
+
var MailingTestResult = z8.object({
|
|
1067
1261
|
message_id: Id,
|
|
1068
|
-
provider_message_id:
|
|
1262
|
+
provider_message_id: z8.string().nullable()
|
|
1069
1263
|
});
|
|
1070
1264
|
var RECIPIENT_STATUSES = ["queued", "sending", "sent", "failed", "skipped"];
|
|
1071
|
-
var RecipientStatus =
|
|
1265
|
+
var RecipientStatus = z8.enum(RECIPIENT_STATUSES);
|
|
1072
1266
|
var SKIP_REASONS = ["suppressed", "not_subscribed", "contact_erased", "cancelled", "outcome_unknown"];
|
|
1073
|
-
var SkipReason =
|
|
1074
|
-
var Recipient =
|
|
1267
|
+
var SkipReason = z8.enum(SKIP_REASONS);
|
|
1268
|
+
var Recipient = z8.object({
|
|
1075
1269
|
id: Id,
|
|
1076
1270
|
mailing_id: Id,
|
|
1077
1271
|
contact_id: Id.nullable(),
|
|
@@ -1079,55 +1273,55 @@ var Recipient = z7.object({
|
|
|
1079
1273
|
merge: Properties,
|
|
1080
1274
|
status: RecipientStatus,
|
|
1081
1275
|
skip_reason: SkipReason.nullable(),
|
|
1082
|
-
attempts:
|
|
1276
|
+
attempts: z8.number().int().min(0),
|
|
1083
1277
|
message_id: Id.nullable(),
|
|
1084
|
-
last_error:
|
|
1278
|
+
last_error: z8.string().nullable(),
|
|
1085
1279
|
created_at: Timestamp,
|
|
1086
1280
|
updated_at: Timestamp
|
|
1087
1281
|
});
|
|
1088
1282
|
var MAX_RECIPIENTS_PER_BATCH = 1e3;
|
|
1089
1283
|
var MAX_LETTER_RECIPIENTS = 1;
|
|
1090
1284
|
var LETTER_RECIPIENT_REFUSAL = "letter_has_one_recipient";
|
|
1091
|
-
var RecipientInput =
|
|
1285
|
+
var RecipientInput = z8.object({
|
|
1092
1286
|
contact_id: Id.optional(),
|
|
1093
|
-
external_id:
|
|
1287
|
+
external_id: z8.string().min(1).max(255).optional(),
|
|
1094
1288
|
email: Email.optional(),
|
|
1095
1289
|
merge: Properties.optional()
|
|
1096
1290
|
}).refine((v) => v.contact_id !== void 0 || v.external_id !== void 0 || v.email !== void 0, {
|
|
1097
1291
|
message: "contact_id, external_id or email is required",
|
|
1098
1292
|
path: ["email"]
|
|
1099
1293
|
});
|
|
1100
|
-
var RecipientBatch =
|
|
1101
|
-
recipients:
|
|
1294
|
+
var RecipientBatch = z8.object({
|
|
1295
|
+
recipients: z8.array(RecipientInput).min(1).max(MAX_RECIPIENTS_PER_BATCH)
|
|
1102
1296
|
});
|
|
1103
1297
|
var RECIPIENT_REJECTIONS = ["unknown_contact", "duplicate_in_batch"];
|
|
1104
|
-
var RecipientBatchResult =
|
|
1298
|
+
var RecipientBatchResult = z8.object({
|
|
1105
1299
|
/** Newly queued. */
|
|
1106
|
-
added:
|
|
1300
|
+
added: z8.number().int().min(0),
|
|
1107
1301
|
/** Already on the mailing from an earlier batch: left unchanged. */
|
|
1108
|
-
already_present:
|
|
1302
|
+
already_present: z8.number().int().min(0),
|
|
1109
1303
|
/** Items not added, by their index in the request. */
|
|
1110
|
-
rejected:
|
|
1304
|
+
rejected: z8.array(z8.object({ index: z8.number().int().min(0), reason: z8.enum(RECIPIENT_REJECTIONS) }))
|
|
1111
1305
|
});
|
|
1112
1306
|
var RecipientListQuery = PageQuery.extend({
|
|
1113
1307
|
status: RecipientStatus.optional(),
|
|
1114
1308
|
skip_reason: SkipReason.optional()
|
|
1115
1309
|
});
|
|
1116
1310
|
var MESSAGE_OUTCOMES = ["sent", "failed"];
|
|
1117
|
-
var MessageOutcome =
|
|
1118
|
-
var MessageSummary =
|
|
1311
|
+
var MessageOutcome = z8.enum(MESSAGE_OUTCOMES);
|
|
1312
|
+
var MessageSummary = z8.object({
|
|
1119
1313
|
id: Id,
|
|
1120
1314
|
mailing_id: Id.nullable(),
|
|
1121
1315
|
recipient_id: Id.nullable(),
|
|
1122
1316
|
contact_id: Id.nullable(),
|
|
1123
1317
|
to: Email,
|
|
1124
|
-
subject:
|
|
1318
|
+
subject: z8.string(),
|
|
1125
1319
|
provider_id: Id,
|
|
1126
|
-
provider_message_id:
|
|
1320
|
+
provider_message_id: z8.string().nullable(),
|
|
1127
1321
|
outcome: MessageOutcome,
|
|
1128
|
-
error:
|
|
1129
|
-
is_test:
|
|
1130
|
-
recipient_count:
|
|
1322
|
+
error: z8.string().nullable(),
|
|
1323
|
+
is_test: z8.boolean(),
|
|
1324
|
+
recipient_count: z8.number().int().min(1),
|
|
1131
1325
|
/** The language version the message was sent in; null for messages archived before languages existed. */
|
|
1132
1326
|
locale: LocaleTag.nullable(),
|
|
1133
1327
|
/**
|
|
@@ -1137,24 +1331,32 @@ var MessageSummary = z7.object({
|
|
|
1137
1331
|
* "otherwise" text. Null when the message had no insertions.
|
|
1138
1332
|
*/
|
|
1139
1333
|
insertions: InsertionChoices.nullable(),
|
|
1334
|
+
/**
|
|
1335
|
+
* The values the message was written with, by name: the text each one
|
|
1336
|
+
* printed and the layer it came from (see `MessageValues`), recorded when it
|
|
1337
|
+
* was sent, so a later change to a default never rewrites what a reader got.
|
|
1338
|
+
* Null for a message archived before values were recorded, and for a signup
|
|
1339
|
+
* form's confirmation, which belongs to no mailing.
|
|
1340
|
+
*/
|
|
1341
|
+
values: MessageValues.nullable().default(null),
|
|
1140
1342
|
created_at: Timestamp
|
|
1141
1343
|
});
|
|
1142
|
-
var Message = MessageSummary.extend({ html:
|
|
1143
|
-
var MailingLanguageStatus =
|
|
1144
|
-
var MailingLanguage =
|
|
1344
|
+
var Message = MessageSummary.extend({ html: z8.string() });
|
|
1345
|
+
var MailingLanguageStatus = z8.enum(["main", "ready", "draft", "not_a_workspace_language"]);
|
|
1346
|
+
var MailingLanguage = z8.object({
|
|
1145
1347
|
locale: LocaleTag,
|
|
1146
1348
|
status: MailingLanguageStatus,
|
|
1147
1349
|
/** Queued recipients who will get this version. */
|
|
1148
|
-
planned:
|
|
1350
|
+
planned: z8.number().int().min(0),
|
|
1149
1351
|
/** Messages already sent in this language (tests excluded). */
|
|
1150
|
-
sent:
|
|
1352
|
+
sent: z8.number().int().min(0)
|
|
1151
1353
|
});
|
|
1152
|
-
var MailingLanguageReport =
|
|
1354
|
+
var MailingLanguageReport = z8.object({
|
|
1153
1355
|
main_locale: LocaleTag,
|
|
1154
1356
|
send_locale: LocaleTag.nullable(),
|
|
1155
|
-
languages:
|
|
1156
|
-
fallback:
|
|
1157
|
-
no_language:
|
|
1357
|
+
languages: z8.array(MailingLanguage),
|
|
1358
|
+
fallback: z8.array(z8.object({ wanted: z8.string(), planned: z8.number().int().min(1) })),
|
|
1359
|
+
no_language: z8.number().int().min(0),
|
|
1158
1360
|
/**
|
|
1159
1361
|
* Values that no choice of an insertion takes, so the "otherwise" text goes
|
|
1160
1362
|
* out (docs/plans/2026-09-25-translated-insertions.md, decision 3): per
|
|
@@ -1163,41 +1365,56 @@ var MailingLanguageReport = z7.object({
|
|
|
1163
1365
|
* template's keys never produces one; a non-empty list means the client and
|
|
1164
1366
|
* the template have drifted apart.
|
|
1165
1367
|
*/
|
|
1166
|
-
insertion_misses:
|
|
1167
|
-
|
|
1168
|
-
insertion:
|
|
1169
|
-
value:
|
|
1170
|
-
planned:
|
|
1171
|
-
sent:
|
|
1368
|
+
insertion_misses: z8.array(
|
|
1369
|
+
z8.object({
|
|
1370
|
+
insertion: z8.string(),
|
|
1371
|
+
value: z8.string(),
|
|
1372
|
+
planned: z8.number().int().min(0),
|
|
1373
|
+
sent: z8.number().int().min(0)
|
|
1172
1374
|
})
|
|
1173
1375
|
)
|
|
1174
1376
|
});
|
|
1377
|
+
var MailingResolveRequest = z8.object({
|
|
1378
|
+
recipients: z8.array(RecipientInput).max(MAX_RECIPIENTS_PER_BATCH).optional()
|
|
1379
|
+
});
|
|
1380
|
+
var MailingResolveResult = z8.object({
|
|
1381
|
+
/** Every value the mailing's content can read, in order of first appearance, without `email` and `unsubscribe_url`. */
|
|
1382
|
+
names: z8.array(z8.string()),
|
|
1383
|
+
/** The values and insertions placed inside a link's address: one of them empty for anybody refuses the send. */
|
|
1384
|
+
link_values: z8.array(z8.string()),
|
|
1385
|
+
/** One entry per resolved recipient, in the request's order; a single entry with `index: null` when none were given. */
|
|
1386
|
+
recipients: z8.array(ResolvedRecipient),
|
|
1387
|
+
/** Items not resolved, by their index in the request, for the reasons `mailings.addRecipients` rejects one. */
|
|
1388
|
+
rejected: z8.array(z8.object({ index: z8.number().int().min(0), reason: z8.enum(RESOLVE_REJECTIONS) })),
|
|
1389
|
+
/** What would refuse `mailings.send` for these recipients, as its `unfilled_link` refusal lists it; empty means none. */
|
|
1390
|
+
blocking: z8.array(UnfilledLink)
|
|
1391
|
+
});
|
|
1175
1392
|
var MessageListQuery = PageQuery.extend({
|
|
1176
1393
|
mailing_id: Id.optional(),
|
|
1177
1394
|
outcome: MessageOutcome.optional()
|
|
1178
1395
|
});
|
|
1179
1396
|
|
|
1180
1397
|
// src/platform.ts
|
|
1181
|
-
var Tag =
|
|
1398
|
+
var Tag = z9.object({
|
|
1182
1399
|
id: Id,
|
|
1183
1400
|
slug: Slug,
|
|
1184
|
-
name:
|
|
1185
|
-
contact_count:
|
|
1401
|
+
name: z9.string().min(1).max(120),
|
|
1402
|
+
contact_count: z9.number().int().min(0),
|
|
1186
1403
|
created_at: Timestamp
|
|
1187
1404
|
});
|
|
1188
|
-
var TagCreate =
|
|
1189
|
-
var TagAssignment =
|
|
1190
|
-
contact_ids:
|
|
1405
|
+
var TagCreate = z9.object({ slug: Slug, name: z9.string().min(1).max(120) });
|
|
1406
|
+
var TagAssignment = z9.object({
|
|
1407
|
+
contact_ids: z9.array(Id).min(1).max(1e3)
|
|
1191
1408
|
});
|
|
1192
1409
|
var CONTACT_PROPERTY_TYPES = ["string", "number", "boolean", "date"];
|
|
1193
|
-
var ContactPropertyType =
|
|
1194
|
-
var ContactPropertyKey =
|
|
1195
|
-
var ContactPropertyDefinition =
|
|
1410
|
+
var ContactPropertyType = z9.enum(CONTACT_PROPERTY_TYPES);
|
|
1411
|
+
var ContactPropertyKey = z9.string().regex(/^[A-Za-z0-9_.-]{1,64}$/, "letters, digits, dot, underscore and hyphen, 1 to 64");
|
|
1412
|
+
var ContactPropertyDefinition = z9.object({
|
|
1196
1413
|
key: ContactPropertyKey,
|
|
1197
|
-
label:
|
|
1414
|
+
label: z9.string().min(1).max(120),
|
|
1198
1415
|
type: ContactPropertyType
|
|
1199
1416
|
});
|
|
1200
|
-
var ContactPropertyParams =
|
|
1417
|
+
var ContactPropertyParams = z9.object({ key: ContactPropertyKey });
|
|
1201
1418
|
var FILTER_OPERATORS = [
|
|
1202
1419
|
"eq",
|
|
1203
1420
|
"neq",
|
|
@@ -1213,8 +1430,8 @@ var FILTER_OPERATORS = [
|
|
|
1213
1430
|
"in",
|
|
1214
1431
|
"not_in"
|
|
1215
1432
|
];
|
|
1216
|
-
var FilterOperator =
|
|
1217
|
-
var FilterField =
|
|
1433
|
+
var FilterOperator = z9.enum(FILTER_OPERATORS);
|
|
1434
|
+
var FilterField = z9.string().regex(
|
|
1218
1435
|
/^(email|first_name|last_name|locale|created_at|tag|topic|engagement:(opened|clicked)|property:[A-Za-z0-9_.-]{1,64})$/,
|
|
1219
1436
|
"unknown filter field"
|
|
1220
1437
|
);
|
|
@@ -1230,18 +1447,18 @@ var FILTER_FIELD_OPERATORS = {
|
|
|
1230
1447
|
property: FILTER_OPERATORS
|
|
1231
1448
|
};
|
|
1232
1449
|
var MAX_FILTER_DEPTH = 5;
|
|
1233
|
-
var FilterCondition =
|
|
1450
|
+
var FilterCondition = z9.object({ field: FilterField, op: FilterOperator, value: JsonValue.optional() }).strict().refine((c) => (c.op === "exists" || c.op === "not_exists") === (c.value === void 0), {
|
|
1234
1451
|
message: "exists and not_exists take no value; every other operator needs one",
|
|
1235
1452
|
path: ["value"]
|
|
1236
1453
|
}).refine((c) => c.op !== "in" && c.op !== "not_in" || Array.isArray(c.value), {
|
|
1237
1454
|
message: "in and not_in take an array",
|
|
1238
1455
|
path: ["value"]
|
|
1239
1456
|
});
|
|
1240
|
-
var SegmentFilter =
|
|
1241
|
-
() =>
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1457
|
+
var SegmentFilter = z9.lazy(
|
|
1458
|
+
() => z9.union([
|
|
1459
|
+
z9.object({ and: z9.array(SegmentFilter).min(1).max(50) }).strict(),
|
|
1460
|
+
z9.object({ or: z9.array(SegmentFilter).min(1).max(50) }).strict(),
|
|
1461
|
+
z9.object({ not: SegmentFilter }).strict(),
|
|
1245
1462
|
FilterCondition
|
|
1246
1463
|
])
|
|
1247
1464
|
);
|
|
@@ -1329,42 +1546,42 @@ function untypedValueProblem(c, every) {
|
|
|
1329
1546
|
if (orderOp) return every(isFilterNumber, "numbers (define the property to compare dates)");
|
|
1330
1547
|
return every((v) => isScalar(v) || v === null, "strings, numbers, booleans or null");
|
|
1331
1548
|
}
|
|
1332
|
-
var Segment =
|
|
1549
|
+
var Segment = z9.object({
|
|
1333
1550
|
id: Id,
|
|
1334
|
-
name:
|
|
1551
|
+
name: z9.string().min(1).max(120),
|
|
1335
1552
|
filter: SegmentFilter,
|
|
1336
1553
|
/** Computed on read. */
|
|
1337
|
-
contact_count:
|
|
1554
|
+
contact_count: z9.number().int().min(0),
|
|
1338
1555
|
created_at: Timestamp,
|
|
1339
1556
|
updated_at: Timestamp
|
|
1340
1557
|
});
|
|
1341
|
-
var SegmentCreate =
|
|
1342
|
-
var SegmentPreviewRequest =
|
|
1343
|
-
var SegmentPreview =
|
|
1344
|
-
contact_count:
|
|
1558
|
+
var SegmentCreate = z9.object({ name: z9.string().min(1).max(120), filter: BoundedSegmentFilter });
|
|
1559
|
+
var SegmentPreviewRequest = z9.object({ filter: BoundedSegmentFilter });
|
|
1560
|
+
var SegmentPreview = z9.object({
|
|
1561
|
+
contact_count: z9.number().int().min(0),
|
|
1345
1562
|
/** Up to five matching contacts, for a human to sanity-check the filter. */
|
|
1346
|
-
sample:
|
|
1563
|
+
sample: z9.array(z9.object({ id: Id, email: Email })).max(5)
|
|
1347
1564
|
});
|
|
1348
|
-
var MailingAudienceFromSegment =
|
|
1565
|
+
var MailingAudienceFromSegment = z9.object({ segment_id: Id });
|
|
1349
1566
|
var SIGNUP_FORM_FIELDS = ["first_name", "last_name"];
|
|
1350
1567
|
var HOSTED_PAGE_LOCALES = ["en", "de", "it", "fr", "es"];
|
|
1351
|
-
var SignupFormTranslation =
|
|
1352
|
-
title:
|
|
1353
|
-
consent_text:
|
|
1568
|
+
var SignupFormTranslation = z9.object({
|
|
1569
|
+
title: z9.string().min(1).max(120),
|
|
1570
|
+
consent_text: z9.string().min(1).max(2e3)
|
|
1354
1571
|
});
|
|
1355
|
-
var SignupForm =
|
|
1572
|
+
var SignupForm = z9.object({
|
|
1356
1573
|
id: Id,
|
|
1357
|
-
name:
|
|
1574
|
+
name: z9.string().min(1).max(120),
|
|
1358
1575
|
/** The heading of the hosted page. */
|
|
1359
|
-
title:
|
|
1576
|
+
title: z9.string().min(1).max(120),
|
|
1360
1577
|
/** What the person agrees to, shown above the button and recorded with the confirmation. */
|
|
1361
|
-
consent_text:
|
|
1362
|
-
translations:
|
|
1363
|
-
topics:
|
|
1364
|
-
tags:
|
|
1578
|
+
consent_text: z9.string().min(1).max(2e3),
|
|
1579
|
+
translations: z9.record(SignupFormTranslation),
|
|
1580
|
+
topics: z9.array(Slug).min(1),
|
|
1581
|
+
tags: z9.array(Slug),
|
|
1365
1582
|
/** Fields shown besides email. */
|
|
1366
|
-
fields:
|
|
1367
|
-
double_opt_in:
|
|
1583
|
+
fields: z9.array(z9.enum(SIGNUP_FORM_FIELDS)),
|
|
1584
|
+
double_opt_in: z9.literal(true),
|
|
1368
1585
|
/** The provider the confirmation mail is sent through. */
|
|
1369
1586
|
provider_id: Id,
|
|
1370
1587
|
/**
|
|
@@ -1373,11 +1590,11 @@ var SignupForm = z8.object({
|
|
|
1373
1590
|
*/
|
|
1374
1591
|
confirmation_template_id: Id.nullable(),
|
|
1375
1592
|
/** Where the person lands after confirming. Null shows the hosted confirmation page. */
|
|
1376
|
-
redirect_url:
|
|
1593
|
+
redirect_url: z9.string().url().nullable(),
|
|
1377
1594
|
/** Origins allowed to embed the form and call the submission route from a browser. */
|
|
1378
|
-
allowed_origins:
|
|
1595
|
+
allowed_origins: z9.array(z9.string().url()).max(20),
|
|
1379
1596
|
/** Bumped on every change; recorded with each confirmation. */
|
|
1380
|
-
version:
|
|
1597
|
+
version: z9.number().int().min(1),
|
|
1381
1598
|
created_at: Timestamp,
|
|
1382
1599
|
updated_at: Timestamp
|
|
1383
1600
|
});
|
|
@@ -1392,56 +1609,56 @@ var SignupFormCreate = SignupForm.omit({
|
|
|
1392
1609
|
tags: SignupForm.shape.tags.optional(),
|
|
1393
1610
|
fields: SignupForm.shape.fields.optional(),
|
|
1394
1611
|
confirmation_template_id: Id.nullable().optional(),
|
|
1395
|
-
redirect_url:
|
|
1612
|
+
redirect_url: z9.string().url().nullable().optional(),
|
|
1396
1613
|
allowed_origins: SignupForm.shape.allowed_origins.optional()
|
|
1397
1614
|
});
|
|
1398
|
-
var SignupFormEmbed =
|
|
1399
|
-
hosted_url:
|
|
1615
|
+
var SignupFormEmbed = z9.object({
|
|
1616
|
+
hosted_url: z9.string().url(),
|
|
1400
1617
|
/** A plain HTML form posting to the hosted page; works without JavaScript. */
|
|
1401
|
-
html:
|
|
1618
|
+
html: z9.string(),
|
|
1402
1619
|
/** A small optional script that adds the time check and submits in place. */
|
|
1403
|
-
script_url:
|
|
1620
|
+
script_url: z9.string().url(),
|
|
1404
1621
|
/** Where a browser fetches a fresh `form_token` from (GET, public, CORS for `allowed_origins`). */
|
|
1405
|
-
token_url:
|
|
1622
|
+
token_url: z9.string().url()
|
|
1406
1623
|
});
|
|
1407
|
-
var SignupSubmission =
|
|
1624
|
+
var SignupSubmission = z9.object({
|
|
1408
1625
|
email: Email,
|
|
1409
|
-
first_name:
|
|
1410
|
-
last_name:
|
|
1411
|
-
locale:
|
|
1626
|
+
first_name: z9.string().max(200).optional(),
|
|
1627
|
+
last_name: z9.string().max(200).optional(),
|
|
1628
|
+
locale: z9.string().min(2).max(35).optional(),
|
|
1412
1629
|
/** Honeypot: must be empty. */
|
|
1413
|
-
website:
|
|
1630
|
+
website: z9.string().max(0).optional(),
|
|
1414
1631
|
/**
|
|
1415
1632
|
* The signed render time from the form's token URL: a submission sooner than a
|
|
1416
1633
|
* human could fill the form, or long after, is refused.
|
|
1417
1634
|
*/
|
|
1418
|
-
form_token:
|
|
1635
|
+
form_token: z9.string().min(1).max(512)
|
|
1419
1636
|
});
|
|
1420
1637
|
var IMPORT_STATUSES = ["uploaded", "validating", "validated", "committing", "completed", "failed", "cancelled"];
|
|
1421
|
-
var ImportStatus =
|
|
1638
|
+
var ImportStatus = z9.enum(IMPORT_STATUSES);
|
|
1422
1639
|
var IMPORT_TERMINAL_STATUSES = ["completed", "failed", "cancelled"];
|
|
1423
|
-
var ImportColumnMapping =
|
|
1424
|
-
|
|
1640
|
+
var ImportColumnMapping = z9.record(
|
|
1641
|
+
z9.string().regex(/^(email|external_id|first_name|last_name|locale|property:[A-Za-z0-9_.-]{1,64}|ignore)$/)
|
|
1425
1642
|
);
|
|
1426
1643
|
var IMPORT_FILE_FIELD = "file";
|
|
1427
1644
|
var MAX_IMPORT_BYTES = 50 * 1024 * 1024;
|
|
1428
1645
|
var MAX_IMPORT_ROWS = 2e5;
|
|
1429
|
-
var ImportMappingRequest =
|
|
1646
|
+
var ImportMappingRequest = z9.object({
|
|
1430
1647
|
/** CSV header -> contact field. Exactly one column maps to `email`. */
|
|
1431
1648
|
mapping: ImportColumnMapping.refine(
|
|
1432
1649
|
(m) => Object.values(m).filter((v) => v === "email").length === 1,
|
|
1433
1650
|
"exactly one column maps to email"
|
|
1434
1651
|
),
|
|
1435
|
-
topics:
|
|
1436
|
-
tags:
|
|
1652
|
+
topics: z9.array(Slug).max(100),
|
|
1653
|
+
tags: z9.array(Slug).max(100),
|
|
1437
1654
|
/** Existing contacts get the mapped fields overwritten; false only adds topics, tags and missing fields. */
|
|
1438
|
-
update_existing:
|
|
1655
|
+
update_existing: z9.boolean().optional(),
|
|
1439
1656
|
/** The importer states the people consented; the service records who said so and when. */
|
|
1440
|
-
consent_confirmed:
|
|
1657
|
+
consent_confirmed: z9.literal(true)
|
|
1441
1658
|
});
|
|
1442
|
-
var ImportCommitRequest =
|
|
1659
|
+
var ImportCommitRequest = z9.object({ mapping_version: z9.number().int().min(1) });
|
|
1443
1660
|
var IMPORT_ROW_OUTCOMES = ["created", "updated", "unchanged", "suppressed", "skipped"];
|
|
1444
|
-
var ImportRowOutcome =
|
|
1661
|
+
var ImportRowOutcome = z9.enum(IMPORT_ROW_OUTCOMES);
|
|
1445
1662
|
var IMPORT_SKIP_REASONS = [
|
|
1446
1663
|
"missing_email",
|
|
1447
1664
|
"invalid_email",
|
|
@@ -1450,90 +1667,90 @@ var IMPORT_SKIP_REASONS = [
|
|
|
1450
1667
|
"external_id_conflict",
|
|
1451
1668
|
"wrong_column_count"
|
|
1452
1669
|
];
|
|
1453
|
-
var ImportSkipReason =
|
|
1454
|
-
var ImportReport =
|
|
1455
|
-
created:
|
|
1456
|
-
updated:
|
|
1457
|
-
unchanged:
|
|
1458
|
-
suppressed:
|
|
1459
|
-
skipped:
|
|
1460
|
-
skipped_by_reason:
|
|
1670
|
+
var ImportSkipReason = z9.enum(IMPORT_SKIP_REASONS);
|
|
1671
|
+
var ImportReport = z9.object({
|
|
1672
|
+
created: z9.number().int().min(0),
|
|
1673
|
+
updated: z9.number().int().min(0),
|
|
1674
|
+
unchanged: z9.number().int().min(0),
|
|
1675
|
+
suppressed: z9.number().int().min(0),
|
|
1676
|
+
skipped: z9.number().int().min(0),
|
|
1677
|
+
skipped_by_reason: z9.record(ImportSkipReason, z9.number().int().min(0)),
|
|
1461
1678
|
/** Rows whose contact was subscribed without some topic, because the address is blocked for it. */
|
|
1462
|
-
topics_withheld:
|
|
1679
|
+
topics_withheld: z9.number().int().min(0)
|
|
1463
1680
|
});
|
|
1464
|
-
var ImportJob =
|
|
1681
|
+
var ImportJob = z9.object({
|
|
1465
1682
|
id: Id,
|
|
1466
1683
|
status: ImportStatus,
|
|
1467
|
-
file_name:
|
|
1468
|
-
file_bytes:
|
|
1469
|
-
total_rows:
|
|
1684
|
+
file_name: z9.string().max(255).nullable(),
|
|
1685
|
+
file_bytes: z9.number().int().min(0),
|
|
1686
|
+
total_rows: z9.number().int().min(0),
|
|
1470
1687
|
/** The header row, as in the file. */
|
|
1471
|
-
columns:
|
|
1688
|
+
columns: z9.array(z9.string()),
|
|
1472
1689
|
/** The first rows, for the mapping screen. */
|
|
1473
|
-
sample:
|
|
1690
|
+
sample: z9.array(z9.array(z9.string())).max(5),
|
|
1474
1691
|
/** A mapping guessed from the headers, for the mapping screen to start from. */
|
|
1475
1692
|
suggested_mapping: ImportColumnMapping,
|
|
1476
1693
|
mapping: ImportColumnMapping.nullable(),
|
|
1477
|
-
mapping_version:
|
|
1478
|
-
topics:
|
|
1479
|
-
tags:
|
|
1480
|
-
update_existing:
|
|
1694
|
+
mapping_version: z9.number().int().min(0),
|
|
1695
|
+
topics: z9.array(Slug),
|
|
1696
|
+
tags: z9.array(Slug),
|
|
1697
|
+
update_existing: z9.boolean(),
|
|
1481
1698
|
/** The dry run of the current mapping; null before one finished. */
|
|
1482
1699
|
dry_run: ImportReport.nullable(),
|
|
1483
1700
|
/** What the commit has written so far; null before it started. */
|
|
1484
1701
|
result: ImportReport.nullable(),
|
|
1485
1702
|
/** Rows the current phase (dry run or commit) has processed. */
|
|
1486
|
-
processed_rows:
|
|
1703
|
+
processed_rows: z9.number().int().min(0),
|
|
1487
1704
|
/** Why a `failed` import failed. */
|
|
1488
|
-
error:
|
|
1705
|
+
error: z9.string().nullable(),
|
|
1489
1706
|
created_at: Timestamp,
|
|
1490
1707
|
updated_at: Timestamp,
|
|
1491
1708
|
finished_at: Timestamp.nullable()
|
|
1492
1709
|
});
|
|
1493
|
-
var ImportRow =
|
|
1710
|
+
var ImportRow = z9.object({
|
|
1494
1711
|
/** The line in the file, 1 being the first line after the header. */
|
|
1495
|
-
row:
|
|
1496
|
-
email:
|
|
1712
|
+
row: z9.number().int().min(1),
|
|
1713
|
+
email: z9.string().nullable(),
|
|
1497
1714
|
outcome: ImportRowOutcome,
|
|
1498
1715
|
reason: ImportSkipReason.nullable(),
|
|
1499
1716
|
/** A human explanation for a skipped row (which value, which column). */
|
|
1500
|
-
message:
|
|
1717
|
+
message: z9.string().nullable(),
|
|
1501
1718
|
contact_id: Id.nullable()
|
|
1502
1719
|
});
|
|
1503
1720
|
var ImportRowListQuery = PageQuery.extend({ outcome: ImportRowOutcome.optional() });
|
|
1504
|
-
var MailingScheduleRequest =
|
|
1505
|
-
var TrackingSettings =
|
|
1506
|
-
var VariantAnalytics =
|
|
1507
|
-
key:
|
|
1508
|
-
sent:
|
|
1509
|
-
unique_opens:
|
|
1510
|
-
unique_clicks:
|
|
1511
|
-
});
|
|
1512
|
-
var MailingAnalytics =
|
|
1721
|
+
var MailingScheduleRequest = z9.object({ send_at: Timestamp });
|
|
1722
|
+
var TrackingSettings = z9.object({ opens: z9.boolean(), clicks: z9.boolean() });
|
|
1723
|
+
var VariantAnalytics = z9.object({
|
|
1724
|
+
key: z9.string(),
|
|
1725
|
+
sent: z9.number().int().min(0),
|
|
1726
|
+
unique_opens: z9.number().int().min(0).nullable(),
|
|
1727
|
+
unique_clicks: z9.number().int().min(0).nullable()
|
|
1728
|
+
});
|
|
1729
|
+
var MailingAnalytics = z9.object({
|
|
1513
1730
|
mailing_id: Id,
|
|
1514
1731
|
counts: MailingCounts,
|
|
1515
1732
|
/** What was tracked for this mailing, fixed when it started; null before it started. */
|
|
1516
1733
|
tracking: TrackingSettings.nullable(),
|
|
1517
1734
|
/** Unique recipients with a human open. Null when opens were not tracked. */
|
|
1518
|
-
unique_opens:
|
|
1735
|
+
unique_opens: z9.number().int().min(0).nullable(),
|
|
1519
1736
|
/**
|
|
1520
1737
|
* Recipients whose only opens came from Apple Mail Privacy Protection, which
|
|
1521
1738
|
* loads every image on delivery: counted apart, never as human opens.
|
|
1522
1739
|
*/
|
|
1523
|
-
apple_mpp_opens:
|
|
1740
|
+
apple_mpp_opens: z9.number().int().min(0).nullable(),
|
|
1524
1741
|
/** Opens and clicks from security scanners and prefetchers, filtered out of the unique counts. */
|
|
1525
|
-
machine_events:
|
|
1526
|
-
unique_clicks:
|
|
1527
|
-
unsubscribes:
|
|
1528
|
-
bounces:
|
|
1529
|
-
complaints:
|
|
1530
|
-
links:
|
|
1531
|
-
variants:
|
|
1742
|
+
machine_events: z9.number().int().min(0).nullable(),
|
|
1743
|
+
unique_clicks: z9.number().int().min(0).nullable(),
|
|
1744
|
+
unsubscribes: z9.number().int().min(0),
|
|
1745
|
+
bounces: z9.number().int().min(0),
|
|
1746
|
+
complaints: z9.number().int().min(0),
|
|
1747
|
+
links: z9.array(z9.object({ url: z9.string(), unique_clicks: z9.number().int().min(0) })).nullable(),
|
|
1748
|
+
variants: z9.array(VariantAnalytics).nullable()
|
|
1532
1749
|
});
|
|
1533
1750
|
|
|
1534
1751
|
// src/automations.ts
|
|
1535
1752
|
var AUTOMATION_FILTER_FIELD_PATTERN = /^(email|first_name|last_name|locale|created_at|tag|topic|segment|engagement:(opened|clicked)|step:(opened|clicked)|property:[A-Za-z0-9_.-]{1,64}|event:[A-Za-z0-9_.-]{1,64})$/;
|
|
1536
|
-
var AutomationFilterField =
|
|
1753
|
+
var AutomationFilterField = z10.string().regex(AUTOMATION_FILTER_FIELD_PATTERN, "unknown filter field");
|
|
1537
1754
|
var AUTOMATION_FILTER_FIELD_OPERATORS = {
|
|
1538
1755
|
...FILTER_FIELD_OPERATORS,
|
|
1539
1756
|
segment: ["eq", "neq", "in", "not_in"],
|
|
@@ -1543,18 +1760,18 @@ var AUTOMATION_FILTER_FIELD_OPERATORS = {
|
|
|
1543
1760
|
step: ["eq", "neq"]
|
|
1544
1761
|
};
|
|
1545
1762
|
var automationFilterFamily = filterFieldFamily;
|
|
1546
|
-
var AutomationFilterCondition =
|
|
1763
|
+
var AutomationFilterCondition = z10.object({ field: AutomationFilterField, op: FilterOperator, value: JsonValue.optional() }).strict().refine((c) => (c.op === "exists" || c.op === "not_exists") === (c.value === void 0), {
|
|
1547
1764
|
message: "exists and not_exists take no value; every other operator needs one",
|
|
1548
1765
|
path: ["value"]
|
|
1549
1766
|
}).refine((c) => c.op !== "in" && c.op !== "not_in" || Array.isArray(c.value), {
|
|
1550
1767
|
message: "in and not_in take an array",
|
|
1551
1768
|
path: ["value"]
|
|
1552
1769
|
});
|
|
1553
|
-
var AutomationFilter =
|
|
1554
|
-
() =>
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1770
|
+
var AutomationFilter = z10.lazy(
|
|
1771
|
+
() => z10.union([
|
|
1772
|
+
z10.object({ and: z10.array(AutomationFilter).min(1).max(50) }).strict(),
|
|
1773
|
+
z10.object({ or: z10.array(AutomationFilter).min(1).max(50) }).strict(),
|
|
1774
|
+
z10.object({ not: AutomationFilter }).strict(),
|
|
1558
1775
|
AutomationFilterCondition
|
|
1559
1776
|
])
|
|
1560
1777
|
);
|
|
@@ -1567,20 +1784,20 @@ function filterConditions(filter) {
|
|
|
1567
1784
|
if ("not" in filter) return filterConditions(filter.not);
|
|
1568
1785
|
return [filter];
|
|
1569
1786
|
}
|
|
1570
|
-
var StepId =
|
|
1571
|
-
var TimeOfDay =
|
|
1572
|
-
var Weekday =
|
|
1573
|
-
var TimezoneName =
|
|
1787
|
+
var StepId = z10.string().regex(/^[A-Za-z0-9_-]{1,64}$/, "letters, digits, underscore and hyphen, 1 to 64");
|
|
1788
|
+
var TimeOfDay = z10.string().regex(/^([01]\d|2[0-3]):[0-5]\d$/, "HH:MM in 24 hours");
|
|
1789
|
+
var Weekday = z10.number().int().min(0).max(6);
|
|
1790
|
+
var TimezoneName = z10.string().min(1).max(64).regex(/^(UTC|[A-Za-z][A-Za-z0-9_+-]*(?:\/[A-Za-z0-9_+-]+)+)$/, "an IANA time zone name such as Europe/Berlin");
|
|
1574
1791
|
var MAX_DELAY_SECONDS = 365 * 24 * 60 * 60;
|
|
1575
|
-
var DateOnly =
|
|
1792
|
+
var DateOnly = z10.string().regex(/^\d{4}-\d{2}-\d{2}$/, "a calendar date such as 2026-09-20").refine((v) => {
|
|
1576
1793
|
const [y, m, d] = v.split("-").map(Number);
|
|
1577
1794
|
const t = new Date(Date.UTC(y, m - 1, d));
|
|
1578
1795
|
return t.getUTCFullYear() === y && t.getUTCMonth() + 1 === m && t.getUTCDate() === d;
|
|
1579
1796
|
}, "a real calendar date");
|
|
1580
|
-
var OffsetDays =
|
|
1797
|
+
var OffsetDays = z10.number().int().min(-365).max(365);
|
|
1581
1798
|
var DATE_PASSED_ANSWERS = ["skip", "stop", "continue"];
|
|
1582
|
-
var DatePassedAnswer =
|
|
1583
|
-
var EventName =
|
|
1799
|
+
var DatePassedAnswer = z10.enum(DATE_PASSED_ANSWERS);
|
|
1800
|
+
var EventName = z10.string().regex(/^[A-Za-z0-9][A-Za-z0-9_.:-]{0,79}$/, "letters, digits, dot, colon, underscore and hyphen, 1 to 80");
|
|
1584
1801
|
var AUTOMATION_TRIGGER_KINDS = [
|
|
1585
1802
|
"topic_subscribed",
|
|
1586
1803
|
"form_confirmed",
|
|
@@ -1593,17 +1810,17 @@ var AUTOMATION_TRIGGER_KINDS = [
|
|
|
1593
1810
|
"segment_entered",
|
|
1594
1811
|
"link_clicked"
|
|
1595
1812
|
];
|
|
1596
|
-
var AutomationTriggerKind =
|
|
1813
|
+
var AutomationTriggerKind = z10.enum(AUTOMATION_TRIGGER_KINDS);
|
|
1597
1814
|
var exclude = { exclude: BoundedAutomationFilter.nullish() };
|
|
1598
|
-
var AutomationTrigger =
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1815
|
+
var AutomationTrigger = z10.discriminatedUnion("kind", [
|
|
1816
|
+
z10.object({ kind: z10.literal("topic_subscribed"), topic: Slug, ...exclude }).strict(),
|
|
1817
|
+
z10.object({ kind: z10.literal("form_confirmed"), form_id: Id, ...exclude }).strict(),
|
|
1818
|
+
z10.object({ kind: z10.literal("tag_added"), tag: Slug, ...exclude }).strict(),
|
|
1602
1819
|
/** `to` given: only a change that lands on this value fires. Absent: any change of the key. */
|
|
1603
|
-
|
|
1820
|
+
z10.object({ kind: z10.literal("property_changed"), key: ContactPropertyKey, to: JsonValue.optional(), ...exclude }).strict(),
|
|
1604
1821
|
/** `where` filters the event's own data, so only `event:<key>` leaves belong in it. */
|
|
1605
|
-
|
|
1606
|
-
|
|
1822
|
+
z10.object({ kind: z10.literal("event"), name: EventName, where: BoundedAutomationFilter.nullish(), ...exclude }).strict(),
|
|
1823
|
+
z10.object({ kind: z10.literal("manual"), ...exclude }).strict(),
|
|
1607
1824
|
/**
|
|
1608
1825
|
* Every year on a date the contact carries: a birthday, a joining date, a
|
|
1609
1826
|
* renewal. `offset_days` moves it ("three days before"), `at` is the wall
|
|
@@ -1613,8 +1830,8 @@ var AutomationTrigger = z9.discriminatedUnion("kind", [
|
|
|
1613
1830
|
* A 29 February date fires on 1 March in a year that has no 29 February, so
|
|
1614
1831
|
* nobody is skipped every fourth year.
|
|
1615
1832
|
*/
|
|
1616
|
-
|
|
1617
|
-
kind:
|
|
1833
|
+
z10.object({
|
|
1834
|
+
kind: z10.literal("date_anniversary"),
|
|
1618
1835
|
key: ContactPropertyKey,
|
|
1619
1836
|
offset_days: OffsetDays,
|
|
1620
1837
|
at: TimeOfDay,
|
|
@@ -1626,8 +1843,8 @@ var AutomationTrigger = z9.discriminatedUnion("kind", [
|
|
|
1626
1843
|
* not ask about a segment or about an event's data: there is no event, and a
|
|
1627
1844
|
* segment is its own trigger kind.
|
|
1628
1845
|
*/
|
|
1629
|
-
|
|
1630
|
-
kind:
|
|
1846
|
+
z10.object({
|
|
1847
|
+
kind: z10.literal("exact_date"),
|
|
1631
1848
|
on: DateOnly,
|
|
1632
1849
|
at: TimeOfDay,
|
|
1633
1850
|
where: BoundedAutomationFilter.nullish(),
|
|
@@ -1646,7 +1863,7 @@ var AutomationTrigger = z9.discriminatedUnion("kind", [
|
|
|
1646
1863
|
* already exists. Entering those people as well is the publish dialog's
|
|
1647
1864
|
* backfill question, answered by a person rather than by the sweep.
|
|
1648
1865
|
*/
|
|
1649
|
-
|
|
1866
|
+
z10.object({ kind: z10.literal("segment_entered"), segment_id: Id, ...exclude }).strict(),
|
|
1650
1867
|
/**
|
|
1651
1868
|
* S4. When somebody clicks a link in a mailing that has already gone out.
|
|
1652
1869
|
*
|
|
@@ -1656,7 +1873,7 @@ var AutomationTrigger = z9.discriminatedUnion("kind", [
|
|
|
1656
1873
|
* tracking off there is no click to hear about, so the validator refuses it
|
|
1657
1874
|
* rather than leaving an automation that can never fire.
|
|
1658
1875
|
*/
|
|
1659
|
-
|
|
1876
|
+
z10.object({ kind: z10.literal("link_clicked"), mailing_id: Id, url: z10.string().min(1).max(2048).optional(), ...exclude }).strict()
|
|
1660
1877
|
]);
|
|
1661
1878
|
var AUTOMATION_DATE_TRIGGER_KINDS = ["date_anniversary", "exact_date"];
|
|
1662
1879
|
var MAX_TRIGGERS = 10;
|
|
@@ -1675,45 +1892,45 @@ var AUTOMATION_STEP_KINDS = [
|
|
|
1675
1892
|
"webhook",
|
|
1676
1893
|
"notify"
|
|
1677
1894
|
];
|
|
1678
|
-
var AutomationStepKind =
|
|
1895
|
+
var AutomationStepKind = z10.enum(AUTOMATION_STEP_KINDS);
|
|
1679
1896
|
var next = { next: StepId.nullable() };
|
|
1680
|
-
var Unchosen =
|
|
1681
|
-
var DraftSlug =
|
|
1682
|
-
var DraftPropertyKey =
|
|
1683
|
-
var label2 = { label:
|
|
1684
|
-
var EmailStepLanguage =
|
|
1685
|
-
subject:
|
|
1686
|
-
preheader:
|
|
1897
|
+
var Unchosen = z10.literal("");
|
|
1898
|
+
var DraftSlug = z10.union([Slug, Unchosen]);
|
|
1899
|
+
var DraftPropertyKey = z10.union([ContactPropertyKey, Unchosen]);
|
|
1900
|
+
var label2 = { label: z10.string().max(120).nullish() };
|
|
1901
|
+
var EmailStepLanguage = z10.object({
|
|
1902
|
+
subject: z10.string().min(1).max(998),
|
|
1903
|
+
preheader: z10.string().max(500).nullable(),
|
|
1687
1904
|
document: TemplateDocument,
|
|
1688
1905
|
/** This language's insertions, its texts over the template's structure (every text present). */
|
|
1689
1906
|
insertions: Insertions.nullish()
|
|
1690
1907
|
}).strict();
|
|
1691
|
-
var EmailStep =
|
|
1692
|
-
kind:
|
|
1908
|
+
var EmailStep = z10.object({
|
|
1909
|
+
kind: z10.literal("email"),
|
|
1693
1910
|
...label2,
|
|
1694
1911
|
...next,
|
|
1695
1912
|
template_id: Id.nullish(),
|
|
1696
1913
|
document: TemplateDocument.nullish(),
|
|
1697
1914
|
/** Empty while the step is being written, or when its template has a subject; else `not_chosen` refuses to publish it. */
|
|
1698
|
-
subject:
|
|
1699
|
-
preheader:
|
|
1915
|
+
subject: z10.string().max(998),
|
|
1916
|
+
preheader: z10.string().max(500).nullish(),
|
|
1700
1917
|
locale: LocaleTag.nullish(),
|
|
1701
|
-
languages:
|
|
1918
|
+
languages: z10.record(LocaleTag, EmailStepLanguage).nullish(),
|
|
1702
1919
|
/** The main language's insertions, snapshot from the template when the automation is published. */
|
|
1703
1920
|
insertions: Insertions.nullish()
|
|
1704
1921
|
}).strict();
|
|
1705
|
-
var DelayStep =
|
|
1706
|
-
kind:
|
|
1922
|
+
var DelayStep = z10.object({
|
|
1923
|
+
kind: z10.literal("delay"),
|
|
1707
1924
|
...label2,
|
|
1708
1925
|
...next,
|
|
1709
|
-
wait:
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1926
|
+
wait: z10.discriminatedUnion("mode", [
|
|
1927
|
+
z10.object({ mode: z10.literal("duration"), seconds: z10.number().int().min(1).max(MAX_DELAY_SECONDS) }).strict(),
|
|
1928
|
+
z10.object({ mode: z10.literal("time_of_day"), at: TimeOfDay }).strict(),
|
|
1929
|
+
z10.object({ mode: z10.literal("weekday"), weekday: Weekday, at: TimeOfDay }).strict(),
|
|
1713
1930
|
/** A fixed day has no next occurrence, so `continue` is not one of its answers. */
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
mode:
|
|
1931
|
+
z10.object({ mode: z10.literal("date"), on: DateOnly, at: TimeOfDay, passed: z10.enum(["skip", "stop"]) }).strict(),
|
|
1932
|
+
z10.object({
|
|
1933
|
+
mode: z10.literal("date_property"),
|
|
1717
1934
|
key: ContactPropertyKey,
|
|
1718
1935
|
offset_days: OffsetDays,
|
|
1719
1936
|
at: TimeOfDay,
|
|
@@ -1721,8 +1938,8 @@ var DelayStep = z9.object({
|
|
|
1721
1938
|
}).strict()
|
|
1722
1939
|
])
|
|
1723
1940
|
}).strict();
|
|
1724
|
-
var ConditionStep =
|
|
1725
|
-
kind:
|
|
1941
|
+
var ConditionStep = z10.object({
|
|
1942
|
+
kind: z10.literal("condition"),
|
|
1726
1943
|
...label2,
|
|
1727
1944
|
filter: BoundedAutomationFilter,
|
|
1728
1945
|
yes: StepId.nullable(),
|
|
@@ -1733,49 +1950,49 @@ var ConditionStep = z9.object({
|
|
|
1733
1950
|
* neither half means anything without the other: a window with no answer
|
|
1734
1951
|
* for the end of it would be a silent decision.
|
|
1735
1952
|
*/
|
|
1736
|
-
wait_for:
|
|
1953
|
+
wait_for: z10.object({ up_to_s: z10.number().int().min(1).max(MAX_DELAY_SECONDS), on_timeout: z10.enum(["yes", "no"]) }).strict().nullish()
|
|
1737
1954
|
}).strict();
|
|
1738
|
-
var SplitBranchKey =
|
|
1955
|
+
var SplitBranchKey = z10.string().regex(/^[A-Za-z0-9_-]{1,32}$/, "letters, digits, underscore and hyphen, 1 to 32");
|
|
1739
1956
|
var MIN_SPLIT_BRANCHES = 2;
|
|
1740
1957
|
var MAX_SPLIT_BRANCHES = 4;
|
|
1741
|
-
var SplitStep =
|
|
1742
|
-
kind:
|
|
1958
|
+
var SplitStep = z10.object({
|
|
1959
|
+
kind: z10.literal("split"),
|
|
1743
1960
|
...label2,
|
|
1744
|
-
branches:
|
|
1745
|
-
|
|
1961
|
+
branches: z10.array(
|
|
1962
|
+
z10.object({
|
|
1746
1963
|
key: SplitBranchKey,
|
|
1747
1964
|
/** A whole percentage. Zero is allowed: a branch kept in the flow but switched off. */
|
|
1748
|
-
weight:
|
|
1965
|
+
weight: z10.number().int().min(0).max(100),
|
|
1749
1966
|
next: StepId.nullable()
|
|
1750
1967
|
}).strict()
|
|
1751
1968
|
).min(MIN_SPLIT_BRANCHES).max(MAX_SPLIT_BRANCHES).refine((bs) => new Set(bs.map((b) => b.key)).size === bs.length, "each branch needs its own key").refine((bs) => bs.reduce((sum, b) => sum + b.weight, 0) === 100, "the weights have to come to 100")
|
|
1752
1969
|
}).strict();
|
|
1753
|
-
var GotoStep =
|
|
1970
|
+
var GotoStep = z10.object({ kind: z10.literal("goto"), ...label2, target: StepId.nullable() }).strict();
|
|
1754
1971
|
var MAX_STEP_VISITS = 20;
|
|
1755
|
-
var WebhookStep =
|
|
1756
|
-
var NotifyRecipients =
|
|
1757
|
-
var NotifyStep =
|
|
1758
|
-
kind:
|
|
1972
|
+
var WebhookStep = z10.object({ kind: z10.literal("webhook"), ...label2, ...next, endpoint_id: Id.nullable(), payload: JsonValue.nullish() }).strict();
|
|
1973
|
+
var NotifyRecipients = z10.union([z10.literal("owner"), z10.literal("admins"), z10.object({ member_id: Id }).strict()]);
|
|
1974
|
+
var NotifyStep = z10.object({
|
|
1975
|
+
kind: z10.literal("notify"),
|
|
1759
1976
|
...label2,
|
|
1760
1977
|
...next,
|
|
1761
1978
|
to: NotifyRecipients,
|
|
1762
1979
|
/** Empty while the step is being written; `not_chosen` refuses to publish it. */
|
|
1763
|
-
subject:
|
|
1764
|
-
body:
|
|
1980
|
+
subject: z10.string().max(998),
|
|
1981
|
+
body: z10.string().max(1e4)
|
|
1765
1982
|
}).strict();
|
|
1766
|
-
var AutomationStep =
|
|
1983
|
+
var AutomationStep = z10.discriminatedUnion("kind", [
|
|
1767
1984
|
EmailStep,
|
|
1768
1985
|
DelayStep,
|
|
1769
1986
|
ConditionStep,
|
|
1770
|
-
|
|
1771
|
-
|
|
1987
|
+
z10.object({ kind: z10.literal("add_tag"), ...label2, ...next, tag: DraftSlug }).strict(),
|
|
1988
|
+
z10.object({ kind: z10.literal("remove_tag"), ...label2, ...next, tag: DraftSlug }).strict(),
|
|
1772
1989
|
/** `value` null clears the property, as a contact upsert does. */
|
|
1773
|
-
|
|
1990
|
+
z10.object({ kind: z10.literal("set_property"), ...label2, ...next, key: DraftPropertyKey, value: JsonValue }).strict(),
|
|
1774
1991
|
/** Subscribing never lifts a suppression: the service checks that, not the flow. */
|
|
1775
|
-
|
|
1992
|
+
z10.object({ kind: z10.literal("subscribe_topic"), ...label2, ...next, topic: DraftSlug }).strict(),
|
|
1776
1993
|
/** `topic` unsubscribes from the automation's own topic, `all` from every topic. */
|
|
1777
|
-
|
|
1778
|
-
|
|
1994
|
+
z10.object({ kind: z10.literal("unsubscribe"), ...label2, ...next, scope: z10.enum(["topic", "all"]) }).strict(),
|
|
1995
|
+
z10.object({ kind: z10.literal("exit"), ...label2 }).strict(),
|
|
1779
1996
|
SplitStep,
|
|
1780
1997
|
GotoStep,
|
|
1781
1998
|
WebhookStep,
|
|
@@ -1799,25 +2016,25 @@ function stepTargets(step) {
|
|
|
1799
2016
|
}
|
|
1800
2017
|
}
|
|
1801
2018
|
var MAX_STEPS = 200;
|
|
1802
|
-
var AutomationFlow =
|
|
1803
|
-
triggers:
|
|
2019
|
+
var AutomationFlow = z10.object({
|
|
2020
|
+
triggers: z10.array(AutomationTrigger).max(MAX_TRIGGERS),
|
|
1804
2021
|
/** The first step. `null` is an empty flow. */
|
|
1805
2022
|
entry: StepId.nullable(),
|
|
1806
|
-
steps:
|
|
2023
|
+
steps: z10.record(StepId, AutomationStep)
|
|
1807
2024
|
}).strict();
|
|
1808
2025
|
var emptyFlow = () => ({ triggers: [], entry: null, steps: {} });
|
|
1809
|
-
var QuietHours =
|
|
2026
|
+
var QuietHours = z10.object({
|
|
1810
2027
|
start: TimeOfDay,
|
|
1811
2028
|
end: TimeOfDay,
|
|
1812
|
-
weekdays:
|
|
2029
|
+
weekdays: z10.array(Weekday).min(1).max(7).nullish()
|
|
1813
2030
|
}).strict();
|
|
1814
2031
|
var RE_ENTRY_MODES = ["off", "after_exit"];
|
|
1815
|
-
var ReEntryMode =
|
|
2032
|
+
var ReEntryMode = z10.enum(RE_ENTRY_MODES);
|
|
1816
2033
|
var MAX_RE_ENTRY_COOLDOWN_SECONDS = 365 * 24 * 60 * 60;
|
|
1817
|
-
var AutomationSettings =
|
|
2034
|
+
var AutomationSettings = z10.object({
|
|
1818
2035
|
re_entry: ReEntryMode,
|
|
1819
2036
|
/** Seconds a person must wait after leaving before entering again. Only with `after_exit`. */
|
|
1820
|
-
re_entry_cooldown_s:
|
|
2037
|
+
re_entry_cooldown_s: z10.number().int().min(0).max(MAX_RE_ENTRY_COOLDOWN_SECONDS).nullish(),
|
|
1821
2038
|
quiet_hours: QuietHours.nullish(),
|
|
1822
2039
|
/** Checked before every step; when it matches, the run ends. */
|
|
1823
2040
|
exit_filter: BoundedAutomationFilter.nullish()
|
|
@@ -1875,18 +2092,18 @@ var AUTOMATION_ISSUE_CODES = [
|
|
|
1875
2092
|
/** A one-off date that is already over everywhere on earth, so it would fire for nobody. */
|
|
1876
2093
|
"date_in_the_past"
|
|
1877
2094
|
];
|
|
1878
|
-
var AutomationIssueCode =
|
|
1879
|
-
var AutomationIssue =
|
|
2095
|
+
var AutomationIssueCode = z10.enum(AUTOMATION_ISSUE_CODES);
|
|
2096
|
+
var AutomationIssue = z10.object({
|
|
1880
2097
|
code: AutomationIssueCode,
|
|
1881
2098
|
/** The step the issue is about, when it is about one. */
|
|
1882
2099
|
step_id: StepId.nullable(),
|
|
1883
2100
|
/** Where inside the document, for the canvas to point at a field. */
|
|
1884
|
-
path:
|
|
1885
|
-
message:
|
|
2101
|
+
path: z10.array(z10.union([z10.string(), z10.number()])),
|
|
2102
|
+
message: z10.string()
|
|
1886
2103
|
});
|
|
1887
|
-
var AutomationValidation =
|
|
1888
|
-
ok:
|
|
1889
|
-
issues:
|
|
2104
|
+
var AutomationValidation = z10.object({
|
|
2105
|
+
ok: z10.boolean(),
|
|
2106
|
+
issues: z10.array(AutomationIssue)
|
|
1890
2107
|
});
|
|
1891
2108
|
function validateAutomation(flow, settings, ctx) {
|
|
1892
2109
|
const issues = [];
|
|
@@ -2277,7 +2494,7 @@ function cycles(flow) {
|
|
|
2277
2494
|
return out.sort();
|
|
2278
2495
|
}
|
|
2279
2496
|
var AUTOMATION_STATUSES = ["draft", "active", "paused", "archived"];
|
|
2280
|
-
var AutomationStatus =
|
|
2497
|
+
var AutomationStatus = z10.enum(AUTOMATION_STATUSES);
|
|
2281
2498
|
var AUTOMATION_END_REASONS = [
|
|
2282
2499
|
"completed",
|
|
2283
2500
|
"exit_filter",
|
|
@@ -2296,9 +2513,9 @@ var AUTOMATION_END_REASONS = [
|
|
|
2296
2513
|
"ended_by_hand",
|
|
2297
2514
|
"failed"
|
|
2298
2515
|
];
|
|
2299
|
-
var AutomationEndReason =
|
|
2516
|
+
var AutomationEndReason = z10.enum(AUTOMATION_END_REASONS);
|
|
2300
2517
|
var AUTOMATION_WAIT_REASONS = ["delay", "quiet_hours", "plan_limit", "sending", "condition"];
|
|
2301
|
-
var AutomationWaitReason =
|
|
2518
|
+
var AutomationWaitReason = z10.enum(AUTOMATION_WAIT_REASONS);
|
|
2302
2519
|
var AUTOMATION_ENTRY_REFUSALS = [
|
|
2303
2520
|
"already_inside",
|
|
2304
2521
|
"re_entry_off",
|
|
@@ -2309,26 +2526,26 @@ var AUTOMATION_ENTRY_REFUSALS = [
|
|
|
2309
2526
|
"not_active",
|
|
2310
2527
|
"unknown_contact"
|
|
2311
2528
|
];
|
|
2312
|
-
var AutomationEntryRefusal =
|
|
2313
|
-
var AutomationCounts =
|
|
2314
|
-
started:
|
|
2315
|
-
in_progress:
|
|
2316
|
-
completed:
|
|
2317
|
-
exited:
|
|
2318
|
-
failed:
|
|
2319
|
-
});
|
|
2320
|
-
var Automation =
|
|
2529
|
+
var AutomationEntryRefusal = z10.enum(AUTOMATION_ENTRY_REFUSALS);
|
|
2530
|
+
var AutomationCounts = z10.object({
|
|
2531
|
+
started: z10.number().int().min(0),
|
|
2532
|
+
in_progress: z10.number().int().min(0),
|
|
2533
|
+
completed: z10.number().int().min(0),
|
|
2534
|
+
exited: z10.number().int().min(0),
|
|
2535
|
+
failed: z10.number().int().min(0)
|
|
2536
|
+
});
|
|
2537
|
+
var Automation = z10.object({
|
|
2321
2538
|
id: Id,
|
|
2322
|
-
name:
|
|
2539
|
+
name: z10.string().min(1).max(200),
|
|
2323
2540
|
status: AutomationStatus,
|
|
2324
2541
|
/** Every automation sends under one topic; there are no transactional automations. */
|
|
2325
2542
|
topic: Slug,
|
|
2326
2543
|
provider_id: Id.nullable(),
|
|
2327
2544
|
settings: AutomationSettings,
|
|
2328
2545
|
/** The flow people are running through, null while the automation has never been published. */
|
|
2329
|
-
published_version:
|
|
2546
|
+
published_version: z10.number().int().min(1).nullable(),
|
|
2330
2547
|
/** The flow being edited. Increments on every save, like a template's version. */
|
|
2331
|
-
draft_version:
|
|
2548
|
+
draft_version: z10.number().int().min(1),
|
|
2332
2549
|
counts: AutomationCounts,
|
|
2333
2550
|
created_at: Timestamp,
|
|
2334
2551
|
updated_at: Timestamp,
|
|
@@ -2341,16 +2558,16 @@ var AutomationWithFlow = Automation.extend({
|
|
|
2341
2558
|
published_flow: AutomationFlow.nullable()
|
|
2342
2559
|
});
|
|
2343
2560
|
var AutomationSummary = Automation;
|
|
2344
|
-
var AutomationCreate =
|
|
2345
|
-
name:
|
|
2561
|
+
var AutomationCreate = z10.object({
|
|
2562
|
+
name: z10.string().min(1).max(200),
|
|
2346
2563
|
topic: Slug,
|
|
2347
2564
|
provider_id: Id.optional(),
|
|
2348
2565
|
flow: AutomationFlow.optional(),
|
|
2349
2566
|
settings: AutomationSettings.partial().optional()
|
|
2350
2567
|
});
|
|
2351
|
-
var AutomationUpdate =
|
|
2352
|
-
base_version:
|
|
2353
|
-
name:
|
|
2568
|
+
var AutomationUpdate = z10.object({
|
|
2569
|
+
base_version: z10.number().int().min(1),
|
|
2570
|
+
name: z10.string().min(1).max(200).optional(),
|
|
2354
2571
|
topic: Slug.optional(),
|
|
2355
2572
|
provider_id: Id.optional(),
|
|
2356
2573
|
flow: AutomationFlow.optional(),
|
|
@@ -2363,42 +2580,42 @@ var AutomationListQuery = PageQuery.extend({
|
|
|
2363
2580
|
status: AutomationStatus.optional(),
|
|
2364
2581
|
topic: Slug.optional()
|
|
2365
2582
|
});
|
|
2366
|
-
var AutomationPublishRequest =
|
|
2583
|
+
var AutomationPublishRequest = z10.object({
|
|
2367
2584
|
/**
|
|
2368
2585
|
* Answer what this publish would do and change nothing, whatever the flow
|
|
2369
2586
|
* looks like. Without it a publish that has no issues and moves nobody
|
|
2370
2587
|
* simply goes ahead, which is right for an API caller and wrong for a
|
|
2371
2588
|
* dialog that is only showing somebody what they are about to do.
|
|
2372
2589
|
*/
|
|
2373
|
-
dry_run:
|
|
2374
|
-
confirm:
|
|
2590
|
+
dry_run: z10.boolean().optional(),
|
|
2591
|
+
confirm: z10.boolean().optional(),
|
|
2375
2592
|
/** Per removed step: the step its waiting runs move to, or null to end them. */
|
|
2376
|
-
moves:
|
|
2593
|
+
moves: z10.record(StepId, StepId.nullable()).optional(),
|
|
2377
2594
|
/** Also enter everyone who matches a trigger's condition right now. */
|
|
2378
|
-
backfill:
|
|
2595
|
+
backfill: z10.boolean().optional()
|
|
2379
2596
|
}).strict();
|
|
2380
|
-
var AutomationPublishMove =
|
|
2597
|
+
var AutomationPublishMove = z10.object({
|
|
2381
2598
|
step_id: StepId,
|
|
2382
2599
|
/** Why the step's people have to move: it is gone, or its delay changed. */
|
|
2383
|
-
change:
|
|
2384
|
-
waiting:
|
|
2600
|
+
change: z10.enum(["removed", "delay_changed", "kind_changed"]),
|
|
2601
|
+
waiting: z10.number().int().min(0),
|
|
2385
2602
|
/** Where they go: a step id, or null to end their runs with `step_removed`. */
|
|
2386
2603
|
destination: StepId.nullable()
|
|
2387
2604
|
});
|
|
2388
|
-
var AutomationPublishResult =
|
|
2389
|
-
published:
|
|
2605
|
+
var AutomationPublishResult = z10.object({
|
|
2606
|
+
published: z10.boolean(),
|
|
2390
2607
|
automation: Automation,
|
|
2391
2608
|
/** Empty when nothing has to move; non-empty and `published` false asks for a confirmation. */
|
|
2392
|
-
moves:
|
|
2609
|
+
moves: z10.array(AutomationPublishMove),
|
|
2393
2610
|
/** How many people the backfill entered, or would enter when it is not confirmed. */
|
|
2394
|
-
backfill:
|
|
2395
|
-
issues:
|
|
2396
|
-
});
|
|
2397
|
-
var AutomationDuplicateRequest =
|
|
2398
|
-
var AutomationEnrollRequest =
|
|
2399
|
-
contact_ids:
|
|
2400
|
-
external_ids:
|
|
2401
|
-
emails:
|
|
2611
|
+
backfill: z10.number().int().min(0),
|
|
2612
|
+
issues: z10.array(AutomationIssue)
|
|
2613
|
+
});
|
|
2614
|
+
var AutomationDuplicateRequest = z10.object({ name: z10.string().min(1).max(200).optional() }).strict();
|
|
2615
|
+
var AutomationEnrollRequest = z10.object({
|
|
2616
|
+
contact_ids: z10.array(Id).min(1).max(1e3).optional(),
|
|
2617
|
+
external_ids: z10.array(z10.string().min(1).max(255)).min(1).max(1e3).optional(),
|
|
2618
|
+
emails: z10.array(Email).min(1).max(1e3).optional(),
|
|
2402
2619
|
segment_id: Id.optional(),
|
|
2403
2620
|
/** Where to start them; the flow's entry step by default. */
|
|
2404
2621
|
step_id: StepId.optional()
|
|
@@ -2406,34 +2623,34 @@ var AutomationEnrollRequest = z9.object({
|
|
|
2406
2623
|
(v) => [v.contact_ids, v.external_ids, v.emails, v.segment_id].filter((x) => x !== void 0).length === 1,
|
|
2407
2624
|
"name the people exactly one way: contact_ids, external_ids, emails or segment_id"
|
|
2408
2625
|
);
|
|
2409
|
-
var AutomationEnrollment =
|
|
2626
|
+
var AutomationEnrollment = z10.object({
|
|
2410
2627
|
contact_id: Id.nullable(),
|
|
2411
2628
|
email: Email.nullable(),
|
|
2412
|
-
external_id:
|
|
2413
|
-
entered:
|
|
2629
|
+
external_id: z10.string().nullable(),
|
|
2630
|
+
entered: z10.boolean(),
|
|
2414
2631
|
run_id: Id.nullable(),
|
|
2415
2632
|
reason: AutomationEntryRefusal.nullable()
|
|
2416
2633
|
});
|
|
2417
|
-
var AutomationEnrollResult =
|
|
2418
|
-
entered:
|
|
2419
|
-
refused:
|
|
2420
|
-
results:
|
|
2634
|
+
var AutomationEnrollResult = z10.object({
|
|
2635
|
+
entered: z10.number().int().min(0),
|
|
2636
|
+
refused: z10.number().int().min(0),
|
|
2637
|
+
results: z10.array(AutomationEnrollment)
|
|
2421
2638
|
});
|
|
2422
|
-
var AutomationRunStatus =
|
|
2423
|
-
var AutomationRun =
|
|
2639
|
+
var AutomationRunStatus = z10.enum(["active", "waiting", "completed", "exited", "failed"]);
|
|
2640
|
+
var AutomationRun = z10.object({
|
|
2424
2641
|
id: Id,
|
|
2425
2642
|
automation_id: Id,
|
|
2426
2643
|
contact_id: Id,
|
|
2427
2644
|
email: Email,
|
|
2428
|
-
external_id:
|
|
2429
|
-
version:
|
|
2645
|
+
external_id: z10.string().nullable(),
|
|
2646
|
+
version: z10.number().int().min(1),
|
|
2430
2647
|
status: AutomationRunStatus,
|
|
2431
2648
|
current_step_id: StepId.nullable(),
|
|
2432
2649
|
next_action_at: Timestamp.nullable(),
|
|
2433
2650
|
wait_reason: AutomationWaitReason.nullable(),
|
|
2434
|
-
end_reason:
|
|
2651
|
+
end_reason: z10.string().nullable(),
|
|
2435
2652
|
/** What went wrong the last time this run tried to take its step, null otherwise. */
|
|
2436
|
-
last_error:
|
|
2653
|
+
last_error: z10.string().nullable(),
|
|
2437
2654
|
entered_at: Timestamp,
|
|
2438
2655
|
ended_at: Timestamp.nullable()
|
|
2439
2656
|
});
|
|
@@ -2443,13 +2660,13 @@ var AutomationRunListQuery = PageQuery.extend({
|
|
|
2443
2660
|
contact_id: Id.optional()
|
|
2444
2661
|
});
|
|
2445
2662
|
var ContactAutomationRun = AutomationRun.extend({
|
|
2446
|
-
automation:
|
|
2663
|
+
automation: z10.object({ id: Id, name: z10.string(), status: AutomationStatus })
|
|
2447
2664
|
});
|
|
2448
2665
|
var ContactAutomationListQuery = PageQuery.extend({ status: AutomationRunStatus.optional() });
|
|
2449
|
-
var AutomationStepExecution =
|
|
2666
|
+
var AutomationStepExecution = z10.object({
|
|
2450
2667
|
step_id: StepId,
|
|
2451
2668
|
step_kind: AutomationStepKind,
|
|
2452
|
-
visit:
|
|
2669
|
+
visit: z10.number().int().min(1),
|
|
2453
2670
|
started_at: Timestamp,
|
|
2454
2671
|
finished_at: Timestamp.nullable(),
|
|
2455
2672
|
/**
|
|
@@ -2461,48 +2678,48 @@ var AutomationStepExecution = z9.object({
|
|
|
2461
2678
|
*/
|
|
2462
2679
|
outcome: Properties
|
|
2463
2680
|
});
|
|
2464
|
-
var AutomationJourney =
|
|
2681
|
+
var AutomationJourney = z10.object({
|
|
2465
2682
|
run: AutomationRun,
|
|
2466
|
-
steps:
|
|
2683
|
+
steps: z10.array(AutomationStepExecution)
|
|
2467
2684
|
});
|
|
2468
|
-
var AutomationStepReport =
|
|
2685
|
+
var AutomationStepReport = z10.object({
|
|
2469
2686
|
step_id: StepId,
|
|
2470
2687
|
step_kind: AutomationStepKind,
|
|
2471
|
-
reached:
|
|
2472
|
-
waiting:
|
|
2473
|
-
passed:
|
|
2688
|
+
reached: z10.number().int().min(0),
|
|
2689
|
+
waiting: z10.number().int().min(0),
|
|
2690
|
+
passed: z10.number().int().min(0),
|
|
2474
2691
|
/** Per branch, for a condition: how many took `yes` and how many `no`. */
|
|
2475
|
-
branches:
|
|
2692
|
+
branches: z10.record(z10.number().int().min(0)),
|
|
2476
2693
|
/** The step mailing's id, for an email step of the published version. */
|
|
2477
2694
|
mailing_id: Id.nullable()
|
|
2478
2695
|
});
|
|
2479
|
-
var AutomationReport =
|
|
2696
|
+
var AutomationReport = z10.object({
|
|
2480
2697
|
counts: AutomationCounts,
|
|
2481
2698
|
/** Why the people who left did: `AutomationEndReason` to a count. */
|
|
2482
|
-
end_reasons:
|
|
2699
|
+
end_reasons: z10.record(z10.number().int().min(0)),
|
|
2483
2700
|
/**
|
|
2484
2701
|
* Why people a trigger named did not enter: `AutomationEntryRefusal` to a
|
|
2485
2702
|
* count. Without it a pause, an exclusion or a missing subscription would
|
|
2486
2703
|
* turn people away invisibly.
|
|
2487
2704
|
*/
|
|
2488
|
-
refusals:
|
|
2489
|
-
steps:
|
|
2490
|
-
});
|
|
2491
|
-
var AutomationExitRunRequest =
|
|
2492
|
-
var AutomationRunParams =
|
|
2493
|
-
var AutomationStepParams =
|
|
2494
|
-
var AutomationTestEmailRequest =
|
|
2495
|
-
var AutomationValidateRequest =
|
|
2496
|
-
var EventCreate =
|
|
2705
|
+
refusals: z10.record(z10.number().int().min(0)),
|
|
2706
|
+
steps: z10.array(AutomationStepReport)
|
|
2707
|
+
});
|
|
2708
|
+
var AutomationExitRunRequest = z10.object({ reason: z10.string().max(200).optional() }).strict();
|
|
2709
|
+
var AutomationRunParams = z10.object({ id: Id, run_id: Id });
|
|
2710
|
+
var AutomationStepParams = z10.object({ id: Id, step_id: StepId });
|
|
2711
|
+
var AutomationTestEmailRequest = z10.object({ to: Email, merge: Properties.optional(), locale: LocaleTag.optional() }).strict();
|
|
2712
|
+
var AutomationValidateRequest = z10.object({ flow: AutomationFlow.optional(), settings: AutomationSettings.partial().optional() }).strict();
|
|
2713
|
+
var EventCreate = z10.object({
|
|
2497
2714
|
name: EventName,
|
|
2498
|
-
contact:
|
|
2715
|
+
contact: z10.object({ external_id: z10.string().min(1).max(255).optional(), email: Email.optional(), id: Id.optional() }).refine((c) => [c.external_id, c.email, c.id].filter((x) => x !== void 0).length === 1, "name the contact exactly one way"),
|
|
2499
2716
|
data: Properties.optional(),
|
|
2500
2717
|
occurred_at: Timestamp.optional(),
|
|
2501
|
-
id:
|
|
2718
|
+
id: z10.string().min(1).max(128).optional()
|
|
2502
2719
|
}).strict();
|
|
2503
|
-
var Event =
|
|
2720
|
+
var Event = z10.object({
|
|
2504
2721
|
id: Id,
|
|
2505
|
-
client_event_id:
|
|
2722
|
+
client_event_id: z10.string().nullable(),
|
|
2506
2723
|
name: EventName,
|
|
2507
2724
|
contact_id: Id,
|
|
2508
2725
|
data: Properties,
|
|
@@ -2513,7 +2730,7 @@ var Event = z9.object({
|
|
|
2513
2730
|
* hand. Each is then admitted or refused by the runtime on its own rules
|
|
2514
2731
|
* (consent, exclusions, re-entry), which `automations.report` counts.
|
|
2515
2732
|
*/
|
|
2516
|
-
entered:
|
|
2733
|
+
entered: z10.number().int().min(0)
|
|
2517
2734
|
});
|
|
2518
2735
|
var EventListQuery = PageQuery.extend({
|
|
2519
2736
|
name: EventName.optional(),
|
|
@@ -2521,19 +2738,15 @@ var EventListQuery = PageQuery.extend({
|
|
|
2521
2738
|
});
|
|
2522
2739
|
|
|
2523
2740
|
// src/workspace.ts
|
|
2524
|
-
var CompanyId =
|
|
2741
|
+
var CompanyId = z11.string().min(1).max(64);
|
|
2525
2742
|
var ASSET_POLICIES = ["any", "service_only"];
|
|
2526
|
-
var AssetPolicy =
|
|
2743
|
+
var AssetPolicy = z11.enum(ASSET_POLICIES);
|
|
2527
2744
|
var MAX_ALLOWED_ASSET_HOSTS = 20;
|
|
2528
|
-
var AssetHost =
|
|
2529
|
-
var
|
|
2530
|
-
var MAX_MERGE_DEFAULT_LENGTH = 2e3;
|
|
2531
|
-
var MergeFieldName = z10.string().regex(/^[a-z][a-z0-9_]*$/, "a merge-field name: lowercase letters, digits and underscores, starting with a letter").max(64);
|
|
2532
|
-
var MergeDefaultValue = z10.union([z10.string().max(MAX_MERGE_DEFAULT_LENGTH), z10.number().finite(), z10.boolean()]);
|
|
2533
|
-
var MergeDefaults = z10.record(MergeFieldName, MergeDefaultValue).refine((m) => Object.keys(m).length <= MAX_MERGE_DEFAULTS, `at most ${MAX_MERGE_DEFAULTS} merge defaults`).refine((m) => !Object.keys(m).some((k) => k in RESERVED_MERGE_FIELDS), {
|
|
2745
|
+
var AssetHost = z11.string().trim().toLowerCase().min(1).max(253).regex(/^(?=.{1,253}(?::\d{1,5})?$)[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?(\.[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?)+(:\d{1,5})?$/, "a host name such as example.com");
|
|
2746
|
+
var MergeDefaults = z11.record(MergeFieldName, MergeDefaultValue).refine((m) => Object.keys(m).length <= MAX_MERGE_DEFAULTS, `at most ${MAX_MERGE_DEFAULTS} merge defaults`).refine((m) => !Object.keys(m).some((k) => k in RESERVED_MERGE_FIELDS), {
|
|
2534
2747
|
message: `a reserved merge field (${Object.keys(RESERVED_MERGE_FIELDS).join(", ")}) cannot have a workspace default`
|
|
2535
2748
|
});
|
|
2536
|
-
var WorkspaceSettings =
|
|
2749
|
+
var WorkspaceSettings = z11.object({
|
|
2537
2750
|
/**
|
|
2538
2751
|
* The workspace's main language (a {@link LocaleTag}, e.g. "de"): the main
|
|
2539
2752
|
* language of a new template, and of the hosted pages when nothing else
|
|
@@ -2546,9 +2759,9 @@ var WorkspaceSettings = z10.object({
|
|
|
2546
2759
|
* each; the hosted pages are offered in those of them they support (English,
|
|
2547
2760
|
* German, French, Italian, Spanish). The service stores canonical casing.
|
|
2548
2761
|
*/
|
|
2549
|
-
locales:
|
|
2762
|
+
locales: z11.array(LocaleTag).min(1).max(20).refine((l) => new Set(l.map((t) => t.toLowerCase())).size === l.length, "a language appears twice"),
|
|
2550
2763
|
/** Open and click tracking. Off by default, opt-in per workspace. */
|
|
2551
|
-
tracking_enabled:
|
|
2764
|
+
tracking_enabled: z11.boolean(),
|
|
2552
2765
|
/** Where images, stylesheets and fonts in a mail may load from. `any` by default. */
|
|
2553
2766
|
asset_policy: AssetPolicy,
|
|
2554
2767
|
/**
|
|
@@ -2556,7 +2769,7 @@ var WorkspaceSettings = z10.object({
|
|
|
2556
2769
|
* the workspace's website, typically. Empty by default. Ignored under `any`,
|
|
2557
2770
|
* which allows everything already.
|
|
2558
2771
|
*/
|
|
2559
|
-
allowed_asset_hosts:
|
|
2772
|
+
allowed_asset_hosts: z11.array(AssetHost).max(MAX_ALLOWED_ASSET_HOSTS),
|
|
2560
2773
|
/** Merge values every mail of the workspace can use (see `MergeDefaults`). Empty by default. */
|
|
2561
2774
|
merge_defaults: MergeDefaults,
|
|
2562
2775
|
/**
|
|
@@ -2565,10 +2778,10 @@ var WorkspaceSettings = z10.object({
|
|
|
2565
2778
|
*/
|
|
2566
2779
|
default_timezone: TimezoneName.nullable()
|
|
2567
2780
|
});
|
|
2568
|
-
var Workspace =
|
|
2781
|
+
var Workspace = z11.object({
|
|
2569
2782
|
id: Id,
|
|
2570
2783
|
slug: Slug,
|
|
2571
|
-
name:
|
|
2784
|
+
name: z11.string().min(1).max(120),
|
|
2572
2785
|
settings: WorkspaceSettings,
|
|
2573
2786
|
/**
|
|
2574
2787
|
* The auth-brain company (tenant) the workspace was created for, or null for
|
|
@@ -2579,13 +2792,13 @@ var Workspace = z10.object({
|
|
|
2579
2792
|
created_at: Timestamp,
|
|
2580
2793
|
updated_at: Timestamp
|
|
2581
2794
|
});
|
|
2582
|
-
var WorkspaceCreate =
|
|
2795
|
+
var WorkspaceCreate = z11.object({
|
|
2583
2796
|
slug: Slug,
|
|
2584
|
-
name:
|
|
2797
|
+
name: z11.string().min(1).max(120),
|
|
2585
2798
|
settings: WorkspaceSettings.partial().optional(),
|
|
2586
|
-
owner:
|
|
2799
|
+
owner: z11.object({
|
|
2587
2800
|
email: Email,
|
|
2588
|
-
name:
|
|
2801
|
+
name: z11.string().max(200).nullable().optional()
|
|
2589
2802
|
}),
|
|
2590
2803
|
/**
|
|
2591
2804
|
* The signed-in person's active auth-brain company. The dashboard always
|
|
@@ -2595,49 +2808,49 @@ var WorkspaceCreate = z10.object({
|
|
|
2595
2808
|
*/
|
|
2596
2809
|
company_id: CompanyId.optional()
|
|
2597
2810
|
});
|
|
2598
|
-
var WorkspaceUpdate =
|
|
2599
|
-
name:
|
|
2811
|
+
var WorkspaceUpdate = z11.object({
|
|
2812
|
+
name: z11.string().min(1).max(120),
|
|
2600
2813
|
settings: WorkspaceSettings.partial()
|
|
2601
2814
|
}).partial().refine((v) => Object.keys(v).length > 0, "at least one field");
|
|
2602
|
-
var WorkspaceMove =
|
|
2815
|
+
var WorkspaceMove = z11.object({ company_id: CompanyId });
|
|
2603
2816
|
var MEMBER_ROLES = ["owner", "admin", "editor", "viewer"];
|
|
2604
|
-
var MemberRole =
|
|
2817
|
+
var MemberRole = z11.enum(MEMBER_ROLES);
|
|
2605
2818
|
var WorkspaceMembership = Workspace.extend({ role: MemberRole });
|
|
2606
|
-
var Member =
|
|
2819
|
+
var Member = z11.object({
|
|
2607
2820
|
id: Id,
|
|
2608
2821
|
/** The person's auth-brain subject. */
|
|
2609
|
-
subject:
|
|
2822
|
+
subject: z11.string().min(1).max(255),
|
|
2610
2823
|
email: Email,
|
|
2611
|
-
name:
|
|
2824
|
+
name: z11.string().max(200).nullable(),
|
|
2612
2825
|
role: MemberRole,
|
|
2613
2826
|
created_at: Timestamp
|
|
2614
2827
|
});
|
|
2615
|
-
var MemberCreate =
|
|
2616
|
-
subject:
|
|
2828
|
+
var MemberCreate = z11.object({
|
|
2829
|
+
subject: z11.string().min(1).max(255),
|
|
2617
2830
|
email: Email,
|
|
2618
|
-
name:
|
|
2831
|
+
name: z11.string().max(200).nullable().optional(),
|
|
2619
2832
|
role: MemberRole
|
|
2620
2833
|
});
|
|
2621
|
-
var MemberUpdate =
|
|
2834
|
+
var MemberUpdate = z11.object({ role: MemberRole });
|
|
2622
2835
|
var API_KEY_SCOPES = ["full", "read", "send"];
|
|
2623
|
-
var ApiKeyScope =
|
|
2624
|
-
var ApiKey =
|
|
2836
|
+
var ApiKeyScope = z11.enum(API_KEY_SCOPES);
|
|
2837
|
+
var ApiKey = z11.object({
|
|
2625
2838
|
id: Id,
|
|
2626
|
-
name:
|
|
2839
|
+
name: z11.string().min(1).max(120),
|
|
2627
2840
|
/** The first characters of the key, for recognising it in a list. */
|
|
2628
|
-
prefix:
|
|
2841
|
+
prefix: z11.string().min(4).max(32),
|
|
2629
2842
|
scope: ApiKeyScope,
|
|
2630
2843
|
last_used_at: Timestamp.nullable(),
|
|
2631
2844
|
revoked_at: Timestamp.nullable(),
|
|
2632
2845
|
created_at: Timestamp
|
|
2633
2846
|
});
|
|
2634
|
-
var ApiKeyCreate =
|
|
2635
|
-
name:
|
|
2847
|
+
var ApiKeyCreate = z11.object({
|
|
2848
|
+
name: z11.string().min(1).max(120),
|
|
2636
2849
|
/** Omitted means `full`. */
|
|
2637
2850
|
scope: ApiKeyScope.optional()
|
|
2638
2851
|
});
|
|
2639
|
-
var ApiKeyCreated =
|
|
2640
|
-
key:
|
|
2852
|
+
var ApiKeyCreated = z11.object({
|
|
2853
|
+
key: z11.string().min(20),
|
|
2641
2854
|
api_key: ApiKey
|
|
2642
2855
|
});
|
|
2643
2856
|
var AUDIT_ACTIONS = [
|
|
@@ -2726,29 +2939,29 @@ var AUDIT_ACTIONS = [
|
|
|
2726
2939
|
"automation.run_ended",
|
|
2727
2940
|
"event.recorded"
|
|
2728
2941
|
];
|
|
2729
|
-
var AuditAction =
|
|
2730
|
-
var AuditActor =
|
|
2731
|
-
|
|
2732
|
-
|
|
2733
|
-
type:
|
|
2942
|
+
var AuditAction = z11.enum(AUDIT_ACTIONS);
|
|
2943
|
+
var AuditActor = z11.discriminatedUnion("type", [
|
|
2944
|
+
z11.object({ type: z11.literal("member"), member_id: Id, subject: z11.string().min(1) }),
|
|
2945
|
+
z11.object({
|
|
2946
|
+
type: z11.literal("api_key"),
|
|
2734
2947
|
api_key_id: Id,
|
|
2735
2948
|
/**
|
|
2736
2949
|
* Who the client's application said it was acting for, from
|
|
2737
2950
|
* `ON_BEHALF_OF_HEADER`: reported by that application, never verified.
|
|
2738
2951
|
* Absent when the call carried no label.
|
|
2739
2952
|
*/
|
|
2740
|
-
on_behalf_of:
|
|
2953
|
+
on_behalf_of: z11.string().min(1).max(ON_BEHALF_OF_MAX_LENGTH).optional()
|
|
2741
2954
|
}),
|
|
2742
2955
|
/** The service itself (the worker, a bounce, the hosted unsubscribe page). */
|
|
2743
|
-
|
|
2956
|
+
z11.object({ type: z11.literal("system"), reason: z11.string().min(1).max(200) })
|
|
2744
2957
|
]);
|
|
2745
|
-
var AuditEntry =
|
|
2958
|
+
var AuditEntry = z11.object({
|
|
2746
2959
|
id: Id,
|
|
2747
2960
|
action: AuditAction,
|
|
2748
2961
|
actor: AuditActor,
|
|
2749
|
-
target_type:
|
|
2962
|
+
target_type: z11.string().min(1).max(64),
|
|
2750
2963
|
target_id: Id.nullable(),
|
|
2751
|
-
details:
|
|
2964
|
+
details: z11.record(z11.unknown()),
|
|
2752
2965
|
created_at: Timestamp
|
|
2753
2966
|
});
|
|
2754
2967
|
var AuditQuery = PageQuery.extend({
|
|
@@ -2757,12 +2970,12 @@ var AuditQuery = PageQuery.extend({
|
|
|
2757
2970
|
});
|
|
2758
2971
|
|
|
2759
2972
|
// src/invites.ts
|
|
2760
|
-
import { z as
|
|
2973
|
+
import { z as z12 } from "zod";
|
|
2761
2974
|
var INVITE_STATUSES = ["pending", "accepted", "revoked", "expired"];
|
|
2762
|
-
var InviteStatus =
|
|
2975
|
+
var InviteStatus = z12.enum(INVITE_STATUSES);
|
|
2763
2976
|
var DEFAULT_INVITE_TTL_DAYS = 7;
|
|
2764
2977
|
var MAX_INVITE_TTL_DAYS = 30;
|
|
2765
|
-
var Invite =
|
|
2978
|
+
var Invite = z12.object({
|
|
2766
2979
|
id: Id,
|
|
2767
2980
|
/** Stored lowercased; only a person signed in with this address can accept. */
|
|
2768
2981
|
email: Email,
|
|
@@ -2770,32 +2983,32 @@ var Invite = z11.object({
|
|
|
2770
2983
|
/** Derived from the timestamps below, never stored. */
|
|
2771
2984
|
status: InviteStatus,
|
|
2772
2985
|
/** The member who invited, while they are still one (null after they left). */
|
|
2773
|
-
invited_by:
|
|
2986
|
+
invited_by: z12.object({ member_id: Id.nullable(), email: Email.nullable() }),
|
|
2774
2987
|
expires_at: Timestamp,
|
|
2775
2988
|
accepted_at: Timestamp.nullable(),
|
|
2776
2989
|
revoked_at: Timestamp.nullable(),
|
|
2777
2990
|
created_at: Timestamp
|
|
2778
2991
|
});
|
|
2779
|
-
var InviteCreate =
|
|
2992
|
+
var InviteCreate = z12.object({
|
|
2780
2993
|
email: Email,
|
|
2781
2994
|
role: MemberRole,
|
|
2782
|
-
expires_in_days:
|
|
2995
|
+
expires_in_days: z12.number().int().min(1).max(MAX_INVITE_TTL_DAYS).optional()
|
|
2783
2996
|
});
|
|
2784
|
-
var InviteCreated =
|
|
2997
|
+
var InviteCreated = z12.object({
|
|
2785
2998
|
invite: Invite,
|
|
2786
|
-
token:
|
|
2999
|
+
token: z12.string().min(32).max(128)
|
|
2787
3000
|
});
|
|
2788
3001
|
var InviteListQuery = PageQuery.extend({
|
|
2789
3002
|
status: InviteStatus.optional()
|
|
2790
3003
|
});
|
|
2791
|
-
var InviteAccept =
|
|
2792
|
-
token:
|
|
3004
|
+
var InviteAccept = z12.object({
|
|
3005
|
+
token: z12.string().min(32).max(128),
|
|
2793
3006
|
email: Email,
|
|
2794
|
-
name:
|
|
3007
|
+
name: z12.string().max(200).nullable().optional()
|
|
2795
3008
|
});
|
|
2796
|
-
var InviteAccepted =
|
|
3009
|
+
var InviteAccepted = z12.object({
|
|
2797
3010
|
workspace: WorkspaceMembership,
|
|
2798
|
-
already_member:
|
|
3011
|
+
already_member: z12.boolean()
|
|
2799
3012
|
});
|
|
2800
3013
|
var INVITE_REFUSAL_REASONS = ["email_mismatch", "inviter_lacks_role", "expired", "revoked", "accepted"];
|
|
2801
3014
|
|
|
@@ -3425,13 +3638,13 @@ function renameInsertionEverywhere(drafts, from, rawTo) {
|
|
|
3425
3638
|
}
|
|
3426
3639
|
|
|
3427
3640
|
// src/providers.ts
|
|
3428
|
-
import { z as
|
|
3641
|
+
import { z as z13 } from "zod";
|
|
3429
3642
|
var PROVIDER_KINDS = ["smtp", "resend"];
|
|
3430
|
-
var ProviderKind =
|
|
3431
|
-
var ProviderPolicy =
|
|
3432
|
-
daily_recipient_budget:
|
|
3433
|
-
min_interval_ms:
|
|
3434
|
-
max_recipients_per_message:
|
|
3643
|
+
var ProviderKind = z13.enum(PROVIDER_KINDS);
|
|
3644
|
+
var ProviderPolicy = z13.object({
|
|
3645
|
+
daily_recipient_budget: z13.number().int().min(1).max(1e7),
|
|
3646
|
+
min_interval_ms: z13.number().int().min(0).max(36e5),
|
|
3647
|
+
max_recipients_per_message: z13.number().int().min(1).max(1e3)
|
|
3435
3648
|
});
|
|
3436
3649
|
var ICLOUD_SMTP_POLICY = {
|
|
3437
3650
|
daily_recipient_budget: 800,
|
|
@@ -3439,17 +3652,17 @@ var ICLOUD_SMTP_POLICY = {
|
|
|
3439
3652
|
max_recipients_per_message: 1
|
|
3440
3653
|
};
|
|
3441
3654
|
var SMTP_SECURITY = ["tls", "starttls"];
|
|
3442
|
-
var SmtpConfigBase =
|
|
3443
|
-
host:
|
|
3444
|
-
port:
|
|
3655
|
+
var SmtpConfigBase = z13.object({
|
|
3656
|
+
host: z13.string().min(1).max(255),
|
|
3657
|
+
port: z13.number().int().min(1).max(65535),
|
|
3445
3658
|
/** `tls` connects encrypted (465), `starttls` upgrades (587). Never plaintext. */
|
|
3446
|
-
security:
|
|
3447
|
-
username:
|
|
3659
|
+
security: z13.enum(SMTP_SECURITY),
|
|
3660
|
+
username: z13.string().min(1).max(255)
|
|
3448
3661
|
});
|
|
3449
|
-
var ResendConfigBase =
|
|
3450
|
-
var ProviderRejections =
|
|
3451
|
-
count:
|
|
3452
|
-
last_error:
|
|
3662
|
+
var ResendConfigBase = z13.object({});
|
|
3663
|
+
var ProviderRejections = z13.object({
|
|
3664
|
+
count: z13.number().int().min(0),
|
|
3665
|
+
last_error: z13.string().nullable(),
|
|
3453
3666
|
last_at: Timestamp.nullable(),
|
|
3454
3667
|
/**
|
|
3455
3668
|
* The last time the bounce circuit breaker tripped on one of this provider's
|
|
@@ -3459,59 +3672,59 @@ var ProviderRejections = z12.object({
|
|
|
3459
3672
|
* run's bounce blocks were undone and the mailing paused. `sample` is the
|
|
3460
3673
|
* reply that tripped it.
|
|
3461
3674
|
*/
|
|
3462
|
-
anomaly:
|
|
3675
|
+
anomaly: z13.object({
|
|
3463
3676
|
at: Timestamp,
|
|
3464
3677
|
/** `mailing`: one mailing was paused. `provider`: 5 refusals in a row with the same reply across the provider's sends (test sends and one-recipient mailings included) within 24 hours. */
|
|
3465
|
-
scope:
|
|
3678
|
+
scope: z13.enum(["mailing", "provider"]),
|
|
3466
3679
|
/**
|
|
3467
3680
|
* True while the provider-wide breaker is open: no bounce blocks through
|
|
3468
3681
|
* this provider, test sends refused and mailings not started or resumed
|
|
3469
3682
|
* (`provider_anomaly`), until an admin clears it (`providers.clearAnomaly`).
|
|
3470
3683
|
*/
|
|
3471
|
-
blocking:
|
|
3684
|
+
blocking: z13.boolean(),
|
|
3472
3685
|
mailing_id: Id.nullable(),
|
|
3473
|
-
reason:
|
|
3474
|
-
sample:
|
|
3686
|
+
reason: z13.string(),
|
|
3687
|
+
sample: z13.string()
|
|
3475
3688
|
}).nullable()
|
|
3476
3689
|
});
|
|
3477
|
-
var ProviderEvents =
|
|
3478
|
-
status:
|
|
3479
|
-
source:
|
|
3480
|
-
url:
|
|
3481
|
-
error:
|
|
3690
|
+
var ProviderEvents = z13.object({
|
|
3691
|
+
status: z13.enum(["active", "needs_secret"]),
|
|
3692
|
+
source: z13.enum(["automatic", "manual"]).nullable(),
|
|
3693
|
+
url: z13.string().url(),
|
|
3694
|
+
error: z13.string().nullable(),
|
|
3482
3695
|
/**
|
|
3483
3696
|
* Events that named an email this provider never sent through the service
|
|
3484
3697
|
* (another workspace or system sharing the Resend account): counted, never
|
|
3485
3698
|
* acted on.
|
|
3486
3699
|
*/
|
|
3487
|
-
unmatched:
|
|
3700
|
+
unmatched: z13.number().int().min(0)
|
|
3488
3701
|
});
|
|
3489
3702
|
var RESEND_EVENT_TYPES = ["email.bounced", "email.complained", "email.delivery_delayed"];
|
|
3490
|
-
var ResendSigningSecret =
|
|
3491
|
-
var ProviderEventsSecret =
|
|
3703
|
+
var ResendSigningSecret = z13.string().max(200).regex(/^whsec_[A-Za-z0-9+/]{16,}={0,2}$/, "must be the signing secret Resend shows, starting with whsec_");
|
|
3704
|
+
var ProviderEventsSecret = z13.object({ signing_secret: ResendSigningSecret });
|
|
3492
3705
|
var ProviderFields = {
|
|
3493
|
-
name:
|
|
3494
|
-
from_name:
|
|
3706
|
+
name: z13.string().min(1).max(120),
|
|
3707
|
+
from_name: z13.string().min(1).max(120),
|
|
3495
3708
|
from_email: Email,
|
|
3496
3709
|
reply_to: Email.nullable(),
|
|
3497
3710
|
policy: ProviderPolicy
|
|
3498
3711
|
};
|
|
3499
|
-
var Provider =
|
|
3500
|
-
|
|
3712
|
+
var Provider = z13.discriminatedUnion("kind", [
|
|
3713
|
+
z13.object({
|
|
3501
3714
|
id: Id,
|
|
3502
|
-
kind:
|
|
3715
|
+
kind: z13.literal("smtp"),
|
|
3503
3716
|
config: SmtpConfigBase,
|
|
3504
|
-
has_secret:
|
|
3717
|
+
has_secret: z13.boolean(),
|
|
3505
3718
|
...ProviderFields,
|
|
3506
3719
|
rejections: ProviderRejections,
|
|
3507
3720
|
created_at: Timestamp,
|
|
3508
3721
|
updated_at: Timestamp
|
|
3509
3722
|
}),
|
|
3510
|
-
|
|
3723
|
+
z13.object({
|
|
3511
3724
|
id: Id,
|
|
3512
|
-
kind:
|
|
3725
|
+
kind: z13.literal("resend"),
|
|
3513
3726
|
config: ResendConfigBase,
|
|
3514
|
-
has_secret:
|
|
3727
|
+
has_secret: z13.boolean(),
|
|
3515
3728
|
...ProviderFields,
|
|
3516
3729
|
rejections: ProviderRejections,
|
|
3517
3730
|
events: ProviderEvents,
|
|
@@ -3519,31 +3732,31 @@ var Provider = z12.discriminatedUnion("kind", [
|
|
|
3519
3732
|
updated_at: Timestamp
|
|
3520
3733
|
})
|
|
3521
3734
|
]);
|
|
3522
|
-
var ProviderCreate =
|
|
3523
|
-
|
|
3524
|
-
kind:
|
|
3525
|
-
config: SmtpConfigBase.extend({ password:
|
|
3735
|
+
var ProviderCreate = z13.discriminatedUnion("kind", [
|
|
3736
|
+
z13.object({
|
|
3737
|
+
kind: z13.literal("smtp"),
|
|
3738
|
+
config: SmtpConfigBase.extend({ password: z13.string().min(1).max(1024) }),
|
|
3526
3739
|
...ProviderFields
|
|
3527
3740
|
}),
|
|
3528
|
-
|
|
3529
|
-
kind:
|
|
3530
|
-
config: ResendConfigBase.extend({ api_key:
|
|
3741
|
+
z13.object({
|
|
3742
|
+
kind: z13.literal("resend"),
|
|
3743
|
+
config: ResendConfigBase.extend({ api_key: z13.string().min(1).max(1024) }),
|
|
3531
3744
|
...ProviderFields
|
|
3532
3745
|
})
|
|
3533
3746
|
]);
|
|
3534
|
-
var ProviderUpdate =
|
|
3535
|
-
|
|
3536
|
-
kind:
|
|
3537
|
-
config: SmtpConfigBase.extend({ password:
|
|
3747
|
+
var ProviderUpdate = z13.discriminatedUnion("kind", [
|
|
3748
|
+
z13.object({
|
|
3749
|
+
kind: z13.literal("smtp"),
|
|
3750
|
+
config: SmtpConfigBase.extend({ password: z13.string().min(1).max(1024).optional() }).partial().optional(),
|
|
3538
3751
|
name: ProviderFields.name.optional(),
|
|
3539
3752
|
from_name: ProviderFields.from_name.optional(),
|
|
3540
3753
|
from_email: ProviderFields.from_email.optional(),
|
|
3541
3754
|
reply_to: ProviderFields.reply_to.optional(),
|
|
3542
3755
|
policy: ProviderPolicy.partial().optional()
|
|
3543
3756
|
}),
|
|
3544
|
-
|
|
3545
|
-
kind:
|
|
3546
|
-
config: ResendConfigBase.extend({ api_key:
|
|
3757
|
+
z13.object({
|
|
3758
|
+
kind: z13.literal("resend"),
|
|
3759
|
+
config: ResendConfigBase.extend({ api_key: z13.string().min(1).max(1024).optional() }).optional(),
|
|
3547
3760
|
name: ProviderFields.name.optional(),
|
|
3548
3761
|
from_name: ProviderFields.from_name.optional(),
|
|
3549
3762
|
from_email: ProviderFields.from_email.optional(),
|
|
@@ -3551,48 +3764,48 @@ var ProviderUpdate = z12.discriminatedUnion("kind", [
|
|
|
3551
3764
|
policy: ProviderPolicy.partial().optional()
|
|
3552
3765
|
})
|
|
3553
3766
|
]);
|
|
3554
|
-
var ProviderUsage =
|
|
3767
|
+
var ProviderUsage = z13.object({
|
|
3555
3768
|
provider_id: Id,
|
|
3556
|
-
recipients_last_24h:
|
|
3557
|
-
remaining_budget:
|
|
3769
|
+
recipients_last_24h: z13.number().int().min(0),
|
|
3770
|
+
remaining_budget: z13.number().int().min(0),
|
|
3558
3771
|
/** When enough of the rolling window frees up to send again, if exhausted. */
|
|
3559
3772
|
next_capacity_at: Timestamp.nullable()
|
|
3560
3773
|
});
|
|
3561
|
-
var ProviderVerifyResult =
|
|
3562
|
-
ok:
|
|
3563
|
-
error:
|
|
3774
|
+
var ProviderVerifyResult = z13.object({
|
|
3775
|
+
ok: z13.boolean(),
|
|
3776
|
+
error: z13.string().nullable()
|
|
3564
3777
|
});
|
|
3565
3778
|
|
|
3566
3779
|
// src/contacts.ts
|
|
3567
|
-
import { z as
|
|
3568
|
-
var Topic =
|
|
3780
|
+
import { z as z14 } from "zod";
|
|
3781
|
+
var Topic = z14.object({
|
|
3569
3782
|
id: Id,
|
|
3570
3783
|
slug: Slug,
|
|
3571
|
-
name:
|
|
3784
|
+
name: z14.string().min(1).max(120),
|
|
3572
3785
|
/** Shown on the hosted preference page. */
|
|
3573
|
-
description:
|
|
3786
|
+
description: z14.string().max(1e3).nullable(),
|
|
3574
3787
|
/** Translations of `name` and `description` per locale, for the hosted page. */
|
|
3575
|
-
translations:
|
|
3576
|
-
|
|
3788
|
+
translations: z14.record(
|
|
3789
|
+
z14.object({ name: z14.string().min(1).max(120), description: z14.string().max(1e3).nullable() })
|
|
3577
3790
|
),
|
|
3578
3791
|
created_at: Timestamp,
|
|
3579
3792
|
updated_at: Timestamp
|
|
3580
3793
|
});
|
|
3581
|
-
var TopicCreate =
|
|
3794
|
+
var TopicCreate = z14.object({
|
|
3582
3795
|
slug: Slug,
|
|
3583
|
-
name:
|
|
3584
|
-
description:
|
|
3796
|
+
name: z14.string().min(1).max(120),
|
|
3797
|
+
description: z14.string().max(1e3).optional(),
|
|
3585
3798
|
translations: Topic.shape.translations.optional()
|
|
3586
3799
|
});
|
|
3587
3800
|
var TopicUpdate = TopicCreate.omit({ slug: true }).partial().refine((v) => Object.keys(v).length > 0, "at least one field");
|
|
3588
|
-
var Contact =
|
|
3801
|
+
var Contact = z14.object({
|
|
3589
3802
|
id: Id,
|
|
3590
|
-
external_id:
|
|
3803
|
+
external_id: z14.string().min(1).max(255).nullable(),
|
|
3591
3804
|
/** Stored lowercased, unique per workspace. */
|
|
3592
3805
|
email: Email,
|
|
3593
|
-
first_name:
|
|
3594
|
-
last_name:
|
|
3595
|
-
locale:
|
|
3806
|
+
first_name: z14.string().max(200).nullable(),
|
|
3807
|
+
last_name: z14.string().max(200).nullable(),
|
|
3808
|
+
locale: z14.string().min(2).max(35).nullable(),
|
|
3596
3809
|
/**
|
|
3597
3810
|
* The person's own time zone, an IANA name such as `Europe/Berlin` (A1).
|
|
3598
3811
|
* Every wall clock an automation waits for is read in it, falling back to the
|
|
@@ -3600,65 +3813,65 @@ var Contact = z13.object({
|
|
|
3600
3813
|
*/
|
|
3601
3814
|
timezone: TimezoneName.nullable(),
|
|
3602
3815
|
properties: Properties,
|
|
3603
|
-
topics:
|
|
3816
|
+
topics: z14.array(Slug),
|
|
3604
3817
|
/** The slugs of the contact's tags (S4). */
|
|
3605
|
-
tags:
|
|
3818
|
+
tags: z14.array(Slug),
|
|
3606
3819
|
created_at: Timestamp,
|
|
3607
3820
|
updated_at: Timestamp
|
|
3608
3821
|
});
|
|
3609
|
-
var ContactUpsert =
|
|
3610
|
-
external_id:
|
|
3822
|
+
var ContactUpsert = z14.object({
|
|
3823
|
+
external_id: z14.string().min(1).max(255).optional(),
|
|
3611
3824
|
email: Email.optional(),
|
|
3612
|
-
first_name:
|
|
3613
|
-
last_name:
|
|
3614
|
-
locale:
|
|
3825
|
+
first_name: z14.string().max(200).nullable().optional(),
|
|
3826
|
+
last_name: z14.string().max(200).nullable().optional(),
|
|
3827
|
+
locale: z14.string().min(2).max(35).nullable().optional(),
|
|
3615
3828
|
timezone: TimezoneName.nullable().optional(),
|
|
3616
3829
|
properties: Properties.optional(),
|
|
3617
|
-
topics:
|
|
3830
|
+
topics: z14.array(Slug).max(100).optional()
|
|
3618
3831
|
}).refine((v) => v.external_id !== void 0 || v.email !== void 0, {
|
|
3619
3832
|
message: "external_id or email is required",
|
|
3620
3833
|
path: ["email"]
|
|
3621
3834
|
});
|
|
3622
|
-
var ContactUpsertResult =
|
|
3835
|
+
var ContactUpsertResult = z14.object({
|
|
3623
3836
|
contact: Contact,
|
|
3624
|
-
created:
|
|
3837
|
+
created: z14.boolean()
|
|
3625
3838
|
});
|
|
3626
3839
|
var ContactListQuery = PageQuery.extend({
|
|
3627
|
-
email:
|
|
3628
|
-
external_id:
|
|
3840
|
+
email: z14.string().min(1).max(254).optional(),
|
|
3841
|
+
external_id: z14.string().min(1).max(255).optional(),
|
|
3629
3842
|
topic: Slug.optional()
|
|
3630
3843
|
});
|
|
3631
|
-
var ContactErased =
|
|
3632
|
-
ok:
|
|
3633
|
-
erased_messages:
|
|
3634
|
-
erased_recipients:
|
|
3635
|
-
suppressions_kept:
|
|
3844
|
+
var ContactErased = z14.object({
|
|
3845
|
+
ok: z14.literal(true),
|
|
3846
|
+
erased_messages: z14.number().int().min(0),
|
|
3847
|
+
erased_recipients: z14.number().int().min(0),
|
|
3848
|
+
suppressions_kept: z14.number().int().min(0)
|
|
3636
3849
|
});
|
|
3637
3850
|
var SUPPRESSION_REASONS = ["unsubscribed", "bounced", "complained", "manual"];
|
|
3638
|
-
var SuppressionReason =
|
|
3639
|
-
var Suppression =
|
|
3851
|
+
var SuppressionReason = z14.enum(SUPPRESSION_REASONS);
|
|
3852
|
+
var Suppression = z14.object({
|
|
3640
3853
|
id: Id,
|
|
3641
3854
|
email: Email,
|
|
3642
3855
|
reason: SuppressionReason,
|
|
3643
3856
|
topic: Slug.nullable(),
|
|
3644
3857
|
source_message_id: Id.nullable(),
|
|
3645
|
-
note:
|
|
3858
|
+
note: z14.string().max(1e3).nullable(),
|
|
3646
3859
|
created_at: Timestamp
|
|
3647
3860
|
});
|
|
3648
|
-
var SuppressionCreate =
|
|
3861
|
+
var SuppressionCreate = z14.object({
|
|
3649
3862
|
email: Email,
|
|
3650
|
-
reason:
|
|
3863
|
+
reason: z14.enum(["manual", "unsubscribed"]),
|
|
3651
3864
|
topic: Slug.nullable().optional(),
|
|
3652
|
-
note:
|
|
3865
|
+
note: z14.string().max(1e3).optional()
|
|
3653
3866
|
});
|
|
3654
3867
|
var SuppressionListQuery = PageQuery.extend({
|
|
3655
|
-
email:
|
|
3868
|
+
email: z14.string().min(1).max(254).optional(),
|
|
3656
3869
|
reason: SuppressionReason.optional(),
|
|
3657
3870
|
topic: Slug.optional()
|
|
3658
3871
|
});
|
|
3659
3872
|
|
|
3660
3873
|
// src/webhooks.ts
|
|
3661
|
-
import { z as
|
|
3874
|
+
import { z as z15 } from "zod";
|
|
3662
3875
|
var WEBHOOK_EVENT_TYPES = [
|
|
3663
3876
|
"message.sent",
|
|
3664
3877
|
"message.failed",
|
|
@@ -3702,23 +3915,23 @@ var WEBHOOK_EVENT_TYPES = [
|
|
|
3702
3915
|
"template.updated",
|
|
3703
3916
|
"template.deleted"
|
|
3704
3917
|
];
|
|
3705
|
-
var WebhookEventType =
|
|
3706
|
-
var MessageEventBase =
|
|
3918
|
+
var WebhookEventType = z15.enum(WEBHOOK_EVENT_TYPES);
|
|
3919
|
+
var MessageEventBase = z15.object({
|
|
3707
3920
|
message_id: Id,
|
|
3708
3921
|
mailing_id: Id.nullable(),
|
|
3709
3922
|
mailing_metadata: MailingMetadata.nullable(),
|
|
3710
3923
|
recipient_id: Id.nullable(),
|
|
3711
3924
|
contact_id: Id.nullable(),
|
|
3712
|
-
external_id:
|
|
3925
|
+
external_id: z15.string().nullable(),
|
|
3713
3926
|
email: Email,
|
|
3714
|
-
subject:
|
|
3927
|
+
subject: z15.string(),
|
|
3715
3928
|
topic: Slug.nullable(),
|
|
3716
|
-
provider_message_id:
|
|
3717
|
-
is_test:
|
|
3929
|
+
provider_message_id: z15.string().nullable(),
|
|
3930
|
+
is_test: z15.boolean()
|
|
3718
3931
|
});
|
|
3719
3932
|
var MessageSentData = MessageEventBase.extend({
|
|
3720
3933
|
/** The final HTML as sent, so the client can archive it beside its own records. */
|
|
3721
|
-
html:
|
|
3934
|
+
html: z15.string(),
|
|
3722
3935
|
sent_at: Timestamp,
|
|
3723
3936
|
/**
|
|
3724
3937
|
* The choice each insertion made (see `Message.insertions`); absent or null
|
|
@@ -3729,20 +3942,20 @@ var MessageSentData = MessageEventBase.extend({
|
|
|
3729
3942
|
insertions: InsertionChoices.nullish()
|
|
3730
3943
|
});
|
|
3731
3944
|
var MessageFailedData = MessageEventBase.extend({
|
|
3732
|
-
error:
|
|
3945
|
+
error: z15.string(),
|
|
3733
3946
|
/** False when the provider rejected permanently; the recipient will not be retried. */
|
|
3734
|
-
retryable:
|
|
3947
|
+
retryable: z15.boolean(),
|
|
3735
3948
|
failed_at: Timestamp
|
|
3736
3949
|
});
|
|
3737
3950
|
var UNSUBSCRIBE_SOURCES = ["hosted_page", "one_click", "api", "dashboard", "signup_form"];
|
|
3738
|
-
var ContactUnsubscribedData =
|
|
3951
|
+
var ContactUnsubscribedData = z15.object({
|
|
3739
3952
|
contact_id: Id.nullable(),
|
|
3740
|
-
external_id:
|
|
3953
|
+
external_id: z15.string().nullable(),
|
|
3741
3954
|
email: Email,
|
|
3742
3955
|
/** The topic unsubscribed from; null means every topic. */
|
|
3743
3956
|
topic: Slug.nullable(),
|
|
3744
3957
|
mailing_id: Id.nullable(),
|
|
3745
|
-
source:
|
|
3958
|
+
source: z15.enum(UNSUBSCRIBE_SOURCES),
|
|
3746
3959
|
unsubscribed_at: Timestamp
|
|
3747
3960
|
});
|
|
3748
3961
|
var ContactResubscribedData = ContactUnsubscribedData.omit({ unsubscribed_at: true }).extend({
|
|
@@ -3751,142 +3964,142 @@ var ContactResubscribedData = ContactUnsubscribedData.omit({ unsubscribed_at: tr
|
|
|
3751
3964
|
* breaker undid a `bounced` block it judged false (a `contact.bounced` for the
|
|
3752
3965
|
* same address and mailing went out before); `topic` is null.
|
|
3753
3966
|
*/
|
|
3754
|
-
source:
|
|
3967
|
+
source: z15.enum([...UNSUBSCRIBE_SOURCES, "bounce_reverted"]),
|
|
3755
3968
|
resubscribed_at: Timestamp
|
|
3756
3969
|
});
|
|
3757
|
-
var ContactBouncedData =
|
|
3970
|
+
var ContactBouncedData = z15.object({
|
|
3758
3971
|
contact_id: Id.nullable(),
|
|
3759
|
-
external_id:
|
|
3972
|
+
external_id: z15.string().nullable(),
|
|
3760
3973
|
email: Email,
|
|
3761
3974
|
/** `bounced` for a hard bounce, `complained` for a spam complaint. */
|
|
3762
|
-
reason:
|
|
3975
|
+
reason: z15.enum(["bounced", "complained"]),
|
|
3763
3976
|
message_id: Id.nullable(),
|
|
3764
|
-
diagnostic:
|
|
3977
|
+
diagnostic: z15.string().nullable(),
|
|
3765
3978
|
bounced_at: Timestamp
|
|
3766
3979
|
});
|
|
3767
|
-
var MailingFinishedData =
|
|
3980
|
+
var MailingFinishedData = z15.object({
|
|
3768
3981
|
mailing_id: Id,
|
|
3769
3982
|
mailing_metadata: MailingMetadata,
|
|
3770
|
-
status:
|
|
3983
|
+
status: z15.enum(["sent", "partially_failed", "cancelled"]),
|
|
3771
3984
|
counts: MailingCounts,
|
|
3772
3985
|
finished_at: Timestamp
|
|
3773
3986
|
});
|
|
3774
|
-
var ContactSubscribedData =
|
|
3987
|
+
var ContactSubscribedData = z15.object({
|
|
3775
3988
|
contact_id: Id,
|
|
3776
|
-
external_id:
|
|
3989
|
+
external_id: z15.string().nullable(),
|
|
3777
3990
|
email: Email,
|
|
3778
|
-
topics:
|
|
3779
|
-
source:
|
|
3991
|
+
topics: z15.array(Slug),
|
|
3992
|
+
source: z15.enum(["signup_form", "api", "dashboard", "automation"]),
|
|
3780
3993
|
signup_form_id: Id.nullable(),
|
|
3781
|
-
signup_form_version:
|
|
3994
|
+
signup_form_version: z15.number().int().min(1).nullable(),
|
|
3782
3995
|
subscribed_at: Timestamp
|
|
3783
3996
|
});
|
|
3784
|
-
var ImportFinishedData =
|
|
3997
|
+
var ImportFinishedData = z15.object({
|
|
3785
3998
|
import_id: Id,
|
|
3786
|
-
status:
|
|
3999
|
+
status: z15.enum(["completed", "cancelled", "failed"]),
|
|
3787
4000
|
result: ImportReport.nullable(),
|
|
3788
|
-
error:
|
|
4001
|
+
error: z15.string().nullable(),
|
|
3789
4002
|
finished_at: Timestamp
|
|
3790
4003
|
});
|
|
3791
|
-
var MailingScheduledData =
|
|
4004
|
+
var MailingScheduledData = z15.object({
|
|
3792
4005
|
mailing_id: Id,
|
|
3793
4006
|
mailing_metadata: MailingMetadata,
|
|
3794
4007
|
scheduled_at: Timestamp.nullable()
|
|
3795
4008
|
});
|
|
3796
|
-
var MailingStartedData =
|
|
4009
|
+
var MailingStartedData = z15.object({
|
|
3797
4010
|
mailing_id: Id,
|
|
3798
4011
|
mailing_metadata: MailingMetadata,
|
|
3799
|
-
trigger:
|
|
3800
|
-
recipients:
|
|
4012
|
+
trigger: z15.enum(["send", "schedule"]),
|
|
4013
|
+
recipients: z15.number().int().min(0),
|
|
3801
4014
|
started_at: Timestamp
|
|
3802
4015
|
});
|
|
3803
|
-
var MailingScheduleFailedData =
|
|
4016
|
+
var MailingScheduleFailedData = z15.object({
|
|
3804
4017
|
mailing_id: Id,
|
|
3805
4018
|
mailing_metadata: MailingMetadata,
|
|
3806
|
-
code:
|
|
3807
|
-
message:
|
|
4019
|
+
code: z15.string(),
|
|
4020
|
+
message: z15.string(),
|
|
3808
4021
|
failed_at: Timestamp
|
|
3809
4022
|
});
|
|
3810
|
-
var MailingAbWinnerSelectedData =
|
|
4023
|
+
var MailingAbWinnerSelectedData = z15.object({
|
|
3811
4024
|
mailing_id: Id,
|
|
3812
4025
|
mailing_metadata: MailingMetadata,
|
|
3813
|
-
winner:
|
|
3814
|
-
decided_by:
|
|
3815
|
-
variants:
|
|
4026
|
+
winner: z15.string(),
|
|
4027
|
+
decided_by: z15.enum(["metric", "manual"]),
|
|
4028
|
+
variants: z15.array(VariantAnalytics),
|
|
3816
4029
|
decided_at: Timestamp
|
|
3817
4030
|
});
|
|
3818
|
-
var ContactTaggedData =
|
|
4031
|
+
var ContactTaggedData = z15.object({
|
|
3819
4032
|
contact_id: Id,
|
|
3820
|
-
external_id:
|
|
4033
|
+
external_id: z15.string().nullable(),
|
|
3821
4034
|
email: Email,
|
|
3822
4035
|
/** Only the tags the contact did not already have. */
|
|
3823
|
-
tags:
|
|
3824
|
-
source:
|
|
4036
|
+
tags: z15.array(Slug).min(1),
|
|
4037
|
+
source: z15.enum(["api", "dashboard", "signup_form", "import", "automation"]),
|
|
3825
4038
|
tagged_at: Timestamp
|
|
3826
4039
|
});
|
|
3827
|
-
var ContactUpdatedData =
|
|
4040
|
+
var ContactUpdatedData = z15.object({
|
|
3828
4041
|
contact_id: Id,
|
|
3829
|
-
external_id:
|
|
4042
|
+
external_id: z15.string().nullable(),
|
|
3830
4043
|
email: Email,
|
|
3831
|
-
changed:
|
|
4044
|
+
changed: z15.array(z15.string()).min(1),
|
|
3832
4045
|
properties: Properties,
|
|
3833
|
-
source:
|
|
4046
|
+
source: z15.enum(["api", "dashboard", "signup_form", "import", "automation"]),
|
|
3834
4047
|
updated_at: Timestamp
|
|
3835
4048
|
});
|
|
3836
|
-
var AutomationRunEventBase =
|
|
4049
|
+
var AutomationRunEventBase = z15.object({
|
|
3837
4050
|
automation_id: Id,
|
|
3838
|
-
automation_name:
|
|
4051
|
+
automation_name: z15.string(),
|
|
3839
4052
|
run_id: Id,
|
|
3840
|
-
version:
|
|
4053
|
+
version: z15.number().int().min(1),
|
|
3841
4054
|
contact_id: Id,
|
|
3842
|
-
external_id:
|
|
4055
|
+
external_id: z15.string().nullable(),
|
|
3843
4056
|
email: Email
|
|
3844
4057
|
});
|
|
3845
4058
|
var AutomationRunStartedData = AutomationRunEventBase.extend({
|
|
3846
4059
|
/** What let them in: a trigger kind, or `manual` for an explicit enrolment. */
|
|
3847
|
-
trigger_kind:
|
|
4060
|
+
trigger_kind: z15.string(),
|
|
3848
4061
|
entered_at: Timestamp
|
|
3849
4062
|
});
|
|
3850
4063
|
var AutomationRunCompletedData = AutomationRunEventBase.extend({
|
|
3851
|
-
last_step_id:
|
|
4064
|
+
last_step_id: z15.string().nullable(),
|
|
3852
4065
|
completed_at: Timestamp
|
|
3853
4066
|
});
|
|
3854
4067
|
var AutomationRunExitedData = AutomationRunEventBase.extend({
|
|
3855
4068
|
reason: AutomationEndReason,
|
|
3856
|
-
step_id:
|
|
4069
|
+
step_id: z15.string().nullable(),
|
|
3857
4070
|
exited_at: Timestamp
|
|
3858
4071
|
});
|
|
3859
4072
|
var AutomationRunFailedData = AutomationRunEventBase.extend({
|
|
3860
|
-
reason:
|
|
3861
|
-
step_id:
|
|
4073
|
+
reason: z15.string(),
|
|
4074
|
+
step_id: z15.string().nullable(),
|
|
3862
4075
|
failed_at: Timestamp
|
|
3863
4076
|
});
|
|
3864
4077
|
var AutomationWebhookData = AutomationRunEventBase.extend({
|
|
3865
|
-
step_id:
|
|
4078
|
+
step_id: z15.string(),
|
|
3866
4079
|
/** The step's own label, when it was given one, so a receiver can branch on a name. */
|
|
3867
|
-
step_label:
|
|
4080
|
+
step_label: z15.string().nullable(),
|
|
3868
4081
|
/** Whatever the step carries, verbatim. Null when it carries nothing. */
|
|
3869
4082
|
payload: JsonValue.nullable(),
|
|
3870
4083
|
fired_at: Timestamp
|
|
3871
4084
|
});
|
|
3872
|
-
var TemplateEventBase =
|
|
4085
|
+
var TemplateEventBase = z15.object({
|
|
3873
4086
|
template_id: Id,
|
|
3874
|
-
name:
|
|
3875
|
-
version:
|
|
3876
|
-
source:
|
|
4087
|
+
name: z15.string().min(1).max(200),
|
|
4088
|
+
version: z15.number().int().min(1),
|
|
4089
|
+
source: z15.enum(["api", "dashboard", "mjml_import"])
|
|
3877
4090
|
});
|
|
3878
4091
|
var TemplateCreatedData = TemplateEventBase.extend({ created_at: Timestamp });
|
|
3879
|
-
var TemplateUpdatedData = TemplateEventBase.extend({ archived:
|
|
4092
|
+
var TemplateUpdatedData = TemplateEventBase.extend({ archived: z15.boolean(), updated_at: Timestamp });
|
|
3880
4093
|
var TemplateDeletedData = TemplateEventBase.extend({ deleted_at: Timestamp });
|
|
3881
|
-
var envelope = (type, data) =>
|
|
4094
|
+
var envelope = (type, data) => z15.object({
|
|
3882
4095
|
/** Unique per event: a receiver deduplicates on it (deliveries may repeat). */
|
|
3883
4096
|
id: Id,
|
|
3884
|
-
type:
|
|
4097
|
+
type: z15.literal(type),
|
|
3885
4098
|
created_at: Timestamp,
|
|
3886
4099
|
workspace_id: Id,
|
|
3887
4100
|
data
|
|
3888
4101
|
});
|
|
3889
|
-
var WebhookEvent =
|
|
4102
|
+
var WebhookEvent = z15.discriminatedUnion("type", [
|
|
3890
4103
|
envelope("message.sent", MessageSentData),
|
|
3891
4104
|
envelope("message.failed", MessageFailedData),
|
|
3892
4105
|
envelope("contact.unsubscribed", ContactUnsubscribedData),
|
|
@@ -3910,16 +4123,16 @@ var WebhookEvent = z14.discriminatedUnion("type", [
|
|
|
3910
4123
|
envelope("template.updated", TemplateUpdatedData),
|
|
3911
4124
|
envelope("template.deleted", TemplateDeletedData)
|
|
3912
4125
|
]);
|
|
3913
|
-
var WebhookEndpoint =
|
|
4126
|
+
var WebhookEndpoint = z15.object({
|
|
3914
4127
|
id: Id,
|
|
3915
|
-
url:
|
|
3916
|
-
description:
|
|
3917
|
-
events:
|
|
3918
|
-
enabled:
|
|
4128
|
+
url: z15.string().url(),
|
|
4129
|
+
description: z15.string().max(500).nullable(),
|
|
4130
|
+
events: z15.array(WebhookEventType).min(1),
|
|
4131
|
+
enabled: z15.boolean(),
|
|
3919
4132
|
created_at: Timestamp,
|
|
3920
4133
|
updated_at: Timestamp
|
|
3921
4134
|
});
|
|
3922
|
-
var WebhookUrl =
|
|
4135
|
+
var WebhookUrl = z15.string().url().refine((u) => {
|
|
3923
4136
|
try {
|
|
3924
4137
|
const url = new URL(u);
|
|
3925
4138
|
if (url.protocol === "https:") return true;
|
|
@@ -3928,35 +4141,35 @@ var WebhookUrl = z14.string().url().refine((u) => {
|
|
|
3928
4141
|
return false;
|
|
3929
4142
|
}
|
|
3930
4143
|
}, "must be https (http only for localhost)");
|
|
3931
|
-
var WebhookEndpointCreate =
|
|
4144
|
+
var WebhookEndpointCreate = z15.object({
|
|
3932
4145
|
url: WebhookUrl,
|
|
3933
|
-
description:
|
|
3934
|
-
events:
|
|
3935
|
-
enabled:
|
|
4146
|
+
description: z15.string().max(500).optional(),
|
|
4147
|
+
events: z15.array(WebhookEventType).min(1),
|
|
4148
|
+
enabled: z15.boolean().optional()
|
|
3936
4149
|
});
|
|
3937
|
-
var WebhookEndpointUpdate =
|
|
4150
|
+
var WebhookEndpointUpdate = z15.object({
|
|
3938
4151
|
url: WebhookUrl,
|
|
3939
|
-
description:
|
|
3940
|
-
events:
|
|
3941
|
-
enabled:
|
|
4152
|
+
description: z15.string().max(500).nullable(),
|
|
4153
|
+
events: z15.array(WebhookEventType).min(1),
|
|
4154
|
+
enabled: z15.boolean()
|
|
3942
4155
|
}).partial().refine((v) => Object.keys(v).length > 0, "at least one field");
|
|
3943
|
-
var WebhookEndpointWithSecret =
|
|
4156
|
+
var WebhookEndpointWithSecret = z15.object({
|
|
3944
4157
|
endpoint: WebhookEndpoint,
|
|
3945
|
-
secret:
|
|
4158
|
+
secret: z15.string().min(32)
|
|
3946
4159
|
});
|
|
3947
4160
|
var WEBHOOK_DELIVERY_STATUSES = ["pending", "succeeded", "failed"];
|
|
3948
|
-
var WebhookDeliveryStatus =
|
|
4161
|
+
var WebhookDeliveryStatus = z15.enum(WEBHOOK_DELIVERY_STATUSES);
|
|
3949
4162
|
var WEBHOOK_MAX_ATTEMPTS = 8;
|
|
3950
4163
|
var WEBHOOK_RETRY_DELAYS_SECONDS = [30, 120, 600, 1800, 3600, 7200, 21600];
|
|
3951
|
-
var WebhookDelivery =
|
|
4164
|
+
var WebhookDelivery = z15.object({
|
|
3952
4165
|
id: Id,
|
|
3953
4166
|
endpoint_id: Id,
|
|
3954
4167
|
event_id: Id,
|
|
3955
4168
|
event_type: WebhookEventType,
|
|
3956
4169
|
status: WebhookDeliveryStatus,
|
|
3957
|
-
attempts:
|
|
3958
|
-
last_status_code:
|
|
3959
|
-
last_error:
|
|
4170
|
+
attempts: z15.number().int().min(0),
|
|
4171
|
+
last_status_code: z15.number().int().min(100).max(599).nullable(),
|
|
4172
|
+
last_error: z15.string().nullable(),
|
|
3960
4173
|
next_attempt_at: Timestamp.nullable(),
|
|
3961
4174
|
delivered_at: Timestamp.nullable(),
|
|
3962
4175
|
created_at: Timestamp
|
|
@@ -3965,7 +4178,7 @@ var WebhookDeliveryListQuery = PageQuery.extend({
|
|
|
3965
4178
|
status: WebhookDeliveryStatus.optional(),
|
|
3966
4179
|
event_type: WebhookEventType.optional()
|
|
3967
4180
|
});
|
|
3968
|
-
var WebhookDeliveryParams =
|
|
4181
|
+
var WebhookDeliveryParams = z15.object({ id: Id, delivery_id: Id });
|
|
3969
4182
|
|
|
3970
4183
|
// src/webhook-signing.ts
|
|
3971
4184
|
var WEBHOOK_SIGNATURE_HEADER = "x-mail-signature";
|
|
@@ -4032,79 +4245,79 @@ async function verifyWebhook(input) {
|
|
|
4032
4245
|
}
|
|
4033
4246
|
|
|
4034
4247
|
// src/billing.ts
|
|
4035
|
-
import { z as
|
|
4248
|
+
import { z as z16 } from "zod";
|
|
4036
4249
|
var PLAN_IDS = ["free", "starter", "growth", "design_partner"];
|
|
4037
|
-
var PlanId =
|
|
4038
|
-
var PlanLimits =
|
|
4039
|
-
monthly_messages:
|
|
4040
|
-
contacts:
|
|
4041
|
-
members:
|
|
4042
|
-
providers:
|
|
4043
|
-
webhook_endpoints:
|
|
4250
|
+
var PlanId = z16.enum(PLAN_IDS);
|
|
4251
|
+
var PlanLimits = z16.object({
|
|
4252
|
+
monthly_messages: z16.number().int().min(0).nullable(),
|
|
4253
|
+
contacts: z16.number().int().min(0).nullable(),
|
|
4254
|
+
members: z16.number().int().min(1).nullable(),
|
|
4255
|
+
providers: z16.number().int().min(1).nullable(),
|
|
4256
|
+
webhook_endpoints: z16.number().int().min(0).nullable(),
|
|
4044
4257
|
/** Automations that may be live (active or paused) at once (A1). */
|
|
4045
|
-
active_automations:
|
|
4258
|
+
active_automations: z16.number().int().min(0).nullable(),
|
|
4046
4259
|
/** Steps one automation may hold. Null is the flow's own cap, not unlimited. */
|
|
4047
|
-
automation_steps:
|
|
4260
|
+
automation_steps: z16.number().int().min(1).nullable()
|
|
4048
4261
|
});
|
|
4049
|
-
var PlanFeatures =
|
|
4262
|
+
var PlanFeatures = z16.object({
|
|
4050
4263
|
/** A/B tests of subject and content on a mailing (S4). */
|
|
4051
|
-
ab_testing:
|
|
4264
|
+
ab_testing: z16.boolean(),
|
|
4052
4265
|
/** Open and click tracking, still off until a workspace turns it on (`settings.tracking_enabled`). */
|
|
4053
|
-
tracking:
|
|
4266
|
+
tracking: z16.boolean(),
|
|
4054
4267
|
/** Sending and hosting pages on the workspace's own domain (a later phase). */
|
|
4055
|
-
custom_domains:
|
|
4268
|
+
custom_domains: z16.boolean(),
|
|
4056
4269
|
/** Automations: flows a person walks through step by step (A1). */
|
|
4057
|
-
automations:
|
|
4270
|
+
automations: z16.boolean()
|
|
4058
4271
|
});
|
|
4059
4272
|
var PLAN_FEATURES = ["ab_testing", "tracking", "custom_domains", "automations"];
|
|
4060
|
-
var Plan =
|
|
4273
|
+
var Plan = z16.object({
|
|
4061
4274
|
id: PlanId,
|
|
4062
|
-
name:
|
|
4275
|
+
name: z16.string().min(1),
|
|
4063
4276
|
/** Price in the smallest currency unit per month; null for plans not sold. */
|
|
4064
|
-
monthly_price_cents:
|
|
4065
|
-
currency:
|
|
4277
|
+
monthly_price_cents: z16.number().int().min(0).nullable(),
|
|
4278
|
+
currency: z16.string().length(3),
|
|
4066
4279
|
limits: PlanLimits,
|
|
4067
4280
|
features: PlanFeatures
|
|
4068
4281
|
});
|
|
4069
|
-
var CatalogPlan = Plan.extend({ sellable:
|
|
4070
|
-
var Subscription =
|
|
4282
|
+
var CatalogPlan = Plan.extend({ sellable: z16.boolean() });
|
|
4283
|
+
var Subscription = z16.object({
|
|
4071
4284
|
workspace_id: Id,
|
|
4072
4285
|
plan: PlanId,
|
|
4073
|
-
status:
|
|
4286
|
+
status: z16.enum(["active", "trialing", "past_due", "cancelled"]),
|
|
4074
4287
|
current_period_start: Timestamp,
|
|
4075
4288
|
current_period_end: Timestamp,
|
|
4076
|
-
cancel_at_period_end:
|
|
4289
|
+
cancel_at_period_end: z16.boolean(),
|
|
4077
4290
|
/**
|
|
4078
4291
|
* A design partner outside billing (plan `design_partner`, no limits, no
|
|
4079
4292
|
* checkout). Set only by an operator, never through the API.
|
|
4080
4293
|
*/
|
|
4081
|
-
billing_exempt:
|
|
4294
|
+
billing_exempt: z16.boolean()
|
|
4082
4295
|
});
|
|
4083
4296
|
var USAGE_METRICS = ["messages", "contacts", "members", "providers", "webhook_endpoints", "active_automations"];
|
|
4084
|
-
var UsageMetric =
|
|
4297
|
+
var UsageMetric = z16.enum(USAGE_METRICS);
|
|
4085
4298
|
var USAGE_WARNING_RATIO = 0.8;
|
|
4086
|
-
var UsageWarning =
|
|
4299
|
+
var UsageWarning = z16.object({
|
|
4087
4300
|
metric: UsageMetric,
|
|
4088
|
-
used:
|
|
4089
|
-
limit:
|
|
4301
|
+
used: z16.number().int().min(0),
|
|
4302
|
+
limit: z16.number().int().min(0),
|
|
4090
4303
|
/** `approaching`: at or above USAGE_WARNING_RATIO of the limit; `reached`: at or above the limit. */
|
|
4091
|
-
level:
|
|
4304
|
+
level: z16.enum(["approaching", "reached"])
|
|
4092
4305
|
});
|
|
4093
|
-
var Usage =
|
|
4306
|
+
var Usage = z16.object({
|
|
4094
4307
|
plan: PlanId,
|
|
4095
4308
|
period_start: Timestamp,
|
|
4096
4309
|
period_end: Timestamp,
|
|
4097
|
-
metrics:
|
|
4098
|
-
warnings:
|
|
4310
|
+
metrics: z16.record(UsageMetric, z16.object({ used: z16.number().int().min(0), limit: z16.number().int().min(0).nullable() })),
|
|
4311
|
+
warnings: z16.array(UsageWarning)
|
|
4099
4312
|
});
|
|
4100
|
-
var CheckoutRequest =
|
|
4101
|
-
plan:
|
|
4102
|
-
success_url:
|
|
4103
|
-
cancel_url:
|
|
4313
|
+
var CheckoutRequest = z16.object({
|
|
4314
|
+
plan: z16.enum(["starter", "growth"]),
|
|
4315
|
+
success_url: z16.string().url(),
|
|
4316
|
+
cancel_url: z16.string().url()
|
|
4104
4317
|
});
|
|
4105
|
-
var CheckoutSession =
|
|
4106
|
-
var PortalRequest =
|
|
4107
|
-
var PortalSession =
|
|
4318
|
+
var CheckoutSession = z16.object({ url: z16.string().url() });
|
|
4319
|
+
var PortalRequest = z16.object({ return_url: z16.string().url() });
|
|
4320
|
+
var PortalSession = z16.object({ url: z16.string().url() });
|
|
4108
4321
|
var USAGE_WARNING_HEADER = "x-mail-usage-warning";
|
|
4109
4322
|
function formatUsageWarningHeader(warnings) {
|
|
4110
4323
|
if (warnings.length === 0) return null;
|
|
@@ -4124,7 +4337,7 @@ function parseUsageWarningHeader(value) {
|
|
|
4124
4337
|
}
|
|
4125
4338
|
|
|
4126
4339
|
// src/routes.ts
|
|
4127
|
-
import { z as
|
|
4340
|
+
import { z as z17 } from "zod";
|
|
4128
4341
|
var list = (item) => page(item);
|
|
4129
4342
|
var foundationRoutes = {
|
|
4130
4343
|
"workspaces.create": {
|
|
@@ -4352,7 +4565,7 @@ var templateRoutes = {
|
|
|
4352
4565
|
path: "/v1/templates/:id/export",
|
|
4353
4566
|
params: IdParams,
|
|
4354
4567
|
query: TemplateExportQuery,
|
|
4355
|
-
response:
|
|
4568
|
+
response: z17.string(),
|
|
4356
4569
|
responseType: "text",
|
|
4357
4570
|
status: 200,
|
|
4358
4571
|
access: "read",
|
|
@@ -4676,7 +4889,7 @@ var sendingRoutes = {
|
|
|
4676
4889
|
path: "/v1/mailings/:id/export",
|
|
4677
4890
|
params: IdParams,
|
|
4678
4891
|
query: MailingExportQuery,
|
|
4679
|
-
response:
|
|
4892
|
+
response: z17.string(),
|
|
4680
4893
|
responseType: "text",
|
|
4681
4894
|
status: 200,
|
|
4682
4895
|
access: "read",
|
|
@@ -4722,6 +4935,21 @@ var sendingRoutes = {
|
|
|
4722
4935
|
access: "write",
|
|
4723
4936
|
phase: "S2"
|
|
4724
4937
|
},
|
|
4938
|
+
/**
|
|
4939
|
+
* A dry run: what each given recipient would be sent (every value's text and
|
|
4940
|
+
* the layer it comes from, each insertion's choice, the problems found),
|
|
4941
|
+
* without adding anybody. Reads only, in any state of the mailing.
|
|
4942
|
+
*/
|
|
4943
|
+
"mailings.resolve": {
|
|
4944
|
+
method: "POST",
|
|
4945
|
+
path: "/v1/mailings/:id/resolve",
|
|
4946
|
+
params: IdParams,
|
|
4947
|
+
body: MailingResolveRequest,
|
|
4948
|
+
response: MailingResolveResult,
|
|
4949
|
+
status: 200,
|
|
4950
|
+
access: "read",
|
|
4951
|
+
phase: "S2"
|
|
4952
|
+
},
|
|
4725
4953
|
"mailings.send": {
|
|
4726
4954
|
method: "POST",
|
|
4727
4955
|
path: "/v1/mailings/:id/send",
|
|
@@ -4909,7 +5137,7 @@ var platformRoutes = {
|
|
|
4909
5137
|
"contactProperties.list": {
|
|
4910
5138
|
method: "GET",
|
|
4911
5139
|
path: "/v1/contact-properties",
|
|
4912
|
-
response:
|
|
5140
|
+
response: z17.object({ data: z17.array(ContactPropertyDefinition) }),
|
|
4913
5141
|
status: 200,
|
|
4914
5142
|
access: "read",
|
|
4915
5143
|
phase: "S4"
|
|
@@ -5208,7 +5436,7 @@ var billingRoutes = {
|
|
|
5208
5436
|
"billing.plans": {
|
|
5209
5437
|
method: "GET",
|
|
5210
5438
|
path: "/v1/billing/plans",
|
|
5211
|
-
response:
|
|
5439
|
+
response: z17.object({ data: z17.array(CatalogPlan) }),
|
|
5212
5440
|
status: 200,
|
|
5213
5441
|
access: "read",
|
|
5214
5442
|
phase: "S5"
|
|
@@ -5350,7 +5578,7 @@ var automationRoutes = {
|
|
|
5350
5578
|
method: "POST",
|
|
5351
5579
|
path: "/v1/automations/:id/pause",
|
|
5352
5580
|
params: IdParams,
|
|
5353
|
-
body:
|
|
5581
|
+
body: z17.object({}).strict(),
|
|
5354
5582
|
response: Automation,
|
|
5355
5583
|
status: 200,
|
|
5356
5584
|
access: "write",
|
|
@@ -5360,7 +5588,7 @@ var automationRoutes = {
|
|
|
5360
5588
|
method: "POST",
|
|
5361
5589
|
path: "/v1/automations/:id/resume",
|
|
5362
5590
|
params: IdParams,
|
|
5363
|
-
body:
|
|
5591
|
+
body: z17.object({}).strict(),
|
|
5364
5592
|
response: Automation,
|
|
5365
5593
|
status: 200,
|
|
5366
5594
|
access: "write",
|
|
@@ -5370,7 +5598,7 @@ var automationRoutes = {
|
|
|
5370
5598
|
method: "POST",
|
|
5371
5599
|
path: "/v1/automations/:id/archive",
|
|
5372
5600
|
params: IdParams,
|
|
5373
|
-
body:
|
|
5601
|
+
body: z17.object({}).strict(),
|
|
5374
5602
|
response: Automation,
|
|
5375
5603
|
status: 200,
|
|
5376
5604
|
access: "write",
|
|
@@ -5504,13 +5732,13 @@ function matchRoute(method, pathname) {
|
|
|
5504
5732
|
for (const [id, route] of Object.entries(routes)) {
|
|
5505
5733
|
if (route.method !== method) continue;
|
|
5506
5734
|
const names = [];
|
|
5507
|
-
const
|
|
5735
|
+
const pattern3 = new RegExp(
|
|
5508
5736
|
"^" + route.path.replace(/[.*+?^${}()|[\]\\]/g, "\\$&").replace(/:([a-z_]+)/g, (_, name) => {
|
|
5509
5737
|
names.push(name);
|
|
5510
5738
|
return "([^/]+)";
|
|
5511
5739
|
}) + "$"
|
|
5512
5740
|
);
|
|
5513
|
-
const match =
|
|
5741
|
+
const match = pattern3.exec(pathname);
|
|
5514
5742
|
if (!match) continue;
|
|
5515
5743
|
const params = {};
|
|
5516
5744
|
names.forEach((name, i) => {
|
|
@@ -5710,6 +5938,7 @@ export {
|
|
|
5710
5938
|
InviteListQuery,
|
|
5711
5939
|
InviteStatus,
|
|
5712
5940
|
JsonValue,
|
|
5941
|
+
LETTER_ONLY_MERGE_FIELDS,
|
|
5713
5942
|
LETTER_RECIPIENT_REFUSAL,
|
|
5714
5943
|
LIST_UNSUBSCRIBE_POST_VALUE,
|
|
5715
5944
|
LOCALE_TAG_PATTERN,
|
|
@@ -5778,7 +6007,10 @@ export {
|
|
|
5778
6007
|
MailingLanguageReport,
|
|
5779
6008
|
MailingLanguageStatus,
|
|
5780
6009
|
MailingListQuery,
|
|
6010
|
+
MailingMerge,
|
|
5781
6011
|
MailingMetadata,
|
|
6012
|
+
MailingResolveRequest,
|
|
6013
|
+
MailingResolveResult,
|
|
5782
6014
|
MailingRetryFailedRequest,
|
|
5783
6015
|
MailingScheduleFailedData,
|
|
5784
6016
|
MailingScheduleRequest,
|
|
@@ -5803,6 +6035,8 @@ export {
|
|
|
5803
6035
|
MessageOutcome,
|
|
5804
6036
|
MessageSentData,
|
|
5805
6037
|
MessageSummary,
|
|
6038
|
+
MessageValues,
|
|
6039
|
+
NAME_ON_BROADCAST_REFUSAL,
|
|
5806
6040
|
NotifyRecipients,
|
|
5807
6041
|
NotifyStep,
|
|
5808
6042
|
ON_BEHALF_OF_HEADER,
|
|
@@ -5838,6 +6072,8 @@ export {
|
|
|
5838
6072
|
REQUEST_ID_HEADER,
|
|
5839
6073
|
RESEND_EVENT_TYPES,
|
|
5840
6074
|
RESERVED_MERGE_FIELDS,
|
|
6075
|
+
RESOLVE_PROBLEMS,
|
|
6076
|
+
RESOLVE_REJECTIONS,
|
|
5841
6077
|
RETRYABLE_ERRORS,
|
|
5842
6078
|
RETRY_AFTER_HEADER,
|
|
5843
6079
|
RE_ENTRY_MODES,
|
|
@@ -5850,6 +6086,9 @@ export {
|
|
|
5850
6086
|
RecipientStatus,
|
|
5851
6087
|
RemoteImage,
|
|
5852
6088
|
ResendSigningSecret,
|
|
6089
|
+
ResolveProblem,
|
|
6090
|
+
ResolvedRecipient,
|
|
6091
|
+
ResolvedValue,
|
|
5853
6092
|
SERVICE_FILLED_MERGE_FIELDS,
|
|
5854
6093
|
SIGNUP_FORM_FIELDS,
|
|
5855
6094
|
SKIP_REASONS,
|
|
@@ -5936,10 +6175,13 @@ export {
|
|
|
5936
6175
|
USAGE_METRICS,
|
|
5937
6176
|
USAGE_WARNING_HEADER,
|
|
5938
6177
|
USAGE_WARNING_RATIO,
|
|
6178
|
+
UnfilledLink,
|
|
5939
6179
|
Usage,
|
|
5940
6180
|
UsageMetric,
|
|
5941
6181
|
UsageWarning,
|
|
6182
|
+
VALUE_LAYERS,
|
|
5942
6183
|
ValidationIssue,
|
|
6184
|
+
ValueLayer,
|
|
5943
6185
|
VariantAnalytics,
|
|
5944
6186
|
WEBHOOK_DELIVERY_STATUSES,
|
|
5945
6187
|
WEBHOOK_EVENT_ID_HEADER,
|
|
@@ -6003,6 +6245,7 @@ export {
|
|
|
6003
6245
|
editMainInsertions,
|
|
6004
6246
|
effectivePreviewPicks,
|
|
6005
6247
|
emptyFlow,
|
|
6248
|
+
emptyLinkValues,
|
|
6006
6249
|
errorBody,
|
|
6007
6250
|
escapeMergeHtml,
|
|
6008
6251
|
exportFilename,
|
|
@@ -6033,6 +6276,8 @@ export {
|
|
|
6033
6276
|
languageInsertions,
|
|
6034
6277
|
languageName,
|
|
6035
6278
|
languageTabs,
|
|
6279
|
+
letterOnlyValues,
|
|
6280
|
+
linkTokens,
|
|
6036
6281
|
mainInsertionText,
|
|
6037
6282
|
matchLocale,
|
|
6038
6283
|
matchRoute,
|
|
@@ -6053,6 +6298,8 @@ export {
|
|
|
6053
6298
|
previewInsertions,
|
|
6054
6299
|
previewPersonalization,
|
|
6055
6300
|
primaryLanguage,
|
|
6301
|
+
printedFields,
|
|
6302
|
+
rawMergeValue,
|
|
6056
6303
|
reachable,
|
|
6057
6304
|
readyWithoutInsertions,
|
|
6058
6305
|
readyWithoutSubject,
|
|
@@ -6069,6 +6316,7 @@ export {
|
|
|
6069
6316
|
renameTokenInDocument,
|
|
6070
6317
|
renameTokenInText,
|
|
6071
6318
|
resolveInsertions,
|
|
6319
|
+
resolveValue,
|
|
6072
6320
|
restorePatch,
|
|
6073
6321
|
routes,
|
|
6074
6322
|
sameJson,
|
|
@@ -6095,5 +6343,7 @@ export {
|
|
|
6095
6343
|
unknownRefusedInsertions,
|
|
6096
6344
|
unplacedInsertions,
|
|
6097
6345
|
validateAutomation,
|
|
6346
|
+
valueNames,
|
|
6347
|
+
valuesRead,
|
|
6098
6348
|
verifyWebhook
|
|
6099
6349
|
};
|