@marlinjai/mail-contract 0.10.0 → 0.12.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 +2418 -988
- package/dist/index.d.ts +2418 -988
- package/dist/index.js +926 -640
- package/dist/index.mjs +900 -640
- 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
|
|
@@ -995,6 +1162,12 @@ var Mailing = z7.object({
|
|
|
995
1162
|
status: MailingStatus,
|
|
996
1163
|
counts: MailingCounts,
|
|
997
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({}),
|
|
998
1171
|
scheduled_at: Timestamp.nullable(),
|
|
999
1172
|
/** The A/B test on subject or content (S4), null when there is none. */
|
|
1000
1173
|
ab_test: AbTestState.nullable(),
|
|
@@ -1006,15 +1179,15 @@ var Mailing = z7.object({
|
|
|
1006
1179
|
* recipients refused as dead addresses in one run (see the provider's
|
|
1007
1180
|
* `rejections.anomaly`). Resuming clears it and starts a new run.
|
|
1008
1181
|
*/
|
|
1009
|
-
pause_reason:
|
|
1182
|
+
pause_reason: z8.string().nullable().default(null),
|
|
1010
1183
|
created_at: Timestamp,
|
|
1011
1184
|
updated_at: Timestamp
|
|
1012
1185
|
});
|
|
1013
1186
|
var MailingSummary = Mailing.omit({ document: true });
|
|
1014
1187
|
var MailingContent = {
|
|
1015
|
-
name:
|
|
1016
|
-
subject:
|
|
1017
|
-
preheader:
|
|
1188
|
+
name: z8.string().max(200).optional(),
|
|
1189
|
+
subject: z8.string().min(1).max(998),
|
|
1190
|
+
preheader: z8.string().max(500).optional(),
|
|
1018
1191
|
/** See `Mailing.send_locale`. */
|
|
1019
1192
|
send_locale: LocaleTag.nullable().optional(),
|
|
1020
1193
|
/**
|
|
@@ -1025,9 +1198,11 @@ var MailingContent = {
|
|
|
1025
1198
|
*/
|
|
1026
1199
|
topic: Slug.nullable().optional(),
|
|
1027
1200
|
provider_id: Id,
|
|
1028
|
-
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()
|
|
1029
1204
|
};
|
|
1030
|
-
var MailingCreate =
|
|
1205
|
+
var MailingCreate = z8.object({
|
|
1031
1206
|
...MailingContent,
|
|
1032
1207
|
subject: MailingContent.subject.optional(),
|
|
1033
1208
|
document: TemplateDocument.optional(),
|
|
@@ -1043,15 +1218,22 @@ var MailingCreate = z7.object({
|
|
|
1043
1218
|
message: "a mailing made from a document needs a subject",
|
|
1044
1219
|
path: ["subject"]
|
|
1045
1220
|
});
|
|
1046
|
-
var MailingUpdate =
|
|
1047
|
-
name:
|
|
1221
|
+
var MailingUpdate = z8.object({
|
|
1222
|
+
name: z8.string().max(200).nullable().optional(),
|
|
1048
1223
|
subject: MailingContent.subject.optional(),
|
|
1049
|
-
preheader:
|
|
1224
|
+
preheader: z8.string().max(500).nullable().optional(),
|
|
1050
1225
|
/** Null turns a draft into a letter, which it can only be with at most one recipient. */
|
|
1051
1226
|
topic: Slug.nullable().optional(),
|
|
1052
1227
|
send_locale: LocaleTag.nullable().optional(),
|
|
1053
1228
|
provider_id: Id.optional(),
|
|
1054
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(),
|
|
1055
1237
|
document: TemplateDocument.optional()
|
|
1056
1238
|
}).refine((v) => Object.keys(v).length > 0, "at least one field");
|
|
1057
1239
|
var MailingListQuery = PageQuery.extend({
|
|
@@ -1060,30 +1242,30 @@ var MailingListQuery = PageQuery.extend({
|
|
|
1060
1242
|
kind: MailingKind.optional(),
|
|
1061
1243
|
automation_id: Id.optional()
|
|
1062
1244
|
});
|
|
1063
|
-
var MailingSendRequest =
|
|
1064
|
-
var MailingRetryFailedRequest =
|
|
1245
|
+
var MailingSendRequest = z8.object({}).strict();
|
|
1246
|
+
var MailingRetryFailedRequest = z8.object({
|
|
1065
1247
|
/**
|
|
1066
1248
|
* Also requeue recipients whose outcome is unknown after a crash. They may
|
|
1067
1249
|
* already have received the message, so this is a human's decision, never a
|
|
1068
1250
|
* default.
|
|
1069
1251
|
*/
|
|
1070
|
-
include_outcome_unknown:
|
|
1252
|
+
include_outcome_unknown: z8.boolean().optional()
|
|
1071
1253
|
}).strict();
|
|
1072
|
-
var MailingActionRequest =
|
|
1073
|
-
var MailingTestRequest =
|
|
1254
|
+
var MailingActionRequest = z8.object({}).strict();
|
|
1255
|
+
var MailingTestRequest = z8.object({
|
|
1074
1256
|
to: Email,
|
|
1075
1257
|
merge: Properties.optional(),
|
|
1076
1258
|
locale: LocaleTag.optional()
|
|
1077
1259
|
});
|
|
1078
|
-
var MailingTestResult =
|
|
1260
|
+
var MailingTestResult = z8.object({
|
|
1079
1261
|
message_id: Id,
|
|
1080
|
-
provider_message_id:
|
|
1262
|
+
provider_message_id: z8.string().nullable()
|
|
1081
1263
|
});
|
|
1082
1264
|
var RECIPIENT_STATUSES = ["queued", "sending", "sent", "failed", "skipped"];
|
|
1083
|
-
var RecipientStatus =
|
|
1265
|
+
var RecipientStatus = z8.enum(RECIPIENT_STATUSES);
|
|
1084
1266
|
var SKIP_REASONS = ["suppressed", "not_subscribed", "contact_erased", "cancelled", "outcome_unknown"];
|
|
1085
|
-
var SkipReason =
|
|
1086
|
-
var Recipient =
|
|
1267
|
+
var SkipReason = z8.enum(SKIP_REASONS);
|
|
1268
|
+
var Recipient = z8.object({
|
|
1087
1269
|
id: Id,
|
|
1088
1270
|
mailing_id: Id,
|
|
1089
1271
|
contact_id: Id.nullable(),
|
|
@@ -1091,55 +1273,55 @@ var Recipient = z7.object({
|
|
|
1091
1273
|
merge: Properties,
|
|
1092
1274
|
status: RecipientStatus,
|
|
1093
1275
|
skip_reason: SkipReason.nullable(),
|
|
1094
|
-
attempts:
|
|
1276
|
+
attempts: z8.number().int().min(0),
|
|
1095
1277
|
message_id: Id.nullable(),
|
|
1096
|
-
last_error:
|
|
1278
|
+
last_error: z8.string().nullable(),
|
|
1097
1279
|
created_at: Timestamp,
|
|
1098
1280
|
updated_at: Timestamp
|
|
1099
1281
|
});
|
|
1100
1282
|
var MAX_RECIPIENTS_PER_BATCH = 1e3;
|
|
1101
1283
|
var MAX_LETTER_RECIPIENTS = 1;
|
|
1102
1284
|
var LETTER_RECIPIENT_REFUSAL = "letter_has_one_recipient";
|
|
1103
|
-
var RecipientInput =
|
|
1285
|
+
var RecipientInput = z8.object({
|
|
1104
1286
|
contact_id: Id.optional(),
|
|
1105
|
-
external_id:
|
|
1287
|
+
external_id: z8.string().min(1).max(255).optional(),
|
|
1106
1288
|
email: Email.optional(),
|
|
1107
1289
|
merge: Properties.optional()
|
|
1108
1290
|
}).refine((v) => v.contact_id !== void 0 || v.external_id !== void 0 || v.email !== void 0, {
|
|
1109
1291
|
message: "contact_id, external_id or email is required",
|
|
1110
1292
|
path: ["email"]
|
|
1111
1293
|
});
|
|
1112
|
-
var RecipientBatch =
|
|
1113
|
-
recipients:
|
|
1294
|
+
var RecipientBatch = z8.object({
|
|
1295
|
+
recipients: z8.array(RecipientInput).min(1).max(MAX_RECIPIENTS_PER_BATCH)
|
|
1114
1296
|
});
|
|
1115
1297
|
var RECIPIENT_REJECTIONS = ["unknown_contact", "duplicate_in_batch"];
|
|
1116
|
-
var RecipientBatchResult =
|
|
1298
|
+
var RecipientBatchResult = z8.object({
|
|
1117
1299
|
/** Newly queued. */
|
|
1118
|
-
added:
|
|
1300
|
+
added: z8.number().int().min(0),
|
|
1119
1301
|
/** Already on the mailing from an earlier batch: left unchanged. */
|
|
1120
|
-
already_present:
|
|
1302
|
+
already_present: z8.number().int().min(0),
|
|
1121
1303
|
/** Items not added, by their index in the request. */
|
|
1122
|
-
rejected:
|
|
1304
|
+
rejected: z8.array(z8.object({ index: z8.number().int().min(0), reason: z8.enum(RECIPIENT_REJECTIONS) }))
|
|
1123
1305
|
});
|
|
1124
1306
|
var RecipientListQuery = PageQuery.extend({
|
|
1125
1307
|
status: RecipientStatus.optional(),
|
|
1126
1308
|
skip_reason: SkipReason.optional()
|
|
1127
1309
|
});
|
|
1128
1310
|
var MESSAGE_OUTCOMES = ["sent", "failed"];
|
|
1129
|
-
var MessageOutcome =
|
|
1130
|
-
var MessageSummary =
|
|
1311
|
+
var MessageOutcome = z8.enum(MESSAGE_OUTCOMES);
|
|
1312
|
+
var MessageSummary = z8.object({
|
|
1131
1313
|
id: Id,
|
|
1132
1314
|
mailing_id: Id.nullable(),
|
|
1133
1315
|
recipient_id: Id.nullable(),
|
|
1134
1316
|
contact_id: Id.nullable(),
|
|
1135
1317
|
to: Email,
|
|
1136
|
-
subject:
|
|
1318
|
+
subject: z8.string(),
|
|
1137
1319
|
provider_id: Id,
|
|
1138
|
-
provider_message_id:
|
|
1320
|
+
provider_message_id: z8.string().nullable(),
|
|
1139
1321
|
outcome: MessageOutcome,
|
|
1140
|
-
error:
|
|
1141
|
-
is_test:
|
|
1142
|
-
recipient_count:
|
|
1322
|
+
error: z8.string().nullable(),
|
|
1323
|
+
is_test: z8.boolean(),
|
|
1324
|
+
recipient_count: z8.number().int().min(1),
|
|
1143
1325
|
/** The language version the message was sent in; null for messages archived before languages existed. */
|
|
1144
1326
|
locale: LocaleTag.nullable(),
|
|
1145
1327
|
/**
|
|
@@ -1149,24 +1331,32 @@ var MessageSummary = z7.object({
|
|
|
1149
1331
|
* "otherwise" text. Null when the message had no insertions.
|
|
1150
1332
|
*/
|
|
1151
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),
|
|
1152
1342
|
created_at: Timestamp
|
|
1153
1343
|
});
|
|
1154
|
-
var Message = MessageSummary.extend({ html:
|
|
1155
|
-
var MailingLanguageStatus =
|
|
1156
|
-
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({
|
|
1157
1347
|
locale: LocaleTag,
|
|
1158
1348
|
status: MailingLanguageStatus,
|
|
1159
1349
|
/** Queued recipients who will get this version. */
|
|
1160
|
-
planned:
|
|
1350
|
+
planned: z8.number().int().min(0),
|
|
1161
1351
|
/** Messages already sent in this language (tests excluded). */
|
|
1162
|
-
sent:
|
|
1352
|
+
sent: z8.number().int().min(0)
|
|
1163
1353
|
});
|
|
1164
|
-
var MailingLanguageReport =
|
|
1354
|
+
var MailingLanguageReport = z8.object({
|
|
1165
1355
|
main_locale: LocaleTag,
|
|
1166
1356
|
send_locale: LocaleTag.nullable(),
|
|
1167
|
-
languages:
|
|
1168
|
-
fallback:
|
|
1169
|
-
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),
|
|
1170
1360
|
/**
|
|
1171
1361
|
* Values that no choice of an insertion takes, so the "otherwise" text goes
|
|
1172
1362
|
* out (docs/plans/2026-09-25-translated-insertions.md, decision 3): per
|
|
@@ -1175,41 +1365,56 @@ var MailingLanguageReport = z7.object({
|
|
|
1175
1365
|
* template's keys never produces one; a non-empty list means the client and
|
|
1176
1366
|
* the template have drifted apart.
|
|
1177
1367
|
*/
|
|
1178
|
-
insertion_misses:
|
|
1179
|
-
|
|
1180
|
-
insertion:
|
|
1181
|
-
value:
|
|
1182
|
-
planned:
|
|
1183
|
-
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)
|
|
1184
1374
|
})
|
|
1185
1375
|
)
|
|
1186
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
|
+
});
|
|
1187
1392
|
var MessageListQuery = PageQuery.extend({
|
|
1188
1393
|
mailing_id: Id.optional(),
|
|
1189
1394
|
outcome: MessageOutcome.optional()
|
|
1190
1395
|
});
|
|
1191
1396
|
|
|
1192
1397
|
// src/platform.ts
|
|
1193
|
-
var Tag =
|
|
1398
|
+
var Tag = z9.object({
|
|
1194
1399
|
id: Id,
|
|
1195
1400
|
slug: Slug,
|
|
1196
|
-
name:
|
|
1197
|
-
contact_count:
|
|
1401
|
+
name: z9.string().min(1).max(120),
|
|
1402
|
+
contact_count: z9.number().int().min(0),
|
|
1198
1403
|
created_at: Timestamp
|
|
1199
1404
|
});
|
|
1200
|
-
var TagCreate =
|
|
1201
|
-
var TagAssignment =
|
|
1202
|
-
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)
|
|
1203
1408
|
});
|
|
1204
1409
|
var CONTACT_PROPERTY_TYPES = ["string", "number", "boolean", "date"];
|
|
1205
|
-
var ContactPropertyType =
|
|
1206
|
-
var ContactPropertyKey =
|
|
1207
|
-
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({
|
|
1208
1413
|
key: ContactPropertyKey,
|
|
1209
|
-
label:
|
|
1414
|
+
label: z9.string().min(1).max(120),
|
|
1210
1415
|
type: ContactPropertyType
|
|
1211
1416
|
});
|
|
1212
|
-
var ContactPropertyParams =
|
|
1417
|
+
var ContactPropertyParams = z9.object({ key: ContactPropertyKey });
|
|
1213
1418
|
var FILTER_OPERATORS = [
|
|
1214
1419
|
"eq",
|
|
1215
1420
|
"neq",
|
|
@@ -1225,8 +1430,8 @@ var FILTER_OPERATORS = [
|
|
|
1225
1430
|
"in",
|
|
1226
1431
|
"not_in"
|
|
1227
1432
|
];
|
|
1228
|
-
var FilterOperator =
|
|
1229
|
-
var FilterField =
|
|
1433
|
+
var FilterOperator = z9.enum(FILTER_OPERATORS);
|
|
1434
|
+
var FilterField = z9.string().regex(
|
|
1230
1435
|
/^(email|first_name|last_name|locale|created_at|tag|topic|engagement:(opened|clicked)|property:[A-Za-z0-9_.-]{1,64})$/,
|
|
1231
1436
|
"unknown filter field"
|
|
1232
1437
|
);
|
|
@@ -1242,18 +1447,18 @@ var FILTER_FIELD_OPERATORS = {
|
|
|
1242
1447
|
property: FILTER_OPERATORS
|
|
1243
1448
|
};
|
|
1244
1449
|
var MAX_FILTER_DEPTH = 5;
|
|
1245
|
-
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), {
|
|
1246
1451
|
message: "exists and not_exists take no value; every other operator needs one",
|
|
1247
1452
|
path: ["value"]
|
|
1248
1453
|
}).refine((c) => c.op !== "in" && c.op !== "not_in" || Array.isArray(c.value), {
|
|
1249
1454
|
message: "in and not_in take an array",
|
|
1250
1455
|
path: ["value"]
|
|
1251
1456
|
});
|
|
1252
|
-
var SegmentFilter =
|
|
1253
|
-
() =>
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
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(),
|
|
1257
1462
|
FilterCondition
|
|
1258
1463
|
])
|
|
1259
1464
|
);
|
|
@@ -1341,42 +1546,42 @@ function untypedValueProblem(c, every) {
|
|
|
1341
1546
|
if (orderOp) return every(isFilterNumber, "numbers (define the property to compare dates)");
|
|
1342
1547
|
return every((v) => isScalar(v) || v === null, "strings, numbers, booleans or null");
|
|
1343
1548
|
}
|
|
1344
|
-
var Segment =
|
|
1549
|
+
var Segment = z9.object({
|
|
1345
1550
|
id: Id,
|
|
1346
|
-
name:
|
|
1551
|
+
name: z9.string().min(1).max(120),
|
|
1347
1552
|
filter: SegmentFilter,
|
|
1348
1553
|
/** Computed on read. */
|
|
1349
|
-
contact_count:
|
|
1554
|
+
contact_count: z9.number().int().min(0),
|
|
1350
1555
|
created_at: Timestamp,
|
|
1351
1556
|
updated_at: Timestamp
|
|
1352
1557
|
});
|
|
1353
|
-
var SegmentCreate =
|
|
1354
|
-
var SegmentPreviewRequest =
|
|
1355
|
-
var SegmentPreview =
|
|
1356
|
-
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),
|
|
1357
1562
|
/** Up to five matching contacts, for a human to sanity-check the filter. */
|
|
1358
|
-
sample:
|
|
1563
|
+
sample: z9.array(z9.object({ id: Id, email: Email })).max(5)
|
|
1359
1564
|
});
|
|
1360
|
-
var MailingAudienceFromSegment =
|
|
1565
|
+
var MailingAudienceFromSegment = z9.object({ segment_id: Id });
|
|
1361
1566
|
var SIGNUP_FORM_FIELDS = ["first_name", "last_name"];
|
|
1362
1567
|
var HOSTED_PAGE_LOCALES = ["en", "de", "it", "fr", "es"];
|
|
1363
|
-
var SignupFormTranslation =
|
|
1364
|
-
title:
|
|
1365
|
-
consent_text:
|
|
1568
|
+
var SignupFormTranslation = z9.object({
|
|
1569
|
+
title: z9.string().min(1).max(120),
|
|
1570
|
+
consent_text: z9.string().min(1).max(2e3)
|
|
1366
1571
|
});
|
|
1367
|
-
var SignupForm =
|
|
1572
|
+
var SignupForm = z9.object({
|
|
1368
1573
|
id: Id,
|
|
1369
|
-
name:
|
|
1574
|
+
name: z9.string().min(1).max(120),
|
|
1370
1575
|
/** The heading of the hosted page. */
|
|
1371
|
-
title:
|
|
1576
|
+
title: z9.string().min(1).max(120),
|
|
1372
1577
|
/** What the person agrees to, shown above the button and recorded with the confirmation. */
|
|
1373
|
-
consent_text:
|
|
1374
|
-
translations:
|
|
1375
|
-
topics:
|
|
1376
|
-
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),
|
|
1377
1582
|
/** Fields shown besides email. */
|
|
1378
|
-
fields:
|
|
1379
|
-
double_opt_in:
|
|
1583
|
+
fields: z9.array(z9.enum(SIGNUP_FORM_FIELDS)),
|
|
1584
|
+
double_opt_in: z9.literal(true),
|
|
1380
1585
|
/** The provider the confirmation mail is sent through. */
|
|
1381
1586
|
provider_id: Id,
|
|
1382
1587
|
/**
|
|
@@ -1385,11 +1590,11 @@ var SignupForm = z8.object({
|
|
|
1385
1590
|
*/
|
|
1386
1591
|
confirmation_template_id: Id.nullable(),
|
|
1387
1592
|
/** Where the person lands after confirming. Null shows the hosted confirmation page. */
|
|
1388
|
-
redirect_url:
|
|
1593
|
+
redirect_url: z9.string().url().nullable(),
|
|
1389
1594
|
/** Origins allowed to embed the form and call the submission route from a browser. */
|
|
1390
|
-
allowed_origins:
|
|
1595
|
+
allowed_origins: z9.array(z9.string().url()).max(20),
|
|
1391
1596
|
/** Bumped on every change; recorded with each confirmation. */
|
|
1392
|
-
version:
|
|
1597
|
+
version: z9.number().int().min(1),
|
|
1393
1598
|
created_at: Timestamp,
|
|
1394
1599
|
updated_at: Timestamp
|
|
1395
1600
|
});
|
|
@@ -1404,56 +1609,56 @@ var SignupFormCreate = SignupForm.omit({
|
|
|
1404
1609
|
tags: SignupForm.shape.tags.optional(),
|
|
1405
1610
|
fields: SignupForm.shape.fields.optional(),
|
|
1406
1611
|
confirmation_template_id: Id.nullable().optional(),
|
|
1407
|
-
redirect_url:
|
|
1612
|
+
redirect_url: z9.string().url().nullable().optional(),
|
|
1408
1613
|
allowed_origins: SignupForm.shape.allowed_origins.optional()
|
|
1409
1614
|
});
|
|
1410
|
-
var SignupFormEmbed =
|
|
1411
|
-
hosted_url:
|
|
1615
|
+
var SignupFormEmbed = z9.object({
|
|
1616
|
+
hosted_url: z9.string().url(),
|
|
1412
1617
|
/** A plain HTML form posting to the hosted page; works without JavaScript. */
|
|
1413
|
-
html:
|
|
1618
|
+
html: z9.string(),
|
|
1414
1619
|
/** A small optional script that adds the time check and submits in place. */
|
|
1415
|
-
script_url:
|
|
1620
|
+
script_url: z9.string().url(),
|
|
1416
1621
|
/** Where a browser fetches a fresh `form_token` from (GET, public, CORS for `allowed_origins`). */
|
|
1417
|
-
token_url:
|
|
1622
|
+
token_url: z9.string().url()
|
|
1418
1623
|
});
|
|
1419
|
-
var SignupSubmission =
|
|
1624
|
+
var SignupSubmission = z9.object({
|
|
1420
1625
|
email: Email,
|
|
1421
|
-
first_name:
|
|
1422
|
-
last_name:
|
|
1423
|
-
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(),
|
|
1424
1629
|
/** Honeypot: must be empty. */
|
|
1425
|
-
website:
|
|
1630
|
+
website: z9.string().max(0).optional(),
|
|
1426
1631
|
/**
|
|
1427
1632
|
* The signed render time from the form's token URL: a submission sooner than a
|
|
1428
1633
|
* human could fill the form, or long after, is refused.
|
|
1429
1634
|
*/
|
|
1430
|
-
form_token:
|
|
1635
|
+
form_token: z9.string().min(1).max(512)
|
|
1431
1636
|
});
|
|
1432
1637
|
var IMPORT_STATUSES = ["uploaded", "validating", "validated", "committing", "completed", "failed", "cancelled"];
|
|
1433
|
-
var ImportStatus =
|
|
1638
|
+
var ImportStatus = z9.enum(IMPORT_STATUSES);
|
|
1434
1639
|
var IMPORT_TERMINAL_STATUSES = ["completed", "failed", "cancelled"];
|
|
1435
|
-
var ImportColumnMapping =
|
|
1436
|
-
|
|
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)$/)
|
|
1437
1642
|
);
|
|
1438
1643
|
var IMPORT_FILE_FIELD = "file";
|
|
1439
1644
|
var MAX_IMPORT_BYTES = 50 * 1024 * 1024;
|
|
1440
1645
|
var MAX_IMPORT_ROWS = 2e5;
|
|
1441
|
-
var ImportMappingRequest =
|
|
1646
|
+
var ImportMappingRequest = z9.object({
|
|
1442
1647
|
/** CSV header -> contact field. Exactly one column maps to `email`. */
|
|
1443
1648
|
mapping: ImportColumnMapping.refine(
|
|
1444
1649
|
(m) => Object.values(m).filter((v) => v === "email").length === 1,
|
|
1445
1650
|
"exactly one column maps to email"
|
|
1446
1651
|
),
|
|
1447
|
-
topics:
|
|
1448
|
-
tags:
|
|
1652
|
+
topics: z9.array(Slug).max(100),
|
|
1653
|
+
tags: z9.array(Slug).max(100),
|
|
1449
1654
|
/** Existing contacts get the mapped fields overwritten; false only adds topics, tags and missing fields. */
|
|
1450
|
-
update_existing:
|
|
1655
|
+
update_existing: z9.boolean().optional(),
|
|
1451
1656
|
/** The importer states the people consented; the service records who said so and when. */
|
|
1452
|
-
consent_confirmed:
|
|
1657
|
+
consent_confirmed: z9.literal(true)
|
|
1453
1658
|
});
|
|
1454
|
-
var ImportCommitRequest =
|
|
1659
|
+
var ImportCommitRequest = z9.object({ mapping_version: z9.number().int().min(1) });
|
|
1455
1660
|
var IMPORT_ROW_OUTCOMES = ["created", "updated", "unchanged", "suppressed", "skipped"];
|
|
1456
|
-
var ImportRowOutcome =
|
|
1661
|
+
var ImportRowOutcome = z9.enum(IMPORT_ROW_OUTCOMES);
|
|
1457
1662
|
var IMPORT_SKIP_REASONS = [
|
|
1458
1663
|
"missing_email",
|
|
1459
1664
|
"invalid_email",
|
|
@@ -1462,90 +1667,90 @@ var IMPORT_SKIP_REASONS = [
|
|
|
1462
1667
|
"external_id_conflict",
|
|
1463
1668
|
"wrong_column_count"
|
|
1464
1669
|
];
|
|
1465
|
-
var ImportSkipReason =
|
|
1466
|
-
var ImportReport =
|
|
1467
|
-
created:
|
|
1468
|
-
updated:
|
|
1469
|
-
unchanged:
|
|
1470
|
-
suppressed:
|
|
1471
|
-
skipped:
|
|
1472
|
-
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)),
|
|
1473
1678
|
/** Rows whose contact was subscribed without some topic, because the address is blocked for it. */
|
|
1474
|
-
topics_withheld:
|
|
1679
|
+
topics_withheld: z9.number().int().min(0)
|
|
1475
1680
|
});
|
|
1476
|
-
var ImportJob =
|
|
1681
|
+
var ImportJob = z9.object({
|
|
1477
1682
|
id: Id,
|
|
1478
1683
|
status: ImportStatus,
|
|
1479
|
-
file_name:
|
|
1480
|
-
file_bytes:
|
|
1481
|
-
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),
|
|
1482
1687
|
/** The header row, as in the file. */
|
|
1483
|
-
columns:
|
|
1688
|
+
columns: z9.array(z9.string()),
|
|
1484
1689
|
/** The first rows, for the mapping screen. */
|
|
1485
|
-
sample:
|
|
1690
|
+
sample: z9.array(z9.array(z9.string())).max(5),
|
|
1486
1691
|
/** A mapping guessed from the headers, for the mapping screen to start from. */
|
|
1487
1692
|
suggested_mapping: ImportColumnMapping,
|
|
1488
1693
|
mapping: ImportColumnMapping.nullable(),
|
|
1489
|
-
mapping_version:
|
|
1490
|
-
topics:
|
|
1491
|
-
tags:
|
|
1492
|
-
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(),
|
|
1493
1698
|
/** The dry run of the current mapping; null before one finished. */
|
|
1494
1699
|
dry_run: ImportReport.nullable(),
|
|
1495
1700
|
/** What the commit has written so far; null before it started. */
|
|
1496
1701
|
result: ImportReport.nullable(),
|
|
1497
1702
|
/** Rows the current phase (dry run or commit) has processed. */
|
|
1498
|
-
processed_rows:
|
|
1703
|
+
processed_rows: z9.number().int().min(0),
|
|
1499
1704
|
/** Why a `failed` import failed. */
|
|
1500
|
-
error:
|
|
1705
|
+
error: z9.string().nullable(),
|
|
1501
1706
|
created_at: Timestamp,
|
|
1502
1707
|
updated_at: Timestamp,
|
|
1503
1708
|
finished_at: Timestamp.nullable()
|
|
1504
1709
|
});
|
|
1505
|
-
var ImportRow =
|
|
1710
|
+
var ImportRow = z9.object({
|
|
1506
1711
|
/** The line in the file, 1 being the first line after the header. */
|
|
1507
|
-
row:
|
|
1508
|
-
email:
|
|
1712
|
+
row: z9.number().int().min(1),
|
|
1713
|
+
email: z9.string().nullable(),
|
|
1509
1714
|
outcome: ImportRowOutcome,
|
|
1510
1715
|
reason: ImportSkipReason.nullable(),
|
|
1511
1716
|
/** A human explanation for a skipped row (which value, which column). */
|
|
1512
|
-
message:
|
|
1717
|
+
message: z9.string().nullable(),
|
|
1513
1718
|
contact_id: Id.nullable()
|
|
1514
1719
|
});
|
|
1515
1720
|
var ImportRowListQuery = PageQuery.extend({ outcome: ImportRowOutcome.optional() });
|
|
1516
|
-
var MailingScheduleRequest =
|
|
1517
|
-
var TrackingSettings =
|
|
1518
|
-
var VariantAnalytics =
|
|
1519
|
-
key:
|
|
1520
|
-
sent:
|
|
1521
|
-
unique_opens:
|
|
1522
|
-
unique_clicks:
|
|
1523
|
-
});
|
|
1524
|
-
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({
|
|
1525
1730
|
mailing_id: Id,
|
|
1526
1731
|
counts: MailingCounts,
|
|
1527
1732
|
/** What was tracked for this mailing, fixed when it started; null before it started. */
|
|
1528
1733
|
tracking: TrackingSettings.nullable(),
|
|
1529
1734
|
/** Unique recipients with a human open. Null when opens were not tracked. */
|
|
1530
|
-
unique_opens:
|
|
1735
|
+
unique_opens: z9.number().int().min(0).nullable(),
|
|
1531
1736
|
/**
|
|
1532
1737
|
* Recipients whose only opens came from Apple Mail Privacy Protection, which
|
|
1533
1738
|
* loads every image on delivery: counted apart, never as human opens.
|
|
1534
1739
|
*/
|
|
1535
|
-
apple_mpp_opens:
|
|
1740
|
+
apple_mpp_opens: z9.number().int().min(0).nullable(),
|
|
1536
1741
|
/** Opens and clicks from security scanners and prefetchers, filtered out of the unique counts. */
|
|
1537
|
-
machine_events:
|
|
1538
|
-
unique_clicks:
|
|
1539
|
-
unsubscribes:
|
|
1540
|
-
bounces:
|
|
1541
|
-
complaints:
|
|
1542
|
-
links:
|
|
1543
|
-
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()
|
|
1544
1749
|
});
|
|
1545
1750
|
|
|
1546
1751
|
// src/automations.ts
|
|
1547
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})$/;
|
|
1548
|
-
var AutomationFilterField =
|
|
1753
|
+
var AutomationFilterField = z10.string().regex(AUTOMATION_FILTER_FIELD_PATTERN, "unknown filter field");
|
|
1549
1754
|
var AUTOMATION_FILTER_FIELD_OPERATORS = {
|
|
1550
1755
|
...FILTER_FIELD_OPERATORS,
|
|
1551
1756
|
segment: ["eq", "neq", "in", "not_in"],
|
|
@@ -1555,18 +1760,18 @@ var AUTOMATION_FILTER_FIELD_OPERATORS = {
|
|
|
1555
1760
|
step: ["eq", "neq"]
|
|
1556
1761
|
};
|
|
1557
1762
|
var automationFilterFamily = filterFieldFamily;
|
|
1558
|
-
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), {
|
|
1559
1764
|
message: "exists and not_exists take no value; every other operator needs one",
|
|
1560
1765
|
path: ["value"]
|
|
1561
1766
|
}).refine((c) => c.op !== "in" && c.op !== "not_in" || Array.isArray(c.value), {
|
|
1562
1767
|
message: "in and not_in take an array",
|
|
1563
1768
|
path: ["value"]
|
|
1564
1769
|
});
|
|
1565
|
-
var AutomationFilter =
|
|
1566
|
-
() =>
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
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(),
|
|
1570
1775
|
AutomationFilterCondition
|
|
1571
1776
|
])
|
|
1572
1777
|
);
|
|
@@ -1579,20 +1784,20 @@ function filterConditions(filter) {
|
|
|
1579
1784
|
if ("not" in filter) return filterConditions(filter.not);
|
|
1580
1785
|
return [filter];
|
|
1581
1786
|
}
|
|
1582
|
-
var StepId =
|
|
1583
|
-
var TimeOfDay =
|
|
1584
|
-
var Weekday =
|
|
1585
|
-
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");
|
|
1586
1791
|
var MAX_DELAY_SECONDS = 365 * 24 * 60 * 60;
|
|
1587
|
-
var DateOnly =
|
|
1792
|
+
var DateOnly = z10.string().regex(/^\d{4}-\d{2}-\d{2}$/, "a calendar date such as 2026-09-20").refine((v) => {
|
|
1588
1793
|
const [y, m, d] = v.split("-").map(Number);
|
|
1589
1794
|
const t = new Date(Date.UTC(y, m - 1, d));
|
|
1590
1795
|
return t.getUTCFullYear() === y && t.getUTCMonth() + 1 === m && t.getUTCDate() === d;
|
|
1591
1796
|
}, "a real calendar date");
|
|
1592
|
-
var OffsetDays =
|
|
1797
|
+
var OffsetDays = z10.number().int().min(-365).max(365);
|
|
1593
1798
|
var DATE_PASSED_ANSWERS = ["skip", "stop", "continue"];
|
|
1594
|
-
var DatePassedAnswer =
|
|
1595
|
-
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");
|
|
1596
1801
|
var AUTOMATION_TRIGGER_KINDS = [
|
|
1597
1802
|
"topic_subscribed",
|
|
1598
1803
|
"form_confirmed",
|
|
@@ -1605,17 +1810,17 @@ var AUTOMATION_TRIGGER_KINDS = [
|
|
|
1605
1810
|
"segment_entered",
|
|
1606
1811
|
"link_clicked"
|
|
1607
1812
|
];
|
|
1608
|
-
var AutomationTriggerKind =
|
|
1813
|
+
var AutomationTriggerKind = z10.enum(AUTOMATION_TRIGGER_KINDS);
|
|
1609
1814
|
var exclude = { exclude: BoundedAutomationFilter.nullish() };
|
|
1610
|
-
var AutomationTrigger =
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
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(),
|
|
1614
1819
|
/** `to` given: only a change that lands on this value fires. Absent: any change of the key. */
|
|
1615
|
-
|
|
1820
|
+
z10.object({ kind: z10.literal("property_changed"), key: ContactPropertyKey, to: JsonValue.optional(), ...exclude }).strict(),
|
|
1616
1821
|
/** `where` filters the event's own data, so only `event:<key>` leaves belong in it. */
|
|
1617
|
-
|
|
1618
|
-
|
|
1822
|
+
z10.object({ kind: z10.literal("event"), name: EventName, where: BoundedAutomationFilter.nullish(), ...exclude }).strict(),
|
|
1823
|
+
z10.object({ kind: z10.literal("manual"), ...exclude }).strict(),
|
|
1619
1824
|
/**
|
|
1620
1825
|
* Every year on a date the contact carries: a birthday, a joining date, a
|
|
1621
1826
|
* renewal. `offset_days` moves it ("three days before"), `at` is the wall
|
|
@@ -1625,8 +1830,8 @@ var AutomationTrigger = z9.discriminatedUnion("kind", [
|
|
|
1625
1830
|
* A 29 February date fires on 1 March in a year that has no 29 February, so
|
|
1626
1831
|
* nobody is skipped every fourth year.
|
|
1627
1832
|
*/
|
|
1628
|
-
|
|
1629
|
-
kind:
|
|
1833
|
+
z10.object({
|
|
1834
|
+
kind: z10.literal("date_anniversary"),
|
|
1630
1835
|
key: ContactPropertyKey,
|
|
1631
1836
|
offset_days: OffsetDays,
|
|
1632
1837
|
at: TimeOfDay,
|
|
@@ -1638,8 +1843,8 @@ var AutomationTrigger = z9.discriminatedUnion("kind", [
|
|
|
1638
1843
|
* not ask about a segment or about an event's data: there is no event, and a
|
|
1639
1844
|
* segment is its own trigger kind.
|
|
1640
1845
|
*/
|
|
1641
|
-
|
|
1642
|
-
kind:
|
|
1846
|
+
z10.object({
|
|
1847
|
+
kind: z10.literal("exact_date"),
|
|
1643
1848
|
on: DateOnly,
|
|
1644
1849
|
at: TimeOfDay,
|
|
1645
1850
|
where: BoundedAutomationFilter.nullish(),
|
|
@@ -1658,7 +1863,7 @@ var AutomationTrigger = z9.discriminatedUnion("kind", [
|
|
|
1658
1863
|
* already exists. Entering those people as well is the publish dialog's
|
|
1659
1864
|
* backfill question, answered by a person rather than by the sweep.
|
|
1660
1865
|
*/
|
|
1661
|
-
|
|
1866
|
+
z10.object({ kind: z10.literal("segment_entered"), segment_id: Id, ...exclude }).strict(),
|
|
1662
1867
|
/**
|
|
1663
1868
|
* S4. When somebody clicks a link in a mailing that has already gone out.
|
|
1664
1869
|
*
|
|
@@ -1668,7 +1873,7 @@ var AutomationTrigger = z9.discriminatedUnion("kind", [
|
|
|
1668
1873
|
* tracking off there is no click to hear about, so the validator refuses it
|
|
1669
1874
|
* rather than leaving an automation that can never fire.
|
|
1670
1875
|
*/
|
|
1671
|
-
|
|
1876
|
+
z10.object({ kind: z10.literal("link_clicked"), mailing_id: Id, url: z10.string().min(1).max(2048).optional(), ...exclude }).strict()
|
|
1672
1877
|
]);
|
|
1673
1878
|
var AUTOMATION_DATE_TRIGGER_KINDS = ["date_anniversary", "exact_date"];
|
|
1674
1879
|
var MAX_TRIGGERS = 10;
|
|
@@ -1687,45 +1892,45 @@ var AUTOMATION_STEP_KINDS = [
|
|
|
1687
1892
|
"webhook",
|
|
1688
1893
|
"notify"
|
|
1689
1894
|
];
|
|
1690
|
-
var AutomationStepKind =
|
|
1895
|
+
var AutomationStepKind = z10.enum(AUTOMATION_STEP_KINDS);
|
|
1691
1896
|
var next = { next: StepId.nullable() };
|
|
1692
|
-
var Unchosen =
|
|
1693
|
-
var DraftSlug =
|
|
1694
|
-
var DraftPropertyKey =
|
|
1695
|
-
var label2 = { label:
|
|
1696
|
-
var EmailStepLanguage =
|
|
1697
|
-
subject:
|
|
1698
|
-
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(),
|
|
1699
1904
|
document: TemplateDocument,
|
|
1700
1905
|
/** This language's insertions, its texts over the template's structure (every text present). */
|
|
1701
1906
|
insertions: Insertions.nullish()
|
|
1702
1907
|
}).strict();
|
|
1703
|
-
var EmailStep =
|
|
1704
|
-
kind:
|
|
1908
|
+
var EmailStep = z10.object({
|
|
1909
|
+
kind: z10.literal("email"),
|
|
1705
1910
|
...label2,
|
|
1706
1911
|
...next,
|
|
1707
1912
|
template_id: Id.nullish(),
|
|
1708
1913
|
document: TemplateDocument.nullish(),
|
|
1709
1914
|
/** Empty while the step is being written, or when its template has a subject; else `not_chosen` refuses to publish it. */
|
|
1710
|
-
subject:
|
|
1711
|
-
preheader:
|
|
1915
|
+
subject: z10.string().max(998),
|
|
1916
|
+
preheader: z10.string().max(500).nullish(),
|
|
1712
1917
|
locale: LocaleTag.nullish(),
|
|
1713
|
-
languages:
|
|
1918
|
+
languages: z10.record(LocaleTag, EmailStepLanguage).nullish(),
|
|
1714
1919
|
/** The main language's insertions, snapshot from the template when the automation is published. */
|
|
1715
1920
|
insertions: Insertions.nullish()
|
|
1716
1921
|
}).strict();
|
|
1717
|
-
var DelayStep =
|
|
1718
|
-
kind:
|
|
1922
|
+
var DelayStep = z10.object({
|
|
1923
|
+
kind: z10.literal("delay"),
|
|
1719
1924
|
...label2,
|
|
1720
1925
|
...next,
|
|
1721
|
-
wait:
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
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(),
|
|
1725
1930
|
/** A fixed day has no next occurrence, so `continue` is not one of its answers. */
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
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"),
|
|
1729
1934
|
key: ContactPropertyKey,
|
|
1730
1935
|
offset_days: OffsetDays,
|
|
1731
1936
|
at: TimeOfDay,
|
|
@@ -1733,8 +1938,8 @@ var DelayStep = z9.object({
|
|
|
1733
1938
|
}).strict()
|
|
1734
1939
|
])
|
|
1735
1940
|
}).strict();
|
|
1736
|
-
var ConditionStep =
|
|
1737
|
-
kind:
|
|
1941
|
+
var ConditionStep = z10.object({
|
|
1942
|
+
kind: z10.literal("condition"),
|
|
1738
1943
|
...label2,
|
|
1739
1944
|
filter: BoundedAutomationFilter,
|
|
1740
1945
|
yes: StepId.nullable(),
|
|
@@ -1745,49 +1950,49 @@ var ConditionStep = z9.object({
|
|
|
1745
1950
|
* neither half means anything without the other: a window with no answer
|
|
1746
1951
|
* for the end of it would be a silent decision.
|
|
1747
1952
|
*/
|
|
1748
|
-
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()
|
|
1749
1954
|
}).strict();
|
|
1750
|
-
var SplitBranchKey =
|
|
1955
|
+
var SplitBranchKey = z10.string().regex(/^[A-Za-z0-9_-]{1,32}$/, "letters, digits, underscore and hyphen, 1 to 32");
|
|
1751
1956
|
var MIN_SPLIT_BRANCHES = 2;
|
|
1752
1957
|
var MAX_SPLIT_BRANCHES = 4;
|
|
1753
|
-
var SplitStep =
|
|
1754
|
-
kind:
|
|
1958
|
+
var SplitStep = z10.object({
|
|
1959
|
+
kind: z10.literal("split"),
|
|
1755
1960
|
...label2,
|
|
1756
|
-
branches:
|
|
1757
|
-
|
|
1961
|
+
branches: z10.array(
|
|
1962
|
+
z10.object({
|
|
1758
1963
|
key: SplitBranchKey,
|
|
1759
1964
|
/** A whole percentage. Zero is allowed: a branch kept in the flow but switched off. */
|
|
1760
|
-
weight:
|
|
1965
|
+
weight: z10.number().int().min(0).max(100),
|
|
1761
1966
|
next: StepId.nullable()
|
|
1762
1967
|
}).strict()
|
|
1763
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")
|
|
1764
1969
|
}).strict();
|
|
1765
|
-
var GotoStep =
|
|
1970
|
+
var GotoStep = z10.object({ kind: z10.literal("goto"), ...label2, target: StepId.nullable() }).strict();
|
|
1766
1971
|
var MAX_STEP_VISITS = 20;
|
|
1767
|
-
var WebhookStep =
|
|
1768
|
-
var NotifyRecipients =
|
|
1769
|
-
var NotifyStep =
|
|
1770
|
-
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"),
|
|
1771
1976
|
...label2,
|
|
1772
1977
|
...next,
|
|
1773
1978
|
to: NotifyRecipients,
|
|
1774
1979
|
/** Empty while the step is being written; `not_chosen` refuses to publish it. */
|
|
1775
|
-
subject:
|
|
1776
|
-
body:
|
|
1980
|
+
subject: z10.string().max(998),
|
|
1981
|
+
body: z10.string().max(1e4)
|
|
1777
1982
|
}).strict();
|
|
1778
|
-
var AutomationStep =
|
|
1983
|
+
var AutomationStep = z10.discriminatedUnion("kind", [
|
|
1779
1984
|
EmailStep,
|
|
1780
1985
|
DelayStep,
|
|
1781
1986
|
ConditionStep,
|
|
1782
|
-
|
|
1783
|
-
|
|
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(),
|
|
1784
1989
|
/** `value` null clears the property, as a contact upsert does. */
|
|
1785
|
-
|
|
1990
|
+
z10.object({ kind: z10.literal("set_property"), ...label2, ...next, key: DraftPropertyKey, value: JsonValue }).strict(),
|
|
1786
1991
|
/** Subscribing never lifts a suppression: the service checks that, not the flow. */
|
|
1787
|
-
|
|
1992
|
+
z10.object({ kind: z10.literal("subscribe_topic"), ...label2, ...next, topic: DraftSlug }).strict(),
|
|
1788
1993
|
/** `topic` unsubscribes from the automation's own topic, `all` from every topic. */
|
|
1789
|
-
|
|
1790
|
-
|
|
1994
|
+
z10.object({ kind: z10.literal("unsubscribe"), ...label2, ...next, scope: z10.enum(["topic", "all"]) }).strict(),
|
|
1995
|
+
z10.object({ kind: z10.literal("exit"), ...label2 }).strict(),
|
|
1791
1996
|
SplitStep,
|
|
1792
1997
|
GotoStep,
|
|
1793
1998
|
WebhookStep,
|
|
@@ -1811,25 +2016,25 @@ function stepTargets(step) {
|
|
|
1811
2016
|
}
|
|
1812
2017
|
}
|
|
1813
2018
|
var MAX_STEPS = 200;
|
|
1814
|
-
var AutomationFlow =
|
|
1815
|
-
triggers:
|
|
2019
|
+
var AutomationFlow = z10.object({
|
|
2020
|
+
triggers: z10.array(AutomationTrigger).max(MAX_TRIGGERS),
|
|
1816
2021
|
/** The first step. `null` is an empty flow. */
|
|
1817
2022
|
entry: StepId.nullable(),
|
|
1818
|
-
steps:
|
|
2023
|
+
steps: z10.record(StepId, AutomationStep)
|
|
1819
2024
|
}).strict();
|
|
1820
2025
|
var emptyFlow = () => ({ triggers: [], entry: null, steps: {} });
|
|
1821
|
-
var QuietHours =
|
|
2026
|
+
var QuietHours = z10.object({
|
|
1822
2027
|
start: TimeOfDay,
|
|
1823
2028
|
end: TimeOfDay,
|
|
1824
|
-
weekdays:
|
|
2029
|
+
weekdays: z10.array(Weekday).min(1).max(7).nullish()
|
|
1825
2030
|
}).strict();
|
|
1826
2031
|
var RE_ENTRY_MODES = ["off", "after_exit"];
|
|
1827
|
-
var ReEntryMode =
|
|
2032
|
+
var ReEntryMode = z10.enum(RE_ENTRY_MODES);
|
|
1828
2033
|
var MAX_RE_ENTRY_COOLDOWN_SECONDS = 365 * 24 * 60 * 60;
|
|
1829
|
-
var AutomationSettings =
|
|
2034
|
+
var AutomationSettings = z10.object({
|
|
1830
2035
|
re_entry: ReEntryMode,
|
|
1831
2036
|
/** Seconds a person must wait after leaving before entering again. Only with `after_exit`. */
|
|
1832
|
-
re_entry_cooldown_s:
|
|
2037
|
+
re_entry_cooldown_s: z10.number().int().min(0).max(MAX_RE_ENTRY_COOLDOWN_SECONDS).nullish(),
|
|
1833
2038
|
quiet_hours: QuietHours.nullish(),
|
|
1834
2039
|
/** Checked before every step; when it matches, the run ends. */
|
|
1835
2040
|
exit_filter: BoundedAutomationFilter.nullish()
|
|
@@ -1887,18 +2092,18 @@ var AUTOMATION_ISSUE_CODES = [
|
|
|
1887
2092
|
/** A one-off date that is already over everywhere on earth, so it would fire for nobody. */
|
|
1888
2093
|
"date_in_the_past"
|
|
1889
2094
|
];
|
|
1890
|
-
var AutomationIssueCode =
|
|
1891
|
-
var AutomationIssue =
|
|
2095
|
+
var AutomationIssueCode = z10.enum(AUTOMATION_ISSUE_CODES);
|
|
2096
|
+
var AutomationIssue = z10.object({
|
|
1892
2097
|
code: AutomationIssueCode,
|
|
1893
2098
|
/** The step the issue is about, when it is about one. */
|
|
1894
2099
|
step_id: StepId.nullable(),
|
|
1895
2100
|
/** Where inside the document, for the canvas to point at a field. */
|
|
1896
|
-
path:
|
|
1897
|
-
message:
|
|
2101
|
+
path: z10.array(z10.union([z10.string(), z10.number()])),
|
|
2102
|
+
message: z10.string()
|
|
1898
2103
|
});
|
|
1899
|
-
var AutomationValidation =
|
|
1900
|
-
ok:
|
|
1901
|
-
issues:
|
|
2104
|
+
var AutomationValidation = z10.object({
|
|
2105
|
+
ok: z10.boolean(),
|
|
2106
|
+
issues: z10.array(AutomationIssue)
|
|
1902
2107
|
});
|
|
1903
2108
|
function validateAutomation(flow, settings, ctx) {
|
|
1904
2109
|
const issues = [];
|
|
@@ -2289,7 +2494,7 @@ function cycles(flow) {
|
|
|
2289
2494
|
return out.sort();
|
|
2290
2495
|
}
|
|
2291
2496
|
var AUTOMATION_STATUSES = ["draft", "active", "paused", "archived"];
|
|
2292
|
-
var AutomationStatus =
|
|
2497
|
+
var AutomationStatus = z10.enum(AUTOMATION_STATUSES);
|
|
2293
2498
|
var AUTOMATION_END_REASONS = [
|
|
2294
2499
|
"completed",
|
|
2295
2500
|
"exit_filter",
|
|
@@ -2308,9 +2513,9 @@ var AUTOMATION_END_REASONS = [
|
|
|
2308
2513
|
"ended_by_hand",
|
|
2309
2514
|
"failed"
|
|
2310
2515
|
];
|
|
2311
|
-
var AutomationEndReason =
|
|
2516
|
+
var AutomationEndReason = z10.enum(AUTOMATION_END_REASONS);
|
|
2312
2517
|
var AUTOMATION_WAIT_REASONS = ["delay", "quiet_hours", "plan_limit", "sending", "condition"];
|
|
2313
|
-
var AutomationWaitReason =
|
|
2518
|
+
var AutomationWaitReason = z10.enum(AUTOMATION_WAIT_REASONS);
|
|
2314
2519
|
var AUTOMATION_ENTRY_REFUSALS = [
|
|
2315
2520
|
"already_inside",
|
|
2316
2521
|
"re_entry_off",
|
|
@@ -2321,26 +2526,26 @@ var AUTOMATION_ENTRY_REFUSALS = [
|
|
|
2321
2526
|
"not_active",
|
|
2322
2527
|
"unknown_contact"
|
|
2323
2528
|
];
|
|
2324
|
-
var AutomationEntryRefusal =
|
|
2325
|
-
var AutomationCounts =
|
|
2326
|
-
started:
|
|
2327
|
-
in_progress:
|
|
2328
|
-
completed:
|
|
2329
|
-
exited:
|
|
2330
|
-
failed:
|
|
2331
|
-
});
|
|
2332
|
-
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({
|
|
2333
2538
|
id: Id,
|
|
2334
|
-
name:
|
|
2539
|
+
name: z10.string().min(1).max(200),
|
|
2335
2540
|
status: AutomationStatus,
|
|
2336
2541
|
/** Every automation sends under one topic; there are no transactional automations. */
|
|
2337
2542
|
topic: Slug,
|
|
2338
2543
|
provider_id: Id.nullable(),
|
|
2339
2544
|
settings: AutomationSettings,
|
|
2340
2545
|
/** The flow people are running through, null while the automation has never been published. */
|
|
2341
|
-
published_version:
|
|
2546
|
+
published_version: z10.number().int().min(1).nullable(),
|
|
2342
2547
|
/** The flow being edited. Increments on every save, like a template's version. */
|
|
2343
|
-
draft_version:
|
|
2548
|
+
draft_version: z10.number().int().min(1),
|
|
2344
2549
|
counts: AutomationCounts,
|
|
2345
2550
|
created_at: Timestamp,
|
|
2346
2551
|
updated_at: Timestamp,
|
|
@@ -2353,16 +2558,16 @@ var AutomationWithFlow = Automation.extend({
|
|
|
2353
2558
|
published_flow: AutomationFlow.nullable()
|
|
2354
2559
|
});
|
|
2355
2560
|
var AutomationSummary = Automation;
|
|
2356
|
-
var AutomationCreate =
|
|
2357
|
-
name:
|
|
2561
|
+
var AutomationCreate = z10.object({
|
|
2562
|
+
name: z10.string().min(1).max(200),
|
|
2358
2563
|
topic: Slug,
|
|
2359
2564
|
provider_id: Id.optional(),
|
|
2360
2565
|
flow: AutomationFlow.optional(),
|
|
2361
2566
|
settings: AutomationSettings.partial().optional()
|
|
2362
2567
|
});
|
|
2363
|
-
var AutomationUpdate =
|
|
2364
|
-
base_version:
|
|
2365
|
-
name:
|
|
2568
|
+
var AutomationUpdate = z10.object({
|
|
2569
|
+
base_version: z10.number().int().min(1),
|
|
2570
|
+
name: z10.string().min(1).max(200).optional(),
|
|
2366
2571
|
topic: Slug.optional(),
|
|
2367
2572
|
provider_id: Id.optional(),
|
|
2368
2573
|
flow: AutomationFlow.optional(),
|
|
@@ -2375,42 +2580,42 @@ var AutomationListQuery = PageQuery.extend({
|
|
|
2375
2580
|
status: AutomationStatus.optional(),
|
|
2376
2581
|
topic: Slug.optional()
|
|
2377
2582
|
});
|
|
2378
|
-
var AutomationPublishRequest =
|
|
2583
|
+
var AutomationPublishRequest = z10.object({
|
|
2379
2584
|
/**
|
|
2380
2585
|
* Answer what this publish would do and change nothing, whatever the flow
|
|
2381
2586
|
* looks like. Without it a publish that has no issues and moves nobody
|
|
2382
2587
|
* simply goes ahead, which is right for an API caller and wrong for a
|
|
2383
2588
|
* dialog that is only showing somebody what they are about to do.
|
|
2384
2589
|
*/
|
|
2385
|
-
dry_run:
|
|
2386
|
-
confirm:
|
|
2590
|
+
dry_run: z10.boolean().optional(),
|
|
2591
|
+
confirm: z10.boolean().optional(),
|
|
2387
2592
|
/** Per removed step: the step its waiting runs move to, or null to end them. */
|
|
2388
|
-
moves:
|
|
2593
|
+
moves: z10.record(StepId, StepId.nullable()).optional(),
|
|
2389
2594
|
/** Also enter everyone who matches a trigger's condition right now. */
|
|
2390
|
-
backfill:
|
|
2595
|
+
backfill: z10.boolean().optional()
|
|
2391
2596
|
}).strict();
|
|
2392
|
-
var AutomationPublishMove =
|
|
2597
|
+
var AutomationPublishMove = z10.object({
|
|
2393
2598
|
step_id: StepId,
|
|
2394
2599
|
/** Why the step's people have to move: it is gone, or its delay changed. */
|
|
2395
|
-
change:
|
|
2396
|
-
waiting:
|
|
2600
|
+
change: z10.enum(["removed", "delay_changed", "kind_changed"]),
|
|
2601
|
+
waiting: z10.number().int().min(0),
|
|
2397
2602
|
/** Where they go: a step id, or null to end their runs with `step_removed`. */
|
|
2398
2603
|
destination: StepId.nullable()
|
|
2399
2604
|
});
|
|
2400
|
-
var AutomationPublishResult =
|
|
2401
|
-
published:
|
|
2605
|
+
var AutomationPublishResult = z10.object({
|
|
2606
|
+
published: z10.boolean(),
|
|
2402
2607
|
automation: Automation,
|
|
2403
2608
|
/** Empty when nothing has to move; non-empty and `published` false asks for a confirmation. */
|
|
2404
|
-
moves:
|
|
2609
|
+
moves: z10.array(AutomationPublishMove),
|
|
2405
2610
|
/** How many people the backfill entered, or would enter when it is not confirmed. */
|
|
2406
|
-
backfill:
|
|
2407
|
-
issues:
|
|
2408
|
-
});
|
|
2409
|
-
var AutomationDuplicateRequest =
|
|
2410
|
-
var AutomationEnrollRequest =
|
|
2411
|
-
contact_ids:
|
|
2412
|
-
external_ids:
|
|
2413
|
-
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(),
|
|
2414
2619
|
segment_id: Id.optional(),
|
|
2415
2620
|
/** Where to start them; the flow's entry step by default. */
|
|
2416
2621
|
step_id: StepId.optional()
|
|
@@ -2418,34 +2623,34 @@ var AutomationEnrollRequest = z9.object({
|
|
|
2418
2623
|
(v) => [v.contact_ids, v.external_ids, v.emails, v.segment_id].filter((x) => x !== void 0).length === 1,
|
|
2419
2624
|
"name the people exactly one way: contact_ids, external_ids, emails or segment_id"
|
|
2420
2625
|
);
|
|
2421
|
-
var AutomationEnrollment =
|
|
2626
|
+
var AutomationEnrollment = z10.object({
|
|
2422
2627
|
contact_id: Id.nullable(),
|
|
2423
2628
|
email: Email.nullable(),
|
|
2424
|
-
external_id:
|
|
2425
|
-
entered:
|
|
2629
|
+
external_id: z10.string().nullable(),
|
|
2630
|
+
entered: z10.boolean(),
|
|
2426
2631
|
run_id: Id.nullable(),
|
|
2427
2632
|
reason: AutomationEntryRefusal.nullable()
|
|
2428
2633
|
});
|
|
2429
|
-
var AutomationEnrollResult =
|
|
2430
|
-
entered:
|
|
2431
|
-
refused:
|
|
2432
|
-
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)
|
|
2433
2638
|
});
|
|
2434
|
-
var AutomationRunStatus =
|
|
2435
|
-
var AutomationRun =
|
|
2639
|
+
var AutomationRunStatus = z10.enum(["active", "waiting", "completed", "exited", "failed"]);
|
|
2640
|
+
var AutomationRun = z10.object({
|
|
2436
2641
|
id: Id,
|
|
2437
2642
|
automation_id: Id,
|
|
2438
2643
|
contact_id: Id,
|
|
2439
2644
|
email: Email,
|
|
2440
|
-
external_id:
|
|
2441
|
-
version:
|
|
2645
|
+
external_id: z10.string().nullable(),
|
|
2646
|
+
version: z10.number().int().min(1),
|
|
2442
2647
|
status: AutomationRunStatus,
|
|
2443
2648
|
current_step_id: StepId.nullable(),
|
|
2444
2649
|
next_action_at: Timestamp.nullable(),
|
|
2445
2650
|
wait_reason: AutomationWaitReason.nullable(),
|
|
2446
|
-
end_reason:
|
|
2651
|
+
end_reason: z10.string().nullable(),
|
|
2447
2652
|
/** What went wrong the last time this run tried to take its step, null otherwise. */
|
|
2448
|
-
last_error:
|
|
2653
|
+
last_error: z10.string().nullable(),
|
|
2449
2654
|
entered_at: Timestamp,
|
|
2450
2655
|
ended_at: Timestamp.nullable()
|
|
2451
2656
|
});
|
|
@@ -2455,13 +2660,13 @@ var AutomationRunListQuery = PageQuery.extend({
|
|
|
2455
2660
|
contact_id: Id.optional()
|
|
2456
2661
|
});
|
|
2457
2662
|
var ContactAutomationRun = AutomationRun.extend({
|
|
2458
|
-
automation:
|
|
2663
|
+
automation: z10.object({ id: Id, name: z10.string(), status: AutomationStatus })
|
|
2459
2664
|
});
|
|
2460
2665
|
var ContactAutomationListQuery = PageQuery.extend({ status: AutomationRunStatus.optional() });
|
|
2461
|
-
var AutomationStepExecution =
|
|
2666
|
+
var AutomationStepExecution = z10.object({
|
|
2462
2667
|
step_id: StepId,
|
|
2463
2668
|
step_kind: AutomationStepKind,
|
|
2464
|
-
visit:
|
|
2669
|
+
visit: z10.number().int().min(1),
|
|
2465
2670
|
started_at: Timestamp,
|
|
2466
2671
|
finished_at: Timestamp.nullable(),
|
|
2467
2672
|
/**
|
|
@@ -2473,48 +2678,48 @@ var AutomationStepExecution = z9.object({
|
|
|
2473
2678
|
*/
|
|
2474
2679
|
outcome: Properties
|
|
2475
2680
|
});
|
|
2476
|
-
var AutomationJourney =
|
|
2681
|
+
var AutomationJourney = z10.object({
|
|
2477
2682
|
run: AutomationRun,
|
|
2478
|
-
steps:
|
|
2683
|
+
steps: z10.array(AutomationStepExecution)
|
|
2479
2684
|
});
|
|
2480
|
-
var AutomationStepReport =
|
|
2685
|
+
var AutomationStepReport = z10.object({
|
|
2481
2686
|
step_id: StepId,
|
|
2482
2687
|
step_kind: AutomationStepKind,
|
|
2483
|
-
reached:
|
|
2484
|
-
waiting:
|
|
2485
|
-
passed:
|
|
2688
|
+
reached: z10.number().int().min(0),
|
|
2689
|
+
waiting: z10.number().int().min(0),
|
|
2690
|
+
passed: z10.number().int().min(0),
|
|
2486
2691
|
/** Per branch, for a condition: how many took `yes` and how many `no`. */
|
|
2487
|
-
branches:
|
|
2692
|
+
branches: z10.record(z10.number().int().min(0)),
|
|
2488
2693
|
/** The step mailing's id, for an email step of the published version. */
|
|
2489
2694
|
mailing_id: Id.nullable()
|
|
2490
2695
|
});
|
|
2491
|
-
var AutomationReport =
|
|
2696
|
+
var AutomationReport = z10.object({
|
|
2492
2697
|
counts: AutomationCounts,
|
|
2493
2698
|
/** Why the people who left did: `AutomationEndReason` to a count. */
|
|
2494
|
-
end_reasons:
|
|
2699
|
+
end_reasons: z10.record(z10.number().int().min(0)),
|
|
2495
2700
|
/**
|
|
2496
2701
|
* Why people a trigger named did not enter: `AutomationEntryRefusal` to a
|
|
2497
2702
|
* count. Without it a pause, an exclusion or a missing subscription would
|
|
2498
2703
|
* turn people away invisibly.
|
|
2499
2704
|
*/
|
|
2500
|
-
refusals:
|
|
2501
|
-
steps:
|
|
2502
|
-
});
|
|
2503
|
-
var AutomationExitRunRequest =
|
|
2504
|
-
var AutomationRunParams =
|
|
2505
|
-
var AutomationStepParams =
|
|
2506
|
-
var AutomationTestEmailRequest =
|
|
2507
|
-
var AutomationValidateRequest =
|
|
2508
|
-
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({
|
|
2509
2714
|
name: EventName,
|
|
2510
|
-
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"),
|
|
2511
2716
|
data: Properties.optional(),
|
|
2512
2717
|
occurred_at: Timestamp.optional(),
|
|
2513
|
-
id:
|
|
2718
|
+
id: z10.string().min(1).max(128).optional()
|
|
2514
2719
|
}).strict();
|
|
2515
|
-
var Event =
|
|
2720
|
+
var Event = z10.object({
|
|
2516
2721
|
id: Id,
|
|
2517
|
-
client_event_id:
|
|
2722
|
+
client_event_id: z10.string().nullable(),
|
|
2518
2723
|
name: EventName,
|
|
2519
2724
|
contact_id: Id,
|
|
2520
2725
|
data: Properties,
|
|
@@ -2525,7 +2730,7 @@ var Event = z9.object({
|
|
|
2525
2730
|
* hand. Each is then admitted or refused by the runtime on its own rules
|
|
2526
2731
|
* (consent, exclusions, re-entry), which `automations.report` counts.
|
|
2527
2732
|
*/
|
|
2528
|
-
entered:
|
|
2733
|
+
entered: z10.number().int().min(0)
|
|
2529
2734
|
});
|
|
2530
2735
|
var EventListQuery = PageQuery.extend({
|
|
2531
2736
|
name: EventName.optional(),
|
|
@@ -2533,19 +2738,15 @@ var EventListQuery = PageQuery.extend({
|
|
|
2533
2738
|
});
|
|
2534
2739
|
|
|
2535
2740
|
// src/workspace.ts
|
|
2536
|
-
var CompanyId =
|
|
2741
|
+
var CompanyId = z11.string().min(1).max(64);
|
|
2537
2742
|
var ASSET_POLICIES = ["any", "service_only"];
|
|
2538
|
-
var AssetPolicy =
|
|
2743
|
+
var AssetPolicy = z11.enum(ASSET_POLICIES);
|
|
2539
2744
|
var MAX_ALLOWED_ASSET_HOSTS = 20;
|
|
2540
|
-
var AssetHost =
|
|
2541
|
-
var
|
|
2542
|
-
var MAX_MERGE_DEFAULT_LENGTH = 2e3;
|
|
2543
|
-
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);
|
|
2544
|
-
var MergeDefaultValue = z10.union([z10.string().max(MAX_MERGE_DEFAULT_LENGTH), z10.number().finite(), z10.boolean()]);
|
|
2545
|
-
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), {
|
|
2546
2747
|
message: `a reserved merge field (${Object.keys(RESERVED_MERGE_FIELDS).join(", ")}) cannot have a workspace default`
|
|
2547
2748
|
});
|
|
2548
|
-
var WorkspaceSettings =
|
|
2749
|
+
var WorkspaceSettings = z11.object({
|
|
2549
2750
|
/**
|
|
2550
2751
|
* The workspace's main language (a {@link LocaleTag}, e.g. "de"): the main
|
|
2551
2752
|
* language of a new template, and of the hosted pages when nothing else
|
|
@@ -2558,9 +2759,9 @@ var WorkspaceSettings = z10.object({
|
|
|
2558
2759
|
* each; the hosted pages are offered in those of them they support (English,
|
|
2559
2760
|
* German, French, Italian, Spanish). The service stores canonical casing.
|
|
2560
2761
|
*/
|
|
2561
|
-
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"),
|
|
2562
2763
|
/** Open and click tracking. Off by default, opt-in per workspace. */
|
|
2563
|
-
tracking_enabled:
|
|
2764
|
+
tracking_enabled: z11.boolean(),
|
|
2564
2765
|
/** Where images, stylesheets and fonts in a mail may load from. `any` by default. */
|
|
2565
2766
|
asset_policy: AssetPolicy,
|
|
2566
2767
|
/**
|
|
@@ -2568,7 +2769,7 @@ var WorkspaceSettings = z10.object({
|
|
|
2568
2769
|
* the workspace's website, typically. Empty by default. Ignored under `any`,
|
|
2569
2770
|
* which allows everything already.
|
|
2570
2771
|
*/
|
|
2571
|
-
allowed_asset_hosts:
|
|
2772
|
+
allowed_asset_hosts: z11.array(AssetHost).max(MAX_ALLOWED_ASSET_HOSTS),
|
|
2572
2773
|
/** Merge values every mail of the workspace can use (see `MergeDefaults`). Empty by default. */
|
|
2573
2774
|
merge_defaults: MergeDefaults,
|
|
2574
2775
|
/**
|
|
@@ -2577,10 +2778,10 @@ var WorkspaceSettings = z10.object({
|
|
|
2577
2778
|
*/
|
|
2578
2779
|
default_timezone: TimezoneName.nullable()
|
|
2579
2780
|
});
|
|
2580
|
-
var Workspace =
|
|
2781
|
+
var Workspace = z11.object({
|
|
2581
2782
|
id: Id,
|
|
2582
2783
|
slug: Slug,
|
|
2583
|
-
name:
|
|
2784
|
+
name: z11.string().min(1).max(120),
|
|
2584
2785
|
settings: WorkspaceSettings,
|
|
2585
2786
|
/**
|
|
2586
2787
|
* The auth-brain company (tenant) the workspace was created for, or null for
|
|
@@ -2591,13 +2792,13 @@ var Workspace = z10.object({
|
|
|
2591
2792
|
created_at: Timestamp,
|
|
2592
2793
|
updated_at: Timestamp
|
|
2593
2794
|
});
|
|
2594
|
-
var WorkspaceCreate =
|
|
2795
|
+
var WorkspaceCreate = z11.object({
|
|
2595
2796
|
slug: Slug,
|
|
2596
|
-
name:
|
|
2797
|
+
name: z11.string().min(1).max(120),
|
|
2597
2798
|
settings: WorkspaceSettings.partial().optional(),
|
|
2598
|
-
owner:
|
|
2799
|
+
owner: z11.object({
|
|
2599
2800
|
email: Email,
|
|
2600
|
-
name:
|
|
2801
|
+
name: z11.string().max(200).nullable().optional()
|
|
2601
2802
|
}),
|
|
2602
2803
|
/**
|
|
2603
2804
|
* The signed-in person's active auth-brain company. The dashboard always
|
|
@@ -2607,49 +2808,49 @@ var WorkspaceCreate = z10.object({
|
|
|
2607
2808
|
*/
|
|
2608
2809
|
company_id: CompanyId.optional()
|
|
2609
2810
|
});
|
|
2610
|
-
var WorkspaceUpdate =
|
|
2611
|
-
name:
|
|
2811
|
+
var WorkspaceUpdate = z11.object({
|
|
2812
|
+
name: z11.string().min(1).max(120),
|
|
2612
2813
|
settings: WorkspaceSettings.partial()
|
|
2613
2814
|
}).partial().refine((v) => Object.keys(v).length > 0, "at least one field");
|
|
2614
|
-
var WorkspaceMove =
|
|
2815
|
+
var WorkspaceMove = z11.object({ company_id: CompanyId });
|
|
2615
2816
|
var MEMBER_ROLES = ["owner", "admin", "editor", "viewer"];
|
|
2616
|
-
var MemberRole =
|
|
2817
|
+
var MemberRole = z11.enum(MEMBER_ROLES);
|
|
2617
2818
|
var WorkspaceMembership = Workspace.extend({ role: MemberRole });
|
|
2618
|
-
var Member =
|
|
2819
|
+
var Member = z11.object({
|
|
2619
2820
|
id: Id,
|
|
2620
2821
|
/** The person's auth-brain subject. */
|
|
2621
|
-
subject:
|
|
2822
|
+
subject: z11.string().min(1).max(255),
|
|
2622
2823
|
email: Email,
|
|
2623
|
-
name:
|
|
2824
|
+
name: z11.string().max(200).nullable(),
|
|
2624
2825
|
role: MemberRole,
|
|
2625
2826
|
created_at: Timestamp
|
|
2626
2827
|
});
|
|
2627
|
-
var MemberCreate =
|
|
2628
|
-
subject:
|
|
2828
|
+
var MemberCreate = z11.object({
|
|
2829
|
+
subject: z11.string().min(1).max(255),
|
|
2629
2830
|
email: Email,
|
|
2630
|
-
name:
|
|
2831
|
+
name: z11.string().max(200).nullable().optional(),
|
|
2631
2832
|
role: MemberRole
|
|
2632
2833
|
});
|
|
2633
|
-
var MemberUpdate =
|
|
2834
|
+
var MemberUpdate = z11.object({ role: MemberRole });
|
|
2634
2835
|
var API_KEY_SCOPES = ["full", "read", "send"];
|
|
2635
|
-
var ApiKeyScope =
|
|
2636
|
-
var ApiKey =
|
|
2836
|
+
var ApiKeyScope = z11.enum(API_KEY_SCOPES);
|
|
2837
|
+
var ApiKey = z11.object({
|
|
2637
2838
|
id: Id,
|
|
2638
|
-
name:
|
|
2839
|
+
name: z11.string().min(1).max(120),
|
|
2639
2840
|
/** The first characters of the key, for recognising it in a list. */
|
|
2640
|
-
prefix:
|
|
2841
|
+
prefix: z11.string().min(4).max(32),
|
|
2641
2842
|
scope: ApiKeyScope,
|
|
2642
2843
|
last_used_at: Timestamp.nullable(),
|
|
2643
2844
|
revoked_at: Timestamp.nullable(),
|
|
2644
2845
|
created_at: Timestamp
|
|
2645
2846
|
});
|
|
2646
|
-
var ApiKeyCreate =
|
|
2647
|
-
name:
|
|
2847
|
+
var ApiKeyCreate = z11.object({
|
|
2848
|
+
name: z11.string().min(1).max(120),
|
|
2648
2849
|
/** Omitted means `full`. */
|
|
2649
2850
|
scope: ApiKeyScope.optional()
|
|
2650
2851
|
});
|
|
2651
|
-
var ApiKeyCreated =
|
|
2652
|
-
key:
|
|
2852
|
+
var ApiKeyCreated = z11.object({
|
|
2853
|
+
key: z11.string().min(20),
|
|
2653
2854
|
api_key: ApiKey
|
|
2654
2855
|
});
|
|
2655
2856
|
var AUDIT_ACTIONS = [
|
|
@@ -2738,29 +2939,41 @@ var AUDIT_ACTIONS = [
|
|
|
2738
2939
|
"automation.run_ended",
|
|
2739
2940
|
"event.recorded"
|
|
2740
2941
|
];
|
|
2741
|
-
var AuditAction =
|
|
2742
|
-
var
|
|
2743
|
-
|
|
2744
|
-
|
|
2745
|
-
|
|
2942
|
+
var AuditAction = z11.enum(AUDIT_ACTIONS);
|
|
2943
|
+
var TRUSTED_APPS = ["booking"];
|
|
2944
|
+
var TrustedApp = z11.enum(TRUSTED_APPS);
|
|
2945
|
+
var AuditActor = z11.discriminatedUnion("type", [
|
|
2946
|
+
z11.object({
|
|
2947
|
+
type: z11.literal("member"),
|
|
2948
|
+
member_id: Id,
|
|
2949
|
+
subject: z11.string().min(1),
|
|
2950
|
+
/**
|
|
2951
|
+
* The application the person acted through, when it was not the dashboard:
|
|
2952
|
+
* the service set it from the service token the call carried, so it is
|
|
2953
|
+
* verified, unlike a key's `on_behalf_of`. Absent for the dashboard.
|
|
2954
|
+
*/
|
|
2955
|
+
app: TrustedApp.optional()
|
|
2956
|
+
}),
|
|
2957
|
+
z11.object({
|
|
2958
|
+
type: z11.literal("api_key"),
|
|
2746
2959
|
api_key_id: Id,
|
|
2747
2960
|
/**
|
|
2748
2961
|
* Who the client's application said it was acting for, from
|
|
2749
2962
|
* `ON_BEHALF_OF_HEADER`: reported by that application, never verified.
|
|
2750
2963
|
* Absent when the call carried no label.
|
|
2751
2964
|
*/
|
|
2752
|
-
on_behalf_of:
|
|
2965
|
+
on_behalf_of: z11.string().min(1).max(ON_BEHALF_OF_MAX_LENGTH).optional()
|
|
2753
2966
|
}),
|
|
2754
2967
|
/** The service itself (the worker, a bounce, the hosted unsubscribe page). */
|
|
2755
|
-
|
|
2968
|
+
z11.object({ type: z11.literal("system"), reason: z11.string().min(1).max(200) })
|
|
2756
2969
|
]);
|
|
2757
|
-
var AuditEntry =
|
|
2970
|
+
var AuditEntry = z11.object({
|
|
2758
2971
|
id: Id,
|
|
2759
2972
|
action: AuditAction,
|
|
2760
2973
|
actor: AuditActor,
|
|
2761
|
-
target_type:
|
|
2974
|
+
target_type: z11.string().min(1).max(64),
|
|
2762
2975
|
target_id: Id.nullable(),
|
|
2763
|
-
details:
|
|
2976
|
+
details: z11.record(z11.unknown()),
|
|
2764
2977
|
created_at: Timestamp
|
|
2765
2978
|
});
|
|
2766
2979
|
var AuditQuery = PageQuery.extend({
|
|
@@ -2769,12 +2982,12 @@ var AuditQuery = PageQuery.extend({
|
|
|
2769
2982
|
});
|
|
2770
2983
|
|
|
2771
2984
|
// src/invites.ts
|
|
2772
|
-
import { z as
|
|
2985
|
+
import { z as z12 } from "zod";
|
|
2773
2986
|
var INVITE_STATUSES = ["pending", "accepted", "revoked", "expired"];
|
|
2774
|
-
var InviteStatus =
|
|
2987
|
+
var InviteStatus = z12.enum(INVITE_STATUSES);
|
|
2775
2988
|
var DEFAULT_INVITE_TTL_DAYS = 7;
|
|
2776
2989
|
var MAX_INVITE_TTL_DAYS = 30;
|
|
2777
|
-
var Invite =
|
|
2990
|
+
var Invite = z12.object({
|
|
2778
2991
|
id: Id,
|
|
2779
2992
|
/** Stored lowercased; only a person signed in with this address can accept. */
|
|
2780
2993
|
email: Email,
|
|
@@ -2782,32 +2995,32 @@ var Invite = z11.object({
|
|
|
2782
2995
|
/** Derived from the timestamps below, never stored. */
|
|
2783
2996
|
status: InviteStatus,
|
|
2784
2997
|
/** The member who invited, while they are still one (null after they left). */
|
|
2785
|
-
invited_by:
|
|
2998
|
+
invited_by: z12.object({ member_id: Id.nullable(), email: Email.nullable() }),
|
|
2786
2999
|
expires_at: Timestamp,
|
|
2787
3000
|
accepted_at: Timestamp.nullable(),
|
|
2788
3001
|
revoked_at: Timestamp.nullable(),
|
|
2789
3002
|
created_at: Timestamp
|
|
2790
3003
|
});
|
|
2791
|
-
var InviteCreate =
|
|
3004
|
+
var InviteCreate = z12.object({
|
|
2792
3005
|
email: Email,
|
|
2793
3006
|
role: MemberRole,
|
|
2794
|
-
expires_in_days:
|
|
3007
|
+
expires_in_days: z12.number().int().min(1).max(MAX_INVITE_TTL_DAYS).optional()
|
|
2795
3008
|
});
|
|
2796
|
-
var InviteCreated =
|
|
3009
|
+
var InviteCreated = z12.object({
|
|
2797
3010
|
invite: Invite,
|
|
2798
|
-
token:
|
|
3011
|
+
token: z12.string().min(32).max(128)
|
|
2799
3012
|
});
|
|
2800
3013
|
var InviteListQuery = PageQuery.extend({
|
|
2801
3014
|
status: InviteStatus.optional()
|
|
2802
3015
|
});
|
|
2803
|
-
var InviteAccept =
|
|
2804
|
-
token:
|
|
3016
|
+
var InviteAccept = z12.object({
|
|
3017
|
+
token: z12.string().min(32).max(128),
|
|
2805
3018
|
email: Email,
|
|
2806
|
-
name:
|
|
3019
|
+
name: z12.string().max(200).nullable().optional()
|
|
2807
3020
|
});
|
|
2808
|
-
var InviteAccepted =
|
|
3021
|
+
var InviteAccepted = z12.object({
|
|
2809
3022
|
workspace: WorkspaceMembership,
|
|
2810
|
-
already_member:
|
|
3023
|
+
already_member: z12.boolean()
|
|
2811
3024
|
});
|
|
2812
3025
|
var INVITE_REFUSAL_REASONS = ["email_mismatch", "inviter_lacks_role", "expired", "revoked", "accepted"];
|
|
2813
3026
|
|
|
@@ -3437,13 +3650,13 @@ function renameInsertionEverywhere(drafts, from, rawTo) {
|
|
|
3437
3650
|
}
|
|
3438
3651
|
|
|
3439
3652
|
// src/providers.ts
|
|
3440
|
-
import { z as
|
|
3653
|
+
import { z as z13 } from "zod";
|
|
3441
3654
|
var PROVIDER_KINDS = ["smtp", "resend"];
|
|
3442
|
-
var ProviderKind =
|
|
3443
|
-
var ProviderPolicy =
|
|
3444
|
-
daily_recipient_budget:
|
|
3445
|
-
min_interval_ms:
|
|
3446
|
-
max_recipients_per_message:
|
|
3655
|
+
var ProviderKind = z13.enum(PROVIDER_KINDS);
|
|
3656
|
+
var ProviderPolicy = z13.object({
|
|
3657
|
+
daily_recipient_budget: z13.number().int().min(1).max(1e7),
|
|
3658
|
+
min_interval_ms: z13.number().int().min(0).max(36e5),
|
|
3659
|
+
max_recipients_per_message: z13.number().int().min(1).max(1e3)
|
|
3447
3660
|
});
|
|
3448
3661
|
var ICLOUD_SMTP_POLICY = {
|
|
3449
3662
|
daily_recipient_budget: 800,
|
|
@@ -3451,17 +3664,17 @@ var ICLOUD_SMTP_POLICY = {
|
|
|
3451
3664
|
max_recipients_per_message: 1
|
|
3452
3665
|
};
|
|
3453
3666
|
var SMTP_SECURITY = ["tls", "starttls"];
|
|
3454
|
-
var SmtpConfigBase =
|
|
3455
|
-
host:
|
|
3456
|
-
port:
|
|
3667
|
+
var SmtpConfigBase = z13.object({
|
|
3668
|
+
host: z13.string().min(1).max(255),
|
|
3669
|
+
port: z13.number().int().min(1).max(65535),
|
|
3457
3670
|
/** `tls` connects encrypted (465), `starttls` upgrades (587). Never plaintext. */
|
|
3458
|
-
security:
|
|
3459
|
-
username:
|
|
3671
|
+
security: z13.enum(SMTP_SECURITY),
|
|
3672
|
+
username: z13.string().min(1).max(255)
|
|
3460
3673
|
});
|
|
3461
|
-
var ResendConfigBase =
|
|
3462
|
-
var ProviderRejections =
|
|
3463
|
-
count:
|
|
3464
|
-
last_error:
|
|
3674
|
+
var ResendConfigBase = z13.object({});
|
|
3675
|
+
var ProviderRejections = z13.object({
|
|
3676
|
+
count: z13.number().int().min(0),
|
|
3677
|
+
last_error: z13.string().nullable(),
|
|
3465
3678
|
last_at: Timestamp.nullable(),
|
|
3466
3679
|
/**
|
|
3467
3680
|
* The last time the bounce circuit breaker tripped on one of this provider's
|
|
@@ -3471,59 +3684,59 @@ var ProviderRejections = z12.object({
|
|
|
3471
3684
|
* run's bounce blocks were undone and the mailing paused. `sample` is the
|
|
3472
3685
|
* reply that tripped it.
|
|
3473
3686
|
*/
|
|
3474
|
-
anomaly:
|
|
3687
|
+
anomaly: z13.object({
|
|
3475
3688
|
at: Timestamp,
|
|
3476
3689
|
/** `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. */
|
|
3477
|
-
scope:
|
|
3690
|
+
scope: z13.enum(["mailing", "provider"]),
|
|
3478
3691
|
/**
|
|
3479
3692
|
* True while the provider-wide breaker is open: no bounce blocks through
|
|
3480
3693
|
* this provider, test sends refused and mailings not started or resumed
|
|
3481
3694
|
* (`provider_anomaly`), until an admin clears it (`providers.clearAnomaly`).
|
|
3482
3695
|
*/
|
|
3483
|
-
blocking:
|
|
3696
|
+
blocking: z13.boolean(),
|
|
3484
3697
|
mailing_id: Id.nullable(),
|
|
3485
|
-
reason:
|
|
3486
|
-
sample:
|
|
3698
|
+
reason: z13.string(),
|
|
3699
|
+
sample: z13.string()
|
|
3487
3700
|
}).nullable()
|
|
3488
3701
|
});
|
|
3489
|
-
var ProviderEvents =
|
|
3490
|
-
status:
|
|
3491
|
-
source:
|
|
3492
|
-
url:
|
|
3493
|
-
error:
|
|
3702
|
+
var ProviderEvents = z13.object({
|
|
3703
|
+
status: z13.enum(["active", "needs_secret"]),
|
|
3704
|
+
source: z13.enum(["automatic", "manual"]).nullable(),
|
|
3705
|
+
url: z13.string().url(),
|
|
3706
|
+
error: z13.string().nullable(),
|
|
3494
3707
|
/**
|
|
3495
3708
|
* Events that named an email this provider never sent through the service
|
|
3496
3709
|
* (another workspace or system sharing the Resend account): counted, never
|
|
3497
3710
|
* acted on.
|
|
3498
3711
|
*/
|
|
3499
|
-
unmatched:
|
|
3712
|
+
unmatched: z13.number().int().min(0)
|
|
3500
3713
|
});
|
|
3501
3714
|
var RESEND_EVENT_TYPES = ["email.bounced", "email.complained", "email.delivery_delayed"];
|
|
3502
|
-
var ResendSigningSecret =
|
|
3503
|
-
var ProviderEventsSecret =
|
|
3715
|
+
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_");
|
|
3716
|
+
var ProviderEventsSecret = z13.object({ signing_secret: ResendSigningSecret });
|
|
3504
3717
|
var ProviderFields = {
|
|
3505
|
-
name:
|
|
3506
|
-
from_name:
|
|
3718
|
+
name: z13.string().min(1).max(120),
|
|
3719
|
+
from_name: z13.string().min(1).max(120),
|
|
3507
3720
|
from_email: Email,
|
|
3508
3721
|
reply_to: Email.nullable(),
|
|
3509
3722
|
policy: ProviderPolicy
|
|
3510
3723
|
};
|
|
3511
|
-
var Provider =
|
|
3512
|
-
|
|
3724
|
+
var Provider = z13.discriminatedUnion("kind", [
|
|
3725
|
+
z13.object({
|
|
3513
3726
|
id: Id,
|
|
3514
|
-
kind:
|
|
3727
|
+
kind: z13.literal("smtp"),
|
|
3515
3728
|
config: SmtpConfigBase,
|
|
3516
|
-
has_secret:
|
|
3729
|
+
has_secret: z13.boolean(),
|
|
3517
3730
|
...ProviderFields,
|
|
3518
3731
|
rejections: ProviderRejections,
|
|
3519
3732
|
created_at: Timestamp,
|
|
3520
3733
|
updated_at: Timestamp
|
|
3521
3734
|
}),
|
|
3522
|
-
|
|
3735
|
+
z13.object({
|
|
3523
3736
|
id: Id,
|
|
3524
|
-
kind:
|
|
3737
|
+
kind: z13.literal("resend"),
|
|
3525
3738
|
config: ResendConfigBase,
|
|
3526
|
-
has_secret:
|
|
3739
|
+
has_secret: z13.boolean(),
|
|
3527
3740
|
...ProviderFields,
|
|
3528
3741
|
rejections: ProviderRejections,
|
|
3529
3742
|
events: ProviderEvents,
|
|
@@ -3531,31 +3744,31 @@ var Provider = z12.discriminatedUnion("kind", [
|
|
|
3531
3744
|
updated_at: Timestamp
|
|
3532
3745
|
})
|
|
3533
3746
|
]);
|
|
3534
|
-
var ProviderCreate =
|
|
3535
|
-
|
|
3536
|
-
kind:
|
|
3537
|
-
config: SmtpConfigBase.extend({ password:
|
|
3747
|
+
var ProviderCreate = z13.discriminatedUnion("kind", [
|
|
3748
|
+
z13.object({
|
|
3749
|
+
kind: z13.literal("smtp"),
|
|
3750
|
+
config: SmtpConfigBase.extend({ password: z13.string().min(1).max(1024) }),
|
|
3538
3751
|
...ProviderFields
|
|
3539
3752
|
}),
|
|
3540
|
-
|
|
3541
|
-
kind:
|
|
3542
|
-
config: ResendConfigBase.extend({ api_key:
|
|
3753
|
+
z13.object({
|
|
3754
|
+
kind: z13.literal("resend"),
|
|
3755
|
+
config: ResendConfigBase.extend({ api_key: z13.string().min(1).max(1024) }),
|
|
3543
3756
|
...ProviderFields
|
|
3544
3757
|
})
|
|
3545
3758
|
]);
|
|
3546
|
-
var ProviderUpdate =
|
|
3547
|
-
|
|
3548
|
-
kind:
|
|
3549
|
-
config: SmtpConfigBase.extend({ password:
|
|
3759
|
+
var ProviderUpdate = z13.discriminatedUnion("kind", [
|
|
3760
|
+
z13.object({
|
|
3761
|
+
kind: z13.literal("smtp"),
|
|
3762
|
+
config: SmtpConfigBase.extend({ password: z13.string().min(1).max(1024).optional() }).partial().optional(),
|
|
3550
3763
|
name: ProviderFields.name.optional(),
|
|
3551
3764
|
from_name: ProviderFields.from_name.optional(),
|
|
3552
3765
|
from_email: ProviderFields.from_email.optional(),
|
|
3553
3766
|
reply_to: ProviderFields.reply_to.optional(),
|
|
3554
3767
|
policy: ProviderPolicy.partial().optional()
|
|
3555
3768
|
}),
|
|
3556
|
-
|
|
3557
|
-
kind:
|
|
3558
|
-
config: ResendConfigBase.extend({ api_key:
|
|
3769
|
+
z13.object({
|
|
3770
|
+
kind: z13.literal("resend"),
|
|
3771
|
+
config: ResendConfigBase.extend({ api_key: z13.string().min(1).max(1024).optional() }).optional(),
|
|
3559
3772
|
name: ProviderFields.name.optional(),
|
|
3560
3773
|
from_name: ProviderFields.from_name.optional(),
|
|
3561
3774
|
from_email: ProviderFields.from_email.optional(),
|
|
@@ -3563,48 +3776,48 @@ var ProviderUpdate = z12.discriminatedUnion("kind", [
|
|
|
3563
3776
|
policy: ProviderPolicy.partial().optional()
|
|
3564
3777
|
})
|
|
3565
3778
|
]);
|
|
3566
|
-
var ProviderUsage =
|
|
3779
|
+
var ProviderUsage = z13.object({
|
|
3567
3780
|
provider_id: Id,
|
|
3568
|
-
recipients_last_24h:
|
|
3569
|
-
remaining_budget:
|
|
3781
|
+
recipients_last_24h: z13.number().int().min(0),
|
|
3782
|
+
remaining_budget: z13.number().int().min(0),
|
|
3570
3783
|
/** When enough of the rolling window frees up to send again, if exhausted. */
|
|
3571
3784
|
next_capacity_at: Timestamp.nullable()
|
|
3572
3785
|
});
|
|
3573
|
-
var ProviderVerifyResult =
|
|
3574
|
-
ok:
|
|
3575
|
-
error:
|
|
3786
|
+
var ProviderVerifyResult = z13.object({
|
|
3787
|
+
ok: z13.boolean(),
|
|
3788
|
+
error: z13.string().nullable()
|
|
3576
3789
|
});
|
|
3577
3790
|
|
|
3578
3791
|
// src/contacts.ts
|
|
3579
|
-
import { z as
|
|
3580
|
-
var Topic =
|
|
3792
|
+
import { z as z14 } from "zod";
|
|
3793
|
+
var Topic = z14.object({
|
|
3581
3794
|
id: Id,
|
|
3582
3795
|
slug: Slug,
|
|
3583
|
-
name:
|
|
3796
|
+
name: z14.string().min(1).max(120),
|
|
3584
3797
|
/** Shown on the hosted preference page. */
|
|
3585
|
-
description:
|
|
3798
|
+
description: z14.string().max(1e3).nullable(),
|
|
3586
3799
|
/** Translations of `name` and `description` per locale, for the hosted page. */
|
|
3587
|
-
translations:
|
|
3588
|
-
|
|
3800
|
+
translations: z14.record(
|
|
3801
|
+
z14.object({ name: z14.string().min(1).max(120), description: z14.string().max(1e3).nullable() })
|
|
3589
3802
|
),
|
|
3590
3803
|
created_at: Timestamp,
|
|
3591
3804
|
updated_at: Timestamp
|
|
3592
3805
|
});
|
|
3593
|
-
var TopicCreate =
|
|
3806
|
+
var TopicCreate = z14.object({
|
|
3594
3807
|
slug: Slug,
|
|
3595
|
-
name:
|
|
3596
|
-
description:
|
|
3808
|
+
name: z14.string().min(1).max(120),
|
|
3809
|
+
description: z14.string().max(1e3).optional(),
|
|
3597
3810
|
translations: Topic.shape.translations.optional()
|
|
3598
3811
|
});
|
|
3599
3812
|
var TopicUpdate = TopicCreate.omit({ slug: true }).partial().refine((v) => Object.keys(v).length > 0, "at least one field");
|
|
3600
|
-
var Contact =
|
|
3813
|
+
var Contact = z14.object({
|
|
3601
3814
|
id: Id,
|
|
3602
|
-
external_id:
|
|
3815
|
+
external_id: z14.string().min(1).max(255).nullable(),
|
|
3603
3816
|
/** Stored lowercased, unique per workspace. */
|
|
3604
3817
|
email: Email,
|
|
3605
|
-
first_name:
|
|
3606
|
-
last_name:
|
|
3607
|
-
locale:
|
|
3818
|
+
first_name: z14.string().max(200).nullable(),
|
|
3819
|
+
last_name: z14.string().max(200).nullable(),
|
|
3820
|
+
locale: z14.string().min(2).max(35).nullable(),
|
|
3608
3821
|
/**
|
|
3609
3822
|
* The person's own time zone, an IANA name such as `Europe/Berlin` (A1).
|
|
3610
3823
|
* Every wall clock an automation waits for is read in it, falling back to the
|
|
@@ -3612,65 +3825,65 @@ var Contact = z13.object({
|
|
|
3612
3825
|
*/
|
|
3613
3826
|
timezone: TimezoneName.nullable(),
|
|
3614
3827
|
properties: Properties,
|
|
3615
|
-
topics:
|
|
3828
|
+
topics: z14.array(Slug),
|
|
3616
3829
|
/** The slugs of the contact's tags (S4). */
|
|
3617
|
-
tags:
|
|
3830
|
+
tags: z14.array(Slug),
|
|
3618
3831
|
created_at: Timestamp,
|
|
3619
3832
|
updated_at: Timestamp
|
|
3620
3833
|
});
|
|
3621
|
-
var ContactUpsert =
|
|
3622
|
-
external_id:
|
|
3834
|
+
var ContactUpsert = z14.object({
|
|
3835
|
+
external_id: z14.string().min(1).max(255).optional(),
|
|
3623
3836
|
email: Email.optional(),
|
|
3624
|
-
first_name:
|
|
3625
|
-
last_name:
|
|
3626
|
-
locale:
|
|
3837
|
+
first_name: z14.string().max(200).nullable().optional(),
|
|
3838
|
+
last_name: z14.string().max(200).nullable().optional(),
|
|
3839
|
+
locale: z14.string().min(2).max(35).nullable().optional(),
|
|
3627
3840
|
timezone: TimezoneName.nullable().optional(),
|
|
3628
3841
|
properties: Properties.optional(),
|
|
3629
|
-
topics:
|
|
3842
|
+
topics: z14.array(Slug).max(100).optional()
|
|
3630
3843
|
}).refine((v) => v.external_id !== void 0 || v.email !== void 0, {
|
|
3631
3844
|
message: "external_id or email is required",
|
|
3632
3845
|
path: ["email"]
|
|
3633
3846
|
});
|
|
3634
|
-
var ContactUpsertResult =
|
|
3847
|
+
var ContactUpsertResult = z14.object({
|
|
3635
3848
|
contact: Contact,
|
|
3636
|
-
created:
|
|
3849
|
+
created: z14.boolean()
|
|
3637
3850
|
});
|
|
3638
3851
|
var ContactListQuery = PageQuery.extend({
|
|
3639
|
-
email:
|
|
3640
|
-
external_id:
|
|
3852
|
+
email: z14.string().min(1).max(254).optional(),
|
|
3853
|
+
external_id: z14.string().min(1).max(255).optional(),
|
|
3641
3854
|
topic: Slug.optional()
|
|
3642
3855
|
});
|
|
3643
|
-
var ContactErased =
|
|
3644
|
-
ok:
|
|
3645
|
-
erased_messages:
|
|
3646
|
-
erased_recipients:
|
|
3647
|
-
suppressions_kept:
|
|
3856
|
+
var ContactErased = z14.object({
|
|
3857
|
+
ok: z14.literal(true),
|
|
3858
|
+
erased_messages: z14.number().int().min(0),
|
|
3859
|
+
erased_recipients: z14.number().int().min(0),
|
|
3860
|
+
suppressions_kept: z14.number().int().min(0)
|
|
3648
3861
|
});
|
|
3649
3862
|
var SUPPRESSION_REASONS = ["unsubscribed", "bounced", "complained", "manual"];
|
|
3650
|
-
var SuppressionReason =
|
|
3651
|
-
var Suppression =
|
|
3863
|
+
var SuppressionReason = z14.enum(SUPPRESSION_REASONS);
|
|
3864
|
+
var Suppression = z14.object({
|
|
3652
3865
|
id: Id,
|
|
3653
3866
|
email: Email,
|
|
3654
3867
|
reason: SuppressionReason,
|
|
3655
3868
|
topic: Slug.nullable(),
|
|
3656
3869
|
source_message_id: Id.nullable(),
|
|
3657
|
-
note:
|
|
3870
|
+
note: z14.string().max(1e3).nullable(),
|
|
3658
3871
|
created_at: Timestamp
|
|
3659
3872
|
});
|
|
3660
|
-
var SuppressionCreate =
|
|
3873
|
+
var SuppressionCreate = z14.object({
|
|
3661
3874
|
email: Email,
|
|
3662
|
-
reason:
|
|
3875
|
+
reason: z14.enum(["manual", "unsubscribed"]),
|
|
3663
3876
|
topic: Slug.nullable().optional(),
|
|
3664
|
-
note:
|
|
3877
|
+
note: z14.string().max(1e3).optional()
|
|
3665
3878
|
});
|
|
3666
3879
|
var SuppressionListQuery = PageQuery.extend({
|
|
3667
|
-
email:
|
|
3880
|
+
email: z14.string().min(1).max(254).optional(),
|
|
3668
3881
|
reason: SuppressionReason.optional(),
|
|
3669
3882
|
topic: Slug.optional()
|
|
3670
3883
|
});
|
|
3671
3884
|
|
|
3672
3885
|
// src/webhooks.ts
|
|
3673
|
-
import { z as
|
|
3886
|
+
import { z as z15 } from "zod";
|
|
3674
3887
|
var WEBHOOK_EVENT_TYPES = [
|
|
3675
3888
|
"message.sent",
|
|
3676
3889
|
"message.failed",
|
|
@@ -3714,23 +3927,23 @@ var WEBHOOK_EVENT_TYPES = [
|
|
|
3714
3927
|
"template.updated",
|
|
3715
3928
|
"template.deleted"
|
|
3716
3929
|
];
|
|
3717
|
-
var WebhookEventType =
|
|
3718
|
-
var MessageEventBase =
|
|
3930
|
+
var WebhookEventType = z15.enum(WEBHOOK_EVENT_TYPES);
|
|
3931
|
+
var MessageEventBase = z15.object({
|
|
3719
3932
|
message_id: Id,
|
|
3720
3933
|
mailing_id: Id.nullable(),
|
|
3721
3934
|
mailing_metadata: MailingMetadata.nullable(),
|
|
3722
3935
|
recipient_id: Id.nullable(),
|
|
3723
3936
|
contact_id: Id.nullable(),
|
|
3724
|
-
external_id:
|
|
3937
|
+
external_id: z15.string().nullable(),
|
|
3725
3938
|
email: Email,
|
|
3726
|
-
subject:
|
|
3939
|
+
subject: z15.string(),
|
|
3727
3940
|
topic: Slug.nullable(),
|
|
3728
|
-
provider_message_id:
|
|
3729
|
-
is_test:
|
|
3941
|
+
provider_message_id: z15.string().nullable(),
|
|
3942
|
+
is_test: z15.boolean()
|
|
3730
3943
|
});
|
|
3731
3944
|
var MessageSentData = MessageEventBase.extend({
|
|
3732
3945
|
/** The final HTML as sent, so the client can archive it beside its own records. */
|
|
3733
|
-
html:
|
|
3946
|
+
html: z15.string(),
|
|
3734
3947
|
sent_at: Timestamp,
|
|
3735
3948
|
/**
|
|
3736
3949
|
* The choice each insertion made (see `Message.insertions`); absent or null
|
|
@@ -3741,20 +3954,20 @@ var MessageSentData = MessageEventBase.extend({
|
|
|
3741
3954
|
insertions: InsertionChoices.nullish()
|
|
3742
3955
|
});
|
|
3743
3956
|
var MessageFailedData = MessageEventBase.extend({
|
|
3744
|
-
error:
|
|
3957
|
+
error: z15.string(),
|
|
3745
3958
|
/** False when the provider rejected permanently; the recipient will not be retried. */
|
|
3746
|
-
retryable:
|
|
3959
|
+
retryable: z15.boolean(),
|
|
3747
3960
|
failed_at: Timestamp
|
|
3748
3961
|
});
|
|
3749
3962
|
var UNSUBSCRIBE_SOURCES = ["hosted_page", "one_click", "api", "dashboard", "signup_form"];
|
|
3750
|
-
var ContactUnsubscribedData =
|
|
3963
|
+
var ContactUnsubscribedData = z15.object({
|
|
3751
3964
|
contact_id: Id.nullable(),
|
|
3752
|
-
external_id:
|
|
3965
|
+
external_id: z15.string().nullable(),
|
|
3753
3966
|
email: Email,
|
|
3754
3967
|
/** The topic unsubscribed from; null means every topic. */
|
|
3755
3968
|
topic: Slug.nullable(),
|
|
3756
3969
|
mailing_id: Id.nullable(),
|
|
3757
|
-
source:
|
|
3970
|
+
source: z15.enum(UNSUBSCRIBE_SOURCES),
|
|
3758
3971
|
unsubscribed_at: Timestamp
|
|
3759
3972
|
});
|
|
3760
3973
|
var ContactResubscribedData = ContactUnsubscribedData.omit({ unsubscribed_at: true }).extend({
|
|
@@ -3763,142 +3976,142 @@ var ContactResubscribedData = ContactUnsubscribedData.omit({ unsubscribed_at: tr
|
|
|
3763
3976
|
* breaker undid a `bounced` block it judged false (a `contact.bounced` for the
|
|
3764
3977
|
* same address and mailing went out before); `topic` is null.
|
|
3765
3978
|
*/
|
|
3766
|
-
source:
|
|
3979
|
+
source: z15.enum([...UNSUBSCRIBE_SOURCES, "bounce_reverted"]),
|
|
3767
3980
|
resubscribed_at: Timestamp
|
|
3768
3981
|
});
|
|
3769
|
-
var ContactBouncedData =
|
|
3982
|
+
var ContactBouncedData = z15.object({
|
|
3770
3983
|
contact_id: Id.nullable(),
|
|
3771
|
-
external_id:
|
|
3984
|
+
external_id: z15.string().nullable(),
|
|
3772
3985
|
email: Email,
|
|
3773
3986
|
/** `bounced` for a hard bounce, `complained` for a spam complaint. */
|
|
3774
|
-
reason:
|
|
3987
|
+
reason: z15.enum(["bounced", "complained"]),
|
|
3775
3988
|
message_id: Id.nullable(),
|
|
3776
|
-
diagnostic:
|
|
3989
|
+
diagnostic: z15.string().nullable(),
|
|
3777
3990
|
bounced_at: Timestamp
|
|
3778
3991
|
});
|
|
3779
|
-
var MailingFinishedData =
|
|
3992
|
+
var MailingFinishedData = z15.object({
|
|
3780
3993
|
mailing_id: Id,
|
|
3781
3994
|
mailing_metadata: MailingMetadata,
|
|
3782
|
-
status:
|
|
3995
|
+
status: z15.enum(["sent", "partially_failed", "cancelled"]),
|
|
3783
3996
|
counts: MailingCounts,
|
|
3784
3997
|
finished_at: Timestamp
|
|
3785
3998
|
});
|
|
3786
|
-
var ContactSubscribedData =
|
|
3999
|
+
var ContactSubscribedData = z15.object({
|
|
3787
4000
|
contact_id: Id,
|
|
3788
|
-
external_id:
|
|
4001
|
+
external_id: z15.string().nullable(),
|
|
3789
4002
|
email: Email,
|
|
3790
|
-
topics:
|
|
3791
|
-
source:
|
|
4003
|
+
topics: z15.array(Slug),
|
|
4004
|
+
source: z15.enum(["signup_form", "api", "dashboard", "automation"]),
|
|
3792
4005
|
signup_form_id: Id.nullable(),
|
|
3793
|
-
signup_form_version:
|
|
4006
|
+
signup_form_version: z15.number().int().min(1).nullable(),
|
|
3794
4007
|
subscribed_at: Timestamp
|
|
3795
4008
|
});
|
|
3796
|
-
var ImportFinishedData =
|
|
4009
|
+
var ImportFinishedData = z15.object({
|
|
3797
4010
|
import_id: Id,
|
|
3798
|
-
status:
|
|
4011
|
+
status: z15.enum(["completed", "cancelled", "failed"]),
|
|
3799
4012
|
result: ImportReport.nullable(),
|
|
3800
|
-
error:
|
|
4013
|
+
error: z15.string().nullable(),
|
|
3801
4014
|
finished_at: Timestamp
|
|
3802
4015
|
});
|
|
3803
|
-
var MailingScheduledData =
|
|
4016
|
+
var MailingScheduledData = z15.object({
|
|
3804
4017
|
mailing_id: Id,
|
|
3805
4018
|
mailing_metadata: MailingMetadata,
|
|
3806
4019
|
scheduled_at: Timestamp.nullable()
|
|
3807
4020
|
});
|
|
3808
|
-
var MailingStartedData =
|
|
4021
|
+
var MailingStartedData = z15.object({
|
|
3809
4022
|
mailing_id: Id,
|
|
3810
4023
|
mailing_metadata: MailingMetadata,
|
|
3811
|
-
trigger:
|
|
3812
|
-
recipients:
|
|
4024
|
+
trigger: z15.enum(["send", "schedule"]),
|
|
4025
|
+
recipients: z15.number().int().min(0),
|
|
3813
4026
|
started_at: Timestamp
|
|
3814
4027
|
});
|
|
3815
|
-
var MailingScheduleFailedData =
|
|
4028
|
+
var MailingScheduleFailedData = z15.object({
|
|
3816
4029
|
mailing_id: Id,
|
|
3817
4030
|
mailing_metadata: MailingMetadata,
|
|
3818
|
-
code:
|
|
3819
|
-
message:
|
|
4031
|
+
code: z15.string(),
|
|
4032
|
+
message: z15.string(),
|
|
3820
4033
|
failed_at: Timestamp
|
|
3821
4034
|
});
|
|
3822
|
-
var MailingAbWinnerSelectedData =
|
|
4035
|
+
var MailingAbWinnerSelectedData = z15.object({
|
|
3823
4036
|
mailing_id: Id,
|
|
3824
4037
|
mailing_metadata: MailingMetadata,
|
|
3825
|
-
winner:
|
|
3826
|
-
decided_by:
|
|
3827
|
-
variants:
|
|
4038
|
+
winner: z15.string(),
|
|
4039
|
+
decided_by: z15.enum(["metric", "manual"]),
|
|
4040
|
+
variants: z15.array(VariantAnalytics),
|
|
3828
4041
|
decided_at: Timestamp
|
|
3829
4042
|
});
|
|
3830
|
-
var ContactTaggedData =
|
|
4043
|
+
var ContactTaggedData = z15.object({
|
|
3831
4044
|
contact_id: Id,
|
|
3832
|
-
external_id:
|
|
4045
|
+
external_id: z15.string().nullable(),
|
|
3833
4046
|
email: Email,
|
|
3834
4047
|
/** Only the tags the contact did not already have. */
|
|
3835
|
-
tags:
|
|
3836
|
-
source:
|
|
4048
|
+
tags: z15.array(Slug).min(1),
|
|
4049
|
+
source: z15.enum(["api", "dashboard", "signup_form", "import", "automation"]),
|
|
3837
4050
|
tagged_at: Timestamp
|
|
3838
4051
|
});
|
|
3839
|
-
var ContactUpdatedData =
|
|
4052
|
+
var ContactUpdatedData = z15.object({
|
|
3840
4053
|
contact_id: Id,
|
|
3841
|
-
external_id:
|
|
4054
|
+
external_id: z15.string().nullable(),
|
|
3842
4055
|
email: Email,
|
|
3843
|
-
changed:
|
|
4056
|
+
changed: z15.array(z15.string()).min(1),
|
|
3844
4057
|
properties: Properties,
|
|
3845
|
-
source:
|
|
4058
|
+
source: z15.enum(["api", "dashboard", "signup_form", "import", "automation"]),
|
|
3846
4059
|
updated_at: Timestamp
|
|
3847
4060
|
});
|
|
3848
|
-
var AutomationRunEventBase =
|
|
4061
|
+
var AutomationRunEventBase = z15.object({
|
|
3849
4062
|
automation_id: Id,
|
|
3850
|
-
automation_name:
|
|
4063
|
+
automation_name: z15.string(),
|
|
3851
4064
|
run_id: Id,
|
|
3852
|
-
version:
|
|
4065
|
+
version: z15.number().int().min(1),
|
|
3853
4066
|
contact_id: Id,
|
|
3854
|
-
external_id:
|
|
4067
|
+
external_id: z15.string().nullable(),
|
|
3855
4068
|
email: Email
|
|
3856
4069
|
});
|
|
3857
4070
|
var AutomationRunStartedData = AutomationRunEventBase.extend({
|
|
3858
4071
|
/** What let them in: a trigger kind, or `manual` for an explicit enrolment. */
|
|
3859
|
-
trigger_kind:
|
|
4072
|
+
trigger_kind: z15.string(),
|
|
3860
4073
|
entered_at: Timestamp
|
|
3861
4074
|
});
|
|
3862
4075
|
var AutomationRunCompletedData = AutomationRunEventBase.extend({
|
|
3863
|
-
last_step_id:
|
|
4076
|
+
last_step_id: z15.string().nullable(),
|
|
3864
4077
|
completed_at: Timestamp
|
|
3865
4078
|
});
|
|
3866
4079
|
var AutomationRunExitedData = AutomationRunEventBase.extend({
|
|
3867
4080
|
reason: AutomationEndReason,
|
|
3868
|
-
step_id:
|
|
4081
|
+
step_id: z15.string().nullable(),
|
|
3869
4082
|
exited_at: Timestamp
|
|
3870
4083
|
});
|
|
3871
4084
|
var AutomationRunFailedData = AutomationRunEventBase.extend({
|
|
3872
|
-
reason:
|
|
3873
|
-
step_id:
|
|
4085
|
+
reason: z15.string(),
|
|
4086
|
+
step_id: z15.string().nullable(),
|
|
3874
4087
|
failed_at: Timestamp
|
|
3875
4088
|
});
|
|
3876
4089
|
var AutomationWebhookData = AutomationRunEventBase.extend({
|
|
3877
|
-
step_id:
|
|
4090
|
+
step_id: z15.string(),
|
|
3878
4091
|
/** The step's own label, when it was given one, so a receiver can branch on a name. */
|
|
3879
|
-
step_label:
|
|
4092
|
+
step_label: z15.string().nullable(),
|
|
3880
4093
|
/** Whatever the step carries, verbatim. Null when it carries nothing. */
|
|
3881
4094
|
payload: JsonValue.nullable(),
|
|
3882
4095
|
fired_at: Timestamp
|
|
3883
4096
|
});
|
|
3884
|
-
var TemplateEventBase =
|
|
4097
|
+
var TemplateEventBase = z15.object({
|
|
3885
4098
|
template_id: Id,
|
|
3886
|
-
name:
|
|
3887
|
-
version:
|
|
3888
|
-
source:
|
|
4099
|
+
name: z15.string().min(1).max(200),
|
|
4100
|
+
version: z15.number().int().min(1),
|
|
4101
|
+
source: z15.enum(["api", "dashboard", "mjml_import"])
|
|
3889
4102
|
});
|
|
3890
4103
|
var TemplateCreatedData = TemplateEventBase.extend({ created_at: Timestamp });
|
|
3891
|
-
var TemplateUpdatedData = TemplateEventBase.extend({ archived:
|
|
4104
|
+
var TemplateUpdatedData = TemplateEventBase.extend({ archived: z15.boolean(), updated_at: Timestamp });
|
|
3892
4105
|
var TemplateDeletedData = TemplateEventBase.extend({ deleted_at: Timestamp });
|
|
3893
|
-
var envelope = (type, data) =>
|
|
4106
|
+
var envelope = (type, data) => z15.object({
|
|
3894
4107
|
/** Unique per event: a receiver deduplicates on it (deliveries may repeat). */
|
|
3895
4108
|
id: Id,
|
|
3896
|
-
type:
|
|
4109
|
+
type: z15.literal(type),
|
|
3897
4110
|
created_at: Timestamp,
|
|
3898
4111
|
workspace_id: Id,
|
|
3899
4112
|
data
|
|
3900
4113
|
});
|
|
3901
|
-
var WebhookEvent =
|
|
4114
|
+
var WebhookEvent = z15.discriminatedUnion("type", [
|
|
3902
4115
|
envelope("message.sent", MessageSentData),
|
|
3903
4116
|
envelope("message.failed", MessageFailedData),
|
|
3904
4117
|
envelope("contact.unsubscribed", ContactUnsubscribedData),
|
|
@@ -3922,16 +4135,16 @@ var WebhookEvent = z14.discriminatedUnion("type", [
|
|
|
3922
4135
|
envelope("template.updated", TemplateUpdatedData),
|
|
3923
4136
|
envelope("template.deleted", TemplateDeletedData)
|
|
3924
4137
|
]);
|
|
3925
|
-
var WebhookEndpoint =
|
|
4138
|
+
var WebhookEndpoint = z15.object({
|
|
3926
4139
|
id: Id,
|
|
3927
|
-
url:
|
|
3928
|
-
description:
|
|
3929
|
-
events:
|
|
3930
|
-
enabled:
|
|
4140
|
+
url: z15.string().url(),
|
|
4141
|
+
description: z15.string().max(500).nullable(),
|
|
4142
|
+
events: z15.array(WebhookEventType).min(1),
|
|
4143
|
+
enabled: z15.boolean(),
|
|
3931
4144
|
created_at: Timestamp,
|
|
3932
4145
|
updated_at: Timestamp
|
|
3933
4146
|
});
|
|
3934
|
-
var WebhookUrl =
|
|
4147
|
+
var WebhookUrl = z15.string().url().refine((u) => {
|
|
3935
4148
|
try {
|
|
3936
4149
|
const url = new URL(u);
|
|
3937
4150
|
if (url.protocol === "https:") return true;
|
|
@@ -3940,35 +4153,35 @@ var WebhookUrl = z14.string().url().refine((u) => {
|
|
|
3940
4153
|
return false;
|
|
3941
4154
|
}
|
|
3942
4155
|
}, "must be https (http only for localhost)");
|
|
3943
|
-
var WebhookEndpointCreate =
|
|
4156
|
+
var WebhookEndpointCreate = z15.object({
|
|
3944
4157
|
url: WebhookUrl,
|
|
3945
|
-
description:
|
|
3946
|
-
events:
|
|
3947
|
-
enabled:
|
|
4158
|
+
description: z15.string().max(500).optional(),
|
|
4159
|
+
events: z15.array(WebhookEventType).min(1),
|
|
4160
|
+
enabled: z15.boolean().optional()
|
|
3948
4161
|
});
|
|
3949
|
-
var WebhookEndpointUpdate =
|
|
4162
|
+
var WebhookEndpointUpdate = z15.object({
|
|
3950
4163
|
url: WebhookUrl,
|
|
3951
|
-
description:
|
|
3952
|
-
events:
|
|
3953
|
-
enabled:
|
|
4164
|
+
description: z15.string().max(500).nullable(),
|
|
4165
|
+
events: z15.array(WebhookEventType).min(1),
|
|
4166
|
+
enabled: z15.boolean()
|
|
3954
4167
|
}).partial().refine((v) => Object.keys(v).length > 0, "at least one field");
|
|
3955
|
-
var WebhookEndpointWithSecret =
|
|
4168
|
+
var WebhookEndpointWithSecret = z15.object({
|
|
3956
4169
|
endpoint: WebhookEndpoint,
|
|
3957
|
-
secret:
|
|
4170
|
+
secret: z15.string().min(32)
|
|
3958
4171
|
});
|
|
3959
4172
|
var WEBHOOK_DELIVERY_STATUSES = ["pending", "succeeded", "failed"];
|
|
3960
|
-
var WebhookDeliveryStatus =
|
|
4173
|
+
var WebhookDeliveryStatus = z15.enum(WEBHOOK_DELIVERY_STATUSES);
|
|
3961
4174
|
var WEBHOOK_MAX_ATTEMPTS = 8;
|
|
3962
4175
|
var WEBHOOK_RETRY_DELAYS_SECONDS = [30, 120, 600, 1800, 3600, 7200, 21600];
|
|
3963
|
-
var WebhookDelivery =
|
|
4176
|
+
var WebhookDelivery = z15.object({
|
|
3964
4177
|
id: Id,
|
|
3965
4178
|
endpoint_id: Id,
|
|
3966
4179
|
event_id: Id,
|
|
3967
4180
|
event_type: WebhookEventType,
|
|
3968
4181
|
status: WebhookDeliveryStatus,
|
|
3969
|
-
attempts:
|
|
3970
|
-
last_status_code:
|
|
3971
|
-
last_error:
|
|
4182
|
+
attempts: z15.number().int().min(0),
|
|
4183
|
+
last_status_code: z15.number().int().min(100).max(599).nullable(),
|
|
4184
|
+
last_error: z15.string().nullable(),
|
|
3972
4185
|
next_attempt_at: Timestamp.nullable(),
|
|
3973
4186
|
delivered_at: Timestamp.nullable(),
|
|
3974
4187
|
created_at: Timestamp
|
|
@@ -3977,7 +4190,7 @@ var WebhookDeliveryListQuery = PageQuery.extend({
|
|
|
3977
4190
|
status: WebhookDeliveryStatus.optional(),
|
|
3978
4191
|
event_type: WebhookEventType.optional()
|
|
3979
4192
|
});
|
|
3980
|
-
var WebhookDeliveryParams =
|
|
4193
|
+
var WebhookDeliveryParams = z15.object({ id: Id, delivery_id: Id });
|
|
3981
4194
|
|
|
3982
4195
|
// src/webhook-signing.ts
|
|
3983
4196
|
var WEBHOOK_SIGNATURE_HEADER = "x-mail-signature";
|
|
@@ -4044,79 +4257,79 @@ async function verifyWebhook(input) {
|
|
|
4044
4257
|
}
|
|
4045
4258
|
|
|
4046
4259
|
// src/billing.ts
|
|
4047
|
-
import { z as
|
|
4260
|
+
import { z as z16 } from "zod";
|
|
4048
4261
|
var PLAN_IDS = ["free", "starter", "growth", "design_partner"];
|
|
4049
|
-
var PlanId =
|
|
4050
|
-
var PlanLimits =
|
|
4051
|
-
monthly_messages:
|
|
4052
|
-
contacts:
|
|
4053
|
-
members:
|
|
4054
|
-
providers:
|
|
4055
|
-
webhook_endpoints:
|
|
4262
|
+
var PlanId = z16.enum(PLAN_IDS);
|
|
4263
|
+
var PlanLimits = z16.object({
|
|
4264
|
+
monthly_messages: z16.number().int().min(0).nullable(),
|
|
4265
|
+
contacts: z16.number().int().min(0).nullable(),
|
|
4266
|
+
members: z16.number().int().min(1).nullable(),
|
|
4267
|
+
providers: z16.number().int().min(1).nullable(),
|
|
4268
|
+
webhook_endpoints: z16.number().int().min(0).nullable(),
|
|
4056
4269
|
/** Automations that may be live (active or paused) at once (A1). */
|
|
4057
|
-
active_automations:
|
|
4270
|
+
active_automations: z16.number().int().min(0).nullable(),
|
|
4058
4271
|
/** Steps one automation may hold. Null is the flow's own cap, not unlimited. */
|
|
4059
|
-
automation_steps:
|
|
4272
|
+
automation_steps: z16.number().int().min(1).nullable()
|
|
4060
4273
|
});
|
|
4061
|
-
var PlanFeatures =
|
|
4274
|
+
var PlanFeatures = z16.object({
|
|
4062
4275
|
/** A/B tests of subject and content on a mailing (S4). */
|
|
4063
|
-
ab_testing:
|
|
4276
|
+
ab_testing: z16.boolean(),
|
|
4064
4277
|
/** Open and click tracking, still off until a workspace turns it on (`settings.tracking_enabled`). */
|
|
4065
|
-
tracking:
|
|
4278
|
+
tracking: z16.boolean(),
|
|
4066
4279
|
/** Sending and hosting pages on the workspace's own domain (a later phase). */
|
|
4067
|
-
custom_domains:
|
|
4280
|
+
custom_domains: z16.boolean(),
|
|
4068
4281
|
/** Automations: flows a person walks through step by step (A1). */
|
|
4069
|
-
automations:
|
|
4282
|
+
automations: z16.boolean()
|
|
4070
4283
|
});
|
|
4071
4284
|
var PLAN_FEATURES = ["ab_testing", "tracking", "custom_domains", "automations"];
|
|
4072
|
-
var Plan =
|
|
4285
|
+
var Plan = z16.object({
|
|
4073
4286
|
id: PlanId,
|
|
4074
|
-
name:
|
|
4287
|
+
name: z16.string().min(1),
|
|
4075
4288
|
/** Price in the smallest currency unit per month; null for plans not sold. */
|
|
4076
|
-
monthly_price_cents:
|
|
4077
|
-
currency:
|
|
4289
|
+
monthly_price_cents: z16.number().int().min(0).nullable(),
|
|
4290
|
+
currency: z16.string().length(3),
|
|
4078
4291
|
limits: PlanLimits,
|
|
4079
4292
|
features: PlanFeatures
|
|
4080
4293
|
});
|
|
4081
|
-
var CatalogPlan = Plan.extend({ sellable:
|
|
4082
|
-
var Subscription =
|
|
4294
|
+
var CatalogPlan = Plan.extend({ sellable: z16.boolean() });
|
|
4295
|
+
var Subscription = z16.object({
|
|
4083
4296
|
workspace_id: Id,
|
|
4084
4297
|
plan: PlanId,
|
|
4085
|
-
status:
|
|
4298
|
+
status: z16.enum(["active", "trialing", "past_due", "cancelled"]),
|
|
4086
4299
|
current_period_start: Timestamp,
|
|
4087
4300
|
current_period_end: Timestamp,
|
|
4088
|
-
cancel_at_period_end:
|
|
4301
|
+
cancel_at_period_end: z16.boolean(),
|
|
4089
4302
|
/**
|
|
4090
4303
|
* A design partner outside billing (plan `design_partner`, no limits, no
|
|
4091
4304
|
* checkout). Set only by an operator, never through the API.
|
|
4092
4305
|
*/
|
|
4093
|
-
billing_exempt:
|
|
4306
|
+
billing_exempt: z16.boolean()
|
|
4094
4307
|
});
|
|
4095
4308
|
var USAGE_METRICS = ["messages", "contacts", "members", "providers", "webhook_endpoints", "active_automations"];
|
|
4096
|
-
var UsageMetric =
|
|
4309
|
+
var UsageMetric = z16.enum(USAGE_METRICS);
|
|
4097
4310
|
var USAGE_WARNING_RATIO = 0.8;
|
|
4098
|
-
var UsageWarning =
|
|
4311
|
+
var UsageWarning = z16.object({
|
|
4099
4312
|
metric: UsageMetric,
|
|
4100
|
-
used:
|
|
4101
|
-
limit:
|
|
4313
|
+
used: z16.number().int().min(0),
|
|
4314
|
+
limit: z16.number().int().min(0),
|
|
4102
4315
|
/** `approaching`: at or above USAGE_WARNING_RATIO of the limit; `reached`: at or above the limit. */
|
|
4103
|
-
level:
|
|
4316
|
+
level: z16.enum(["approaching", "reached"])
|
|
4104
4317
|
});
|
|
4105
|
-
var Usage =
|
|
4318
|
+
var Usage = z16.object({
|
|
4106
4319
|
plan: PlanId,
|
|
4107
4320
|
period_start: Timestamp,
|
|
4108
4321
|
period_end: Timestamp,
|
|
4109
|
-
metrics:
|
|
4110
|
-
warnings:
|
|
4322
|
+
metrics: z16.record(UsageMetric, z16.object({ used: z16.number().int().min(0), limit: z16.number().int().min(0).nullable() })),
|
|
4323
|
+
warnings: z16.array(UsageWarning)
|
|
4111
4324
|
});
|
|
4112
|
-
var CheckoutRequest =
|
|
4113
|
-
plan:
|
|
4114
|
-
success_url:
|
|
4115
|
-
cancel_url:
|
|
4325
|
+
var CheckoutRequest = z16.object({
|
|
4326
|
+
plan: z16.enum(["starter", "growth"]),
|
|
4327
|
+
success_url: z16.string().url(),
|
|
4328
|
+
cancel_url: z16.string().url()
|
|
4116
4329
|
});
|
|
4117
|
-
var CheckoutSession =
|
|
4118
|
-
var PortalRequest =
|
|
4119
|
-
var PortalSession =
|
|
4330
|
+
var CheckoutSession = z16.object({ url: z16.string().url() });
|
|
4331
|
+
var PortalRequest = z16.object({ return_url: z16.string().url() });
|
|
4332
|
+
var PortalSession = z16.object({ url: z16.string().url() });
|
|
4120
4333
|
var USAGE_WARNING_HEADER = "x-mail-usage-warning";
|
|
4121
4334
|
function formatUsageWarningHeader(warnings) {
|
|
4122
4335
|
if (warnings.length === 0) return null;
|
|
@@ -4136,7 +4349,7 @@ function parseUsageWarningHeader(value) {
|
|
|
4136
4349
|
}
|
|
4137
4350
|
|
|
4138
4351
|
// src/routes.ts
|
|
4139
|
-
import { z as
|
|
4352
|
+
import { z as z17 } from "zod";
|
|
4140
4353
|
var list = (item) => page(item);
|
|
4141
4354
|
var foundationRoutes = {
|
|
4142
4355
|
"workspaces.create": {
|
|
@@ -4364,7 +4577,7 @@ var templateRoutes = {
|
|
|
4364
4577
|
path: "/v1/templates/:id/export",
|
|
4365
4578
|
params: IdParams,
|
|
4366
4579
|
query: TemplateExportQuery,
|
|
4367
|
-
response:
|
|
4580
|
+
response: z17.string(),
|
|
4368
4581
|
responseType: "text",
|
|
4369
4582
|
status: 200,
|
|
4370
4583
|
access: "read",
|
|
@@ -4688,7 +4901,7 @@ var sendingRoutes = {
|
|
|
4688
4901
|
path: "/v1/mailings/:id/export",
|
|
4689
4902
|
params: IdParams,
|
|
4690
4903
|
query: MailingExportQuery,
|
|
4691
|
-
response:
|
|
4904
|
+
response: z17.string(),
|
|
4692
4905
|
responseType: "text",
|
|
4693
4906
|
status: 200,
|
|
4694
4907
|
access: "read",
|
|
@@ -4734,6 +4947,21 @@ var sendingRoutes = {
|
|
|
4734
4947
|
access: "write",
|
|
4735
4948
|
phase: "S2"
|
|
4736
4949
|
},
|
|
4950
|
+
/**
|
|
4951
|
+
* A dry run: what each given recipient would be sent (every value's text and
|
|
4952
|
+
* the layer it comes from, each insertion's choice, the problems found),
|
|
4953
|
+
* without adding anybody. Reads only, in any state of the mailing.
|
|
4954
|
+
*/
|
|
4955
|
+
"mailings.resolve": {
|
|
4956
|
+
method: "POST",
|
|
4957
|
+
path: "/v1/mailings/:id/resolve",
|
|
4958
|
+
params: IdParams,
|
|
4959
|
+
body: MailingResolveRequest,
|
|
4960
|
+
response: MailingResolveResult,
|
|
4961
|
+
status: 200,
|
|
4962
|
+
access: "read",
|
|
4963
|
+
phase: "S2"
|
|
4964
|
+
},
|
|
4737
4965
|
"mailings.send": {
|
|
4738
4966
|
method: "POST",
|
|
4739
4967
|
path: "/v1/mailings/:id/send",
|
|
@@ -4921,7 +5149,7 @@ var platformRoutes = {
|
|
|
4921
5149
|
"contactProperties.list": {
|
|
4922
5150
|
method: "GET",
|
|
4923
5151
|
path: "/v1/contact-properties",
|
|
4924
|
-
response:
|
|
5152
|
+
response: z17.object({ data: z17.array(ContactPropertyDefinition) }),
|
|
4925
5153
|
status: 200,
|
|
4926
5154
|
access: "read",
|
|
4927
5155
|
phase: "S4"
|
|
@@ -5220,7 +5448,7 @@ var billingRoutes = {
|
|
|
5220
5448
|
"billing.plans": {
|
|
5221
5449
|
method: "GET",
|
|
5222
5450
|
path: "/v1/billing/plans",
|
|
5223
|
-
response:
|
|
5451
|
+
response: z17.object({ data: z17.array(CatalogPlan) }),
|
|
5224
5452
|
status: 200,
|
|
5225
5453
|
access: "read",
|
|
5226
5454
|
phase: "S5"
|
|
@@ -5362,7 +5590,7 @@ var automationRoutes = {
|
|
|
5362
5590
|
method: "POST",
|
|
5363
5591
|
path: "/v1/automations/:id/pause",
|
|
5364
5592
|
params: IdParams,
|
|
5365
|
-
body:
|
|
5593
|
+
body: z17.object({}).strict(),
|
|
5366
5594
|
response: Automation,
|
|
5367
5595
|
status: 200,
|
|
5368
5596
|
access: "write",
|
|
@@ -5372,7 +5600,7 @@ var automationRoutes = {
|
|
|
5372
5600
|
method: "POST",
|
|
5373
5601
|
path: "/v1/automations/:id/resume",
|
|
5374
5602
|
params: IdParams,
|
|
5375
|
-
body:
|
|
5603
|
+
body: z17.object({}).strict(),
|
|
5376
5604
|
response: Automation,
|
|
5377
5605
|
status: 200,
|
|
5378
5606
|
access: "write",
|
|
@@ -5382,7 +5610,7 @@ var automationRoutes = {
|
|
|
5382
5610
|
method: "POST",
|
|
5383
5611
|
path: "/v1/automations/:id/archive",
|
|
5384
5612
|
params: IdParams,
|
|
5385
|
-
body:
|
|
5613
|
+
body: z17.object({}).strict(),
|
|
5386
5614
|
response: Automation,
|
|
5387
5615
|
status: 200,
|
|
5388
5616
|
access: "write",
|
|
@@ -5499,6 +5727,12 @@ var routes = {
|
|
|
5499
5727
|
...billingRoutes,
|
|
5500
5728
|
...automationRoutes
|
|
5501
5729
|
};
|
|
5730
|
+
var TRUSTED_APP_OPERATIONS = {
|
|
5731
|
+
booking: ["workspaces.list", "workspace.update", "providers.list", "apiKeys.create", "apiKeys.revoke"]
|
|
5732
|
+
};
|
|
5733
|
+
function trustedAppMayCall(app, id) {
|
|
5734
|
+
return TRUSTED_APP_OPERATIONS[app].includes(id);
|
|
5735
|
+
}
|
|
5502
5736
|
function acceptsIdempotencyKey(route) {
|
|
5503
5737
|
return route.method !== "GET";
|
|
5504
5738
|
}
|
|
@@ -5516,13 +5750,13 @@ function matchRoute(method, pathname) {
|
|
|
5516
5750
|
for (const [id, route] of Object.entries(routes)) {
|
|
5517
5751
|
if (route.method !== method) continue;
|
|
5518
5752
|
const names = [];
|
|
5519
|
-
const
|
|
5753
|
+
const pattern3 = new RegExp(
|
|
5520
5754
|
"^" + route.path.replace(/[.*+?^${}()|[\]\\]/g, "\\$&").replace(/:([a-z_]+)/g, (_, name) => {
|
|
5521
5755
|
names.push(name);
|
|
5522
5756
|
return "([^/]+)";
|
|
5523
5757
|
}) + "$"
|
|
5524
5758
|
);
|
|
5525
|
-
const match =
|
|
5759
|
+
const match = pattern3.exec(pathname);
|
|
5526
5760
|
if (!match) continue;
|
|
5527
5761
|
const params = {};
|
|
5528
5762
|
names.forEach((name, i) => {
|
|
@@ -5722,6 +5956,7 @@ export {
|
|
|
5722
5956
|
InviteListQuery,
|
|
5723
5957
|
InviteStatus,
|
|
5724
5958
|
JsonValue,
|
|
5959
|
+
LETTER_ONLY_MERGE_FIELDS,
|
|
5725
5960
|
LETTER_RECIPIENT_REFUSAL,
|
|
5726
5961
|
LIST_UNSUBSCRIBE_POST_VALUE,
|
|
5727
5962
|
LOCALE_TAG_PATTERN,
|
|
@@ -5790,7 +6025,10 @@ export {
|
|
|
5790
6025
|
MailingLanguageReport,
|
|
5791
6026
|
MailingLanguageStatus,
|
|
5792
6027
|
MailingListQuery,
|
|
6028
|
+
MailingMerge,
|
|
5793
6029
|
MailingMetadata,
|
|
6030
|
+
MailingResolveRequest,
|
|
6031
|
+
MailingResolveResult,
|
|
5794
6032
|
MailingRetryFailedRequest,
|
|
5795
6033
|
MailingScheduleFailedData,
|
|
5796
6034
|
MailingScheduleRequest,
|
|
@@ -5815,6 +6053,8 @@ export {
|
|
|
5815
6053
|
MessageOutcome,
|
|
5816
6054
|
MessageSentData,
|
|
5817
6055
|
MessageSummary,
|
|
6056
|
+
MessageValues,
|
|
6057
|
+
NAME_ON_BROADCAST_REFUSAL,
|
|
5818
6058
|
NotifyRecipients,
|
|
5819
6059
|
NotifyStep,
|
|
5820
6060
|
ON_BEHALF_OF_HEADER,
|
|
@@ -5850,6 +6090,8 @@ export {
|
|
|
5850
6090
|
REQUEST_ID_HEADER,
|
|
5851
6091
|
RESEND_EVENT_TYPES,
|
|
5852
6092
|
RESERVED_MERGE_FIELDS,
|
|
6093
|
+
RESOLVE_PROBLEMS,
|
|
6094
|
+
RESOLVE_REJECTIONS,
|
|
5853
6095
|
RETRYABLE_ERRORS,
|
|
5854
6096
|
RETRY_AFTER_HEADER,
|
|
5855
6097
|
RE_ENTRY_MODES,
|
|
@@ -5862,6 +6104,9 @@ export {
|
|
|
5862
6104
|
RecipientStatus,
|
|
5863
6105
|
RemoteImage,
|
|
5864
6106
|
ResendSigningSecret,
|
|
6107
|
+
ResolveProblem,
|
|
6108
|
+
ResolvedRecipient,
|
|
6109
|
+
ResolvedValue,
|
|
5865
6110
|
SERVICE_FILLED_MERGE_FIELDS,
|
|
5866
6111
|
SIGNUP_FORM_FIELDS,
|
|
5867
6112
|
SKIP_REASONS,
|
|
@@ -5898,6 +6143,8 @@ export {
|
|
|
5898
6143
|
TEMPLATE_SORTS,
|
|
5899
6144
|
TEMPLATE_SORT_DEFAULT_ORDER,
|
|
5900
6145
|
TERMINAL_MAILING_STATUSES,
|
|
6146
|
+
TRUSTED_APPS,
|
|
6147
|
+
TRUSTED_APP_OPERATIONS,
|
|
5901
6148
|
Tag,
|
|
5902
6149
|
TagAssignment,
|
|
5903
6150
|
TagCreate,
|
|
@@ -5943,15 +6190,19 @@ export {
|
|
|
5943
6190
|
TrackingSettings,
|
|
5944
6191
|
TranslationInsertion,
|
|
5945
6192
|
TranslationInsertions,
|
|
6193
|
+
TrustedApp,
|
|
5946
6194
|
UNSUBSCRIBE_PATH_PREFIX,
|
|
5947
6195
|
UNSUBSCRIBE_SOURCES,
|
|
5948
6196
|
USAGE_METRICS,
|
|
5949
6197
|
USAGE_WARNING_HEADER,
|
|
5950
6198
|
USAGE_WARNING_RATIO,
|
|
6199
|
+
UnfilledLink,
|
|
5951
6200
|
Usage,
|
|
5952
6201
|
UsageMetric,
|
|
5953
6202
|
UsageWarning,
|
|
6203
|
+
VALUE_LAYERS,
|
|
5954
6204
|
ValidationIssue,
|
|
6205
|
+
ValueLayer,
|
|
5955
6206
|
VariantAnalytics,
|
|
5956
6207
|
WEBHOOK_DELIVERY_STATUSES,
|
|
5957
6208
|
WEBHOOK_EVENT_ID_HEADER,
|
|
@@ -6015,6 +6266,7 @@ export {
|
|
|
6015
6266
|
editMainInsertions,
|
|
6016
6267
|
effectivePreviewPicks,
|
|
6017
6268
|
emptyFlow,
|
|
6269
|
+
emptyLinkValues,
|
|
6018
6270
|
errorBody,
|
|
6019
6271
|
escapeMergeHtml,
|
|
6020
6272
|
exportFilename,
|
|
@@ -6045,6 +6297,8 @@ export {
|
|
|
6045
6297
|
languageInsertions,
|
|
6046
6298
|
languageName,
|
|
6047
6299
|
languageTabs,
|
|
6300
|
+
letterOnlyValues,
|
|
6301
|
+
linkTokens,
|
|
6048
6302
|
mainInsertionText,
|
|
6049
6303
|
matchLocale,
|
|
6050
6304
|
matchRoute,
|
|
@@ -6065,6 +6319,8 @@ export {
|
|
|
6065
6319
|
previewInsertions,
|
|
6066
6320
|
previewPersonalization,
|
|
6067
6321
|
primaryLanguage,
|
|
6322
|
+
printedFields,
|
|
6323
|
+
rawMergeValue,
|
|
6068
6324
|
reachable,
|
|
6069
6325
|
readyWithoutInsertions,
|
|
6070
6326
|
readyWithoutSubject,
|
|
@@ -6081,6 +6337,7 @@ export {
|
|
|
6081
6337
|
renameTokenInDocument,
|
|
6082
6338
|
renameTokenInText,
|
|
6083
6339
|
resolveInsertions,
|
|
6340
|
+
resolveValue,
|
|
6084
6341
|
restorePatch,
|
|
6085
6342
|
routes,
|
|
6086
6343
|
sameJson,
|
|
@@ -6104,8 +6361,11 @@ export {
|
|
|
6104
6361
|
translationInsertionProblems,
|
|
6105
6362
|
translationTextFromInput,
|
|
6106
6363
|
triggerKey,
|
|
6364
|
+
trustedAppMayCall,
|
|
6107
6365
|
unknownRefusedInsertions,
|
|
6108
6366
|
unplacedInsertions,
|
|
6109
6367
|
validateAutomation,
|
|
6368
|
+
valueNames,
|
|
6369
|
+
valuesRead,
|
|
6110
6370
|
verifyWebhook
|
|
6111
6371
|
};
|